Skip to content

Commit a7125ee

Browse files
committed
[[ Bug 20931 ]] Bridge values in LCB assign-array and assign-list ops
This patch fixes a bug where the behavior of code built using the [ ... ] and { ... } syntax in LCB is different from that when using explicit code. A new method 'Bridge' has been added to the VM's execute context. This method performs a 'convert to optional any' on the input value resulting in bridgeable foreign values being imported, and all other values being retained. This method is now called on the input values in the assign-list and assign-array opcodes meaning that foreign values being built into lists will bridge. Additionally, an extra compiler check has been added to variadic arguments (i.e. arguments to the variadic portion of a C variadic handler). This check restricts such arguments to being variable identifiers where the variable has an explicit type declaration. This is necessary to ensure that code explicitly states the type which is being passed in variadic positions as there is no other means for the compiler or the VM to know what the type should be.
1 parent 66d18be commit a7125ee

File tree

6 files changed

+103
-14
lines changed

6 files changed

+103
-14
lines changed

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

toolchain/lc-compile/src/check.g

Lines changed: 15 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,20 @@
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 -> variable)
1516+
QuerySymbolId(Id -> Info)
1517+
Info'Type -> Type
1518+
ne(Type, unspecified)
1519+
1520+
'rule' CheckExpressionIsExplicitlyTypedVariable(Expr):
1521+
GetExpressionPosition(Expr -> Position)
1522+
Error_VariadicArgumentNotExplicitlyTyped(Position)
1523+
15101524
'action' CheckExpressionIsEvaluatable(EXPRESSION)
15111525
15121526
'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)