Skip to content

Commit 6e854f1

Browse files
NullVoxPopuliclaude
andcommitted
Make the scope bag an object instead of an array
Now that the scope bag is a `Record<string, unknown>`, the lexical symbol names are always carried alongside their values. This eliminates the need for the `lexicalSymbols` slot in the wire format block (previously only available when `debugSymbols` was enabled) and allows `meta()` to derive `lexical` names and `scopeValues` directly from the scope object's keys and values. See also: upstream PR emberjs#21224, PR emberjs#21068. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
1 parent 54835c6 commit 6e854f1

7 files changed

Lines changed: 25 additions & 21 deletions

File tree

packages/@glimmer-workspace/integration-tests/lib/compile.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,16 @@ export function createTemplate(
2424
): TemplateFactory {
2525
options.locals = options.locals ?? Object.keys(scopeValues ?? {});
2626
let [block, usedLocals] = precompileJSON(templateSource, options);
27-
let reifiedScopeValues = usedLocals.map((key) => scopeValues[key]);
28-
29-
if ('emit' in options && options.emit?.debugSymbols) {
30-
block.push(usedLocals);
31-
}
27+
let reifiedScope: Record<string, unknown> | null =
28+
usedLocals.length > 0
29+
? Object.fromEntries(usedLocals.map((key) => [key, scopeValues[key]]))
30+
: null;
3231

3332
let templateBlock: SerializedTemplateWithLazyBlock = {
3433
id: String(templateId++),
3534
block: JSON.stringify(block),
3635
moduleName: options.meta?.moduleName ?? '(unknown template module)',
37-
scope: reifiedScopeValues.length > 0 ? () => reifiedScopeValues : null,
36+
scope: reifiedScope ? () => reifiedScope! : null,
3837
isStrictMode: options.strictMode ?? false,
3938
};
4039

packages/@glimmer-workspace/integration-tests/test/compiler/compile-options-test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ module('[glimmer-compiler] precompile', ({ test }) => {
5757
...WireFormat.Statement[],
5858
];
5959

60-
assert.deepEqual(wire.scope?.(), [hello]);
60+
assert.deepEqual(wire.scope?.(), { hello });
6161

6262
assert.deepEqual(
6363
componentNameExpr,
@@ -84,7 +84,7 @@ module('[glimmer-compiler] precompile', ({ test }) => {
8484
...WireFormat.Statement[],
8585
];
8686

87-
assert.deepEqual(wire.scope?.(), [f]);
87+
assert.deepEqual(wire.scope?.(), { f });
8888
assert.deepEqual(
8989
componentNameExpr,
9090
[SexpOpcodes.GetLexicalSymbol, 0, ['hello']],
@@ -218,7 +218,7 @@ module('[glimmer-compiler] precompile', ({ test }) => {
218218
_wire = compile(`{{this.message}}`, ['this'], (source) => eval(source));
219219
}).call(target);
220220
let wire = _wire!;
221-
assert.deepEqual(wire.scope?.(), [target]);
221+
assert.deepEqual(wire.scope?.(), { 'this': target });
222222
assert.deepEqual(wire.block[0], [
223223
[SexpOpcodes.Append, [SexpOpcodes.GetLexicalSymbol, 0, ['message']]],
224224
]);

packages/@glimmer/compiler/lib/compiler.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -123,9 +123,8 @@ export function precompile(
123123
): TemplateJavascript {
124124
const [block, usedLocals] = precompileJSON(source, options);
125125

126-
if ('emit' in options && options.emit?.debugSymbols && usedLocals.length > 0) {
127-
block.push(usedLocals);
128-
}
126+
// lexical symbol names are now carried by the scope object itself (as keys),
127+
// so we no longer need to push them into the wire format block for debug symbols.
129128

130129
const moduleName = options.meta?.moduleName;
131130
const idFn = options.id || defaultId;
@@ -148,7 +147,10 @@ export function precompile(
148147
let stringified = JSON.stringify(templateJSONObject);
149148

150149
if (usedLocals.length > 0) {
151-
const scopeFn = `()=>[${usedLocals.join(',')}]`;
150+
const scopeEntries = usedLocals.map((l) =>
151+
l === 'this' ? '"this":this' : l
152+
);
153+
const scopeFn = `()=>({${scopeEntries.join(',')}})`;
152154

153155
stringified = stringified.replace(`"${SCOPE_PLACEHOLDER}"`, scopeFn);
154156
}

packages/@glimmer/interfaces/lib/compile/wire-format/api.d.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -373,7 +373,6 @@ export type SerializedTemplateBlock = [
373373
statements: Statements.Statement[],
374374
locals: string[],
375375
upvars: string[],
376-
lexicalSymbols?: string[],
377376
];
378377

379378
/**
@@ -397,7 +396,7 @@ export interface SerializedTemplateWithLazyBlock {
397396
id?: Nullable<string>;
398397
block: SerializedTemplateBlockJSON;
399398
moduleName: string;
400-
scope?: (() => unknown[]) | undefined | null;
399+
scope?: (() => Record<string, unknown>) | undefined | null;
401400
isStrictMode: boolean;
402401
}
403402

packages/@glimmer/interfaces/lib/template.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export interface LayoutWithContext {
1818
readonly block: SerializedTemplateBlock;
1919
readonly moduleName: string;
2020
readonly owner: Owner | null;
21-
readonly scope: (() => unknown[]) | undefined | null;
21+
readonly scope: (() => Record<string, unknown>) | undefined | null;
2222
readonly isStrictMode: boolean;
2323
}
2424

packages/@glimmer/opcode-compiler/lib/opcode-builder/helpers/shared.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,15 +106,16 @@ export function CompilePositional(
106106
}
107107

108108
export function meta(layout: LayoutWithContext): BlockMetadata {
109-
let [, locals, upvars, lexicalSymbols] = layout.block;
109+
let [, locals, upvars] = layout.block;
110+
let scopeRecord = layout.scope?.() ?? null;
110111

111112
return {
112113
symbols: {
113114
locals,
114115
upvars,
115-
lexical: lexicalSymbols,
116+
lexical: scopeRecord ? Object.keys(scopeRecord) : undefined,
116117
},
117-
scopeValues: layout.scope?.() ?? null,
118+
scopeValues: scopeRecord ? Object.values(scopeRecord) : null,
118119
isStrictMode: layout.isStrictMode,
119120
moduleName: layout.moduleName,
120121
owner: layout.owner,

packages/internal-test-helpers/lib/compile.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,15 @@ export default function compile(
2424
): TemplateFactory {
2525
options.locals = options.locals ?? Object.keys(scopeValues ?? {});
2626
let [block, usedLocals] = precompileJSON(templateSource, compileOptions(options));
27-
let reifiedScopeValues = usedLocals.map((key) => scopeValues[key]);
27+
let reifiedScope: Record<string, unknown> | null =
28+
usedLocals.length > 0
29+
? Object.fromEntries(usedLocals.map((key) => [key, scopeValues[key]]))
30+
: null;
2831

2932
let templateBlock: SerializedTemplateWithLazyBlock = {
3033
block: JSON.stringify(block),
3134
moduleName: options.moduleName ?? options.meta?.moduleName ?? '(unknown template module)',
32-
scope: reifiedScopeValues.length > 0 ? () => reifiedScopeValues : null,
35+
scope: reifiedScope ? () => reifiedScope! : null,
3336
isStrictMode: options.strictMode ?? false,
3437
};
3538

0 commit comments

Comments
 (0)