@@ -8230,39 +8230,55 @@ export class Compiler extends DiagnosticEmitter {
82308230 let arrayInstance = assert ( this . resolver . resolveClass ( this . program . staticArrayPrototype , [ stringType ] ) ) ;
82318231 let segment = this . addStaticBuffer ( stringType , values , arrayInstance . id ) ;
82328232 this . program . OBJECTInstance . writeField ( "gcInfo" , 3 , segment . buffer , 0 ) ; // use transparent gcinfo
8233- let offset = i64_add ( segment . offset , i64_new ( this . program . totalOverhead ) ) ;
8233+ let arrayOffset = i64_add ( segment . offset , i64_new ( this . program . totalOverhead ) ) ;
82348234 let joinInstance = assert ( arrayInstance . getMethod ( "join" ) ) ;
82358235 let indexedSetInstance = assert ( arrayInstance . lookupOverload ( OperatorKind . IndexedSet , true ) ) ;
8236- let stmts = new Array < ExpressionRef > ( 2 * numExpressions + 1 ) ;
8237- // Use one local per toString'ed subexpression, since otherwise recursion on the same
8238- // static array would overwrite already prepared parts. Avoids a temporary array.
8239- let temps = new Array < Local > ( numExpressions ) ;
82408236 let flow = this . currentFlow ;
8237+
8238+ // Store the static array address in a tmp local so the shadow stack
8239+ // keeps it reachable. GC will visit it and all dynamic children.
8240+ let arrayLocal = flow . getTempLocal ( arrayInstance . type ) ;
8241+ let stmts = new Array < ExpressionRef > ( ) ;
8242+ stmts . push ( module . local_set ( arrayLocal . index , module . usize ( arrayOffset ) , true ) ) ;
8243+
8244+ // Evaluate each expression into a temp local.
8245+ // One local per expression prevents recursion on the same static array
8246+ // from overwriting already prepared parts.
8247+ let temps = new Array < Local > ( numExpressions ) ;
82418248 for ( let i = 0 ; i < numExpressions ; ++ i ) {
8242- let expression = expressions [ i ] ;
8249+ let subExpression = expressions [ i ] ;
82438250 let temp = flow . getTempLocal ( stringType ) ;
82448251 temps [ i ] = temp ;
8245- stmts [ i ] = module . local_set ( temp . index ,
8246- this . makeToString (
8247- this . compileExpression ( expression , stringType ) ,
8248- this . currentType , expression
8249- ) ,
8250- true
8252+ stmts . push (
8253+ module . local_set (
8254+ temp . index ,
8255+ this . makeToString ( this . compileExpression ( subExpression , stringType ) , this . currentType , subExpression ) ,
8256+ true
8257+ )
82518258 ) ;
82528259 }
8253- // Populate the static array with the toString'ed subexpressions and call .join("")
8260+
8261+ // Populate the static array slots with the temp locals
82548262 for ( let i = 0 ; i < numExpressions ; ++ i ) {
8255- stmts [ numExpressions + i ] = this . makeCallDirect ( indexedSetInstance , [
8256- module . usize ( offset ) ,
8257- module . i32 ( expressionPositions [ i ] ) ,
8258- module . local_get ( temps [ i ] . index , stringType . toRef ( ) )
8259- ] , expression ) ;
8260- }
8261- stmts [ 2 * numExpressions ] = this . makeCallDirect ( joinInstance , [
8262- module . usize ( offset ) ,
8263- this . ensureStaticString ( "" )
8264- ] , expression ) ;
8265- return module . flatten ( stmts , stringType . toRef ( ) ) ;
8263+ stmts . push (
8264+ this . makeCallDirect (
8265+ indexedSetInstance ,
8266+ [
8267+ module . local_get ( arrayLocal . index , stringType . toRef ( ) ) ,
8268+ module . i32 ( expressionPositions [ i ] ) ,
8269+ module . local_get ( temps [ i ] . index , stringType . toRef ( ) ) ,
8270+ ] ,
8271+ expression
8272+ ) ) ;
8273+ }
8274+ // Call join("") and return the result
8275+ stmts . push (
8276+ this . makeCallDirect (
8277+ joinInstance ,
8278+ [ module . local_get ( arrayLocal . index , stringType . toRef ( ) ) , this . ensureStaticString ( "" ) ] ,
8279+ expression
8280+ ) ) ;
8281+ return module . block ( null , stmts , stringType . toRef ( ) ) ;
82668282 }
82678283
82688284 // Try to find out whether the template function takes a full-blown TemplateStringsArray or if
0 commit comments