From babdd85df3e62db3e75a916a55c4325749fec9b2 Mon Sep 17 00:00:00 2001 From: "Peter G Bouillon [fernewelten]" Date: Sun, 25 Aug 2024 10:40:42 +0200 Subject: [PATCH] Imply `*` in import stmt iff` SCOPT_NOAUTOPTRIMPORT` is `false` Introduce the compiler flag `SCOPT_NOAUTOPTRIMPORT` which is `false` by default. Normally, whenever AGS code declares an object variable then imply that it is pointered. However, in import declarations of non-autopointered varables only do so when the flag is `false`. In legacy AGS, the engine autogenerates code such as `import Object object[20];` where `object` is meant to be an array of `Object` and not an array of `Object *`. In this legacy situation, the flag should be set so that the compiler generates the correct code for this. --- Common/script/cc_common.h | 3 +- Compiler/script2/cs_parser.cpp | 28 ++----- Compiler/test2/cc_bytecode_test_0.cpp | 113 +------------------------- Compiler/test2/cc_bytecode_test_1.cpp | 72 +++++++++++++++- Compiler/test2/cc_parser_test_0.cpp | 1 + 5 files changed, 81 insertions(+), 136 deletions(-) diff --git a/Common/script/cc_common.h b/Common/script/cc_common.h index 0b85fb1f374..ffaaef7ba9a 100644 --- a/Common/script/cc_common.h +++ b/Common/script/cc_common.h @@ -33,7 +33,8 @@ #define SCOPT_RTTI 0x0200 // generate and export RTTI #define SCOPT_RTTIOPS 0x0400 // enable syntax & opcodes that require RTTI to work #define SCOPT_SCRIPT_TOC 0x0800 // generate and export ScriptTOC -#define SCOPT_HIGHEST SCOPT_SCRIPT_TOC +#define SCOPT_NOAUTOPTRIMPORT 0x1000 // object pointers in imports must be declared explicitly +#define SCOPT_HIGHEST SCOPT_NOAUTOPTRIMPORT extern void ccSetOption(int, int); extern int ccGetOption(int); diff --git a/Compiler/script2/cs_parser.cpp b/Compiler/script2/cs_parser.cpp index a4a4e19507f..6ae573d7224 100644 --- a/Compiler/script2/cs_parser.cpp +++ b/Compiler/script2/cs_parser.cpp @@ -5719,32 +5719,18 @@ void AGS::Parser::ParseVartypeClause(TypeQualifierSet tqs, Symbol &struct_of_cur _src.BackUp(); Vartype vartype = ParseVartype(false); - // Imply a pointer for managed vartypes. However, do NOT do this in 'import' statements. - // (Reason: automatically generated code does things like "import Object oFoo;" and - // then it really does mean "Object", not "Object *". - // This can only happen in automatically generated code: - // Users are never allowed to define unpointered managed entities. - if (kKW_Dynpointer == _src.PeekNext() || - _sym.IsAutoptrVartype(vartype) || - (ScT::kImport != scope_type && _sym.IsManagedVartype(vartype))) - { - vartype = _sym.VartypeWithDynpointer(vartype); - } - EatDynpointerSymbolIfPresent(vartype); - - // Imply a pointer for managed vartypes. However, do NOT - // do this in import statements. (Reason: automatically - // generated code does things like "import Object oFoo;" and - // then it really does mean "Object", not "Object *". - // This can only happen in automatically generated code: - // Users are never allowed to define unpointered managed entities. + // A pointer symbol is generally implied for managed vartypes + bool managed_vartype_has_implied_pointer = _sym.IsManagedVartype(vartype); + // However, when the option is set then don't do this in import statements + if (ScT::kImport == scope_type && FlagIsSet(_options, SCOPT_NOAUTOPTRIMPORT)) + managed_vartype_has_implied_pointer = false; + if (kKW_Dynpointer == _src.PeekNext() || _sym.IsAutoptrVartype(vartype) || - (ScT::kImport != scope_type && _sym.IsManagedVartype(vartype))) + managed_vartype_has_implied_pointer) { vartype = _sym.VartypeWithDynpointer(vartype); } - EatDynpointerSymbolIfPresent(vartype); // "int [] func(...)" diff --git a/Compiler/test2/cc_bytecode_test_0.cpp b/Compiler/test2/cc_bytecode_test_0.cpp index f7b8d6c8159..7a1f25a5e00 100644 --- a/Compiler/test2/cc_bytecode_test_0.cpp +++ b/Compiler/test2/cc_bytecode_test_0.cpp @@ -2685,118 +2685,6 @@ TEST_F(Bytecode0, Struct08) { EXPECT_EQ(stringssize, scrip.strings.size()); } -TEST_F(Bytecode0, Struct09_NoRTTI) { - - // Should be able to find SetCharacter as a component of - // VehicleBase as an extension of Vehicle Cars[5]; - // should generate call of VehicleBase::SetCharacter() - - char const *inpl = "\ - enum CharacterDirection \n\ - { \n\ - eDirectionUp = 3 \n\ - }; \n\ - \n\ - builtin managed struct Character \n\ - { \n\ - readonly import attribute int ID; \n\ - }; \n\ - import Character character[7]; \n\ - import Character cAICar1; \n\ - \n\ - struct VehicleBase \n\ - { \n\ - import void SetCharacter(Character *c, \n\ - int carSprite, \n\ - CharacterDirection carSpriteDir, \n\ - int view = 0, \n\ - int loop = 0, \n\ - int frame = 0); \n\ - }; \n\ - \n\ - struct Vehicle extends VehicleBase \n\ - { \n\ - float bodyMass; \n\ - }; \n\ - import Vehicle Cars[6]; \n\ - \n\ - int main() \n\ - { \n\ - int drivers[] = new int[6]; \n\ - int i = 5; \n\ - Cars[i].SetCharacter( \n\ - character[cAICar1.ID + i], \n\ - 7 + drivers[i], \n\ - eDirectionUp, \n\ - 3 + i, 0, 0); \n\ - } \n\ - "; - - ccSetOption(SCOPT_RTTIOPS, false); - - int compileResult = cc_compile(inpl, scrip); - ASSERT_STREQ("Ok", (compileResult >= 0) ? "Ok" : last_seen_cc_error()); - - // WriteOutput("Struct09_NoRtti", scrip); - - size_t const codesize = 205; - EXPECT_EQ(codesize, scrip.code.size()); - - int32_t code[] = { - 36, 30, 38, 0, 36, 31, 6, 3, // 7 - 6, 72, 3, 4, 0, 51, 0, 47, // 15 - 3, 1, 1, 4, 36, 32, 6, 3, // 23 - 5, 29, 3, 36, 33, 51, 4, 7, // 31 - 3, 46, 3, 6, 32, 3, 4, 6, // 39 - 2, 4, 11, 2, 3, 29, 2, 36, // 47 - 37, 6, 3, 0, 34, 3, 6, 3, // 55 - 0, 34, 3, 6, 3, 3, 29, 3, // 63 - 51, 12, 7, 3, 30, 4, 11, 4, // 71 - 3, 3, 4, 3, 34, 3, 36, 36, // 79 - 6, 3, 3, 34, 3, 36, 35, 6, // 87 - 3, 7, 29, 3, 51, 16, 48, 2, // 95 - 52, 29, 2, 51, 16, 7, 3, 30, // 103 - 2, 32, 3, 4, 71, 3, 11, 2, // 111 - 3, 7, 3, 30, 4, 11, 4, 3, // 119 - 3, 4, 3, 34, 3, 36, 34, 6, // 127 - 2, 2, 29, 6, 45, 2, 39, 0, // 135 - 6, 3, 0, 33, 3, 30, 6, 29, // 143 - 3, 51, 12, 7, 3, 30, 4, 11, // 151 - 4, 3, 3, 4, 3, 46, 3, 7, // 159 - 32, 3, 0, 6, 2, 1, 11, 2, // 167 - 3, 3, 2, 3, 34, 3, 36, 37, // 175 - 51, 4, 7, 2, 45, 2, 39, 6, // 183 - 6, 3, 3, 33, 3, 35, 6, 30, // 191 - 2, 36, 38, 51, 8, 49, 2, 1, // 199 - 8, 6, 3, 0, 5, -999 - }; - CompareCode(&scrip, codesize, code); - - size_t const numfixups = 5; - EXPECT_EQ(numfixups, scrip.fixups.size()); - - int32_t fixups[] = { - 41, 129, 138, 165, 186, -999 - }; - char fixuptypes[] = { - 4, 4, 4, 4, 4, '\0' - }; - CompareFixups(&scrip, numfixups, fixups, fixuptypes); - - int const numimports = 5; - std::string imports[] = { - "Character::get_ID^0", "character", "cAICar1", "VehicleBase::SetCharacter^6", // 3 - "Cars", "[[SENTINEL]]" - }; - CompareImports(&scrip, numimports, imports); - - size_t const numexports = 0; - EXPECT_EQ(numexports, scrip.exports.size()); - - size_t const stringssize = 0; - EXPECT_EQ(stringssize, scrip.strings.size()); -} - TEST_F(Bytecode0, Struct09_RTTI) { // Should be able to find SetCharacter as a component of @@ -2845,6 +2733,7 @@ TEST_F(Bytecode0, Struct09_RTTI) { "; ccSetOption(SCOPT_RTTIOPS, true); + ccSetOption(SCOPT_NOAUTOPTRIMPORT, true); int compileResult = cc_compile(inpl, scrip); ASSERT_STREQ("Ok", (compileResult >= 0) ? "Ok" : last_seen_cc_error()); diff --git a/Compiler/test2/cc_bytecode_test_1.cpp b/Compiler/test2/cc_bytecode_test_1.cpp index 21896d9cf93..fdc19dc5683 100644 --- a/Compiler/test2/cc_bytecode_test_1.cpp +++ b/Compiler/test2/cc_bytecode_test_1.cpp @@ -638,6 +638,7 @@ TEST_F(Bytecode1, AccessStructAsPointer01) { } \n\ "; + ccSetOption(SCOPT_NOAUTOPTRIMPORT, true); int compileResult = cc_compile(inpl, scrip); ASSERT_STREQ("Ok", (compileResult >= 0) ? "Ok" : last_seen_cc_error()); @@ -1397,7 +1398,7 @@ TEST_F(Bytecode1, Attributes06) { EXPECT_EQ(stringssize, scrip.strings.size()); } -TEST_F(Bytecode1, Attributes07) { +TEST_F(Bytecode1, Attributes07a) { // Assignment to attribute -- should not generate null dereference error @@ -1415,11 +1416,12 @@ TEST_F(Bytecode1, Attributes07) { } \n\ "; + ccSetOption(SCOPT_NOAUTOPTRIMPORT, true); int compileResult = cc_compile(input, scrip); ASSERT_STREQ("Ok", (compileResult >= 0) ? "Ok" : last_seen_cc_error()); - // WriteOutput("Attributes07", scrip); + // WriteOutput("Attributes07a", scrip); size_t const codesize = 34; EXPECT_EQ(codesize, scrip.code.size()); @@ -1462,6 +1464,72 @@ TEST_F(Bytecode1, Attributes07) { CompareStrings(&scrip, stringssize, strings); } +TEST_F(Bytecode1, Attributes07b) { + + // Assignment to attribute -- should not generate null dereference error + // Assume that builtin managed objects are not autopointered. + + std::string input = g_Input_Bool; + input += g_Input_String; + input += "\ + builtin managed struct Label { \n\ + attribute String Text; \n\ + }; \n\ + import Label lbl; \n\ + \n\ + void main() \n\ + { \n\ + lbl.Text = \"\"; \n\ + } \n\ + "; + + ccSetOption(SCOPT_NOAUTOPTRIMPORT, false); + int compileResult = cc_compile(input, scrip); + + ASSERT_STREQ("Ok", (compileResult >= 0) ? "Ok" : last_seen_cc_error()); + + // WriteOutput("Attributes07b", scrip); + size_t const codesize = 37; + EXPECT_EQ(codesize, scrip.code.size()); + + int32_t code[] = { + 36, 7, 38, 0, 36, 8, 6, 3, // 7 + 0, 6, 2, 22, 48, 2, 52, 64, // 15 + 3, 29, 6, 34, 3, 45, 2, 39, // 23 + 1, 6, 3, 21, 33, 3, 35, 1, // 31 + 30, 6, 36, 9, 5, -999 + }; + CompareCode(&scrip, codesize, code); + + size_t const numfixups = 3; + EXPECT_EQ(numfixups, scrip.fixups.size()); + + int32_t fixups[] = { + 8, 11, 27, -999 + }; + char fixuptypes[] = { + 3, 4, 4, '\0' + }; + CompareFixups(&scrip, numfixups, fixups, fixuptypes); + + int const numimports = 2; + std::string imports[] = { + "Label::set_Text^1", "lbl", "[[SENTINEL]]" + }; + CompareImports(&scrip, numimports, imports); + + size_t const numexports = 0; + EXPECT_EQ(numexports, scrip.exports.size()); + + size_t const stringssize = 1; + EXPECT_EQ(stringssize, scrip.strings.size()); + + char strings[] = { + 0, '\0' + }; + CompareStrings(&scrip, stringssize, strings); +} + TEST_F(Bytecode1, Attributes08) { char const *inpl = "\ diff --git a/Compiler/test2/cc_parser_test_0.cpp b/Compiler/test2/cc_parser_test_0.cpp index 19e0a2663db..39bc7c87f8c 100644 --- a/Compiler/test2/cc_parser_test_0.cpp +++ b/Compiler/test2/cc_parser_test_0.cpp @@ -37,6 +37,7 @@ int cc_compile(std::string const &inpl, AGS::ccCompiledScript &scrip) (0 != ccGetOption(SCOPT_NOIMPORTOVERRIDE)) * SCOPT_NOIMPORTOVERRIDE | (0 != ccGetOption(SCOPT_OLDSTRINGS)) * SCOPT_OLDSTRINGS | (0 != ccGetOption(SCOPT_RTTIOPS)) * SCOPT_RTTIOPS | + (0 != ccGetOption(SCOPT_NOAUTOPTRIMPORT)) * SCOPT_NOAUTOPTRIMPORT | false; int const error_code = cc_compile(inpl, options, scrip, mh);