UNPKG

ember-source

Version:

A JavaScript framework for creating ambitious web applications

1,424 lines 62.8 kB
import { isSmallInt, encodeImmediate, assert as debugAssert, EMPTY_STRING_ARRAY, unwrap, reverse, expect, debugToString as debugToString$1, Stack as StackImpl, isPresentArray, encodeHandle, EMPTY_ARRAY, assign, enumerate, dict } from '../util/index.js';
import { Op, MachineOp, $v0, $fp, InternalComponentCapabilities, $s0, $sp, ContentType, $s1, TYPE_SIZE, isMachineOp, MACHINE_MASK, ARG_SHIFT } from '../vm/index.js';
import { InstructionEncoderImpl } from '../encoder/index.js';
import { SexpOpcodes as opcodes } from '../wire-format/index.js';
import { hasCapability } from '../manager/index.js';
import { isDevelopingApp } from '@embroider/macros';

let debugCompiler;
function makeResolutionTypeVerifier(typeToVerify) {
  return opcode => {
    if (!function (opcode) {
      return Array.isArray(opcode) && 2 === opcode.length;
    }(opcode)) return !1;
    let type = opcode[0];
    return type === opcodes.GetStrictKeyword || type === opcodes.GetLexicalSymbol || type === typeToVerify;
  };
}
new Array(Op.Size).fill(null), new Array(Op.Size).fill(null);
const isGetFreeComponent = makeResolutionTypeVerifier(opcodes.GetFreeAsComponentHead),
  isGetFreeModifier = makeResolutionTypeVerifier(opcodes.GetFreeAsModifierHead),
  isGetFreeHelper = makeResolutionTypeVerifier(opcodes.GetFreeAsHelperHead),
  isGetFreeComponentOrHelper = makeResolutionTypeVerifier(opcodes.GetFreeAsComponentOrHelperHead);
function assertResolverInvariants(meta) {
  if (isDevelopingApp()) {
    if (!meta.upvars) throw new Error("Attempted to resolve a component, helper, or modifier, but no free vars were found");
    if (!meta.owner) throw new Error("Attempted to resolve a component, helper, or modifier, but no owner was associated with the template it was being resolved from");
  }
  return meta;
}

/**
 * <Foo/>
 * <Foo></Foo>
 * <Foo @arg={{true}} />
 */
function lookupBuiltInHelper(expr, resolver, meta, constants, type) {
  let {
      upvars: upvars
    } = assertResolverInvariants(meta),
    name = unwrap(upvars[expr[1]]),
    helper = resolver.lookupBuiltInHelper(name);
  if (isDevelopingApp() && null === helper)
    // Keyword helper did not exist, which means that we're attempting to use a
    // value of some kind that is not in scope
    throw debugAssert(!meta.isStrictMode, "Strict mode errors should already be handled at compile time"), new Error(`Attempted to resolve a ${type} in a strict mode template, but that value was not in scope: ${meta.upvars[expr[1]] ?? "{unknown variable}"}`);
  return constants.helper(helper, name);
}
const HighLevelResolutionOpcodes = {
    Modifier: 1003,
    Component: 1004,
    Helper: 1005,
    ComponentOrHelper: 1007,
    OptionalComponentOrHelper: 1008,
    Local: 1010,
    TemplateLocal: 1011
  },
  HighLevelBuilderOpcodes = {
    Label: 1e3,
    StartLabels: 1001,
    StopLabels: 1002,
    Start: 1e3,
    End: 1002
  },
  HighLevelOperands = {
    Label: 1,
    IsStrictMode: 2,
    DebugSymbols: 3,
    Block: 4,
    StdLib: 5,
    NonSmallInt: 6,
    SymbolTable: 7,
    Layout: 8
  };
