From bc9c799d9409bd395eef1b9d8855fe348bd3c78f Mon Sep 17 00:00:00 2001 From: Daniel Gibson Date: Sun, 6 Sep 2020 04:56:53 +0200 Subject: [PATCH] Fix "t->c->value.argSize == func->parmTotal" Assertion in Scripts, #303 If a "class" (object) in a Script has multiple member function prototypes, and one function implementation later calls another before that is implemented, there was an assertion when the script was parsed (at map start), because the size of function arguments at the call-site didn't match what the function expected - because the function hadn't calculated that size yet, that only happened once its own implementation was parsed. Now it's calculated (and stored) when the prototype/declaration is parsed to prevent this issue, which seems to be kinda common with Mods, esp. Script-only mods, as the release game DLLs had Assertions disabled. --- d3xp/script/Script_Compiler.cpp | 55 ++++++++++++++++++++------------- game/script/Script_Compiler.cpp | 55 ++++++++++++++++++++------------- 2 files changed, 68 insertions(+), 42 deletions(-) diff --git a/d3xp/script/Script_Compiler.cpp b/d3xp/script/Script_Compiler.cpp index 7620c60..6fac0a6 100644 --- a/d3xp/script/Script_Compiler.cpp +++ b/d3xp/script/Script_Compiler.cpp @@ -2144,33 +2144,46 @@ void idCompiler::ParseFunctionDef( idTypeDef *returnType, const char *name ) { } } + // DG: make sure parmSize gets calculated when parsing prototype (not just when parsing + // implementation) so calling this function/method before implementation has been parsed + // works without getting Assertions in IdInterpreter::Execute() and ::LeaveFunction() + // ("st->c->value.argSize == func->parmTotal", "localstackUsed == localstackBase", see #303) + + // calculate stack space used by parms + numParms = type->NumParameters(); + if( numParms != func->parmSize.Num() ) { // DG: make sure not to do this twice + + // if it hasn't been parsed yet, parmSize.Num() should be 0.. + assert( func->parmSize.Num() == 0 && "function had different number of arguments before?!" ); + + func->parmSize.SetNum( numParms ); + for( i = 0; i < numParms; i++ ) { + parmType = type->GetParmType( i ); + if ( parmType->Inherits( &type_object ) ) { + func->parmSize[ i ] = type_object.Size(); + } else { + func->parmSize[ i ] = parmType->Size(); + } + func->parmTotal += func->parmSize[ i ]; + } + + // define the parms + for( i = 0; i < numParms; i++ ) { + if ( gameLocal.program.GetDef( type->GetParmType( i ), type->GetParmName( i ), def ) ) { + Error( "'%s' defined more than once in function parameters", type->GetParmName( i ) ); + } + gameLocal.program.AllocDef( type->GetParmType( i ), type->GetParmName( i ), def, false ); + } + } + + // DG: moved this down here so parmSize also gets calculated when parsing prototype // check if this is a prototype or declaration if ( !CheckToken( "{" ) ) { // it's just a prototype, so get the ; and move on ExpectToken( ";" ); return; } - - // calculate stack space used by parms - numParms = type->NumParameters(); - func->parmSize.SetNum( numParms ); - for( i = 0; i < numParms; i++ ) { - parmType = type->GetParmType( i ); - if ( parmType->Inherits( &type_object ) ) { - func->parmSize[ i ] = type_object.Size(); - } else { - func->parmSize[ i ] = parmType->Size(); - } - func->parmTotal += func->parmSize[ i ]; - } - - // define the parms - for( i = 0; i < numParms; i++ ) { - if ( gameLocal.program.GetDef( type->GetParmType( i ), type->GetParmName( i ), def ) ) { - Error( "'%s' defined more than once in function parameters", type->GetParmName( i ) ); - } - gameLocal.program.AllocDef( type->GetParmType( i ), type->GetParmName( i ), def, false ); - } + // DG end oldscope = scope; scope = def; diff --git a/game/script/Script_Compiler.cpp b/game/script/Script_Compiler.cpp index 1757233..1ad2693 100644 --- a/game/script/Script_Compiler.cpp +++ b/game/script/Script_Compiler.cpp @@ -2144,33 +2144,46 @@ void idCompiler::ParseFunctionDef( idTypeDef *returnType, const char *name ) { } } + // DG: make sure parmSize gets calculated when parsing prototype (not just when parsing + // implementation) so calling this function/method before implementation has been parsed + // works without getting Assertions in IdInterpreter::Execute() and ::LeaveFunction() + // ("st->c->value.argSize == func->parmTotal", "localstackUsed == localstackBase", see #303) + + // calculate stack space used by parms + numParms = type->NumParameters(); + if( numParms != func->parmSize.Num() ) { // DG: make sure not to do this twice + + // if it hasn't been parsed yet, parmSize.Num() should be 0.. + assert( func->parmSize.Num() == 0 && "function had different number of arguments before?!" ); + + func->parmSize.SetNum( numParms ); + for( i = 0; i < numParms; i++ ) { + parmType = type->GetParmType( i ); + if ( parmType->Inherits( &type_object ) ) { + func->parmSize[ i ] = type_object.Size(); + } else { + func->parmSize[ i ] = parmType->Size(); + } + func->parmTotal += func->parmSize[ i ]; + } + + // define the parms + for( i = 0; i < numParms; i++ ) { + if ( gameLocal.program.GetDef( type->GetParmType( i ), type->GetParmName( i ), def ) ) { + Error( "'%s' defined more than once in function parameters", type->GetParmName( i ) ); + } + gameLocal.program.AllocDef( type->GetParmType( i ), type->GetParmName( i ), def, false ); + } + } + + // DG: moved this down here so parmSize also gets calculated when parsing prototype // check if this is a prototype or declaration if ( !CheckToken( "{" ) ) { // it's just a prototype, so get the ; and move on ExpectToken( ";" ); return; } - - // calculate stack space used by parms - numParms = type->NumParameters(); - func->parmSize.SetNum( numParms ); - for( i = 0; i < numParms; i++ ) { - parmType = type->GetParmType( i ); - if ( parmType->Inherits( &type_object ) ) { - func->parmSize[ i ] = type_object.Size(); - } else { - func->parmSize[ i ] = parmType->Size(); - } - func->parmTotal += func->parmSize[ i ]; - } - - // define the parms - for( i = 0; i < numParms; i++ ) { - if ( gameLocal.program.GetDef( type->GetParmType( i ), type->GetParmName( i ), def ) ) { - Error( "'%s' defined more than once in function parameters", type->GetParmName( i ) ); - } - gameLocal.program.AllocDef( type->GetParmType( i ), type->GetParmName( i ), def, false ); - } + // DG end oldscope = scope; scope = def;