ember-source
Version:
A JavaScript framework for creating ambitious web applications
1,380 lines (1,374 loc) • 167 kB
JavaScript
import { registerDestructor, destroy, associateDestroyableChild, _hasDestroyableChildren, isDestroying, isDestroyed, destroyChildren } from '../destroyable/index.js';
import { toBool, warnIfStyleNotTrusted, debugAssert, assertGlobalContextWasSet, setPath, getPath } from '../global-context/index.js';
import { getInternalModifierManager, managerHasCapability, setInternalComponentManager, setInternalModifierManager, getInternalHelperManager, hasInternalComponentManager, hasInternalHelperManager, setInternalHelperManager, hasValue, hasDestroyable } from '../manager/index.js';
import { createConstRef, UNDEFINED_REFERENCE, NULL_REFERENCE, TRUE_REFERENCE, FALSE_REFERENCE, createPrimitiveRef, valueForRef, isConstRef, createComputeRef, childRefFor, createIteratorRef, createIteratorItemRef, updateRef, createDebugAliasRef, isInvokableRef } from '../reference/index.js';
import { isIndexable as isIndexable$1, EMPTY_STRING_ARRAY, dict, enumerate, emptyArray, assign, clearElement, Stack as StackImpl, reverse, isDict } from '../util/index.js';
import { $t0, $t1, $s0, $v0, ContentType, $pc, $ra, $fp, $sp, isLowLevelRegister, InternalComponentCapabilities } from '../vm/index.js';
import { consumeTag, valueForTag, validateTag, CURRENT_TAG, track, updateTag as UPDATE_TAG, createCache, getValue, debug, resetTracking, beginTrackFrame, endTrackFrame, CONSTANT_TAG, INITIAL, createUpdatableTag } from '../validator/index.js';
import { ProgramImpl } from '../program/index.js';
import { getOwner } from '../owner/index.js';
import { isDevelopingApp } from '@embroider/macros';
const NS_MATHML = "http://www.w3.org/1998/Math/MathML",
NS_SVG = "http://www.w3.org/2000/svg";
let debugToString;
if (isDevelopingApp()) {
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 (isDevelopingApp()) {
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), false),
has: (_target, property) => (assertOnProperty(property), false)
});
}
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;
});
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 = false) {
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 = false) {
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 = 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 (isDevelopingApp() && 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 (isDevelopingApp() && 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 (isDevelopingApp() && 0 === this.stack.size) throw new Error("BUG: unbalanced pop");
this.stack.pop();
}
nodeFor(state) {
return this.nodes.get(state);
}
appendChild(node, state) {
if (isDevelopingApp() && 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 StackImpl(), 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), false));
}), 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 : true === value ? TRUE_REFERENCE : false === 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, true);
if (null === manager) throw isDevelopingApp() ? 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 = false;
this.updateRef = createComputeRef(() => {
let value = valueForRef(reference);
initialized ? attribute.update(value, env) : initialized = true;
}), 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 (isDevelopingApp() && 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 (isDevelopingApp() && !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 (isDevelopingApp() && "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(), true), isDevelopingApp() && 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, false);
}
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 (isDevelopingApp() && !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 (isDevelopingApp()) {
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 = false) {
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, true), 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, true);
-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 DidUpdateLayoutOpcode(instance, bounds)));
}), APPEND_OPCODES.add(98, vm => {
vm.commitCacheGroup();
});
class UpdateComponentOpcode {
constructor(component, manager, dynamicScope) {
this.component = component, this.manager = manager, this.dynamicScope = dynamicScope;
}
evaluate(_vm) {
let {
component: component,
manager: manager,
dynamicScope: dynamicScope
} = this;
manager.update(component, dynamicScope);
}
}
class DidUpdateLayoutOpcode {
constructor(component, bounds) {
this.component = component, this.bounds = bounds;
}
evaluate(vm) {
let {
component: component,
bounds: bounds
} = this,
{
manager: manager,
state: state
} = component;
manager.didUpdateLayout(state, bounds), vm.env.didUpdate(component);
}
}
class DebugRenderTreeUpdateOpcode {
constructor(bucket) {
this.bucket = bucket;
}
evaluate(vm) {
vm.env.debugRenderTree?.update(this.bucket);
}
}
class DebugRenderTreeDidRenderOpcode {
constructor(bucket, bounds) {
this.bucket = bucket, this.bounds = bounds;
}
evaluate(vm) {
vm.env.debugRenderTree?.didRender(this.bucket, this.bounds);
}
}
/*
The calling convention is:
* 0-N block arguments at the bottom
* 0-N positional arguments next (left-to-right)
* 0-N named arguments next
*/
class VMArgumentsImpl {