import { SymbolTable } from '@glimmer/interfaces'; import { Blocks, Inlines, populateBuiltins } from './syntax/functions'; import { Constants } from './opcodes'; import * as Simple from './dom/interfaces'; import { DOMChanges, DOMTreeConstruction } from './dom/helper'; import { Reference, PathReference, OpaqueIterable } from '@glimmer/reference'; import { UNDEFINED_REFERENCE, ConditionalReference } from './references'; import { defaultManagers, AttributeManager } from './dom/attribute-managers'; import { PartialDefinition } from './partial'; import { Component, ComponentManager, ComponentDefinition } from './component/interfaces'; import { ModifierManager } from './modifier/interfaces'; import { Option, Destroyable, Opaque, HasGuid, ensureGuid, expect } from '@glimmer/util'; import { TemplateMeta } from '@glimmer/wire-format'; import { EvaluatedArgs } from './compiled/expressions/args'; import { InlineBlock } from './scanner'; import { PublicVM } from './vm/append'; export type ScopeSlot = PathReference | InlineBlock | EvaluatedArgs; export interface DynamicScope { get(key: string): PathReference; set(key: string, reference: PathReference): PathReference; child(): DynamicScope; } export class Scope { static root(self: PathReference, size = 0) { let refs: PathReference[] = new Array(size + 1); for (let i = 0; i <= size; i++) { refs[i] = UNDEFINED_REFERENCE; } return new Scope(refs).init({ self }); } // the 0th slot is `self` private slots: ScopeSlot[]; private callerScope: Option = null; constructor(references: ScopeSlot[], callerScope: Option = null) { this.slots = references; this.callerScope = callerScope; } init({ self }: { self: PathReference }): this { this.slots[0] = self; return this; } getSelf(): PathReference { return this.slots[0] as PathReference; } getSymbol(symbol: number): PathReference { return this.slots[symbol] as PathReference; } getBlock(symbol: number): InlineBlock { return this.slots[symbol] as InlineBlock; } getPartialArgs(symbol: number): EvaluatedArgs { return this.slots[symbol] as EvaluatedArgs; } bindSymbol(symbol: number, value: PathReference) { this.slots[symbol] = value; } bindBlock(symbol: number, value: InlineBlock) { this.slots[symbol] = value; } bindPartialArgs(symbol: number, value: EvaluatedArgs) { this.slots[symbol] = value; } bindCallerScope(scope: Scope) { this.callerScope = scope; } getCallerScope(): Option { return this.callerScope; } child(): Scope { return new Scope(this.slots.slice(), this.callerScope); } } class Transaction { public scheduledInstallManagers: ModifierManager[] = []; public scheduledInstallModifiers: Object[] = []; public scheduledUpdateModifierManagers: ModifierManager[] = []; public scheduledUpdateModifiers: Object[] = []; public createdComponents: Component[] = []; public createdManagers: ComponentManager[] = []; public updatedComponents: Component[] = []; public updatedManagers: ComponentManager[] = []; public destructors: Destroyable[] = []; didCreate(component: T, manager: ComponentManager) { this.createdComponents.push(component); this.createdManagers.push(manager); } didUpdate(component: T, manager: ComponentManager) { this.updatedComponents.push(component); this.updatedManagers.push(manager); } scheduleInstallModifier(modifier: T, manager: ModifierManager) { this.scheduledInstallManagers.push(manager); this.scheduledInstallModifiers.push(modifier); } scheduleUpdateModifier(modifier: T, manager: ModifierManager) { this.scheduledUpdateModifierManagers.push(manager); this.scheduledUpdateModifiers.push(modifier); } didDestroy(d: Destroyable) { this.destructors.push(d); } commit() { let { createdComponents, createdManagers } = this; for (let i=0; i) {} get type() { return this.array[this.offset]; } get op1() { return this.array[this.offset + 1]; } get op2() { return this.array[this.offset + 2]; } get op3() { return this.array[this.offset + 3]; } } export class Program { [key: number]: never; private opcodes: number[] = []; private _offset = 0; private _opcode: Opcode; constructor() { this._opcode = new Opcode(this.opcodes); } get next(): number { return this._offset; } get current(): number { return this._offset - 4; } opcode(offset: number): Opcode { this._opcode.offset = offset; return this._opcode; } set(pos: number, type: number, op1 = 0, op2 = 0, op3 = 0) { this.opcodes[pos] = type; this.opcodes[pos + 1] = op1; this.opcodes[pos + 2] = op2; this.opcodes[pos + 3] = op3; } push(type: number, op1 = 0, op2 = 0, op3 = 0): number { let offset = this._offset; this.opcodes[this._offset++] = type; this.opcodes[this._offset++] = op1; this.opcodes[this._offset++] = op2; this.opcodes[this._offset++] = op3; return offset; } } export abstract class Environment { protected updateOperations: DOMChanges; protected appendOperations: DOMTreeConstruction; private _macros: Option<{ blocks: Blocks, inlines: Inlines }> = null; private _transaction: Option = null; public constants: Constants = new Constants(); public program = new Program(); constructor({ appendOperations, updateOperations }: { appendOperations: DOMTreeConstruction, updateOperations: DOMChanges }) { this.appendOperations = appendOperations; this.updateOperations = updateOperations; } toConditionalReference(reference: Reference): Reference { return new ConditionalReference(reference); } abstract iterableFor(reference: Reference, args: EvaluatedArgs): OpaqueIterable; abstract protocolForURL(s: string): string; getAppendOperations(): DOMTreeConstruction { return this.appendOperations; } getDOM(): DOMChanges { return this.updateOperations; } getIdentity(object: HasGuid): string { return ensureGuid(object) + ''; } begin() { this._transaction = new Transaction(); } private get transaction(): Transaction { return expect(this._transaction, 'must be in a transaction'); } didCreate(component: T, manager: ComponentManager) { this.transaction.didCreate(component, manager); } didUpdate(component: T, manager: ComponentManager) { this.transaction.didUpdate(component, manager); } scheduleInstallModifier(modifier: T, manager: ModifierManager) { this.transaction.scheduleInstallModifier(modifier, manager); } scheduleUpdateModifier(modifier: T, manager: ModifierManager) { this.transaction.scheduleUpdateModifier(modifier, manager); } didDestroy(d: Destroyable) { this.transaction.didDestroy(d); } commit() { this.transaction.commit(); this._transaction = null; } attributeFor(element: Simple.Element, attr: string, isTrusting: boolean, namespace?: string): AttributeManager { return defaultManagers(element, attr, isTrusting, namespace === undefined ? null : namespace); } macros(): { blocks: Blocks, inlines: Inlines } { let macros = this._macros; if (!macros) { this._macros = macros = populateBuiltins(); } return macros; } abstract hasHelper(helperName: string, blockMeta: TemplateMeta): boolean; abstract lookupHelper(helperName: string, blockMeta: TemplateMeta): Helper; abstract hasModifier(modifierName: string, blockMeta: TemplateMeta): boolean; abstract lookupModifier(modifierName: string, blockMeta: TemplateMeta): ModifierManager; abstract hasComponentDefinition(tagName: string, symbolTable: SymbolTable): boolean; abstract getComponentDefinition(tagName: string, symbolTable: SymbolTable): ComponentDefinition; abstract hasPartial(partialName: string, symbolTable: SymbolTable): boolean; abstract lookupPartial(PartialName: string, symbolTable: SymbolTable): PartialDefinition; } export default Environment; export interface Helper { (vm: PublicVM, args: EvaluatedArgs, symbolTable: SymbolTable): PathReference; }