From f9ff226315dd7445349e00ba5c36617872de75e6 Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Tue, 29 Oct 2024 22:11:58 -0700 Subject: [PATCH 1/3] Streamline append VM implementation --- packages/@glimmer/debug/lib/debug.ts | 2 +- packages/@glimmer/interfaces/lib/program.d.ts | 2 + .../interfaces/lib/runtime/runtime.d.ts | 11 +- packages/@glimmer/program/lib/program.ts | 5 +- packages/@glimmer/runtime/index.ts | 3 +- .../runtime/lib/compiled/opcodes/component.ts | 56 ++-- .../runtime/lib/compiled/opcodes/debugger.ts | 5 +- .../runtime/lib/compiled/opcodes/dom.ts | 21 +- .../lib/compiled/opcodes/expressions.ts | 9 +- .../runtime/lib/compiled/opcodes/vm.ts | 15 +- packages/@glimmer/runtime/lib/environment.ts | 1 + packages/@glimmer/runtime/lib/opcodes.ts | 38 +-- packages/@glimmer/runtime/lib/render.ts | 13 +- packages/@glimmer/runtime/lib/vm.ts | 2 +- packages/@glimmer/runtime/lib/vm/append.ts | 272 +++++++----------- packages/@glimmer/runtime/lib/vm/update.ts | 6 +- 16 files changed, 198 insertions(+), 263 deletions(-) diff --git a/packages/@glimmer/debug/lib/debug.ts b/packages/@glimmer/debug/lib/debug.ts index 33fc534036..ad2ef20b2e 100644 --- a/packages/@glimmer/debug/lib/debug.ts +++ b/packages/@glimmer/debug/lib/debug.ts @@ -96,7 +96,7 @@ export function debug( let metadata = opcodeMetadata(op.type, isMachine); if (!metadata) { - throw new Error(`Missing Opcode Metadata for ${op}`); + throw new Error(`Missing Opcode Metadata for ${op.type}`); } let out = Object.create(null); diff --git a/packages/@glimmer/interfaces/lib/program.d.ts b/packages/@glimmer/interfaces/lib/program.d.ts index 3a516982ac..3500ca61d2 100644 --- a/packages/@glimmer/interfaces/lib/program.d.ts +++ b/packages/@glimmer/interfaces/lib/program.d.ts @@ -139,6 +139,8 @@ export interface RuntimeConstants { getArray(handle: number): T[]; } +export type JitConstants = CompileTimeConstants & ResolutionTimeConstants & RuntimeConstants; + export interface CompileTimeArtifacts { heap: CompileTimeHeap; constants: CompileTimeConstants & ResolutionTimeConstants; diff --git a/packages/@glimmer/interfaces/lib/runtime/runtime.d.ts b/packages/@glimmer/interfaces/lib/runtime/runtime.d.ts index a91a3a6cdb..e1adcbb980 100644 --- a/packages/@glimmer/interfaces/lib/runtime/runtime.d.ts +++ b/packages/@glimmer/interfaces/lib/runtime/runtime.d.ts @@ -1,9 +1,4 @@ -import type { - ResolutionTimeConstants, - RuntimeConstants, - RuntimeHeap, - RuntimeOp, -} from '../program.js'; +import type { JitConstants, RuntimeHeap, RuntimeOp } from '../program.js'; import type { RuntimeResolver } from '../serialize.js'; import type { Environment } from './environment.js'; @@ -21,13 +16,13 @@ export interface RuntimeContext { } export interface RuntimeProgram { - readonly constants: RuntimeConstants & ResolutionTimeConstants; + readonly constants: JitConstants; readonly heap: RuntimeHeap; opcode(offset: number): RuntimeOp; } export interface RuntimeArtifacts { - readonly constants: RuntimeConstants & ResolutionTimeConstants; + readonly constants: JitConstants; readonly heap: RuntimeHeap; } diff --git a/packages/@glimmer/program/lib/program.ts b/packages/@glimmer/program/lib/program.ts index 8305704979..a4f5d8587b 100644 --- a/packages/@glimmer/program/lib/program.ts +++ b/packages/@glimmer/program/lib/program.ts @@ -1,7 +1,6 @@ import type { CompileTimeHeap, - ResolutionTimeConstants, - RuntimeConstants, + JitConstants, RuntimeHeap, RuntimeProgram, SerializedHeap, @@ -209,7 +208,7 @@ export class RuntimeProgramImpl implements RuntimeProgram { private _opcode: RuntimeOpImpl; constructor( - public constants: RuntimeConstants & ResolutionTimeConstants, + public constants: JitConstants, public heap: RuntimeHeap ) { this._opcode = new RuntimeOpImpl(this.heap); diff --git a/packages/@glimmer/runtime/index.ts b/packages/@glimmer/runtime/index.ts index 0c80f9eceb..49d2d96477 100644 --- a/packages/@glimmer/runtime/index.ts +++ b/packages/@glimmer/runtime/index.ts @@ -41,7 +41,7 @@ export { on } from './lib/modifiers/on'; export { renderComponent, renderMain, renderSync } from './lib/render'; export { DynamicScopeImpl, PartialScopeImpl } from './lib/scope'; export type { SafeString } from './lib/upsert'; -export { type InternalVM, VM as LowLevelVM, UpdatingVM } from './lib/vm'; +export { UpdatingVM, type VM } from './lib/vm'; export { createCapturedArgs, EMPTY_ARGS, @@ -62,6 +62,7 @@ export { RemoteLiveBlock, UpdatableBlockImpl, } from './lib/vm/element-builder'; +export { LowLevelVM } from './lib/vm/low-level'; export { isSerializationFirstNode, RehydrateBuilder, diff --git a/packages/@glimmer/runtime/lib/compiled/opcodes/component.ts b/packages/@glimmer/runtime/lib/compiled/opcodes/component.ts index 4477ca8f96..ff57b53717 100644 --- a/packages/@glimmer/runtime/lib/compiled/opcodes/component.ts +++ b/packages/@glimmer/runtime/lib/compiled/opcodes/component.ts @@ -53,7 +53,7 @@ import { $t0, $t1, CurriedTypes, InternalComponentCapabilities, Op } from '@glim import type { CurriedValue } from '../../curried-value'; import type { UpdatingVM } from '../../vm'; -import type { InternalVM } from '../../vm/append'; +import type { VM } from '../../vm/append'; import type { BlockArgumentsImpl } from '../../vm/arguments'; import { ConcreteBounds } from '../../bounds'; @@ -63,7 +63,6 @@ import { isCurriedType, isCurriedValue, resolveCurriedValue } from '../../currie import { getDebugName } from '../../debug-render-tree'; import { APPEND_OPCODES } from '../../opcodes'; import createClassListRef from '../../references/class-list'; -import { ARGS, CONSTANTS } from '../../symbols'; import { EMPTY_ARGS, VMArgumentsImpl } from '../../vm/arguments'; import { CheckArguments, @@ -112,7 +111,7 @@ export interface PartialComponentDefinition { } APPEND_OPCODES.add(Op.PushComponentDefinition, (vm, { op1: handle }) => { - let definition = vm[CONSTANTS].getValue(handle); + let definition = vm.constants.getValue(handle); assert(!!definition, `Missing component for ${handle}`); let { manager, capabilities } = definition; @@ -136,7 +135,7 @@ APPEND_OPCODES.add(Op.ResolveDynamicComponent, (vm, { op1: _isStrict }) => { valueForRef(check(stack.pop(), CheckReference)), CheckOr(CheckString, CheckCurriedComponentDefinition) ); - let constants = vm[CONSTANTS]; + let constants = vm.constants; let owner = vm.getOwner(); let isStrict = constants.getValue(_isStrict); @@ -167,7 +166,7 @@ APPEND_OPCODES.add(Op.ResolveCurriedComponent, (vm) => { let stack = vm.stack; let ref = check(stack.pop(), CheckReference); let value = valueForRef(ref); - let constants = vm[CONSTANTS]; + let constants = vm.constants; let definition: CurriedValue | ComponentDefinition | null; @@ -219,21 +218,20 @@ APPEND_OPCODES.add(Op.PushDynamicComponentInstance, (vm) => { APPEND_OPCODES.add(Op.PushArgs, (vm, { op1: _names, op2: _blockNames, op3: flags }) => { let stack = vm.stack; - let names = vm[CONSTANTS].getArray(_names); + let names = vm.constants.getArray(_names); let positionalCount = flags >> 4; let atNames = flags & 0b1000; - let blockNames = - flags & 0b0111 ? vm[CONSTANTS].getArray(_blockNames) : EMPTY_STRING_ARRAY; + let blockNames = flags & 0b0111 ? vm.constants.getArray(_blockNames) : EMPTY_STRING_ARRAY; - vm[ARGS].setup(stack, names, blockNames, positionalCount, !!atNames); - stack.push(vm[ARGS]); + vm.args.setup(stack, names, blockNames, positionalCount, !!atNames); + stack.push(vm.args); }); APPEND_OPCODES.add(Op.PushEmptyArgs, (vm) => { let { stack } = vm; - stack.push(vm[ARGS].empty(stack)); + stack.push(vm.args.empty(stack)); }); APPEND_OPCODES.add(Op.CaptureArgs, (vm) => { @@ -257,7 +255,7 @@ APPEND_OPCODES.add(Op.PrepareArgs, (vm, { op1: _state }) => { "If the component definition was curried, we don't yet have a manager" ); - let constants = vm[CONSTANTS]; + let constants = vm.constants; let { definition: resolvedDefinition, @@ -430,10 +428,10 @@ APPEND_OPCODES.add(Op.PutComponentOperations, (vm) => { }); APPEND_OPCODES.add(Op.ComponentAttr, (vm, { op1: _name, op2: _trusting, op3: _namespace }) => { - let name = vm[CONSTANTS].getValue(_name); - let trusting = vm[CONSTANTS].getValue(_trusting); + let name = vm.constants.getValue(_name); + let trusting = vm.constants.getValue(_trusting); let reference = check(vm.stack.pop(), CheckReference); - let namespace = _namespace ? vm[CONSTANTS].getValue(_namespace) : null; + let namespace = _namespace ? vm.constants.getValue(_namespace) : null; check(vm.fetchValue($t0), CheckInstanceof(ComponentElementOperations)).setAttribute( name, @@ -444,9 +442,9 @@ APPEND_OPCODES.add(Op.ComponentAttr, (vm, { op1: _name, op2: _trusting, op3: _na }); APPEND_OPCODES.add(Op.StaticComponentAttr, (vm, { op1: _name, op2: _value, op3: _namespace }) => { - let name = vm[CONSTANTS].getValue(_name); - let value = vm[CONSTANTS].getValue(_value); - let namespace = _namespace ? vm[CONSTANTS].getValue(_namespace) : null; + let name = vm.constants.getValue(_name); + let value = vm.constants.getValue(_value); + let namespace = _namespace ? vm.constants.getValue(_namespace) : null; check(vm.fetchValue($t0), CheckInstanceof(ComponentElementOperations)).setStaticAttribute( name, @@ -491,7 +489,7 @@ export class ComponentElementOperations implements ElementOperations { this.attributes[name] = deferred; } - addModifier(vm: InternalVM, modifier: ModifierInstance, capturedArgs: CapturedArguments): void { + addModifier(vm: VM, modifier: ModifierInstance, capturedArgs: CapturedArguments): void { this.modifiers.push(modifier); if (vm.env.debugRenderTree !== undefined) { @@ -533,7 +531,7 @@ export class ComponentElementOperations implements ElementOperations { } } - flush(vm: InternalVM): ModifierInstance[] { + flush(vm: VM): ModifierInstance[] { let type: DeferredAttribute | undefined; let attributes = this.attributes; @@ -578,7 +576,7 @@ function allStringClasses(classes: (string | Reference)[]): classes is } function setDeferredAttr( - vm: InternalVM, + vm: VM, name: string, value: string | Reference, namespace: Nullable, @@ -621,12 +619,12 @@ APPEND_OPCODES.add(Op.GetComponentSelf, (vm, { op1: _state, op2: _names }) => { let args: CapturedArguments; - if (vm.stack.peek() === vm[ARGS]) { - args = vm[ARGS].capture(); + if (vm.stack.peek() === vm.args) { + args = vm.args.capture(); } else { - let names = vm[CONSTANTS].getArray(_names); - vm[ARGS].setup(vm.stack, names, [], 0, true); - args = vm[ARGS].capture(); + let names = vm.constants.getArray(_names); + vm.args.setup(vm.stack, names, [], 0, true); + args = vm.args.capture(); } let moduleName: string; @@ -729,9 +727,9 @@ APPEND_OPCODES.add(Op.GetComponentLayout, (vm, { op1: _state }) => { if (compilable === null) { if (managerHasCapability(manager, capabilities, InternalComponentCapabilities.wrapped)) { - compilable = unwrapTemplate(vm[CONSTANTS].defaultTemplate).asWrappedLayout(); + compilable = unwrapTemplate(vm.constants.defaultTemplate).asWrappedLayout(); } else { - compilable = unwrapTemplate(vm[CONSTANTS].defaultTemplate).asLayout(); + compilable = unwrapTemplate(vm.constants.defaultTemplate).asLayout(); } } } @@ -835,7 +833,7 @@ function bindBlock( blockName: string, state: ComponentInstance, blocks: BlockArgumentsImpl, - vm: InternalVM + vm: VM ) { let symbol = state.table.symbols.indexOf(symbolName); let block = blocks.get(blockName); diff --git a/packages/@glimmer/runtime/lib/compiled/opcodes/debugger.ts b/packages/@glimmer/runtime/lib/compiled/opcodes/debugger.ts index 84e40231ac..0de01f1ea1 100644 --- a/packages/@glimmer/runtime/lib/compiled/opcodes/debugger.ts +++ b/packages/@glimmer/runtime/lib/compiled/opcodes/debugger.ts @@ -5,7 +5,6 @@ import { decodeHandle, dict, unwrap } from '@glimmer/util'; import { Op } from '@glimmer/vm'; import { APPEND_OPCODES } from '../../opcodes'; -import { CONSTANTS } from '../../symbols'; export type DebugGet = (path: string) => unknown; @@ -72,8 +71,8 @@ class ScopeInspector { } APPEND_OPCODES.add(Op.Debugger, (vm, { op1: _symbols, op2: _debugInfo }) => { - let symbols = vm[CONSTANTS].getArray(_symbols); - let debugInfo = vm[CONSTANTS].getArray(decodeHandle(_debugInfo)); + let symbols = vm.constants.getArray(_symbols); + let debugInfo = vm.constants.getArray(decodeHandle(_debugInfo)); let inspector = new ScopeInspector(vm.scope(), symbols, debugInfo); callback(valueForRef(vm.getSelf()), (path) => valueForRef(inspector.get(path))); }); diff --git a/packages/@glimmer/runtime/lib/compiled/opcodes/dom.ts b/packages/@glimmer/runtime/lib/compiled/opcodes/dom.ts index f575240eb5..f88880ea25 100644 --- a/packages/@glimmer/runtime/lib/compiled/opcodes/dom.ts +++ b/packages/@glimmer/runtime/lib/compiled/opcodes/dom.ts @@ -31,21 +31,20 @@ import type { DynamicAttribute } from '../../vm/attributes/dynamic'; import { isCurriedType, resolveCurriedValue } from '../../curried-value'; import { APPEND_OPCODES } from '../../opcodes'; -import { CONSTANTS } from '../../symbols'; import { createCapturedArgs } from '../../vm/arguments'; import { CheckArguments, CheckOperations, CheckReference } from './-debug-strip'; import { Assert } from './vm'; APPEND_OPCODES.add(Op.Text, (vm, { op1: text }) => { - vm.elements().appendText(vm[CONSTANTS].getValue(text)); + vm.elements().appendText(vm.constants.getValue(text)); }); APPEND_OPCODES.add(Op.Comment, (vm, { op1: text }) => { - vm.elements().appendComment(vm[CONSTANTS].getValue(text)); + vm.elements().appendComment(vm.constants.getValue(text)); }); APPEND_OPCODES.add(Op.OpenElement, (vm, { op1: tag }) => { - vm.elements().openElement(vm[CONSTANTS].getValue(tag)); + vm.elements().openElement(vm.constants.getValue(tag)); }); APPEND_OPCODES.add(Op.OpenDynamicElement, (vm) => { @@ -138,7 +137,7 @@ APPEND_OPCODES.add(Op.Modifier, (vm, { op1: handle }) => { let owner = vm.getOwner(); let args = check(vm.stack.pop(), CheckArguments); - let definition = vm[CONSTANTS].getValue(handle); + let definition = vm.constants.getValue(handle); let { manager } = definition; @@ -356,19 +355,19 @@ export class UpdateDynamicModifierOpcode implements UpdatingOpcode { } APPEND_OPCODES.add(Op.StaticAttr, (vm, { op1: _name, op2: _value, op3: _namespace }) => { - let name = vm[CONSTANTS].getValue(_name); - let value = vm[CONSTANTS].getValue(_value); - let namespace = _namespace ? vm[CONSTANTS].getValue(_namespace) : null; + let name = vm.constants.getValue(_name); + let value = vm.constants.getValue(_value); + let namespace = _namespace ? vm.constants.getValue(_namespace) : null; vm.elements().setStaticAttribute(name, value, namespace); }); APPEND_OPCODES.add(Op.DynamicAttr, (vm, { op1: _name, op2: _trusting, op3: _namespace }) => { - let name = vm[CONSTANTS].getValue(_name); - let trusting = vm[CONSTANTS].getValue(_trusting); + let name = vm.constants.getValue(_name); + let trusting = vm.constants.getValue(_trusting); let reference = check(vm.stack.pop(), CheckReference); let value = valueForRef(reference); - let namespace = _namespace ? vm[CONSTANTS].getValue(_namespace) : null; + let namespace = _namespace ? vm.constants.getValue(_namespace) : null; let attribute = vm.elements().setDynamicAttribute(name, value, trusting, namespace); diff --git a/packages/@glimmer/runtime/lib/compiled/opcodes/expressions.ts b/packages/@glimmer/runtime/lib/compiled/opcodes/expressions.ts index 2fa7af7475..5a7a1573dc 100644 --- a/packages/@glimmer/runtime/lib/compiled/opcodes/expressions.ts +++ b/packages/@glimmer/runtime/lib/compiled/opcodes/expressions.ts @@ -33,7 +33,6 @@ import { $v0, CurriedTypes, Op } from '@glimmer/vm'; import { isCurriedType, resolveCurriedValue } from '../../curried-value'; import { APPEND_OPCODES } from '../../opcodes'; import createCurryRef from '../../references/curry-value'; -import { CONSTANTS } from '../../symbols'; import { reifyPositional } from '../../vm/arguments'; import { createConcatRef } from '../expressions/concat'; import { @@ -62,7 +61,7 @@ APPEND_OPCODES.add(Op.Curry, (vm, { op1: type, op2: _isStrict }) => { if (import.meta.env.DEV) { // strict check only happens in import.meta.env.DEV builds, no reason to load it otherwise - isStrict = vm[CONSTANTS].getValue(decodeHandle(_isStrict)); + isStrict = vm.constants.getValue(decodeHandle(_isStrict)); } vm.loadValue( @@ -151,7 +150,7 @@ function resolveHelper(definition: HelperDefinitionState, ref: Reference): Helpe APPEND_OPCODES.add(Op.Helper, (vm, { op1: handle }) => { let stack = vm.stack; - let helper = check(vm[CONSTANTS].getValue(handle), CheckHelper); + let helper = check(vm.constants.getValue(handle), CheckHelper); let args = check(stack.pop(), CheckArguments); let value = helper(args.capture(), vm.getOwner(), vm.dynamicScope()); @@ -182,7 +181,7 @@ APPEND_OPCODES.add(Op.SetBlock, (vm, { op1: symbol }) => { }); APPEND_OPCODES.add(Op.ResolveMaybeLocal, (vm, { op1: _name }) => { - let name = vm[CONSTANTS].getValue(_name); + let name = vm.constants.getValue(_name); let locals = vm.scope().getPartialMap()!; let ref = locals[name]; @@ -198,7 +197,7 @@ APPEND_OPCODES.add(Op.RootScope, (vm, { op1: symbols }) => { }); APPEND_OPCODES.add(Op.GetProperty, (vm, { op1: _key }) => { - let key = vm[CONSTANTS].getValue(_key); + let key = vm.constants.getValue(_key); let expr = check(vm.stack.pop(), CheckReference); vm.stack.push(childRefFor(expr, key)); }); diff --git a/packages/@glimmer/runtime/lib/compiled/opcodes/vm.ts b/packages/@glimmer/runtime/lib/compiled/opcodes/vm.ts index 6ac1d0585d..92d2da111a 100644 --- a/packages/@glimmer/runtime/lib/compiled/opcodes/vm.ts +++ b/packages/@glimmer/runtime/lib/compiled/opcodes/vm.ts @@ -35,10 +35,9 @@ import { import { Op } from '@glimmer/vm'; import type { UpdatingVM } from '../../vm'; -import type { InternalVM } from '../../vm/append'; +import type { VM } from '../../vm/append'; import { APPEND_OPCODES } from '../../opcodes'; -import { CONSTANTS } from '../../symbols'; import { VMArgumentsImpl } from '../../vm/arguments'; import { CheckReference, CheckScope } from './-debug-strip'; import { stackAssert } from './assert'; @@ -52,11 +51,11 @@ APPEND_OPCODES.add(Op.PushDynamicScope, (vm) => vm.pushDynamicScope()); APPEND_OPCODES.add(Op.PopDynamicScope, (vm) => vm.popDynamicScope()); APPEND_OPCODES.add(Op.Constant, (vm, { op1: other }) => { - vm.stack.push(vm[CONSTANTS].getValue(decodeHandle(other))); + vm.stack.push(vm.constants.getValue(decodeHandle(other))); }); APPEND_OPCODES.add(Op.ConstantReference, (vm, { op1: other }) => { - vm.stack.push(createConstRef(vm[CONSTANTS].getValue(decodeHandle(other)), false)); + vm.stack.push(createConstRef(vm.constants.getValue(decodeHandle(other)), false)); }); APPEND_OPCODES.add(Op.Primitive, (vm, { op1: primitive }) => { @@ -64,7 +63,7 @@ APPEND_OPCODES.add(Op.Primitive, (vm, { op1: primitive }) => { if (isHandle(primitive)) { // it is a handle which does not already exist on the stack - let value = vm[CONSTANTS].getValue(decodeHandle(primitive)); + let value = vm.constants.getValue(decodeHandle(primitive)); stack.push(value as object); } else { // is already an encoded immediate or primitive handle @@ -110,7 +109,7 @@ APPEND_OPCODES.add(Op.Fetch, (vm, { op1: register }) => { }); APPEND_OPCODES.add(Op.BindDynamicScope, (vm, { op1: _names }) => { - let names = vm[CONSTANTS].getArray(_names); + let names = vm.constants.getArray(_names); vm.bindDynamicScope(names); }); @@ -124,7 +123,7 @@ APPEND_OPCODES.add(Op.Exit, (vm) => { APPEND_OPCODES.add(Op.PushSymbolTable, (vm, { op1: _table }) => { let stack = vm.stack; - stack.push(vm[CONSTANTS].getValue(_table)); + stack.push(vm.constants.getValue(_table)); }); APPEND_OPCODES.add(Op.PushBlockScope, (vm) => { @@ -132,7 +131,7 @@ APPEND_OPCODES.add(Op.PushBlockScope, (vm) => { stack.push(vm.scope()); }); -APPEND_OPCODES.add(Op.CompileBlock, (vm: InternalVM) => { +APPEND_OPCODES.add(Op.CompileBlock, (vm: VM) => { let stack = vm.stack; let block = stack.pop | 0>(); diff --git a/packages/@glimmer/runtime/lib/environment.ts b/packages/@glimmer/runtime/lib/environment.ts index 46a1b5a18b..d6bcaa6c64 100644 --- a/packages/@glimmer/runtime/lib/environment.ts +++ b/packages/@glimmer/runtime/lib/environment.ts @@ -102,6 +102,7 @@ export class EnvironmentImpl implements Environment { // Delegate methods and values public isInteractive: boolean; + // eslint-disable-next-line @typescript-eslint/no-explicit-any isArgumentCaptureError: ((error: any) => boolean) | undefined; debugRenderTree: DebugRenderTree | undefined; diff --git a/packages/@glimmer/runtime/lib/opcodes.ts b/packages/@glimmer/runtime/lib/opcodes.ts index c9e6b3f957..abc9c89356 100644 --- a/packages/@glimmer/runtime/lib/opcodes.ts +++ b/packages/@glimmer/runtime/lib/opcodes.ts @@ -11,13 +11,11 @@ import { debug, logOpcode, opcodeMetadata, recordStackSize } from '@glimmer/debu import { LOCAL_DEBUG, LOCAL_SHOULD_LOG } from '@glimmer/local-debug-flags'; import { valueForRef } from '@glimmer/reference'; import { assert, LOCAL_LOGGER, unwrap } from '@glimmer/util'; -import { $fp, $pc, $ra, $sp, Op } from '@glimmer/vm'; +import { $fp, $pc, $ra, $s0, $s1, $sp, $t0, $t1, $v0, Op } from '@glimmer/vm'; import type { LowLevelVM, VM } from './vm'; -import type { InternalVM } from './vm/append'; import { isScopeReference } from './scope'; -import { CONSTANTS, DESTROYABLE_STACK, INNER_VM, STACKS } from './symbols'; import { CURSOR_STACK } from './vm/element-builder'; export interface OpcodeJSON { @@ -33,7 +31,7 @@ export type Operand1 = number; export type Operand2 = number; export type Operand3 = number; -export type Syscall = (vm: InternalVM, opcode: RuntimeOp) => void; +export type Syscall = (vm: VM, opcode: RuntimeOp) => void; export type MachineOpcode = (vm: LowLevelVM, opcode: RuntimeOp) => void; export type Evaluate = @@ -72,9 +70,10 @@ export class AppendOpcodes { let opName: string | undefined = undefined; if (LOCAL_SHOULD_LOG) { - let pos = vm[INNER_VM].fetchRegister($pc) - opcode.size; + const lowlevel = unwrap(vm.debug).lowlevel; + let pos = lowlevel.fetchRegister($pc) - opcode.size; - [opName, params] = debug(vm[CONSTANTS], opcode, opcode.isMachine)!; + [opName, params] = debug(vm.constants, opcode, opcode.isMachine)!; // console.log(`${typePos(vm['pc'])}.`); LOCAL_LOGGER.log(`${pos}. ${logOpcode(opName, params)}`); @@ -110,6 +109,8 @@ export class AppendOpcodes { let { sp, type, isMachine, pc } = pre; if (LOCAL_DEBUG) { + const debug = unwrap(vm.debug); + let meta = opcodeMetadata(type, isMachine); let actualChange = vm.fetchValue($sp) - sp; if ( @@ -127,27 +128,28 @@ export class AppendOpcodes { } if (LOCAL_SHOULD_LOG) { + const { lowlevel, registers } = unwrap(vm.debug); LOCAL_LOGGER.log( '%c -> pc: %d, ra: %d, fp: %d, sp: %d, s0: %O, s1: %O, t0: %O, t1: %O, v0: %O', 'color: orange', - vm[INNER_VM].registers[$pc], - vm[INNER_VM].registers[$ra], - vm[INNER_VM].registers[$fp], - vm[INNER_VM].registers[$sp], - vm['s0'], - vm['s1'], - vm['t0'], - vm['t1'], - vm['v0'] + lowlevel.registers[$pc], + lowlevel.registers[$ra], + lowlevel.registers[$fp], + lowlevel.registers[$sp], + registers[$s0], + registers[$s1], + registers[$t0], + registers[$t1], + registers[$v0] ); LOCAL_LOGGER.log('%c -> eval stack', 'color: red', vm.stack.toArray()); LOCAL_LOGGER.log('%c -> block stack', 'color: magenta', vm.elements().debugBlocks()); LOCAL_LOGGER.log( '%c -> destructor stack', 'color: violet', - vm[DESTROYABLE_STACK].toArray() + debug.destroyableStack.toArray() ); - if (vm[STACKS].scope.current === null) { + if (debug.stacks.scope.current === null) { LOCAL_LOGGER.log('%c -> scope', 'color: green', 'null'); } else { LOCAL_LOGGER.log( @@ -182,7 +184,7 @@ export class AppendOpcodes { opcode.isMachine, `BUG: Mismatch between operation.syscall (${operation.syscall}) and opcode.isMachine (${opcode.isMachine}) for ${opcode.type}` ); - operation.evaluate(vm[INNER_VM], opcode); + operation.evaluate(vm.lowlevel, opcode); } } } diff --git a/packages/@glimmer/runtime/lib/render.ts b/packages/@glimmer/runtime/lib/render.ts index c792f2401f..9d4e207a40 100644 --- a/packages/@glimmer/runtime/lib/render.ts +++ b/packages/@glimmer/runtime/lib/render.ts @@ -16,15 +16,12 @@ import { childRefFor, createConstRef } from '@glimmer/reference'; import { expect, unwrapHandle } from '@glimmer/util'; import { debug } from '@glimmer/validator'; -import type { InternalVM } from './vm/append'; - import { inTransaction } from './environment'; import { DynamicScopeImpl } from './scope'; -import { ARGS, CONSTANTS } from './symbols'; import { VM } from './vm/append'; class TemplateIteratorImpl implements TemplateIterator { - constructor(private vm: InternalVM) {} + constructor(private vm: VM) {} next(): RichIteratorResult { return this.vm.next(); } @@ -69,7 +66,7 @@ export function renderMain( } function renderInvocation( - vm: InternalVM, + vm: VM, context: CompileTimeCompilationContext, owner: Owner, definition: ComponentDefinitionState, @@ -83,7 +80,7 @@ function renderInvocation( // Prefix argument names with `@` symbol const argNames = argList.map(([name]) => `@${name}`); - let reified = vm[CONSTANTS].component(definition, owner, undefined, '{ROOT}'); + let reified = vm.constants.component(definition, owner, undefined, '{ROOT}'); vm.pushFrame(); @@ -100,7 +97,7 @@ function renderInvocation( }); // Configure VM based on blocks and args just pushed on to the stack. - vm[ARGS].setup(vm.stack, argNames, blockNames, 0, true); + vm.args.setup(vm.stack, argNames, blockNames, 0, true); const compilable = expect( reified.compilable, @@ -111,7 +108,7 @@ function renderInvocation( // Needed for the Op.Main opcode: arguments, component invocation object, and // component definition. - vm.stack.push(vm[ARGS]); + vm.stack.push(vm.args); vm.stack.push(invocation); vm.stack.push(reified); diff --git a/packages/@glimmer/runtime/lib/vm.ts b/packages/@glimmer/runtime/lib/vm.ts index 5ae12b7170..3c364da16b 100644 --- a/packages/@glimmer/runtime/lib/vm.ts +++ b/packages/@glimmer/runtime/lib/vm.ts @@ -1,3 +1,3 @@ -export { type InternalVM, VM } from './vm/append'; +export { VM } from './vm/append'; export { LowLevelVM } from './vm/low-level'; export { UpdatingVM } from './vm/update'; diff --git a/packages/@glimmer/runtime/lib/vm/append.ts b/packages/@glimmer/runtime/lib/vm/append.ts index 352341d390..20200d4564 100644 --- a/packages/@glimmer/runtime/lib/vm/append.ts +++ b/packages/@glimmer/runtime/lib/vm/append.ts @@ -5,13 +5,12 @@ import type { DynamicScope, ElementBuilder, Environment, + JitConstants, Nullable, Owner, PartialScope, RenderResult, - ResolutionTimeConstants, RichIteratorResult, - RuntimeConstants, RuntimeContext, RuntimeHeap, RuntimeProgram, @@ -42,84 +41,13 @@ import { } from '../compiled/opcodes/vm'; import { APPEND_OPCODES } from '../opcodes'; import { PartialScopeImpl } from '../scope'; -import { ARGS, CONSTANTS, DESTROYABLE_STACK, HEAP, INNER_VM, REGISTERS, STACKS } from '../symbols'; +import { REGISTERS } from '../symbols'; import { VMArgumentsImpl } from './arguments'; import { LowLevelVM } from './low-level'; import RenderResultImpl from './render-result'; import EvaluationStackImpl from './stack'; import { ListBlockOpcode, ListItemOpcode, ResumableVMStateImpl, TryOpcode } from './update'; -/** - * This interface is used by internal opcodes, and is more stable than - * the implementation of the Append VM itself. - */ -export interface InternalVM { - readonly [CONSTANTS]: RuntimeConstants & ResolutionTimeConstants; - readonly [ARGS]: VMArgumentsImpl; - - readonly env: Environment; - readonly stack: EvaluationStack; - readonly runtime: RuntimeContext; - readonly context: CompileTimeCompilationContext; - - loadValue(register: MachineRegister, value: number): void; - loadValue(register: Register, value: unknown): void; - loadValue(register: Register | MachineRegister, value: unknown): void; - - fetchValue(register: MachineRegister.ra | MachineRegister.pc): number; - // TODO: Something better than a type assertion? - fetchValue(register: Register): T; - fetchValue(register: Register): unknown; - - load(register: Register): void; - fetch(register: Register): void; - - compile(block: CompilableTemplate): number; - - scope(): Scope; - elements(): ElementBuilder; - - getOwner(): Owner; - getSelf(): Reference; - - updateWith(opcode: UpdatingOpcode): void; - - associateDestroyable(d: Destroyable): void; - - beginCacheGroup(name?: string): void; - commitCacheGroup(): void; - - /// Iteration /// - - enterList(iterableRef: Reference, offset: number): void; - exitList(): void; - enterItem(item: OpaqueIterationItem): ListItemOpcode; - registerItem(item: ListItemOpcode): void; - - pushRootScope(size: number, owner: Owner): PartialScope; - pushChildScope(): void; - popScope(): void; - pushScope(scope: Scope): void; - - dynamicScope(): DynamicScope; - bindDynamicScope(names: string[]): void; - pushDynamicScope(): void; - popDynamicScope(): void; - - enter(args: number): void; - exit(): void; - - goto(pc: number): void; - call(handle: number): void; - pushFrame(): void; - - referenceForSymbol(symbol: number): Reference; - - execute(initialize?: (vm: this) => void): RenderResult; - pushUpdating(list?: UpdatingOpcode[]): void; - next(): RichIteratorResult; -} - class Stacks { readonly scope = new Stack(); readonly dynamicScope = new Stack(); @@ -128,30 +56,61 @@ class Stacks { readonly list = new Stack(); } -export class VM implements PublicVM, InternalVM { - private readonly [STACKS] = new Stacks(); - private readonly [HEAP]: RuntimeHeap; - private readonly destructor: object; - private readonly [DESTROYABLE_STACK] = new Stack(); - readonly [CONSTANTS]: RuntimeConstants & ResolutionTimeConstants; - readonly [ARGS]: VMArgumentsImpl; - readonly [INNER_VM]: LowLevelVM; +interface SyscallRegisters { + [$s0]: unknown; + [$s1]: unknown; + [$t0]: unknown; + [$t1]: unknown; + [$v0]: unknown; +} + +interface DebugVmState { + readonly stacks: Stacks; + readonly destroyableStack: Stack; + readonly constants: JitConstants; + readonly lowlevel: LowLevelVM; + readonly registers: SyscallRegisters; +} + +export class VM implements PublicVM { + readonly #stacks = new Stacks(); + readonly #heap: RuntimeHeap; + readonly #destructor: object; + readonly #destroyableStack = new Stack(); + readonly #constants: JitConstants; + readonly #args: VMArgumentsImpl; + readonly #lowlevel: LowLevelVM; + readonly debug?: DebugVmState; get stack(): EvaluationStack { - return this[INNER_VM].stack as EvaluationStack; + return this.#lowlevel.stack as EvaluationStack; + } + + get constants(): JitConstants { + return this.#constants; + } + + get args(): VMArgumentsImpl { + return this.#args; + } + + get lowlevel(): LowLevelVM { + return this.#lowlevel; } /* Registers */ get pc(): number { - return this[INNER_VM].fetchRegister($pc); + return this.#lowlevel.fetchRegister($pc); } - public s0: unknown = null; - public s1: unknown = null; - public t0: unknown = null; - public t1: unknown = null; - public v0: unknown = null; + #registers: SyscallRegisters = { + [$s0]: null, + [$s1]: null, + [$t0]: null, + [$t1]: null, + [$v0]: null, + }; // Fetch a value from a register onto the stack fetch(register: SyscallRegister): void { @@ -172,46 +131,20 @@ export class VM implements PublicVM, InternalVM { fetchValue(register: Register): T; fetchValue(register: Register | MachineRegister): unknown { if (isLowLevelRegister(register)) { - return this[INNER_VM].fetchRegister(register); + return this.#lowlevel.fetchRegister(register); } - switch (register) { - case $s0: - return this.s0; - case $s1: - return this.s1; - case $t0: - return this.t0; - case $t1: - return this.t1; - case $v0: - return this.v0; - } + return this.#registers[register]; } // Load a value into a register loadValue(register: Register | MachineRegister, value: T): void { if (isLowLevelRegister(register)) { - this[INNER_VM].loadRegister(register, value as any as number); - } - - switch (register) { - case $s0: - this.s0 = value; - break; - case $s1: - this.s1 = value; - break; - case $t0: - this.t0 = value; - break; - case $t1: - this.t1 = value; - break; - case $v0: - this.v0 = value; - break; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + this.#lowlevel.loadRegister(register, value as any as number); + } else { + this.#registers[register] = value; } } @@ -221,32 +154,32 @@ export class VM implements PublicVM, InternalVM { // Start a new frame and save $ra and $fp on the stack pushFrame() { - this[INNER_VM].pushFrame(); + this.#lowlevel.pushFrame(); } // Restore $ra, $sp and $fp popFrame() { - this[INNER_VM].popFrame(); + this.#lowlevel.popFrame(); } // Jump to an address in `program` goto(offset: number) { - this[INNER_VM].goto(offset); + this.#lowlevel.goto(offset); } // Save $pc into $ra, then jump to a new address in `program` (jal in MIPS) call(handle: number) { - this[INNER_VM].call(handle); + this.#lowlevel.call(handle); } // Put a specific `program` address in $ra returnTo(offset: number) { - this[INNER_VM].returnTo(offset); + this.#lowlevel.returnTo(offset); } // Return to the `program` address stored in $ra return() { - this[INNER_VM].return(); + this.#lowlevel.return(); } /** @@ -272,15 +205,15 @@ export class VM implements PublicVM, InternalVM { evalStack[REGISTERS][$sp] = stack.length - 1; evalStack[REGISTERS][$fp] = -1; - this[HEAP] = this.program.heap; - this[CONSTANTS] = this.program.constants; + this.#heap = this.program.heap; + this.#constants = this.program.constants; this.elementStack = elementStack; - this[STACKS].scope.push(scope); - this[STACKS].dynamicScope.push(dynamicScope); - this[ARGS] = new VMArgumentsImpl(); - this[INNER_VM] = new LowLevelVM( + this.#stacks.scope.push(scope); + this.#stacks.dynamicScope.push(dynamicScope); + this.#args = new VMArgumentsImpl(); + this.#lowlevel = new LowLevelVM( evalStack, - this[HEAP], + this.#heap, runtime.program, { debugBefore: (opcode: RuntimeOpImpl): DebugState => { @@ -294,8 +227,18 @@ export class VM implements PublicVM, InternalVM { evalStack[REGISTERS] ); - this.destructor = {}; - this[DESTROYABLE_STACK].push(this.destructor); + this.#destructor = {}; + this.#destroyableStack.push(this.#destructor); + + if (import.meta.env.DEV) { + this.debug = { + stacks: this.#stacks, + destroyableStack: this.#destroyableStack, + constants: this.#constants, + lowlevel: this.#lowlevel, + registers: this.#registers, + }; + } } static initial( @@ -344,7 +287,7 @@ export class VM implements PublicVM, InternalVM { return this.runtime.env; } - captureState(args: number, pc = this[INNER_VM].fetchRegister($pc)): VMState { + captureState(args: number, pc = this.#lowlevel.fetchRegister($pc)): VMState { return { pc, scope: this.scope(), @@ -353,7 +296,7 @@ export class VM implements PublicVM, InternalVM { }; } - capture(args: number, pc = this[INNER_VM].fetchRegister($pc)): ResumableVMState { + capture(args: number, pc = this.#lowlevel.fetchRegister($pc)): ResumableVMState { return new ResumableVMStateImpl(this.captureState(args, pc), this.resume); } @@ -363,14 +306,14 @@ export class VM implements PublicVM, InternalVM { opcodes.push(guard); opcodes.push(new BeginTrackFrameOpcode(name)); - this[STACKS].cache.push(guard); + this.#stacks.cache.push(guard); beginTrackFrame(name); } commitCacheGroup() { let opcodes = this.updating(); - let guard = expect(this[STACKS].cache.pop(), 'VM BUG: Expected a cache group'); + let guard = expect(this.#stacks.cache.pop(), 'VM BUG: Expected a cache group'); let tag = endTrackFrame(); opcodes.push(new EndTrackFrameOpcode(guard)); @@ -414,41 +357,41 @@ export class VM implements PublicVM, InternalVM { enterList(iterableRef: Reference, offset: number) { let updating: ListItemOpcode[] = []; - let addr = this[INNER_VM].target(offset); + let addr = this.#lowlevel.target(offset); let state = this.capture(0, addr); let list = this.elements().pushBlockList(updating) as LiveBlockList; let opcode = new ListBlockOpcode(state, this.runtime, list, updating, iterableRef); - this[STACKS].list.push(opcode); + this.#stacks.list.push(opcode); this.didEnter(opcode); } private didEnter(opcode: BlockOpcode) { this.associateDestroyable(opcode); - this[DESTROYABLE_STACK].push(opcode); + this.#destroyableStack.push(opcode); this.updateWith(opcode); this.pushUpdating(opcode.children); } exit() { - this[DESTROYABLE_STACK].pop(); + this.#destroyableStack.pop(); this.elements().popBlock(); this.popUpdating(); } exitList() { this.exit(); - this[STACKS].list.pop(); + this.#stacks.list.pop(); } pushUpdating(list: UpdatingOpcode[] = []): void { - this[STACKS].updating.push(list); + this.#stacks.updating.push(list); } popUpdating(): UpdatingOpcode[] { - return expect(this[STACKS].updating.pop(), "can't pop an empty stack"); + return expect(this.#stacks.updating.pop(), "can't pop an empty stack"); } updateWith(opcode: UpdatingOpcode) { @@ -456,21 +399,21 @@ export class VM implements PublicVM, InternalVM { } listBlock(): ListBlockOpcode { - return expect(this[STACKS].list.current, 'expected a list block'); + return expect(this.#stacks.list.current, 'expected a list block'); } associateDestroyable(child: Destroyable): void { - let parent = expect(this[DESTROYABLE_STACK].current, 'Expected destructor parent'); + let parent = expect(this.#destroyableStack.current, 'Expected destructor parent'); associateDestroyableChild(parent, child); } tryUpdating(): Nullable { - return this[STACKS].updating.current; + return this.#stacks.updating.current; } updating(): UpdatingOpcode[] { return expect( - this[STACKS].updating.current, + this.#stacks.updating.current, 'expected updating opcode on the updating opcode stack' ); } @@ -480,42 +423,42 @@ export class VM implements PublicVM, InternalVM { } scope(): Scope { - return expect(this[STACKS].scope.current, 'expected scope on the scope stack'); + return expect(this.#stacks.scope.current, 'expected scope on the scope stack'); } dynamicScope(): DynamicScope { return expect( - this[STACKS].dynamicScope.current, + this.#stacks.dynamicScope.current, 'expected dynamic scope on the dynamic scope stack' ); } pushChildScope() { - this[STACKS].scope.push(this.scope().child()); + this.#stacks.scope.push(this.scope().child()); } pushDynamicScope(): DynamicScope { let child = this.dynamicScope().child(); - this[STACKS].dynamicScope.push(child); + this.#stacks.dynamicScope.push(child); return child; } pushRootScope(size: number, owner: Owner): PartialScope { let scope = PartialScopeImpl.sized(size, owner); - this[STACKS].scope.push(scope); + this.#stacks.scope.push(scope); return scope; } pushScope(scope: Scope) { - this[STACKS].scope.push(scope); + this.#stacks.scope.push(scope); } popScope() { - this[STACKS].scope.pop(); + this.#stacks.scope.pop(); } popDynamicScope() { - this[STACKS].dynamicScope.pop(); + this.#stacks.dynamicScope.pop(); } /// SCOPE HELPERS @@ -524,6 +467,7 @@ export class VM implements PublicVM, InternalVM { return this.scope().owner; } + // eslint-disable-next-line @typescript-eslint/no-explicit-any getSelf(): Reference { return this.scope().getSelf(); } @@ -566,7 +510,7 @@ export class VM implements PublicVM, InternalVM { private _execute(initialize?: (vm: this) => void): RenderResult { if (LOCAL_SHOULD_LOG) { - LOCAL_LOGGER.log(`EXECUTING FROM ${this[INNER_VM].fetchRegister($pc)}`); + LOCAL_LOGGER.log(`EXECUTING FROM ${this.#lowlevel.fetchRegister($pc)}`); } if (initialize) initialize(this); @@ -581,10 +525,10 @@ export class VM implements PublicVM, InternalVM { next(): RichIteratorResult { let { env, elementStack } = this; - let opcode = this[INNER_VM].nextStatement(); + let opcode = this.#lowlevel.nextStatement(); let result: RichIteratorResult; if (opcode !== null) { - this[INNER_VM].evaluateOuter(opcode, this); + this.#lowlevel.evaluateOuter(opcode, this); result = { done: false, value: null }; } else { // Unload the stack @@ -596,7 +540,7 @@ export class VM implements PublicVM, InternalVM { env, this.popUpdating(), elementStack.popBlock(), - this.destructor + this.#destructor ), }; } @@ -638,7 +582,7 @@ export type VmInitCallback = ( runtime: RuntimeContext, state: VMState, builder: ElementBuilder -) => InternalVM; +) => VM; function initVM(context: CompileTimeCompilationContext): VmInitCallback { return (runtime, state, builder) => new VM(runtime, state, builder, context); diff --git a/packages/@glimmer/runtime/lib/vm/update.ts b/packages/@glimmer/runtime/lib/vm/update.ts index 764749b2ee..5b460008fe 100644 --- a/packages/@glimmer/runtime/lib/vm/update.ts +++ b/packages/@glimmer/runtime/lib/vm/update.ts @@ -21,7 +21,7 @@ import { updateRef, valueForRef } from '@glimmer/reference'; import { expect, logStep, Stack, unwrap } from '@glimmer/util'; import { debug, resetTracking } from '@glimmer/validator'; -import type { InternalVM, VmInitCallback } from './append'; +import type { VM, VmInitCallback } from './append'; import type { LiveBlockList } from './element-builder'; import { clear, move as moveBounds } from '../bounds'; @@ -106,7 +106,7 @@ export interface VMState { } export interface ResumableVMState { - resume(runtime: RuntimeContext, builder: ElementBuilder): InternalVM; + resume(runtime: RuntimeContext, builder: ElementBuilder): VM; } export class ResumableVMStateImpl implements ResumableVMState { @@ -115,7 +115,7 @@ export class ResumableVMStateImpl implements ResumableVMState { private resumeCallback: VmInitCallback ) {} - resume(runtime: RuntimeContext, builder: ElementBuilder): InternalVM { + resume(runtime: RuntimeContext, builder: ElementBuilder): VM { return this.resumeCallback(runtime, this.state, builder); } } From 764160e0a4df86a4d41443ebd8d071ae0c093b57 Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Tue, 29 Oct 2024 23:18:54 -0700 Subject: [PATCH 2/3] Remove some enums --- .../integration-tests/lib/setup-harness.ts | 12 +++ .../compiler/lib/builder/builder-interface.ts | 70 +++++++++-------- .../@glimmer/compiler/lib/builder/builder.ts | 3 +- packages/@glimmer/debug/index.ts | 3 + packages/@glimmer/debug/lib/debug.ts | 5 +- packages/@glimmer/debug/lib/stack-check.ts | 62 +++++++++++++++ .../runtime/lib/compiled/opcodes/component.ts | 77 +++++++++++-------- .../runtime/lib/compiled/opcodes/vm.ts | 8 +- packages/@glimmer/runtime/lib/vm/append.ts | 15 +--- packages/@glimmer/runtime/lib/vm/arguments.ts | 8 +- packages/@glimmer/runtime/lib/vm/low-level.ts | 12 ++- packages/@glimmer/runtime/lib/vm/stack.ts | 57 +++++++++----- packages/@glimmer/util/lib/immediate.ts | 74 +++++++----------- packages/@glimmer/util/test/immediate-test.ts | 13 +++- packages/@glimmer/vm/index.ts | 20 +---- packages/@glimmer/vm/lib/registers.ts | 50 ++++++------ 16 files changed, 284 insertions(+), 205 deletions(-) diff --git a/packages/@glimmer-workspace/integration-tests/lib/setup-harness.ts b/packages/@glimmer-workspace/integration-tests/lib/setup-harness.ts index 485f7539f9..0627b9943b 100644 --- a/packages/@glimmer-workspace/integration-tests/lib/setup-harness.ts +++ b/packages/@glimmer-workspace/integration-tests/lib/setup-harness.ts @@ -23,6 +23,18 @@ export async function setupQunit() { tooltip: 'CI mode makes tests run faster by sacrificing UI responsiveness', }); + QUnit.config.urlConfig.push({ + id: 'enable_local_should_log', + label: 'Trace Logging', + tooltip: 'Enable LOCAL_SHOULD_LOG (extra debug logging info)', + }); + + QUnit.config.urlConfig.push({ + id: 'disable_local_debug', + label: 'Disable Debug Assertions', + tooltip: 'Disable LOCAL_DEBUG (debug assertions)', + }); + await Promise.resolve(); const qunitDiv = document.createElement('div'); diff --git a/packages/@glimmer/compiler/lib/builder/builder-interface.ts b/packages/@glimmer/compiler/lib/builder/builder-interface.ts index 0e2cfca37c..a4681a9742 100644 --- a/packages/@glimmer/compiler/lib/builder/builder-interface.ts +++ b/packages/@glimmer/compiler/lib/builder/builder-interface.ts @@ -12,7 +12,7 @@ export type NormalizedHash = Dict; export type NormalizedBlock = NormalizedStatement[]; export type NormalizedBlocks = Dict; export type NormalizedAttrs = Dict; -export type NormalizedAttr = HeadKind.Splat | NormalizedExpression; +export type NormalizedAttr = HeadKinds['Splat'] | NormalizedExpression; export interface NormalizedElement { name: string; @@ -26,27 +26,33 @@ export interface NormalizedAngleInvocation { block: Nullable; } -export enum HeadKind { - Block = 'Block', - Call = 'Call', - Element = 'Element', - AppendPath = 'AppendPath', - AppendExpr = 'AppendExpr', - Literal = 'Literal', - Modifier = 'Modifier', - DynamicComponent = 'DynamicComponent', - Comment = 'Comment', - Splat = 'Splat', - Keyword = 'Keyword', -} - -export enum VariableKind { - Local = 'Local', - Free = 'Free', - Arg = 'Arg', - Block = 'Block', - This = 'This', -} +export const HeadKind = { + Block: 'Block', + Call: 'Call', + Element: 'Element', + AppendPath: 'AppendPath', + AppendExpr: 'AppendExpr', + Literal: 'Literal', + Modifier: 'Modifier', + DynamicComponent: 'DynamicComponent', + Comment: 'Comment', + Splat: 'Splat', + Keyword: 'Keyword', +} as const; + +export type HeadKinds = typeof HeadKind; +export type HeadKind = keyof HeadKinds; + +export const VariableKind = { + Local: 'Local', + Free: 'Free', + Arg: 'Arg', + Block: 'Block', + This: 'This', +} as const; + +export type VariableKinds = typeof VariableKind; +export type VariableKind = keyof VariableKinds; export interface Variable { kind: VariableKind; @@ -67,19 +73,19 @@ export interface Path { } export interface AppendExpr { - kind: HeadKind.AppendExpr; + kind: HeadKinds['AppendExpr']; expr: NormalizedExpression; trusted: boolean; } export interface AppendPath { - kind: HeadKind.AppendPath; + kind: HeadKinds['AppendPath']; path: NormalizedPath; trusted: boolean; } export interface NormalizedKeywordStatement { - kind: HeadKind.Keyword; + kind: HeadKinds['Keyword']; name: string; params: Nullable; hash: Nullable; @@ -89,14 +95,14 @@ export interface NormalizedKeywordStatement { export type NormalizedStatement = | { - kind: HeadKind.Call; + kind: HeadKinds['Call']; head: NormalizedHead; params: Nullable; hash: Nullable; trusted: boolean; } | { - kind: HeadKind.Block; + kind: HeadKinds['Block']; head: NormalizedHead; params: Nullable; hash: Nullable; @@ -105,18 +111,18 @@ export type NormalizedStatement = } | NormalizedKeywordStatement | { - kind: HeadKind.Element; + kind: HeadKinds['Element']; name: string; attrs: NormalizedAttrs; block: NormalizedBlock; } - | { kind: HeadKind.Comment; value: string } - | { kind: HeadKind.Literal; value: string } + | { kind: HeadKinds['Comment']; value: string } + | { kind: HeadKinds['Literal']; value: string } | AppendPath | AppendExpr - | { kind: HeadKind.Modifier; params: NormalizedParams; hash: Nullable } + | { kind: HeadKinds['Modifier']; params: NormalizedParams; hash: Nullable } | { - kind: HeadKind.DynamicComponent; + kind: HeadKinds['DynamicComponent']; expr: NormalizedExpression; hash: Nullable; block: NormalizedBlock; diff --git a/packages/@glimmer/compiler/lib/builder/builder.ts b/packages/@glimmer/compiler/lib/builder/builder.ts index 80cdb6e2b3..62c9a8e0f0 100644 --- a/packages/@glimmer/compiler/lib/builder/builder.ts +++ b/packages/@glimmer/compiler/lib/builder/builder.ts @@ -37,6 +37,7 @@ import type { NormalizedPath, NormalizedStatement, Variable, + VariableKinds, } from './builder-interface'; import { @@ -671,7 +672,7 @@ export function buildVar( } function getSymbolForVar( - kind: Exclude, + kind: Exclude, symbols: Symbols, name: string ) { diff --git a/packages/@glimmer/debug/index.ts b/packages/@glimmer/debug/index.ts index d8aa97aa18..2835fb2313 100644 --- a/packages/@glimmer/debug/index.ts +++ b/packages/@glimmer/debug/index.ts @@ -23,6 +23,7 @@ export { CheckHandle, CheckInstanceof, CheckInterface, + CheckMachineRegister, CheckMaybe, CheckNode, CheckNumber, @@ -31,8 +32,10 @@ export { CheckOr, CheckPrimitive, CheckProgramSymbolTable, + CheckRegister, CheckSafeString, CheckString, + CheckSyscallRegister, CheckUndefined, CheckUnknown, recordStackSize, diff --git a/packages/@glimmer/debug/lib/debug.ts b/packages/@glimmer/debug/lib/debug.ts index ad2ef20b2e..7177310233 100644 --- a/packages/@glimmer/debug/lib/debug.ts +++ b/packages/@glimmer/debug/lib/debug.ts @@ -7,7 +7,6 @@ import type { RuntimeOp, TemplateCompilationContext, } from '@glimmer/interfaces'; -import type { Register } from '@glimmer/vm'; import { LOCAL_SHOULD_LOG } from '@glimmer/local-debug-flags'; import { decodeHandle, decodeImmediate, enumerate, LOCAL_LOGGER } from '@glimmer/util'; import { $fp, $pc, $ra, $s0, $s1, $sp, $t0, $t1, $v0 } from '@glimmer/vm'; @@ -161,7 +160,7 @@ function opcodeOperand(opcode: RuntimeOp, index: number): number { } } -function decodeRegister(register: Register): string { +function decodeRegister(register: number): string { switch (register) { case $pc: return 'pc'; @@ -181,6 +180,8 @@ function decodeRegister(register: Register): string { return 't1'; case $v0: return 'v0'; + default: + throw new Error(`Unexpected register ${register}`); } } diff --git a/packages/@glimmer/debug/lib/stack-check.ts b/packages/@glimmer/debug/lib/stack-check.ts index a0b81969a3..bd70e69f98 100644 --- a/packages/@glimmer/debug/lib/stack-check.ts +++ b/packages/@glimmer/debug/lib/stack-check.ts @@ -8,7 +8,9 @@ import type { SimpleElement, SimpleNode, } from '@glimmer/interfaces'; +import type { MachineRegister, Register, SyscallRegister } from '@glimmer/vm'; import { LOCAL_DEBUG } from '@glimmer/local-debug-flags'; +import { $fp, $pc, $ra, $s0, $s1, $sp, $t0, $t1, $v0 } from '@glimmer/vm'; export interface Checker { type: T; @@ -307,6 +309,66 @@ export function CheckInstanceof(Class: Constructor): Checker { return new InstanceofChecker(Class); } +export const CheckRegister: Checker = new (class { + declare type: Register; + validate(value: unknown): value is Register { + switch (value) { + case $s0: + case $s1: + case $sp: + case $fp: + case $ra: + case $pc: + case $t0: + case $t1: + case $v0: + return true; + default: + return false; + } + } + expected(): string { + return `Register`; + } +})(); + +export const CheckSyscallRegister: Checker = new (class { + declare type: SyscallRegister; + validate(value: unknown): value is SyscallRegister { + switch (value) { + case $s0: + case $s1: + case $t0: + case $t1: + case $v0: + return true; + default: + return false; + } + } + expected(): string { + return `syscall register ($s0, $s1, $t0, $t1, $v0)`; + } +})(); + +export const CheckMachineRegister: Checker = new (class { + declare type: MachineRegister; + validate(value: unknown): value is MachineRegister { + switch (value) { + case $sp: + case $fp: + case $ra: + case $pc: + return true; + default: + return false; + } + } + expected(): string { + return `machine register ($sp, $fp, $ra, $pc)`; + } +})(); + export function CheckOption(checker: Checker): Checker> { if (!LOCAL_DEBUG) { return new NoopChecker(); diff --git a/packages/@glimmer/runtime/lib/compiled/opcodes/component.ts b/packages/@glimmer/runtime/lib/compiled/opcodes/component.ts index ff57b53717..eb6bc49f7f 100644 --- a/packages/@glimmer/runtime/lib/compiled/opcodes/component.ts +++ b/packages/@glimmer/runtime/lib/compiled/opcodes/component.ts @@ -33,6 +33,7 @@ import { CheckInterface, CheckOr, CheckProgramSymbolTable, + CheckRegister, CheckString, } from '@glimmer/debug'; import { registerDestructor } from '@glimmer/destroyable'; @@ -242,9 +243,9 @@ APPEND_OPCODES.add(Op.CaptureArgs, (vm) => { stack.push(capturedArgs); }); -APPEND_OPCODES.add(Op.PrepareArgs, (vm, { op1: _state }) => { +APPEND_OPCODES.add(Op.PrepareArgs, (vm, { op1: register }) => { let stack = vm.stack; - let instance = vm.fetchValue(_state); + let instance = vm.fetchValue(check(register, CheckRegister)); let args = check(stack.pop(), CheckInstanceof(VMArgumentsImpl)); let { definition } = instance; @@ -344,8 +345,8 @@ APPEND_OPCODES.add(Op.PrepareArgs, (vm, { op1: _state }) => { stack.push(args); }); -APPEND_OPCODES.add(Op.CreateComponent, (vm, { op1: flags, op2: _state }) => { - let instance = check(vm.fetchValue(_state), CheckComponentInstance); +APPEND_OPCODES.add(Op.CreateComponent, (vm, { op1: flags, op2: register }) => { + let instance = check(vm.fetchValue(check(register, CheckRegister)), CheckComponentInstance); let { definition, manager, capabilities } = instance; if (!managerHasCapability(manager, capabilities, InternalComponentCapabilities.createInstance)) { @@ -391,8 +392,11 @@ APPEND_OPCODES.add(Op.CreateComponent, (vm, { op1: flags, op2: _state }) => { } }); -APPEND_OPCODES.add(Op.RegisterComponentDestructor, (vm, { op1: _state }) => { - let { manager, state, capabilities } = check(vm.fetchValue(_state), CheckComponentInstance); +APPEND_OPCODES.add(Op.RegisterComponentDestructor, (vm, { op1: register }) => { + let { manager, state, capabilities } = check( + vm.fetchValue(check(register, CheckRegister)), + CheckComponentInstance + ); let d = manager.getDestroyable(state); @@ -410,11 +414,14 @@ APPEND_OPCODES.add(Op.RegisterComponentDestructor, (vm, { op1: _state }) => { if (d) vm.associateDestroyable(d); }); -APPEND_OPCODES.add(Op.BeginComponentTransaction, (vm, { op1: _state }) => { +APPEND_OPCODES.add(Op.BeginComponentTransaction, (vm, { op1: register }) => { let name; if (import.meta.env.DEV) { - let { definition, manager } = check(vm.fetchValue(_state), CheckComponentInstance); + let { definition, manager } = check( + vm.fetchValue(check(register, CheckRegister)), + CheckComponentInstance + ); name = getDebugName(definition, manager); } @@ -594,8 +601,11 @@ function setDeferredAttr( } } -APPEND_OPCODES.add(Op.DidCreateElement, (vm, { op1: _state }) => { - let { definition, state } = check(vm.fetchValue(_state), CheckComponentInstance); +APPEND_OPCODES.add(Op.DidCreateElement, (vm, { op1: register }) => { + let { definition, state } = check( + vm.fetchValue(check(register, CheckRegister)), + CheckComponentInstance + ); let { manager } = definition; let operations = check(vm.fetchValue($t0), CheckInstanceof(ComponentElementOperations)); @@ -607,14 +617,14 @@ APPEND_OPCODES.add(Op.DidCreateElement, (vm, { op1: _state }) => { ); }); -APPEND_OPCODES.add(Op.GetComponentSelf, (vm, { op1: _state, op2: _names }) => { - let instance = check(vm.fetchValue(_state), CheckComponentInstance); +APPEND_OPCODES.add(Op.GetComponentSelf, (vm, { op1: register, op2: _names }) => { + let instance = check(vm.fetchValue(check(register, CheckRegister)), CheckComponentInstance); let { definition, state } = instance; let { manager } = definition; let selfRef = manager.getSelf(state); if (vm.env.debugRenderTree !== undefined) { - let instance = check(vm.fetchValue(_state), CheckComponentInstance); + let instance = check(vm.fetchValue(check(register, CheckRegister)), CheckComponentInstance); let { definition, manager } = instance; let args: CapturedArguments; @@ -694,8 +704,11 @@ APPEND_OPCODES.add(Op.GetComponentSelf, (vm, { op1: _state, op2: _names }) => { vm.stack.push(selfRef); }); -APPEND_OPCODES.add(Op.GetComponentTagName, (vm, { op1: _state }) => { - let { definition, state } = check(vm.fetchValue(_state), CheckComponentInstance); +APPEND_OPCODES.add(Op.GetComponentTagName, (vm, { op1: register }) => { + let { definition, state } = check( + vm.fetchValue(check(register, CheckRegister)), + CheckComponentInstance + ); let { manager } = definition; let tagName = ( @@ -707,8 +720,8 @@ APPEND_OPCODES.add(Op.GetComponentTagName, (vm, { op1: _state }) => { }); // Dynamic Invocation Only -APPEND_OPCODES.add(Op.GetComponentLayout, (vm, { op1: _state }) => { - let instance = check(vm.fetchValue(_state), CheckComponentInstance); +APPEND_OPCODES.add(Op.GetComponentLayout, (vm, { op1: register }) => { + let instance = check(vm.fetchValue(check(register, CheckRegister)), CheckComponentInstance); let { manager, definition } = instance; let { stack } = vm; @@ -756,25 +769,25 @@ APPEND_OPCODES.add(Op.Main, (vm, { op1: register }) => { lookup: null, }; - vm.loadValue(register, state); + vm.loadValue(check(register, CheckRegister), state); }); -APPEND_OPCODES.add(Op.PopulateLayout, (vm, { op1: _state }) => { +APPEND_OPCODES.add(Op.PopulateLayout, (vm, { op1: register }) => { let { stack } = vm; // In import.meta.env.DEV handles could be ErrHandle objects let handle = check(stack.pop(), CheckHandle); let table = check(stack.pop(), CheckProgramSymbolTable); - let state = check(vm.fetchValue(_state), CheckComponentInstance); + let state = check(vm.fetchValue(check(register, CheckRegister)), CheckComponentInstance); state.handle = handle; state.table = table; }); -APPEND_OPCODES.add(Op.VirtualRootScope, (vm, { op1: _state }) => { +APPEND_OPCODES.add(Op.VirtualRootScope, (vm, { op1: register }) => { let { table, manager, capabilities, state } = check( - vm.fetchValue(_state), + vm.fetchValue(check(register, CheckRegister)), CheckFinishedComponentInstance ); @@ -802,8 +815,8 @@ APPEND_OPCODES.add(Op.VirtualRootScope, (vm, { op1: _state }) => { vm.pushRootScope(table.symbols.length + 1, owner); }); -APPEND_OPCODES.add(Op.SetupForEval, (vm, { op1: _state }) => { - let state = check(vm.fetchValue(_state), CheckFinishedComponentInstance); +APPEND_OPCODES.add(Op.SetupForEval, (vm, { op1: register }) => { + let state = check(vm.fetchValue(check(register, CheckRegister)), CheckFinishedComponentInstance); if (state.table.hasEval) { let lookup = (state.lookup = dict()); @@ -811,8 +824,8 @@ APPEND_OPCODES.add(Op.SetupForEval, (vm, { op1: _state }) => { } }); -APPEND_OPCODES.add(Op.SetNamedVariables, (vm, { op1: _state }) => { - let state = check(vm.fetchValue(_state), CheckFinishedComponentInstance); +APPEND_OPCODES.add(Op.SetNamedVariables, (vm, { op1: register }) => { + let state = check(vm.fetchValue(check(register, CheckRegister)), CheckFinishedComponentInstance); let scope = vm.scope(); let args = check(vm.stack.peek(), CheckArguments); @@ -842,8 +855,8 @@ function bindBlock( if (state.lookup) state.lookup[symbolName] = block; } -APPEND_OPCODES.add(Op.SetBlocks, (vm, { op1: _state }) => { - let state = check(vm.fetchValue(_state), CheckFinishedComponentInstance); +APPEND_OPCODES.add(Op.SetBlocks, (vm, { op1: register }) => { + let state = check(vm.fetchValue(check(register, CheckRegister)), CheckFinishedComponentInstance); let { blocks } = check(vm.stack.peek(), CheckArguments); for (const [i] of enumerate(blocks.names)) { @@ -852,14 +865,14 @@ APPEND_OPCODES.add(Op.SetBlocks, (vm, { op1: _state }) => { }); // Dynamic Invocation Only -APPEND_OPCODES.add(Op.InvokeComponentLayout, (vm, { op1: _state }) => { - let state = check(vm.fetchValue(_state), CheckFinishedComponentInstance); +APPEND_OPCODES.add(Op.InvokeComponentLayout, (vm, { op1: register }) => { + let state = check(vm.fetchValue(check(register, CheckRegister)), CheckFinishedComponentInstance); vm.call(state.handle); }); -APPEND_OPCODES.add(Op.DidRenderLayout, (vm, { op1: _state }) => { - let instance = check(vm.fetchValue(_state), CheckComponentInstance); +APPEND_OPCODES.add(Op.DidRenderLayout, (vm, { op1: register }) => { + let instance = check(vm.fetchValue(check(register, CheckRegister)), CheckComponentInstance); let { manager, state, capabilities } = instance; let bounds = vm.elements().popBlock(); diff --git a/packages/@glimmer/runtime/lib/compiled/opcodes/vm.ts b/packages/@glimmer/runtime/lib/compiled/opcodes/vm.ts index 92d2da111a..f9a34ea782 100644 --- a/packages/@glimmer/runtime/lib/compiled/opcodes/vm.ts +++ b/packages/@glimmer/runtime/lib/compiled/opcodes/vm.ts @@ -9,6 +9,8 @@ import { CheckNumber, CheckOption, CheckPrimitive, + CheckRegister, + CheckSyscallRegister, } from '@glimmer/debug'; import { toBool } from '@glimmer/global-context'; import { @@ -92,7 +94,7 @@ APPEND_OPCODES.add(Op.PrimitiveReference, (vm) => { }); APPEND_OPCODES.add(Op.Dup, (vm, { op1: register, op2: offset }) => { - let position = check(vm.fetchValue(register), CheckNumber) - offset; + let position = check(vm.fetchValue(check(register, CheckRegister)), CheckNumber) - offset; vm.stack.dup(position); }); @@ -101,11 +103,11 @@ APPEND_OPCODES.add(Op.Pop, (vm, { op1: count }) => { }); APPEND_OPCODES.add(Op.Load, (vm, { op1: register }) => { - vm.load(register); + vm.load(check(register, CheckSyscallRegister)); }); APPEND_OPCODES.add(Op.Fetch, (vm, { op1: register }) => { - vm.fetch(register); + vm.fetch(check(register, CheckSyscallRegister)); }); APPEND_OPCODES.add(Op.BindDynamicScope, (vm, { op1: _names }) => { diff --git a/packages/@glimmer/runtime/lib/vm/append.ts b/packages/@glimmer/runtime/lib/vm/append.ts index 20200d4564..93b45e229a 100644 --- a/packages/@glimmer/runtime/lib/vm/append.ts +++ b/packages/@glimmer/runtime/lib/vm/append.ts @@ -25,9 +25,9 @@ import { associateDestroyableChild } from '@glimmer/destroyable'; import { assertGlobalContextWasSet } from '@glimmer/global-context'; import { LOCAL_SHOULD_LOG } from '@glimmer/local-debug-flags'; import { createIteratorItemRef, UNDEFINED_REFERENCE } from '@glimmer/reference'; -import { assert, expect, LOCAL_LOGGER, reverse, Stack, unwrapHandle } from '@glimmer/util'; +import { expect, LOCAL_LOGGER, reverse, Stack, unwrapHandle } from '@glimmer/util'; import { beginTrackFrame, endTrackFrame, resetTracking } from '@glimmer/validator'; -import { $fp, $pc, $s0, $s1, $sp, $t0, $t1, $v0, isLowLevelRegister } from '@glimmer/vm'; +import { $pc, $s0, $s1, $t0, $t1, $v0, isLowLevelRegister } from '@glimmer/vm'; import type { DebugState } from '../opcodes'; import type { LiveBlockList } from './element-builder'; @@ -41,7 +41,6 @@ import { } from '../compiled/opcodes/vm'; import { APPEND_OPCODES } from '../opcodes'; import { PartialScopeImpl } from '../scope'; -import { REGISTERS } from '../symbols'; import { VMArgumentsImpl } from './arguments'; import { LowLevelVM } from './low-level'; import RenderResultImpl from './render-result'; @@ -197,13 +196,7 @@ export class VM implements PublicVM { } this.resume = initVM(context); - let evalStack = EvaluationStackImpl.restore(stack); - - assert(typeof pc === 'number', 'pc is a number'); - - evalStack[REGISTERS][$pc] = pc; - evalStack[REGISTERS][$sp] = stack.length - 1; - evalStack[REGISTERS][$fp] = -1; + let evalStack = EvaluationStackImpl.restore(stack, pc); this.#heap = this.program.heap; this.#constants = this.program.constants; @@ -224,7 +217,7 @@ export class VM implements PublicVM { APPEND_OPCODES.debugAfter(this, state); }, }, - evalStack[REGISTERS] + evalStack.registers ); this.#destructor = {}; diff --git a/packages/@glimmer/runtime/lib/vm/arguments.ts b/packages/@glimmer/runtime/lib/vm/arguments.ts index ce94d4a494..b4047cff43 100644 --- a/packages/@glimmer/runtime/lib/vm/arguments.ts +++ b/packages/@glimmer/runtime/lib/vm/arguments.ts @@ -22,12 +22,10 @@ import { check, CheckBlockSymbolTable, CheckHandle, CheckOption, CheckOr } from import { createDebugAliasRef, UNDEFINED_REFERENCE, valueForRef } from '@glimmer/reference'; import { dict, EMPTY_STRING_ARRAY, emptyArray, enumerate, unwrap } from '@glimmer/util'; import { CONSTANT_TAG } from '@glimmer/validator'; -import { $sp } from '@glimmer/vm'; import type { EvaluationStack } from './stack'; import { CheckCompilableBlock, CheckReference, CheckScope } from '../compiled/opcodes/-debug-strip'; -import { REGISTERS } from '../symbols'; /* The calling convention is: @@ -44,7 +42,7 @@ export class VMArgumentsImpl implements VMArguments { public blocks = new BlockArgumentsImpl(); empty(stack: EvaluationStack): this { - let base = stack[REGISTERS][$sp] + 1; + let base = stack.$sp + 1; this.named.empty(stack, base); this.positional.empty(stack, base); @@ -72,7 +70,7 @@ export class VMArgumentsImpl implements VMArguments { let named = this.named; let namedCount = names.length; - let namedBase = stack[REGISTERS][$sp] - namedCount + 1; + let namedBase = stack.$sp - namedCount + 1; named.setup(stack, namedBase, namedCount, names, atNames); @@ -113,7 +111,7 @@ export class VMArgumentsImpl implements VMArguments { positional.base += offset; named.base += offset; - stack[REGISTERS][$sp] += offset; + stack.$sp += offset; } } diff --git a/packages/@glimmer/runtime/lib/vm/low-level.ts b/packages/@glimmer/runtime/lib/vm/low-level.ts index 219577e30b..30d4eed69f 100644 --- a/packages/@glimmer/runtime/lib/vm/low-level.ts +++ b/packages/@glimmer/runtime/lib/vm/low-level.ts @@ -9,16 +9,20 @@ import type { VM } from './append'; import { APPEND_OPCODES } from '../opcodes'; export interface LowLevelRegisters { - [MachineRegister.pc]: number; - [MachineRegister.ra]: number; - [MachineRegister.sp]: number; - [MachineRegister.fp]: number; + [$pc]: number; + [$ra]: number; + [$sp]: number; + [$fp]: number; } export function initializeRegisters(): LowLevelRegisters { return [0, -1, 0, 0]; } +export function restoreRegisters(pc: number, sp: number): LowLevelRegisters { + return [pc, -1, sp, 0]; +} + export function initializeRegistersWithSP(sp: number): LowLevelRegisters { return [0, -1, sp, 0]; } diff --git a/packages/@glimmer/runtime/lib/vm/stack.ts b/packages/@glimmer/runtime/lib/vm/stack.ts index 0fc3b0b40c..e5164179d5 100644 --- a/packages/@glimmer/runtime/lib/vm/stack.ts +++ b/packages/@glimmer/runtime/lib/vm/stack.ts @@ -1,17 +1,16 @@ -import type { MachineRegister } from '@glimmer/vm'; import { LOCAL_DEBUG } from '@glimmer/local-debug-flags'; -import { $fp, $sp } from '@glimmer/vm'; +import { assert } from '@glimmer/util'; +import { $fp, $pc, $sp } from '@glimmer/vm'; import type { LowLevelRegisters } from './low-level'; -import { REGISTERS } from '../symbols'; import { initializeRegistersWithSP } from './low-level'; export interface EvaluationStack { - [REGISTERS]: LowLevelRegisters; + $sp: number; push(value: unknown): void; - dup(position?: MachineRegister): void; + dup(position?: number): void; copy(from: number, to: number): void; pop(n?: number): T; peek(offset?: number): T; @@ -24,30 +23,50 @@ export interface EvaluationStack { } export default class EvaluationStackImpl implements EvaluationStack { - static restore(snapshot: unknown[]): EvaluationStackImpl { - return new this(snapshot.slice(), initializeRegistersWithSP(snapshot.length - 1)); + static restore(snapshot: unknown[], pc: number): EvaluationStackImpl { + const stack = new this(snapshot.slice(), initializeRegistersWithSP(snapshot.length - 1)); + + assert(typeof pc === 'number', 'pc is a number'); + + stack.registers[$pc] = pc; + stack.registers[$sp] = snapshot.length - 1; + stack.registers[$fp] = -1; + + return stack; } - readonly [REGISTERS]: LowLevelRegisters; + readonly #registers: LowLevelRegisters; // fp -> sp constructor( private stack: unknown[] = [], registers: LowLevelRegisters ) { - this[REGISTERS] = registers; + this.#registers = registers; if (LOCAL_DEBUG) { Object.seal(this); } } + get registers(): LowLevelRegisters { + return this.#registers; + } + + get $sp(): number { + return this.#registers[$sp]; + } + + set $sp(sp: number) { + this.#registers[$sp] = sp; + } + push(value: unknown): void { - this.stack[++this[REGISTERS][$sp]] = value; + this.stack[++this.$sp] = value; } - dup(position = this[REGISTERS][$sp]): void { - this.stack[++this[REGISTERS][$sp]] = this.stack[position]; + dup(position = this.$sp): void { + this.stack[++this.$sp] = this.stack[position]; } copy(from: number, to: number): void { @@ -55,20 +74,20 @@ export default class EvaluationStackImpl implements EvaluationStack { } pop(n = 1): T { - let top = this.stack[this[REGISTERS][$sp]] as T; - this[REGISTERS][$sp] -= n; + let top = this.stack[this.$sp] as T; + this.$sp -= n; return top; } peek(offset = 0): T { - return this.stack[this[REGISTERS][$sp] - offset] as T; + return this.stack[this.$sp - offset] as T; } - get(offset: number, base = this[REGISTERS][$fp]): T { + get(offset: number, base = this.#registers[$fp]): T { return this.stack[base + offset] as T; } - set(value: unknown, offset: number, base = this[REGISTERS][$fp]) { + set(value: unknown, offset: number, base = this.#registers[$fp]) { this.stack[base + offset] = value; } @@ -77,7 +96,7 @@ export default class EvaluationStackImpl implements EvaluationStack { } capture(items: number): unknown[] { - let end = this[REGISTERS][$sp] + 1; + let end = this.$sp + 1; let start = end - items; return this.stack.slice(start, end); } @@ -87,6 +106,6 @@ export default class EvaluationStackImpl implements EvaluationStack { } toArray() { - return this.stack.slice(this[REGISTERS][$fp], this[REGISTERS][$sp] + 1); + return this.stack.slice(this.#registers[$fp], this.#registers[$sp] + 1); } } diff --git a/packages/@glimmer/util/lib/immediate.ts b/packages/@glimmer/util/lib/immediate.ts index 692c4337a7..2103e64062 100644 --- a/packages/@glimmer/util/lib/immediate.ts +++ b/packages/@glimmer/util/lib/immediate.ts @@ -38,30 +38,28 @@ import { debugAssert as assert } from './assert'; strategy. */ -export enum ImmediateConstants { - MAX_SMI = 2 ** 30 - 1, - MIN_SMI = ~MAX_SMI, - SIGN_BIT = ~(2 ** 29), - MAX_INT = ~SIGN_BIT - 1, - MIN_INT = ~MAX_INT, - - FALSE_HANDLE = 0, - TRUE_HANDLE = 1, - NULL_HANDLE = 2, - UNDEFINED_HANDLE = 3, - - ENCODED_FALSE_HANDLE = FALSE_HANDLE, - ENCODED_TRUE_HANDLE = TRUE_HANDLE, - ENCODED_NULL_HANDLE = NULL_HANDLE, - ENCODED_UNDEFINED_HANDLE = UNDEFINED_HANDLE, -} +export const MAX_SMI = 2 ** 30 - 1; +export const MIN_SMI = ~MAX_SMI; +export const SIGN_BIT = ~(2 ** 29); +export const MAX_INT = ~SIGN_BIT - 1; +export const MIN_INT = ~MAX_INT; + +export const FALSE_HANDLE = 0; +export const TRUE_HANDLE = 1; +export const NULL_HANDLE = 2; +export const UNDEFINED_HANDLE = 3; + +export const ENCODED_FALSE_HANDLE = FALSE_HANDLE; +export const ENCODED_TRUE_HANDLE = TRUE_HANDLE; +export const ENCODED_NULL_HANDLE = NULL_HANDLE; +export const ENCODED_UNDEFINED_HANDLE = UNDEFINED_HANDLE; export function isHandle(value: number) { return value >= 0; } export function isNonPrimitiveHandle(value: number) { - return value > ImmediateConstants.ENCODED_UNDEFINED_HANDLE; + return value > ENCODED_UNDEFINED_HANDLE; } export function constants(...values: unknown[]): unknown[] { @@ -69,39 +67,28 @@ export function constants(...values: unknown[]): unknown[] { } export function isSmallInt(value: number) { - return ( - value % 1 === 0 && value <= ImmediateConstants.MAX_INT && value >= ImmediateConstants.MIN_INT - ); + return value % 1 === 0 && value <= MAX_INT && value >= MIN_INT; } export function encodeNegative(num: number) { if (LOCAL_DEBUG) { - assert( - num % 1 === 0 && num >= ImmediateConstants.MIN_INT && num < 0, - `Could not encode negative: ${num}` - ); + assert(num % 1 === 0 && num >= MIN_INT && num < 0, `Could not encode negative: ${num}`); } - return num & ImmediateConstants.SIGN_BIT; + return num & SIGN_BIT; } export function decodeNegative(num: number) { if (LOCAL_DEBUG) { - assert( - num % 1 === 0 && num < ~ImmediateConstants.MAX_INT && num >= ImmediateConstants.MIN_SMI, - `Could not decode negative: ${num}` - ); + assert(num % 1 === 0 && num < ~MAX_INT && num >= MIN_SMI, `Could not decode negative: ${num}`); } - return num | ~ImmediateConstants.SIGN_BIT; + return num | ~SIGN_BIT; } export function encodePositive(num: number) { if (LOCAL_DEBUG) { - assert( - num % 1 === 0 && num >= 0 && num <= ImmediateConstants.MAX_INT, - `Could not encode positive: ${num}` - ); + assert(num % 1 === 0 && num >= 0 && num <= MAX_INT, `Could not encode positive: ${num}`); } return ~num; @@ -109,10 +96,7 @@ export function encodePositive(num: number) { export function decodePositive(num: number) { if (LOCAL_DEBUG) { - assert( - num % 1 === 0 && num <= 0 && num >= ~ImmediateConstants.MAX_INT, - `Could not decode positive: ${num}` - ); + assert(num % 1 === 0 && num <= 0 && num >= ~MAX_INT, `Could not decode positive: ${num}`); } return ~num; @@ -120,10 +104,7 @@ export function decodePositive(num: number) { export function encodeHandle(num: number) { if (LOCAL_DEBUG) { - assert( - num % 1 === 0 && num >= 0 && num <= ImmediateConstants.MAX_SMI, - `Could not encode handle: ${num}` - ); + assert(num % 1 === 0 && num >= 0 && num <= MAX_SMI, `Could not encode handle: ${num}`); } return num; @@ -131,10 +112,7 @@ export function encodeHandle(num: number) { export function decodeHandle(num: number) { if (LOCAL_DEBUG) { - assert( - num % 1 === 0 && num <= ImmediateConstants.MAX_SMI && num >= 0, - `Could not decode handle: ${num}` - ); + assert(num % 1 === 0 && num <= MAX_SMI && num >= 0, `Could not decode handle: ${num}`); } return num; @@ -147,7 +125,7 @@ export function encodeImmediate(num: number) { export function decodeImmediate(num: number) { num |= 0; - return num > ImmediateConstants.SIGN_BIT ? decodePositive(num) : decodeNegative(num); + return num > SIGN_BIT ? decodePositive(num) : decodeNegative(num); } // Warm diff --git a/packages/@glimmer/util/test/immediate-test.ts b/packages/@glimmer/util/test/immediate-test.ts index 43f6d6e8b2..efea9c3d60 100644 --- a/packages/@glimmer/util/test/immediate-test.ts +++ b/packages/@glimmer/util/test/immediate-test.ts @@ -1,16 +1,23 @@ -import { decodeImmediate, encodeImmediate, ImmediateConstants } from '@glimmer/util'; +import { + decodeImmediate, + encodeImmediate, + MAX_INT, + MAX_SMI, + MIN_INT, + MIN_SMI, +} from '@glimmer/util'; const { module, test } = QUnit; module('immediate encoding tests', () => { test('it works', (assert) => { - let cases = [ImmediateConstants.MIN_INT, -1, 0, ImmediateConstants.MAX_INT]; + let cases = [MIN_INT, -1, 0, MAX_INT]; for (const val of cases) { let encoded = encodeImmediate(val); assert.strictEqual(val, decodeImmediate(encoded), 'correctly encoded and decoded'); - const isSMI = encoded >= ImmediateConstants.MIN_SMI && encoded <= ImmediateConstants.MAX_SMI; + const isSMI = encoded >= MIN_SMI && encoded <= MAX_SMI; assert.true(isSMI, 'encoded as an SMI'); assert.step(`testing ${val}`); } diff --git a/packages/@glimmer/vm/index.ts b/packages/@glimmer/vm/index.ts index 3c9d22144a..25f74bed96 100644 --- a/packages/@glimmer/vm/index.ts +++ b/packages/@glimmer/vm/index.ts @@ -14,20 +14,6 @@ export { TYPE_SIZE, } from './lib/flags'; export { isMachineOp, isOp, MachineOp, Op } from './lib/opcodes'; -export { - $fp, - $pc, - $ra, - $s0, - $s1, - $sp, - $t0, - $t1, - $v0, - isLowLevelRegister, - MachineRegister, - type Register, - SavedRegister, - type SyscallRegister, - TemporaryRegister, -} from './lib/registers'; +export type { MachineRegister, Register, SyscallRegister } from './lib/registers'; +export type { SavedRegister, TemporaryRegister } from './lib/registers'; +export { $fp, $pc, $ra, $s0, $s1, $sp, $t0, $t1, $v0, isLowLevelRegister } from './lib/registers'; diff --git a/packages/@glimmer/vm/lib/registers.ts b/packages/@glimmer/vm/lib/registers.ts index 14257d23a6..51adb04829 100644 --- a/packages/@glimmer/vm/lib/registers.ts +++ b/packages/@glimmer/vm/lib/registers.ts @@ -6,31 +6,32 @@ */ // $0 or $pc (program counter): pointer into `program` for the next insturction; -1 means exit -export const $pc: MachineRegister.pc = 0; +export type $pc = 0; +export const $pc: $pc = 0; // $1 or $ra (return address): pointer into `program` for the return -export const $ra: MachineRegister.ra = 1; +export type $ra = 1; +export const $ra: $ra = 1; // $2 or $fp (frame pointer): pointer into the `evalStack` for the base of the stack -export const $fp: MachineRegister.fp = 2; +export type $fp = 2; +export const $fp: $fp = 2; // $3 or $sp (stack pointer): pointer into the `evalStack` for the top of the stack -export const $sp: MachineRegister.sp = 3; +export type $sp = 3; +export const $sp: $sp = 3; // $4-$5 or $s0-$s1 (saved): callee saved general-purpose registers -export const $s0: SavedRegister.s0 = 4; -export const $s1: SavedRegister.s1 = 5; +export type $s0 = 4; +export const $s0: $s0 = 4; +export type $s1 = 5; +export const $s1: $s1 = 5; // $6-$7 or $t0-$t1 (temporaries): caller saved general-purpose registers -export const $t0: TemporaryRegister.t0 = 6; -export const $t1: TemporaryRegister.t1 = 7; +export type $t0 = 6; +export const $t0: $t0 = 6; +export type $t1 = 7; +export const $t1: $t1 = 7; // $8 or $v0 (return value) +export type $v0 = 8; export const $v0 = 8; -export enum MachineRegister { - // These must be in sync with the computed values - // above, but TypeScript doesn't like it - - 'pc' = 0, - 'ra' = 1, - 'fp' = 2, - 'sp' = 3, -} +export type MachineRegister = $pc | $ra | $fp | $sp; export function isLowLevelRegister( register: Register | MachineRegister @@ -38,15 +39,8 @@ export function isLowLevelRegister( return (register as number) <= $sp; } -export enum SavedRegister { - 's0' = 4, - 's1' = 5, -} - -export enum TemporaryRegister { - 't0' = 6, - 't1' = 7, -} +export type SavedRegister = $s0 | $s1; +export type TemporaryRegister = $t0 | $t1; -export type Register = MachineRegister | SavedRegister | TemporaryRegister | typeof $v0; -export type SyscallRegister = SavedRegister | TemporaryRegister | typeof $v0; +export type Register = MachineRegister | SavedRegister | TemporaryRegister | $v0; +export type SyscallRegister = SavedRegister | TemporaryRegister | $v0; From 9f84b28043cf1a3bfd13cd6352792082402ebaf7 Mon Sep 17 00:00:00 2001 From: Yehuda Katz Date: Wed, 30 Oct 2024 09:30:56 -0700 Subject: [PATCH 3/3] Remove enums Historically, many of these enums used `const enum`s, which at least had the benefit of being compiled away at build time. We've now moved to mostly normal `enum`s, which are both less efficient than normal constants, and a relic of an earlier TypeScript era before literal types. This PR moves all enums to normal constants, and creates types for the constant values when the code needs them. --- packages/@glimmer/compiler/index.ts | 3 +- .../compiler/lib/builder/builder-interface.ts | 289 +++++++++--------- .../@glimmer/compiler/lib/builder/builder.ts | 103 ++++--- .../compiler/lib/builder/constants.ts | 127 ++++++++ .../1-normalization/visitors/constants.ts | 23 ++ .../1-normalization/visitors/strict-mode.ts | 44 +-- .../@glimmer/compiler/test/compiler-test.ts | 7 +- packages/@glimmer/destroyable/index.ts | 27 +- .../@glimmer/interfaces/lib/serialize.d.ts | 7 - packages/@glimmer/program/lib/program.ts | 26 +- .../runtime/lib/compat/svg-inner-html-fix.ts | 7 - .../@glimmer/syntax/lib/generation/util.ts | 14 +- .../@glimmer/syntax/lib/source/loc/kinds.ts | 70 +++-- .../@glimmer/syntax/lib/source/loc/match.ts | 25 +- .../@glimmer/syntax/lib/source/loc/offset.ts | 27 +- .../@glimmer/syntax/lib/source/loc/span.ts | 82 ++--- .../syntax/lib/v2/objects/constants.ts | 10 + .../syntax/lib/v2/objects/resolution.ts | 34 +-- 18 files changed, 549 insertions(+), 376 deletions(-) create mode 100644 packages/@glimmer/compiler/lib/builder/constants.ts create mode 100644 packages/@glimmer/compiler/lib/passes/1-normalization/visitors/constants.ts create mode 100644 packages/@glimmer/syntax/lib/v2/objects/constants.ts diff --git a/packages/@glimmer/compiler/index.ts b/packages/@glimmer/compiler/index.ts index b4a5d141f4..92fb63337c 100644 --- a/packages/@glimmer/compiler/index.ts +++ b/packages/@glimmer/compiler/index.ts @@ -7,7 +7,8 @@ export { s, unicode, } from './lib/builder/builder'; -export { Builder, type BuilderStatement } from './lib/builder/builder-interface'; +export { type BuilderStatement } from './lib/builder/builder-interface'; +export * from './lib/builder/constants'; export { defaultId, precompile, precompileJSON, type PrecompileOptions } from './lib/compiler'; // exported only for tests! diff --git a/packages/@glimmer/compiler/lib/builder/builder-interface.ts b/packages/@glimmer/compiler/lib/builder/builder-interface.ts index a4681a9742..c97bd462b8 100644 --- a/packages/@glimmer/compiler/lib/builder/builder-interface.ts +++ b/packages/@glimmer/compiler/lib/builder/builder-interface.ts @@ -1,6 +1,43 @@ import type { Dict, DictValue, Nullable, PresentArray } from '@glimmer/interfaces'; import { assertNever, dict, expect, isPresentArray } from '@glimmer/util'; +import type { VariableKind } from './constants'; + +import { + APPEND_EXPR_HEAD, + APPEND_PATH_HEAD, + ARG_VAR, + BLOCK_HEAD, + BLOCK_VAR, + BUILDER_APPEND, + BUILDER_COMMENT, + BUILDER_CONCAT, + BUILDER_DYNAMIC_COMPONENT, + BUILDER_GET, + BUILDER_HAS_BLOCK, + BUILDER_HAS_BLOCK_PARAMS, + BUILDER_LITERAL, + BUILDER_MODIFIER, + CALL_EXPR, + CALL_HEAD, + COMMENT_HEAD, + CONCAT_EXPR, + DYNAMIC_COMPONENT_HEAD, + ELEMENT_HEAD, + FREE_VAR, + GET_PATH_EXPR, + GET_VAR_EXPR, + HAS_BLOCK_EXPR, + HAS_BLOCK_PARAMS_EXPR, + KEYWORD_HEAD, + LITERAL_EXPR, + LITERAL_HEAD, + LOCAL_VAR, + MODIFIER_HEAD, + SPLAT_HEAD, + THIS_VAR, +} from './constants'; + export type BuilderParams = BuilderExpression[]; export type BuilderHash = Nullable>; export type BuilderBlockHash = BuilderHash | { as: string | string[] }; @@ -12,7 +49,7 @@ export type NormalizedHash = Dict; export type NormalizedBlock = NormalizedStatement[]; export type NormalizedBlocks = Dict; export type NormalizedAttrs = Dict; -export type NormalizedAttr = HeadKinds['Splat'] | NormalizedExpression; +export type NormalizedAttr = SPLAT_HEAD | NormalizedExpression; export interface NormalizedElement { name: string; @@ -26,34 +63,6 @@ export interface NormalizedAngleInvocation { block: Nullable; } -export const HeadKind = { - Block: 'Block', - Call: 'Call', - Element: 'Element', - AppendPath: 'AppendPath', - AppendExpr: 'AppendExpr', - Literal: 'Literal', - Modifier: 'Modifier', - DynamicComponent: 'DynamicComponent', - Comment: 'Comment', - Splat: 'Splat', - Keyword: 'Keyword', -} as const; - -export type HeadKinds = typeof HeadKind; -export type HeadKind = keyof HeadKinds; - -export const VariableKind = { - Local: 'Local', - Free: 'Free', - Arg: 'Arg', - Block: 'Block', - This: 'This', -} as const; - -export type VariableKinds = typeof VariableKind; -export type VariableKind = keyof VariableKinds; - export interface Variable { kind: VariableKind; name: string; @@ -73,19 +82,19 @@ export interface Path { } export interface AppendExpr { - kind: HeadKinds['AppendExpr']; + kind: APPEND_EXPR_HEAD; expr: NormalizedExpression; trusted: boolean; } export interface AppendPath { - kind: HeadKinds['AppendPath']; + kind: APPEND_PATH_HEAD; path: NormalizedPath; trusted: boolean; } export interface NormalizedKeywordStatement { - kind: HeadKinds['Keyword']; + kind: KEYWORD_HEAD; name: string; params: Nullable; hash: Nullable; @@ -95,14 +104,14 @@ export interface NormalizedKeywordStatement { export type NormalizedStatement = | { - kind: HeadKinds['Call']; + kind: CALL_HEAD; head: NormalizedHead; params: Nullable; hash: Nullable; trusted: boolean; } | { - kind: HeadKinds['Block']; + kind: BLOCK_HEAD; head: NormalizedHead; params: Nullable; hash: Nullable; @@ -111,18 +120,18 @@ export type NormalizedStatement = } | NormalizedKeywordStatement | { - kind: HeadKinds['Element']; + kind: ELEMENT_HEAD; name: string; attrs: NormalizedAttrs; block: NormalizedBlock; } - | { kind: HeadKinds['Comment']; value: string } - | { kind: HeadKinds['Literal']; value: string } + | { kind: COMMENT_HEAD; value: string } + | { kind: LITERAL_HEAD; value: string } | AppendPath | AppendExpr - | { kind: HeadKinds['Modifier']; params: NormalizedParams; hash: Nullable } + | { kind: MODIFIER_HEAD; params: NormalizedParams; hash: Nullable } | { - kind: HeadKinds['DynamicComponent']; + kind: DYNAMIC_COMPONENT_HEAD; expr: NormalizedExpression; hash: Nullable; block: NormalizedBlock; @@ -148,15 +157,15 @@ export function normalizeAppendHead( head: NormalizedHead, trusted: boolean ): AppendExpr | AppendPath { - if (head.type === ExpressionKind.GetPath) { + if (head.type === GET_PATH_EXPR) { return { - kind: HeadKind.AppendPath, + kind: APPEND_PATH_HEAD, path: head, trusted, }; } else { return { - kind: HeadKind.AppendExpr, + kind: APPEND_EXPR_HEAD, expr: head, trusted, }; @@ -203,7 +212,7 @@ export function normalizeSugaryArrayStatement( } return { - kind: HeadKind.Call, + kind: CALL_HEAD, head: normalizeCallHead(name), params, hash, @@ -221,7 +230,7 @@ export function normalizeSugaryArrayStatement( } = normalizeBuilderBlockStatement(statement as BuilderBlockStatement); return { - kind: HeadKind.Block, + kind: BLOCK_HEAD, head: path, params, hash, @@ -237,7 +246,7 @@ export function normalizeSugaryArrayStatement( ); return { - kind: HeadKind.Keyword, + kind: KEYWORD_HEAD, name, params, hash, @@ -262,7 +271,7 @@ export function normalizeSugaryArrayStatement( } return { - kind: HeadKind.Element, + kind: ELEMENT_HEAD, name: expect(extractElement(name), `BUG: expected ${name} to look like a tag name`), attrs, block, @@ -276,37 +285,37 @@ export function normalizeSugaryArrayStatement( function normalizeVerboseStatement(statement: VerboseStatement): NormalizedStatement { switch (statement[0]) { - case Builder.Literal: { + case BUILDER_LITERAL: { return { - kind: HeadKind.Literal, + kind: LITERAL_HEAD, value: statement[1], }; } - case Builder.Append: { + case BUILDER_APPEND: { return normalizeAppendExpression(statement[1], statement[2]); } - case Builder.Modifier: { + case BUILDER_MODIFIER: { return { - kind: HeadKind.Modifier, + kind: MODIFIER_HEAD, params: normalizeParams(statement[1]), hash: normalizeHash(statement[2]), }; } - case Builder.DynamicComponent: { + case BUILDER_DYNAMIC_COMPONENT: { return { - kind: HeadKind.DynamicComponent, + kind: DYNAMIC_COMPONENT_HEAD, expr: normalizeExpression(statement[1]), hash: normalizeHash(statement[2]), block: normalizeBlock(statement[3]), }; } - case Builder.Comment: { + case BUILDER_COMMENT: { return { - kind: HeadKind.Comment, + kind: COMMENT_HEAD, value: statement[1], }; } @@ -338,7 +347,7 @@ function normalizePath(head: string, tail: string[] = []): NormalizedHead { if (isPresentArray(tail)) { return { - type: ExpressionKind.GetPath, + type: GET_PATH_EXPR, path: { head: pathHead, tail, @@ -346,7 +355,7 @@ function normalizePath(head: string, tail: string[] = []): NormalizedHead { }; } else { return { - type: ExpressionKind.GetVar, + type: GET_VAR_EXPR, variable: pathHead, }; } @@ -360,9 +369,9 @@ function normalizeDottedPath(whole: string): NormalizedHead { const variable: Variable = { kind, name, mode: 'loose' }; if (isPresentArray(tail)) { - return { type: ExpressionKind.GetPath, path: { head: variable, tail } }; + return { type: GET_PATH_EXPR, path: { head: variable, tail } }; } else { - return { type: ExpressionKind.GetVar, variable }; + return { type: GET_VAR_EXPR, variable }; } } @@ -372,7 +381,7 @@ export function normalizePathHead(whole: string): Variable { if (/^this(?:\.|$)/u.test(whole)) { return { - kind: VariableKind.This, + kind: THIS_VAR, name: whole, mode: 'loose', }; @@ -380,22 +389,22 @@ export function normalizePathHead(whole: string): Variable { switch (whole[0]) { case '^': - kind = VariableKind.Free; + kind = FREE_VAR; name = whole.slice(1); break; case '@': - kind = VariableKind.Arg; + kind = ARG_VAR; name = whole.slice(1); break; case '&': - kind = VariableKind.Block; + kind = BLOCK_VAR; name = whole.slice(1); break; default: - kind = VariableKind.Local; + kind = LOCAL_VAR; name = whole; } @@ -500,7 +509,7 @@ function normalizeAttrs(attrs: BuilderAttrs): NormalizedAttrs { function normalizeAttr(attr: BuilderAttr): { expr: NormalizedAttr; trusted: boolean } { if (attr === 'splat') { - return { expr: HeadKind.Splat, trusted: false }; + return { expr: SPLAT_HEAD, trusted: false }; } else { const expr = normalizeExpression(attr); return { expr, trusted: false }; @@ -526,7 +535,7 @@ export type BuilderElement = | [string, BuilderBlock] | [string, BuilderAttrs]; -export type BuilderComment = [Builder.Comment, string]; +export type BuilderComment = [BUILDER_COMMENT, string]; export type InvocationElement = | [string] @@ -560,25 +569,13 @@ export function isBlock(input: [string, ...unknown[]]): input is BuilderBlockSta return !!match && !!match[1]; } -export enum Builder { - Literal, - Comment, - Append, - Modifier, - DynamicComponent, - Get, - Concat, - HasBlock, - HasBlockParams, -} - export type VerboseStatement = - | [Builder.Literal, string] - | [Builder.Comment, string] - | [Builder.Append, BuilderExpression, true] - | [Builder.Append, BuilderExpression] - | [Builder.Modifier, Params, Hash] - | [Builder.DynamicComponent, BuilderExpression, Hash, BuilderBlock]; + | [BUILDER_LITERAL, string] + | [BUILDER_COMMENT, string] + | [BUILDER_APPEND, BuilderExpression, true] + | [BUILDER_APPEND, BuilderExpression] + | [BUILDER_MODIFIER, Params, Hash] + | [BUILDER_DYNAMIC_COMPONENT, BuilderExpression, Hash, BuilderBlock]; export type BuilderStatement = | VerboseStatement @@ -589,54 +586,44 @@ export type BuilderStatement = export type BuilderAttr = 'splat' | BuilderExpression; export type TupleBuilderExpression = - | [Builder.Literal, string | boolean | null | undefined] - | [Builder.Get, string] - | [Builder.Get, string, string[]] - | [Builder.Concat, ...BuilderExpression[]] - | [Builder.HasBlock, string] - | [Builder.HasBlockParams, string] + | [BUILDER_LITERAL, string | boolean | null | undefined] + | [BUILDER_GET, string] + | [BUILDER_GET, string, string[]] + | [BUILDER_CONCAT, ...BuilderExpression[]] + | [BUILDER_HAS_BLOCK, string] + | [BUILDER_HAS_BLOCK_PARAMS, string] | BuilderCallExpression; type Params = BuilderParams; type Hash = Dict; -export enum ExpressionKind { - Literal = 'Literal', - Call = 'Call', - GetPath = 'GetPath', - GetVar = 'GetVar', - Concat = 'Concat', - HasBlock = 'HasBlock', - HasBlockParams = 'HasBlockParams', -} - export interface NormalizedCallExpression { - type: ExpressionKind.Call; + type: CALL_EXPR; head: NormalizedHead; params: Nullable; hash: Nullable; } export interface NormalizedPath { - type: ExpressionKind.GetPath; + type: GET_PATH_EXPR; path: Path; } export interface NormalizedVar { - type: ExpressionKind.GetVar; + type: GET_VAR_EXPR; variable: Variable; } export type NormalizedHead = NormalizedPath | NormalizedVar; export interface NormalizedConcat { - type: ExpressionKind.Concat; + type: CONCAT_EXPR; params: [NormalizedExpression, ...NormalizedExpression[]]; } export type NormalizedExpression = | { - type: ExpressionKind.Literal; + type: LITERAL_EXPR; value: null | undefined | boolean | string | number; } | NormalizedCallExpression @@ -644,11 +631,11 @@ export type NormalizedExpression = | NormalizedVar | NormalizedConcat | { - type: ExpressionKind.HasBlock; + type: HAS_BLOCK_EXPR; name: string; } | { - type: ExpressionKind.HasBlockParams; + type: HAS_BLOCK_PARAMS_EXPR; name: string; }; @@ -659,27 +646,27 @@ export function normalizeAppendExpression( if (expression === null || expression === undefined) { return { expr: { - type: ExpressionKind.Literal, + type: LITERAL_EXPR, value: expression, }, - kind: HeadKind.AppendExpr, + kind: APPEND_EXPR_HEAD, trusted: false, }; } else if (Array.isArray(expression)) { switch (expression[0]) { - case Builder.Literal: + case BUILDER_LITERAL: return { - expr: { type: ExpressionKind.Literal, value: expression[1] }, - kind: HeadKind.AppendExpr, + expr: { type: LITERAL_EXPR, value: expression[1] }, + kind: APPEND_EXPR_HEAD, trusted: false, }; - case Builder.Get: { + case BUILDER_GET: { return normalizeAppendHead(normalizePath(expression[1], expression[2]), forceTrusted); } - case Builder.Concat: { + case BUILDER_CONCAT: { const expr: NormalizedConcat = { - type: ExpressionKind.Concat, + type: CONCAT_EXPR, params: normalizeParams(expression.slice(1)) as [ NormalizedExpression, ...NormalizedExpression[], @@ -688,28 +675,28 @@ export function normalizeAppendExpression( return { expr, - kind: HeadKind.AppendExpr, + kind: APPEND_EXPR_HEAD, trusted: forceTrusted, }; } - case Builder.HasBlock: + case BUILDER_HAS_BLOCK: return { expr: { - type: ExpressionKind.HasBlock, + type: HAS_BLOCK_EXPR, name: expression[1], }, - kind: HeadKind.AppendExpr, + kind: APPEND_EXPR_HEAD, trusted: forceTrusted, }; - case Builder.HasBlockParams: + case BUILDER_HAS_BLOCK_PARAMS: return { expr: { - type: ExpressionKind.HasBlockParams, + type: HAS_BLOCK_PARAMS_EXPR, name: expression[1], }, - kind: HeadKind.AppendExpr, + kind: APPEND_EXPR_HEAD, trusted: forceTrusted, }; @@ -717,7 +704,7 @@ export function normalizeAppendExpression( if (isBuilderCallExpression(expression)) { return { expr: normalizeCallExpression(expression), - kind: HeadKind.AppendExpr, + kind: APPEND_EXPR_HEAD, trusted: forceTrusted, }; } else { @@ -738,8 +725,8 @@ export function normalizeAppendExpression( case 'boolean': case 'number': return { - expr: { type: ExpressionKind.Literal, value: expression }, - kind: HeadKind.AppendExpr, + expr: { type: LITERAL_EXPR, value: expression }, + kind: APPEND_EXPR_HEAD, trusted: true, }; @@ -754,20 +741,20 @@ export function normalizeAppendExpression( export function normalizeExpression(expression: BuilderExpression): NormalizedExpression { if (expression === null || expression === undefined) { return { - type: ExpressionKind.Literal, + type: LITERAL_EXPR, value: expression, }; } else if (Array.isArray(expression)) { switch (expression[0]) { - case Builder.Literal: - return { type: ExpressionKind.Literal, value: expression[1] }; + case BUILDER_LITERAL: + return { type: LITERAL_EXPR, value: expression[1] }; - case Builder.Get: { + case BUILDER_GET: { return normalizePath(expression[1], expression[2]); } - case Builder.Concat: { + case BUILDER_CONCAT: { const expr: NormalizedConcat = { - type: ExpressionKind.Concat, + type: CONCAT_EXPR, params: normalizeParams(expression.slice(1)) as [ NormalizedExpression, ...NormalizedExpression[], @@ -777,15 +764,15 @@ export function normalizeExpression(expression: BuilderExpression): NormalizedEx return expr; } - case Builder.HasBlock: + case BUILDER_HAS_BLOCK: return { - type: ExpressionKind.HasBlock, + type: HAS_BLOCK_EXPR, name: expression[1], }; - case Builder.HasBlockParams: + case BUILDER_HAS_BLOCK_PARAMS: return { - type: ExpressionKind.HasBlockParams, + type: HAS_BLOCK_PARAMS_EXPR, name: expression[1], }; @@ -809,7 +796,7 @@ export function normalizeExpression(expression: BuilderExpression): NormalizedEx } case 'boolean': case 'number': - return { type: ExpressionKind.Literal, value: expression }; + return { type: LITERAL_EXPR, value: expression }; default: throw assertNever(expression); @@ -819,11 +806,11 @@ export function normalizeExpression(expression: BuilderExpression): NormalizedEx } } -// | [Builder.Get, string] -// | [Builder.Get, string, string[]] -// | [Builder.Concat, Params] -// | [Builder.HasBlock, string] -// | [Builder.HasBlockParams, string] +// | [GET, string] +// | [GET, string, string[]] +// | [CONCAT, Params] +// | [HAS_BLOCK, string] +// | [HAS_BLOCK_PARAMS, string] export type BuilderExpression = | TupleBuilderExpression @@ -842,7 +829,7 @@ export function isBuilderExpression( export function isLiteral( expr: BuilderExpression | BuilderCallExpression -): expr is [Builder.Literal, string | boolean | undefined] { +): expr is [BUILDER_LITERAL, string | boolean | undefined] { return Array.isArray(expr) && expr[0] === 'literal'; } @@ -857,11 +844,11 @@ export function statementIsExpression( if (typeof name === 'number') { switch (name) { - case Builder.Literal: - case Builder.Get: - case Builder.Concat: - case Builder.HasBlock: - case Builder.HasBlockParams: + case BUILDER_LITERAL: + case BUILDER_GET: + case BUILDER_CONCAT: + case BUILDER_HAS_BLOCK: + case BUILDER_HAS_BLOCK_PARAMS: return true; default: return false; @@ -900,7 +887,7 @@ export function normalizeCallExpression(expr: BuilderCallExpression): Normalized switch (expr.length) { case 1: return { - type: ExpressionKind.Call, + type: CALL_EXPR, head: normalizeCallHead(expr[0]), params: null, hash: null, @@ -908,14 +895,14 @@ export function normalizeCallExpression(expr: BuilderCallExpression): Normalized case 2: { if (Array.isArray(expr[1])) { return { - type: ExpressionKind.Call, + type: CALL_EXPR, head: normalizeCallHead(expr[0]), params: normalizeParams(expr[1]), hash: null, }; } else { return { - type: ExpressionKind.Call, + type: CALL_EXPR, head: normalizeCallHead(expr[0]), params: null, hash: normalizeHash(expr[1]), @@ -925,7 +912,7 @@ export function normalizeCallExpression(expr: BuilderCallExpression): Normalized case 3: return { - type: ExpressionKind.Call, + type: CALL_EXPR, head: normalizeCallHead(expr[0]), params: normalizeParams(expr[1]), hash: normalizeHash(expr[2]), diff --git a/packages/@glimmer/compiler/lib/builder/builder.ts b/packages/@glimmer/compiler/lib/builder/builder.ts index 62c9a8e0f0..a082dcccdf 100644 --- a/packages/@glimmer/compiler/lib/builder/builder.ts +++ b/packages/@glimmer/compiler/lib/builder/builder.ts @@ -37,16 +37,37 @@ import type { NormalizedPath, NormalizedStatement, Variable, - VariableKinds, } from './builder-interface'; +import type { VariableKind } from './constants'; +import { normalizeStatement } from './builder-interface'; import { - Builder, - ExpressionKind, - HeadKind, - normalizeStatement, - VariableKind, -} from './builder-interface'; + APPEND_EXPR_HEAD, + APPEND_PATH_HEAD, + ARG_VAR, + BLOCK_HEAD, + BLOCK_VAR, + BUILDER_COMMENT, + BUILDER_LITERAL, + CALL_EXPR, + CALL_HEAD, + COMMENT_HEAD, + CONCAT_EXPR, + DYNAMIC_COMPONENT_HEAD, + ELEMENT_HEAD, + FREE_VAR, + GET_PATH_EXPR, + GET_VAR_EXPR, + HAS_BLOCK_EXPR, + HAS_BLOCK_PARAMS_EXPR, + KEYWORD_HEAD, + LITERAL_EXPR, + LITERAL_HEAD, + LOCAL_VAR, + MODIFIER_HEAD, + SPLAT_HEAD, + THIS_VAR, +} from './constants'; interface Symbols { top: ProgramSymbols; @@ -217,7 +238,7 @@ export function buildStatement( symbols: Symbols = new ProgramSymbols() ): WireFormat.Statement[] { switch (normalized.kind) { - case HeadKind.AppendPath: { + case APPEND_PATH_HEAD: { return [ [ normalized.trusted ? Op.TrustingAppend : Op.Append, @@ -226,7 +247,7 @@ export function buildStatement( ]; } - case HeadKind.AppendExpr: { + case APPEND_EXPR_HEAD: { return [ [ normalized.trusted ? Op.TrustingAppend : Op.Append, @@ -239,7 +260,7 @@ export function buildStatement( ]; } - case HeadKind.Call: { + case CALL_HEAD: { let { head: path, params, hash, trusted } = normalized; let builtParams: Nullable = params ? buildParams(params, symbols) @@ -258,15 +279,15 @@ export function buildStatement( ]; } - case HeadKind.Literal: { + case LITERAL_HEAD: { return [[Op.Append, normalized.value]]; } - case HeadKind.Comment: { + case COMMENT_HEAD: { return [[Op.Comment, normalized.value]]; } - case HeadKind.Block: { + case BLOCK_HEAD: { let blocks = buildBlocks(normalized.blocks, normalized.blockParams, symbols); let hash = buildHash(normalized.hash, symbols); let params = buildParams(normalized.params, symbols); @@ -279,17 +300,17 @@ export function buildStatement( return [[Op.Block, path, params, hash, blocks]]; } - case HeadKind.Keyword: { + case KEYWORD_HEAD: { return [buildKeyword(normalized, symbols)]; } - case HeadKind.Element: + case ELEMENT_HEAD: return buildElement(normalized, symbols); - case HeadKind.Modifier: + case MODIFIER_HEAD: throw unimpl('modifier'); - case HeadKind.DynamicComponent: + case DYNAMIC_COMPONENT_HEAD: throw unimpl('dynamic component'); default: @@ -300,13 +321,13 @@ export function buildStatement( export function s( arr: TemplateStringsArray, ...interpolated: unknown[] -): [Builder.Literal, string] { +): [BUILDER_LITERAL, string] { let result = arr.reduce( (result, string, i) => result + `${string}${interpolated[i] ? String(interpolated[i]) : ''}`, '' ); - return [Builder.Literal, result]; + return [BUILDER_LITERAL, result]; } export function c(arr: TemplateStringsArray, ...interpolated: unknown[]): BuilderComment { @@ -315,7 +336,7 @@ export function c(arr: TemplateStringsArray, ...interpolated: unknown[]): Builde '' ); - return [Builder.Comment, result]; + return [BUILDER_COMMENT, result]; } export function unicode(charCode: string): string { @@ -387,7 +408,7 @@ function buildElement( function hasSplat(attrs: Nullable): boolean { if (attrs === null) return false; - return Object.keys(attrs).some((a) => attrs[a] === HeadKind.Splat); + return Object.keys(attrs).some((a) => attrs[a] === SPLAT_HEAD); } export function buildAngleInvocation( @@ -424,7 +445,7 @@ export function buildElementParams( let values: WireFormat.Expression[] = []; for (const [key, value] of Object.entries(attrs)) { - if (value === HeadKind.Splat) { + if (value === SPLAT_HEAD) { params.push([Op.AttrSplat, symbols.block('&attrs')]); } else if (key[0] === '@') { keys.push(key); @@ -477,7 +498,7 @@ export function buildAttributeValue( symbols: Symbols ): WireFormat.Attribute[] { switch (value.type) { - case ExpressionKind.Literal: { + case LITERAL_EXPR: { let val = value.value; if (val === false) { @@ -530,19 +551,19 @@ export function buildExpression( symbols: Symbols ): WireFormat.Expression { switch (expr.type) { - case ExpressionKind.GetPath: { + case GET_PATH_EXPR: { return buildGetPath(expr, symbols); } - case ExpressionKind.GetVar: { + case GET_VAR_EXPR: { return buildVar(expr.variable, varContext(context, true), symbols); } - case ExpressionKind.Concat: { + case CONCAT_EXPR: { return [Op.Concat, buildConcat(expr.params, symbols)]; } - case ExpressionKind.Call: { + case CALL_EXPR: { let builtParams = buildParams(expr.params, symbols); let builtHash = buildHash(expr.hash, symbols); let builtExpr = buildCallHead( @@ -554,29 +575,29 @@ export function buildExpression( return [Op.Call, builtExpr, builtParams, builtHash]; } - case ExpressionKind.HasBlock: { + case HAS_BLOCK_EXPR: { return [ Op.HasBlock, buildVar( - { kind: VariableKind.Block, name: expr.name, mode: 'loose' }, + { kind: BLOCK_VAR, name: expr.name, mode: 'loose' }, VariableResolutionContext.Strict, symbols ), ]; } - case ExpressionKind.HasBlockParams: { + case HAS_BLOCK_PARAMS_EXPR: { return [ Op.HasBlockParams, buildVar( - { kind: VariableKind.Block, name: expr.name, mode: 'loose' }, + { kind: BLOCK_VAR, name: expr.name, mode: 'loose' }, VariableResolutionContext.Strict, symbols ), ]; } - case ExpressionKind.Literal: { + case LITERAL_EXPR: { if (expr.value === undefined) { return [Op.Undefined]; } else { @@ -594,7 +615,7 @@ export function buildCallHead( context: VarResolution, symbols: Symbols ): Expressions.GetVar | Expressions.GetPath { - if (callHead.type === ExpressionKind.GetVar) { + if (callHead.type === GET_VAR_EXPR) { return buildVar(callHead.variable, context, symbols); } else { return buildGetPath(callHead, symbols); @@ -636,7 +657,7 @@ export function buildVar( let op: Expressions.GetPath[0] | Expressions.GetVar[0] = Op.GetSymbol; let sym: number; switch (head.kind) { - case VariableKind.Free: + case FREE_VAR: if (context === 'Strict') { op = Op.GetStrictKeyword; } else if (context === 'AppendBare') { @@ -671,19 +692,15 @@ export function buildVar( } } -function getSymbolForVar( - kind: Exclude, - symbols: Symbols, - name: string -) { +function getSymbolForVar(kind: Exclude, symbols: Symbols, name: string) { switch (kind) { - case VariableKind.Arg: + case ARG_VAR: return symbols.arg(name); - case VariableKind.Block: + case BLOCK_VAR: return symbols.block(name); - case VariableKind.Local: + case LOCAL_VAR: return symbols.local(name); - case VariableKind.This: + case THIS_VAR: return symbols.this(); default: return exhausted(kind); diff --git a/packages/@glimmer/compiler/lib/builder/constants.ts b/packages/@glimmer/compiler/lib/builder/constants.ts new file mode 100644 index 0000000000..ea2caaa8cf --- /dev/null +++ b/packages/@glimmer/compiler/lib/builder/constants.ts @@ -0,0 +1,127 @@ +/// Builder /// + +export type BUILDER_LITERAL = 0; +export const BUILDER_LITERAL: BUILDER_LITERAL = 0; + +export type BUILDER_COMMENT = 1; +export const BUILDER_COMMENT: BUILDER_COMMENT = 1; + +export type BUILDER_APPEND = 2; +export const BUILDER_APPEND: BUILDER_APPEND = 2; + +export type BUILDER_MODIFIER = 3; +export const BUILDER_MODIFIER: BUILDER_MODIFIER = 3; + +export type BUILDER_DYNAMIC_COMPONENT = 4; +export const BUILDER_DYNAMIC_COMPONENT: BUILDER_DYNAMIC_COMPONENT = 4; + +export type BUILDER_GET = 5; +export const BUILDER_GET: BUILDER_GET = 5; + +export type BUILDER_CONCAT = 6; +export const BUILDER_CONCAT: BUILDER_CONCAT = 6; + +export type BUILDER_HAS_BLOCK = 7; +export const BUILDER_HAS_BLOCK: BUILDER_HAS_BLOCK = 7; + +export type BUILDER_HAS_BLOCK_PARAMS = 8; +export const BUILDER_HAS_BLOCK_PARAMS: BUILDER_HAS_BLOCK_PARAMS = 8; + +/// HeadKind /// + +export type BLOCK_HEAD = 'Block'; +export const BLOCK_HEAD: BLOCK_HEAD = 'Block'; + +export type CALL_HEAD = 'Call'; +export const CALL_HEAD: CALL_HEAD = 'Call'; + +export type ELEMENT_HEAD = 'Element'; +export const ELEMENT_HEAD: ELEMENT_HEAD = 'Element'; + +export type APPEND_PATH_HEAD = 'AppendPath'; +export const APPEND_PATH_HEAD: APPEND_PATH_HEAD = 'AppendPath'; + +export type APPEND_EXPR_HEAD = 'AppendExpr'; +export const APPEND_EXPR_HEAD: APPEND_EXPR_HEAD = 'AppendExpr'; + +export type LITERAL_HEAD = 'Literal'; +export const LITERAL_HEAD: LITERAL_HEAD = 'Literal'; + +export type MODIFIER_HEAD = 'Modifier'; +export const MODIFIER_HEAD: MODIFIER_HEAD = 'Modifier'; + +export type DYNAMIC_COMPONENT_HEAD = 'DynamicComponent'; +export const DYNAMIC_COMPONENT_HEAD: DYNAMIC_COMPONENT_HEAD = 'DynamicComponent'; + +export type COMMENT_HEAD = 'Comment'; +export const COMMENT_HEAD: COMMENT_HEAD = 'Comment'; + +export type SPLAT_HEAD = 'Splat'; +export const SPLAT_HEAD: SPLAT_HEAD = 'Splat'; + +export type KEYWORD_HEAD = 'Keyword'; +export const KEYWORD_HEAD: KEYWORD_HEAD = 'Keyword'; + +export type HeadKind = + | BLOCK_HEAD + | CALL_HEAD + | ELEMENT_HEAD + | APPEND_PATH_HEAD + | APPEND_EXPR_HEAD + | LITERAL_HEAD + | MODIFIER_HEAD + | DYNAMIC_COMPONENT_HEAD + | COMMENT_HEAD + | SPLAT_HEAD + | KEYWORD_HEAD; + +/// VariableKind /// + +export type LOCAL_VAR = 'Local'; +export const LOCAL_VAR: LOCAL_VAR = 'Local'; + +export type FREE_VAR = 'Free'; +export const FREE_VAR: FREE_VAR = 'Free'; + +export type ARG_VAR = 'Arg'; +export const ARG_VAR: ARG_VAR = 'Arg'; + +export type BLOCK_VAR = 'Block'; +export const BLOCK_VAR: BLOCK_VAR = 'Block'; + +export type THIS_VAR = 'This'; +export const THIS_VAR: THIS_VAR = 'This'; + +export type VariableKind = LOCAL_VAR | FREE_VAR | ARG_VAR | BLOCK_VAR | THIS_VAR; + +/// ExpressionKind /// + +export type LITERAL_EXPR = 'Literal'; +export const LITERAL_EXPR: LITERAL_EXPR = 'Literal'; + +export type CALL_EXPR = 'Call'; +export const CALL_EXPR: CALL_EXPR = 'Call'; + +export type GET_PATH_EXPR = 'GetPath'; +export const GET_PATH_EXPR: GET_PATH_EXPR = 'GetPath'; + +export type GET_VAR_EXPR = 'GetVar'; +export const GET_VAR_EXPR: GET_VAR_EXPR = 'GetVar'; + +export type CONCAT_EXPR = 'Concat'; +export const CONCAT_EXPR: CONCAT_EXPR = 'Concat'; + +export type HAS_BLOCK_EXPR = 'HasBlock'; +export const HAS_BLOCK_EXPR: HAS_BLOCK_EXPR = 'HasBlock'; + +export type HAS_BLOCK_PARAMS_EXPR = 'HasBlockParams'; +export const HAS_BLOCK_PARAMS_EXPR: HAS_BLOCK_PARAMS_EXPR = 'HasBlockParams'; + +export type ExpressionKind = + | LITERAL_EXPR + | CALL_EXPR + | GET_PATH_EXPR + | GET_VAR_EXPR + | CONCAT_EXPR + | HAS_BLOCK_EXPR + | HAS_BLOCK_PARAMS_EXPR; diff --git a/packages/@glimmer/compiler/lib/passes/1-normalization/visitors/constants.ts b/packages/@glimmer/compiler/lib/passes/1-normalization/visitors/constants.ts new file mode 100644 index 0000000000..31408e936d --- /dev/null +++ b/packages/@glimmer/compiler/lib/passes/1-normalization/visitors/constants.ts @@ -0,0 +1,23 @@ +/// ResolutionType /// + +export type VALUE_RESOLUTION = 'value'; +export const VALUE_RESOLUTION: VALUE_RESOLUTION = 'value'; + +export type COMPONENT_RESOLUTION = 'component'; +export const COMPONENT_RESOLUTION: COMPONENT_RESOLUTION = 'component'; + +export type HELPER_RESOLUTION = 'helper'; +export const HELPER_RESOLUTION: HELPER_RESOLUTION = 'helper'; + +export type MODIFIER_RESOLUTION = 'modifier'; +export const MODIFIER_RESOLUTION: MODIFIER_RESOLUTION = 'modifier'; + +export type COMPONENT_OR_HELPER_RESOLUTION = 'component or helper'; +export const COMPONENT_OR_HELPER_RESOLUTION: COMPONENT_OR_HELPER_RESOLUTION = 'component or helper'; + +export type ResolutionType = + | VALUE_RESOLUTION + | COMPONENT_RESOLUTION + | HELPER_RESOLUTION + | MODIFIER_RESOLUTION + | COMPONENT_OR_HELPER_RESOLUTION; diff --git a/packages/@glimmer/compiler/lib/passes/1-normalization/visitors/strict-mode.ts b/packages/@glimmer/compiler/lib/passes/1-normalization/visitors/strict-mode.ts index 433e905711..ab9787f200 100644 --- a/packages/@glimmer/compiler/lib/passes/1-normalization/visitors/strict-mode.ts +++ b/packages/@glimmer/compiler/lib/passes/1-normalization/visitors/strict-mode.ts @@ -4,16 +4,16 @@ import { CurriedTypes } from '@glimmer/vm'; import type { Result } from '../../../shared/result'; import type * as mir from '../../2-encoding/mir'; +import type { ResolutionType } from './constants'; import { Err, Ok } from '../../../shared/result'; - -enum ResolutionType { - Value = 'value', - Component = 'component', - Helper = 'helper', - Modifier = 'modifier', - ComponentOrHelper = 'component or helper', -} +import { + COMPONENT_OR_HELPER_RESOLUTION, + COMPONENT_RESOLUTION, + HELPER_RESOLUTION, + MODIFIER_RESOLUTION, + VALUE_RESOLUTION, +} from './constants'; export default class StrictModeValidationPass { // This is done at the end of all the keyword normalizations @@ -140,7 +140,7 @@ export default class StrictModeValidationPass { return this.InterpolateExpression(expression, span, resolution); case 'CallExpression': - return this.CallExpression(expression, span, resolution ?? ResolutionType.Helper); + return this.CallExpression(expression, span, resolution ?? HELPER_RESOLUTION); case 'Not': return this.Expression(expression.value, span, resolution); @@ -188,7 +188,7 @@ export default class StrictModeValidationPass { NamedArgument(arg: mir.NamedArgument): Result { if (arg.value.type === 'CallExpression') { - return this.Expression(arg.value, arg, ResolutionType.Helper); + return this.Expression(arg.value, arg, HELPER_RESOLUTION); } else { return this.Expression(arg.value, arg); } @@ -219,14 +219,14 @@ export default class StrictModeValidationPass { DynamicAttr(attr: mir.DynamicAttr): Result { if (attr.value.type === 'CallExpression') { - return this.Expression(attr.value, attr, ResolutionType.Helper); + return this.Expression(attr.value, attr, HELPER_RESOLUTION); } else { return this.Expression(attr.value, attr); } } Modifier(modifier: mir.Modifier): Result { - return this.Expression(modifier.callee, modifier, ResolutionType.Modifier).andThen(() => + return this.Expression(modifier.callee, modifier, MODIFIER_RESOLUTION).andThen(() => this.Args(modifier.args) ); } @@ -250,14 +250,14 @@ export default class StrictModeValidationPass { AppendTextNode(statement: mir.AppendTextNode): Result { if (statement.text.type === 'CallExpression') { - return this.Expression(statement.text, statement, ResolutionType.ComponentOrHelper); + return this.Expression(statement.text, statement, COMPONENT_OR_HELPER_RESOLUTION); } else { return this.Expression(statement.text, statement); } } Component(statement: mir.Component): Result { - return this.Expression(statement.tag, statement, ResolutionType.Component) + return this.Expression(statement.tag, statement, COMPONENT_RESOLUTION) .andThen(() => this.ElementParameters(statement.params)) .andThen(() => this.NamedArguments(statement.args)) .andThen(() => this.NamedBlocks(statement.blocks)); @@ -268,7 +268,7 @@ export default class StrictModeValidationPass { } InvokeBlock(statement: mir.InvokeBlock): Result { - return this.Expression(statement.head, statement.head, ResolutionType.Component) + return this.Expression(statement.head, statement.head, COMPONENT_RESOLUTION) .andThen(() => this.Args(statement.args)) .andThen(() => this.NamedBlocks(statement.blocks)); } @@ -313,7 +313,7 @@ export default class StrictModeValidationPass { } InvokeComponent(statement: mir.InvokeComponent): Result { - return this.Expression(statement.definition, statement, ResolutionType.Component) + return this.Expression(statement.definition, statement, COMPONENT_RESOLUTION) .andThen(() => this.Args(statement.args)) .andThen(() => { if (statement.blocks) { @@ -364,11 +364,11 @@ export default class StrictModeValidationPass { let resolution: ResolutionType; if (expression.curriedType === CurriedTypes.Component) { - resolution = ResolutionType.Component; + resolution = COMPONENT_RESOLUTION; } else if (expression.curriedType === CurriedTypes.Helper) { - resolution = ResolutionType.Helper; + resolution = HELPER_RESOLUTION; } else { - resolution = ResolutionType.Modifier; + resolution = MODIFIER_RESOLUTION; } return this.Expression(expression.definition, expression, resolution).andThen(() => @@ -380,7 +380,11 @@ export default class StrictModeValidationPass { return this.Positional(expression.positional, expression); } - errorFor(name: string, span: HasSourceSpan, type = ResolutionType.Value): Result { + errorFor( + name: string, + span: HasSourceSpan, + type: ResolutionType = VALUE_RESOLUTION + ): Result { return Err( generateSyntaxError( `Attempted to resolve a ${type} in a strict mode template, but that value was not in scope: ${name}`, diff --git a/packages/@glimmer/compiler/test/compiler-test.ts b/packages/@glimmer/compiler/test/compiler-test.ts index 839fa80ce1..4be43680a3 100644 --- a/packages/@glimmer/compiler/test/compiler-test.ts +++ b/packages/@glimmer/compiler/test/compiler-test.ts @@ -7,7 +7,8 @@ import type { SerializedTemplateWithLazyBlock, } from '@glimmer/interfaces'; import { - Builder, + BUILDER_APPEND, + BUILDER_CONCAT, buildStatements, c, NEWLINE, @@ -50,8 +51,8 @@ function test(desc: string, template: string, ...expectedStatements: BuilderStat }); } -const Append = Builder.Append; -const Concat = Builder.Concat; +const Append = BUILDER_APPEND; +const Concat = BUILDER_CONCAT; QUnit.test( '@arguments are on regular non-component/regular HTML nodes throws syntax error', diff --git a/packages/@glimmer/destroyable/index.ts b/packages/@glimmer/destroyable/index.ts index e9ccc577a8..0468661c6d 100644 --- a/packages/@glimmer/destroyable/index.ts +++ b/packages/@glimmer/destroyable/index.ts @@ -2,11 +2,10 @@ import type { Destroyable, Destructor } from '@glimmer/interfaces'; import { scheduleDestroy, scheduleDestroyed } from '@glimmer/global-context'; import { debugToString } from '@glimmer/util'; -const enum DestroyingState { - Live = 0, - Destroying = 1, - Destroyed = 2, -} +const LIVE_STATE = 0; +const DESTROYING_STATE = 1; +const DESTROYED_STATE = 2; +type DestroyableState = 0 | 1 | 2; type OneOrMany = null | T | T[]; @@ -16,7 +15,7 @@ interface DestroyableMeta { children: OneOrMany; eagerDestructors: OneOrMany>; destructors: OneOrMany>; - state: DestroyingState; + state: DestroyableState; } interface UndestroyedDestroyablesError extends Error { @@ -74,7 +73,7 @@ function getDestroyableMeta(destroyable: T): DestroyableM children: null, eagerDestructors: null, destructors: null, - state: DestroyingState.Live, + state: LIVE_STATE, }; if (import.meta.env.DEV) { @@ -153,11 +152,11 @@ export function unregisterDestructor( export function destroy(destroyable: Destroyable) { let meta = getDestroyableMeta(destroyable); - if (meta.state >= DestroyingState.Destroying) return; + if (meta.state >= DESTROYING_STATE) return; let { parents, children, eagerDestructors, destructors } = meta; - meta.state = DestroyingState.Destroying; + meta.state = DESTROYING_STATE; iterate(children, destroy); iterate(eagerDestructors, (destructor) => destructor(destroyable)); @@ -166,14 +165,14 @@ export function destroy(destroyable: Destroyable) { scheduleDestroyed(() => { iterate(parents, (parent) => removeChildFromParent(destroyable, parent)); - meta.state = DestroyingState.Destroyed; + meta.state = DESTROYED_STATE; }); } function removeChildFromParent(child: Destroyable, parent: Destroyable) { let parentMeta = getDestroyableMeta(parent); - if (parentMeta.state === DestroyingState.Live) { + if (parentMeta.state === LIVE_STATE) { parentMeta.children = remove( parentMeta.children, child, @@ -198,13 +197,13 @@ export function _hasDestroyableChildren(destroyable: Destroyable) { export function isDestroying(destroyable: Destroyable) { let meta = DESTROYABLE_META.get(destroyable); - return meta === undefined ? false : meta.state >= DestroyingState.Destroying; + return meta === undefined ? false : meta.state >= DESTROYING_STATE; } export function isDestroyed(destroyable: Destroyable) { let meta = DESTROYABLE_META.get(destroyable); - return meta === undefined ? false : meta.state >= DestroyingState.Destroyed; + return meta === undefined ? false : meta.state >= DESTROYED_STATE; } //////////// @@ -243,7 +242,7 @@ if (import.meta.env.DEV) { let undestroyed: object[] = []; map.forEach((meta) => { - if (meta.state !== DestroyingState.Destroyed) { + if (meta.state !== DESTROYED_STATE) { undestroyed.push(meta.source!); } }); diff --git a/packages/@glimmer/interfaces/lib/serialize.d.ts b/packages/@glimmer/interfaces/lib/serialize.d.ts index b40be8868a..ee6a89dd8e 100644 --- a/packages/@glimmer/interfaces/lib/serialize.d.ts +++ b/packages/@glimmer/interfaces/lib/serialize.d.ts @@ -75,13 +75,6 @@ export interface ResolvedComponentDefinition< template: Template | null; } -export enum ResolverContext { - Component, - Modifier, - Helper, - HelperOrComponent, -} - export interface CompileTimeResolver { lookupHelper(name: string, owner: O): Nullable; lookupModifier(name: string, owner: O): Nullable; diff --git a/packages/@glimmer/program/lib/program.ts b/packages/@glimmer/program/lib/program.ts index a4f5d8587b..1795d40a5c 100644 --- a/packages/@glimmer/program/lib/program.ts +++ b/packages/@glimmer/program/lib/program.ts @@ -12,12 +12,12 @@ import { MACHINE_MASK } from '@glimmer/vm'; import { RuntimeOpImpl } from './opcode'; -const enum TableSlotState { - Allocated, - Freed, - Purged, - Pointer, -} +const ALLOCATED = 0; +const FREED = 1; +const PURGED = 2; +const POINTER = 3; + +type TableSlotState = typeof ALLOCATED | typeof FREED | typeof PURGED | typeof POINTER; export type Placeholder = [number, () => number]; export type StdlibPlaceholder = [number, StdLibOperand]; @@ -130,7 +130,7 @@ export class HeapImpl implements CompileTimeHeap, RuntimeHeap { // wrapped to prevent us from allocating extra space in prod. In the future, // if we start using the compact API, we should change this. if (LOCAL_DEBUG) { - this.handleState[handle] = TableSlotState.Allocated; + this.handleState[handle] = ALLOCATED; } } @@ -150,7 +150,7 @@ export class HeapImpl implements CompileTimeHeap, RuntimeHeap { } free(handle: number): void { - this.handleState[handle] = TableSlotState.Freed; + this.handleState[handle] = FREED; } /** @@ -169,21 +169,21 @@ export class HeapImpl implements CompileTimeHeap, RuntimeHeap { let size = unwrap(handleTable[i + 1]) - unwrap(offset); let state = handleState[i]; - if (state === TableSlotState.Purged) { + if (state === PURGED) { continue; - } else if (state === TableSlotState.Freed) { + } else if (state === FREED) { // transition to "already freed" aka "purged" // a good improvement would be to reuse // these slots - handleState[i] = TableSlotState.Purged; + handleState[i] = PURGED; compactedSize += size; - } else if (state === TableSlotState.Allocated) { + } else if (state === ALLOCATED) { for (let j = offset; j <= i + size; j++) { heap[j - compactedSize] = unwrap(heap[j]); } handleTable[i] = offset - compactedSize; - } else if (state === TableSlotState.Pointer) { + } else if (state === POINTER) { handleTable[i] = offset - compactedSize; } } diff --git a/packages/@glimmer/runtime/lib/compat/svg-inner-html-fix.ts b/packages/@glimmer/runtime/lib/compat/svg-inner-html-fix.ts index 0b6bf251bd..5e397ddf57 100644 --- a/packages/@glimmer/runtime/lib/compat/svg-inner-html-fix.ts +++ b/packages/@glimmer/runtime/lib/compat/svg-inner-html-fix.ts @@ -19,13 +19,6 @@ import type { DOMOperations } from '../dom/operations'; import { moveNodesBefore } from '../dom/operations'; -export enum InsertPosition { - beforebegin = 'beforebegin', - afterbegin = 'afterbegin', - beforeend = 'beforeend', - afterend = 'afterend', -} - // Patch: insertAdjacentHTML on SVG Fix // Browsers: Safari, IE, Edge, Firefox ~33-34 // Reason: insertAdjacentHTML does not exist on SVG elements in Safari. It is diff --git a/packages/@glimmer/syntax/lib/generation/util.ts b/packages/@glimmer/syntax/lib/generation/util.ts index 5da9b18f0e..e72c0c3d1a 100644 --- a/packages/@glimmer/syntax/lib/generation/util.ts +++ b/packages/@glimmer/syntax/lib/generation/util.ts @@ -1,12 +1,12 @@ import type * as ASTv1 from '../v1/api'; -const enum Char { - NBSP = 0xa0, - QUOT = 0x22, - LT = 0x3c, - GT = 0x3e, - AMP = 0x26, -} +const Char = { + NBSP: 0xa0, + QUOT: 0x22, + LT: 0x3c, + GT: 0x3e, + AMP: 0x26, +}; const ATTR_VALUE_REGEX_TEST = /["&\xA0]/u; const ATTR_VALUE_REGEX_REPLACE = new RegExp(ATTR_VALUE_REGEX_TEST.source, 'gu'); diff --git a/packages/@glimmer/syntax/lib/source/loc/kinds.ts b/packages/@glimmer/syntax/lib/source/loc/kinds.ts index eac96366c0..1822ff9947 100644 --- a/packages/@glimmer/syntax/lib/source/loc/kinds.ts +++ b/packages/@glimmer/syntax/lib/source/loc/kinds.ts @@ -1,31 +1,45 @@ -export enum OffsetKind { - /** - * We have already computed the character position of this offset or span. - */ - CharPosition = 'CharPosition', +/** + * We have already computed the character position of this offset or span. + */ +export type CharOffsetKind = 'CharPosition'; +export const CHAR_OFFSET_KIND: CharOffsetKind = 'CharPosition'; +/** + * This offset or span was instantiated with a Handlebars SourcePosition or SourceLocation. Its + * character position will be computed on demand. + */ +export type HbsPositionKind = 'HbsPosition'; +export const HBS_POSITION_KIND: HbsPositionKind = 'HbsPosition'; +/** + * for (rare) situations where a node is created but there was no source location (e.g. the name + * "default" in default blocks when the word "default" never appeared in source). This is used + * by the internals when there is a legitimate reason for the internals to synthesize a node + * with no location. + */ +export type InternalSyntheticKind = 'InternalsSynthetic'; +export const INTERNAL_SYNTHETIC_KIND: InternalSyntheticKind = 'InternalsSynthetic'; - /** - * This offset or span was instantiated with a Handlebars SourcePosition or SourceLocation. Its - * character position will be computed on demand. - */ - HbsPosition = 'HbsPosition', +/** + * For situations where a node represents zero parts of the source (for example, empty arguments). + * In general, we attempt to assign these nodes *some* position (empty arguments can be + * positioned immediately after the callee), but it's not always possible + */ +export type NonExistentKind = 'NonExistent'; +export const NON_EXISTENT_KIND: NonExistentKind = 'NonExistent'; - /** - * for (rare) situations where a node is created but there was no source location (e.g. the name - * "default" in default blocks when the word "default" never appeared in source). This is used - * by the internals when there is a legitimate reason for the internals to synthesize a node - * with no location. - */ - InternalsSynthetic = 'InternalsSynthetic', - /** - * For situations where a node represents zero parts of the source (for example, empty arguments). - * In general, we attempt to assign these nodes *some* position (empty arguments can be - * positioned immediately after the callee), but it's not always possible - */ - NonExistent = 'NonExistent', - /** - * For situations where a source location was expected, but it didn't correspond to the node in - * the source. This happens if a plugin creates broken locations. - */ - Broken = 'Broken', +/** + * For situations where a source location was expected, but it didn't correspond to the node in + * the source. This happens if a plugin creates broken locations. + */ +export type BrokenKind = 'Broken'; +export const BROKEN_KIND: BrokenKind = 'Broken'; + +export type OffsetKind = CharOffsetKind | HbsPositionKind | InvisibleKind; + +/** + * These kinds describe spans that don't have a concrete location in the original source. + */ +export type InvisibleKind = BrokenKind | InternalSyntheticKind | NonExistentKind; + +export function isInvisible(kind: OffsetKind): kind is InvisibleKind { + return kind !== CHAR_OFFSET_KIND && kind !== HBS_POSITION_KIND; } diff --git a/packages/@glimmer/syntax/lib/source/loc/match.ts b/packages/@glimmer/syntax/lib/source/loc/match.ts index 1d725ff60c..a20ac7492c 100644 --- a/packages/@glimmer/syntax/lib/source/loc/match.ts +++ b/packages/@glimmer/syntax/lib/source/loc/match.ts @@ -1,8 +1,9 @@ import { assert, isPresentArray } from '@glimmer/util'; +import type { CharOffsetKind, HbsPositionKind, OffsetKind } from './kinds'; import type { CharPosition, HbsPosition, InvisiblePosition, PositionData } from './offset'; -import { OffsetKind } from './kinds'; +import { BROKEN_KIND, INTERNAL_SYNTHETIC_KIND, NON_EXISTENT_KIND } from './kinds'; /** * This file implements the DSL used by span and offset in places where they need to exhaustively @@ -141,23 +142,23 @@ class Matcher { // checking so that matchers can ensure they've actually covered all the cases (and TypeScript // will treat it as an exhaustive match). when( - left: OffsetKind.CharPosition, - right: OffsetKind.HbsPosition, + left: CharOffsetKind, + right: HbsPositionKind, callback: (left: CharPosition, right: HbsPosition) => Out ): ExhaustiveCheck; when( - left: OffsetKind.HbsPosition, - right: OffsetKind.CharPosition, + left: HbsPositionKind, + right: CharOffsetKind, callback: (left: HbsPosition, right: CharPosition) => Out ): ExhaustiveCheck; when( - left: OffsetKind.HbsPosition, - right: OffsetKind.HbsPosition, + left: HbsPositionKind, + right: HbsPositionKind, callback: (left: HbsPosition, right: HbsPosition) => Out ): ExhaustiveCheck; when( - left: OffsetKind.CharPosition, - right: OffsetKind.CharPosition, + left: CharOffsetKind, + right: CharOffsetKind, callback: (left: CharPosition, right: CharPosition) => Out ): ExhaustiveCheck; when( @@ -189,9 +190,9 @@ class Matcher { function patternFor(kind: OffsetKind): Pattern { switch (kind) { - case OffsetKind.Broken: - case OffsetKind.InternalsSynthetic: - case OffsetKind.NonExistent: + case BROKEN_KIND: + case INTERNAL_SYNTHETIC_KIND: + case NON_EXISTENT_KIND: return IsInvisible; default: return kind; diff --git a/packages/@glimmer/syntax/lib/source/loc/offset.ts b/packages/@glimmer/syntax/lib/source/loc/offset.ts index 1dd54e40ee..eb22f07e56 100644 --- a/packages/@glimmer/syntax/lib/source/loc/offset.ts +++ b/packages/@glimmer/syntax/lib/source/loc/offset.ts @@ -1,9 +1,10 @@ import type { SourcePosition } from '../location'; import type { Source } from '../source'; +import type { BrokenKind, InternalSyntheticKind, NonExistentKind, OffsetKind } from './kinds'; import type { SourceSpan } from './span'; import { UNKNOWN_POSITION } from '../location'; -import { OffsetKind } from './kinds'; +import { BROKEN_KIND, CHAR_OFFSET_KIND, HBS_POSITION_KIND } from './kinds'; import { match, MatchAny } from './match'; import { span } from './span'; @@ -53,7 +54,7 @@ export class SourceOffset { * any part of the source. */ static broken(pos: SourcePosition = UNKNOWN_POSITION): SourceOffset { - return new InvisiblePosition(OffsetKind.Broken, pos).wrap(); + return new InvisiblePosition(BROKEN_KIND, pos).wrap(); } constructor(readonly data: PositionData & AnyPosition) {} @@ -130,7 +131,7 @@ export class SourceOffset { } export class CharPosition implements PositionData { - readonly kind = OffsetKind.CharPosition; + readonly kind = CHAR_OFFSET_KIND; /** Computed from char offset */ _locPos: HbsPosition | BROKEN | null = null; @@ -193,7 +194,7 @@ export class CharPosition implements PositionData { } export class HbsPosition implements PositionData { - readonly kind = OffsetKind.HbsPosition; + readonly kind = HBS_POSITION_KIND; _charPos: CharPosition | BROKEN | null; @@ -251,7 +252,7 @@ export class HbsPosition implements PositionData { export class InvisiblePosition implements PositionData { constructor( - readonly kind: OffsetKind.Broken | OffsetKind.InternalsSynthetic | OffsetKind.NonExistent, + readonly kind: BrokenKind | InternalSyntheticKind | NonExistentKind, // whatever was provided, possibly broken readonly pos: SourcePosition ) {} @@ -291,24 +292,24 @@ export class InvisiblePosition implements PositionData { const eql = match((m) => m .when( - OffsetKind.HbsPosition, - OffsetKind.HbsPosition, + HBS_POSITION_KIND, + HBS_POSITION_KIND, ({ hbsPos: left }, { hbsPos: right }) => left.column === right.column && left.line === right.line ) .when( - OffsetKind.CharPosition, - OffsetKind.CharPosition, + CHAR_OFFSET_KIND, + CHAR_OFFSET_KIND, ({ charPos: left }, { charPos: right }) => left === right ) .when( - OffsetKind.CharPosition, - OffsetKind.HbsPosition, + CHAR_OFFSET_KIND, + HBS_POSITION_KIND, ({ offset: left }, right) => left === right.toCharPos()?.offset ) .when( - OffsetKind.HbsPosition, - OffsetKind.CharPosition, + HBS_POSITION_KIND, + CHAR_OFFSET_KIND, (left, { offset: right }) => left.toCharPos()?.offset === right ) .when(MatchAny, MatchAny, () => false) diff --git a/packages/@glimmer/syntax/lib/source/loc/span.ts b/packages/@glimmer/syntax/lib/source/loc/span.ts index 627de87a77..dd6aa0c9ce 100644 --- a/packages/@glimmer/syntax/lib/source/loc/span.ts +++ b/packages/@glimmer/syntax/lib/source/loc/span.ts @@ -3,12 +3,20 @@ import { assertNever } from '@glimmer/util'; import type { SourceLocation, SourcePosition } from '../location'; import type { Source } from '../source'; +import type { BrokenKind, InvisibleKind, NonExistentKind, OffsetKind } from './kinds'; import type { MatchFn } from './match'; import type { AnyPosition, SourceOffset } from './offset'; import { BROKEN_LOCATION, NON_EXISTENT_LOCATION } from '../location'; import { SourceSlice } from '../slice'; -import { OffsetKind } from './kinds'; +import { + BROKEN_KIND, + CHAR_OFFSET_KIND, + HBS_POSITION_KIND, + INTERNAL_SYNTHETIC_KIND, + isInvisible, + NON_EXISTENT_KIND, +} from './kinds'; import { IsInvisible, match, MatchAny } from './match'; import { BROKEN, CharPosition, HbsPosition, InvisiblePosition } from './offset'; @@ -95,7 +103,7 @@ interface SpanData { */ export class SourceSpan implements SourceLocation { static get NON_EXISTENT(): SourceSpan { - return new InvisibleSpan(OffsetKind.NonExistent, NON_EXISTENT_LOCATION).wrap(); + return new InvisibleSpan(NON_EXISTENT_KIND, NON_EXISTENT_LOCATION).wrap(); } static load(source: Source, serialized: SerializedSourceSpan): SourceSpan { @@ -105,9 +113,9 @@ export class SourceSpan implements SourceLocation { return SourceSpan.synthetic(serialized); } else if (Array.isArray(serialized)) { return SourceSpan.forCharPositions(source, serialized[0], serialized[1]); - } else if (serialized === OffsetKind.NonExistent) { + } else if (serialized === NON_EXISTENT_KIND) { return SourceSpan.NON_EXISTENT; - } else if (serialized === OffsetKind.Broken) { + } else if (serialized === BROKEN_KIND) { return SourceSpan.broken(BROKEN_LOCATION); } @@ -128,18 +136,17 @@ export class SourceSpan implements SourceLocation { } static synthetic(chars: string): SourceSpan { - return new InvisibleSpan(OffsetKind.InternalsSynthetic, NON_EXISTENT_LOCATION, chars).wrap(); + return new InvisibleSpan(INTERNAL_SYNTHETIC_KIND, NON_EXISTENT_LOCATION, chars).wrap(); } static broken(pos: SourceLocation = BROKEN_LOCATION): SourceSpan { - return new InvisibleSpan(OffsetKind.Broken, pos).wrap(); + return new InvisibleSpan(BROKEN_KIND, pos).wrap(); } readonly isInvisible: boolean; constructor(private data: SpanData & AnySpan) { - this.isInvisible = - data.kind !== OffsetKind.CharPosition && data.kind !== OffsetKind.HbsPosition; + this.isInvisible = isInvisible(data.kind); } getStart(): SourceOffset { @@ -301,9 +308,9 @@ export class SourceSpan implements SourceLocation { type AnySpan = HbsSpan | CharPositionSpan | InvisibleSpan; class CharPositionSpan implements SpanData { - readonly kind = OffsetKind.CharPosition; + readonly kind = CHAR_OFFSET_KIND; - _locPosSpan: HbsSpan | BROKEN | null = null; + #locPosSpan: HbsSpan | BROKEN | null = null; constructor( readonly source: Source, @@ -340,16 +347,16 @@ class CharPositionSpan implements SpanData { } toHbsSpan(): HbsSpan | null { - let locPosSpan = this._locPosSpan; + let locPosSpan = this.#locPosSpan; if (locPosSpan === null) { const start = this.charPositions.start.toHbsPos(); const end = this.charPositions.end.toHbsPos(); if (start === null || end === null) { - locPosSpan = this._locPosSpan = BROKEN; + locPosSpan = this.#locPosSpan = BROKEN; } else { - locPosSpan = this._locPosSpan = new HbsSpan(this.source, { + locPosSpan = this.#locPosSpan = new HbsSpan(this.source, { start, end, }); @@ -378,24 +385,24 @@ class CharPositionSpan implements SpanData { } export class HbsSpan implements SpanData { - readonly kind = OffsetKind.HbsPosition; + readonly kind = HBS_POSITION_KIND; - _charPosSpan: CharPositionSpan | BROKEN | null = null; + #charPosSpan: CharPositionSpan | BROKEN | null = null; // the source location from Handlebars + AST Plugins -- could be wrong - _providedHbsLoc: SourceLocation | null; + #providedHbsLoc: SourceLocation | null; constructor( readonly source: Source, readonly hbsPositions: { start: HbsPosition; end: HbsPosition }, providedHbsLoc: SourceLocation | null = null ) { - this._providedHbsLoc = providedHbsLoc; + this.#providedHbsLoc = providedHbsLoc; } serialize(): SerializedConcreteSourceSpan { const charPos = this.toCharPosSpan(); - return charPos === null ? OffsetKind.Broken : charPos.wrap().serialize(); + return charPos === null ? BROKEN_KIND : charPos.wrap().serialize(); } wrap(): SourceSpan { @@ -403,13 +410,13 @@ export class HbsSpan implements SpanData { } private updateProvided(pos: SourcePosition, edge: 'start' | 'end') { - if (this._providedHbsLoc) { - this._providedHbsLoc[edge] = pos; + if (this.#providedHbsLoc) { + this.#providedHbsLoc[edge] = pos; } // invalidate computed character offsets - this._charPosSpan = null; - this._providedHbsLoc = { + this.#charPosSpan = null; + this.#providedHbsLoc = { start: pos, end: pos, }; @@ -456,19 +463,19 @@ export class HbsSpan implements SpanData { } toCharPosSpan(): CharPositionSpan | null { - let charPosSpan = this._charPosSpan; + let charPosSpan = this.#charPosSpan; if (charPosSpan === null) { const start = this.hbsPositions.start.toCharPos(); const end = this.hbsPositions.end.toCharPos(); if (start && end) { - charPosSpan = this._charPosSpan = new CharPositionSpan(this.source, { + charPosSpan = this.#charPosSpan = new CharPositionSpan(this.source, { start, end, }); } else { - charPosSpan = this._charPosSpan = BROKEN; + charPosSpan = this.#charPosSpan = BROKEN; return null; } } @@ -479,7 +486,7 @@ export class HbsSpan implements SpanData { class InvisibleSpan implements SpanData { constructor( - readonly kind: OffsetKind.Broken | OffsetKind.InternalsSynthetic | OffsetKind.NonExistent, + readonly kind: InvisibleKind, // whatever was provided, possibly broken readonly loc: SourceLocation, // if the span represents a synthetic string @@ -488,10 +495,10 @@ class InvisibleSpan implements SpanData { serialize(): SerializedConcreteSourceSpan { switch (this.kind) { - case OffsetKind.Broken: - case OffsetKind.NonExistent: + case BROKEN_KIND: + case NON_EXISTENT_KIND: return this.kind; - case OffsetKind.InternalsSynthetic: + case INTERNAL_SYNTHETIC_KIND: return this.string || ''; } } @@ -542,32 +549,32 @@ class InvisibleSpan implements SpanData { export const span: MatchFn = match((m) => m - .when(OffsetKind.HbsPosition, OffsetKind.HbsPosition, (left, right) => + .when(HBS_POSITION_KIND, HBS_POSITION_KIND, (left, right) => new HbsSpan(left.source, { start: left, end: right, }).wrap() ) - .when(OffsetKind.CharPosition, OffsetKind.CharPosition, (left, right) => + .when(CHAR_OFFSET_KIND, CHAR_OFFSET_KIND, (left, right) => new CharPositionSpan(left.source, { start: left, end: right, }).wrap() ) - .when(OffsetKind.CharPosition, OffsetKind.HbsPosition, (left, right) => { + .when(CHAR_OFFSET_KIND, HBS_POSITION_KIND, (left, right) => { const rightCharPos = right.toCharPos(); if (rightCharPos === null) { - return new InvisibleSpan(OffsetKind.Broken, BROKEN_LOCATION).wrap(); + return new InvisibleSpan(BROKEN_KIND, BROKEN_LOCATION).wrap(); } else { return span(left, rightCharPos); } }) - .when(OffsetKind.HbsPosition, OffsetKind.CharPosition, (left, right) => { + .when(HBS_POSITION_KIND, CHAR_OFFSET_KIND, (left, right) => { const leftCharPos = left.toCharPos(); if (leftCharPos === null) { - return new InvisibleSpan(OffsetKind.Broken, BROKEN_LOCATION).wrap(); + return new InvisibleSpan(BROKEN_KIND, BROKEN_LOCATION).wrap(); } else { return span(leftCharPos, right); } @@ -583,7 +590,4 @@ export type SerializedConcreteSourceSpan = | /** normal */ [start: number, size: number] | /** synthetic */ string; -export type SerializedSourceSpan = - | SerializedConcreteSourceSpan - | OffsetKind.NonExistent - | OffsetKind.Broken; +export type SerializedSourceSpan = SerializedConcreteSourceSpan | NonExistentKind | BrokenKind; diff --git a/packages/@glimmer/syntax/lib/v2/objects/constants.ts b/packages/@glimmer/syntax/lib/v2/objects/constants.ts new file mode 100644 index 0000000000..5c47a029e8 --- /dev/null +++ b/packages/@glimmer/syntax/lib/v2/objects/constants.ts @@ -0,0 +1,10 @@ +/// FreeVarNamespace /// + +export type HELPER_VAR_NS = 'Helper'; +export const HELPER_VAR_NS: HELPER_VAR_NS = 'Helper'; +export type MODIFIER_VAR_NS = 'Modifier'; +export const MODIFIER_VAR_NS: MODIFIER_VAR_NS = 'Modifier'; +export type COMPONENT_VAR_NS = 'Component'; +export const COMPONENT_VAR_NS: COMPONENT_VAR_NS = 'Component'; + +export type FreeVarNamespace = HELPER_VAR_NS | MODIFIER_VAR_NS | COMPONENT_VAR_NS; diff --git a/packages/@glimmer/syntax/lib/v2/objects/resolution.ts b/packages/@glimmer/syntax/lib/v2/objects/resolution.ts index a13317a976..6be6345ddd 100644 --- a/packages/@glimmer/syntax/lib/v2/objects/resolution.ts +++ b/packages/@glimmer/syntax/lib/v2/objects/resolution.ts @@ -8,6 +8,10 @@ import type { GetContextualFreeOpcode } from '@glimmer/interfaces'; import { SexpOpcodes } from '@glimmer/wire-format'; +import type { FreeVarNamespace } from './constants'; + +import { COMPONENT_VAR_NS, HELPER_VAR_NS, MODIFIER_VAR_NS } from './constants'; + /** * Strict resolution is used: * @@ -67,7 +71,7 @@ export class LooseModeResolution { * ^ In either case, `x` should be resolved in the `component` and `helper` namespaces. */ static append(): LooseModeResolution { - return new LooseModeResolution([FreeVarNamespace.Component, FreeVarNamespace.Helper]); + return new LooseModeResolution([COMPONENT_VAR_NS, HELPER_VAR_NS]); } /** @@ -85,7 +89,7 @@ export class LooseModeResolution { * ^ In either case, `x` should be resolved in the `helper` namespace. */ static trustingAppend(): LooseModeResolution { - return this.namespaced(FreeVarNamespace.Helper); + return this.namespaced(HELPER_VAR_NS); } constructor( @@ -96,11 +100,11 @@ export class LooseModeResolution { resolution(): GetContextualFreeOpcode { if (this.namespaces.length === 1) { switch (this.namespaces[0]) { - case FreeVarNamespace.Helper: + case HELPER_VAR_NS: return SexpOpcodes.GetFreeAsHelperHead; - case FreeVarNamespace.Modifier: + case MODIFIER_VAR_NS: return SexpOpcodes.GetFreeAsModifierHead; - case FreeVarNamespace.Component: + case COMPONENT_VAR_NS: return SexpOpcodes.GetFreeAsComponentHead; } } else { @@ -117,15 +121,9 @@ export class LooseModeResolution { } } -export enum FreeVarNamespace { - Helper = 'Helper', - Modifier = 'Modifier', - Component = 'Component', -} - -export const HELPER_NAMESPACE = FreeVarNamespace.Helper; -export const MODIFIER_NAMESPACE = FreeVarNamespace.Modifier; -export const COMPONENT_NAMESPACE = FreeVarNamespace.Component; +export const HELPER_NAMESPACE = HELPER_VAR_NS; +export const MODIFIER_NAMESPACE = MODIFIER_VAR_NS; +export const COMPONENT_NAMESPACE = COMPONENT_VAR_NS; /** * A `Namespaced` must be resolved in one or more namespaces. @@ -149,10 +147,10 @@ export const COMPONENT_NAMESPACE = FreeVarNamespace.Component; * ^ `x` is resolved in the `modifier` namespace */ type Namespaces = - | [FreeVarNamespace.Helper] - | [FreeVarNamespace.Modifier] - | [FreeVarNamespace.Component] - | [FreeVarNamespace.Component, FreeVarNamespace.Helper]; + | [HELPER_VAR_NS] + | [MODIFIER_VAR_NS] + | [COMPONENT_VAR_NS] + | [COMPONENT_VAR_NS, HELPER_VAR_NS]; export type FreeVarResolution = StrictResolution | HtmlResolution | LooseModeResolution;