function labelOperand(value) {
  return {
    type: HighLevelOperands.Label,
    value: value
  };
}
function isStrictMode() {
  return {
    type: HighLevelOperands.IsStrictMode,
    value: void 0
  };
}
function stdlibOperand(value) {
  return {
    type: HighLevelOperands.StdLib,
    value: value
  };
}
function symbolTableOperand(value) {
  return {
    type: HighLevelOperands.SymbolTable,
    value: value
  };
}
function layoutOperand(value) {
  return {
    type: HighLevelOperands.Layout,
    value: value
  };
}
class Labels {
  labels = dict();
  targets = [];
  label(name, index) {
    this.labels[name] = index;
  }
  target(at, target) {
    this.targets.push({
      at: at,
      target: target
    });
  }
  patch(heap) {
    let {
      targets: targets,
      labels: labels
    } = this;
    for (const {
      at: at,
      target: target
    } of targets) {
      let address = labels[target] - at;
      debugAssert(-1 === heap.getbyaddr(at), "Expected heap to contain a placeholder, but it did not"), heap.setbyaddr(at, address);
    }
  }
}
function encodeOp(encoder, constants, resolver, meta, op) {
  if (function (op) {
    return op < HighLevelBuilderOpcodes.Start;
  }(op[0])) {
    let [type, ...operands] = op;
    encoder.push(constants, type, ...operands);
  } else switch (op[0]) {
    case HighLevelBuilderOpcodes.Label:
      return encoder.label(op[1]);
    case HighLevelBuilderOpcodes.StartLabels:
      return encoder.startLabels();
    case HighLevelBuilderOpcodes.StopLabels:
      return encoder.stopLabels();
    case HighLevelResolutionOpcodes.Component:
      return function (resolver, constants, meta, [, expr, then]) {
        debugAssert(isGetFreeComponent(expr), "Attempted to resolve a component with incorrect opcode");
        let type = expr[0];
        if (isDevelopingApp() && expr[0] === opcodes.GetStrictKeyword) throw debugAssert(!meta.isStrictMode, "Strict mode errors should already be handled at compile time"), new Error(`Attempted to resolve a component in a strict mode template, but that value was not in scope: ${meta.upvars[expr[1]] ?? "{unknown variable}"}`);
        if (type === opcodes.GetLexicalSymbol) {
          let {
              scopeValues: scopeValues,
              owner: owner
            } = meta,
            definition = expect(scopeValues, "BUG: scopeValues must exist if template symbol is used")[expr[1]];
          then(constants.component(definition, expect(owner, "BUG: expected owner when resolving component definition")));
        } else {
          let {
              upvars: upvars,
              owner: owner
            } = assertResolverInvariants(meta),
            name = unwrap(upvars[expr[1]]),
            definition = resolver.lookupComponent(name, owner);
          if (isDevelopingApp() && ("object" != typeof definition || null === definition)) throw debugAssert(!meta.isStrictMode, "Strict mode errors should already be handled at compile time"), new Error(`Attempted to resolve \`${name}\`, which was expected to be a component, but nothing was found.`);
          then(constants.resolvedComponent(definition, name));
        }
      }
      /**
      * (helper)
      * (helper arg)
      */(resolver, constants, meta, op);
    case HighLevelResolutionOpcodes.Modifier:
      /**
      * <div {{modifier}}/>
      * <div {{modifier arg}}/>
      * <Foo {{modifier}}/>
      */
      return function (resolver, constants, meta, [, expr, then]) {
        debugAssert(isGetFreeModifier(expr), "Attempted to resolve a modifier with incorrect opcode");
        let type = expr[0];
        if (type === opcodes.GetLexicalSymbol) {
          let {
              scopeValues: scopeValues
            } = meta,
            definition = expect(scopeValues, "BUG: scopeValues must exist if template symbol is used")[expr[1]];
          then(constants.modifier(definition));
        } else if (type === opcodes.GetStrictKeyword) {
          let {
              upvars: upvars
            } = assertResolverInvariants(meta),
            name = unwrap(upvars[expr[1]]),
            modifier = resolver.lookupBuiltInModifier(name);
          if (isDevelopingApp() && null === modifier) throw debugAssert(!meta.isStrictMode, "Strict mode errors should already be handled at compile time"), new Error(`Attempted to resolve a modifier in a strict mode template, but it was not in scope: ${name}`);
          then(constants.modifier(modifier, name));
        } else {
          let {
              upvars: upvars,
              owner: owner
            } = assertResolverInvariants(meta),
            name = unwrap(upvars[expr[1]]),
            modifier = resolver.lookupModifier(name, owner);
          if (isDevelopingApp() && null === modifier) throw debugAssert(!meta.isStrictMode, "Strict mode errors should already be handled at compile time"), new Error(`Attempted to resolve \`${name}\`, which was expected to be a modifier, but nothing was found.`);
          then(constants.modifier(modifier, name));
        }
      }
      /**
      * {{component-or-helper arg}}
      */(resolver, constants, meta, op);
    case HighLevelResolutionOpcodes.Helper:
      return function (resolver, constants, meta, [, expr, then]) {
        debugAssert(isGetFreeHelper(expr), "Attempted to resolve a helper with incorrect opcode");
        let type = expr[0];
        if (type === opcodes.GetLexicalSymbol) {
          let {
              scopeValues: scopeValues
            } = meta,
            definition = expect(scopeValues, "BUG: scopeValues must exist if template symbol is used")[expr[1]];
          then(constants.helper(definition));
        } else if (type === opcodes.GetStrictKeyword) then(lookupBuiltInHelper(expr, resolver, meta, constants, "helper"));else {
          let {
              upvars: upvars,
              owner: owner
            } = assertResolverInvariants(meta),
            name = unwrap(upvars[expr[1]]),
            helper = resolver.lookupHelper(name, owner);
          if (isDevelopingApp() && null === helper) throw debugAssert(!meta.isStrictMode, "Strict mode errors should already be handled at compile time"), new Error(`Attempted to resolve \`${name}\`, which was expected to be a helper, but nothing was found.`);
          then(constants.helper(helper, name));
        }
      }(resolver, constants, meta, op);
    case HighLevelResolutionOpcodes.ComponentOrHelper:
      return function (resolver, constants, meta, [, expr, {
        ifComponent: ifComponent,
        ifHelper: ifHelper
      }]) {
        debugAssert(isGetFreeComponentOrHelper(expr), "Attempted to resolve a component or helper with incorrect opcode");
        let type = expr[0];
        if (type === opcodes.GetLexicalSymbol) {
          let {
              scopeValues: scopeValues,
              owner: owner
            } = meta,
            definition = expect(scopeValues, "BUG: scopeValues must exist if template symbol is used")[expr[1]],
            component = constants.component(definition, expect(owner, "BUG: expected owner when resolving component definition"), !0);
          if (null !== component) return void ifComponent(component);
          let helper = constants.helper(definition, null, !0);
          if (isDevelopingApp() && null === helper) throw debugAssert(!meta.isStrictMode, "Strict mode errors should already be handled at compile time"), new Error(`Attempted to use a value as either a component or helper, but it did not have a component manager or helper manager associated with it. The value was: ${debugToString$1(definition)}`);
          ifHelper(expect(helper, "BUG: helper must exist"));
        } else if (type === opcodes.GetStrictKeyword) ifHelper(lookupBuiltInHelper(expr, resolver, meta, constants, "component or helper"));else {
          let {
              upvars: upvars,
              owner: owner
            } = assertResolverInvariants(meta),
            name = unwrap(upvars[expr[1]]),
            definition = resolver.lookupComponent(name, owner);
          if (null !== definition) ifComponent(constants.resolvedComponent(definition, name));else {
            let helper = resolver.lookupHelper(name, owner);
            if (isDevelopingApp() && null === helper) throw debugAssert(!meta.isStrictMode, "Strict mode errors should already be handled at compile time"), new Error(`Attempted to resolve \`${name}\`, which was expected to be a component or helper, but nothing was found.`);
            ifHelper(constants.helper(helper, name));
          }
        }
      }
      /**
      * {{maybeHelperOrComponent}}
      */(resolver, constants, meta, op);
    case HighLevelResolutionOpcodes.OptionalComponentOrHelper:
      return function (resolver, constants, meta, [, expr, {
        ifComponent: ifComponent,
        ifHelper: ifHelper,
        ifValue: ifValue
      }]) {
        debugAssert(isGetFreeComponentOrHelper(expr), "Attempted to resolve an optional component or helper with incorrect opcode");
        let type = expr[0];
        if (type === opcodes.GetLexicalSymbol) {
          let {
              scopeValues: scopeValues,
              owner: owner
            } = meta,
            definition = expect(scopeValues, "BUG: scopeValues must exist if template symbol is used")[expr[1]];
          if ("function" != typeof definition && ("object" != typeof definition || null === definition))
            // The value is not an object, so it can't be a component or helper.
            return void ifValue(constants.value(definition));
          let component = constants.component(definition, expect(owner, "BUG: expected owner when resolving component definition"), !0);
          if (null !== component) return void ifComponent(component);
          let helper = constants.helper(definition, null, !0);
          if (null !== helper) return void ifHelper(helper);
          ifValue(constants.value(definition));
        } else if (type === opcodes.GetStrictKeyword) ifHelper(lookupBuiltInHelper(expr, resolver, meta, constants, "value"));else {
          let {
              upvars: upvars,
              owner: owner
            } = assertResolverInvariants(meta),
            name = unwrap(upvars[expr[1]]),
            definition = resolver.lookupComponent(name, owner);
          if (null !== definition) return void ifComponent(constants.resolvedComponent(definition, name));
          let helper = resolver.lookupHelper(name, owner);
          null !== helper && ifHelper(constants.helper(helper, name));
        }
      }(resolver, constants, meta, op);
    case HighLevelResolutionOpcodes.Local:
      {
        let freeVar = op[1],
          name = expect(meta.upvars, "BUG: attempted to resolve value but no upvars found")[freeVar];
        (0, op[2])(name, meta.moduleName);
        break;
      }
    case HighLevelResolutionOpcodes.TemplateLocal:
      {
        let [, valueIndex, then] = op,
          value = expect(meta.scopeValues, "BUG: Attempted to get a template local, but template does not have any")[valueIndex];
        then(constants.value(value));
        break;
      }
    default:
      throw new Error(`Unexpected high level opcode ${op[0]}`);
  }
}
class EncoderImpl {
  labelsStack = new StackImpl();
  encoder = new InstructionEncoderImpl([]);
  errors = [];
  handle;
  constructor(heap, meta, stdlib) {
    this.heap = heap, this.meta = meta, this.stdlib = stdlib, this.handle = heap.malloc();
  }
  error(error) {
    this.encoder.encode(Op.Primitive, 0), this.errors.push(error);
  }
  commit(size) {
    let handle = this.handle;
    return this.heap.pushMachine(MachineOp.Return), this.heap.finishMalloc(handle, size), isPresentArray(this.errors) ? {
      errors: this.errors,
      handle: handle
    } : handle;
  }
  push(constants, type, ...args) {
    let {
      heap: heap
    } = this;
    if (isDevelopingApp() && type > TYPE_SIZE) throw new Error(`Opcode type over 8-bits. Got ${type}.`);
    let first = type | (isMachineOp(type) ? MACHINE_MASK : 0) | args.length << ARG_SHIFT;
    heap.pushRaw(first);
    for (let i = 0; i < args.length; i++) {
      let op = args[i];
      heap.pushRaw(this.operand(constants, op));
    }
  }
  operand(constants, operand) {
    if ("number" == typeof operand) return operand;
    if ("object" == typeof operand && null !== operand) {
      if (Array.isArray(operand)) return encodeHandle(constants.array(operand));
      switch (operand.type) {
        case HighLevelOperands.Label:
          return this.currentLabels.target(this.heap.offset, operand.value), -1;
        case HighLevelOperands.IsStrictMode:
          return encodeHandle(constants.value(this.meta.isStrictMode));
        case HighLevelOperands.DebugSymbols:
          return encodeHandle(constants.array(this.meta.evalSymbols || EMPTY_STRING_ARRAY));
        case HighLevelOperands.Block:
          return encodeHandle(constants.value((block = operand.value, containing = this.meta, new CompilableTemplateImpl(block[0], containing, {
            parameters: block[1] || EMPTY_ARRAY
          }))));
        case HighLevelOperands.StdLib:
          return expect(this.stdlib, "attempted to encode a stdlib operand, but the encoder did not have a stdlib. Are you currently building the stdlib?")[operand.value];
        case HighLevelOperands.NonSmallInt:
        case HighLevelOperands.SymbolTable:
        case HighLevelOperands.Layout:
          return constants.value(operand.value);
      }
    }
    var block, containing;
    return encodeHandle(constants.value(operand));
  }
  get currentLabels() {
    return expect(this.labelsStack.current, "bug: not in a label stack");
  }
  label(name) {
    this.currentLabels.label(name, this.heap.offset + 1);
  }
  startLabels() {
    this.labelsStack.push(new Labels());
  }
  stopLabels() {
    expect(this.labelsStack.pop(), "unbalanced push and pop labels").patch(this.heap);
  }
}
class StdLib {
  constructor(main, trustingGuardedAppend, cautiousGuardedAppend, trustingNonDynamicAppend, cautiousNonDynamicAppend) {
    this.main = main, this.trustingGuardedAppend = trustingGuardedAppend, this.cautiousGuardedAppend = cautiousGuardedAppend, this.trustingNonDynamicAppend = trustingNonDynamicAppend, this.cautiousNonDynamicAppend = cautiousNonDynamicAppend;
  }
  get "trusting-append"() {
    return this.trustingGuardedAppend;
  }
  get "cautious-append"() {
    return this.cautiousGuardedAppend;
  }
  get "trusting-non-dynamic-append"() {
    return this.trustingNonDynamicAppend;
  }
  get "cautious-non-dynamic-append"() {
    return this.cautiousNonDynamicAppend;
  }
  getAppend(trusting) {
    return trusting ? this.trustingGuardedAppend : this.cautiousGuardedAppend;
  }
}
class NamedBlocksImpl {
  names;
  constructor(blocks) {
    this.blocks = blocks, this.names = blocks ? Object.keys(blocks) : [];
  }
  get(name) {
    return this.blocks && this.blocks[name] || null;
  }
  has(name) {
    let {
      blocks: blocks
    } = this;
    return null !== blocks && name in blocks;
  }
  with(name, block) {
    let {
      blocks: blocks
    } = this;
    return new NamedBlocksImpl(blocks ? assign({}, blocks, {
      [name]: block
    }) : {
      [name]: block
    });
  }
  get hasAny() {
    return null !== this.blocks;
  }
}
const EMPTY_BLOCKS = new NamedBlocksImpl(null);
function namedBlocks(blocks) {
  if (null === blocks) return EMPTY_BLOCKS;
  let out = dict(),
    [keys, values] = blocks;
  for (const [i, key] of enumerate(keys)) out[key] = unwrap(values[i]);
  return new NamedBlocksImpl(out);
}

