UNPKG

ember-legacy-class-transform

Version:
486 lines 14.9 kB
import { dict, EMPTY_ARRAY, expect, fillNulls, Stack, typePos } from '@glimmer/util'; import { ComponentBuilder } from '../../compiler'; import { Register } from '../../opcodes'; import { expr, InvokeDynamicLayout } from '../../syntax/functions'; import RawInlineBlock from '../../syntax/raw-block'; import { IsComponentDefinitionReference } from '../opcodes/content'; import * as content from './content'; import * as vm from './vm'; class Labels { constructor() { this.labels = dict(); this.targets = []; } label(name, index) { this.labels[name] = index; } target(at, Target, target) { this.targets.push({ at, Target, target }); } patch(program) { let { targets, labels } = this; for (let i = 0; i < targets.length; i++) { let { at, target } = targets[i]; let goto = labels[target] - at; program.heap.setbyaddr(at + 1, goto); } } } export class BasicOpcodeBuilder { constructor(env, meta, program) { this.env = env; this.meta = meta; this.program = program; this.labelsStack = new Stack(); this.constants = program.constants; this.heap = program.heap; this.start = this.heap.malloc(); } get pos() { return typePos(this.heap.size()); } get nextPos() { return this.heap.size(); } upvars(count) { return fillNulls(count); } reserve(name) { this.push(name, 0, 0, 0); } push(name, op1 = 0, op2 = 0, op3 = 0) { this.heap.push(name); this.heap.push(op1); this.heap.push(op2); this.heap.push(op3); } finalize() { this.push(22 /* Return */); this.heap.finishMalloc(this.start); return this.start; } // args pushArgs(synthetic) { this.push(58 /* PushArgs */, synthetic === true ? 1 : 0); } // helpers get labels() { return expect(this.labelsStack.current, 'bug: not in a label stack'); } startLabels() { this.labelsStack.push(new Labels()); } stopLabels() { let label = expect(this.labelsStack.pop(), 'unbalanced push and pop labels'); label.patch(this.program); } // components pushComponentManager(definition) { this.push(56 /* PushComponentManager */, this.other(definition)); } pushDynamicComponentManager() { this.push(57 /* PushDynamicComponentManager */); } prepareArgs(state) { this.push(59 /* PrepareArgs */, state); } createComponent(state, hasDefault, hasInverse) { let flag = (hasDefault === true ? 1 : 0) | (hasInverse === true ? 1 : 0) << 1; this.push(60 /* CreateComponent */, flag, state); } registerComponentDestructor(state) { this.push(61 /* RegisterComponentDestructor */, state); } beginComponentTransaction() { this.push(65 /* BeginComponentTransaction */); } commitComponentTransaction() { this.push(66 /* CommitComponentTransaction */); } pushComponentOperations() { this.push(62 /* PushComponentOperations */); } getComponentSelf(state) { this.push(63 /* GetComponentSelf */, state); } getComponentLayout(state) { this.push(64 /* GetComponentLayout */, state); } didCreateElement(state) { this.push(67 /* DidCreateElement */, state); } didRenderLayout(state) { this.push(68 /* DidRenderLayout */, state); } // partial getPartialTemplate() { this.push(69 /* GetPartialTemplate */); } resolveMaybeLocal(name) { this.push(70 /* ResolveMaybeLocal */, this.string(name)); } // debugger debugger(symbols, evalInfo) { this.push(71 /* Debugger */, this.constants.other(symbols), this.constants.array(evalInfo)); } // content dynamicContent(Opcode) { this.push(26 /* DynamicContent */, this.other(Opcode)); } cautiousAppend() { this.dynamicContent(new content.OptimizedCautiousAppendOpcode()); } trustingAppend() { this.dynamicContent(new content.OptimizedTrustingAppendOpcode()); } // dom text(text) { this.push(24 /* Text */, this.constants.string(text)); } openPrimitiveElement(tag) { this.push(27 /* OpenElement */, this.constants.string(tag)); } openElementWithOperations(tag) { this.push(28 /* OpenElementWithOperations */, this.constants.string(tag)); } openDynamicElement() { this.push(29 /* OpenDynamicElement */); } flushElement() { this.push(33 /* FlushElement */); } closeElement() { this.push(34 /* CloseElement */); } staticAttr(_name, _namespace, _value) { let name = this.constants.string(_name); let namespace = _namespace ? this.constants.string(_namespace) : 0; let value = this.constants.string(_value); this.push(30 /* StaticAttr */, name, value, namespace); } dynamicAttrNS(_name, _namespace, trusting) { let name = this.constants.string(_name); let namespace = this.constants.string(_namespace); this.push(32 /* DynamicAttrNS */, name, namespace, trusting === true ? 1 : 0); } dynamicAttr(_name, trusting) { let name = this.constants.string(_name); this.push(31 /* DynamicAttr */, name, trusting === true ? 1 : 0); } comment(_comment) { let comment = this.constants.string(_comment); this.push(25 /* Comment */, comment); } modifier(_definition) { this.push(35 /* Modifier */, this.other(_definition)); } // lists putIterator() { this.push(54 /* PutIterator */); } enterList(start) { this.reserve(52 /* EnterList */); this.labels.target(this.pos, 52 /* EnterList */, start); } exitList() { this.push(53 /* ExitList */); } iterate(breaks) { this.reserve(55 /* Iterate */); this.labels.target(this.pos, 55 /* Iterate */, breaks); } // expressions setVariable(symbol) { this.push(4 /* SetVariable */, symbol); } getVariable(symbol) { this.push(5 /* GetVariable */, symbol); } getProperty(key) { this.push(6 /* GetProperty */, this.string(key)); } getBlock(symbol) { this.push(8 /* GetBlock */, symbol); } hasBlock(symbol) { this.push(9 /* HasBlock */, symbol); } hasBlockParams(symbol) { this.push(10 /* HasBlockParams */, symbol); } concat(size) { this.push(11 /* Concat */, size); } function(f) { this.push(2 /* Function */, this.func(f)); } load(register) { this.push(17 /* Load */, register); } fetch(register) { this.push(18 /* Fetch */, register); } dup(register = Register.sp, offset = 0) { return this.push(15 /* Dup */, register, offset); } pop(count = 1) { return this.push(16 /* Pop */, count); } // vm pushRemoteElement() { this.push(36 /* PushRemoteElement */); } popRemoteElement() { this.push(37 /* PopRemoteElement */); } label(name) { this.labels.label(name, this.nextPos); } pushRootScope(symbols, bindCallerScope) { this.push(19 /* RootScope */, symbols, bindCallerScope ? 1 : 0); } pushChildScope() { this.push(20 /* ChildScope */); } popScope() { this.push(21 /* PopScope */); } returnTo(label) { this.reserve(23 /* ReturnTo */); this.labels.target(this.pos, 23 /* ReturnTo */, label); } pushDynamicScope() { this.push(39 /* PushDynamicScope */); } popDynamicScope() { this.push(40 /* PopDynamicScope */); } pushImmediate(value) { this.push(13 /* Constant */, this.other(value)); } primitive(_primitive) { let flag = 0; let primitive; switch (typeof _primitive) { case 'number': if (_primitive % 1 === 0 && _primitive > 0) { primitive = _primitive; } else { primitive = this.float(_primitive); flag = 1; } break; case 'string': primitive = this.string(_primitive); flag = 2; break; case 'boolean': primitive = _primitive | 0; flag = 3; break; case 'object': // assume null primitive = 2; flag = 3; break; case 'undefined': primitive = 3; flag = 3; break; default: throw new Error('Invalid primitive passed to pushPrimitive'); } this.push(14 /* PrimitiveReference */, flag << 30 | primitive); } helper(func) { this.push(1 /* Helper */, this.func(func)); } pushBlock(block) { this.push(7 /* PushBlock */, this.block(block)); } bindDynamicScope(_names) { this.push(38 /* BindDynamicScope */, this.names(_names)); } enter(args) { this.push(49 /* Enter */, args); } exit() { this.push(50 /* Exit */); } return() { this.push(22 /* Return */); } pushFrame() { this.push(47 /* PushFrame */); } popFrame() { this.push(48 /* PopFrame */); } compileDynamicBlock() { this.push(41 /* CompileDynamicBlock */); } invokeDynamic(invoker) { this.push(43 /* InvokeDynamic */, this.other(invoker)); } invokeStatic(block, callerCount = 0) { let { parameters } = block.symbolTable; let calleeCount = parameters.length; let count = Math.min(callerCount, calleeCount); this.pushFrame(); if (count) { this.pushChildScope(); for (let i = 0; i < count; i++) { this.dup(Register.fp, callerCount - i); this.setVariable(parameters[i]); } } let _block = this.constants.block(block); this.push(42 /* InvokeStatic */, _block); if (count) { this.popScope(); } this.popFrame(); } test(testFunc) { let _func; if (testFunc === 'const') { _func = vm.ConstTest; } else if (testFunc === 'simple') { _func = vm.SimpleTest; } else if (testFunc === 'environment') { _func = vm.EnvironmentTest; } else if (typeof testFunc === 'function') { _func = testFunc; } else { throw new Error('unreachable'); } let func = this.constants.function(_func); this.push(51 /* Test */, func); } jump(target) { this.reserve(44 /* Jump */); this.labels.target(this.pos, 44 /* Jump */, target); } jumpIf(target) { this.reserve(45 /* JumpIf */); this.labels.target(this.pos, 45 /* JumpIf */, target); } jumpUnless(target) { this.reserve(46 /* JumpUnless */); this.labels.target(this.pos, 46 /* JumpUnless */, target); } string(_string) { return this.constants.string(_string); } float(num) { return this.constants.float(num); } names(_names) { let names = []; for (let i = 0; i < _names.length; i++) { let n = _names[i]; names[i] = this.constants.string(n); } return this.constants.array(names); } symbols(symbols) { return this.constants.array(symbols); } other(value) { return this.constants.other(value); } block(block) { return block ? this.constants.block(block) : 0; } func(func) { return this.constants.function(func); } } function isCompilableExpression(expr) { return typeof expr === 'object' && expr !== null && typeof expr.compile === 'function'; } export default class OpcodeBuilder extends BasicOpcodeBuilder { constructor(env, meta, program = env.program) { super(env, meta, program); this.component = new ComponentBuilder(this); } compileArgs(params, hash, synthetic) { let positional = 0; if (params) { for (let i = 0; i < params.length; i++) { expr(params[i], this); } positional = params.length; } this.pushImmediate(positional); let names = EMPTY_ARRAY; if (hash) { names = hash[0]; let val = hash[1]; for (let i = 0; i < val.length; i++) { expr(val[i], this); } } this.pushImmediate(names); this.pushArgs(synthetic); } compile(expr) { if (isCompilableExpression(expr)) { return expr.compile(this); } else { return expr; } } guardedAppend(expression, trusting) { this.startLabels(); this.pushFrame(); this.returnTo('END'); expr(expression, this); this.dup(); this.test(reference => { return IsComponentDefinitionReference.create(reference); }); this.enter(2); this.jumpUnless('ELSE'); this.pushDynamicComponentManager(); this.invokeComponent(null, null, null, null, null); this.exit(); this.return(); this.label('ELSE'); if (trusting) { this.trustingAppend(); } else { this.cautiousAppend(); } this.exit(); this.return(); this.label('END'); this.popFrame(); this.stopLabels(); } invokeComponent(attrs, params, hash, block, inverse = null) { this.fetch(Register.s0); this.dup(Register.sp, 1); this.load(Register.s0); this.pushBlock(block); this.pushBlock(inverse); this.compileArgs(params, hash, false); this.prepareArgs(Register.s0); this.beginComponentTransaction(); this.pushDynamicScope(); this.createComponent(Register.s0, block !== null, inverse !== null); this.registerComponentDestructor(Register.s0); this.getComponentSelf(Register.s0); this.getComponentLayout(Register.s0); this.invokeDynamic(new InvokeDynamicLayout(attrs && attrs.scan())); this.popFrame(); this.popScope(); this.popDynamicScope(); this.commitComponentTransaction(); this.load(Register.s0); } template(block) { if (!block) return null; return new RawInlineBlock(this.meta, block.statements, block.parameters); } }