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_SETCUROBJECT);
codeStream.emit(OP_SETCURFIELD); codeStream.emit(OP_SETCURFIELD);
codeStream.emitSTE(slotName); codeStream.emitSTE(slotName);
codeStream.emit(OP_POP_STK);
if (arrayExpr) if (arrayExpr)
{ {
codeStream.emit(OP_SETCURFIELD_ARRAY); 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(OP_SETCUROBJECT_INTERNAL);
codeStream.emit(recurse); codeStream.emit(recurse);
codeStream.emit(OP_POP_STK);
return codeStream.tell(); return codeStream.tell();
} }
@ -1319,34 +1322,6 @@ TypeReq InternalSlotAccessNode::getPreferredType()
U32 SlotAssignNode::compile(CodeStream& codeStream, U32 ip, TypeReq type) 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); precompileIdent(slotName);
ip = valueExpr->compile(codeStream, ip, TypeReqString); ip = valueExpr->compile(codeStream, ip, TypeReqString);
@ -1364,6 +1339,13 @@ U32 SlotAssignNode::compile(CodeStream& codeStream, U32 ip, TypeReq type)
codeStream.emit(OP_SETCURFIELD); codeStream.emit(OP_SETCURFIELD);
codeStream.emitSTE(slotName); 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) if (arrayExpr)
{ {
codeStream.emit(OP_SETCURFIELD_ARRAY); codeStream.emit(OP_SETCURFIELD_ARRAY);
@ -1429,6 +1411,8 @@ U32 SlotAssignOpNode::compile(CodeStream& codeStream, U32 ip, TypeReq type)
codeStream.emit(OP_SETCURFIELD); codeStream.emit(OP_SETCURFIELD);
codeStream.emitSTE(slotName); codeStream.emitSTE(slotName);
codeStream.emit(OP_POP_STK);
if (arrayExpr) if (arrayExpr)
{ {
codeStream.emit(OP_SETCURFIELD_ARRAY); codeStream.emit(OP_SETCURFIELD_ARRAY);

View file

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

View file

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

View file

@ -471,6 +471,17 @@ TEST(Script, Basic_SimObject)
ASSERT_STREQ(parentFn.getString(), "FooBar"); 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"( ConsoleValue grp = RunScript(R"(
new SimGroup(FudgeCollectorGroup) new SimGroup(FudgeCollectorGroup)
{ {
@ -587,4 +598,54 @@ TEST(Script, Basic_Package)
ASSERT_EQ(deactivatedValue.getInt(), 3); 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 #endif