/**
 * Push a reference onto the stack corresponding to a statically known primitive
 * @param value A JavaScript primitive (undefined, null, boolean, number or string)
 */
function PushPrimitiveReference(op, value) {
  PushPrimitive(op, value), op(Op.PrimitiveReference);
}

/**
 * Push an encoded representation of a JavaScript primitive on the stack
 *
 * @param value A JavaScript primitive (undefined, null, boolean, number or string)
 */
function PushPrimitive(op, primitive) {
  let p = primitive;
  var value;
  "number" == typeof p && (p = isSmallInt(p) ? encodeImmediate(p) : (debugAssert(!isSmallInt(value = p), "Attempted to make a operand for an int that was not a small int, you should encode this as an immediate"), {
    type: HighLevelOperands.NonSmallInt,
    value: value
  })), op(Op.Primitive, p);
}

/**
 * Invoke a foreign function (a "helper") based on a statically known handle
 *
 * @param op The op creation function
 * @param handle A handle
 * @param positional An optional list of expressions to compile
 * @param named An optional list of named arguments (name + expression) to compile
 */
function Call(op, handle, positional, named) {
  op(MachineOp.PushFrame), SimpleArgs(op, positional, named, !1), op(Op.Helper, handle), op(MachineOp.PopFrame), op(Op.Fetch, $v0);
}

/**
 * Invoke a foreign function (a "helper") based on a dynamically loaded definition
 *
 * @param op The op creation function
 * @param positional An optional list of expressions to compile
 * @param named An optional list of named arguments (name + expression) to compile
 */
function CallDynamic(op, positional, named, append) {
  op(MachineOp.PushFrame), SimpleArgs(op, positional, named, !1), op(Op.Dup, $fp, 1), op(Op.DynamicHelper), append ? (op(Op.Fetch, $v0), append(), op(MachineOp.PopFrame), op(Op.Pop, 1)) : (op(MachineOp.PopFrame), op(Op.Pop, 1), op(Op.Fetch, $v0));
}

/**
 * Evaluate statements in the context of new dynamic scope entries. Move entries from the
 * stack into named entries in the dynamic scope, then evaluate the statements, then pop
 * the dynamic scope
 *
 * @param names a list of dynamic scope names
 * @param block a function that returns a list of statements to evaluate
 */
function Curry(op, type, definition, positional, named) {
  op(MachineOp.PushFrame), SimpleArgs(op, positional, named, !1), op(Op.CaptureArgs), expr(op, definition), op(Op.Curry, type, isStrictMode()), op(MachineOp.PopFrame), op(Op.Fetch, $v0);
}
class Compilers {
  names = {};
  funcs = [];
  add(name, func) {
    this.names[name] = this.funcs.push(func) - 1;
  }
  compile(op, sexp) {
    let name = sexp[0],
      index = unwrap(this.names[name]),
      func = this.funcs[index];
    debugAssert(!!func, `expected an implementation for ${sexp[0]}`), func(op, sexp);
  }
}
const EXPRESSIONS = new Compilers();
function withPath(op, path) {
  if (void 0 !== path && 0 !== path.length) for (let i = 0; i < path.length; i++) op(Op.GetProperty, path[i]);
}
function expr(op, expression) {
  Array.isArray(expression) ? EXPRESSIONS.compile(op, expression) : (PushPrimitive(op, expression), op(Op.PrimitiveReference));
}

/**
 * Compile arguments, pushing an Arguments object onto the stack.
 *
 * @param args.params
 * @param args.hash
 * @param args.blocks
 * @param args.atNames
 */
function SimpleArgs(op, positional, named, atNames) {
  if (null === positional && null === named) return void op(Op.PushEmptyArgs);
  let flags = CompilePositional(op, positional) << 4;
  atNames && (flags |= 8);
  let names = EMPTY_STRING_ARRAY;
  if (named) {
    names = named[0];
    let val = named[1];
    for (let i = 0; i < val.length; i++) expr(op, val[i]);
  }
  op(Op.PushArgs, names, EMPTY_STRING_ARRAY, flags);
}

/**
 * Compile an optional list of positional arguments, which pushes each argument
 * onto the stack and returns the number of parameters compiled
 *
 * @param positional an optional list of positional arguments
 */
function CompilePositional(op, positional) {
  if (null === positional) return 0;
  for (let i = 0; i < positional.length; i++) expr(op, positional[i]);
  return positional.length;
}
function meta(layout) {
  let [, symbols,, upvars] = layout.block;
  return {
    evalSymbols: evalSymbols(layout),
    upvars: upvars,
    scopeValues: layout.scope?.() ?? null,
    isStrictMode: layout.isStrictMode,
    moduleName: layout.moduleName,
    owner: layout.owner,
    size: symbols.length
  };
}
function evalSymbols(layout) {
  let {
      block: block
    } = layout,
    [, symbols, hasEval] = block;
  return hasEval ? symbols : null;
}

/**
 * Yield to a block located at a particular symbol location.
 *
 * @param to the symbol containing the block to yield to
 * @param params optional block parameters to yield to the block
 */
function YieldBlock(op, to, positional) {
  SimpleArgs(op, positional, null, !0), op(Op.GetBlock, to), op(Op.SpreadBlock), op(Op.CompileBlock), op(Op.InvokeYield), op(Op.PopScope), op(MachineOp.PopFrame);
}

/**
 * Push an (optional) yieldable block onto the stack. The yieldable block must be known
 * statically at compile time.
 *
 * @param block An optional Compilable block
 */
function PushYieldableBlock(op, block) {
  !function (op, parameters) {
    null !== parameters ? op(Op.PushSymbolTable, symbolTableOperand({
      parameters: parameters
    })) : PushPrimitive(op, null);
  }(op, block && block[1]), op(Op.PushBlockScope), PushCompilable(op, block);
}

/**
 * Invoke a block that is known statically at compile time.
 *
 * @param block a Compilable block
 */
function InvokeStaticBlock(op, block) {
  op(MachineOp.PushFrame), PushCompilable(op, block), op(Op.CompileBlock), op(MachineOp.InvokeVirtual), op(MachineOp.PopFrame);
}

/**
 * Invoke a static block, preserving some number of stack entries for use in
 * updating.
 *
 * @param block A compilable block
 * @param callerCount A number of stack entries to preserve
 */
