ember-source
Version:
A JavaScript framework for creating ambitious web applications
1,603 lines (1,571 loc) • 116 kB
JavaScript
import { r as VM_SYSCALL_SIZE, C as CURRIED_COMPONENT, s as VM_CHILD_SCOPE_OP, t as VM_POP_SCOPE_OP, u as VM_PUSH_DYNAMIC_SCOPE_OP, v as VM_POP_DYNAMIC_SCOPE_OP, w as VM_CONSTANT_OP, x as decodeHandle, y as VM_CONSTANT_REFERENCE_OP, z as VM_PRIMITIVE_OP, A as isHandle, B as decodeImmediate, D as VM_PRIMITIVE_REFERENCE_OP, E as VM_DUP_OP, F as VM_POP_OP, G as VM_LOAD_OP, H as VM_FETCH_OP, I as VM_BIND_DYNAMIC_SCOPE_OP, J as VM_ENTER_OP, K as VM_EXIT_OP, L as VM_PUSH_SYMBOL_TABLE_OP, M as VM_PUSH_BLOCK_SCOPE_OP, N as VM_COMPILE_BLOCK_OP, O as VM_INVOKE_YIELD_OP, P as VM_JUMP_IF_OP, Q as VM_JUMP_UNLESS_OP, R as VM_JUMP_EQ_OP, b as VM_ASSERT_SAME_OP, S as VM_TO_BOOLEAN_OP, T as VM_TEXT_OP, U as VM_COMMENT_OP, W as VM_OPEN_ELEMENT_OP, X as VM_OPEN_DYNAMIC_ELEMENT_OP, Y as VM_PUSH_REMOTE_ELEMENT_OP, Z as VM_POP_REMOTE_ELEMENT_OP, _ as VM_FLUSH_ELEMENT_OP, $ as VM_CLOSE_ELEMENT_OP, a0 as VM_MODIFIER_OP, a1 as VM_DYNAMIC_MODIFIER_OP, a2 as VM_STATIC_ATTR_OP, a3 as VM_DYNAMIC_ATTR_OP, a4 as CURRIED_MODIFIER, a5 as VM_PUSH_COMPONENT_DEFINITION_OP, a6 as VM_RESOLVE_DYNAMIC_COMPONENT_OP, f as VM_RESOLVE_CURRIED_COMPONENT_OP, g as VM_PUSH_DYNAMIC_COMPONENT_INSTANCE_OP, a7 as VM_PUSH_ARGS_OP, a8 as VM_PUSH_EMPTY_ARGS_OP, a9 as VM_CAPTURE_ARGS_OP, aa as VM_PREPARE_ARGS_OP, ab as VM_CREATE_COMPONENT_OP, ac as VM_REGISTER_COMPONENT_DESTRUCTOR_OP, ad as VM_BEGIN_COMPONENT_TRANSACTION_OP, ae as VM_PUT_COMPONENT_OPERATIONS_OP, af as VM_COMPONENT_ATTR_OP, ag as VM_STATIC_COMPONENT_ATTR_OP, ah as VM_DID_CREATE_ELEMENT_OP, ai as VM_GET_COMPONENT_SELF_OP, aj as VM_GET_COMPONENT_TAG_NAME_OP, ak as VM_GET_COMPONENT_LAYOUT_OP, V as VM_MAIN_OP, al as VM_POPULATE_LAYOUT_OP, am as VM_VIRTUAL_ROOT_SCOPE_OP, an as VM_SET_NAMED_VARIABLES_OP, ao as VM_SET_BLOCKS_OP, ap as VM_INVOKE_COMPONENT_LAYOUT_OP, aq as VM_DID_RENDER_LAYOUT_OP, ar as VM_COMMIT_COMPONENT_TRANSACTION_OP, as as VM_CURRY_OP, at as VM_DYNAMIC_HELPER_OP, au as CURRIED_HELPER, av as VM_HELPER_OP, aw as VM_GET_VARIABLE_OP, ax as VM_SET_VARIABLE_OP, ay as VM_SET_BLOCK_OP, az as VM_ROOT_SCOPE_OP, aA as VM_GET_PROPERTY_OP, aB as VM_GET_BLOCK_OP, aC as VM_SPREAD_BLOCK_OP, aD as VM_HAS_BLOCK_OP, aE as VM_HAS_BLOCK_PARAMS_OP, aF as VM_CONCAT_OP, aG as VM_IF_INLINE_OP, aH as VM_NOT_OP, aI as VM_GET_DYNAMIC_VAR_OP, aJ as VM_LOG_OP, a as VM_CONTENT_TYPE_OP, aK as VM_DYNAMIC_CONTENT_TYPE_OP, d as VM_APPEND_HTML_OP, h as VM_APPEND_SAFE_HTML_OP, e as VM_APPEND_TEXT_OP, i as VM_APPEND_DOCUMENT_FRAGMENT_OP, j as VM_APPEND_NODE_OP, aL as VM_DEBUGGER_OP, aM as VM_ENTER_LIST_OP, aN as VM_EXIT_LIST_OP, aO as VM_ITERATE_OP } from './fragment-Cc5k9Oy4.js';
import { d as debugToString } from './debug-to-string-CFb7h0lY.js';
import { u as unwrap, e as expect, a as isIndexable$1, S as StackImpl, d as dict, i as isDict } from './collections-D_nY_0UJ.js';
import { a as assign } from './object-utils-AijlD-JH.js';
import { toBool, debugAssert, setPath, getPath, warnIfStyleNotTrusted } from '../@glimmer/global-context/index.js';
import { CONSTANT_TAG, INITIAL, validateTag, consumeTag, valueForTag, beginTrackFrame, endTrackFrame, CURRENT_TAG, createUpdatableTag } from '../@glimmer/validator/index.js';
import { c as createComputeRef, v as valueForRef, a as createConstRef, d as createPrimitiveRef, i as isConstRef, U as UNDEFINED_REFERENCE, N as NULL_REFERENCE, T as TRUE_REFERENCE, F as FALSE_REFERENCE, R as REFERENCE, e as createDebugAliasRef, b as childRefFor, f as isInvokableRef, u as updateRef } from './reference-C3TKDRnP.js';
import { e as $v0, f as $t1, g as $t0, a as $pc, b as $ra, c as $fp, d as $sp, h as $s1, $ as $s0 } from './registers-ylirb0dq.js';
import { registerDestructor, destroy, associateDestroyableChild, _hasDestroyableChildren } from '../@glimmer/destroyable/index.js';
import { d as getInternalModifierManager, g as getInternalHelperManager, b as hasInternalComponentManager, f as hasInternalHelperManager, s as setInternalComponentManager, i as setInternalHelperManager, j as setInternalModifierManager } from './api-BqXkkT0p.js';
import { m as managerHasCapability } from './capabilities-O_xc7Yqk.js';
import { ContentType } from '../@glimmer/vm/index.js';
import { createIteratorRef } from '../@glimmer/reference/index.js';
import { s as setLocalDebugType } from './debug-brand-B1TWjOCH.js';
import { b as EMPTY_STRING_ARRAY, e as enumerate, c as emptyArray } from './array-utils-CZQxrdD3.js';
import { a as assert } from './assert-CUCJBR2C.js';
import { a as unwrapTemplate } from './constants-eoaL3OJQ.js';
import { I as InternalComponentCapabilities } from './flags-B9qxc-pB.js';
import { a as castToBrowser } from './simple-cast-DCvJLSin.js';
/* eslint-disable @typescript-eslint/no-empty-object-type */
function buildUntouchableThis(source) {
let context = null;
{
let assertOnProperty = property => {
let access = typeof property === 'symbol' || typeof property === 'number' ? `[${String(property)}]` : `.${property}`;
throw new Error(`You accessed \`this${access}\` from a function passed to the ${source}, but the function itself was not bound to a valid \`this\` context. Consider updating to use a bound function (for instance, use an arrow function, \`() => {}\`).`);
};
context = new Proxy({}, {
get(_target, property) {
assertOnProperty(property);
},
set(_target, property) {
assertOnProperty(property);
return false;
},
has(_target, property) {
assertOnProperty(property);
return false;
}
});
}
return context;
}
const ELEMENT_NODE = 1;
const TEXT_NODE = 3;
const COMMENT_NODE = 8;
const NS_MATHML = 'http://www.w3.org/1998/Math/MathML';
const NS_SVG = 'http://www.w3.org/2000/svg';
const INSERT_BEFORE_BEGIN = 'beforebegin';
const INSERT_BEFORE_END = 'beforeend';
function CheckInstanceof(Class) {
}
const CheckRegister = new class {
validate(value) {
switch (value) {
case $s0:
case $s1:
case $sp:
case $fp:
case $ra:
case $pc:
case $t0:
case $t1:
case $v0:
return true;
default:
return false;
}
}
expected() {
return `Register`;
}
}();
/*@__NO_SIDE_EFFECTS__*/
function check(value, checker, message) {
{
return value;
}
}
class AppendOpcodes {
// This code is intentionally putting unsafe `null`s into the array that it
// will intentionally overwrite before anyone can see them.
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
evaluateOpcode = new Array(VM_SYSCALL_SIZE).fill(null);
constructor() {
}
add(name, evaluate, kind = 'syscall') {
this.evaluateOpcode[name] = {
syscall: kind !== 'machine',
evaluate
};
}
evaluate(vm, opcode, type) {
let operation = unwrap(this.evaluateOpcode[type]);
if (operation.syscall) {
assert(!opcode.isMachine, `BUG: Mismatch between operation.syscall (${operation.syscall}) and opcode.isMachine (${opcode.isMachine}) for ${opcode.type}`);
operation.evaluate(vm, opcode);
} else {
assert(opcode.isMachine, `BUG: Mismatch between operation.syscall (${operation.syscall}) and opcode.isMachine (${opcode.isMachine}) for ${opcode.type}`);
operation.evaluate(vm.lowlevel, opcode);
}
}
}
function externs(vm) {
return undefined;
}
const APPEND_OPCODES = new AppendOpcodes();
const TYPE = Symbol('TYPE');
const INNER = Symbol('INNER');
const OWNER = Symbol('OWNER');
const ARGS = Symbol('ARGS');
const RESOLVED = Symbol('RESOLVED');
const CURRIED_VALUES = new WeakSet();
function isCurriedValue(value) {
return CURRIED_VALUES.has(value);
}
function isCurriedType(value, type) {
return isCurriedValue(value) && value[TYPE] === type;
}
class CurriedValue {
[TYPE];
[INNER];
[OWNER];
[ARGS];
[RESOLVED];
/** @internal */
constructor(type, inner, owner, args, resolved = false) {
CURRIED_VALUES.add(this);
this[TYPE] = type;
this[INNER] = inner;
this[OWNER] = owner;
this[ARGS] = args;
this[RESOLVED] = resolved;
}
}
function resolveCurriedValue(curriedValue) {
let currentWrapper = curriedValue;
let positional;
let named;
let definition, owner, resolved;
while (true) {
let {
[ARGS]: curriedArgs,
[INNER]: inner
} = currentWrapper;
if (curriedArgs !== null) {
let {
named: curriedNamed,
positional: curriedPositional
} = curriedArgs;
if (curriedPositional.length > 0) {
positional = positional === undefined ? curriedPositional : curriedPositional.concat(positional);
}
if (named === undefined) {
named = [];
}
named.unshift(curriedNamed);
}
if (!isCurriedValue(inner)) {
// Save off the owner that this helper was curried with. Later on,
// we'll fetch the value of this register and set it as the owner on the
// new root scope.
definition = inner;
owner = currentWrapper[OWNER];
resolved = currentWrapper[RESOLVED];
break;
}
currentWrapper = inner;
}
return {
definition,
owner,
resolved,
positional,
named
};
}
function curry(type, spec, owner, args, resolved = false) {
return new CurriedValue(type, spec, owner, args, resolved);
}
function createCurryRef(type, inner, owner, args, resolver, isStrict) {
let lastValue, curriedDefinition;
return createComputeRef(() => {
let value = valueForRef(inner);
if (value === lastValue) {
return curriedDefinition;
}
if (isCurriedType(value, type)) {
curriedDefinition = args ? curry(type, value, owner, args) : args;
} else if (type === CURRIED_COMPONENT && typeof value === 'string' && value) {
// Only components should enter this path, as helpers and modifiers do not
// support string based resolution
{
if (isStrict) {
throw new Error(`Attempted to resolve a dynamic component with a string definition, \`${value}\` in a strict mode template. In strict mode, using strings to resolve component definitions is prohibited. You can instead import the component definition and use it directly.`);
}
let resolvedDefinition = expect(resolver).lookupComponent?.(value, owner) ?? null;
if (!resolvedDefinition) {
throw new Error(`Attempted to resolve \`${value}\`, which was expected to be a component, but nothing was found.`);
}
}
curriedDefinition = curry(type, value, owner, args);
} else if (isIndexable$1(value)) {
curriedDefinition = curry(type, value, owner, args);
} else {
curriedDefinition = null;
}
lastValue = value;
return curriedDefinition;
});
}
class CursorImpl {
constructor(element, nextSibling) {
this.element = element;
this.nextSibling = nextSibling;
setLocalDebugType('cursor', this);
}
}
class ConcreteBounds {
constructor(parentNode, first, last) {
this.parentNode = parentNode;
this.first = first;
this.last = last;
}
parentElement() {
return this.parentNode;
}
firstNode() {
return this.first;
}
lastNode() {
return this.last;
}
}
function move(bounds, reference) {
let parent = bounds.parentElement();
let first = bounds.firstNode();
let last = bounds.lastNode();
let current = first;
while (true) {
let next = current.nextSibling;
parent.insertBefore(current, reference);
if (current === last) {
return next;
}
current = expect(next);
}
}
function clear(bounds) {
let parent = bounds.parentElement();
let first = bounds.firstNode();
let last = bounds.lastNode();
let current = first;
while (true) {
let next = current.nextSibling;
parent.removeChild(current);
if (current === last) {
return next;
}
current = expect(next);
}
}
/** @internal */
function hasCustomDebugRenderTreeLifecycle(manager) {
return 'getDebugCustomRenderTree' in manager;
}
function resolveComponent(resolver, constants, name, owner) {
let definition = resolver?.lookupComponent?.(name, expect(owner)) ?? null;
if (!definition) {
throw new Error(`Attempted to resolve \`${name}\`, which was expected to be a component, but nothing was found.`);
}
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- @fixme
return constants.resolvedComponent(definition, name);
}
let GUID = 0;
class Ref {
id = GUID++;
value;
constructor(value) {
this.value = value;
}
get() {
return this.value;
}
release() {
if (this.value === null) {
throw new Error('BUG: double release?');
}
this.value = null;
}
toString() {
let label = `Ref ${this.id}`;
if (this.value === null) {
return `${label} (released)`;
} else {
try {
// eslint-disable-next-line @typescript-eslint/no-base-to-string
return `${label}: ${this.value}`;
} catch {
return label;
}
}
}
}
class DebugRenderTreeImpl {
stack = new StackImpl();
refs = new WeakMap();
roots = new Set();
nodes = new WeakMap();
begin() {
this.reset();
}
create(state, node) {
let internalNode = assign({}, node, {
bounds: null,
refs: new Set()
});
this.nodes.set(state, internalNode);
this.appendChild(internalNode, state);
this.enter(state);
}
update(state) {
this.enter(state);
}
didRender(state, bounds) {
if (this.stack.current !== state) {
// eslint-disable-next-line @typescript-eslint/no-base-to-string
throw new Error(`BUG: expecting ${this.stack.current}, got ${state}`);
}
this.nodeFor(state).bounds = bounds;
this.exit();
}
willDestroy(state) {
expect(this.refs.get(state)).release();
}
commit() {
this.reset();
}
capture() {
return this.captureRefs(this.roots);
}
reset() {
if (this.stack.size !== 0) {
// We probably encountered an error during the rendering loop. This will
// likely trigger undefined behavior and memory leaks as the error left
// things in an inconsistent state. It is recommended that the user
// refresh the page.
// TODO: We could warn here? But this happens all the time in our tests?
// Clean up the root reference to prevent errors from happening if we
// attempt to capture the render tree (Ember Inspector may do this)
let root = expect(this.stack.toArray()[0]);
let ref = this.refs.get(root);
if (ref !== undefined) {
this.roots.delete(ref);
}
while (!this.stack.isEmpty()) {
this.stack.pop();
}
}
}
enter(state) {
this.stack.push(state);
}
exit() {
if (this.stack.size === 0) {
throw new Error('BUG: unbalanced pop');
}
this.stack.pop();
}
nodeFor(state) {
return expect(this.nodes.get(state));
}
appendChild(node, state) {
if (this.refs.has(state)) {
throw new Error('BUG: child already appended');
}
let parent = this.stack.current;
let ref = new Ref(state);
this.refs.set(state, ref);
if (parent) {
let parentNode = this.nodeFor(parent);
parentNode.refs.add(ref);
node.parent = parentNode;
} else {
this.roots.add(ref);
}
}
captureRefs(refs) {
let captured = [];
refs.forEach(ref => {
let state = ref.get();
if (state) {
captured.push(this.captureNode(`render-node:${ref.id}`, state));
} else {
refs.delete(ref);
}
});
return captured;
}
captureNode(id, state) {
let node = this.nodeFor(state);
let {
type,
name,
args,
instance,
refs
} = node;
let template = this.captureTemplate(node);
let bounds = this.captureBounds(node);
let children = this.captureRefs(refs);
return {
id,
type,
name,
args: reifyArgsDebug(args),
instance,
template,
bounds,
children
};
}
captureTemplate({
template
}) {
return template || null;
}
captureBounds(node) {
let bounds = expect(node.bounds);
let parentElement = bounds.parentElement();
let firstNode = bounds.firstNode();
let lastNode = bounds.lastNode();
return {
parentElement,
firstNode,
lastNode
};
}
}
function getDebugName(definition, manager = definition.manager) {
return definition.resolvedName ?? definition.debugName ?? manager.getDebugName(definition.state);
}
function normalizeStringValue(value) {
if (isEmpty$1(value)) {
return '';
}
return String(value);
}
function shouldCoerce(value) {
return isString(value) || isEmpty$1(value) || typeof value === 'boolean' || typeof value === 'number';
}
function isEmpty$1(value) {
return value === null || value === undefined || typeof value.toString !== 'function';
}
function isIndexable(value) {
return value !== null && typeof value === 'object';
}
function isSafeString(value) {
return isIndexable(value) && typeof value['toHTML'] === 'function';
}
function isNode(value) {
return isIndexable(value) && typeof value['nodeType'] === 'number';
}
function isFragment(value) {
return isIndexable(value) && value['nodeType'] === 11;
}
function isString(value) {
return typeof value === 'string';
}
function createClassListRef(list) {
return createComputeRef(() => {
let ret = [];
for (const ref of list) {
let value = normalizeStringValue(typeof ref === 'string' ? ref : valueForRef(ref));
if (value) ret.push(value);
}
return ret.length === 0 ? null : ret.join(' ');
});
}
APPEND_OPCODES.add(VM_CHILD_SCOPE_OP, vm => vm.pushChildScope());
APPEND_OPCODES.add(VM_POP_SCOPE_OP, vm => vm.popScope());
APPEND_OPCODES.add(VM_PUSH_DYNAMIC_SCOPE_OP, vm => vm.pushDynamicScope());
APPEND_OPCODES.add(VM_POP_DYNAMIC_SCOPE_OP, vm => vm.popDynamicScope());
APPEND_OPCODES.add(VM_CONSTANT_OP, (vm, {
op1: other
}) => {
vm.stack.push(vm.constants.getValue(decodeHandle(other)));
});
APPEND_OPCODES.add(VM_CONSTANT_REFERENCE_OP, (vm, {
op1: other
}) => {
vm.stack.push(createConstRef(vm.constants.getValue(decodeHandle(other)), false));
});
APPEND_OPCODES.add(VM_PRIMITIVE_OP, (vm, {
op1: primitive
}) => {
let stack = vm.stack;
if (isHandle(primitive)) {
// it is a handle which does not already exist on the stack
let value = vm.constants.getValue(decodeHandle(primitive));
stack.push(value);
} else {
// is already an encoded immediate or primitive handle
stack.push(decodeImmediate(primitive));
}
});
APPEND_OPCODES.add(VM_PRIMITIVE_REFERENCE_OP, vm => {
let stack = vm.stack;
let value = check(stack.pop());
let ref;
if (value === undefined) {
ref = UNDEFINED_REFERENCE;
} else if (value === null) {
ref = NULL_REFERENCE;
} else if (value === true) {
ref = TRUE_REFERENCE;
} else if (value === false) {
ref = FALSE_REFERENCE;
} else {
ref = createPrimitiveRef(value);
}
stack.push(ref);
});
APPEND_OPCODES.add(VM_DUP_OP, (vm, {
op1: register,
op2: offset
}) => {
let position = check(vm.fetchValue(check(register, CheckRegister))) - offset;
vm.stack.dup(position);
});
APPEND_OPCODES.add(VM_POP_OP, (vm, {
op1: count
}) => {
vm.stack.pop(count);
});
APPEND_OPCODES.add(VM_LOAD_OP, (vm, {
op1: register
}) => {
vm.load(check(register));
});
APPEND_OPCODES.add(VM_FETCH_OP, (vm, {
op1: register
}) => {
vm.fetch(check(register));
});
APPEND_OPCODES.add(VM_BIND_DYNAMIC_SCOPE_OP, (vm, {
op1: _names
}) => {
let names = vm.constants.getArray(_names);
vm.bindDynamicScope(names);
});
APPEND_OPCODES.add(VM_ENTER_OP, (vm, {
op1: args
}) => {
vm.enter(args);
});
APPEND_OPCODES.add(VM_EXIT_OP, vm => {
vm.exit();
});
APPEND_OPCODES.add(VM_PUSH_SYMBOL_TABLE_OP, (vm, {
op1: _table
}) => {
let stack = vm.stack;
stack.push(vm.constants.getValue(_table));
});
APPEND_OPCODES.add(VM_PUSH_BLOCK_SCOPE_OP, vm => {
let stack = vm.stack;
stack.push(vm.scope());
});
APPEND_OPCODES.add(VM_COMPILE_BLOCK_OP, vm => {
let stack = vm.stack;
let block = stack.pop();
if (block) {
stack.push(vm.compile(block));
} else {
stack.push(null);
}
});
APPEND_OPCODES.add(VM_INVOKE_YIELD_OP, vm => {
let {
stack
} = vm;
let handle = check(stack.pop());
let scope = check(stack.pop());
let table = check(stack.pop());
let args = check(stack.pop());
if (table === null || handle === null) {
// To balance the pop{Frame,Scope}
vm.lowlevel.pushFrame();
vm.pushScope(scope ?? vm.scope());
return;
}
let invokingScope = expect(scope);
// If necessary, create a child scope
{
let locals = table.parameters;
let localsCount = locals.length;
if (localsCount > 0) {
invokingScope = invokingScope.child();
for (let i = 0; i < localsCount; i++) {
invokingScope.bindSymbol(unwrap(locals[i]), args.at(i));
}
}
}
vm.lowlevel.pushFrame();
vm.pushScope(invokingScope);
vm.call(handle);
});
APPEND_OPCODES.add(VM_JUMP_IF_OP, (vm, {
op1: target
}) => {
let reference = check(vm.stack.pop());
let value = Boolean(valueForRef(reference));
if (isConstRef(reference)) {
if (value) {
vm.lowlevel.goto(target);
}
} else {
if (value) {
vm.lowlevel.goto(target);
}
vm.updateWith(new Assert(reference));
}
});
APPEND_OPCODES.add(VM_JUMP_UNLESS_OP, (vm, {
op1: target
}) => {
let reference = check(vm.stack.pop());
let value = Boolean(valueForRef(reference));
if (isConstRef(reference)) {
if (!value) {
vm.lowlevel.goto(target);
}
} else {
if (!value) {
vm.lowlevel.goto(target);
}
vm.updateWith(new Assert(reference));
}
});
APPEND_OPCODES.add(VM_JUMP_EQ_OP, (vm, {
op1: target,
op2: comparison
}) => {
let other = check(vm.stack.peek());
if (other === comparison) {
vm.lowlevel.goto(target);
}
});
APPEND_OPCODES.add(VM_ASSERT_SAME_OP, vm => {
let reference = check(vm.stack.peek());
if (!isConstRef(reference)) {
vm.updateWith(new Assert(reference));
}
});
APPEND_OPCODES.add(VM_TO_BOOLEAN_OP, vm => {
let {
stack
} = vm;
let valueRef = check(stack.pop());
stack.push(createComputeRef(() => toBool(valueForRef(valueRef))));
});
class Assert {
last;
constructor(ref) {
this.ref = ref;
this.last = valueForRef(ref);
}
evaluate(vm) {
let {
last,
ref
} = this;
let current = valueForRef(ref);
if (last !== current) {
vm.throw();
}
}
}
class AssertFilter {
last;
constructor(ref, filter) {
this.ref = ref;
this.filter = filter;
this.last = filter(valueForRef(ref));
}
evaluate(vm) {
let {
last,
ref,
filter
} = this;
let current = filter(valueForRef(ref));
if (last !== current) {
vm.throw();
}
}
}
class JumpIfNotModifiedOpcode {
tag = CONSTANT_TAG;
lastRevision = INITIAL;
target;
finalize(tag, target) {
this.target = target;
this.didModify(tag);
}
evaluate(vm) {
let {
tag,
target,
lastRevision
} = this;
if (!vm.alwaysRevalidate && validateTag(tag, lastRevision)) {
consumeTag(tag);
vm.goto(expect(target));
}
}
didModify(tag) {
this.tag = tag;
this.lastRevision = valueForTag(this.tag);
consumeTag(tag);
}
}
class BeginTrackFrameOpcode {
constructor(debugLabel) {
this.debugLabel = debugLabel;
}
evaluate() {
beginTrackFrame(this.debugLabel);
}
}
class EndTrackFrameOpcode {
constructor(target) {
this.target = target;
}
evaluate() {
let tag = endTrackFrame();
this.target.didModify(tag);
}
}
APPEND_OPCODES.add(VM_TEXT_OP, (vm, {
op1: text
}) => {
vm.tree().appendText(vm.constants.getValue(text));
});
APPEND_OPCODES.add(VM_COMMENT_OP, (vm, {
op1: text
}) => {
vm.tree().appendComment(vm.constants.getValue(text));
});
APPEND_OPCODES.add(VM_OPEN_ELEMENT_OP, (vm, {
op1: tag
}) => {
vm.tree().openElement(vm.constants.getValue(tag));
});
APPEND_OPCODES.add(VM_OPEN_DYNAMIC_ELEMENT_OP, vm => {
let tagName = check(valueForRef(check(vm.stack.pop(), CheckReference)));
vm.tree().openElement(tagName);
});
APPEND_OPCODES.add(VM_PUSH_REMOTE_ELEMENT_OP, vm => {
let elementRef = check(vm.stack.pop());
let insertBeforeRef = check(vm.stack.pop());
let guidRef = check(vm.stack.pop());
let element = check(valueForRef(elementRef));
let insertBefore = check(valueForRef(insertBeforeRef));
let guid = valueForRef(guidRef);
if (!isConstRef(elementRef)) {
vm.updateWith(new Assert(elementRef));
}
if (insertBefore !== undefined && !isConstRef(insertBeforeRef)) {
vm.updateWith(new Assert(insertBeforeRef));
}
let block = vm.tree().pushRemoteElement(element, guid, insertBefore);
vm.associateDestroyable(block);
if (vm.env.debugRenderTree !== undefined) {
// Note that there is nothing to update – when the args for an
// {{#in-element}} changes it gets torn down and a new one is
// re-created/rendered in its place (see the `Assert`s above)
let args = createCapturedArgs(insertBefore === undefined ? {} : {
insertBefore: insertBeforeRef
}, [elementRef]);
vm.env.debugRenderTree.create(block, {
type: 'keyword',
name: 'in-element',
args,
instance: null
});
registerDestructor(block, () => {
vm.env.debugRenderTree?.willDestroy(block);
});
}
});
APPEND_OPCODES.add(VM_POP_REMOTE_ELEMENT_OP, vm => {
let bounds = vm.tree().popRemoteElement();
if (vm.env.debugRenderTree !== undefined) {
// The RemoteBlock is also its bounds
vm.env.debugRenderTree.didRender(bounds, bounds);
}
});
APPEND_OPCODES.add(VM_FLUSH_ELEMENT_OP, vm => {
let operations = check(vm.fetchValue($t0));
let modifiers = null;
if (operations) {
modifiers = operations.flush(vm);
vm.loadValue($t0, null);
}
vm.tree().flushElement(modifiers);
});
APPEND_OPCODES.add(VM_CLOSE_ELEMENT_OP, vm => {
let modifiers = vm.tree().closeElement();
if (modifiers !== null) {
modifiers.forEach(modifier => {
vm.env.scheduleInstallModifier(modifier);
const d = modifier.manager.getDestroyable(modifier.state);
if (d !== null) {
vm.associateDestroyable(d);
}
});
}
});
APPEND_OPCODES.add(VM_MODIFIER_OP, (vm, {
op1: handle
}) => {
let args = check(vm.stack.pop());
if (!vm.env.isInteractive) {
return;
}
let owner = vm.getOwner();
let definition = vm.constants.getValue(handle);
let {
manager
} = definition;
let {
constructing
} = vm.tree();
let capturedArgs = args.capture();
let state = manager.create(owner, expect(constructing), definition.state, capturedArgs);
let instance = {
manager,
state,
definition
};
let operations = expect(check(vm.fetchValue($t0)));
operations.addModifier(vm, instance, capturedArgs);
let tag = manager.getTag(state);
if (tag !== null) {
consumeTag(tag);
return vm.updateWith(new UpdateModifierOpcode(tag, instance));
}
});
APPEND_OPCODES.add(VM_DYNAMIC_MODIFIER_OP, vm => {
let {
stack
} = vm;
let ref = check(stack.pop());
let args = check(stack.pop());
if (!vm.env.isInteractive) {
return;
}
let capturedArgs = args.capture();
let {
positional: outerPositional,
named: outerNamed
} = capturedArgs;
let {
constructing
} = vm.tree();
let initialOwner = vm.getOwner();
let instanceRef = createComputeRef(() => {
let value = valueForRef(ref);
let owner;
if (!isIndexable$1(value)) {
return;
}
let hostDefinition;
if (isCurriedType(value, CURRIED_MODIFIER)) {
let {
definition: resolvedDefinition,
owner: curriedOwner,
positional,
named
} = resolveCurriedValue(value);
hostDefinition = resolvedDefinition;
owner = curriedOwner;
if (positional !== undefined) {
capturedArgs.positional = positional.concat(outerPositional);
}
if (named !== undefined) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
capturedArgs.named = Object.assign({}, ...named, outerNamed);
}
} else {
hostDefinition = value;
owner = initialOwner;
}
let manager = getInternalModifierManager(hostDefinition, true);
if (manager === null) {
{
throw new Error(`Expected a dynamic modifier definition, but received an object or function that did not have a modifier manager associated with it. The dynamic invocation was \`{{${ref.debugLabel}}}\`, and the incorrect definition is the value at the path \`${ref.debugLabel}\`, which was: ${debugToString?.(hostDefinition)}`);
}
}
let definition = {
resolvedName: null,
manager,
state: hostDefinition
};
let state = manager.create(owner, expect(constructing), definition.state, capturedArgs);
return {
manager,
state,
definition
};
});
let instance = valueForRef(instanceRef);
let tag = null;
if (instance !== undefined) {
let operations = expect(check(vm.fetchValue($t0)));
operations.addModifier(vm, instance, capturedArgs);
tag = instance.manager.getTag(instance.state);
if (tag !== null) {
consumeTag(tag);
}
}
if (!isConstRef(ref) || tag) {
return vm.updateWith(new UpdateDynamicModifierOpcode(tag, instance, instanceRef));
}
});
class UpdateModifierOpcode {
lastUpdated;
constructor(tag, modifier) {
this.tag = tag;
this.modifier = modifier;
this.lastUpdated = valueForTag(tag);
}
evaluate(vm) {
let {
modifier,
tag,
lastUpdated
} = this;
consumeTag(tag);
if (!validateTag(tag, lastUpdated)) {
vm.env.scheduleUpdateModifier(modifier);
this.lastUpdated = valueForTag(tag);
}
}
}
class UpdateDynamicModifierOpcode {
lastUpdated;
constructor(tag, instance, instanceRef) {
this.tag = tag;
this.instance = instance;
this.instanceRef = instanceRef;
this.lastUpdated = valueForTag(tag ?? CURRENT_TAG);
}
evaluate(vm) {
let {
tag,
lastUpdated,
instance,
instanceRef
} = this;
let newInstance = valueForRef(instanceRef);
if (newInstance !== instance) {
if (instance !== undefined) {
let destroyable = instance.manager.getDestroyable(instance.state);
if (destroyable !== null) {
destroy(destroyable);
}
}
if (newInstance !== undefined) {
let {
manager,
state
} = newInstance;
let destroyable = manager.getDestroyable(state);
if (destroyable !== null) {
associateDestroyableChild(this, destroyable);
}
tag = manager.getTag(state);
if (tag !== null) {
this.lastUpdated = valueForTag(tag);
}
this.tag = tag;
vm.env.scheduleInstallModifier(newInstance);
}
this.instance = newInstance;
} else if (tag !== null && !validateTag(tag, lastUpdated)) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- @fixme
vm.env.scheduleUpdateModifier(instance);
this.lastUpdated = valueForTag(tag);
}
if (tag !== null) {
consumeTag(tag);
}
}
}
APPEND_OPCODES.add(VM_STATIC_ATTR_OP, (vm, {
op1: _name,
op2: _value,
op3: _namespace
}) => {
let name = vm.constants.getValue(_name);
let value = vm.constants.getValue(_value);
let namespace = _namespace ? vm.constants.getValue(_namespace) : null;
vm.tree().setStaticAttribute(name, value, namespace);
});
APPEND_OPCODES.add(VM_DYNAMIC_ATTR_OP, (vm, {
op1: _name,
op2: _trusting,
op3: _namespace
}) => {
let name = vm.constants.getValue(_name);
let trusting = vm.constants.getValue(_trusting);
let reference = check(vm.stack.pop());
let value = valueForRef(reference);
let namespace = _namespace ? vm.constants.getValue(_namespace) : null;
let attribute = vm.tree().setDynamicAttribute(name, value, trusting, namespace);
if (!isConstRef(reference)) {
vm.updateWith(new UpdateDynamicAttributeOpcode(reference, attribute, vm.env));
}
});
class UpdateDynamicAttributeOpcode {
updateRef;
constructor(reference, attribute, env) {
let initialized = false;
this.updateRef = createComputeRef(() => {
let value = valueForRef(reference);
if (initialized) {
attribute.update(value, env);
} else {
initialized = true;
}
});
valueForRef(this.updateRef);
}
evaluate() {
valueForRef(this.updateRef);
}
}
/**
* The VM creates a new ComponentInstance data structure for every component
* invocation it encounters.
*
* Similar to how a ComponentDefinition contains state about all components of a
* particular type, a ComponentInstance contains state specific to a particular
* instance of a component type. It also contains a pointer back to its
* component type's ComponentDefinition.
*/
APPEND_OPCODES.add(VM_PUSH_COMPONENT_DEFINITION_OP, (vm, {
op1: handle
}) => {
let definition = vm.constants.getValue(handle);
let {
manager,
capabilities
} = definition;
let instance = {
definition,
manager,
capabilities,
state: null,
handle: null,
table: null,
lookup: null
};
vm.stack.push(instance);
});
APPEND_OPCODES.add(VM_RESOLVE_DYNAMIC_COMPONENT_OP, (vm, {
op1: _isStrict
}) => {
let stack = vm.stack;
let ref = check(stack.pop());
let component = check(valueForRef(ref));
let constants = vm.constants;
let owner = vm.getOwner();
let isStrict = constants.getValue(_isStrict);
vm.loadValue($t1, null); // Clear the temp register
let definition;
if (typeof component === 'string') {
if (isStrict) {
throw new Error(`Attempted to resolve a dynamic component with a string definition, \`${component}\` in a strict mode template. In strict mode, using strings to resolve component definitions is prohibited. You can instead import the component definition and use it directly.`);
}
let resolvedDefinition = resolveComponent(vm.context.resolver, constants, component, owner);
definition = expect(resolvedDefinition);
} else if (isCurriedValue(component)) {
definition = component;
} else {
definition = constants.component(component, owner);
}
if (!isCurriedValue(definition) && !definition.resolvedName && !definition.debugName) {
let debugLabel = ref.debugLabel;
if (debugLabel) {
definition.debugName = debugLabel;
}
}
stack.push(definition);
});
APPEND_OPCODES.add(VM_RESOLVE_CURRIED_COMPONENT_OP, vm => {
let stack = vm.stack;
let ref = check(stack.pop());
let value = valueForRef(ref);
let constants = vm.constants;
let definition;
if (!(typeof value === 'function' || typeof value === 'object' && value !== null)) {
throw new Error(`Expected a component definition, but received ${value}. You may have accidentally done <${ref.debugLabel}>, where "${ref.debugLabel}" was a string instead of a curried component definition. You must either use the component definition directly, or use the {{component}} helper to create a curried component definition when invoking dynamically.`);
}
if (isCurriedValue(value)) {
definition = value;
} else {
definition = constants.component(value, vm.getOwner(), true);
if (definition === null) {
throw new Error(`Expected a dynamic component definition, but received an object or function that did not have a component manager associated with it. The dynamic invocation was \`<${ref.debugLabel}>\` or \`{{${ref.debugLabel}}}\`, and the incorrect definition is the value at the path \`${ref.debugLabel}\`, which was: ${debugToString?.(value) ?? value}`);
}
}
if (definition && !isCurriedValue(definition) && !definition.resolvedName && !definition.debugName) {
let debugLabel = ref.debugLabel;
if (debugLabel) {
definition.debugName = debugLabel;
}
}
stack.push(definition);
});
APPEND_OPCODES.add(VM_PUSH_DYNAMIC_COMPONENT_INSTANCE_OP, vm => {
let {
stack
} = vm;
let definition = stack.pop();
let capabilities, manager;
if (isCurriedValue(definition)) {
manager = capabilities = null;
} else {
manager = definition.manager;
capabilities = definition.capabilities;
}
stack.push({
definition,
capabilities,
manager,
state: null,
handle: null,
table: null
});
});
APPEND_OPCODES.add(VM_PUSH_ARGS_OP, (vm, {
op1: _names,
op2: _blockNames,
op3: flags
}) => {
let stack = vm.stack;
let names = vm.constants.getArray(_names);
let positionalCount = flags >> 4;
let atNames = flags & 0b1000;
let blockNames = flags & 0b0111 ? vm.constants.getArray(_blockNames) : EMPTY_STRING_ARRAY;
vm.args.setup(stack, names, blockNames, positionalCount, !!atNames);
stack.push(vm.args);
});
APPEND_OPCODES.add(VM_PUSH_EMPTY_ARGS_OP, vm => {
let {
stack
} = vm;
stack.push(vm.args.empty(stack));
});
APPEND_OPCODES.add(VM_CAPTURE_ARGS_OP, vm => {
let stack = vm.stack;
let args = check(stack.pop());
let capturedArgs = args.capture();
stack.push(capturedArgs);
});
APPEND_OPCODES.add(VM_PREPARE_ARGS_OP, (vm, {
op1: register
}) => {
let stack = vm.stack;
let instance = vm.fetchValue(check(register));
let args = check(stack.pop());
let {
definition
} = instance;
if (isCurriedType(definition, CURRIED_COMPONENT)) {
assert(!definition.manager);
let constants = vm.constants;
let {
definition: resolvedDefinition,
owner,
resolved,
positional,
named
} = resolveCurriedValue(definition);
if (resolved) {
definition = resolvedDefinition;
} else if (typeof resolvedDefinition === 'string') {
let resolvedValue = vm.context.resolver?.lookupComponent?.(resolvedDefinition, owner) ?? null;
definition = constants.resolvedComponent(expect(resolvedValue), resolvedDefinition);
} else {
definition = constants.component(resolvedDefinition, owner);
}
if (named !== undefined) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
args.named.merge(assign({}, ...named));
}
if (positional !== undefined) {
args.realloc(positional.length);
args.positional.prepend(positional);
}
let {
manager
} = definition;
instance.definition = definition;
instance.manager = manager;
instance.capabilities = definition.capabilities;
// Save off the owner that this component was curried with. Later on,
// we'll fetch the value of this register and set it as the owner on the
// new root scope.
vm.loadValue($t1, owner);
}
let {
manager,
state
} = definition;
let capabilities = instance.capabilities;
if (!managerHasCapability(manager, capabilities, InternalComponentCapabilities.prepareArgs)) {
stack.push(args);
return;
}
let blocks = args.blocks.values;
let blockNames = args.blocks.names;
let preparedArgs = manager.prepareArgs(state, args);
if (preparedArgs) {
args.clear();
for (let i = 0; i < blocks.length; i++) {
stack.push(blocks[i]);
}
let {
positional,
named
} = preparedArgs;
let positionalCount = positional.length;
for (let i = 0; i < positionalCount; i++) {
stack.push(positional[i]);
}
let names = Object.keys(named);
for (let i = 0; i < names.length; i++) {
stack.push(named[unwrap(names[i])]);
}
args.setup(stack, names, blockNames, positionalCount, false);
}
stack.push(args);
});
APPEND_OPCODES.add(VM_CREATE_COMPONENT_OP, (vm, {
op1: flags
}) => {
let instance = check(vm.fetchValue($s0));
let {
definition,
manager,
capabilities
} = instance;
if (!managerHasCapability(manager, capabilities, InternalComponentCapabilities.createInstance)) {
// TODO: Closure and Main components are always invoked dynamically, so this
// opcode may run even if this capability is not enabled. In the future we
// should handle this in a better way.
return;
}
let dynamicScope = null;
if (managerHasCapability(manager, capabilities, InternalComponentCapabilities.dynamicScope)) {
dynamicScope = vm.dynamicScope();
}
let hasDefaultBlock = flags & 1;
let args = null;
if (managerHasCapability(manager, capabilities, InternalComponentCapabilities.createArgs)) {
args = check(vm.stack.peek());
}
let self = null;
if (managerHasCapability(manager, capabilities, InternalComponentCapabilities.createCaller)) {
self = vm.getSelf();
}
let state = manager.create(vm.getOwner(), definition.state, args, vm.env, dynamicScope, self, !!hasDefaultBlock);
// We want to reuse the `state` POJO here, because we know that the opcodes
// only transition at exactly one place.
instance.state = state;
if (managerHasCapability(manager, capabilities, InternalComponentCapabilities.updateHook)) {
vm.updateWith(new UpdateComponentOpcode(state, manager, dynamicScope));
}
});
APPEND_OPCODES.add(VM_REGISTER_COMPONENT_DESTRUCTOR_OP, (vm, {
op1: register
}) => {
let {
manager,
state,
capabilities
} = check(vm.fetchValue(check(register, CheckRegister)));
let d = manager.getDestroyable(state);
if (!managerHasCapability(manager, capabilities, InternalComponentCapabilities.willDestroy) && d !== null && "string" in d) {
throw new Error('BUG: Destructor has willDestroy, but the willDestroy capability was not enabled for this component. Pre-destruction hooks must be explicitly opted into');
}
if (d) vm.associateDestroyable(d);
});
APPEND_OPCODES.add(VM_BEGIN_COMPONENT_TRANSACTION_OP, (vm, {
op1: register
}) => {
let name;
{
let {
definition,
manager
} = check(vm.fetchValue(check(register, CheckRegister)));
name = getDebugName(definition, manager);
}
vm.beginCacheGroup(name);
vm.tree().pushAppendingBlock();
});
APPEND_OPCODES.add(VM_PUT_COMPONENT_OPERATIONS_OP, vm => {
vm.loadValue($t0, new ComponentElementOperations());
});
APPEND_OPCODES.add(VM_COMPONENT_ATTR_OP, (vm, {
op1: _name,
op2: _trusting,
op3: _namespace
}) => {
let name = vm.constants.getValue(_name);
let trusting = vm.constants.getValue(_trusting);
let reference = check(vm.stack.pop());
let namespace = _namespace ? vm.constants.getValue(_namespace) : null;
check(vm.fetchValue($t0), CheckInstanceof(ComponentElementOperations)).setAttribute(name, reference, trusting, namespace);
});
APPEND_OPCODES.add(VM_STATIC_COMPONENT_ATTR_OP, (vm, {
op1: _name,
op2: _value,
op3: _namespace
}) => {
let name = vm.constants.getValue(_name);
let value = vm.constants.getValue(_value);
let namespace = _namespace ? vm.constants.getValue(_namespace) : null;
check(vm.fetchValue($t0), CheckInstanceof(ComponentElementOperations)).setStaticAttribute(name, value, namespace);
});
class ComponentElementOperations {
attributes = dict();
classes = [];
modifiers = [];
setAttribute(name, value, trusting, namespace) {
let deferred = {
value,
namespace,
trusting
};
if (name === 'class') {
this.classes.push(value);
}
this.attributes[name] = deferred;
}
setStaticAttribute(name, value, namespace) {
let deferred = {
value,
namespace
};
if (name === 'class') {
this.classes.push(value);
}
this.attributes[name] = deferred;
}
addModifier(vm, modifier, capturedArgs) {
this.modifiers.push(modifier);
if (vm.env.debugRenderTree !== undefined) {
const {
manager,
definition,
state
} = modifier;
// TODO: we need a stable object for the debugRenderTree as the key, add support for
// the case where the state is a primitive, or if in practice we always have/require
// an object, then change the internal types to reflect that
if (state === null || typeof state !== 'object' && typeof state !== 'function') {
return;
}
let {
element,
constructing
} = vm.tree();
let name = definition.resolvedName ?? manager.getDebugName(definition.state);
let instance = manager.getDebugInstance(state);
let bounds = new ConcreteBounds(element, constructing, constructing);
vm.env.debugRenderTree.create(state, {
type: 'modifier',
name,
args: capturedArgs,
instance
});
vm.env.debugRenderTree.didRender(state, bounds);
// For tearing down the debugRenderTree
vm.associateDestroyable(state);
vm.updateWith(new DebugRenderTreeUpdateOpcode(state));
vm.updateWith(new DebugRenderTreeDidRenderOpcode(state, bounds));
registerDestructor(state, () => {
vm.env.debugRenderTree?.willDestroy(state);
});
}
}
flush(vm) {
let type;
let attributes = this.attributes;
for (let name in this.attributes) {
if (name === 'type') {
type = attributes[name];
continue;
}
let attr = unwrap(this.attributes[name]);
if (name === 'class') {
setDeferredAttr(vm, 'class', mergeClasses(this.classes), attr.namespace, attr.trusting);
} else {
setDeferredAttr(vm, name, attr.value, attr.namespace, attr.trusting);
}
}
if (type !== undefined) {
setDeferredAttr(vm, 'type', type.value, type.namespace, type.trusting);
}
return this.modifiers;
}
}
function mergeClasses(classes) {
if (classes.length === 0) {
return '';
}
if (classes.length === 1) {
return unwrap(classes[0]);
}
if (allStringClasses(classes)) {
return classes.join(' ');
}
return createClassListRef(classes);
}
function allStringClasses(classes) {
return classes.every(c => typeof c === 'string');
}
function setDeferredAttr(vm, name, value, namespace, trusting = false) {
if (typeof value === 'string') {
vm.tree().setStaticAttribute(name, value, namespace);
} else {
let attribute = vm.tree().setDynamicAttribute(name, valueForRef(value), trusting, namespace);
if (!isConstRef(value)) {
vm.updateWith(new UpdateDynamicAttributeOpcode(value, attribute, vm.env));
}
}
}
APPEND_OPCODES.add(VM_DID_CREATE_ELEMENT_OP, (vm, {
op1: register
}) => {
let {
definition,
state
} = check(vm.fetchValue(check(register, CheckRegister)));
let {
manager
} = definition;
let operations = check(vm.fetchValue($t0));
manager.didCreateElement(state, expect(vm.tree().constructing), operations);
});
APPEND_OPCODES.add(VM_GET_COMPONENT_SELF_OP, (vm, {
op1: register,
op2: _names
}) => {
let instance = check(vm.fetchValue(check(register, CheckRegister)));
let {
definition,
state
} = instance;
let {
manager
} = definition;
let selfRef = manager.getSelf(state);
if (vm.env.debugRenderTree !== undefined) {
let instance = check(vm.fetchValue(check(register, CheckRegister)));
let {
definition,
manager
} = instance;
let args;
if (vm.stack.peek() === vm.args) {
args = vm.args.capture();
} else {
let names = vm.constants.getArray(_names);
vm.args.setup(vm.stack, names, [], 0, true);
args = vm.args.capture();
}
let moduleName;
let compilable = definition.compilable;
if (compilable === null) {
assert(managerHasCapability(manager, instance.capabilities, InternalComponentCapabilities.dynamicLayout));
let resolver = vm.context.resolver;
compilable = resolver === null ? null : manager.getDynamicLayout(state, resolver);
if (compilable !== null) {
moduleName = compilable.moduleName;
} else {
moduleName = '__default__.hbs';
}
} else {
moduleName = compilable.moduleName;
}
// For tearing down the debugRenderTree
vm.associateDestroyable(instance);
if (hasCustomDebugRenderTreeLifecycle(manager)) {
let nodes = manager.getDebugCustomRenderTree(instance.definition.state, instance.state, args, moduleName);
nodes.forEach(node => {
let {
bucket
} = node;
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- @fixme
vm.env.debugRenderTree.create(bucket, node);
registerDestructor(instance, () => {
vm.env.debugRenderTree?.willDestroy(bucket);
});
vm.updateWith(new DebugRenderTreeUpdateOpcode(bucket));
});
} else {
let name = getDebugName(definition, manager);
vm.env.debugRenderTree.create(instance, {
type: 'component',
name,
args,
template: moduleName,
instance: valueForRef(selfRef)
});
registerDestructor(instance, () => {
vm.env.debugRenderTree?.willDestroy(instance);
});
vm.updateWith(new DebugRenderTreeUpdateOpcode(instance));
}
}
vm.stack.push(selfRef);
});
APPEND_OPCODES.add(VM_GET_COMPONENT_TAG_NAME_OP, (vm, {
op1: register
}) => {
let {
definition,
state
} = check(vm.fetchValue(check(register, CheckRegister)));
let {
manager
} = definition;
let tagName = manager.getTagName(state);
// User provided value from JS, so we don't bother to encode
vm.stack.push(tagName);
});
// Dynamic Invocation Only
APPEND_OPCODES.add(VM_GET_COMPONENT_LAYOUT_OP, (vm, {
op1: register
}) => {
let instance = check(vm.fetchValue(check(register, CheckRegister)));
let {
manager,
definition
} = instance;
let {
stack
} = vm;
let {
compilable
} = definition;
if (compilable === null) {
let {
capabilities
} = instance;
assert(managerHasCapability(manager, capabilities, InternalComponentCapabilities.dynamicLayout));
let resolver = vm.context.resolver;
compilable = resolver === null ? null : manager.getDynamicLayout(instance.state, resolver);
if (compilable === null) {
if (managerHasCapability(manager, capabilities, InternalComponentCapabilities.wrapped)) {
compilable = unwrapTemplate(vm.constants.defaultTemplate).asWrappedLayout();
} else {
co