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.
This commit is contained in:
Daniel Gibson 2020-09-06 04:56:53 +02:00
parent 6401071bc1
commit bc9c799d94
2 changed files with 68 additions and 42 deletions

View file

@ -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 // check if this is a prototype or declaration
if ( !CheckToken( "{" ) ) { if ( !CheckToken( "{" ) ) {
// it's just a prototype, so get the ; and move on // it's just a prototype, so get the ; and move on
ExpectToken( ";" ); ExpectToken( ";" );
return; return;
} }
// DG end
// 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 );
}
oldscope = scope; oldscope = scope;
scope = def; scope = def;

View file

@ -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 // check if this is a prototype or declaration
if ( !CheckToken( "{" ) ) { if ( !CheckToken( "{" ) ) {
// it's just a prototype, so get the ; and move on // it's just a prototype, so get the ; and move on
ExpectToken( ";" ); ExpectToken( ";" );
return; return;
} }
// DG end
// 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 );
}
oldscope = scope; oldscope = scope;
scope = def; scope = def;