Skip to content

Commit 07474c7

Browse files
Merge pull request livecode#6310 from runrevmark/bugfix-20931
[[ Bug 20931 ]] Bridge values in LCB assign-array and assign-list ops
2 parents e96c297 + 615a87b commit 07474c7

File tree

10 files changed

+235
-19
lines changed

10 files changed

+235
-19
lines changed

docs/guides/LiveCode Builder Language Reference.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1062,7 +1062,8 @@ Result expressions are not assignable.
10621062
: '[' [ <Elements: ExpressionList> ] ']'
10631063

10641064
A list expression evaluates all the elements in the expression list from
1065-
left to right and constructs a list value with them as elements.
1065+
left to right and constructs a list value with them as elements. Each
1066+
expression is converted to `optional any` when constructing the list.
10661067

10671068
The elements list is optional, so the empty list can be specified as
10681069
*[]*.
@@ -1080,7 +1081,8 @@ List expressions are not assignable.
10801081

10811082
An array expression evaluates all of the key and value expressions
10821083
from left to right, and constructs an **Array** value as appropriate.
1083-
Each key expression must evaluate to a **String**.
1084+
Each key expression must evaluate to a **String**. Each value expression
1085+
is converted to `optional any` when constructing the array.
10841086

10851087
The contents are optional, so the empty array can be written as `{}`.
10861088

docs/lcb/notes/20931.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# LiveCode Builder Virtual Machine
2+
## Array and list assign ops
3+
4+
Previously there was a difference between constructing a list or array
5+
using `push` or `put` and using list or array assigment expressions `[]`
6+
and `{}`, namely values were converted to `optional any` only in the
7+
latter case. For consistency, they are now converted in both cases.
8+
9+
# [20931] Values should bridge to optional any in array and list assign

libscript/src/script-bytecode.hpp

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -753,7 +753,7 @@ struct MCScriptBytecodeOp_AssignList
753753

