@glimmer/runtime
Version:
Minimal runtime needed to render Glimmer templates
1,040 lines (980 loc) • 174 kB
JavaScript
import { DEBUG } from "@glimmer/env";
import { registerDestructor, destroy, associateDestroyableChild, _hasDestroyableChildren, isDestroying, isDestroyed, destroyChildren } from "@glimmer/destroyable";
export { destroy, isDestroyed, isDestroying, registerDestructor } from "@glimmer/destroyable";
import { toBool, debugAssert, getPath, setPath, warnIfStyleNotTrusted, assertGlobalContextWasSet } from "@glimmer/global-context";
import { getInternalModifierManager, managerHasCapability, getInternalHelperManager, hasInternalComponentManager, hasInternalHelperManager, setInternalComponentManager, setInternalHelperManager, hasValue, hasDestroyable, setInternalModifierManager } from "@glimmer/manager";
import { createComputeRef, valueForRef, UNDEFINED_REFERENCE, createConstRef, createPrimitiveRef, isConstRef, NULL_REFERENCE, TRUE_REFERENCE, FALSE_REFERENCE, REFERENCE, createDebugAliasRef, childRefFor, createIteratorRef, isInvokableRef, updateRef, createIteratorItemRef } from "@glimmer/reference";
import { isIndexable as isIndexable$1, assign, Stack, EMPTY_STRING_ARRAY, dict, enumerate, emptyArray, clearElement, isDict, reverse } from "@glimmer/util";
import { $v0, $t1, $t0, $pc, $ra, $fp, $sp, $s1, $s0, InternalComponentCapabilities, ContentType, isLowLevelRegister } from "@glimmer/vm";
import { validateTag, consumeTag, valueForTag, CONSTANT_TAG, INITIAL, beginTrackFrame, endTrackFrame, CURRENT_TAG, track, updateTag, createCache, getValue, createUpdatableTag, debug, resetTracking } from "@glimmer/validator";
import { ProgramImpl } from "@glimmer/program";
import { getOwner } from "@glimmer/owner";
const NS_MATHML = "http://www.w3.org/1998/Math/MathML", NS_SVG = "http://www.w3.org/2000/svg";
let debugToString;
if (DEBUG) {
let getFunctionName = fn => {
let functionName = fn.name;
if ("" === functionName) {
let match = /function (\w+)\s*\(/u.exec(String(fn));
functionName = match && match[1] || "";
}
return functionName.replace(/^bound /u, "");
}, getObjectName = obj => {
let name, className;
// If the class has a decent looking name, and the `toString` is one of the
// default Ember toStrings, replace the constructor portion of the toString
// with the class name. We check the length of the class name to prevent doing
// this when the value is minified.
return "function" == typeof obj.constructor && (className = getFunctionName(obj.constructor)),
"toString" in obj && obj.toString !== Object.prototype.toString && obj.toString !== Function.prototype.toString && (
// eslint-disable-next-line @typescript-eslint/no-base-to-string
name = obj.toString()), name && /<.*:ember\d+>/u.test(name) && className && "_" !== className[0] && className.length > 2 && "Class" !== className ? name.replace(/<.*:/u, `<${className}:`) : name || className;
}, getPrimitiveName = value => String(value);
debugToString = value => "function" == typeof value ? getFunctionName(value) || "(unknown function)" : "object" == typeof value && null !== value ? getObjectName(value) || "(unknown object)" : getPrimitiveName(value);
}
var debugToString$1 = debugToString;
function castToSimple(node) {
return function(node) {
node.nodeType;
}(node), node;
}
function unwrapHandle(handle) {
if ("number" == typeof handle) return handle;
{
let error = handle.errors[0];
throw new Error(`Compile Error: ${error.problem} @ ${error.span.start}..${error.span.end}`);
}
}
function unwrapTemplate(template) {
if ("error" === template.result) throw new Error(`Compile Error: ${template.problem} @ ${template.span.start}..${template.span.end}`);
return template;
}
/* eslint-disable @typescript-eslint/no-empty-object-type */ function buildUntouchableThis(source) {
let context = null;
if (DEBUG) {
let assertOnProperty = property => {
let access = "symbol" == typeof property || "number" == typeof property ? `[${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), !1),
has: (_target, property) => (assertOnProperty(property), !1)
});
}
return context;
}
function decodeImmediate(num) {
return (num |= 0) > -536870913 ? function(num) {
return ~num;
}(num) : function(num) {
return 536870912 | num;
}(num);
}
[ 1, -1 ].forEach((x => {
return decodeImmediate((num = x, (num |= 0) < 0 ? function(num) {
return -536870913 & num;
}(num) : function(num) {
return ~num;
}(num)));
var num;
})), 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 !0;
default:
return !1;
}
}
expected() {
return "Register";
}
};
const APPEND_OPCODES = new class {
constructor() {
// 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
this.evaluateOpcode = new Array(113).fill(null);
}
add(name, evaluate, kind = "syscall") {
this.evaluateOpcode[name] = {
syscall: "machine" !== kind,
evaluate: evaluate
};
}
evaluate(vm, opcode, type) {
let operation = this.evaluateOpcode[type];
operation.syscall ? (opcode.isMachine, operation.syscall, opcode.isMachine, opcode.type,
operation.evaluate(vm, opcode)) : (opcode.isMachine, operation.syscall, opcode.isMachine,
opcode.type, operation.evaluate(vm.lowlevel, opcode));
}
}, TYPE = Symbol("TYPE"), INNER = Symbol("INNER"), OWNER = Symbol("OWNER"), ARGS = Symbol("ARGS"), RESOLVED = Symbol("RESOLVED"), CURRIED_VALUES = new WeakSet;
function isCurriedValue(value) {
return CURRIED_VALUES.has(value);
}
function isCurriedType(value, type) {
return isCurriedValue(value) && value[TYPE] === type;
}
class CurriedValue {
/** @internal */ constructor(type, inner, owner, args, resolved = !1) {
CURRIED_VALUES.add(this), this[TYPE] = type, this[INNER] = inner, this[OWNER] = owner,
this[ARGS] = args, this[RESOLVED] = resolved;
}
}
function resolveCurriedValue(curriedValue) {
let positional, named, definition, owner, resolved, currentWrapper = curriedValue;
for (;;) {
let {[ARGS]: curriedArgs, [INNER]: inner} = currentWrapper;
if (null !== curriedArgs) {
let {named: curriedNamed, positional: curriedPositional} = curriedArgs;
curriedPositional.length > 0 && (positional = void 0 === positional ? curriedPositional : curriedPositional.concat(positional)),
void 0 === named && (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: definition,
owner: owner,
resolved: resolved,
positional: positional,
named: named
};
}
function curry(type, spec, owner, args, resolved = !1) {
return new CurriedValue(type, spec, owner, args, resolved);
}
class DynamicScopeImpl {
constructor(bucket) {
this.bucket = bucket ? assign({}, bucket) : {};
}
get(key) {
return this.bucket[key];
}
set(key, reference) {
return this.bucket[key] = reference;
}
child() {
return new DynamicScopeImpl(this.bucket);
}
}
class ScopeImpl {
static root(owner, {self: self, size: size = 0}) {
let refs = new Array(size + 1).fill(UNDEFINED_REFERENCE);
return new ScopeImpl(owner, refs, null).init({
self: self
});
}
static sized(owner, size = 0) {
let refs = new Array(size + 1).fill(UNDEFINED_REFERENCE);
return new ScopeImpl(owner, refs, null);
}
constructor(owner, // the 0th slot is `self`
slots, // a single program can mix owners via curried components, and the state lives on root scopes
callerScope) {
this.owner = owner, this.slots = slots, this.callerScope = callerScope;
}
init({self: self}) {
return this.slots[0] = self, this;
}
/**
* @debug
*/ snapshot() {
return this.slots.slice();
}
getSelf() {
return this.get(0);
}
getSymbol(symbol) {
return this.get(symbol);
}
getBlock(symbol) {
let block = this.get(symbol);
return block === UNDEFINED_REFERENCE ? null : block;
}
bind(symbol, value) {
this.set(symbol, value);
}
bindSelf(self) {
this.set(0, self);
}
bindSymbol(symbol, value) {
this.set(symbol, value);
}
bindBlock(symbol, value) {
this.set(symbol, value);
}
bindCallerScope(scope) {
this.callerScope = scope;
}
getCallerScope() {
return this.callerScope;
}
child() {
return new ScopeImpl(this.owner, this.slots.slice(), this.callerScope);
}
get(index) {
if (index >= this.slots.length) throw new RangeError(`BUG: cannot get $${index} from scope; length=${this.slots.length}`);
return this.slots[index];
}
set(index, value) {
if (index >= this.slots.length) throw new RangeError(`BUG: cannot get $${index} from scope; length=${this.slots.length}`);
this.slots[index] = value;
}
}
class CursorImpl {
constructor(element, nextSibling) {
this.element = element, this.nextSibling = nextSibling;
}
}
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(), first = bounds.firstNode(), last = bounds.lastNode(), current = first;
for (;;) {
let next = current.nextSibling;
if (parent.insertBefore(current, reference), current === last) return next;
current = next;
}
}
function clear(bounds) {
let parent = bounds.parentElement(), first = bounds.firstNode(), last = bounds.lastNode(), current = first;
for (;;) {
let next = current.nextSibling;
if (parent.removeChild(current), current === last) return next;
current = next;
}
}
/** @internal */ function hasCustomDebugRenderTreeLifecycle(manager) {
return "getDebugCustomRenderTree" in manager;
}
let GUID = 0;
class Ref {
constructor(value) {
this.id = GUID++, this.value = value;
}
get() {
return this.value;
}
release() {
if (DEBUG && null === this.value) throw new Error("BUG: double release?");
this.value = null;
}
toString() {
let label = `Ref ${this.id}`;
if (null === this.value) return `${label} (released)`;
try {
// eslint-disable-next-line @typescript-eslint/no-base-to-string
return `${label}: ${this.value}`;
} catch {
return label;
}
}
}
class DebugRenderTreeImpl {
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 (DEBUG && 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) {
this.refs.get(state).release();
}
commit() {
this.reset();
}
capture() {
return this.captureRefs(this.roots);
}
reset() {
if (0 !== this.stack.size) {
// 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 = this.stack.toArray()[0], ref = this.refs.get(root);
for (void 0 !== ref && this.roots.delete(ref); !this.stack.isEmpty(); ) this.stack.pop();
}
}
enter(state) {
this.stack.push(state);
}
exit() {
if (DEBUG && 0 === this.stack.size) throw new Error("BUG: unbalanced pop");
this.stack.pop();
}
nodeFor(state) {
return this.nodes.get(state);
}
appendChild(node, state) {
if (DEBUG && this.refs.has(state)) throw new Error("BUG: child already appended");
let parent = this.stack.current, ref = new Ref(state);
if (this.refs.set(state, ref), parent) {
let parentNode = this.nodeFor(parent);
parentNode.refs.add(ref), node.parent = parentNode;
} else this.roots.add(ref);
}
captureRefs(refs) {
let captured = [];
return refs.forEach((ref => {
let state = ref.get();
state ? captured.push(this.captureNode(`render-node:${ref.id}`, state)) : refs.delete(ref);
})), captured;
}
captureNode(id, state) {
let node = this.nodeFor(state), {type: type, name: name, args: args, instance: instance, refs: refs} = node, template = this.captureTemplate(node), bounds = this.captureBounds(node), children = this.captureRefs(refs);
return {
id: id,
type: type,
name: name,
args: reifyArgsDebug(args),
instance: instance,
template: template,
bounds: bounds,
children: children
};
}
captureTemplate({template: template}) {
return template || null;
}
captureBounds(node) {
let bounds = node.bounds;
return {
parentElement: bounds.parentElement(),
firstNode: bounds.firstNode(),
lastNode: bounds.lastNode()
};
}
constructor() {
this.stack = new Stack, this.refs = new WeakMap, this.roots = new Set, this.nodes = new WeakMap;
}
}
function getDebugName(definition, manager = definition.manager) {
return definition.resolvedName ?? definition.debugName ?? manager.getDebugName(definition.state);
}
function normalizeStringValue(value) {
return isEmpty$2(value) ? "" : String(value);
}
function isEmpty$2(value) {
return null == value || "function" != typeof value.toString;
}
function isIndexable(value) {
return null !== value && "object" == typeof value;
}
function isSafeString(value) {
return isIndexable(value) && "function" == typeof value.toHTML;
}
function isString(value) {
return "string" == typeof value;
}
APPEND_OPCODES.add(39, (vm => vm.pushChildScope())), APPEND_OPCODES.add(40, (vm => vm.popScope())),
APPEND_OPCODES.add(59, (vm => vm.pushDynamicScope())), APPEND_OPCODES.add(60, (vm => vm.popDynamicScope())),
APPEND_OPCODES.add(28, ((vm, {op1: other}) => {
vm.stack.push(vm.constants.getValue(other));
})), APPEND_OPCODES.add(29, ((vm, {op1: other}) => {
vm.stack.push(createConstRef(vm.constants.getValue(other), !1));
})), APPEND_OPCODES.add(30, ((vm, {op1: primitive}) => {
let stack = vm.stack;
if (primitive >= 0) {
// it is a handle which does not already exist on the stack
let value = vm.constants.getValue(primitive);
stack.push(value);
} else
// is already an encoded immediate or primitive handle
stack.push(decodeImmediate(primitive));
})), APPEND_OPCODES.add(31, (vm => {
let ref, stack = vm.stack, value = stack.pop();
ref = void 0 === value ? UNDEFINED_REFERENCE : null === value ? NULL_REFERENCE : !0 === value ? TRUE_REFERENCE : !1 === value ? FALSE_REFERENCE : createPrimitiveRef(value),
stack.push(ref);
})), APPEND_OPCODES.add(33, ((vm, {op1: register, op2: offset}) => {
let position = vm.fetchValue(register) - offset;
vm.stack.dup(position);
})), APPEND_OPCODES.add(34, ((vm, {op1: count}) => {
vm.stack.pop(count);
})), APPEND_OPCODES.add(35, ((vm, {op1: register}) => {
vm.load(register);
})), APPEND_OPCODES.add(36, ((vm, {op1: register}) => {
vm.fetch(register);
})), APPEND_OPCODES.add(58, ((vm, {op1: _names}) => {
let names = vm.constants.getArray(_names);
vm.bindDynamicScope(names);
})), APPEND_OPCODES.add(69, ((vm, {op1: args}) => {
vm.enter(args);
})), APPEND_OPCODES.add(70, (vm => {
vm.exit();
})), APPEND_OPCODES.add(63, ((vm, {op1: _table}) => {
vm.stack.push(vm.constants.getValue(_table));
})), APPEND_OPCODES.add(62, (vm => {
vm.stack.push(vm.scope());
})), APPEND_OPCODES.add(61, (vm => {
let stack = vm.stack, block = stack.pop();
block ? stack.push(vm.compile(block)) : stack.push(null);
})), APPEND_OPCODES.add(64, (vm => {
let {stack: stack} = vm, handle = stack.pop(), scope = stack.pop(), table = stack.pop(), args = stack.pop();
if (null === table || null === handle)
// To balance the pop{Frame,Scope}
return vm.lowlevel.pushFrame(), void vm.pushScope(scope ?? vm.scope());
let invokingScope = scope;
// If necessary, create a child scope
{
let locals = table.parameters, localsCount = locals.length;
if (localsCount > 0) {
invokingScope = invokingScope.child();
for (let i = 0; i < localsCount; i++) invokingScope.bindSymbol(locals[i], args.at(i));
}
}
vm.lowlevel.pushFrame(), vm.pushScope(invokingScope), vm.call(handle);
})), APPEND_OPCODES.add(65, ((vm, {op1: target}) => {
let reference = vm.stack.pop(), value = Boolean(valueForRef(reference));
isConstRef(reference) ? value && vm.lowlevel.goto(target) : (value && vm.lowlevel.goto(target),
vm.updateWith(new Assert(reference)));
})), APPEND_OPCODES.add(66, ((vm, {op1: target}) => {
let reference = vm.stack.pop(), value = Boolean(valueForRef(reference));
isConstRef(reference) ? value || vm.lowlevel.goto(target) : (value || vm.lowlevel.goto(target),
vm.updateWith(new Assert(reference)));
})), APPEND_OPCODES.add(67, ((vm, {op1: target, op2: comparison}) => {
vm.stack.peek() === comparison && vm.lowlevel.goto(target);
})), APPEND_OPCODES.add(68, (vm => {
let reference = vm.stack.peek();
isConstRef(reference) || vm.updateWith(new Assert(reference));
})), APPEND_OPCODES.add(71, (vm => {
let {stack: stack} = vm, valueRef = stack.pop();
stack.push(createComputeRef((() => toBool(valueForRef(valueRef)))));
}));
class Assert {
constructor(ref) {
this.ref = ref, this.last = valueForRef(ref);
}
evaluate(vm) {
let {last: last, ref: ref} = this;
last !== valueForRef(ref) && vm.throw();
}
}
class AssertFilter {
constructor(ref, filter) {
this.ref = ref, this.filter = filter, this.last = filter(valueForRef(ref));
}
evaluate(vm) {
let {last: last, ref: ref, filter: filter} = this;
last !== filter(valueForRef(ref)) && vm.throw();
}
}
class JumpIfNotModifiedOpcode {
finalize(tag, target) {
this.target = target, this.didModify(tag);
}
evaluate(vm) {
let {tag: tag, target: target, lastRevision: lastRevision} = this;
!vm.alwaysRevalidate && validateTag(tag, lastRevision) && (consumeTag(tag), vm.goto(target));
}
didModify(tag) {
this.tag = tag, this.lastRevision = valueForTag(this.tag), consumeTag(tag);
}
constructor() {
this.tag = CONSTANT_TAG, this.lastRevision = INITIAL;
}
}
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(41, ((vm, {op1: text}) => {
vm.tree().appendText(vm.constants.getValue(text));
})), APPEND_OPCODES.add(42, ((vm, {op1: text}) => {
vm.tree().appendComment(vm.constants.getValue(text));
})), APPEND_OPCODES.add(48, ((vm, {op1: tag}) => {
vm.tree().openElement(vm.constants.getValue(tag));
})), APPEND_OPCODES.add(49, (vm => {
let tagName = valueForRef(vm.stack.pop());
vm.tree().openElement(tagName);
})), APPEND_OPCODES.add(50, (vm => {
let elementRef = vm.stack.pop(), insertBeforeRef = vm.stack.pop(), guidRef = vm.stack.pop(), element = valueForRef(elementRef), insertBefore = valueForRef(insertBeforeRef), guid = valueForRef(guidRef);
isConstRef(elementRef) || vm.updateWith(new Assert(elementRef)), void 0 === insertBefore || isConstRef(insertBeforeRef) || vm.updateWith(new Assert(insertBeforeRef));
let block = vm.tree().pushRemoteElement(element, guid, insertBefore);
if (vm.associateDestroyable(block), void 0 !== vm.env.debugRenderTree) {
// 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(void 0 === insertBefore ? {} : {
insertBefore: insertBeforeRef
}, [ elementRef ]);
vm.env.debugRenderTree.create(block, {
type: "keyword",
name: "in-element",
args: args,
instance: null
}), registerDestructor(block, (() => {
vm.env.debugRenderTree?.willDestroy(block);
}));
}
})), APPEND_OPCODES.add(56, (vm => {
let bounds = vm.tree().popRemoteElement();
void 0 !== vm.env.debugRenderTree &&
// The RemoteBlock is also its bounds
vm.env.debugRenderTree.didRender(bounds, bounds);
})), APPEND_OPCODES.add(54, (vm => {
let operations = vm.fetchValue($t0), modifiers = null;
operations && (modifiers = operations.flush(vm), vm.loadValue($t0, null)), vm.tree().flushElement(modifiers);
})), APPEND_OPCODES.add(55, (vm => {
let modifiers = vm.tree().closeElement();
null !== modifiers && modifiers.forEach((modifier => {
vm.env.scheduleInstallModifier(modifier);
const d = modifier.manager.getDestroyable(modifier.state);
null !== d && vm.associateDestroyable(d);
}));
})), APPEND_OPCODES.add(57, ((vm, {op1: handle}) => {
if (!vm.env.isInteractive) return;
let owner = vm.getOwner(), args = vm.stack.pop(), definition = vm.constants.getValue(handle), {manager: manager} = definition, {constructing: constructing} = vm.tree(), capturedArgs = args.capture(), state = manager.create(owner, constructing, definition.state, capturedArgs), instance = {
manager: manager,
state: state,
definition: definition
};
vm.fetchValue($t0).addModifier(vm, instance, capturedArgs);
let tag = manager.getTag(state);
return null !== tag ? (consumeTag(tag), vm.updateWith(new UpdateModifierOpcode(tag, instance))) : void 0;
})), APPEND_OPCODES.add(108, (vm => {
if (!vm.env.isInteractive) return;
let {stack: stack} = vm, ref = stack.pop(), args = stack.pop().capture(), {positional: outerPositional, named: outerNamed} = args, {constructing: constructing} = vm.tree(), initialOwner = vm.getOwner(), instanceRef = createComputeRef((() => {
let owner, hostDefinition, value = valueForRef(ref);
if (!isIndexable$1(value)) return;
if (isCurriedType(value, 2)) {
let {definition: resolvedDefinition, owner: curriedOwner, positional: positional, named: named} = resolveCurriedValue(value);
hostDefinition = resolvedDefinition, owner = curriedOwner, void 0 !== positional && (args.positional = positional.concat(outerPositional)),
void 0 !== named && (
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
args.named = Object.assign({}, ...named, outerNamed));
} else hostDefinition = value, owner = initialOwner;
let manager = getInternalModifierManager(hostDefinition, !0);
if (null === manager) throw DEBUG ? 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$1?.(hostDefinition)}`) : new Error("BUG: modifier manager expected");
let definition = {
resolvedName: null,
manager: manager,
state: hostDefinition
}, state = manager.create(owner, constructing, definition.state, args);
return {
manager: manager,
state: state,
definition: definition
};
})), instance = valueForRef(instanceRef), tag = null;
return void 0 !== instance && (vm.fetchValue($t0).addModifier(vm, instance, args),
tag = instance.manager.getTag(instance.state), null !== tag && consumeTag(tag)),
!isConstRef(ref) || tag ? vm.updateWith(new UpdateDynamicModifierOpcode(tag, instance, instanceRef)) : void 0;
}));
class UpdateModifierOpcode {
constructor(tag, modifier) {
this.tag = tag, this.modifier = modifier, this.lastUpdated = valueForTag(tag);
}
evaluate(vm) {
let {modifier: modifier, tag: tag, lastUpdated: lastUpdated} = this;
consumeTag(tag), validateTag(tag, lastUpdated) || (vm.env.scheduleUpdateModifier(modifier),
this.lastUpdated = valueForTag(tag));
}
}
class UpdateDynamicModifierOpcode {
constructor(tag, instance, instanceRef) {
this.tag = tag, this.instance = instance, this.instanceRef = instanceRef, this.lastUpdated = valueForTag(tag ?? CURRENT_TAG);
}
evaluate(vm) {
let {tag: tag, lastUpdated: lastUpdated, instance: instance, instanceRef: instanceRef} = this, newInstance = valueForRef(instanceRef);
if (newInstance !== instance) {
if (void 0 !== instance) {
let destroyable = instance.manager.getDestroyable(instance.state);
null !== destroyable && destroy(destroyable);
}
if (void 0 !== newInstance) {
let {manager: manager, state: state} = newInstance, destroyable = manager.getDestroyable(state);
null !== destroyable && associateDestroyableChild(this, destroyable), tag = manager.getTag(state),
null !== tag && (this.lastUpdated = valueForTag(tag)), this.tag = tag, vm.env.scheduleInstallModifier(newInstance);
}
this.instance = newInstance;
} else null === tag || validateTag(tag, lastUpdated) || (
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- @fixme
vm.env.scheduleUpdateModifier(instance), this.lastUpdated = valueForTag(tag));
null !== tag && consumeTag(tag);
}
}
APPEND_OPCODES.add(51, ((vm, {op1: _name, op2: _value, op3: _namespace}) => {
let name = vm.constants.getValue(_name), value = vm.constants.getValue(_value), namespace = _namespace ? vm.constants.getValue(_namespace) : null;
vm.tree().setStaticAttribute(name, value, namespace);
})), APPEND_OPCODES.add(52, ((vm, {op1: _name, op2: _trusting, op3: _namespace}) => {
let name = vm.constants.getValue(_name), trusting = vm.constants.getValue(_trusting), reference = vm.stack.pop(), value = valueForRef(reference), namespace = _namespace ? vm.constants.getValue(_namespace) : null, attribute = vm.tree().setDynamicAttribute(name, value, trusting, namespace);
isConstRef(reference) || vm.updateWith(new UpdateDynamicAttributeOpcode(reference, attribute, vm.env));
}));
class UpdateDynamicAttributeOpcode {
constructor(reference, attribute, env) {
let initialized = !1;
this.updateRef = createComputeRef((() => {
let value = valueForRef(reference);
initialized ? attribute.update(value, env) : initialized = !0;
})), valueForRef(this.updateRef);
}
evaluate() {
valueForRef(this.updateRef);
}
}
APPEND_OPCODES.add(78, ((vm, {op1: handle}) => {
let definition = vm.constants.getValue(handle), {manager: manager, capabilities: capabilities} = definition, instance = {
definition: definition,
manager: manager,
capabilities: capabilities,
state: null,
handle: null,
table: null,
lookup: null
};
vm.stack.push(instance);
})), APPEND_OPCODES.add(80, ((vm, {op1: _isStrict}) => {
let definition, stack = vm.stack, component = valueForRef(stack.pop()), constants = vm.constants, owner = vm.getOwner(), isStrict = constants.getValue(_isStrict);
if (vm.loadValue($t1, null), "string" == typeof component) {
if (DEBUG && 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 = function(resolver, constants, name, owner) {
let definition = resolver?.lookupComponent?.(name, owner) ?? null;
if (DEBUG && !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);
}(vm.context.resolver, constants, component, owner);
definition = resolvedDefinition;
} else definition = isCurriedValue(component) ? component : constants.component(component, owner);
stack.push(definition);
})), APPEND_OPCODES.add(81, (vm => {
let definition, stack = vm.stack, ref = stack.pop(), value = valueForRef(ref), constants = vm.constants;
if (DEBUG && "function" != typeof value && ("object" != typeof value || null === value)) 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 if (definition = constants.component(value, vm.getOwner(), !0),
DEBUG && null === definition) 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$1?.(value) ?? value}`);
stack.push(definition);
})), APPEND_OPCODES.add(79, (vm => {
let capabilities, manager, {stack: stack} = vm, definition = stack.pop();
isCurriedValue(definition) ? manager = capabilities = null : (manager = definition.manager,
capabilities = definition.capabilities), stack.push({
definition: definition,
capabilities: capabilities,
manager: manager,
state: null,
handle: null,
table: null
});
})), APPEND_OPCODES.add(82, ((vm, {op1: _names, op2: _blockNames, op3: flags}) => {
let stack = vm.stack, names = vm.constants.getArray(_names), positionalCount = flags >> 4, atNames = 8 & flags, blockNames = 7 & flags ? vm.constants.getArray(_blockNames) : EMPTY_STRING_ARRAY;
vm.args.setup(stack, names, blockNames, positionalCount, !!atNames), stack.push(vm.args);
})), APPEND_OPCODES.add(83, (vm => {
let {stack: stack} = vm;
stack.push(vm.args.empty(stack));
})), APPEND_OPCODES.add(86, (vm => {
let stack = vm.stack, capturedArgs = stack.pop().capture();
stack.push(capturedArgs);
})), APPEND_OPCODES.add(85, ((vm, {op1: register}) => {
let stack = vm.stack, instance = vm.fetchValue(register), args = stack.pop(), {definition: definition} = instance;
if (isCurriedType(definition, 0)) {
definition.manager;
let constants = vm.constants, {definition: resolvedDefinition, owner: owner, resolved: resolved, positional: positional, named: named} = resolveCurriedValue(definition);
if (resolved) definition = resolvedDefinition; else if ("string" == typeof resolvedDefinition) {
let resolvedValue = vm.context.resolver?.lookupComponent?.(resolvedDefinition, owner) ?? null;
definition = constants.resolvedComponent(resolvedValue, resolvedDefinition);
} else definition = constants.component(resolvedDefinition, owner);
void 0 !== named &&
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
args.named.merge(assign({}, ...named)), void 0 !== positional && (args.realloc(positional.length),
args.positional.prepend(positional));
let {manager: 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: manager, state: state} = definition, capabilities = instance.capabilities;
if (!managerHasCapability(manager, capabilities, InternalComponentCapabilities.prepareArgs)) return void stack.push(args);
let blocks = args.blocks.values, blockNames = args.blocks.names, preparedArgs = manager.prepareArgs(state, args);
if (preparedArgs) {
args.clear();
for (let i = 0; i < blocks.length; i++) stack.push(blocks[i]);
let {positional: positional, named: named} = preparedArgs, 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[names[i]]);
args.setup(stack, names, blockNames, positionalCount, !1);
}
stack.push(args);
})), APPEND_OPCODES.add(87, ((vm, {op1: flags}) => {
let instance = vm.fetchValue($s0), {definition: definition, manager: manager, capabilities: 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;
managerHasCapability(manager, capabilities, InternalComponentCapabilities.dynamicScope) && (dynamicScope = vm.dynamicScope());
let hasDefaultBlock = 1 & flags, args = null;
managerHasCapability(manager, capabilities, InternalComponentCapabilities.createArgs) && (args = vm.stack.peek());
let self = null;
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, managerHasCapability(manager, capabilities, InternalComponentCapabilities.updateHook) && vm.updateWith(new UpdateComponentOpcode(state, manager, dynamicScope));
})), APPEND_OPCODES.add(88, ((vm, {op1: register}) => {
let {manager: manager, state: state, capabilities: capabilities} = vm.fetchValue(register), d = manager.getDestroyable(state);
if (DEBUG && !managerHasCapability(manager, capabilities, InternalComponentCapabilities.willDestroy) && null !== d && "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");
d && vm.associateDestroyable(d);
})), APPEND_OPCODES.add(97, ((vm, {op1: register}) => {
let name;
if (DEBUG) {
let {definition: definition, manager: manager} = vm.fetchValue(register);
name = getDebugName(definition, manager);
}
vm.beginCacheGroup(name), vm.tree().pushAppendingBlock();
})), APPEND_OPCODES.add(89, (vm => {
vm.loadValue($t0, new ComponentElementOperations);
})), APPEND_OPCODES.add(53, ((vm, {op1: _name, op2: _trusting, op3: _namespace}) => {
let name = vm.constants.getValue(_name), trusting = vm.constants.getValue(_trusting), reference = vm.stack.pop(), namespace = _namespace ? vm.constants.getValue(_namespace) : null;
vm.fetchValue($t0).setAttribute(name, reference, trusting, namespace);
})), APPEND_OPCODES.add(105, ((vm, {op1: _name, op2: _value, op3: _namespace}) => {
let name = vm.constants.getValue(_name), value = vm.constants.getValue(_value), namespace = _namespace ? vm.constants.getValue(_namespace) : null;
vm.fetchValue($t0).setStaticAttribute(name, value, namespace);
}));
class ComponentElementOperations {
setAttribute(name, value, trusting, namespace) {
let deferred = {
value: value,
namespace: namespace,
trusting: trusting
};
"class" === name && this.classes.push(value), this.attributes[name] = deferred;
}
setStaticAttribute(name, value, namespace) {
let deferred = {
value: value,
namespace: namespace
};
"class" === name && this.classes.push(value), this.attributes[name] = deferred;
}
addModifier(vm, modifier, capturedArgs) {
if (this.modifiers.push(modifier), void 0 !== vm.env.debugRenderTree) {
const {manager: manager, definition: definition, state: 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 (null === state || "object" != typeof state && "function" != typeof state) return;
let {element: element, constructing: constructing} = vm.tree(), name = definition.resolvedName ?? manager.getDebugName(definition.state), instance = manager.getDebugInstance(state), bounds = new ConcreteBounds(element, constructing, constructing);
vm.env.debugRenderTree.create(state, {
type: "modifier",
name: name,
args: capturedArgs,
instance: 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, attributes = this.attributes;
for (let name in this.attributes) {
if ("type" === name) {
type = attributes[name];
continue;
}
let attr = this.attributes[name];
"class" === name ? setDeferredAttr(vm, "class", mergeClasses(this.classes), attr.namespace, attr.trusting) : setDeferredAttr(vm, name, attr.value, attr.namespace, attr.trusting);
}
return void 0 !== type && setDeferredAttr(vm, "type", type.value, type.namespace, type.trusting),
this.modifiers;
}
constructor() {
this.attributes = dict(), this.classes = [], this.modifiers = [];
}
}
function mergeClasses(classes) {
return 0 === classes.length ? "" : 1 === classes.length ? classes[0] : function(classes) {
return classes.every((c => "string" == typeof c));
}(classes) ? classes.join(" ") : (list = classes, createComputeRef((() => {
let ret = [];
for (const ref of list) {
let value = normalizeStringValue("string" == typeof ref ? ref : valueForRef(ref));
value && ret.push(value);
}
return 0 === ret.length ? null : ret.join(" ");
})));
var list;
}
function setDeferredAttr(vm, name, value, namespace, trusting = !1) {
if ("string" == typeof value) vm.tree().setStaticAttribute(name, value, namespace); else {
let attribute = vm.tree().setDynamicAttribute(name, valueForRef(value), trusting, namespace);
isConstRef(value) || vm.updateWith(new UpdateDynamicAttributeOpcode(value, attribute, vm.env));
}
}
function bindBlock(symbolName, blockName, state, blocks, vm) {
let symbol = state.table.symbols.indexOf(symbolName), block = blocks.get(blockName);
-1 !== symbol && vm.scope().bindBlock(symbol + 1, block), state.lookup && (state.lookup[symbolName] = block);
}
APPEND_OPCODES.add(99, ((vm, {op1: register}) => {
let {definition: definition, state: state} = vm.fetchValue(register), {manager: manager} = definition, operations = vm.fetchValue($t0);
manager.didCreateElement(state, vm.tree().constructing, operations);
})), APPEND_OPCODES.add(90, ((vm, {op1: register, op2: _names}) => {
let instance = vm.fetchValue(register), {definition: definition, state: state} = instance, {manager: manager} = definition, selfRef = manager.getSelf(state);
if (void 0 !== vm.env.debugRenderTree) {
let args, moduleName, instance = vm.fetchValue(register), {definition: definition, manager: manager} = instance;
if (vm.stack.peek() === vm.args) args = vm.args.capture(); else {
let names = vm.constants.getArray(_names);
vm.args.setup(vm.stack, names, [], 0, !0), args = vm.args.capture();
}
let compilable = definition.compilable;
if (null === compilable) {
managerHasCapability(manager, instance.capabilities, InternalComponentCapabilities.dynamicLayout);
let resolver = vm.context.resolver;
compilable = null === resolver ? null : manager.getDynamicLayout(state, resolver),
moduleName = null !== compilable ? compilable.moduleName : "__default__.hbs";
} else moduleName = compilable.moduleName;
// For tearing down the debugRenderTree
if (vm.associateDestroyable(instance), hasCustomDebugRenderTreeLifecycle(manager)) manager.getDebugCustomRenderTree(instance.definition.state, instance.state, args, moduleName).forEach((node => {
let {bucket: 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: name,
args: 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(91, ((vm, {op1: register}) => {
let {definition: definition, state: state} = vm.fetchValue(register), {manager: manager} = definition, 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(92, ((vm, {op1: register}) => {
let instance = vm.fetchValue(register), {manager: manager, definition: definition} = instance, {stack: stack} = vm, {compilable: compilable} = definition;
if (null === compilable) {
let {capabilities: capabilities} = instance;
managerHasCapability(manager, capabilities, InternalComponentCapabilities.dynamicLayout);
let resolver = vm.context.resolver;
compilable = null === resolver ? null : manager.getDynamicLayout(instance.state, resolver),
null === compilable && (compilable = managerHasCapability(manager, capabilities, InternalComponentCapabilities.wrapped) ? unwrapTemplate(vm.constants.defaultTemplate).asWrappedLayout() : unwrapTemplate(vm.constants.defaultTemplate).asLayout());
}
let handle = compilable.compile(vm.context);
stack.push(compilable.symbolTable), stack.push(handle);
})), APPEND_OPCODES.add(75, ((vm, {op1: register}) => {
let definition = vm.stack.pop(), invocation = vm.stack.pop(), {manager: manager, capabilities: capabilities} = definition, state = {
definition: definition,
manager: manager,
capabilities: capabilities,
state: null,
handle: invocation.handle,
table: invocation.symbolTable,
lookup: null
};
vm.loadValue(register, state);
})), APPEND_OPCODES.add(95, ((vm, {op1: register}) => {
let {stack: stack} = vm, handle = stack.pop(), table = stack.pop(), state = vm.fetchValue(register);
// In DEBUG handles could be ErrHandle objects
state.handle = handle, state.table = table;
})), APPEND_OPCODES.add(38, ((vm, {op1: register}) => {
let owner, {table: table, manager: manager, capabilities: capabilities, state: state} = vm.fetchValue(register);
managerHasCapability(manager, capabilities, InternalComponentCapabilities.hasSubOwner) ? (owner = manager.getOwner(state),
vm.loadValue($t1, null)) : (
// Check the temp register to see if an owner was resolved from currying
owner = vm.fetchValue($t1), null === owner ?
// If an owner wasn't found, default to using the current owner. This
// will happen for normal dynamic component invocation,
// e.g. <SomeClassicEmberComponent/>
owner = vm.getOwner() :
// Else the owner was found, so clear the temp register. This will happen
// if we are loading a curried component, e.g. <@someCurriedComponent/>
vm.loadValue($t1, null)), vm.pushRootScope(table.symbols.length + 1, owner);
})), APPEND_OPCODES.add(17, ((vm, {op1: register}) => {
let state = vm.fetchValue(register), scope = vm.scope(), args = vm.stack.peek(), callerNames = args.named.atNames;
for (let i = callerNames.length - 1; i >= 0; i--) {
let atName = callerNames[i], symbol = state.table.symbols.indexOf(atName), value = args.named.get(atName, !0);
-1 !== symbol && scope.bindSymbol(symbol + 1, value), state.lookup && (state.lookup[atName] = value);
}
})), APPEND_OPCODES.add(18, ((vm, {op1: register}) => {
let state = vm.fetchValue(register), {blocks: blocks} = vm.stack.peek();
for (const [i] of enumerate(blocks.names)) bindBlock(blocks.symbolNames[i], blocks.names[i], state, blocks, vm);
})),
// Dynamic Invocation Only
APPEND_OPCODES.add(96, ((vm, {op1: register}) => {
let state = vm.fetchValue(register);
vm.call(state.handle);
})), APPEND_OPCODES.add(100, ((vm, {op1: register}) => {
let instance = vm.fetchValue(register), {manager: manager, state: state, capabilities: capabilities} = instance, bounds = vm.tree().popBlock();
void 0 !== vm.env.debugRenderTree && (hasCustomDebugRenderTreeLifecycle(manager) ? manager.getDebugCustomRenderTree(instance.definition.state, state, EMPTY_ARGS).reverse().forEach((node => {
let {bucket: bucket} = node;
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- @fixme
vm.env.debugRenderTree.didRender(bucket, bounds), vm.updateWith(new DebugRenderTreeDidRenderOpcode(bucket, bounds));
})) : (vm.env.debugRenderTree.didRender(instance, bounds), vm.updateWith(new DebugRenderTreeDidRenderOpcode(instance, bounds)))),
managerHasCapability(manager, capabilities, InternalComponentCapabilities.createInstance) && (
// eslint-disable-next-line @typescript-eslint/no-unsafe-call -- @fixme
manager.didRenderLayout(state, bounds), vm.env.didCreate(instance), vm.updateWith(new DidUpdateLa