Fix bugs with certain properties (and .x .y .z accessors) and add tests.

This commit is contained in:
Jeff Hutchinson 2021-08-16 22:02:24 -04:00
parent a449fadde2
commit 6ec40e86da
4 changed files with 125 additions and 48 deletions

View file

@ -1262,9 +1262,10 @@ U32 SlotAccessNode::compile(CodeStream& codeStream, U32 ip, TypeReq type)
codeStream.emit(OP_SETCUROBJECT);
codeStream.emit(OP_SETCURFIELD);
codeStream.emitSTE(slotName);
codeStream.emit(OP_POP_STK);
if (arrayExpr)
{
codeStream.emit(OP_SETCURFIELD_ARRAY);
@ -1307,6 +1308,8 @@ U32 InternalSlotAccessNode::compile(CodeStream& codeStream, U32 ip, TypeReq type
codeStream.emit(OP_SETCUROBJECT_INTERNAL);
codeStream.emit(recurse);
codeStream.emit(OP_POP_STK);
return codeStream.tell();
}
@ -1319,34 +1322,6 @@ TypeReq InternalSlotAccessNode::getPreferredType()
U32 SlotAssignNode::compile(CodeStream& codeStream, U32 ip, TypeReq type)
{
// first eval the expression TypeReqString
// if it's an array:
// if OP_ADVANCE_STR 1
// eval array
// OP_ADVANCE_STR 1
// evaluate object expr
// OP_SETCUROBJECT 1
// OP_SETCURFIELD 1
// fieldName 1
// OP_TERMINATE_REWIND_STR 1
// OP_SETCURFIELDARRAY 1
// OP_TERMINATE_REWIND_STR 1
// else
// OP_ADVANCE_STR
// evaluate object expr
// OP_SETCUROBJECT
// OP_SETCURFIELD
// fieldName
// OP_TERMINATE_REWIND_STR
// OP_SAVEFIELD
// convert to return type if necessary.
precompileIdent(slotName);
ip = valueExpr->compile(codeStream, ip, TypeReqString);
@ -1364,6 +1339,13 @@ U32 SlotAssignNode::compile(CodeStream& codeStream, U32 ip, TypeReq type)
codeStream.emit(OP_SETCURFIELD);
codeStream.emitSTE(slotName);
if (objectExpr)
{
// Don't pop unless we are assigning a field to an object
// (For initializer fields, we don't wanna pop)
codeStream.emit(OP_POP_STK);
}
if (arrayExpr)
{
codeStream.emit(OP_SETCURFIELD_ARRAY);
@ -1429,6 +1411,8 @@ U32 SlotAssignOpNode::compile(CodeStream& codeStream, U32 ip, TypeReq type)
codeStream.emit(OP_SETCURFIELD);
codeStream.emitSTE(slotName);
codeStream.emit(OP_POP_STK);
if (arrayExpr)
{
codeStream.emit(OP_SETCURFIELD_ARRAY);

View file

@ -1114,7 +1114,7 @@ void CodeBlock::dumpInstructions(U32 startIp, bool upToReturn)
case OP_SETCUROBJECT:
{
Con::printf("%i: OP_SETCUROBJECT stk=-1", ip - 1);
Con::printf("%i: OP_SETCUROBJECT stk=0", ip - 1);
break;
}

View file

@ -166,18 +166,17 @@ namespace Con
// Gets a component of an object's field value or a variable and returns it
// in val.
static void getFieldComponent(SimObject* object, StringTableEntry field, const char* array, StringTableEntry subField, char val[])
static void getFieldComponent(SimObject* object, StringTableEntry field, const char* array, StringTableEntry subField, char val[], S32 currentLocalRegister)
{
const char* prevVal = NULL;
// Grab value from object.
if (object && field)
prevVal = object->getDataField(field, array);
// Otherwise, grab from the string stack. The value coming in will always
// be a string because that is how multicomponent variables are handled.
else
prevVal = stack[_STK].getString();
else if (currentLocalRegister != -1)
prevVal = gEvalState.getLocalStringVariable(currentLocalRegister);
else if (gEvalState.currentVariable)
prevVal = gEvalState.getStringVariable();
// Make sure we got a value.
if (prevVal && *prevVal)
@ -198,15 +197,22 @@ static void getFieldComponent(SimObject* object, StringTableEntry field, const c
StringTable->insert("a")
};
static const StringTableEntry uvw[] =
{
StringTable->insert("u"),
StringTable->insert("v"),
StringTable->insert("w")
};
// Translate xyzw and rgba into the indexed component
// of the variable or field.
if (subField == xyzw[0] || subField == rgba[0])
if (subField == xyzw[0] || subField == rgba[0] || subField == uvw[0])
dStrcpy(val, StringUnit::getUnit(prevVal, 0, " \t\n"), 128);
else if (subField == xyzw[1] || subField == rgba[1])
else if (subField == xyzw[1] || subField == rgba[1] || subField == uvw[1])
dStrcpy(val, StringUnit::getUnit(prevVal, 1, " \t\n"), 128);
else if (subField == xyzw[2] || subField == rgba[2])
else if (subField == xyzw[2] || subField == rgba[2] || subField == uvw[2])
dStrcpy(val, StringUnit::getUnit(prevVal, 2, " \t\n"), 128);
else if (subField == xyzw[3] || subField == rgba[3])
@ -221,7 +227,7 @@ static void getFieldComponent(SimObject* object, StringTableEntry field, const c
// Sets a component of an object's field value based on the sub field. 'x' will
// set the first field, 'y' the second, and 'z' the third.
static void setFieldComponent(SimObject* object, StringTableEntry field, const char* array, StringTableEntry subField)
static void setFieldComponent(SimObject* object, StringTableEntry field, const char* array, StringTableEntry subField, S32 currentLocalRegister)
{
// Copy the current string value
char strValue[1024];
@ -233,7 +239,8 @@ static void setFieldComponent(SimObject* object, StringTableEntry field, const c
// Set the value on an object field.
if (object && field)
prevVal = object->getDataField(field, array);
else if (currentLocalRegister != -1)
prevVal = gEvalState.getLocalStringVariable(currentLocalRegister);
// Set the value on a variable.
else if (gEvalState.currentVariable)
prevVal = gEvalState.getStringVariable();
@ -277,6 +284,8 @@ static void setFieldComponent(SimObject* object, StringTableEntry field, const c
// Update the field or variable.
if (object && field)
object->setDataField(field, 0, val);
else if (currentLocalRegister != -1)
gEvalState.setLocalStringVariable(currentLocalRegister, val, dStrlen(val));
else if (gEvalState.currentVariable)
gEvalState.setStringVariable(val);
}
@ -758,6 +767,7 @@ ConsoleValue CodeBlock::exec(U32 ip, const char* functionName, Namespace* thisNa
}
const char* val;
S32 reg;
S32 currentRegister = -1;
// The frame temp is used by the variable accessor ops (OP_SAVEFIELD_* and
// OP_LOADFIELD_*) to store temporary values for the fields.
@ -1427,6 +1437,10 @@ ConsoleValue CodeBlock::exec(U32 ip, const char* functionName, Namespace* thisNa
prevObject = NULL;
curObject = NULL;
// Used for local variable caching of what is active...when we
// set a global, we aren't active
currentRegister = -1;
gEvalState.setCurVarName(var);
// In order to let docblocks work properly with variables, we have
@ -1445,6 +1459,10 @@ ConsoleValue CodeBlock::exec(U32 ip, const char* functionName, Namespace* thisNa
prevObject = NULL;
curObject = NULL;
// Used for local variable caching of what is active...when we
// set a global, we aren't active
currentRegister = -1;
gEvalState.setCurVarNameCreate(var);
// See OP_SETCURVAR for why we do this.
@ -1460,6 +1478,10 @@ ConsoleValue CodeBlock::exec(U32 ip, const char* functionName, Namespace* thisNa
prevObject = NULL;
curObject = NULL;
// Used for local variable caching of what is active...when we
// set a global, we aren't active
currentRegister = -1;
gEvalState.setCurVarName(var);
// See OP_SETCURVAR for why we do this.
@ -1475,6 +1497,10 @@ ConsoleValue CodeBlock::exec(U32 ip, const char* functionName, Namespace* thisNa
prevObject = NULL;
curObject = NULL;
// Used for local variable caching of what is active...when we
// set a global, we aren't active
currentRegister = -1;
gEvalState.setCurVarNameCreate(var);
// See OP_SETCURVAR for why we do this.
@ -1483,16 +1509,19 @@ ConsoleValue CodeBlock::exec(U32 ip, const char* functionName, Namespace* thisNa
break;
case OP_LOADVAR_UINT:
currentRegister = -1;
stack[_STK + 1].setInt(gEvalState.getIntVariable());
_STK++;
break;
case OP_LOADVAR_FLT:
currentRegister = -1;
stack[_STK + 1].setFloat(gEvalState.getFloatVariable());
_STK++;
break;
case OP_LOADVAR_STR:
currentRegister = -1;
stack[_STK + 1].setString(gEvalState.getStringVariable());
_STK++;
break;
@ -1511,18 +1540,21 @@ ConsoleValue CodeBlock::exec(U32 ip, const char* functionName, Namespace* thisNa
case OP_LOAD_LOCAL_VAR_UINT:
reg = code[ip++];
currentRegister = reg;
stack[_STK + 1].setInt(gEvalState.getLocalIntVariable(reg));
_STK++;
break;
case OP_LOAD_LOCAL_VAR_FLT:
reg = code[ip++];
currentRegister = reg;
stack[_STK + 1].setFloat(gEvalState.getLocalFloatVariable(reg));
_STK++;
break;
case OP_LOAD_LOCAL_VAR_STR:
reg = code[ip++];
currentRegister = reg;
val = gEvalState.getLocalStringVariable(reg);
stack[_STK + 1].setString(val);
_STK++;
@ -1548,7 +1580,7 @@ ConsoleValue CodeBlock::exec(U32 ip, const char* functionName, Namespace* thisNa
// Save the previous object for parsing vector fields.
prevObject = curObject;
val = stack[_STK].getString();
_STK--;
//_STK--;
// Sim::findObject will sometimes find valid objects from
// multi-component strings. This makes sure that doesn't
@ -1616,7 +1648,7 @@ ConsoleValue CodeBlock::exec(U32 ip, const char* functionName, Namespace* thisNa
// a special accessor?
char buff[FieldBufferSizeNumeric];
memset(buff, 0, sizeof(buff));
getFieldComponent(prevObject, prevField, prevFieldArray, curField, buff);
getFieldComponent(prevObject, prevField, prevFieldArray, curField, buff, currentRegister);
stack[_STK + 1].setInt(dAtol(buff));
}
_STK++;
@ -1631,7 +1663,7 @@ ConsoleValue CodeBlock::exec(U32 ip, const char* functionName, Namespace* thisNa
// a special accessor?
char buff[FieldBufferSizeNumeric];
memset(buff, 0, sizeof(buff));
getFieldComponent(prevObject, prevField, prevFieldArray, curField, buff);
getFieldComponent(prevObject, prevField, prevFieldArray, curField, buff, currentRegister);
stack[_STK + 1].setFloat(dAtod(buff));
}
_STK++;
@ -1649,7 +1681,7 @@ ConsoleValue CodeBlock::exec(U32 ip, const char* functionName, Namespace* thisNa
// a special accessor?
char buff[FieldBufferSizeString];
memset(buff, 0, sizeof(buff));
getFieldComponent(prevObject, prevField, prevFieldArray, curField, buff);
getFieldComponent(prevObject, prevField, prevFieldArray, curField, buff, currentRegister);
stack[_STK + 1].setString(buff);
}
_STK++;
@ -1662,7 +1694,7 @@ ConsoleValue CodeBlock::exec(U32 ip, const char* functionName, Namespace* thisNa
{
// The field is not being set on an object. Maybe it's
// a special accessor?
setFieldComponent( prevObject, prevField, prevFieldArray, curField );
setFieldComponent( prevObject, prevField, prevFieldArray, curField, currentRegister );
prevObject = NULL;
}
break;
@ -1674,7 +1706,7 @@ ConsoleValue CodeBlock::exec(U32 ip, const char* functionName, Namespace* thisNa
{
// The field is not being set on an object. Maybe it's
// a special accessor?
setFieldComponent( prevObject, prevField, prevFieldArray, curField );
setFieldComponent( prevObject, prevField, prevFieldArray, curField, currentRegister );
prevObject = NULL;
}
break;
@ -1686,7 +1718,7 @@ ConsoleValue CodeBlock::exec(U32 ip, const char* functionName, Namespace* thisNa
{
// The field is not being set on an object. Maybe it's
// a special accessor?
setFieldComponent( prevObject, prevField, prevFieldArray, curField );
setFieldComponent( prevObject, prevField, prevFieldArray, curField, currentRegister );
prevObject = NULL;
}
break;

View file

@ -471,6 +471,17 @@ TEST(Script, Basic_SimObject)
ASSERT_STREQ(parentFn.getString(), "FooBar");
ConsoleValue simpleFieldTest = RunScript(R"(
function a()
{
FudgeCollector.field = "A";
return FudgeCollector.field;
}
return a();
)");
ASSERT_STREQ(simpleFieldTest.getString(), "A");
ConsoleValue grp = RunScript(R"(
new SimGroup(FudgeCollectorGroup)
{
@ -587,4 +598,54 @@ TEST(Script, Basic_Package)
ASSERT_EQ(deactivatedValue.getInt(), 3);
}
TEST(Script, Sugar_Syntax)
{
ConsoleValue value = RunScript(R"(
function a()
{
%vector = "1 2 3";
return %vector.y;
}
return a();
)");
ASSERT_EQ(value.getInt(), 2);
ConsoleValue setValue = RunScript(R"(
function a()
{
%vector = "1 2 3";
%vector.y = 4;
return %vector.y;
}
return a();
)");
ASSERT_EQ(setValue.getInt(), 4);
ConsoleValue valueArray = RunScript(R"(
function a()
{
%vector[0] = "1 2 3";
return %vector[0].y;
}
return a();
)");
ASSERT_EQ(valueArray.getInt(), 2);
ConsoleValue valueSetArray = RunScript(R"(
function a()
{
%vector[0] = "1 2 3";
%vector[0].z = 5;
return %vector[0].z;
}
return a();
)");
ASSERT_EQ(valueSetArray.getInt(), 5);
}
#endif