function InvokeStaticBlockWithStack(op, block, callerCount) {
  let parameters = block[1],
    calleeCount = parameters.length,
    count = Math.min(callerCount, calleeCount);
  if (0 !== count) {
    if (op(MachineOp.PushFrame), count) {
      op(Op.ChildScope);
      for (let i = 0; i < count; i++) op(Op.Dup, $fp, callerCount - i), op(Op.SetVariable, parameters[i]);
    }
    PushCompilable(op, block), op(Op.CompileBlock), op(MachineOp.InvokeVirtual), count && op(Op.PopScope), op(MachineOp.PopFrame);
  } else InvokeStaticBlock(op, block);
}
function PushCompilable(op, _block) {
  var value;
  null === _block ? PushPrimitive(op, null) : op(Op.Constant, (value = _block, {
    type: HighLevelOperands.Block,
    value: value
  }));
}
function SwitchCases(op, bootstrap, matcher) {
  // Setup the switch DSL
  let clauses = [],
    count = 0;
  // Call the callback
  matcher(function (match, callback) {
    clauses.push({
      match: match,
      callback: callback,
      label: "CLAUSE" + count++
    });
  }),
  // Emit the opcodes for the switch
  op(Op.Enter, 1), bootstrap(), op(HighLevelBuilderOpcodes.StartLabels);
  // First, emit the jump opcodes. We don't need a jump for the last
  // opcode, since it bleeds directly into its clause.
  for (let clause of clauses.slice(0, -1)) op(Op.JumpEq, labelOperand(clause.label), clause.match);
  // Enumerate the clauses in reverse order. Earlier matches will
  // require fewer checks.
  for (let i = clauses.length - 1; i >= 0; i--) {
    let clause = unwrap(clauses[i]);
    op(HighLevelBuilderOpcodes.Label, clause.label), op(Op.Pop, 1), clause.callback(),
    // The first match is special: it is placed directly before the END
    // label, so no additional jump is needed at the end of it.
    0 !== i && op(MachineOp.Jump, labelOperand("END"));
  }
  op(HighLevelBuilderOpcodes.Label, "END"), op(HighLevelBuilderOpcodes.StopLabels), op(Op.Exit);
}

/**
 * A convenience for pushing some arguments on the stack and
 * running some code if the code needs to be re-executed during
 * updating execution if some of the arguments have changed.
 *
 * # Initial Execution
 *
 * The `args` function should push zero or more arguments onto
 * the stack and return the number of arguments pushed.
 *
 * The `body` function provides the instructions to execute both
 * during initial execution and during updating execution.
 *
 * Internally, this function starts by pushing a new frame, so
 * that the body can return and sets the return point ($ra) to
 * the ENDINITIAL label.
 *
 * It then executes the `args` function, which adds instructions
 * responsible for pushing the arguments for the block to the
 * stack. These arguments will be restored to the stack before
 * updating execution.
 *
 * Next, it adds the Enter opcode, which marks the current position
 * in the DOM, and remembers the current $pc (the next instruction)
 * as the first instruction to execute during updating execution.
 *
 * Next, it runs `body`, which adds the opcodes that should
 * execute both during initial execution and during updating execution.
 * If the `body` wishes to finish early, it should Jump to the
 * `FINALLY` label.
 *
 * Next, it adds the FINALLY label, followed by:
 *
 * - the Exit opcode, which finalizes the marked DOM started by the
 *   Enter opcode.
 * - the Return opcode, which returns to the current return point
 *   ($ra).
 *
 * Finally, it adds the ENDINITIAL label followed by the PopFrame
 * instruction, which restores $fp, $sp and $ra.
 *
 * # Updating Execution
 *
 * Updating execution for this `replayable` occurs if the `body` added an
 * assertion, via one of the `JumpIf`, `JumpUnless` or `AssertSame` opcodes.
 *
 * If, during updating executon, the assertion fails, the initial VM is
 * restored, and the stored arguments are pushed onto the stack. The DOM
 * between the starting and ending markers is cleared, and the VM's cursor
 * is set to the area just cleared.
 *
 * The return point ($ra) is set to -1, the exit instruction.
 *
 * Finally, the $pc is set to to the instruction saved off by the
 * Enter opcode during initial execution, and execution proceeds as
 * usual.
 *
 * The only difference is that when a `Return` instruction is
 * encountered, the program jumps to -1 rather than the END label,
 * and the PopFrame opcode is not needed.
 */
function Replayable(op, args, body) {
  // Start a new label frame, to give END and RETURN
  // a unique meaning.
  op(HighLevelBuilderOpcodes.StartLabels), op(MachineOp.PushFrame),
  // If the body invokes a block, its return will return to
  // END. Otherwise, the return in RETURN will return to END.
  op(MachineOp.ReturnTo, labelOperand("ENDINITIAL"));
  // Push the arguments onto the stack. The args() function
  // tells us how many stack elements to retain for re-execution
  // when updating.
  let count = args();
  // Start a new updating closure, remembering `count` elements
  // from the stack. Everything after this point, and before END,
  // will execute both initially and to update the block.

  // The enter and exit opcodes also track the area of the DOM
  // associated with this block. If an assertion inside the block
  // fails (for example, the test value changes from true to false
  // in an #if), the DOM is cleared and the program is re-executed,
  // restoring `count` elements to the stack and executing the
  // instructions between the enter and exit.
  op(Op.Enter, count),
  // Evaluate the body of the block. The body of the block may
  // return, which will jump execution to END during initial
  // execution, and exit the updating routine.
  body(),
  // All execution paths in the body should run the FINALLY once
  // they are done. It is executed both during initial execution
  // and during updating execution.
  op(HighLevelBuilderOpcodes.Label, "FINALLY"),
  // Finalize the DOM.
  op(Op.Exit),
  // In initial execution, this is a noop: it returns to the
  // immediately following opcode. In updating execution, this
  // exits the updating routine.
  op(MachineOp.Return),
  // Cleanup code for the block. Runs on initial execution
  // but not on updating.
  op(HighLevelBuilderOpcodes.Label, "ENDINITIAL"), op(MachineOp.PopFrame), op(HighLevelBuilderOpcodes.StopLabels);
}

/**
 * A specialized version of the `replayable` convenience that allows the
 * caller to provide different code based upon whether the item at
 * the top of the stack is true or false.
 *
 * As in `replayable`, the `ifTrue` and `ifFalse` code can invoke `return`.
 *
 * During the initial execution, a `return` will continue execution
 * in the cleanup code, which finalizes the current DOM block and pops
 * the current frame.
 *
 * During the updating execution, a `return` will exit the updating
 * routine, as it can reuse the DOM block and is always only a single
 * frame deep.
 */
function ReplayableIf(op, args, ifTrue, ifFalse) {
  return Replayable(op, args, () => {
    // If the conditional is false, jump to the ELSE label.
    op(Op.JumpUnless, labelOperand("ELSE")),
    // Otherwise, execute the code associated with the true branch.
    ifTrue(),
    // We're done, so return. In the initial execution, this runs
    // the cleanup code. In the updating VM, it exits the updating
    // routine.
    op(MachineOp.Jump, labelOperand("FINALLY")), op(HighLevelBuilderOpcodes.Label, "ELSE"),
    // If the conditional is false, and code associatied ith the
    // false branch was provided, execute it. If there was no code
    // associated with the false branch, jumping to the else statement
    // has no other behavior.
    void 0 !== ifFalse && ifFalse();
  });
}

