ember-legacy-class-transform
Version:
The default blueprint for ember-cli addons.
219 lines (218 loc) • 6.87 kB
JavaScript
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);
}