Skip to content

Commit

Permalink
Imply * in import stmt iff SCOPT_NOAUTOPTRIMPORT is false
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
fernewelten committed Aug 25, 2024
1 parent 2c58c4c commit babdd85
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 136 deletions.
3 changes: 2 additions & 1 deletion Common/script/cc_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
28 changes: 7 additions & 21 deletions Compiler/script2/cs_parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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(...)"
Expand Down
113 changes: 1 addition & 112 deletions Compiler/test2/cc_bytecode_test_0.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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());
Expand Down
72 changes: 70 additions & 2 deletions Compiler/test2/cc_bytecode_test_1.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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());

Expand Down Expand Up @@ -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

Expand All @@ -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());
Expand Down Expand Up @@ -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 = "\
Expand Down
1 change: 1 addition & 0 deletions Compiler/test2/cc_parser_test_0.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down

0 comments on commit babdd85

Please sign in to comment.