// {{component}}
// <Component>
// chokepoint
function InvokeComponent(op, component, _elementBlock, positional, named, _blocks) {
  let {
      compilable: compilable,
      capabilities: capabilities,
      handle: handle
    } = component,
    elementBlock = _elementBlock ? [_elementBlock, []] : null,
    blocks = Array.isArray(_blocks) || null === _blocks ? namedBlocks(_blocks) : _blocks;
  compilable ? (op(Op.PushComponentDefinition, handle), function (op, {
    capabilities: capabilities,
    layout: layout,
    elementBlock: elementBlock,
    positional: positional,
    named: named,
    blocks: blocks
  }) {
    let {
      symbolTable: symbolTable
    } = layout;
    if (symbolTable.hasEval || hasCapability(capabilities, InternalComponentCapabilities.prepareArgs)) return void InvokeNonStaticComponent(op, {
      capabilities: capabilities,
      elementBlock: elementBlock,
      positional: positional,
      named: named,
      atNames: !0,
      blocks: blocks,
      layout: layout
    });
    op(Op.Fetch, $s0), op(Op.Dup, $sp, 1), op(Op.Load, $s0), op(MachineOp.PushFrame);
    // Setup arguments
    let {
        symbols: symbols
      } = symbolTable,
      blockSymbols = [],
      argSymbols = [],
      argNames = [],
      blockNames = blocks.names;
    // As we push values onto the stack, we store the symbols associated  with them
    // so that we can set them on the scope later on with SetVariable and SetBlock
    // Starting with the attrs block, if it exists and is referenced in the component
    if (null !== elementBlock) {
      let symbol = symbols.indexOf("&attrs");
      -1 !== symbol && (PushYieldableBlock(op, elementBlock), blockSymbols.push(symbol));
    }
    // Followed by the other blocks, if they exist and are referenced in the component.
    // Also store the index of the associated symbol.
    for (const name of blockNames) {
      let symbol = symbols.indexOf(`&${name}`);
      -1 !== symbol && (PushYieldableBlock(op, blocks.get(name)), blockSymbols.push(symbol));
    }
    // Next up we have arguments. If the component has the `createArgs` capability,
    // then it wants access to the arguments in JavaScript. We can't know whether
    // or not an argument is used, so we have to give access to all of them.
    if (hasCapability(capabilities, InternalComponentCapabilities.createArgs)) {
      // First we push positional arguments
      let flags = CompilePositional(op, positional) << 4;
      // setup the flags with the count of positionals, and to indicate that atNames
      // are used
      flags |= 8;
      let names = EMPTY_STRING_ARRAY;
      // Next, if named args exist, push them all. If they have an associated symbol
      // in the invoked component (e.g. they are used within its template), we push
      // that symbol. If not, we still push the expression as it may be used, and
      // we store the symbol as -1 (this is used later).
      if (null !== named) {
        names = named[0];
        let val = named[1];
        for (let i = 0; i < val.length; i++) {
          let symbol = symbols.indexOf(unwrap(names[i]));
          expr(op, val[i]), argSymbols.push(symbol);
        }
      }
      // Finally, push the VM arguments themselves. These args won't need access
      // to blocks (they aren't accessible from userland anyways), so we push an
      // empty array instead of the actual block names.
      op(Op.PushArgs, names, EMPTY_STRING_ARRAY, flags),
      // And push an extra pop operation to remove the args before we begin setting
      // variables on the local context
      argSymbols.push(-1);
    } else if (null !== named) {
      // If the component does not have the `createArgs` capability, then the only
      // expressions we need to push onto the stack are those that are actually
      // referenced in the template of the invoked component (e.g. have symbols).
      let names = named[0],
        val = named[1];
      for (let i = 0; i < val.length; i++) {
        let name = unwrap(names[i]),
          symbol = symbols.indexOf(name);
        -1 !== symbol && (expr(op, val[i]), argSymbols.push(symbol), argNames.push(name));
      }
    }
    op(Op.BeginComponentTransaction, $s0), hasCapability(capabilities, InternalComponentCapabilities.dynamicScope) && op(Op.PushDynamicScope), hasCapability(capabilities, InternalComponentCapabilities.createInstance) && op(Op.CreateComponent, 0 | blocks.has("default"), $s0), op(Op.RegisterComponentDestructor, $s0), hasCapability(capabilities, InternalComponentCapabilities.createArgs) ? op(Op.GetComponentSelf, $s0) : op(Op.GetComponentSelf, $s0, argNames),
    // Setup the new root scope for the component
    op(Op.RootScope, symbols.length + 1, Object.keys(blocks).length > 0 ? 1 : 0),
    // Pop the self reference off the stack and set it to the symbol for `this`
    // in the new scope. This is why all subsequent symbols are increased by one.
    op(Op.SetVariable, 0);
    // Going in reverse, now we pop the args/blocks off the stack, starting with
    // arguments, and assign them to their symbols in the new scope.
    for (const symbol of reverse(argSymbols))
    // for (let i = argSymbols.length - 1; i >= 0; i--) {
    //   let symbol = argSymbols[i];
    -1 === symbol ?
    // The expression was not bound to a local symbol, it was only pushed to be
    // used with VM args in the javascript side
    op(Op.Pop, 1) : op(Op.SetVariable, symbol + 1);
    // if any positional params exist, pop them off the stack as well
    null !== positional && op(Op.Pop, positional.length);
    // Finish up by popping off and assigning blocks
    for (const symbol of reverse(blockSymbols)) op(Op.SetBlock, symbol + 1);
    op(Op.Constant, layoutOperand(layout)), op(Op.CompileBlock), op(MachineOp.InvokeVirtual), op(Op.DidRenderLayout, $s0), op(MachineOp.PopFrame), op(Op.PopScope), hasCapability(capabilities, InternalComponentCapabilities.dynamicScope) && op(Op.PopDynamicScope), op(Op.CommitComponentTransaction), op(Op.Load, $s0);
  }(op, {
    capabilities: capabilities,
    layout: compilable,
    elementBlock: elementBlock,
    positional: positional,
    named: named,
    blocks: blocks
  })) : (op(Op.PushComponentDefinition, handle), InvokeNonStaticComponent(op, {
    capabilities: capabilities,
    elementBlock: elementBlock,
    positional: positional,
    named: named,
    atNames: !0,
    blocks: blocks
  }));
}
function InvokeDynamicComponent(op, definition, _elementBlock, positional, named, _blocks, atNames, curried) {
  let elementBlock = _elementBlock ? [_elementBlock, []] : null,
    blocks = Array.isArray(_blocks) || null === _blocks ? namedBlocks(_blocks) : _blocks;
  Replayable(op, () => (expr(op, definition), op(Op.Dup, $sp, 0), 2), () => {
    op(Op.JumpUnless, labelOperand("ELSE")), curried ? op(Op.ResolveCurriedComponent) : op(Op.ResolveDynamicComponent, isStrictMode()), op(Op.PushDynamicComponentInstance), InvokeNonStaticComponent(op, {
      capabilities: !0,
      elementBlock: elementBlock,
      positional: positional,
      named: named,
      atNames: atNames,
      blocks: blocks
    }), op(HighLevelBuilderOpcodes.Label, "ELSE");
  });
}
function InvokeNonStaticComponent(op, {
  capabilities: capabilities,
  elementBlock: elementBlock,
  positional: positional,
  named: named,
  atNames: atNames,
  blocks: namedBlocks,
  layout: layout
}) {
  let bindableBlocks = !!namedBlocks,
    bindableAtNames = !0 === capabilities || hasCapability(capabilities, InternalComponentCapabilities.prepareArgs) || !(!named || 0 === named[0].length),
    blocks = namedBlocks.with("attrs", elementBlock);
  op(Op.Fetch, $s0), op(Op.Dup, $sp, 1), op(Op.Load, $s0), op(MachineOp.PushFrame), function (op, positional, named, blocks, atNames) {
    let blockNames = blocks.names;
    for (const name of blockNames) PushYieldableBlock(op, blocks.get(name));
    let flags = CompilePositional(op, positional) << 4;
    atNames && (flags |= 8), blocks && (flags |= 7);
    let names = EMPTY_ARRAY;
    if (named) {
      names = named[0];
      let val = named[1];
      for (let i = 0; i < val.length; i++) expr(op, val[i]);
    }
    op(Op.PushArgs, names, blockNames, flags);
  }(op, positional, named, blocks, atNames), op(Op.PrepareArgs, $s0), invokePreparedComponent(op, blocks.has("default"), bindableBlocks, bindableAtNames, () => {
    layout ? (op(Op.PushSymbolTable, symbolTableOperand(layout.symbolTable)), op(Op.Constant, layoutOperand(layout)), op(Op.CompileBlock)) : op(Op.GetComponentLayout, $s0), op(Op.PopulateLayout, $s0);
  }), op(Op.Load, $s0);
}
function invokePreparedComponent(op, hasBlock, bindableBlocks, bindableAtNames, populateLayout = null) {
  op(Op.BeginComponentTransaction, $s0), op(Op.PushDynamicScope), op(Op.CreateComponent, 0 | hasBlock, $s0),
  // this has to run after createComponent to allow
  // for late-bound layouts, but a caller is free
  // to populate the layout earlier if it wants to
  // and do nothing here.
  populateLayout && populateLayout(), op(Op.RegisterComponentDestructor, $s0), op(Op.GetComponentSelf, $s0), op(Op.VirtualRootScope, $s0), op(Op.SetVariable, 0), op(Op.SetupForEval, $s0), bindableAtNames && op(Op.SetNamedVariables, $s0), bindableBlocks && op(Op.SetBlocks, $s0), op(Op.Pop, 1), op(Op.InvokeComponentLayout, $s0), op(Op.DidRenderLayout, $s0), op(MachineOp.PopFrame), op(Op.PopScope), op(Op.PopDynamicScope), op(Op.CommitComponentTransaction);
}

