Skip to content

Commit 1d11f4b

Browse files
authored
fix: Fix value confusion in recursive template literals (#2070)
1 parent c56a7f5 commit 1d11f4b

File tree

4 files changed

+992
-435
lines changed

4 files changed

+992
-435
lines changed

src/compiler.ts

Lines changed: 58 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -8136,7 +8136,8 @@ export class Compiler extends DiagnosticEmitter {
81368136
var parts = expression.parts;
81378137
var numParts = parts.length;
81388138
var expressions = expression.expressions;
8139-
assert(numParts - 1 == expressions.length);
8139+
var numExpressions = expressions.length;
8140+
assert(numExpressions == numParts - 1);
81408141

81418142
var module = this.module;
81428143
var stringInstance = this.program.stringInstance;
@@ -8202,8 +8203,8 @@ export class Compiler extends DiagnosticEmitter {
82028203
return this.makeCallDirect(concatMethod, [ lhs, rhs ], expression);
82038204
}
82048205

8205-
// Compile to a `StaticArray<string>#join("") for general case
8206-
let length = 2 * numParts - 1;
8206+
// Compile to a `StaticArray<string>#join("") in the general case
8207+
let length = numParts + numExpressions;
82078208
let values = new Array<usize>(length);
82088209
values[0] = this.ensureStaticString(parts[0]);
82098210
for (let i = 1; i < numParts; ++i) {
@@ -8215,19 +8216,33 @@ export class Compiler extends DiagnosticEmitter {
82158216
let offset = i64_add(segment.offset, i64_new(this.program.totalOverhead));
82168217
let joinInstance = assert(arrayInstance.getMethod("join"));
82178218
let indexedSetInstance = assert(arrayInstance.lookupOverload(OperatorKind.INDEXED_SET, true));
8218-
let stmts = new Array<ExpressionRef>(numParts);
8219-
for (let i = 0, k = numParts - 1; i < k; ++i) {
8219+
let stmts = new Array<ExpressionRef>(2 * numExpressions + 1);
8220+
// Use one local per toString'ed subexpression, since otherwise recursion on the same
8221+
// static array would overwrite already prepared parts. Avoids a temporary array.
8222+
let temps = new Array<Local>(numExpressions);
8223+
let flow = this.currentFlow;
8224+
for (let i = 0; i < numExpressions; ++i) {
82208225
let expression = expressions[i];
8221-
stmts[i] = this.makeCallDirect(indexedSetInstance, [
8222-
module.usize(offset),
8223-
module.i32(2 * i + 1),
8226+
let temp = flow.getTempLocal(stringType);
8227+
temps[i] = temp;
8228+
stmts[i] = module.local_set(temp.index,
82248229
this.makeToString(
82258230
this.compileExpression(expression, stringType),
82268231
this.currentType, expression
8227-
)
8232+
),
8233+
true
8234+
);
8235+
}
8236+
// Populate the static array with the toString'ed subexpressions and call .join("")
8237+
for (let i = 0; i < numExpressions; ++i) {
8238+
stmts[numExpressions + i] = this.makeCallDirect(indexedSetInstance, [
8239+
module.usize(offset),
8240+
module.i32(2 * i + 1),
8241+
module.local_get(temps[i].index, stringType.toRef())
82288242
], expression);
8243+
flow.freeTempLocal(temps[i]);
82298244
}
8230-
stmts[numParts - 1] = this.makeCallDirect(joinInstance, [
8245+
stmts[2 * numExpressions] = this.makeCallDirect(joinInstance, [
82318246
module.usize(offset),
82328247
this.ensureStaticString("")
82338248
], expression);
@@ -8385,19 +8400,7 @@ export class Compiler extends DiagnosticEmitter {
83858400

83868401
// otherwise allocate a new array header and make it wrap a copy of the static buffer
83878402
} else {
8388-
// __newArray(length, alignLog2, classId, staticBuffer)
8389-
let expr = this.makeCallDirect(program.newArrayInstance, [
8390-
module.i32(length),
8391-
program.options.isWasm64
8392-
? module.i64(elementType.alignLog2)
8393-
: module.i32(elementType.alignLog2),
8394-
module.i32(arrayInstance.id),
8395-
program.options.isWasm64
8396-
? module.i64(i64_low(bufferAddress), i64_high(bufferAddress))
8397-
: module.i32(i64_low(bufferAddress))
8398-
], expression);
8399-
this.currentType = arrayType;
8400-
return expr;
8403+
return this.makeNewArray(arrayInstance, length, bufferAddress, expression);
84018404
}
84028405
}
84038406

@@ -8419,16 +8422,7 @@ export class Compiler extends DiagnosticEmitter {
84198422
// tempThis = __newArray(length, alignLog2, classId, source = 0)
84208423
stmts.push(
84218424
module.local_set(tempThis.index,
8422-
this.makeCallDirect(program.newArrayInstance, [
8423-
module.i32(length),
8424-
program.options.isWasm64
8425-
? module.i64(elementType.alignLog2)
8426-
: module.i32(elementType.alignLog2),
8427-
module.i32(arrayInstance.id),
8428-
program.options.isWasm64
8429-
? module.i64(0)
8430-
: module.i32(0)
8431-
], expression),
8425+
this.makeNewArray(arrayInstance, length, i64_new(0), expression),
84328426
arrayType.isManaged
84338427
)
84348428
);
@@ -8466,6 +8460,37 @@ export class Compiler extends DiagnosticEmitter {
84668460
return module.flatten(stmts, arrayTypeRef);
84678461
}
84688462

8463+
/** Makes a new array instance from a static buffer segment. */
8464+
private makeNewArray(
8465+
/** Concrete array class. */
8466+
arrayInstance: Class,
8467+
/** Length of the array. */
8468+
length: i32,
8469+
/** Source address to copy from. Array is zeroed if `0`. */
8470+
source: i64,
8471+
/** Report node. */
8472+
reportNode: Node
8473+
): ExpressionRef {
8474+
var program = this.program;
8475+
var module = this.module;
8476+
assert(!arrayInstance.extends(program.staticArrayPrototype));
8477+
var elementType = arrayInstance.getArrayValueType(); // asserts
8478+
8479+
// __newArray(length, alignLog2, classId, staticBuffer)
8480+
var expr = this.makeCallDirect(program.newArrayInstance, [
8481+
module.i32(length),
8482+
program.options.isWasm64
8483+
? module.i64(elementType.alignLog2)
8484+
: module.i32(elementType.alignLog2),
8485+
module.i32(arrayInstance.id),
8486+
program.options.isWasm64
8487+
? module.i64(i64_low(source), i64_high(source))
8488+
: module.i32(i64_low(source))
8489+
], reportNode);
8490+
this.currentType = arrayInstance.type;
8491+
return expr;
8492+
}
8493+
84698494
/** Compiles a special `fixed` array literal. */
84708495
private compileStaticArrayLiteral(
84718496
expression: ArrayLiteralExpression,

0 commit comments

Comments
 (0)