diff --git a/Engine/source/console/astNodes.cpp b/Engine/source/console/astNodes.cpp index 104671b1f..c95a45cae 100644 --- a/Engine/source/console/astNodes.cpp +++ b/Engine/source/console/astNodes.cpp @@ -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); diff --git a/Engine/source/console/codeBlock.cpp b/Engine/source/console/codeBlock.cpp index b58156c4a..8d0cde4d5 100644 --- a/Engine/source/console/codeBlock.cpp +++ b/Engine/source/console/codeBlock.cpp @@ -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; } diff --git a/Engine/source/console/compiledEval.cpp b/Engine/source/console/compiledEval.cpp index b80530162..f3c432fcb 100644 --- a/Engine/source/console/compiledEval.cpp +++ b/Engine/source/console/compiledEval.cpp @@ -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; diff --git a/Engine/source/console/test/ScriptTest.cpp b/Engine/source/console/test/ScriptTest.cpp index 58441c3d2..28d291528 100644 --- a/Engine/source/console/test/ScriptTest.cpp +++ b/Engine/source/console/test/ScriptTest.cpp @@ -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