/**
 * Append content to the DOM. This standard function triages content and does the
 * right thing based upon whether it's a string, safe string, component, fragment
 * or node.
 *
 * @param trusting whether to interpolate a string as raw HTML (corresponds to
 * triple curlies)
 */
function StdAppend(op, trusting, nonDynamicAppend) {
  SwitchCases(op, () => op(Op.ContentType), when => {
    when(ContentType.String, () => {
      trusting ? (op(Op.AssertSame), op(Op.AppendHTML)) : op(Op.AppendText);
    }), "number" == typeof nonDynamicAppend ? (when(ContentType.Component, () => {
      op(Op.ResolveCurriedComponent), op(Op.PushDynamicComponentInstance), function (op) {
        op(Op.Fetch, $s0), op(Op.Dup, $sp, 1), op(Op.Load, $s0), op(MachineOp.PushFrame), op(Op.PushEmptyArgs), op(Op.PrepareArgs, $s0), invokePreparedComponent(op, !1, !1, !0, () => {
          op(Op.GetComponentLayout, $s0), op(Op.PopulateLayout, $s0);
        }), op(Op.Load, $s0);
      }(op);
    }), when(ContentType.Helper, () => {
      CallDynamic(op, null, null, () => {
        op(MachineOp.InvokeStatic, nonDynamicAppend);
      });
    })) : (
    // when non-dynamic, we can no longer call the value (potentially because we've already called it)
    // this prevents infinite loops. We instead coerce the value, whatever it is, into the DOM.
    when(ContentType.Component, () => {
      op(Op.AppendText);
    }), when(ContentType.Helper, () => {
      op(Op.AppendText);
    })), when(ContentType.SafeString, () => {
      op(Op.AssertSame), op(Op.AppendSafeHTML);
    }), when(ContentType.Fragment, () => {
      op(Op.AssertSame), op(Op.AppendDocumentFragment);
    }), when(ContentType.Node, () => {
      op(Op.AssertSame), op(Op.AppendNode);
    });
  });
}
function compileStd(context) {
  let mainHandle = build(context, op => function (op) {
      op(Op.Main, $s0), invokePreparedComponent(op, !1, !1, !0);
    }(op)),
    trustingGuardedNonDynamicAppend = build(context, op => StdAppend(op, !0, null)),
    cautiousGuardedNonDynamicAppend = build(context, op => StdAppend(op, !1, null)),
    trustingGuardedDynamicAppend = build(context, op => StdAppend(op, !0, trustingGuardedNonDynamicAppend)),
    cautiousGuardedDynamicAppend = build(context, op => StdAppend(op, !1, cautiousGuardedNonDynamicAppend));
  return new StdLib(mainHandle, trustingGuardedDynamicAppend, cautiousGuardedDynamicAppend, trustingGuardedNonDynamicAppend, cautiousGuardedNonDynamicAppend);
}
EXPRESSIONS.add(opcodes.Concat, (op, [, parts]) => {
  for (let part of parts) expr(op, part);
  op(Op.Concat, parts.length);
}), EXPRESSIONS.add(opcodes.Call, (op, [, expression, positional, named]) => {
  isGetFreeHelper(expression) ? op(HighLevelResolutionOpcodes.Helper, expression, handle => {
    Call(op, handle, positional, named);
  }) : (expr(op, expression), CallDynamic(op, positional, named));
}), EXPRESSIONS.add(opcodes.Curry, (op, [, expr, type, positional, named]) => {
  Curry(op, type, expr, positional, named);
}), EXPRESSIONS.add(opcodes.GetSymbol, (op, [, sym, path]) => {
  op(Op.GetVariable, sym), withPath(op, path);
}), EXPRESSIONS.add(opcodes.GetLexicalSymbol, (op, [, sym, path]) => {
  op(HighLevelResolutionOpcodes.TemplateLocal, sym, handle => {
    op(Op.ConstantReference, handle), withPath(op, path);
  });
}), EXPRESSIONS.add(opcodes.GetStrictKeyword, (op, expr) => {
  op(HighLevelResolutionOpcodes.Local, expr[1], _name => {
    op(HighLevelResolutionOpcodes.Helper, expr, handle => {
      Call(op, handle, null, null);
    });
  });
}), EXPRESSIONS.add(opcodes.GetFreeAsHelperHead, (op, expr) => {
  op(HighLevelResolutionOpcodes.Local, expr[1], _name => {
    op(HighLevelResolutionOpcodes.Helper, expr, handle => {
      Call(op, handle, null, null);
    });
  });
}), EXPRESSIONS.add(opcodes.Undefined, op => PushPrimitiveReference(op, void 0)), EXPRESSIONS.add(opcodes.HasBlock, (op, [, block]) => {
  expr(op, block), op(Op.HasBlock);
}), EXPRESSIONS.add(opcodes.HasBlockParams, (op, [, block]) => {
  expr(op, block), op(Op.SpreadBlock), op(Op.CompileBlock), op(Op.HasBlockParams);
}), EXPRESSIONS.add(opcodes.IfInline, (op, [, condition, truthy, falsy]) => {
  // Push in reverse order
  expr(op, falsy), expr(op, truthy), expr(op, condition), op(Op.IfInline);
}), EXPRESSIONS.add(opcodes.Not, (op, [, value]) => {
  expr(op, value), op(Op.Not);
}), EXPRESSIONS.add(opcodes.GetDynamicVar, (op, [, expression]) => {
  expr(op, expression), op(Op.GetDynamicVar);
}), EXPRESSIONS.add(opcodes.Log, (op, [, positional]) => {
  op(MachineOp.PushFrame), SimpleArgs(op, positional, null, !1), op(Op.Log), op(MachineOp.PopFrame), op(Op.Fetch, $v0);
});
const STDLIB_META = {
  evalSymbols: null,
  upvars: null,
  moduleName: "stdlib",
  // TODO: ??
  scopeValues: null,
  isStrictMode: !0,
  owner: null,
  size: 0
};
function build(program, builder) {
  let {
      constants: constants,
      heap: heap,
      resolver: resolver
    } = program,
    encoder = new EncoderImpl(heap, STDLIB_META);
  builder(function (...op) {
    encodeOp(encoder, constants, resolver, STDLIB_META, op);
  });
  let result = encoder.commit(0);
  if ("number" != typeof result)
    // This shouldn't be possible
    throw new Error("Unexpected errors compiling std");
  return result;
}
class CompileTimeCompilationContextImpl {
  constants;
  heap;
  stdlib;
  constructor({
    constants: constants,
    heap: heap
  }, resolver, createOp) {
    this.resolver = resolver, this.createOp = createOp, this.constants = constants, this.heap = heap, this.stdlib = compileStd(this);
  }
}
function programCompilationContext(artifacts, resolver, createOp) {
  return new CompileTimeCompilationContextImpl(artifacts, resolver, createOp);
}
function templateCompilationContext(program, meta) {
  return {
    program: program,
    encoder: new EncoderImpl(program.heap, meta, program.stdlib),
    meta: meta
  };
}
const STATEMENTS = new Compilers(),
  INFLATE_ATTR_TABLE = ["class", "id", "value", "name", "type", "style", "href"],
  INFLATE_TAG_TABLE = ["div", "span", "p", "a"];
