UNPKG

ember-legacy-class-transform

Version:
219 lines (218 loc) 6.87 kB
import { CompiledDynamicTemplate } from './compiled/blocks'; import { Ops } from '@glimmer/wire-format'; import { Register, debugSlice } from './opcodes'; import { ATTRS_BLOCK, compileStatement } from './syntax/functions'; import * as ClientSide from './syntax/client-side'; import { expr } from './syntax/functions'; import OpcodeBuilderDSL from './compiled/opcodes/builder'; export function compileLayout(compilable, env) { let builder = new ComponentLayoutBuilder(env); compilable.compile(builder); return builder.compile(); } class ComponentLayoutBuilder { constructor(env) { this.env = env; } wrapLayout(layout) { this.inner = new WrappedBuilder(this.env, layout); } fromLayout(componentName, layout) { this.inner = new UnwrappedBuilder(this.env, componentName, layout); } compile() { return this.inner.compile(); } get tag() { return this.inner.tag; } get attrs() { return this.inner.attrs; } } class WrappedBuilder { constructor(env, layout) { this.env = env; this.layout = layout; this.tag = new ComponentTagBuilder(); this.attrs = new ComponentAttrsBuilder(); } compile() { //========DYNAMIC // PutValue(TagExpr) // Test // JumpUnless(BODY) // OpenDynamicPrimitiveElement // DidCreateElement // ...attr statements... // FlushElement // BODY: Noop // ...body statements... // PutValue(TagExpr) // Test // JumpUnless(END) // CloseElement // END: Noop // DidRenderLayout // Exit // //========STATIC // OpenPrimitiveElementOpcode // DidCreateElement // ...attr statements... // FlushElement // ...body statements... // CloseElement // DidRenderLayout // Exit let { env, layout } = this; let meta = { templateMeta: layout.meta, symbols: layout.symbols, asPartial: false }; let dynamicTag = this.tag.getDynamic(); let staticTag = this.tag.getStatic(); let b = builder(env, meta); b.startLabels(); if (dynamicTag) { b.fetch(Register.s1); expr(dynamicTag, b); b.dup(); b.load(Register.s1); b.test('simple'); b.jumpUnless('BODY'); b.fetch(Register.s1); b.pushComponentOperations(); b.openDynamicElement(); } else if (staticTag) { b.pushComponentOperations(); b.openElementWithOperations(staticTag); } if (dynamicTag || staticTag) { b.didCreateElement(Register.s0); let attrs = this.attrs.buffer; for (let i = 0; i < attrs.length; i++) { compileStatement(attrs[i], b); } b.flushElement(); } b.label('BODY'); b.invokeStatic(layout.asBlock()); if (dynamicTag) { b.fetch(Register.s1); b.test('simple'); b.jumpUnless('END'); b.closeElement(); } else if (staticTag) { b.closeElement(); } b.label('END'); b.didRenderLayout(Register.s0); if (dynamicTag) { b.load(Register.s1); } b.stopLabels(); let start = b.start; let end = b.finalize(); if (false) { debugSlice(env, env.program.heap.getaddr(start), env.program.heap.getaddr(end)); } return new CompiledDynamicTemplate(start, { meta, hasEval: layout.hasEval, symbols: layout.symbols.concat([ATTRS_BLOCK]) }); } } class UnwrappedBuilder { constructor(env, componentName, layout) { this.env = env; this.componentName = componentName; this.layout = layout; this.attrs = new ComponentAttrsBuilder(); } get tag() { throw new Error('BUG: Cannot call `tag` on an UnwrappedBuilder'); } compile() { let { env, layout } = this; return layout.asLayout(this.componentName, this.attrs.buffer).compileDynamic(env); } } class ComponentTagBuilder { constructor() { this.isDynamic = null; this.isStatic = null; this.staticTagName = null; this.dynamicTagName = null; } getDynamic() { if (this.isDynamic) { return this.dynamicTagName; } } getStatic() { if (this.isStatic) { return this.staticTagName; } } static(tagName) { this.isStatic = true; this.staticTagName = tagName; } dynamic(tagName) { this.isDynamic = true; this.dynamicTagName = [Ops.ClientSideExpression, ClientSide.Ops.FunctionExpression, tagName]; } } class ComponentAttrsBuilder { constructor() { this.buffer = []; } static(name, value) { this.buffer.push([Ops.StaticAttr, name, value, null]); } dynamic(name, value) { this.buffer.push([Ops.DynamicAttr, name, [Ops.ClientSideExpression, ClientSide.Ops.FunctionExpression, value], null]); } } export class ComponentBuilder { constructor(builder) { this.builder = builder; this.env = builder.env; } static(definition, args) { let [params, hash, _default, inverse] = args; let { builder } = this; builder.pushComponentManager(definition); builder.invokeComponent(null, params, hash, _default, inverse); } dynamic(definitionArgs, getDefinition, args) { let [params, hash, block, inverse] = args; let { builder } = this; if (!definitionArgs || definitionArgs.length === 0) { throw new Error("Dynamic syntax without an argument"); } let meta = this.builder.meta.templateMeta; function helper(vm, a) { return getDefinition(vm, a, meta); } builder.startLabels(); builder.pushFrame(); builder.returnTo('END'); builder.compileArgs(definitionArgs[0], definitionArgs[1], true); builder.helper(helper); builder.dup(); builder.test('simple'); builder.enter(2); builder.jumpUnless('ELSE'); builder.pushDynamicComponentManager(); builder.invokeComponent(null, params, hash, block, inverse); builder.label('ELSE'); builder.exit(); builder.return(); builder.label('END'); builder.popFrame(); builder.stopLabels(); } } export function builder(env, meta) { return new OpcodeBuilderDSL(env, meta); }