754754
static void Execute(MCScriptExecuteContext& ctxt)
755755
{
756-
MCAutoArray<MCValueRef> t_elements;
756+
MCAutoValueRefArray t_elements;
757757
if (!t_elements.Resize(ctxt.GetArgumentCount() - 1))
758758
{
759759
ctxt.Rethrow();
@@ -762,16 +762,26 @@ struct MCScriptBytecodeOp_AssignList
762762

763763
for(uindex_t t_element_idx = 0; t_element_idx < t_elements.Size(); t_element_idx++)
764764
{
765-
t_elements[t_element_idx] = ctxt.CheckedFetchRegister(ctxt.GetArgument(t_element_idx + 1));
766-
if (t_elements[t_element_idx] == nil)
767-
return;
765+
MCValueRef t_raw_value = ctxt.CheckedFetchRegister(ctxt.GetArgument(t_element_idx + 1));
766+
if (t_raw_value == nullptr)
767+
{
768+
return;
769+
}
770+
771+
/* When assigning to a normal slot, Convert is called which bridges
772+
* foreign values when being placed into an explicitly typed non-
773+
* foreign slot. The case of assigning to an array element is the
774+
* same, so we must explicitly bridge. */
775+
if (!ctxt.Bridge(t_raw_value,
776+
t_elements[t_element_idx]))
777+
{
778+
return;
779+
}
768780
}
769781

770782
MCAutoProperListRef t_list;
771-
if (!MCProperListCreate(t_elements.Ptr(),
772-
t_elements.Size(),
773-
&t_list))
774-
{
783+
if (!t_elements.TakeAsProperList(&t_list))
784+
{
775785
ctxt.Rethrow();
776786
return;
777787
}
@@ -828,6 +838,7 @@ struct MCScriptBytecodeOp_AssignArray
828838
{
829839
return;
830840
}
841+
831842
if (MCValueGetTypeCode(t_raw_key) != kMCValueTypeCodeString)
832843
{
833844
ctxt.ThrowNotAStringValue(t_raw_key);
@@ -842,17 +853,28 @@ struct MCScriptBytecodeOp_AssignArray
842853
return;
843854
}
844855

845-
MCValueRef t_value;
846-
t_value = ctxt.CheckedFetchRegister(ctxt.GetArgument(t_arg_idx + 1));
847-
if (t_value == nil)
856+
MCValueRef t_raw_value;
857+
t_raw_value = ctxt.CheckedFetchRegister(ctxt.GetArgument(t_arg_idx + 1));
858+
if (t_raw_value == nil)
848859
{
849860
return;
850861
}
862+
863+
/* When assigning to a normal slot, Convert is called which bridges
864+
* foreign values when being placed into an explicitly typed non-
865+
* foreign slot. The case of assigning to an array element is the
866+
* same, so we must explicitly bridge. */
867+
MCAutoValueRef t_value;
868+
if (!ctxt.Bridge(t_raw_value,
869+
&t_value))
870+
{
871+
return;
872+
}
851873

852874
if (!MCArrayStoreValue(*t_array,
853875
false,
854876
*t_key,
855-
t_value))
877+
*t_value))
856878
{
857879
ctxt.Rethrow();
858880
return;

libscript/src/script-execute.cpp

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -888,6 +888,47 @@ MCScriptExecuteContext::InvokeForeign(MCScriptInstanceRef p_instance,
888888
}
889889
}
890890

891+
bool
892+
MCScriptExecuteContext::Bridge(MCValueRef p_value,
893+
MCValueRef& r_output_value)
894+
{
895+
// Get resolved typeinfo for the value.
896+
MCResolvedTypeInfo t_resolved_type;
897+
if (!ResolveTypeInfo(MCValueGetTypeInfo(p_value),
898+
t_resolved_type))
899+
{
900+
return false;
901+
}
902+
903+
// Get the foreign type descriptor for the value's type, if any.
904+
const MCForeignTypeDescriptor *t_desc = nullptr;
905+
if (MCTypeInfoIsForeign(t_resolved_type.type))
906+
{
907+
t_desc = MCForeignTypeInfoGetDescriptor(t_resolved_type.type);
908+
}
909+
910+
// If the type is not foreign or is not bridgeable foreign, just retain;
911+
// otherwise use doimport.
912+
if (t_desc == nullptr ||
913+
t_desc->doimport == nullptr)
914+
{
915+
r_output_value = MCValueRetain(p_value);
916+
}
917+
else
918+
{
919+
if (!t_desc->doimport(t_desc,
920+
MCForeignValueGetContentsPtr(p_value),
921+
false,
922+
r_output_value))
923+
{
924+
Rethrow();
925+
return false;
926+
}
927+
}
928+
929+
return true;
930+
}
931+
891932
bool
892933
MCScriptExecuteContext::Convert(MCValueRef p_value,
893934
MCTypeInfoRef p_to_type,

libscript/src/script-execute.hpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,16 @@ class MCScriptExecuteContext
179179
// Leave the LCB VM. If a execution completed successfully then true is
180180
// returned, otherwise false is returned.
181181
bool Leave(void);
182-
182+
183+
// Attempt to bridge the input value. If the input value is foreign and
184+
// the foreign type is bridgeable, doimport is used to create the output
185+
// value. Otherwise, the input value is retained and returned as the output
186+
// value.
187+
// Note: This is a specialization of Convert where the to_type is 'optional
188+
// any'.
189+
bool Bridge(MCValueRef value,
190+
MCValueRef& r_bridged_value);
191+
183192
// Attempt to convert the input value to the specified type. If the conversion
184193
// cannot be performed because the type does not conform, then 'true' will
185194
// be returned, but r_new_value will be nil; allowing the context to throw

tests/lcb/compiler/frontend/variadic.compilertest

Lines changed: 58 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,14 +42,69 @@ end module
4242
%ERROR "Variadic parameters only allowed in foreign handlers" AT BEFORE_VARIADIC
4343
%ENDTEST
4444

45+
%% Variadic arguments must be an explicitly-typed variable
46+
%TEST VariadicNotAllowedImplicitlyTypedArgument
47+
module compiler_test
48+
__safe foreign handler TestVariadic(in pA as any, ...) returns nothing binds to ""
49+
handler CallVariadic()
50+
TestVariadic("pA", %{BEFORE_IMPLICIT_ARG}1)
51+
end handler
52+
end module
53+
%EXPECT PASS
54+
%ERROR "Variadic arguments must be an explicitly-typed variable" AT BEFORE_IMPLICIT_ARG
55+
%ENDTEST
56+
57+
%% Variadic arguments can be local variables
58+
%TEST VariadicArgumentModuleLocal
59+
module compiler_test
60+
__safe foreign handler TestVariadic(in pA as any, ...) returns nothing binds to ""
61+
handler CallVariadic()
62+
variable tVar as Integer
63+
TestVariadic("pA", tVar)
64+
end handler
65+
end module
66+
%EXPECT PASS
67+
%SUCCESS
68+
%ENDTEST
69+
70+
%% Variadic arguments can be module locals
71+
%TEST VariadicArgumentModuleLocal
72+
module compiler_test
73+
private variable mVar as Integer
74+
__safe foreign handler TestVariadic(in pA as any, ...) returns nothing binds to ""
75+
handler CallVariadic()
76+
TestVariadic("pA", mVar)
77+
end handler
78+
end module
79+
%EXPECT PASS
80+
%SUCCESS
81+
%ENDTEST
82+
83+
%% Variadic arguments can be parameter variables
84+
%TEST VariadicArgumentParameterVariable
85+
module compiler_test
86+
__safe foreign handler TestVariadic(in pA as any, ...) returns nothing binds to ""
87+
handler CallVariadic(in pVar as Integer)
88+
TestVariadic("pA", pVar)
89+
end handler
90+
end module
91+
%EXPECT PASS
92+
%SUCCESS
93+
%ENDTEST
94+
4595
%% You can have 0 to any number of variadic arguments
4696
%TEST VariadicParametersAnyCount
4797
module compiler_test
4898
__safe foreign handler TestVariadic(in pA as any, ...) returns nothing binds to ""
4999
handler CallVariadic()
50-
TestVariadic(1)
51-
TestVariadic(1, 2)
52-
TestVariadic(1, 2, 3)
100+
TestVariadic("pA")
101+
102+
variable tX as Integer
103+
variable tY as Integer
104+
variable tZ as Integer
105+
TestVariadic("pA", tX)
106+
TestVariadic("pA", tX, tY)
107+
TestVariadic("pA", tX, tY, tZ)
53108
end handler
54109
end module
55110
%EXPECT PASS

tests/lcb/vm/assign-ops.lcb

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
module __VMTEST.assign_ops
2+
3+
use com.livecode.foreign
4+
5+
foreign handler MCValueGetTypeInfo(in pValue as Pointer) returns Pointer binds to "<builtin>"
6+
7+
foreign handler MCTypeInfoIsForeign(in pTypeInfo as Pointer) returns CBool binds to "<builtin>"
8+
9+
foreign handler MCArrayFetchValue(in pArray as Array, in pCaseSensitive as CBool, in pKey as Pointer, out rValue as Pointer) returns CBool binds to "<builtin>"
10+
11+
foreign handler MCNameCreate(in pString as String, out rName as Pointer) returns CBool binds to "<builtin>"
12+
13+
foreign handler MCProperListFetchElementAtIndex(in pList as List, in pIndex as LCUIndex) returns Pointer binds to "<builtin>"
14+
15+
unsafe handler __IsForeignValue(in pValue as Pointer) returns Boolean
16+
return MCTypeInfoIsForeign(MCValueGetTypeInfo(pValue))
17+
end handler
18+
19+
public handler TestAssignArrayOpForeignBridge()
20+
unsafe
21+
variable tVar as CBool
22+
put false into tVar
23+
24+
variable tKey as Pointer
25+
MCNameCreate("key", tKey)
26+
27+
variable tValue as Pointer
28+
MCArrayFetchValue({"key": tVar}, false, tKey, tValue)
29+
30+
test "foreign value bridged to optional any in array assign" \
31+
when not __IsForeignValue(tValue)
32+
end unsafe
33+
end handler
34+
35+
public handler TestAssignListOpForeignBridge()
36+
unsafe
37+
variable tVar as CBool
38+
put false into tVar
39+
40+
variable tValue as Pointer
41+
put MCProperListFetchElementAtIndex([tVar], 0) into tValue
42+
43+
test "foreign value bridged to optional any in list assign" \
44+
when not __IsForeignValue(tValue)
45+
end unsafe
46+
end handler
47+
48+
end module

toolchain/lc-compile/src/check.g

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1463,7 +1463,7 @@
14631463

14641464
-- variadic parameter 'sticks' and matches the rest of the argument list
14651465
'rule' CheckCallArguments(Position, ParamRest:parameterlist(parameter(_, variadic, _, _), _), expressionlist(Argument, ArgRest)):
1466-
CheckExpressionIsEvaluatable(Argument)
1466+
CheckExpressionIsExplicitlyTypedVariable(Argument)
14671467
CheckCallArguments(Position, ParamRest, ArgRest)
14681468

14691469
'rule' CheckCallArguments(Position, parameterlist(parameter(_, variadic, _, _), _), nil):
@@ -1507,6 +1507,33 @@
15071507
|]
15081508
CheckInvokeArguments(Position, SigTail, Arguments)
15091509

1510+
'action' CheckExpressionIsExplicitlyTypedVariable(EXPRESSION)
1511+
1512+
'rule' CheckExpressionIsExplicitlyTypedVariable(slot(Position, Id)):
1513+
-- Only expressions binding to a slot which has a specified type are
1514+
-- 'explicitly typed'
1515+
QueryKindOfSymbolId(Id -> Kind)
1516+
1517+
-- Expression must be a variable of some kind, i.e. one of:
1518+
(|
1519+
-- A local variable
1520+
eq(Kind, local)
1521+
||
1522+
-- A parameter variable
1523+
eq(Kind, parameter)
1524+
||
1525+
-- A module local variable
1526+
eq(Kind, variable)
1527+
|)
1528+
1529+
QuerySymbolId(Id -> Info)
1530+
Info'Type -> Type
1531+
ne(Type, unspecified)
1532+
1533+
'rule' CheckExpressionIsExplicitlyTypedVariable(Expr):
1534+
GetExpressionPosition(Expr -> Position)
1535+
Error_VariadicArgumentNotExplicitlyTyped(Position)
1536+
15101537
'action' CheckExpressionIsEvaluatable(EXPRESSION)
15111538
15121539
'rule' CheckExpressionIsEvaluatable(invoke(Position, Info, Arguments)):

toolchain/lc-compile/src/support.g

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,7 @@
357357
Error_InvalidNameForNamespace
358358
Error_VariadicParametersOnlyAllowedInForeignHandlers
359359
Error_VariadicParameterMustBeLast
360+
Error_VariadicArgumentNotExplicitlyTyped
360361

361362
Warning_MetadataClausesShouldComeAfterUseClauses
362363
Warning_DeprecatedTypeName
@@ -788,6 +789,7 @@
788789

789790
'action' Error_VariadicParametersOnlyAllowedInForeignHandlers(Position: POS)
790791
'action' Error_VariadicParameterMustBeLast(Position: POS)
792+
'action' Error_VariadicArgumentNotExplicitlyTyped(Position: POS)
791793

792794
'action' Warning_MetadataClausesShouldComeAfterUseClauses(Position: POS)
793795
'action' Warning_DeprecatedTypeName(Position: POS, NewType: STRING)

toolchain/libcompile/src/report.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,7 @@ DEFINE_ERROR_I(UnsafeHandlerCallNotAllowedInSafeContext, "Unsafe handler '%s' ca
403403

404404
DEFINE_ERROR(VariadicParameterMustBeLast, "Variadic parameter must be the last")
405405
DEFINE_ERROR(VariadicParametersOnlyAllowedInForeignHandlers, "Variadic parameters only allowed in foreign handlers")
406+
DEFINE_ERROR(VariadicArgumentNotExplicitlyTyped, "Variadic arguments must be an explicitly-typed variable")
406407

407408
#define DEFINE_WARNING(Name, Message) \
408409
void Warning_##Name(intptr_t p_position) { _Warning(p_position, Message); }

0 commit comments

Comments
 (0)