function inflateTagName(tagName) {
  return "string" == typeof tagName ? tagName : INFLATE_TAG_TABLE[tagName];
}
function inflateAttrName(attrName) {
  return "string" == typeof attrName ? attrName : INFLATE_ATTR_TABLE[attrName];
}
function hashToArgs(hash) {
  return null === hash ? null : [hash[0].map(key => `@${key}`), hash[1]];
}
STATEMENTS.add(opcodes.Comment, (op, sexp) => op(Op.Comment, sexp[1])), STATEMENTS.add(opcodes.CloseElement, op => op(Op.CloseElement)), STATEMENTS.add(opcodes.FlushElement, op => op(Op.FlushElement)), STATEMENTS.add(opcodes.Modifier, (op, [, expression, positional, named]) => {
  isGetFreeModifier(expression) ? op(HighLevelResolutionOpcodes.Modifier, expression, handle => {
    op(MachineOp.PushFrame), SimpleArgs(op, positional, named, !1), op(Op.Modifier, handle), op(MachineOp.PopFrame);
  }) : (expr(op, expression), op(MachineOp.PushFrame), SimpleArgs(op, positional, named, !1), op(Op.Dup, $fp, 1), op(Op.DynamicModifier), op(MachineOp.PopFrame));
}), STATEMENTS.add(opcodes.StaticAttr, (op, [, name, value, namespace]) => {
  op(Op.StaticAttr, inflateAttrName(name), value, namespace ?? null);
}), STATEMENTS.add(opcodes.StaticComponentAttr, (op, [, name, value, namespace]) => {
  op(Op.StaticComponentAttr, inflateAttrName(name), value, namespace ?? null);
}), STATEMENTS.add(opcodes.DynamicAttr, (op, [, name, value, namespace]) => {
  expr(op, value), op(Op.DynamicAttr, inflateAttrName(name), !1, namespace ?? null);
}), STATEMENTS.add(opcodes.TrustingDynamicAttr, (op, [, name, value, namespace]) => {
  expr(op, value), op(Op.DynamicAttr, inflateAttrName(name), !0, namespace ?? null);
}), STATEMENTS.add(opcodes.ComponentAttr, (op, [, name, value, namespace]) => {
  expr(op, value), op(Op.ComponentAttr, inflateAttrName(name), !1, namespace ?? null);
}), STATEMENTS.add(opcodes.TrustingComponentAttr, (op, [, name, value, namespace]) => {
  expr(op, value), op(Op.ComponentAttr, inflateAttrName(name), !0, namespace ?? null);
}), STATEMENTS.add(opcodes.OpenElement, (op, [, tag]) => {
  op(Op.OpenElement, inflateTagName(tag));
}), STATEMENTS.add(opcodes.OpenElementWithSplat, (op, [, tag]) => {
  op(Op.PutComponentOperations), op(Op.OpenElement, inflateTagName(tag));
}), STATEMENTS.add(opcodes.Component, (op, [, expr, elementBlock, named, blocks]) => {
  isGetFreeComponent(expr) ? op(HighLevelResolutionOpcodes.Component, expr, component => {
    InvokeComponent(op, component, elementBlock, null, named, blocks);
  }) :
  // otherwise, the component name was an expression, so resolve the expression
  // and invoke it as a dynamic component
  InvokeDynamicComponent(op, expr, elementBlock, null, named, blocks, !0, !0);
}), STATEMENTS.add(opcodes.Yield, (op, [, to, params]) => YieldBlock(op, to, params)), STATEMENTS.add(opcodes.AttrSplat, (op, [, to]) => YieldBlock(op, to, null)), STATEMENTS.add(opcodes.Debugger, (op, [, debugInfo]) => op(Op.Debugger, {
  type: HighLevelOperands.DebugSymbols,
  value: void 0
}, debugInfo)), STATEMENTS.add(opcodes.Append, (op, [, value]) => {
  // Special case for static values
  if (Array.isArray(value)) {
    if (isGetFreeComponentOrHelper(value)) op(HighLevelResolutionOpcodes.OptionalComponentOrHelper, value, {
      ifComponent(component) {
        InvokeComponent(op, component, null, null, null, null);
      },
      ifHelper(handle) {
        op(MachineOp.PushFrame), Call(op, handle, null, null), op(MachineOp.InvokeStatic, stdlibOperand("cautious-non-dynamic-append")), op(MachineOp.PopFrame);
      },
      ifValue(handle) {
        op(MachineOp.PushFrame), op(Op.ConstantReference, handle), op(MachineOp.InvokeStatic, stdlibOperand("cautious-non-dynamic-append")), op(MachineOp.PopFrame);
      }
    });else if (value[0] === opcodes.Call) {
      let [, expression, positional, named] = value;
      isGetFreeComponentOrHelper(expression) ? op(HighLevelResolutionOpcodes.ComponentOrHelper, expression, {
        ifComponent(component) {
          InvokeComponent(op, component, null, positional, hashToArgs(named), null);
        },
        ifHelper(handle) {
          op(MachineOp.PushFrame), Call(op, handle, positional, named), op(MachineOp.InvokeStatic, stdlibOperand("cautious-non-dynamic-append")), op(MachineOp.PopFrame);
        }
      }) : SwitchCases(op, () => {
        expr(op, expression), op(Op.DynamicContentType);
      }, when => {
        when(ContentType.Component, () => {
          op(Op.ResolveCurriedComponent), op(Op.PushDynamicComponentInstance), InvokeNonStaticComponent(op, {
            capabilities: !0,
            elementBlock: null,
            positional: positional,
            named: named,
            atNames: !1,
            blocks: namedBlocks(null)
          });
        }), when(ContentType.Helper, () => {
          CallDynamic(op, positional, named, () => {
            op(MachineOp.InvokeStatic, stdlibOperand("cautious-non-dynamic-append"));
          });
        });
      });
    } else op(MachineOp.PushFrame), expr(op, value), op(MachineOp.InvokeStatic, stdlibOperand("cautious-append")), op(MachineOp.PopFrame);
  } else op(Op.Text, null == value ? "" : String(value));
}), STATEMENTS.add(opcodes.TrustingAppend, (op, [, value]) => {
  Array.isArray(value) ? (op(MachineOp.PushFrame), expr(op, value), op(MachineOp.InvokeStatic, stdlibOperand("trusting-append")), op(MachineOp.PopFrame)) : op(Op.Text, null == value ? "" : String(value));
}), STATEMENTS.add(opcodes.Block, (op, [, expr, positional, named, blocks]) => {
  isGetFreeComponent(expr) ? op(HighLevelResolutionOpcodes.Component, expr, component => {
    InvokeComponent(op, component, null, positional, hashToArgs(named), blocks);
  }) : InvokeDynamicComponent(op, expr, null, positional, named, blocks, !1, !1);
}), STATEMENTS.add(opcodes.InElement, (op, [, block, guid, destination, insertBefore]) => {
  ReplayableIf(op, () => (expr(op, guid), void 0 === insertBefore ? PushPrimitiveReference(op, void 0) : expr(op, insertBefore), expr(op, destination), op(Op.Dup, $sp, 0), 4), () => {
    op(Op.PushRemoteElement), InvokeStaticBlock(op, block), op(Op.PopRemoteElement);
  });
}), STATEMENTS.add(opcodes.If, (op, [, condition, block, inverse]) => ReplayableIf(op, () => (expr(op, condition), op(Op.ToBoolean), 1), () => {
  InvokeStaticBlock(op, block);
}, inverse ? () => {
  InvokeStaticBlock(op, inverse);
} : void 0)), STATEMENTS.add(opcodes.Each, (op, [, value, key, block, inverse]) => Replayable(op, () => (key ? expr(op, key) : PushPrimitiveReference(op, null), expr(op, value), 2), () => {
  op(Op.EnterList, labelOperand("BODY"), labelOperand("ELSE")), op(MachineOp.PushFrame), op(Op.Dup, $fp, 1), op(MachineOp.ReturnTo, labelOperand("ITER")), op(HighLevelBuilderOpcodes.Label, "ITER"), op(Op.Iterate, labelOperand("BREAK")), op(HighLevelBuilderOpcodes.Label, "BODY"), InvokeStaticBlockWithStack(op, block, 2), op(Op.Pop, 2), op(MachineOp.Jump, labelOperand("FINALLY")), op(HighLevelBuilderOpcodes.Label, "BREAK"), op(MachineOp.PopFrame), op(Op.ExitList), op(MachineOp.Jump, labelOperand("FINALLY")), op(HighLevelBuilderOpcodes.Label, "ELSE"), inverse && InvokeStaticBlock(op, inverse);
})), STATEMENTS.add(opcodes.Let, (op, [, positional, block]) => {
  InvokeStaticBlockWithStack(op, block, CompilePositional(op, positional));
}), STATEMENTS.add(opcodes.WithDynamicVars, (op, [, named, block]) => {
  if (named) {
    let [names, expressions] = named;
    CompilePositional(op, expressions), function (op, names, block) {
      op(Op.PushDynamicScope), op(Op.BindDynamicScope, names), block(), op(Op.PopDynamicScope);
    }(op, names, () => {
      InvokeStaticBlock(op, block);
    });
  } else InvokeStaticBlock(op, block);
}), STATEMENTS.add(opcodes.InvokeComponent, (op, [, expr, positional, named, blocks]) => {
  isGetFreeComponent(expr) ? op(HighLevelResolutionOpcodes.Component, expr, component => {
    InvokeComponent(op, component, null, positional, hashToArgs(named), blocks);
  }) : InvokeDynamicComponent(op, expr, null, positional, named, blocks, !1, !1);
});
class CompilableTemplateImpl {
  compiled = null;
  constructor(statements, meta,
  // Part of CompilableTemplate
  symbolTable,
  // Used for debugging
  moduleName = "plain block") {
    this.statements = statements, this.meta = meta, this.symbolTable = symbolTable, this.moduleName = moduleName;
  }
  // Part of CompilableTemplate
  compile(context) {
    return function (compilable, context) {
      if (null !== compilable.compiled) return compilable.compiled;
      compilable.compiled = -1;
      let {
          statements: statements,
          meta: meta
        } = compilable,
        result = compileStatements(statements, meta, context);
      return compilable.compiled = result, result;
    }(this, context);
  }
}
function compilable(layout, moduleName) {
  let [statements, symbols, hasEval] = layout.block;
  return new CompilableTemplateImpl(statements, meta(layout), {
    symbols: symbols,
    hasEval: hasEval
  }, moduleName);
}
function compileStatements(statements, meta, syntaxContext) {
  let sCompiler = STATEMENTS,
    context = templateCompilationContext(syntaxContext, meta),
    {
      encoder: encoder,
      program: {
        constants: constants,
        resolver: resolver
      }
    } = context;
  function pushOp(...op) {
    encodeOp(encoder, constants, resolver, meta, op);
  }
  for (const statement of statements) sCompiler.compile(pushOp, statement);
  return context.encoder.commit(meta.size);
}
const DEFAULT_CAPABILITIES = {
    dynamicLayout: !0,
    dynamicTag: !0,
    prepareArgs: !0,
    createArgs: !0,
    attributeHook: !1,
    elementHook: !1,
    dynamicScope: !0,
    createCaller: !1,
    updateHook: !0,
    createInstance: !0,
    wrapped: !1,
    willDestroy: !1,
    hasSubOwner: !1
  },
  MINIMAL_CAPABILITIES = {
    dynamicLayout: !1,
    dynamicTag: !1,
    prepareArgs: !1,
    createArgs: !1,
    attributeHook: !1,
    elementHook: !1,
    dynamicScope: !1,
    createCaller: !1,
    updateHook: !1,
    createInstance: !1,
    wrapped: !1,
    willDestroy: !1,
    hasSubOwner: !1
  };
class WrappedBuilder {
  symbolTable;
  compiled = null;
  attrsBlockNumber;
  constructor(layout, moduleName) {
    this.layout = layout, this.moduleName = moduleName;
    let {
        block: block
      } = layout,
      [, symbols, hasEval] = block;
    symbols = symbols.slice();
    // ensure ATTRS_BLOCK is always included (only once) in the list of symbols
    let attrsBlockIndex = symbols.indexOf("&attrs");
    this.attrsBlockNumber = -1 === attrsBlockIndex ? symbols.push("&attrs") : attrsBlockIndex + 1, this.symbolTable = {
      hasEval: hasEval,
      symbols: symbols
    };
  }
  compile(syntax) {
    if (null !== this.compiled) return this.compiled;
    let m = meta(this.layout),
      context = templateCompilationContext(syntax, m),
      {
        encoder: encoder,
        program: {
          constants: constants,
          resolver: resolver
        }
      } = context;
    var op, layout, attrsBlockNumber;
    op = function (...op) {
      encodeOp(encoder, constants, resolver, m, op);
    }, layout = this.layout, attrsBlockNumber = this.attrsBlockNumber, op(HighLevelBuilderOpcodes.StartLabels), function (op, register, block) {
      op(Op.Fetch, register), block(), op(Op.Load, register);
    }(op, $s1, () => {
      op(Op.GetComponentTagName, $s0), op(Op.PrimitiveReference), op(Op.Dup, $sp, 0);
    }), op(Op.JumpUnless, labelOperand("BODY")), op(Op.Fetch, $s1), op(Op.PutComponentOperations), op(Op.OpenDynamicElement), op(Op.DidCreateElement, $s0), YieldBlock(op, attrsBlockNumber, null), op(Op.FlushElement), op(HighLevelBuilderOpcodes.Label, "BODY"), InvokeStaticBlock(op, [layout.block[0], []]), op(Op.Fetch, $s1), op(Op.JumpUnless, labelOperand("END")), op(Op.CloseElement), op(HighLevelBuilderOpcodes.Label, "END"), op(Op.Load, $s1), op(HighLevelBuilderOpcodes.StopLabels);
    let handle = context.encoder.commit(m.size);
    return "number" != typeof handle || (this.compiled = handle), handle;
  }
}
let clientId = 0,
  templateCacheCounters = {
    cacheHit: 0,
    cacheMiss: 0
  };

// These interfaces are for backwards compatibility, some addons use these intimate APIs
/**
 * Wraps a template js in a template module to change it into a factory
 * that handles lazy parsing the template and to create per env singletons
 * of the template.
 */
function templateFactory({
  id: templateId,
  moduleName: moduleName,
  block: block,
  scope: scope,
  isStrictMode: isStrictMode
}) {
  // TODO(template-refactors): This should be removed in the near future, as it
  // appears that id is unused. It is currently kept for backwards compat reasons.
  let parsedBlock,
    id = templateId || "client-" + clientId++,
    ownerlessTemplate = null,
    templateCache = new WeakMap(),
    factory = owner => {
      if (void 0 === parsedBlock && (parsedBlock = JSON.parse(block)), void 0 === owner) return null === ownerlessTemplate ? (templateCacheCounters.cacheMiss++, ownerlessTemplate = new TemplateImpl({
        id: id,
        block: parsedBlock,
        moduleName: moduleName,
        owner: null,
        scope: scope,
        isStrictMode: isStrictMode
      })) : templateCacheCounters.cacheHit++, ownerlessTemplate;
      let result = templateCache.get(owner);
      return void 0 === result ? (templateCacheCounters.cacheMiss++, result = new TemplateImpl({
        id: id,
        block: parsedBlock,
        moduleName: moduleName,
        owner: owner,
        scope: scope,
        isStrictMode: isStrictMode
      }), templateCache.set(owner, result)) : templateCacheCounters.cacheHit++, result;
    };
  // TODO: This caches JSON serialized output once in case a template is
  // compiled by multiple owners, but we haven't verified if this is actually
  // helpful. We should benchmark this in the future.
  return factory.__id = id, factory.__meta = {
    moduleName: moduleName
  }, factory;
}
class TemplateImpl {
  result = "ok";
  layout = null;
  wrappedLayout = null;
  constructor(parsedLayout) {
    this.parsedLayout = parsedLayout;
  }
  get moduleName() {
    return this.parsedLayout.moduleName;
  }
  get id() {
    return this.parsedLayout.id;
  }
  // TODO(template-refactors): This should be removed in the near future, it is
  // only being exposed for backwards compatibility
  get referrer() {
    return {
      moduleName: this.parsedLayout.moduleName,
      owner: this.parsedLayout.owner
    };
  }
  asLayout() {
    return this.layout ? this.layout : this.layout = compilable(assign({}, this.parsedLayout), this.moduleName);
  }
  asWrappedLayout() {
    return this.wrappedLayout ? this.wrappedLayout : this.wrappedLayout = new WrappedBuilder(assign({}, this.parsedLayout), this.moduleName);
  }
}

export { CompileTimeCompilationContextImpl, DEFAULT_CAPABILITIES, EMPTY_BLOCKS, MINIMAL_CAPABILITIES, StdLib, WrappedBuilder, compilable, compileStatements, compileStd, debugCompiler, InvokeStaticBlock as invokeStaticBlock, InvokeStaticBlockWithStack as invokeStaticBlockWithStack, meta, programCompilationContext, templateCacheCounters, templateCompilationContext, templateFactory };