ember-source
Version:
A JavaScript framework for creating ambitious web applications
4,361 lines • 167 kB
JavaScript
import { Op, $t0, $t1, $v0, $pc, CurriedType as CurriedTypes, InternalComponentCapabilities, ContentType, isLowLevelRegister, $s1, $s0, $sp, $fp, $ra, MachineOp } from '../vm/index.js';
import { createConstRef, UNDEFINED_REFERENCE, NULL_REFERENCE, TRUE_REFERENCE, FALSE_REFERENCE, createPrimitiveRef, valueForRef, isConstRef, createComputeRef, childRefFor, createIteratorRef, createIteratorItemRef, createDebugAliasRef, isInvokableRef, updateRef } from '../reference/index.js';
import { decodeHandle, isHandle, decodeImmediate, assert as debugAssert, expect, unwrap, debugToString as debugToString$1, EMPTY_STRING_ARRAY, assign, dict, unwrapTemplate, enumerate, castToSimple, buildUntouchableThis, NS_SVG, castToBrowser, Stack as StackImpl, isObject, INSERT_BEFORE_END, clearElement, INSERT_AFTER_BEGIN, unwrapHandle, reverse, COMMENT_NODE, emptyArray, INSERT_BEFORE_BEGIN, isDict } from '../util/index.js';
import { registerDestructor, destroy, associateDestroyableChild, _hasDestroyableChildren, isDestroying, isDestroyed, destroyChildren } from '../destroyable/index.js';
import { warnIfStyleNotTrusted, toBool, assertGlobalContextWasSet, setPath, getPath } from '../global-context/index.js';
import { managerHasCapability, setInternalComponentManager, setInternalModifierManager, getInternalModifierManager, hasInternalComponentManager, hasInternalHelperManager, setInternalHelperManager, getInternalHelperManager, hasValue, hasDestroyable } from '../manager/index.js';
import { consumeTag, valueForTag, validateTag, CURRENT_TAG, createCache, debug, resetTracking, beginTrackFrame, endTrackFrame, CONSTANT_TAG, track, updateTag as UPDATE_TAG, INITIAL, getValue, createUpdatableTag } from '../validator/index.js';
import { RuntimeProgramImpl } from '../program/index.js';
import { getOwner } from '../owner/index.js';
import { isDevelopingApp } from '@embroider/macros';
new Array(Op.Size).fill(null), new Array(Op.Size).fill(null);
class DynamicScopeImpl {
bucket;
constructor(bucket) {
this.bucket = bucket ? assign({}, bucket) : {};
}
get(key) {
return unwrap(this.bucket[key]);
}
set(key, reference) {
return this.bucket[key] = reference;
}
child() {
return new DynamicScopeImpl(this.bucket);
}
}
class PartialScopeImpl {
static root(self, size = 0, owner) {
let refs = new Array(size + 1).fill(UNDEFINED_REFERENCE);
return new PartialScopeImpl(refs, owner, null, null, null).init({
self: self
});
}
static sized(size = 0, owner) {
let refs = new Array(size + 1).fill(UNDEFINED_REFERENCE);
return new PartialScopeImpl(refs, owner, null, null, null);
}
constructor(
// the 0th slot is `self`
slots, owner, callerScope,
// named arguments and blocks passed to a layout that uses eval
evalScope,
// locals in scope when the partial was invoked
partialMap) {
this.slots = slots, this.owner = owner, this.callerScope = callerScope, this.evalScope = evalScope, this.partialMap = partialMap;
}
init({
self: self
}) {
return this.slots[0] = self, this;
}
getSelf() {
return this.get(0);
}
getSymbol(symbol) {
return this.get(symbol);
}
getBlock(symbol) {
let block = this.get(symbol);
return block === UNDEFINED_REFERENCE ? null : block;
}
getEvalScope() {
return this.evalScope;
}
getPartialMap() {
return this.partialMap;
}
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);
}
bindEvalScope(map) {
this.evalScope = map;
}
bindPartialMap(map) {
this.partialMap = map;
}
bindCallerScope(scope) {
this.callerScope = scope;
}
getCallerScope() {
return this.callerScope;
}
child() {
return new PartialScopeImpl(this.slots.slice(), this.owner, this.callerScope, this.evalScope, this.partialMap);
}
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;
}
}
// These symbols represent "friend" properties that are used inside of
// the VM in other classes, but are not intended to be a part of
// Glimmer's API.
const INNER_VM = Symbol("INNER_VM"),
DESTROYABLE_STACK = Symbol("DESTROYABLE_STACK"),
STACKS = Symbol("STACKS"),
REGISTERS = Symbol("REGISTERS"),
HEAP = Symbol("HEAP"),
CONSTANTS = Symbol("CONSTANTS"),
ARGS$1 = Symbol("ARGS");
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;
// eslint-disable-next-line no-constant-condition
for (;;) {
let next = current.nextSibling;
if (parent.insertBefore(current, reference), current === last) return next;
current = expect(next, "invalid bounds");
}
}
function clear(bounds) {
let parent = bounds.parentElement(),
first = bounds.firstNode(),
last = bounds.lastNode(),
current = first;
// eslint-disable-next-line no-constant-condition
for (;;) {
let next = current.nextSibling;
if (parent.removeChild(current), current === last) return next;
current = expect(next, "invalid bounds");
}
}
function normalizeStringValue(value) {
return isEmpty$2(value) ? "" : String(value);
}
function isEmpty$2(value) {
return null == value || "function" != typeof value.toString;
}
function isSafeString(value) {
return "object" == typeof value && null !== value && "function" == typeof value.toHTML;
}
function isNode(value) {
return "object" == typeof value && null !== value && "number" == typeof value.nodeType;
}
function isString(value) {
return "string" == typeof value;
}
/*
* @method normalizeProperty
* @param element {HTMLElement}
* @param slotName {String}
* @returns {Object} { name, type }
*/
function normalizeProperty(element, slotName) {
let type, normalized;
if (slotName in element) normalized = slotName, type = "prop";else {
let lower = slotName.toLowerCase();
lower in element ? (type = "prop", normalized = lower) : (type = "attr", normalized = slotName);
}
return "prop" !== type || "style" !== normalized.toLowerCase() && !function (tagName, propName) {
let tag = ATTR_OVERRIDES[tagName.toUpperCase()];
return tag && tag[propName.toLowerCase()] || !1;
}(element.tagName, normalized) || (type = "attr"), {
normalized: normalized,
type: type
};
}
// properties that MUST be set as attributes, due to:
// * browser bug
// * strange spec outlier
const ATTR_OVERRIDES = {
INPUT: {
form: !0,
// Chrome 46.0.2464.0: 'autocorrect' in document.createElement('input') === false
// Safari 8.0.7: 'autocorrect' in document.createElement('input') === false
// Mobile Safari (iOS 8.4 simulator): 'autocorrect' in document.createElement('input') === true
autocorrect: !0,
// Chrome 54.0.2840.98: 'list' in document.createElement('input') === true
// Safari 9.1.3: 'list' in document.createElement('input') === false
list: !0
},
// element.form is actually a legitimate readOnly property, that is to be
// mutated, but must be mutated by setAttribute...
SELECT: {
form: !0
},
OPTION: {
form: !0
},
TEXTAREA: {
form: !0
},
LABEL: {
form: !0
},
FIELDSET: {
form: !0
},
LEGEND: {
form: !0
},
OBJECT: {
form: !0
},
OUTPUT: {
form: !0
},
BUTTON: {
form: !0
}
},
badProtocols = ["javascript:", "vbscript:"],
badTags = ["A", "BODY", "LINK", "IMG", "IFRAME", "BASE", "FORM"],
badTagsForDataURI = ["EMBED"],
badAttributes = ["href", "src", "background", "action"],
badAttributesForDataURI = ["src"];
function has(array, item) {
return -1 !== array.indexOf(item);
}
function checkURI(tagName, attribute) {
return (null === tagName || has(badTags, tagName)) && has(badAttributes, attribute);
}
function checkDataURI(tagName, attribute) {
return null !== tagName && has(badTagsForDataURI, tagName) && has(badAttributesForDataURI, attribute);
}
function requiresSanitization(tagName, attribute) {
return checkURI(tagName, attribute) || checkDataURI(tagName, attribute);
}
let _protocolForUrlImplementation, DebugStyleAttributeManager;
function sanitizeAttributeValue(element, attribute, value) {
let tagName = null;
if (null == value) return value;
if (isSafeString(value)) return value.toHTML();
tagName = element ? element.tagName.toUpperCase() : null;
let str = normalizeStringValue(value);
if (checkURI(tagName, attribute)) {
let protocol = (url = str, _protocolForUrlImplementation || (_protocolForUrlImplementation = function () {
if ("object" == typeof URL && null !== URL &&
// this is super annoying, TS thinks that URL **must** be a function so `URL.parse` check
// thinks it is `never` without this `as unknown as any`
"function" == typeof URL.parse) {
// In Ember-land the `fastboot` package sets the `URL` global to `require('url')`
// ultimately, this should be changed (so that we can either rely on the natural `URL` global
// that exists) but for now we have to detect the specific `FastBoot` case first
// a future version of `fastboot` will detect if this legacy URL setup is required (by
// inspecting Ember version) and if new enough, it will avoid shadowing the `URL` global
// constructor with `require('url')`.
let nodeURL = URL;
return url => {
let protocol = null;
return "string" == typeof url && (protocol = nodeURL.parse(url).protocol), null === protocol ? ":" : protocol;
};
}
if ("function" == typeof URL) return _url => {
try {
return new URL(_url).protocol;
} catch (error) {
// any non-fully qualified url string will trigger an error (because there is no
// baseURI that we can provide; in that case we **know** that the protocol is
// "safe" because it isn't specifically one of the `badProtocols` listed above
// (and those protocols can never be the default baseURI)
return ":";
}
};
throw new Error('@glimmer/runtime needs a valid "globalThis.URL"');
}()), _protocolForUrlImplementation(url));
if (has(badProtocols, protocol)) return `unsafe:${str}`;
}
var url;
return checkDataURI(tagName, attribute) ? `unsafe:${str}` : str;
}
function dynamicAttribute(element, attr, namespace, isTrusting = !1) {
const {
tagName: tagName,
namespaceURI: namespaceURI
} = element,
attribute = {
element: element,
name: attr,
namespace: namespace
};
if (isDevelopingApp() && "style" === attr && !isTrusting) return new DebugStyleAttributeManager(attribute);
if (namespaceURI === NS_SVG) return buildDynamicAttribute(tagName, attr, attribute);
const {
type: type,
normalized: normalized
} = normalizeProperty(element, attr);
return "attr" === type ? buildDynamicAttribute(tagName, normalized, attribute) : function (tagName, name, attribute) {
return requiresSanitization(tagName, name) ? new SafeDynamicProperty(name, attribute) : function (tagName, attribute) {
return ("INPUT" === tagName || "TEXTAREA" === tagName) && "value" === attribute;
}(tagName, name) ? new InputValueDynamicAttribute(name, attribute) : function (tagName, attribute) {
return "OPTION" === tagName && "selected" === attribute;
}(tagName, name) ? new OptionSelectedDynamicAttribute(name, attribute) : new DefaultDynamicProperty(name, attribute);
}(tagName, normalized, attribute);
}
function buildDynamicAttribute(tagName, name, attribute) {
return requiresSanitization(tagName, name) ? new SafeDynamicAttribute(attribute) : new SimpleDynamicAttribute(attribute);
}
class DynamicAttribute {
constructor(attribute) {
this.attribute = attribute;
}
}
class SimpleDynamicAttribute extends DynamicAttribute {
set(dom, value, _env) {
const normalizedValue = normalizeValue(value);
if (null !== normalizedValue) {
const {
name: name,
namespace: namespace
} = this.attribute;
dom.__setAttribute(name, normalizedValue, namespace);
}
}
update(value, _env) {
const normalizedValue = normalizeValue(value),
{
element: element,
name: name
} = this.attribute;
null === normalizedValue ? element.removeAttribute(name) : element.setAttribute(name, normalizedValue);
}
}
class DefaultDynamicProperty extends DynamicAttribute {
constructor(normalizedName, attribute) {
super(attribute), this.normalizedName = normalizedName;
}
value;
set(dom, value, _env) {
null != value && (this.value = value, dom.__setProperty(this.normalizedName, value));
}
update(value, _env) {
const {
element: element
} = this.attribute;
this.value !== value && (element[this.normalizedName] = this.value = value, null == value && this.removeAttribute());
}
removeAttribute() {
// TODO this sucks but to preserve properties first and to meet current
// semantics we must do this.
const {
element: element,
namespace: namespace
} = this.attribute;
namespace ? element.removeAttributeNS(namespace, this.normalizedName) : element.removeAttribute(this.normalizedName);
}
}
class SafeDynamicProperty extends DefaultDynamicProperty {
set(dom, value, env) {
const {
element: element,
name: name
} = this.attribute,
sanitized = sanitizeAttributeValue(element, name, value);
super.set(dom, sanitized, env);
}
update(value, env) {
const {
element: element,
name: name
} = this.attribute,
sanitized = sanitizeAttributeValue(element, name, value);
super.update(sanitized, env);
}
}
class SafeDynamicAttribute extends SimpleDynamicAttribute {
set(dom, value, env) {
const {
element: element,
name: name
} = this.attribute,
sanitized = sanitizeAttributeValue(element, name, value);
super.set(dom, sanitized, env);
}
update(value, env) {
const {
element: element,
name: name
} = this.attribute,
sanitized = sanitizeAttributeValue(element, name, value);
super.update(sanitized, env);
}
}
class InputValueDynamicAttribute extends DefaultDynamicProperty {
set(dom, value) {
dom.__setProperty("value", normalizeStringValue(value));
}
update(value) {
const input = castToBrowser(this.attribute.element, ["input", "textarea"]),
currentValue = input.value,
normalizedValue = normalizeStringValue(value);
currentValue !== normalizedValue && (input.value = normalizedValue);
}
}
class OptionSelectedDynamicAttribute extends DefaultDynamicProperty {
set(dom, value) {
null != value && !1 !== value && dom.__setProperty("selected", !0);
}
update(value) {
castToBrowser(this.attribute.element, "option").selected = !!value;
}
}
function normalizeValue(value) {
return !1 === value || null == value || void 0 === value.toString ? null : !0 === value ? "" :
// onclick function etc in SSR
"function" == typeof value ? null : String(value);
}
isDevelopingApp() && (DebugStyleAttributeManager = class extends SimpleDynamicAttribute {
set(dom, value, env) {
warnIfStyleNotTrusted(value), super.set(dom, value, env);
}
update(value, env) {
warnIfStyleNotTrusted(value), super.update(value, env);
}
});
class First {
constructor(node) {
this.node = node;
}
firstNode() {
return this.node;
}
}
class Last {
constructor(node) {
this.node = node;
}
lastNode() {
return this.node;
}
}
const CURSOR_STACK = Symbol("CURSOR_STACK");
class NewElementBuilder {
dom;
updateOperations;
constructing = null;
operations = null;
env;
[CURSOR_STACK] = new StackImpl();
modifierStack = new StackImpl();
blockStack = new StackImpl();
static forInitialRender(env, cursor) {
return new this(env, cursor.element, cursor.nextSibling).initialize();
}
static resume(env, block) {
let stack = new this(env, block.parentElement(), block.reset(env)).initialize();
return stack.pushLiveBlock(block), stack;
}
constructor(env, parentNode, nextSibling) {
this.pushElement(parentNode, nextSibling), this.env = env, this.dom = env.getAppendOperations(), this.updateOperations = env.getDOM();
}
initialize() {
return this.pushSimpleBlock(), this;
}
debugBlocks() {
return this.blockStack.toArray();
}
get element() {
return this[CURSOR_STACK].current.element;
}
get nextSibling() {
return this[CURSOR_STACK].current.nextSibling;
}
get hasBlocks() {
return this.blockStack.size > 0;
}
block() {
return expect(this.blockStack.current, "Expected a current live block");
}
popElement() {
this[CURSOR_STACK].pop(), expect(this[CURSOR_STACK].current, "can't pop past the last element");
}
pushSimpleBlock() {
return this.pushLiveBlock(new SimpleLiveBlock(this.element));
}
pushUpdatableBlock() {
return this.pushLiveBlock(new UpdatableBlockImpl(this.element));
}
pushBlockList(list) {
return this.pushLiveBlock(new LiveBlockList(this.element, list));
}
pushLiveBlock(block, isRemote = !1) {
let current = this.blockStack.current;
return null !== current && (isRemote || current.didAppendBounds(block)), this.__openBlock(), this.blockStack.push(block), block;
}
popBlock() {
return this.block().finalize(this), this.__closeBlock(), expect(this.blockStack.pop(), "Expected popBlock to return a block");
}
__openBlock() {}
__closeBlock() {}
// todo return seems unused
openElement(tag) {
let element = this.__openElement(tag);
return this.constructing = element, element;
}
__openElement(tag) {
return this.dom.createElement(tag, this.element);
}
flushElement(modifiers) {
let parent = this.element,
element = expect(this.constructing, "flushElement should only be called when constructing an element");
this.__flushElement(parent, element), this.constructing = null, this.operations = null, this.pushModifiers(modifiers), this.pushElement(element, null), this.didOpenElement(element);
}
__flushElement(parent, constructing) {
this.dom.insertBefore(parent, constructing, this.nextSibling);
}
closeElement() {
return this.willCloseElement(), this.popElement(), this.popModifiers();
}
pushRemoteElement(element, guid, insertBefore) {
return this.__pushRemoteElement(element, guid, insertBefore);
}
__pushRemoteElement(element, _guid, insertBefore) {
if (this.pushElement(element, insertBefore), void 0 === insertBefore) for (; element.lastChild;) element.removeChild(element.lastChild);
let block = new RemoteLiveBlock(element);
return this.pushLiveBlock(block, !0);
}
popRemoteElement() {
const block = this.popBlock();
return debugAssert(block instanceof RemoteLiveBlock, "[BUG] expecting a RemoteLiveBlock"), this.popElement(), block;
}
pushElement(element, nextSibling = null) {
this[CURSOR_STACK].push(new CursorImpl(element, nextSibling));
}
pushModifiers(modifiers) {
this.modifierStack.push(modifiers);
}
popModifiers() {
return this.modifierStack.pop();
}
didAppendBounds(bounds) {
return this.block().didAppendBounds(bounds), bounds;
}
didAppendNode(node) {
return this.block().didAppendNode(node), node;
}
didOpenElement(element) {
return this.block().openElement(element), element;
}
willCloseElement() {
this.block().closeElement();
}
appendText(string) {
return this.didAppendNode(this.__appendText(string));
}
__appendText(text) {
let {
dom: dom,
element: element,
nextSibling: nextSibling
} = this,
node = dom.createTextNode(text);
return dom.insertBefore(element, node, nextSibling), node;
}
__appendNode(node) {
return this.dom.insertBefore(this.element, node, this.nextSibling), node;
}
__appendFragment(fragment) {
let first = fragment.firstChild;
if (first) {
let ret = new ConcreteBounds(this.element, first, fragment.lastChild);
return this.dom.insertBefore(this.element, fragment, this.nextSibling), ret;
}
{
const comment = this.__appendComment("");
return new ConcreteBounds(this.element, comment, comment);
}
}
__appendHTML(html) {
return this.dom.insertHTMLBefore(this.element, this.nextSibling, html);
}
appendDynamicHTML(value) {
let bounds = this.trustedContent(value);
this.didAppendBounds(bounds);
}
appendDynamicText(value) {
let node = this.untrustedContent(value);
return this.didAppendNode(node), node;
}
appendDynamicFragment(value) {
let bounds = this.__appendFragment(value);
this.didAppendBounds(bounds);
}
appendDynamicNode(value) {
let node = this.__appendNode(value),
bounds = new ConcreteBounds(this.element, node, node);
this.didAppendBounds(bounds);
}
trustedContent(value) {
return this.__appendHTML(value);
}
untrustedContent(value) {
return this.__appendText(value);
}
appendComment(string) {
return this.didAppendNode(this.__appendComment(string));
}
__appendComment(string) {
let {
dom: dom,
element: element,
nextSibling: nextSibling
} = this,
node = dom.createComment(string);
return dom.insertBefore(element, node, nextSibling), node;
}
__setAttribute(name, value, namespace) {
this.dom.setAttribute(this.constructing, name, value, namespace);
}
__setProperty(name, value) {
this.constructing[name] = value;
}
setStaticAttribute(name, value, namespace) {
this.__setAttribute(name, value, namespace);
}
setDynamicAttribute(name, value, trusting, namespace) {
let attribute = dynamicAttribute(this.constructing, name, namespace, trusting);
return attribute.set(this, value, this.env), attribute;
}
}
class SimpleLiveBlock {
first = null;
last = null;
nesting = 0;
constructor(parent) {
this.parent = parent;
}
parentElement() {
return this.parent;
}
firstNode() {
return expect(this.first, "cannot call `firstNode()` while `SimpleLiveBlock` is still initializing").firstNode();
}
lastNode() {
return expect(this.last, "cannot call `lastNode()` while `SimpleLiveBlock` is still initializing").lastNode();
}
openElement(element) {
this.didAppendNode(element), this.nesting++;
}
closeElement() {
this.nesting--;
}
didAppendNode(node) {
0 === this.nesting && (this.first || (this.first = new First(node)), this.last = new Last(node));
}
didAppendBounds(bounds) {
0 === this.nesting && (this.first || (this.first = bounds), this.last = bounds);
}
finalize(stack) {
null === this.first && stack.appendComment("");
}
}
class RemoteLiveBlock extends SimpleLiveBlock {
constructor(parent) {
super(parent), registerDestructor(this, () => {
// In general, you only need to clear the root of a hierarchy, and should never
// need to clear any child nodes. This is an important constraint that gives us
// a strong guarantee that clearing a subtree is a single DOM operation.
// Because remote blocks are not normally physically nested inside of the tree
// that they are logically nested inside, we manually clear remote blocks when
// a logical parent is cleared.
// HOWEVER, it is currently possible for a remote block to be physically nested
// inside of the block it is logically contained inside of. This happens when
// the remote block is appended to the end of the application's entire element.
// The problem with that scenario is that Glimmer believes that it owns more of
// the DOM than it actually does. The code is attempting to write past the end
// of the Glimmer-managed root, but Glimmer isn't aware of that.
// The correct solution to that problem is for Glimmer to be aware of the end
// of the bounds that it owns, and once we make that change, this check could
// be removed.
// For now, a more targeted fix is to check whether the node was already removed
// and avoid clearing the node if it was. In most cases this shouldn't happen,
// so this might hide bugs where the code clears nested nodes unnecessarily,
// so we should eventually try to do the correct fix.
this.parentElement() === this.firstNode().parentNode && clear(this);
});
}
}
class UpdatableBlockImpl extends SimpleLiveBlock {
reset() {
destroy(this);
let nextSibling = clear(this);
return this.first = null, this.last = null, this.nesting = 0, nextSibling;
}
}
// FIXME: All the noops in here indicate a modelling problem
class LiveBlockList {
constructor(parent, boundList) {
this.parent = parent, this.boundList = boundList, this.parent = parent, this.boundList = boundList;
}
parentElement() {
return this.parent;
}
firstNode() {
return expect(this.boundList[0], "cannot call `firstNode()` while `LiveBlockList` is still initializing").firstNode();
}
lastNode() {
let boundList = this.boundList;
return expect(boundList[boundList.length - 1], "cannot call `lastNode()` while `LiveBlockList` is still initializing").lastNode();
}
openElement(_element) {
debugAssert(!1, "Cannot openElement directly inside a block list");
}
closeElement() {
debugAssert(!1, "Cannot closeElement directly inside a block list");
}
didAppendNode(_node) {
debugAssert(!1, "Cannot create a new node directly inside a block list");
}
didAppendBounds(_bounds) {}
finalize(_stack) {
debugAssert(this.boundList.length > 0, "boundsList cannot be empty");
}
}
function clientBuilder(env, cursor) {
return NewElementBuilder.forInitialRender(env, cursor);
}
const APPEND_OPCODES = new class {
evaluateOpcode = new Array(Op.Size).fill(null);
add(name, evaluate, kind = "syscall") {
this.evaluateOpcode[name] = {
syscall: "machine" !== kind,
evaluate: evaluate
};
}
debugBefore(vm, opcode) {
return {
sp: void 0,
pc: vm.fetchValue($pc),
name: void 0,
params: void 0,
type: opcode.type,
isMachine: opcode.isMachine,
size: opcode.size,
state: void 0
};
}
debugAfter(vm, pre) {}
evaluate(vm, opcode, type) {
let operation = unwrap(this.evaluateOpcode[type]);
operation.syscall ? (debugAssert(!opcode.isMachine, `BUG: Mismatch between operation.syscall (${operation.syscall}) and opcode.isMachine (${opcode.isMachine}) for ${opcode.type}`), operation.evaluate(vm, opcode)) : (debugAssert(opcode.isMachine, `BUG: Mismatch between operation.syscall (${operation.syscall}) and opcode.isMachine (${opcode.isMachine}) for ${opcode.type}`), operation.evaluate(vm[INNER_VM], 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 {
[TYPE];
[INNER];
[OWNER];
[ARGS];
[RESOLVED];
/** @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;
// eslint-disable-next-line no-constant-condition
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);
}
/** @internal */
function hasCustomDebugRenderTreeLifecycle(manager) {
return "getDebugCustomRenderTree" in manager;
}
APPEND_OPCODES.add(Op.ChildScope, vm => vm.pushChildScope()), APPEND_OPCODES.add(Op.PopScope, vm => vm.popScope()), APPEND_OPCODES.add(Op.PushDynamicScope, vm => vm.pushDynamicScope()), APPEND_OPCODES.add(Op.PopDynamicScope, vm => vm.popDynamicScope()), APPEND_OPCODES.add(Op.Constant, (vm, {
op1: other
}) => {
vm.stack.push(vm[CONSTANTS].getValue(decodeHandle(other)));
}), APPEND_OPCODES.add(Op.ConstantReference, (vm, {
op1: other
}) => {
vm.stack.push(createConstRef(vm[CONSTANTS].getValue(decodeHandle(other)), !1));
}), APPEND_OPCODES.add(Op.Primitive, (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(Op.PrimitiveReference, 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(Op.Dup, (vm, {
op1: register,
op2: offset
}) => {
let position = vm.fetchValue(register) - offset;
vm.stack.dup(position);
}), APPEND_OPCODES.add(Op.Pop, (vm, {
op1: count
}) => {
vm.stack.pop(count);
}), APPEND_OPCODES.add(Op.Load, (vm, {
op1: register
}) => {
vm.load(register);
}), APPEND_OPCODES.add(Op.Fetch, (vm, {
op1: register
}) => {
vm.fetch(register);
}), APPEND_OPCODES.add(Op.BindDynamicScope, (vm, {
op1: _names
}) => {
let names = vm[CONSTANTS].getArray(_names);
vm.bindDynamicScope(names);
}), APPEND_OPCODES.add(Op.Enter, (vm, {
op1: args
}) => {
vm.enter(args);
}), APPEND_OPCODES.add(Op.Exit, vm => {
vm.exit();
}), APPEND_OPCODES.add(Op.PushSymbolTable, (vm, {
op1: _table
}) => {
vm.stack.push(vm[CONSTANTS].getValue(_table));
}), APPEND_OPCODES.add(Op.PushBlockScope, vm => {
vm.stack.push(vm.scope());
}), APPEND_OPCODES.add(Op.CompileBlock, vm => {
let stack = vm.stack,
block = stack.pop();
block ? stack.push(vm.compile(block)) : stack.push(null);
}), APPEND_OPCODES.add(Op.InvokeYield, vm => {
let {
stack: stack
} = vm,
handle = stack.pop(),
scope = stack.pop(),
table = stack.pop();
debugAssert(null === table || table && "object" == typeof table && Array.isArray(table.parameters), `Expected top of stack to be Option<BlockSymbolTable>, was ${String(table)}`);
let args = stack.pop();
if (null === table)
// To balance the pop{Frame,Scope}
return vm.pushFrame(), void vm.pushScope(scope ?? vm.scope());
let invokingScope = expect(scope, "BUG: expected 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(unwrap(locals[i]), args.at(i));
}
}
vm.pushFrame(), vm.pushScope(invokingScope), vm.call(handle);
}), APPEND_OPCODES.add(Op.JumpIf, (vm, {
op1: target
}) => {
let reference = vm.stack.pop(),
value = Boolean(valueForRef(reference));
isConstRef(reference) ? !0 === value && vm.goto(target) : (!0 === value && vm.goto(target), vm.updateWith(new Assert(reference)));
}), APPEND_OPCODES.add(Op.JumpUnless, (vm, {
op1: target
}) => {
let reference = vm.stack.pop(),
value = Boolean(valueForRef(reference));
isConstRef(reference) ? !1 === value && vm.goto(target) : (!1 === value && vm.goto(target), vm.updateWith(new Assert(reference)));
}), APPEND_OPCODES.add(Op.JumpEq, (vm, {
op1: target,
op2: comparison
}) => {
vm.stack.peek() === comparison && vm.goto(target);
}), APPEND_OPCODES.add(Op.AssertSame, vm => {
let reference = vm.stack.peek();
!1 === isConstRef(reference) && vm.updateWith(new Assert(reference));
}), APPEND_OPCODES.add(Op.ToBoolean, vm => {
let {
stack: stack
} = vm,
valueRef = stack.pop();
stack.push(createComputeRef(() => toBool(valueForRef(valueRef))));
});
class Assert {
last;
constructor(ref) {
this.ref = ref, this.last = valueForRef(ref);
}
evaluate(vm) {
let {
last: last,
ref: ref
} = this;
last !== valueForRef(ref) && vm.throw();
}
}
class AssertFilter {
last;
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 {
tag = CONSTANT_TAG;
lastRevision = INITIAL;
target;
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(expect(target, "VM BUG: Target must be set before attempting to jump")));
}
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(Op.Text, (vm, {
op1: text
}) => {
vm.elements().appendText(vm[CONSTANTS].getValue(text));
}), APPEND_OPCODES.add(Op.Comment, (vm, {
op1: text
}) => {
vm.elements().appendComment(vm[CONSTANTS].getValue(text));
}), APPEND_OPCODES.add(Op.OpenElement, (vm, {
op1: tag
}) => {
vm.elements().openElement(vm[CONSTANTS].getValue(tag));
}), APPEND_OPCODES.add(Op.OpenDynamicElement, vm => {
let tagName = valueForRef(vm.stack.pop());
vm.elements().openElement(tagName);
}), APPEND_OPCODES.add(Op.PushRemoteElement, 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.elements().pushRemoteElement(element, guid, insertBefore);
if (block && 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(Op.PopRemoteElement, vm => {
let bounds = vm.elements().popRemoteElement();
void 0 !== vm.env.debugRenderTree &&
// The RemoteLiveBlock is also its bounds
vm.env.debugRenderTree.didRender(bounds, bounds);
}), APPEND_OPCODES.add(Op.FlushElement, vm => {
let operations = vm.fetchValue($t0),
modifiers = null;
operations && (modifiers = operations.flush(vm), vm.loadValue($t0, null)), vm.elements().flushElement(modifiers);
}), APPEND_OPCODES.add(Op.CloseElement, vm => {
let modifiers = vm.elements().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(Op.Modifier, (vm, {
op1: handle
}) => {
if (!1 === vm.env.isInteractive) return;
let owner = vm.getOwner(),
args = vm.stack.pop(),
definition = vm[CONSTANTS].getValue(handle),
{
manager: manager
} = definition,
{
constructing: constructing
} = vm.elements(),
capturedArgs = args.capture(),
state = manager.create(owner, expect(constructing, "BUG: ElementModifier could not find the element it applies to"), definition.state, capturedArgs),
instance = {
manager: manager,
state: state,
definition: definition
};
expect(vm.fetchValue($t0), "BUG: ElementModifier could not find operations to append to").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(Op.DynamicModifier, vm => {
if (!1 === vm.env.isInteractive) return;
let {
stack: stack
} = vm,
ref = stack.pop(),
args = stack.pop().capture(),
{
positional: outerPositional,
named: outerNamed
} = args,
{
constructing: constructing
} = vm.elements(),
initialOwner = vm.getOwner(),
instanceRef = createComputeRef(() => {
let owner,
hostDefinition,
value = valueForRef(ref);
if (!isObject(value)) return;
if (isCurriedType(value, CurriedTypes.Modifier)) {
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 && (args.named = Object.assign({}, ...named, outerNamed));
} else hostDefinition = value, owner = initialOwner;
let manager = getInternalModifierManager(hostDefinition, !0);
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, expect(constructing, "BUG: ElementModifier could not find the element it applies to"), definition.state, args);
return {
manager: manager,
state: state,
definition: definition
};
}),
instance = valueForRef(instanceRef),
tag = null;
return void 0 !== instance && (expect(vm.fetchValue($t0), "BUG: ElementModifier could not find operations to append to").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 {
lastUpdated;
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 {
lastUpdated;
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) || (vm.env.scheduleUpdateModifier(instance), this.lastUpdated = valueForTag(tag));
null !== tag && consumeTag(tag);
}
}
APPEND_OPCODES.add(Op.StaticAttr, (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.elements().setStaticAttribute(name, value, namespace);
}), APPEND_OPCODES.add(Op.DynamicAttr, (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.elements().setDynamicAttribute(name, value, trusting, namespace);
isConstRef(reference) || vm.updateWith(new UpdateDynamicAttributeOpcode(reference, attribute, vm.env));
});
class UpdateDynamicAttributeOpcode {
updateRef;
constructor(reference, attribute, env) {
let initialized = !1;
this.updateRef = createComputeRef(() => {
let value = valueForRef(reference);
!0 === initialized ? attribute.update(value, env) : initialized = !0;
}), 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(Op.PushComponentDefinition, (vm, {
op1: handle
}) => {
let definition = vm[CONSTANTS].getValue(handle);
debugAssert(!!definition, `Missing component for ${handle}`);
let {
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(Op.ResolveDynamicComponent, (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, expect(owner, "BUG: expected owner when looking up component"));
if (isDevelopingApp() && !definition) throw new Error(`Attempted to resolve \`${name}\`, which was expected to be a component, but nothing was found.`);
return constants.resolvedComponent(definition, name);
}(vm.runtime.resolver, constants, component, owner);
definition = expect(resolvedDefinition, `Could not find a component named "${component}"`);
} else definition = isCurriedValue(component) ? component : constants.component(component, owner);
stack.push(definition);
}), APPEND_OPCODES.add(Op.ResolveCurriedComponent, 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(), !0), 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)}`);
stack.push(definition);
}), APPEND_OPCODES.add(Op.PushDynamicComponentInstance, 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(Op.PushArgs, (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$1].setup(stack, names, blockNames, positionalCount, !!atNames), stack.push(vm[ARGS$1]);
}), APPEND_OPCODES.add(Op.PushEmptyArgs, vm => {
let {
stack: stack
} = vm;
stack.push(vm[ARGS$1].empty(stack));
}), APPEND_OPCODES.add(Op.CaptureArgs, vm => {
let stack = vm.stack,
capturedArgs = stack.pop().capture();
stack.push(capturedArgs);
}), APPEND_OPCODES.add(Op.PrepareArgs, (vm, {
op1: _state
}) => {
let stack = vm.stack,
instance = vm.fetchValue(_state),
args = stack.pop(),
{
definition: definition
} = instance;
if (isCurriedType(definition, CurriedTypes.Component)) {
debugAssert(!definition.manager, "If the component definition was curried, we don't yet have a manager");
let constants = vm[CONSTANTS],
{
definition: resolvedDefinition,
owner: owner,
resolved: resolved,
positional: positional,
named: named
} = resolveCurriedValue(definition);
if (!0 === resolved) definition = resolvedDefinition;else if ("string" == typeof resolvedDefinition) {
let resolvedValue = vm.runtime.resolver.lookupComponent(resolvedDefinition, owner);
definition = constants.resolvedComponent(expect(resolvedValue, "BUG: expected resolved component"), resolvedDefinition);
} else definition = constants.component(resolvedDefinition, owner);
void 0 !== named && args.named.merge(assign({}, ...named)), void 0 !== positional && (args.realloc(positional.length), args.positional.prepend(positional));
let {
manager: manager
} = definition;
debugAssert(null === instance.manager, "component instance manager should not be populated yet"), debugAssert(null === instance.capabilities, "component instance manager should not be populated yet"), 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[unwrap(names[i])]);
args.setup(stack, names, blockNames, positionalCount, !1);
}
stack.push(args);
}), APPEND_OPCODES.add(Op.CreateComponent, (vm, {
op1: flags,
op2: _state
}) => {
let instance = vm.fetchValue(_state),
{
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(Op.RegisterComponentDestructor, (vm, {
op1: _state
}) => {
let {
manager: manager,
state: state,
capabilities: capabilities
} = vm.fetchValue(_state),
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(Op.BeginComponentTransaction, (vm, {
op1: _state
}) => {
let name;
if (isDevelopingApp()) {
let {
definition: definition,
manager: manager
} = vm.fetchValue(_state);
name = definition.resolvedName ?? manager.getDebugName(definition.state);
}
vm.beginCacheGroup(name), vm.elements().pushSimpleBlock();
}), APPEND_OPCODES.add(Op.PutComponentOperations, vm => {
vm.loadValue($t0, new ComponentElementOperations());
}), APPEND_OPCODES.add(Op.ComponentAttr, (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(Op.StaticComponentAttr, (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 {
attributes = dict();
classes = [];
modifiers = [];
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.elements(),
name = manager.getDebugName(definition.state),
instance = manager.getDebugInstance(state);
debugAssert(constructing, "Expected a constructing element in addModifier");
let 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 = unwrap(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;
}
}
function mergeClasses(classes) {
return 0 === classes.length ? "" : 1 === classes.length ? unwrap(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.elements().setStaticAttribute(name, value, namespace);else {
let attribute = vm.elements().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(Op.DidCreateElement, (vm, {
op1: _state
}) => {
let {
definition: definition,
state: state
} = vm.fetchValue(_state),
{
manager: manager
} = definition,
operations = vm.fetchValue($t0);
manager.didCreateElement(state, expect(vm.elements().constructing, "Expected a constructing element in DidCreateOpcode"), operations);
}), APPEND_OPCODES.add(Op.GetComponentSelf, (vm, {
op1: _state,
op2: _names
}) => {
let instance = vm.fetchValue(_state),
{
definition: definition,
state: state
} = instance,
{
manager: manager
} = definition,
selfRef = manager.getSelf(state);
if (void 0 !== vm.env.debugRenderTree) {
let args,
moduleName,
instance = vm.fetchValue(_state),
{
definition: definition,
manager: manager
} = instance;
if (vm.stack.peek() === vm[ARGS$1]) args = vm[ARGS$1].capture();else {
let names = vm[CONSTANTS].getArray(_names);
vm[ARGS$1].setup(vm.stack, names, [], 0, !0), args = vm[ARGS$1].capture();
}
let compilable = definition.compilable;
if (null === compilable ? (debugAssert(managerHasCapability(manager, instance.capabilities, InternalComponentCapabilities.dynamicLayout), "BUG: No template was found for this component, and the component did not have the dynamic layout capability"), compilable = manager.getDynamicLayout(state, vm.runtime.resolver), moduleName = null !== compilable ? compilable.moduleName : "__default__.hbs") : moduleName = compilable.moduleName,
// For tearing down the debugRenderTree
vm.associateDestroyable(instance), hasCustomDebugRenderTreeLifecycle(manager)) manager.getDebugCustomRenderTree(instance.definition.state, instance.state, args, moduleName).forEach(node => {
let {
bucket: bucket
} = node;
vm.env.debugRenderTree.create(bucket, node), registerDestructor(instance, () => {
vm.env.debugRenderTree?.willDestroy(bucket);
}), vm.updateWith(new DebugRenderTreeUpdateOpcode(bucket));
});else {
let name = definition.resolvedName ?? manager.getDebugName(definition.state);
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(Op.GetComponentTagName, (vm, {
op1: _state
}) => {
let {
definition: definition,
state: state
} = vm.fetchValue(_state),
{
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(Op.GetComponentLayout, (vm, {
op1: _state
}) => {
let instance = vm.fetchValue(_state),
{
manager: manager,
definition: definition
} = instance,
{
stack: stack
} = vm,
{
compilable: compilable
} = definition;
if (null === compilable) {
let {
capabilities: capabilities
} = instance;
debugAssert(managerHasCapability(manager, capabilities, InternalComponentCapabilities.dynamicLayout), "BUG: No template was found for this component, and the component did not have the dynamic layout capability"), compilable = manager.getDynamicLayout(instance.state, vm.runtime.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(Op.Main, (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(Op.PopulateLayout, (vm, {
op1: _state
}) => {
let {
stack: stack
} = vm,
handle = stack.pop(),
table = stack.pop(),
state = vm.fetchValue(_state);
// In DEBUG handles could be ErrHandle objects
state.handle = handle, state.table = table;
}), APPEND_OPCODES.add(Op.VirtualRootScope, (vm, {
op1: _state
}) => {
let owner,
{
table: table,
manager: manager,
capabilities: capabilities,
state: state
} = vm.fetchValue(_state);
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(Op.SetupForEval, (vm, {
op1: _state
}) => {
let state = vm.fetchValue(_state);
if (state.table.hasEval) {
let lookup = state.lookup = dict();
vm.scope().bindEvalScope(lookup);
}
}), APPEND_OPCODES.add(Op.SetNamedVariables, (vm, {
op1: _state
}) => {
let state = vm.fetchValue(_state),
scope = vm.scope(),
args = vm.stack.peek(),
callerNames = args.named.atNames;
for (let i = callerNames.length - 1; i >= 0; i--) {
let atName = unwrap(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(Op.SetBlocks, (vm, {
op1: _state
}) => {
let state = vm.fetchValue(_state),
{
blocks: blocks
} = vm.stack.peek();
for (const [i] of enumerate(blocks.names)) bindBlock(unwrap(blocks.symbolNames[i]), unwrap(blocks.names[i]), state, blocks, vm);
}),
// Dynamic Invocation Only
APPEND_OPCODES.add(Op.InvokeComponentLayout, (vm, {
op1: _state
}) => {
let state = vm.fetchValue(_state);
vm.call(state.handle);
}), APPEND_OPCODES.add(Op.DidRenderLayout, (vm, {
op1: _state
}) => {
let instance = vm.fetchValue(_state),
{
manager: manager,
state: state,
capabilities: capabilities
} = instance,
bounds = vm.elements().popBlock();
void 0 !== vm.env.debugRenderTree && (hasCustomDebugRenderTreeLifecycle(manager) ? manager.getDebugCustomRenderTree(instance.definition.state, state, EMPTY_ARGS).reverse().forEach(node => {
let {
bucket: bucket
} = node;
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) && (manager.didRenderLayout(state, bounds), vm.env.didCreate(instance), vm.updateWith(new DidUpdateLayoutOpcode(instance, bounds)));
}), APPEND_OPCODES.add(Op.CommitComponentTransaction, 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 {
stack = null;
positional = new PositionalArgumentsImpl();
named = new NamedArgumentsImpl();
blocks = new BlockArgumentsImpl();
empty(stack) {
let base = stack[REGISTERS][$sp] + 1;
return this.named.empty(stack, base), this.positional.empty(stack, base), this.blocks.empty(stack, base), this;
}
setup(stack, names, blockNames, positionalCount, atNames) {
this.stack = stack;
/*
| ... | blocks | positional | named |
| ... | b0 b1 | p0 p1 p2 p3 | n0 n1 |
index | ... | 4/5/6 7/8/9 | 10 11 12 13 | 14 15 |
^ ^ ^ ^
bbase pbase nbase sp
*/
let named = this.named,
namedCount = names.length,
namedBase = stack[REGISTERS][$sp] - namedCount + 1;
named.setup(stack, namedBase, namedCount, names, atNames);
let positionalBase = namedBase - positionalCount;
this.positional.setup(stack, positionalBase, positionalCount);
let blocks = this.blocks,
blocksCount = blockNames.length,
blocksBase = positionalBase - 3 * blocksCount;
blocks.setup(stack, blocksBase, blocksCount, blockNames);
}
get base() {
return this.blocks.base;
}
get length() {
return this.positional.length + this.named.length + 3 * this.blocks.length;
}
at(pos) {
return this.positional.at(pos);
}
realloc(offset) {
let {
stack: stack
} = this;
if (offset > 0 && null !== stack) {
let {
positional: positional,
named: named
} = this,
newBase = positional.base + offset;
for (let i = positional.length + named.length - 1; i >= 0; i--) stack.copy(i + positional.base, i + newBase);
positional.base += offset, named.base += offset, stack[REGISTERS][$sp] += offset;
}
}
capture() {
let positional = 0 === this.positional.length ? EMPTY_POSITIONAL : this.positional.capture();
return {
named: 0 === this.named.length ? EMPTY_NAMED : this.named.capture(),
positional: positional
};
}
clear() {
let {
stack: stack,
length: length
} = this;
length > 0 && null !== stack && stack.pop(length);
}
}
const EMPTY_REFERENCES = emptyArray();
class PositionalArgumentsImpl {
base = 0;
length = 0;
stack = null;
_references = null;
empty(stack, base) {
this.stack = stack, this.base = base, this.length = 0, this._references = EMPTY_REFERENCES;
}
setup(stack, base, length) {
this.stack = stack, this.base = base, this.length = length, this._references = 0 === length ? EMPTY_REFERENCES : null;
}
at(position) {
let {
base: base,
length: length,
stack: stack
} = this;
return position < 0 || position >= length ? UNDEFINED_REFERENCE : stack.get(position, base);
}
capture() {
return this.references;
}
prepend(other) {
let additions = other.length;
if (additions > 0) {
let {
base: base,
length: length,
stack: stack
} = this;
this.base = base -= additions, this.length = length + additions;
for (let i = 0; i < additions; i++) stack.set(other[i], i, base);
this._references = null;
}
}
get references() {
let references = this._references;
if (!references) {
let {
stack: stack,
base: base,
length: length
} = this;
references = this._references = stack.slice(base, base + length);
}
return references;
}
}
class NamedArgumentsImpl {
base = 0;
length = 0;
_references = null;
_names = EMPTY_STRING_ARRAY;
_atNames = EMPTY_STRING_ARRAY;
empty(stack, base) {
this.stack = stack, this.base = base, this.length = 0, this._references = EMPTY_REFERENCES, this._names = EMPTY_STRING_ARRAY, this._atNames = EMPTY_STRING_ARRAY;
}
setup(stack, base, length, names, atNames) {
this.stack = stack, this.base = base, this.length = length, 0 === length ? (this._references = EMPTY_REFERENCES, this._names = EMPTY_STRING_ARRAY, this._atNames = EMPTY_STRING_ARRAY) : (this._references = null, atNames ? (this._names = null, this._atNames = names) : (this._names = names, this._atNames = null));
}
get names() {
let names = this._names;
return names || (names = this._names = this._atNames.map(this.toSyntheticName)), names;
}
get atNames() {
let atNames = this._atNames;
return atNames || (atNames = this._atNames = this._names.map(this.toAtName)), atNames;
}
has(name) {
return -1 !== this.names.indexOf(name);
}
get(name, atNames = !1) {
let {
base: base,
stack: stack
} = this,
idx = (atNames ? this.atNames : this.names).indexOf(name);
if (-1 === idx) return UNDEFINED_REFERENCE;
let ref = stack.get(idx, base);
return isDevelopingApp() ? createDebugAliasRef(atNames ? name : `@${name}`, ref) : ref;
}
capture() {
let {
names: names,
references: references
} = this,
map = dict();
for (const [i, name] of enumerate(names)) map[name] = isDevelopingApp() ? createDebugAliasRef(`@${name}`, unwrap(references[i])) : unwrap(references[i]);
return map;
}
merge(other) {
let keys = Object.keys(other);
if (keys.length > 0) {
let {
names: names,
length: length,
stack: stack
} = this,
newNames = names.slice();
for (const name of keys) -1 === newNames.indexOf(name) && (length = newNames.push(name), stack.push(other[name]));
this.length = length, this._references = null, this._names = newNames, this._atNames = null;
}
}
get references() {
let references = this._references;
if (!references) {
let {
base: base,
length: length,
stack: stack
} = this;
references = this._references = stack.slice(base, base + length);
}
return references;
}
toSyntheticName(name) {
return name.slice(1);
}
toAtName(name) {
return `@${name}`;
}
}
function toSymbolName(name) {
return `&${name}`;
}
const EMPTY_BLOCK_VALUES = emptyArray();
class BlockArgumentsImpl {
internalValues = null;
_symbolNames = null;
internalTag = null;
names = EMPTY_STRING_ARRAY;
length = 0;
base = 0;
empty(stack, base) {
this.stack = stack, this.names = EMPTY_STRING_ARRAY, this.base = base, this.length = 0, this._symbolNames = null, this.internalTag = CONSTANT_TAG, this.internalValues = EMPTY_BLOCK_VALUES;
}
setup(stack, base, length, names) {
this.stack = stack, this.names = names, this.base = base, this.length = length, this._symbolNames = null, 0 === length ? (this.internalTag = CONSTANT_TAG, this.internalValues = EMPTY_BLOCK_VALUES) : (this.internalTag = null, this.internalValues = null);
}
get values() {
let values = this.internalValues;
if (!values) {
let {
base: base,
length: length,
stack: stack
} = this;
values = this.internalValues = stack.slice(base, base + 3 * length);
}
return values;
}
has(name) {
return -1 !== this.names.indexOf(name);
}
get(name) {
let idx = this.names.indexOf(name);
if (-1 === idx) return null;
let {
base: base,
stack: stack
} = this,
table = stack.get(3 * idx, base),
scope = stack.get(3 * idx + 1, base),
handle = stack.get(3 * idx + 2, base);
return null === handle ? null : [handle, scope, table];
}
capture() {
return new CapturedBlockArgumentsImpl(this.names, this.values);
}
get symbolNames() {
let symbolNames = this._symbolNames;
return null === symbolNames && (symbolNames = this._symbolNames = this.names.map(toSymbolName)), symbolNames;
}
}
class CapturedBlockArgumentsImpl {
length;
constructor(names, values) {
this.names = names, this.values = values, this.length = names.length;
}
has(name) {
return -1 !== this.names.indexOf(name);
}
get(name) {
let idx = this.names.indexOf(name);
return -1 === idx ? null : [this.values[3 * idx + 2], this.values[3 * idx + 1], this.values[3 * idx]];
}
}
function createCapturedArgs(named, positional) {
return {
named: named,
positional: positional
};
}
function reifyNamed(named) {
let reified = dict();
for (const [key, value] of Object.entries(named)) reified[key] = valueForRef(value);
return reified;
}
function reifyPositional(positional) {
return positional.map(valueForRef);
}
function reifyArgs(args) {
return {
named: reifyNamed(args.named),
positional: reifyPositional(args.positional)
};
}
const ARGUMENT_ERROR = Symbol("ARGUMENT_ERROR");
function isArgumentError(arg) {
return null !== arg && "object" == typeof arg && arg[ARGUMENT_ERROR];
}
function ArgumentErrorImpl(error) {
return {
[ARGUMENT_ERROR]: !0,
error: error
};
}
function reifyArgsDebug(args) {
return {
named: function (named) {
let reified = dict();
for (const [key, value] of Object.entries(named)) try {
reified[key] = valueForRef(value);
} catch (e) {
reified[key] = ArgumentErrorImpl(e);
}
return reified;
}(args.named),
positional: (positional = args.positional, positional.map(p => {
try {
return valueForRef(p);
} catch (e) {
return ArgumentErrorImpl(e);
}
}))
};
var positional;
}
const EMPTY_NAMED = Object.freeze(Object.create(null)),
EMPTY_POSITIONAL = EMPTY_REFERENCES,
EMPTY_ARGS = createCapturedArgs(EMPTY_NAMED, EMPTY_POSITIONAL);
function castToString(value) {
return "string" == typeof value ? value : "function" != typeof value.toString ? "" : String(value);
}
function resolveHelper(definition, ref) {
let helper,
managerOrHelper = getInternalHelperManager(definition, !0);
if (null === managerOrHelper ? helper = null : (helper = "function" == typeof managerOrHelper ? managerOrHelper : managerOrHelper.getHelper(definition), debugAssert(managerOrHelper, "BUG: expected manager or helper")), isDevelopingApp() && null === helper) throw new Error(`Expected a dynamic helper definition, but received an object or function that did not have a helper 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(definition)}`);
return helper;
}
function isUndefinedReference(input) {
return debugAssert(Array.isArray(input) || input === UNDEFINED_REFERENCE, "a reference other than UNDEFINED_REFERENCE is illegal here"), input === UNDEFINED_REFERENCE;
}
APPEND_OPCODES.add(Op.Curry, (vm, {
op1: type,
op2: _isStrict
}) => {
let stack = vm.stack,
definition = stack.pop(),
capturedArgs = stack.pop(),
owner = vm.getOwner(),
resolver = vm.runtime.resolver,
isStrict = !1;
isDevelopingApp() && (
// strict check only happens in DEBUG builds, no reason to load it otherwise
isStrict = vm[CONSTANTS].getValue(decodeHandle(_isStrict))), vm.loadValue($v0, function (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 === CurriedTypes.Component && "string" == typeof value && value) {
// Only components should enter this path, as helpers and modifiers do not
// support string based resolution
if (isDevelopingApp()) {
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.`);
if (!expect(resolver, "BUG: expected resolver for curried component definitions").lookupComponent(value, owner)) 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 curriedDefinition = isObject(value) ? curry(type, value, owner, args) : null;
return lastValue = value, curriedDefinition;
});
}(type, definition, owner, capturedArgs, resolver, isStrict));
}), APPEND_OPCODES.add(Op.DynamicHelper, vm => {
let helperRef,
stack = vm.stack,
ref = stack.pop(),
args = stack.pop().capture(),
initialOwner = vm.getOwner(),
helperInstanceRef = createComputeRef(() => {
void 0 !== helperRef && destroy(helperRef);
let definition = valueForRef(ref);
if (isCurriedType(definition, CurriedTypes.Helper)) {
let {
definition: resolvedDef,
owner: owner,
positional: positional,
named: named
} = resolveCurriedValue(definition),
helper = resolveHelper(resolvedDef, ref);
void 0 !== named && (args.named = assign({}, ...named, args.named)), void 0 !== positional && (args.positional = positional.concat(args.positional)), helperRef = helper(args, owner), associateDestroyableChild(helperInstanceRef, helperRef);
} else if (isObject(definition)) {
let helper = resolveHelper(definition, ref);
helperRef = helper(args, initialOwner), _hasDestroyableChildren(helperRef) && associateDestroyableChild(helperInstanceRef, helperRef);
} else helperRef = UNDEFINED_REFERENCE;
}),
helperValueRef = createComputeRef(() => (valueForRef(helperInstanceRef), valueForRef(helperRef)));
vm.associateDestroyable(helperInstanceRef), vm.loadValue($v0, helperValueRef);
}), APPEND_OPCODES.add(Op.Helper, (vm, {
op1: handle
}) => {
let stack = vm.stack,
value = vm[CONSTANTS].getValue(handle)(stack.pop().capture(), vm.getOwner(), vm.dynamicScope());
_hasDestroyableChildren(value) && vm.associateDestroyable(value), vm.loadValue($v0, value);
}), APPEND_OPCODES.add(Op.GetVariable, (vm, {
op1: symbol
}) => {
let expr = vm.referenceForSymbol(symbol);
vm.stack.push(expr);
}), APPEND_OPCODES.add(Op.SetVariable, (vm, {
op1: symbol
}) => {
let expr = vm.stack.pop();
vm.scope().bindSymbol(symbol, expr);
}), APPEND_OPCODES.add(Op.SetBlock, (vm, {
op1: symbol
}) => {
let handle = vm.stack.pop(),
scope = vm.stack.pop(),
table = vm.stack.pop();
vm.scope().bindBlock(symbol, [handle, scope, table]);
}), APPEND_OPCODES.add(Op.ResolveMaybeLocal, (vm, {
op1: _name
}) => {
let name = vm[CONSTANTS].getValue(_name),
ref = vm.scope().getPartialMap()[name];
void 0 === ref && (ref = childRefFor(vm.getSelf(), name)), vm.stack.push(ref);
}), APPEND_OPCODES.add(Op.RootScope, (vm, {
op1: symbols
}) => {
vm.pushRootScope(symbols, vm.getOwner());
}), APPEND_OPCODES.add(Op.GetProperty, (vm, {
op1: _key
}) => {
let key = vm[CONSTANTS].getValue(_key),
expr = vm.stack.pop();
vm.stack.push(childRefFor(expr, key));
}), APPEND_OPCODES.add(Op.GetBlock, (vm, {
op1: _block
}) => {
let {
stack: stack
} = vm,
block = vm.scope().getBlock(_block);
stack.push(block);
}), APPEND_OPCODES.add(Op.SpreadBlock, vm => {
let {
stack: stack
} = vm,
block = stack.pop();
if (block && !isUndefinedReference(block)) {
let [handleOrCompilable, scope, table] = block;
stack.push(table), stack.push(scope), stack.push(handleOrCompilable);
} else stack.push(null), stack.push(null), stack.push(null);
}), APPEND_OPCODES.add(Op.HasBlock, vm => {
let {
stack: stack
} = vm,
block = stack.pop();
block && !isUndefinedReference(block) ? stack.push(TRUE_REFERENCE) : stack.push(FALSE_REFERENCE);
}), APPEND_OPCODES.add(Op.HasBlockParams, vm => {
// FIXME(mmun): should only need to push the symbol table
vm.stack.pop(), vm.stack.pop();
let table = vm.stack.pop(),
hasBlockParams = table && table.parameters.length;
vm.stack.push(hasBlockParams ? TRUE_REFERENCE : FALSE_REFERENCE);
}), APPEND_OPCODES.add(Op.Concat, (vm, {
op1: count
}) => {
let out = new Array(count);
for (let i = count; i > 0; i--) out[i - 1] = vm.stack.pop();
var partsRefs;
vm.stack.push((partsRefs = out, createComputeRef(() => {
const parts = [];
for (const ref of partsRefs) {
const value = valueForRef(ref);
null != value && parts.push(castToString(value));
}
return parts.length > 0 ? parts.join("") : null;
})));
}), APPEND_OPCODES.add(Op.IfInline, vm => {
let condition = vm.stack.pop(),
truthy = vm.stack.pop(),
falsy = vm.stack.pop();
vm.stack.push(createComputeRef(() => !0 === toBool(valueForRef(condition)) ? valueForRef(truthy) : valueForRef(falsy)));
}), APPEND_OPCODES.add(Op.Not, vm => {
let ref = vm.stack.pop();
vm.stack.push(createComputeRef(() => !toBool(valueForRef(ref))));
}), APPEND_OPCODES.add(Op.GetDynamicVar, vm => {
let scope = vm.dynamicScope(),
stack = vm.stack,
nameRef = stack.pop();
stack.push(createComputeRef(() => {
let name = String(valueForRef(nameRef));
return valueForRef(scope.get(name));
}));
}), APPEND_OPCODES.add(Op.Log, vm => {
let {
positional: positional
} = vm.stack.pop().capture();
vm.loadValue($v0, createComputeRef(() => {
// eslint-disable-next-line no-console
console.log(...reifyPositional(positional));
}));
});
class DynamicTextContent {
constructor(node, reference, lastValue) {
this.node = node, this.reference = reference, this.lastValue = lastValue;
}
evaluate() {
let normalized,
value = valueForRef(this.reference),
{
lastValue: lastValue
} = this;
value !== lastValue && (normalized = isEmpty$2(value) ? "" : isString(value) ? value : String(value), normalized !== lastValue) && (this.node.nodeValue = this.lastValue = normalized);
}
}
function toContentType(value) {
return function (value) {
return isString(value) || isEmpty$2(value) || "boolean" == typeof value || "number" == typeof value;
}(value) ? ContentType.String : isCurriedType(value, CurriedTypes.Component) || hasInternalComponentManager(value) ? ContentType.Component : isCurriedType(value, CurriedTypes.Helper) || hasInternalHelperManager(value) ? ContentType.Helper : isSafeString(value) ? ContentType.SafeString : function (value) {
return isNode(value) && 11 === value.nodeType;
}(value) ? ContentType.Fragment : isNode(value) ? ContentType.Node : ContentType.String;
}
function toDynamicContentType(value) {
if (!isObject(value)) return ContentType.String;
if (isCurriedType(value, CurriedTypes.Component) || hasInternalComponentManager(value)) return ContentType.Component;
if (isDevelopingApp() && !isCurriedType(value, CurriedTypes.Helper) && !hasInternalHelperManager(value)) throw new Error(`Attempted use a dynamic value as a component or helper, but that value did not have an associated component or helper manager. The value was: ${value}`);
return ContentType.Helper;
}
function debugCallback(context, get) {
// eslint-disable-next-line no-console
console.info("Use `context`, and `get(<path>)` to debug this template."), get("this");
}
APPEND_OPCODES.add(Op.ContentType, vm => {
let reference = vm.stack.peek();
vm.stack.push(toContentType(valueForRef(reference))), isConstRef(reference) || vm.updateWith(new AssertFilter(reference, toContentType));
}), APPEND_OPCODES.add(Op.DynamicContentType, vm => {
let reference = vm.stack.peek();
vm.stack.push(toDynamicContentType(valueForRef(reference))), isConstRef(reference) || vm.updateWith(new AssertFilter(reference, toDynamicContentType));
}), APPEND_OPCODES.add(Op.AppendHTML, vm => {
let reference = vm.stack.pop(),
rawValue = valueForRef(reference),
value = isEmpty$2(rawValue) ? "" : String(rawValue);
vm.elements().appendDynamicHTML(value);
}), APPEND_OPCODES.add(Op.AppendSafeHTML, vm => {
let reference = vm.stack.pop(),
rawValue = valueForRef(reference).toHTML(),
value = isEmpty$2(rawValue) ? "" : rawValue;
vm.elements().appendDynamicHTML(value);
}), APPEND_OPCODES.add(Op.AppendText, vm => {
let reference = vm.stack.pop(),
rawValue = valueForRef(reference),
value = isEmpty$2(rawValue) ? "" : String(rawValue),
node = vm.elements().appendDynamicText(value);
isConstRef(reference) || vm.updateWith(new DynamicTextContent(node, reference, value));
}), APPEND_OPCODES.add(Op.AppendDocumentFragment, vm => {
let reference = vm.stack.pop(),
value = valueForRef(reference);
vm.elements().appendDynamicFragment(value);
}), APPEND_OPCODES.add(Op.AppendNode, vm => {
let reference = vm.stack.pop(),
value = valueForRef(reference);
vm.elements().appendDynamicNode(value);
});
let callback = debugCallback;
// For testing purposes
function setDebuggerCallback(cb) {
callback = cb;
}
function resetDebuggerCallback() {
callback = debugCallback;
}
class ScopeInspector {
locals = dict();
constructor(scope, symbols, debugInfo) {
this.scope = scope;
for (const slot of debugInfo) {
let name = unwrap(symbols[slot - 1]),
ref = scope.getSymbol(slot);
this.locals[name] = ref;
}
}
get(path) {
let ref,
{
scope: scope,
locals: locals
} = this,
parts = path.split("."),
[head, ...tail] = path.split("."),
evalScope = scope.getEvalScope();
return "this" === head ? ref = scope.getSelf() : locals[head] ? ref = unwrap(locals[head]) : 0 === head.indexOf("@") && evalScope[head] ? ref = evalScope[head] : (ref = this.scope.getSelf(), tail = parts), tail.reduce((r, part) => childRefFor(r, part), ref);
}
}
APPEND_OPCODES.add(Op.Debugger, (vm, {
op1: _symbols,
op2: _debugInfo
}) => {
let symbols = vm[CONSTANTS].getArray(_symbols),
debugInfo = vm[CONSTANTS].getArray(decodeHandle(_debugInfo)),
inspector = new ScopeInspector(vm.scope(), symbols, debugInfo);
callback(valueForRef(vm.getSelf()), path => valueForRef(inspector.get(path)));
}), APPEND_OPCODES.add(Op.EnterList, (vm, {
op1: relativeStart,
op2: elseTarget
}) => {
let stack = vm.stack,
listRef = stack.pop(),
keyRef = stack.pop(),
keyValue = valueForRef(keyRef),
key = null === keyValue ? "@identity" : String(keyValue),
iteratorRef = createIteratorRef(listRef, key),
iterator = valueForRef(iteratorRef);
vm.updateWith(new AssertFilter(iteratorRef, iterator => iterator.isEmpty())), !0 === iterator.isEmpty() ?
// TODO: Fix this offset, should be accurate
vm.goto(elseTarget + 1) : (vm.enterList(iteratorRef, relativeStart), vm.stack.push(iterator));
}), APPEND_OPCODES.add(Op.ExitList, vm => {
vm.exitList();
}), APPEND_OPCODES.add(Op.Iterate, (vm, {
op1: breaks
}) => {
let item = vm.stack.peek().next();
null !== item ? vm.registerItem(vm.enterItem(item)) : vm.goto(breaks);
});
const CAPABILITIES = {
dynamicLayout: !1,
dynamicTag: !1,
prepareArgs: !1,
createArgs: !1,
attributeHook: !1,
elementHook: !1,
createCaller: !1,
dynamicScope: !1,
updateHook: !1,
createInstance: !1,
wrapped: !1,
willDestroy: !1,
hasSubOwner: !1
};
class TemplateOnlyComponentManager {
getCapabilities() {
return CAPABILITIES;
}
getDebugName({
name: name
}) {
return name;
}
getSelf() {
return NULL_REFERENCE;
}
getDestroyable() {
return null;
}
}
const TEMPLATE_ONLY_COMPONENT_MANAGER = new TemplateOnlyComponentManager();
// This is only exported for types, don't use this class directly
class TemplateOnlyComponentDefinition {
constructor(moduleName = "@glimmer/component/template-only", name = "(unknown template-only component)") {
this.moduleName = moduleName, this.name = name;
}
toString() {
return this.moduleName;
}
}
/**
This utility function is used to declare a given component has no backing class. When the rendering engine detects this it
is able to perform a number of optimizations. Templates that are associated with `templateOnly()` will be rendered _as is_
without adding a wrapping `<div>` (or any of the other element customization behaviors of [@ember/component](/ember/release/classes/Component)).
Specifically, this means that the template will be rendered as "outer HTML".
In general, this method will be used by build time tooling and would not be directly written in an application. However,
at times it may be useful to use directly to leverage the "outer HTML" semantics mentioned above. For example, if an addon would like
to use these semantics for its templates but cannot be certain it will only be consumed by applications that have enabled the
`template-only-glimmer-components` optional feature.
@example
```js
import { templateOnlyComponent } from '@glimmer/runtime';
export default templateOnlyComponent();
```
@public
@method templateOnly
@param {String} moduleName the module name that the template only component represents, this will be used for debugging purposes
@category EMBER_GLIMMER_SET_COMPONENT_TEMPLATE
*/
function templateOnlyComponent(moduleName, name) {
return new TemplateOnlyComponentDefinition(moduleName, name);
}
// http://www.w3.org/TR/html/syntax.html#html-integration-point
setInternalComponentManager(TEMPLATE_ONLY_COMPONENT_MANAGER, TemplateOnlyComponentDefinition.prototype);
const SVG_INTEGRATION_POINTS = {
foreignObject: 1,
desc: 1,
title: 1
},
BLACKLIST_TABLE = Object.create(null);
// http://www.w3.org/TR/html/syntax.html#adjust-svg-attributes
// TODO: Adjust SVG attributes
// http://www.w3.org/TR/html/syntax.html#parsing-main-inforeign
// TODO: Adjust SVG elements
// http://www.w3.org/TR/html/syntax.html#parsing-main-inforeign
class DOMOperations {
// Set by this.setupUselessElement() in constructor
constructor(document) {
this.document = document, this.setupUselessElement();
}
// split into separate method so that NodeDOMTreeConstruction
// can override it.
setupUselessElement() {
this.uselessElement = this.document.createElement("div");
}
createElement(tag, context) {
let isElementInSVGNamespace, isHTMLIntegrationPoint;
if (context ? (isElementInSVGNamespace = context.namespaceURI === NS_SVG || "svg" === tag, isHTMLIntegrationPoint = !!SVG_INTEGRATION_POINTS[context.tagName]) : (isElementInSVGNamespace = "svg" === tag, isHTMLIntegrationPoint = !1), isElementInSVGNamespace && !isHTMLIntegrationPoint) {
// FIXME: This does not properly handle <font> with color, face, or
// size attributes, which is also disallowed by the spec. We should fix
// this.
if (BLACKLIST_TABLE[tag]) throw new Error(`Cannot create a ${tag} inside an SVG context`);
return this.document.createElementNS(NS_SVG, tag);
}
return this.document.createElement(tag);
}
insertBefore(parent, node, reference) {
parent.insertBefore(node, reference);
}
insertHTMLBefore(parent, nextSibling, html) {
if ("" === html) {
const comment = this.createComment("");
return parent.insertBefore(comment, nextSibling), new ConcreteBounds(parent, comment, comment);
}
const prev = nextSibling ? nextSibling.previousSibling : parent.lastChild;
let last;
if (null === nextSibling) parent.insertAdjacentHTML(INSERT_BEFORE_END, html), last = expect(parent.lastChild, "bug in insertAdjacentHTML?");else if (nextSibling instanceof HTMLElement) nextSibling.insertAdjacentHTML("beforebegin", html), last = expect(nextSibling.previousSibling, "bug in insertAdjacentHTML?");else {
// Non-element nodes do not support insertAdjacentHTML, so add an
// element and call it on that element. Then remove the element.
// This also protects Edge, IE and Firefox w/o the inspector open
// from merging adjacent text nodes. See ./compat/text-node-merging-fix.ts
const {
uselessElement: uselessElement
} = this;
parent.insertBefore(uselessElement, nextSibling), uselessElement.insertAdjacentHTML(INSERT_BEFORE_BEGIN, html), last = expect(uselessElement.previousSibling, "bug in insertAdjacentHTML?"), parent.removeChild(uselessElement);
}
const first = expect(prev ? prev.nextSibling : parent.firstChild, "bug in insertAdjacentHTML?");
return new ConcreteBounds(parent, first, last);
}
createTextNode(text) {
return this.document.createTextNode(text);
}
createComment(data) {
return this.document.createComment(data);
}
}
// Patch: insertAdjacentHTML on SVG Fix
// Browsers: Safari, IE, Edge, Firefox ~33-34
// Reason: insertAdjacentHTML does not exist on SVG elements in Safari. It is
// present but throws an exception on IE and Edge. Old versions of
// Firefox create nodes in the incorrect namespace.
// Fix: Since IE and Edge silently fail to create SVG nodes using
// innerHTML, and because Firefox may create nodes in the incorrect
// namespace using innerHTML on SVG elements, an HTML-string wrapping
// approach is used. A pre/post SVG tag is added to the string, then
// that whole string is added to a div. The created nodes are plucked
// out and applied to the target location on DOM.
function applySVGInnerHTMLFix(document, DOMClass, svgNamespace) {
if (!document) return DOMClass;
if (!function (document, svgNamespace) {
const svg = document.createElementNS(svgNamespace, "svg");
try {
svg.insertAdjacentHTML(INSERT_BEFORE_END, "<circle></circle>");
} catch (e) {
// IE, Edge: Will throw, insertAdjacentHTML is unsupported on SVG
// Safari: Will throw, insertAdjacentHTML is not present on SVG
} finally {
// FF: Old versions will create a node in the wrong namespace
return 1 !== svg.childNodes.length || castToBrowser(unwrap(svg.firstChild), "SVG").namespaceURI !== NS_SVG;
// eslint-disable-next-line no-unsafe-finally
}
}
// Patch: Adjacent text node merging fix
// Browsers: IE, Edge, Firefox w/o inspector open
// Reason: These browsers will merge adjacent text nodes. For example given
// <div>Hello</div> with div.insertAdjacentHTML(' world') browsers
// with proper behavior will populate div.childNodes with two items.
// These browsers will populate it with one merged node instead.
// Fix: Add these nodes to a wrapper element, then iterate the childNodes
// of that wrapper and move the nodes to their target location. Note
// that potential SVG bugs will have been handled before this fix.
// Note that this fix must only apply to the previous text node, as
// the base implementation of `insertHTMLBefore` already handles
// following text nodes correctly.
(document, svgNamespace)) return DOMClass;
const div = document.createElement("div");
return class extends DOMClass {
insertHTMLBefore(parent, nextSibling, html) {
return "" === html || parent.namespaceURI !== svgNamespace ? super.insertHTMLBefore(parent, nextSibling, html) : function (parent, div, html, reference) {
let source;
// This is important, because descendants of the <foreignObject> integration
// point are parsed in the HTML namespace
if (debugAssert("" !== html, "html cannot be empty"), "FOREIGNOBJECT" === parent.tagName.toUpperCase()) {
// IE, Edge: also do not correctly support using `innerHTML` on SVG
// namespaced elements. So here a wrapper is used.
const wrappedHtml = "<svg><foreignObject>" + html + "</foreignObject></svg>";
clearElement(div), div.insertAdjacentHTML(INSERT_AFTER_BEGIN, wrappedHtml), source = div.firstChild.firstChild;
} else {
// IE, Edge: also do not correctly support using `innerHTML` on SVG
// namespaced elements. So here a wrapper is used.
const wrappedHtml = "<svg>" + html + "</svg>";
clearElement(div), div.insertAdjacentHTML(INSERT_AFTER_BEGIN, wrappedHtml), source = div.firstChild;
}
return function (source, target, nextSibling) {
const first = expect(source.firstChild, "source is empty");
let last = first,
current = first;
for (; current;) {
const next = current.nextSibling;
target.insertBefore(current, nextSibling), last = current, current = next;
}
return new ConcreteBounds(target, first, last);
}(source, parent, reference);
}(parent, div, html, nextSibling);
}
};
}
function applyTextNodeMergingFix(document, DOMClass) {
return document && function (document) {
const mergingTextDiv = document.createElement("div");
return mergingTextDiv.appendChild(document.createTextNode("first")), mergingTextDiv.insertAdjacentHTML(INSERT_BEFORE_END, "second"), 2 !== mergingTextDiv.childNodes.length;
}(document) ? class extends DOMClass {
uselessComment;
constructor(document) {
super(document), this.uselessComment = document.createComment("");
}
insertHTMLBefore(parent, nextSibling, html) {
if ("" === html) return super.insertHTMLBefore(parent, nextSibling, html);
let didSetUselessComment = !1;
const nextPrevious = nextSibling ? nextSibling.previousSibling : parent.lastChild;
nextPrevious && nextPrevious instanceof Text && (didSetUselessComment = !0, parent.insertBefore(this.uselessComment, nextSibling));
const bounds = super.insertHTMLBefore(parent, nextSibling, html);
return didSetUselessComment && parent.removeChild(this.uselessComment), bounds;
}
} : DOMClass;
}
const doc$1 = "undefined" == typeof document ? null : castToSimple(document);
let appliedTreeConstruction = class extends DOMOperations {
createElementNS(namespace, tag) {
return this.document.createElementNS(namespace, tag);
}
setAttribute(element, name, value, namespace = null) {
namespace ? element.setAttributeNS(namespace, name, value) : element.setAttribute(name, value);
}
};
appliedTreeConstruction = applyTextNodeMergingFix(doc$1, appliedTreeConstruction), appliedTreeConstruction = applySVGInnerHTMLFix(doc$1, appliedTreeConstruction, NS_SVG);
const DOMTreeConstruction = appliedTreeConstruction;
["b", "big", "blockquote", "body", "br", "center", "code", "dd", "div", "dl", "dt", "em", "embed", "h1", "h2", "h3", "h4", "h5", "h6", "head", "hr", "i", "img", "li", "listing", "main", "meta", "nobr", "ol", "p", "pre", "ruby", "s", "small", "span", "strong", "strike", "sub", "sup", "table", "tt", "u", "ul", "var"].forEach(tag => BLACKLIST_TABLE[tag] = 1);
const WHITESPACE = /[\t\n\v\f\r \xA0\u{1680}\u{180e}\u{2000}-\u{200a}\u{2028}\u{2029}\u{202f}\u{205f}\u{3000}\u{feff}]/u,
doc = "undefined" == typeof document ? null : castToSimple(document);
function isWhitespace(string) {
return WHITESPACE.test(string);
}
class DOMChangesImpl extends DOMOperations {
namespace;
constructor(document) {
super(document), this.document = document, this.namespace = null;
}
setAttribute(element, name, value) {
element.setAttribute(name, value);
}
removeAttribute(element, name) {
element.removeAttribute(name);
}
insertAfter(element, node, reference) {
this.insertBefore(element, node, reference.nextSibling);
}
}
let helper = DOMChangesImpl;
helper = applyTextNodeMergingFix(doc, helper), helper = applySVGInnerHTMLFix(doc, helper, NS_SVG);
const DOMChanges = helper;
let GUID = 0;
class Ref {
id = GUID++;
value;
constructor(value) {
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 {
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 (isDevelopingApp() && this.stack.current !== state) throw new Error(`BUG: expecting ${this.stack.current}, got ${state}`);
this.nodeFor(state).bounds = bounds, this.exit();
}
willDestroy(state) {
expect(this.refs.get(state), "BUG: missing ref").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 = expect(this.stack.toArray()[0], "expected root state when resetting render tree"),
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 expect(this.nodes.get(state), "BUG: missing node");
}
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 = expect(node.bounds, "BUG: missing bounds");
return {
parentElement: bounds.parentElement(),
firstNode: bounds.firstNode(),
lastNode: bounds.lastNode()
};
}
}
const TRANSACTION = Symbol("TRANSACTION");
class TransactionImpl {
scheduledInstallModifiers = [];
scheduledUpdateModifiers = [];
createdComponents = [];
updatedComponents = [];
didCreate(component) {
this.createdComponents.push(component);
}
didUpdate(component) {
this.updatedComponents.push(component);
}
scheduleInstallModifier(modifier) {
this.scheduledInstallModifiers.push(modifier);
}
scheduleUpdateModifier(modifier) {
this.scheduledUpdateModifiers.push(modifier);
}
commit() {
let {
createdComponents: createdComponents,
updatedComponents: updatedComponents
} = this;
for (const {
manager: manager,
state: state
} of createdComponents) manager.didCreate(state);
for (const {
manager: manager,
state: state
} of updatedComponents) manager.didUpdate(state);
let {
scheduledInstallModifiers: scheduledInstallModifiers,
scheduledUpdateModifiers: scheduledUpdateModifiers
} = this;
for (const {
manager: manager,
state: state,
definition: definition
} of scheduledInstallModifiers) {
let modifierTag = manager.getTag(state);
if (null !== modifierTag) {
let tag = track(() => manager.install(state), isDevelopingApp() && `- While rendering:\n (instance of a \`${definition.resolvedName || manager.getDebugName(definition.state)}\` modifier)`);
UPDATE_TAG(modifierTag, tag);
} else manager.install(state);
}
for (const {
manager: manager,
state: state,
definition: definition
} of scheduledUpdateModifiers) {
let modifierTag = manager.getTag(state);
if (null !== modifierTag) {
let tag = track(() => manager.update(state), isDevelopingApp() && `- While rendering:\n (instance of a \`${definition.resolvedName || manager.getDebugName(definition.state)}\` modifier)`);
UPDATE_TAG(modifierTag, tag);
} else manager.update(state);
}
}
}
class EnvironmentImpl {
[TRANSACTION] = null;
updateOperations;
// Delegate methods and values
isInteractive;
isArgumentCaptureError;
debugRenderTree;
constructor(options, delegate) {
if (this.delegate = delegate, this.isInteractive = delegate.isInteractive, this.debugRenderTree = this.delegate.enableDebugTooling ? new DebugRenderTreeImpl() : void 0, this.isArgumentCaptureError = this.delegate.enableDebugTooling ? isArgumentError : void 0, options.appendOperations) this.appendOperations = options.appendOperations, this.updateOperations = options.updateOperations;else if (options.document) this.appendOperations = new DOMTreeConstruction(options.document), this.updateOperations = new DOMChangesImpl(options.document);else if (isDevelopingApp()) throw new Error("you must pass document or appendOperations to a new runtime");
}
getAppendOperations() {
return this.appendOperations;
}
getDOM() {
return expect(this.updateOperations, "Attempted to get DOM updateOperations, but they were not provided by the environment. You may be attempting to rerender in an environment which does not support rerendering, such as SSR.");
}
begin() {
debugAssert(!this[TRANSACTION], "A glimmer transaction was begun, but one already exists. You may have a nested transaction, possibly caused by an earlier runtime exception while rendering. Please check your console for the stack trace of any prior exceptions."), this.debugRenderTree?.begin(), this[TRANSACTION] = new TransactionImpl();
}
get transaction() {
return expect(this[TRANSACTION], "must be in a transaction");
}
didCreate(component) {
this.transaction.didCreate(component);
}
didUpdate(component) {
this.transaction.didUpdate(component);
}
scheduleInstallModifier(modifier) {
this.isInteractive && this.transaction.scheduleInstallModifier(modifier);
}
scheduleUpdateModifier(modifier) {
this.isInteractive && this.transaction.scheduleUpdateModifier(modifier);
}
commit() {
let transaction = this.transaction;
this[TRANSACTION] = null, transaction.commit(), this.debugRenderTree?.commit(), this.delegate.onTransactionCommit();
}
}
function runtimeContext(options, delegate, artifacts, resolver) {
return {
env: new EnvironmentImpl(options, delegate),
program: new RuntimeProgramImpl(artifacts.constants, artifacts.heap),
resolver: resolver
};
}
function inTransaction(env, block) {
if (env[TRANSACTION]) block();else {
env.begin();
try {
block();
} finally {
env.commit();
}
}
}
function internalHelper(helper) {
return setInternalHelperManager(helper, {});
}
/**
Use the `{{array}}` helper to create an array to pass as an option to your
components.
```handlebars
<MyComponent @people={{array
'Tom Dale'
'Yehuda Katz'
this.myOtherPerson}}
/>
```
or
```handlebars
{{my-component people=(array
'Tom Dale'
'Yehuda Katz'
this.myOtherPerson)
}}
```
Would result in an object such as:
```js
['Tom Dale', 'Yehuda Katz', this.get('myOtherPerson')]
```
Where the 3rd item in the array is bound to updates of the `myOtherPerson` property.
@method array
@param {Array} options
@return {Array} Array
@public
*/
const array = internalHelper(({
positional: positional
}) => createComputeRef(() => reifyPositional(positional), null, "array")),
normalizeTextValue = value => (value => null == value || "function" != typeof value.toString)(value) ? "" : String(value),
concat = internalHelper(({
positional: positional
}) => createComputeRef(() => reifyPositional(positional).map(normalizeTextValue).join(""), null, "concat")),
context = buildUntouchableThis("`fn` helper"),
fn = internalHelper(({
positional: positional
}) => {
let callbackRef = positional[0];
return createComputeRef(() => (...invocationArgs) => {
let [fn, ...args] = reifyPositional(positional);
if (isDevelopingApp() && function (callbackRef) {
if (!callbackRef || !isInvokableRef(callbackRef) && "function" != typeof valueForRef(callbackRef)) throw new Error(`You must pass a function as the \`fn\` helper's first argument, you passed ${callbackRef ? valueForRef(callbackRef) : callbackRef}. While rendering:\n\n${callbackRef?.debugLabel}`);
}
/**
Dynamically look up a property on an object. The second argument to `{{get}}`
should have a string value, although it can be bound.
For example, these two usages are equivalent:
```app/components/developer-detail.js
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
export default class extends Component {
@tracked developer = {
name: "Sandi Metz",
language: "Ruby"
}
}
```
```handlebars
{{this.developer.name}}
{{get this.developer "name"}}
```
If there were several facts about a person, the `{{get}}` helper can dynamically
pick one:
```app/templates/application.hbs
<DeveloperDetail @factName="language" />
```
```handlebars
{{get this.developer @factName}}
```
For a more complex example, this template would allow the user to switch
between showing the user's height and weight with a click:
```app/components/developer-detail.js
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
export default class extends Component {
@tracked developer = {
name: "Sandi Metz",
language: "Ruby"
}
@tracked currentFact = 'name'
showFact = (fact) => {
this.currentFact = fact;
}
}
```
```app/components/developer-detail.js
{{get this.developer this.currentFact}}
<button {{on 'click' (fn this.showFact "name")}}>Show name</button>
<button {{on 'click' (fn this.showFact "language")}}>Show language</button>
```
The `{{get}}` helper can also respect mutable values itself. For example:
```app/components/developer-detail.js
<Input @value={{mut (get this.person this.currentFact)}} />
<button {{on 'click' (fn this.showFact "name")}}>Show name</button>
<button {{on 'click' (fn this.showFact "language")}}>Show language</button>
```
Would allow the user to swap what fact is being displayed, and also edit
that fact via a two-way mutable binding.
@public
@method get
*/(callbackRef), isInvokableRef(callbackRef)) {
let value = args.length > 0 ? args[0] : invocationArgs[0];
return updateRef(callbackRef, value);
}
return fn.call(context, ...args, ...invocationArgs);
}, null, "fn");
}),
get = internalHelper(({
positional: positional
}) => {
let sourceRef = positional[0] ?? UNDEFINED_REFERENCE,
pathRef = positional[1] ?? UNDEFINED_REFERENCE;
return createComputeRef(() => {
let source = valueForRef(sourceRef);
if (isDict(source)) return getPath(source, String(valueForRef(pathRef)));
}, value => {
let source = valueForRef(sourceRef);
if (isDict(source)) return setPath(source, String(valueForRef(pathRef)), value);
}, "get");
}),
hash = internalHelper(({
named: named
}) => {
let ref = createComputeRef(() => reifyNamed(named), null, "hash"),
children = new Map();
// Setup the children so that templates can bypass getting the value of
// the reference and treat children lazily
for (let name in named) children.set(name, named[name]);
return ref.children = children, ref;
});
let ARGS_CACHES = isDevelopingApp() ? new WeakMap() : void 0;
function getArgs(proxy) {
return getValue(isDevelopingApp() ? ARGS_CACHES.get(proxy) : proxy.argsCache);
}
class SimpleArgsProxy {
argsCache;
constructor(context, computeArgs = () => EMPTY_ARGS) {
let argsCache = createCache(() => computeArgs(context));
isDevelopingApp() ? (ARGS_CACHES.set(this, argsCache), Object.freeze(this)) : this.argsCache = argsCache;
}
get named() {
return getArgs(this).named || EMPTY_NAMED;
}
get positional() {
return getArgs(this).positional || EMPTY_POSITIONAL;
}
}
////////////
function invokeHelper(context, definition, computeArgs) {
if (isDevelopingApp() && ("object" != typeof context || null === context)) throw new Error(`Expected a context object to be passed as the first parameter to invokeHelper, got ${context}`);
const owner = getOwner(context),
internalManager = getInternalHelperManager(definition);
// TODO: figure out why assert isn't using the TS assert thing
if (isDevelopingApp() && !internalManager) throw new Error(`Expected a helper definition to be passed as the second parameter to invokeHelper, but no helper manager was found. The definition value that was passed was \`${debugToString$1(definition)}\`. Did you use setHelperManager to associate a helper manager with this value?`);
if (isDevelopingApp() && "function" == typeof internalManager) throw new Error("Found a helper manager, but it was an internal built-in helper manager. `invokeHelper` does not support internal helpers yet.");
const manager = internalManager.getDelegateFor(owner);
let cache,
args = new SimpleArgsProxy(context, computeArgs),
bucket = manager.createHelper(definition, args);
if (!hasValue(manager)) throw new Error("TODO: unreachable, to be implemented with hasScheduledEffect");
if (cache = createCache(() => {
if (isDevelopingApp() && (isDestroying(cache) || isDestroyed(cache))) throw new Error("You attempted to get the value of a helper after the helper was destroyed, which is not allowed");
return manager.getValue(bucket);
}), associateDestroyableChild(context, cache), hasDestroyable(manager)) {
let destroyable = manager.getDestroyable(bucket);
associateDestroyableChild(cache, destroyable);
}
return cache;
}
const untouchableContext = buildUntouchableThis("`on` modifier");
class OnModifierState {
tag = createUpdatableTag();
element;
args;
listener = null;
constructor(element, args) {
this.element = element, this.args = args, registerDestructor(this, () => {
let {
element: element,
listener: listener
} = this;
if (listener) {
let {
eventName: eventName,
callback: callback,
options: options
} = listener;
removeEventListener(element, eventName, callback, options);
}
});
}
// Update this.listener if needed
updateListener() {
let {
element: element,
args: args,
listener: listener
} = this;
debugAssert(args.positional[0], "You must pass a valid DOM event name as the first argument to the `on` modifier");
let eventName = valueForRef(args.positional[0]);
debugAssert(args.positional[1], "You must pass a function as the second argument to the `on` modifier");
let once,
passive,
capture,
userProvidedCallback = valueForRef(args.positional[1]);
if (isDevelopingApp() && 2 !== args.positional.length) throw new Error(`You can only pass two positional arguments (event name and callback) to the \`on\` modifier, but you provided ${args.positional.length}. Consider using the \`fn\` helper to provide additional arguments to the \`on\` callback.`);
if (isDevelopingApp()) {
let {
once: _once,
passive: _passive,
capture: _capture,
...extra
} = reifyNamed(args.named);
if (once = _once, passive = _passive, capture = _capture, Object.keys(extra).length > 0) throw new Error(`You can only \`once\`, \`passive\` or \`capture\` named arguments to the \`on\` modifier, but you provided ${Object.keys(extra).join(", ")}.`);
} else {
let {
once: _once,
passive: _passive,
capture: _capture
} = args.named;
_once && (once = valueForRef(_once)), _passive && (passive = valueForRef(_passive)), _capture && (capture = valueForRef(_capture));
}
let options,
shouldUpdate = !1;
if (shouldUpdate = null === listener || eventName !== listener.eventName || userProvidedCallback !== listener.userProvidedCallback || once !== listener.once || passive !== listener.passive || capture !== listener.capture,
// we want to handle both `true` and `false` because both have a meaning:
// https://bugs.chromium.org/p/chromium/issues/detail?id=770208
shouldUpdate && (void 0 === once && void 0 === passive && void 0 === capture || (options = {
once: once,
passive: passive,
capture: capture
})), shouldUpdate) {
let callback = userProvidedCallback;
if (isDevelopingApp() && (callback = userProvidedCallback.bind(untouchableContext), passive)) {
let _callback = callback;
callback = event => (event.preventDefault = () => {
throw new Error(`You marked this listener as 'passive', meaning that you must not call 'event.preventDefault()': \n\n${userProvidedCallback.name ?? "{anonymous function}"}`);
}, _callback(event));
}
this.listener = {
eventName: eventName,
callback: callback,
userProvidedCallback: userProvidedCallback,
once: once,
passive: passive,
capture: capture,
options: options
}, listener && removeEventListener(element, listener.eventName, listener.callback, listener.options), function (element, eventName, callback, options) {
adds++, element.addEventListener(eventName, callback, options);
}
/**
The `{{on}}` modifier lets you easily add event listeners (it uses
[EventTarget.addEventListener](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener)
internally).
For example, if you'd like to run a function on your component when a `<button>`
in the components template is clicked you might do something like:
```app/components/like-post.hbs
<button {{on 'click' this.saveLike}}>Like this post!</button>
```
```app/components/like-post.js
import Component from '@glimmer/component';
import { action } from '@ember/object';
export default class LikePostComponent extends Component {
saveLike = () => {
// someone likes your post!
// better send a request off to your server...
}
}
```
### Arguments
`{{on}}` accepts two positional arguments, and a few named arguments.
The positional arguments are:
- `event` -- the name to use when calling `addEventListener`
- `callback` -- the function to be passed to `addEventListener`
The named arguments are:
- capture -- a `true` value indicates that events of this type will be dispatched
to the registered listener before being dispatched to any EventTarget beneath it
in the DOM tree.
- once -- indicates that the listener should be invoked at most once after being
added. If true, the listener would be automatically removed when invoked.
- passive -- if `true`, indicates that the function specified by listener will never
call preventDefault(). If a passive listener does call preventDefault(), the user
agent will do nothing other than generate a console warning. See
[Improving scrolling performance with passive listeners](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#Improving_scrolling_performance_with_passive_listeners)
to learn more.
The callback function passed to `{{on}}` will receive any arguments that are passed
to the event handler. Most commonly this would be the `event` itself.
If you would like to pass additional arguments to the function you should use
the `{{fn}}` helper.
For example, in our example case above if you'd like to pass in the post that
was being liked when the button is clicked you could do something like:
```app/components/like-post.hbs
<button {{on 'click' (fn this.saveLike @post)}}>Like this post!</button>
```
In this case, the `saveLike` function will receive two arguments: the click event
and the value of `@post`.
### Function Context
In the example above, we used an arrow function to ensure that `likePost` is
properly bound to the `items-list`, but let's explore what happens if we
left out the arrow function:
```app/components/like-post.js
import Component from '@glimmer/component';
export default class LikePostComponent extends Component {
saveLike() {
// ...snip...
}
}
```
In this example, when the button is clicked `saveLike` will be invoked,
it will **not** have access to the component instance. In other
words, it will have no `this` context, so please make sure your functions
are bound (via an arrow function or other means) before passing into `on`!
@method on
@public
*/(element, eventName, callback, options);
}
}
}
let adds = 0,
removes = 0;
function removeEventListener(element, eventName, callback, options) {
removes++, element.removeEventListener(eventName, callback, options);
}
const on = setInternalModifierManager(new class {
getDebugName() {
return "on";
}
getDebugInstance() {
return null;
}
get counters() {
return {
adds: adds,
removes: removes
};
}
create(_owner, element, _state, args) {
return new OnModifierState(element, args);
}
getTag({
tag: tag
}) {
return tag;
}
install(state) {
state.updateListener();
}
update(state) {
state.updateListener();
}
getDestroyable(state) {
return state;
}
}(), {});
class LowLevelVM {
currentOpSize = 0;
constructor(stack, heap, program, externs, registers) {
this.stack = stack, this.heap = heap, this.program = program, this.externs = externs, this.registers = registers;
}
fetchRegister(register) {
return this.registers[register];
}
loadRegister(register, value) {
this.registers[register] = value;
}
setPc(pc) {
debugAssert("number" == typeof pc && !isNaN(pc), "pc is set to a number"), this.registers[$pc] = pc;
}
// Start a new frame and save $ra and $fp on the stack
pushFrame() {
this.stack.push(this.registers[$ra]), this.stack.push(this.registers[$fp]), this.registers[$fp] = this.registers[$sp] - 1;
}
// Restore $ra, $sp and $fp
popFrame() {
this.registers[$sp] = this.registers[$fp] - 1, this.registers[$ra] = this.stack.get(0), this.registers[$fp] = this.stack.get(1);
}
pushSmallFrame() {
this.stack.push(this.registers[$ra]);
}
popSmallFrame() {
this.registers[$ra] = this.stack.pop();
}
// Jump to an address in `program`
goto(offset) {
this.setPc(this.target(offset));
}
target(offset) {
return this.registers[$pc] + offset - this.currentOpSize;
}
// Save $pc into $ra, then jump to a new address in `program` (jal in MIPS)
call(handle) {
debugAssert(handle < 4294967295, "Jumping to placeholder address"), this.registers[$ra] = this.registers[$pc], this.setPc(this.heap.getaddr(handle));
}
// Put a specific `program` address in $ra
returnTo(offset) {
this.registers[$ra] = this.target(offset);
}
// Return to the `program` address stored in $ra
return() {
this.setPc(this.registers[$ra]);
}
nextStatement() {
let {
registers: registers,
program: program
} = this,
pc = registers[$pc];
if (debugAssert("number" == typeof pc, "pc is a number"), -1 === pc) return null;
// We have to save off the current operations size so that
// when we do a jump we can calculate the correct offset
// to where we are going. We can't simply ask for the size
// in a jump because we have have already incremented the
// program counter to the next instruction prior to executing.
let opcode = program.opcode(pc),
operationSize = this.currentOpSize = opcode.size;
return this.registers[$pc] += operationSize, opcode;
}
evaluateOuter(opcode, vm) {
this.evaluateInner(opcode, vm);
}
evaluateInner(opcode, vm) {
opcode.isMachine ? this.evaluateMachine(opcode) : this.evaluateSyscall(opcode, vm);
}
evaluateMachine(opcode) {
switch (opcode.type) {
case MachineOp.PushFrame:
return this.pushFrame();
case MachineOp.PopFrame:
return this.popFrame();
case MachineOp.InvokeStatic:
return this.call(opcode.op1);
case MachineOp.InvokeVirtual:
return this.call(this.stack.pop());
case MachineOp.Jump:
return this.goto(opcode.op1);
case MachineOp.Return:
return this.return();
case MachineOp.ReturnTo:
return this.returnTo(opcode.op1);
}
}
evaluateSyscall(opcode, vm) {
APPEND_OPCODES.evaluate(vm, opcode, opcode.type);
}
}
class UpdatingVM {
env;
dom;
alwaysRevalidate;
frameStack = new StackImpl();
constructor(env, {
alwaysRevalidate = !1
}) {
this.env = env, this.dom = env.getDOM(), this.alwaysRevalidate = alwaysRevalidate;
}
execute(opcodes, handler) {
if (isDevelopingApp()) {
let hasErrored = !0;
try {
debug.runInTrackingTransaction(() => this._execute(opcodes, handler), "- While rendering:"),
// using a boolean here to avoid breaking ergonomics of "pause on uncaught exceptions"
// which would happen with a `catch` + `throw`
hasErrored = !1;
} finally {
hasErrored &&
// eslint-disable-next-line no-console
console.error(`\n\nError occurred:\n\n${resetTracking()}\n\n`);
}
} else this._execute(opcodes, handler);
}
_execute(opcodes, handler) {
let {
frameStack: frameStack
} = this;
for (this.try(opcodes, handler); !frameStack.isEmpty();) {
let opcode = this.frame.nextStatement();
void 0 !== opcode ? opcode.evaluate(this) : frameStack.pop();
}
}
get frame() {
return expect(this.frameStack.current, "bug: expected a frame");
}
goto(index) {
this.frame.goto(index);
}
try(ops, handler) {
this.frameStack.push(new UpdatingVMFrame(ops, handler));
}
throw() {
this.frame.handleException(), this.frameStack.pop();
}
}
class ResumableVMStateImpl {
constructor(state, resumeCallback) {
this.state = state, this.resumeCallback = resumeCallback;
}
resume(runtime, builder) {
return this.resumeCallback(runtime, this.state, builder);
}
}
class BlockOpcode {
children;
bounds;
constructor(state, runtime, bounds, children) {
this.state = state, this.runtime = runtime, this.children = children, this.bounds = bounds;
}
parentElement() {
return this.bounds.parentElement();
}
firstNode() {
return this.bounds.firstNode();
}
lastNode() {
return this.bounds.lastNode();
}
evaluate(vm) {
vm.try(this.children, null);
}
}
class TryOpcode extends BlockOpcode {
type = "try";
// Hides property on base class
evaluate(vm) {
vm.try(this.children, this);
}
handleException() {
let {
state: state,
bounds: bounds,
runtime: runtime
} = this;
destroyChildren(this);
let elementStack = NewElementBuilder.resume(runtime.env, bounds),
vm = state.resume(runtime, elementStack),
updating = [],
children = this.children = [],
result = vm.execute(vm => {
vm.pushUpdating(updating), vm.updateWith(this), vm.pushUpdating(children);
});
associateDestroyableChild(this, result.drop);
}
}
class ListItemOpcode extends TryOpcode {
retained = !1;
index = -1;
constructor(state, runtime, bounds, key, memo, value) {
super(state, runtime, bounds, []), this.key = key, this.memo = memo, this.value = value;
}
updateReferences(item) {
this.retained = !0, updateRef(this.value, item.value), updateRef(this.memo, item.memo);
}
shouldRemove() {
return !this.retained;
}
reset() {
this.retained = !1;
}
}
class ListBlockOpcode extends BlockOpcode {
type = "list-block";
opcodeMap = new Map();
marker = null;
lastIterator;
constructor(state, runtime, bounds, children, iterableRef) {
super(state, runtime, bounds, children), this.iterableRef = iterableRef, this.lastIterator = valueForRef(iterableRef);
}
initializeChild(opcode) {
opcode.index = this.children.length - 1, this.opcodeMap.set(opcode.key, opcode);
}
evaluate(vm) {
let iterator = valueForRef(this.iterableRef);
if (this.lastIterator !== iterator) {
let {
bounds: bounds
} = this,
{
dom: dom
} = vm,
marker = this.marker = dom.createComment("");
dom.insertAfter(bounds.parentElement(), marker, expect(bounds.lastNode(), "can't insert after an empty bounds")), this.sync(iterator), this.parentElement().removeChild(marker), this.marker = null, this.lastIterator = iterator;
}
// Run now-updated updating opcodes
super.evaluate(vm);
}
sync(iterator) {
let {
opcodeMap: itemMap,
children: children
} = this,
currentOpcodeIndex = 0,
seenIndex = 0;
// eslint-disable-next-line no-constant-condition
for (this.children = this.bounds.boundList = [];;) {
let item = iterator.next();
if (null === item) break;
let opcode = children[currentOpcodeIndex],
{
key: key
} = item;
// Items that have already been found and moved will already be retained,
// we can continue until we find the next unretained item
for (; void 0 !== opcode && !0 === opcode.retained;) opcode = children[++currentOpcodeIndex];
if (void 0 !== opcode && opcode.key === key) this.retainItem(opcode, item), currentOpcodeIndex++;else if (itemMap.has(key)) {
let itemOpcode = itemMap.get(key);
// The item opcode was seen already, so we should move it.
if (itemOpcode.index < seenIndex) this.moveItem(itemOpcode, item, opcode);else {
// Update the seen index, we are going to be moving this item around
// so any other items that come before it will likely need to move as
// well.
seenIndex = itemOpcode.index;
let seenUnretained = !1;
// iterate through all of the opcodes between the current position and
// the position of the item's opcode, and determine if they are all
// retained.
for (let i = currentOpcodeIndex + 1; i < seenIndex; i++) if (!1 === unwrap(children[i]).retained) {
seenUnretained = !0;
break;
}
// If we have seen only retained opcodes between this and the matching
// opcode, it means that all the opcodes in between have been moved
// already, and we can safely retain this item's opcode.
!1 === seenUnretained ? (this.retainItem(itemOpcode, item), currentOpcodeIndex = seenIndex + 1) : (this.moveItem(itemOpcode, item, opcode), currentOpcodeIndex++);
}
} else this.insertItem(item, opcode);
}
for (const opcode of children) !1 === opcode.retained ? this.deleteItem(opcode) : opcode.reset();
}
retainItem(opcode, item) {
let {
children: children
} = this;
updateRef(opcode.memo, item.memo), updateRef(opcode.value, item.value), opcode.retained = !0, opcode.index = children.length, children.push(opcode);
}
insertItem(item, before) {
let {
opcodeMap: opcodeMap,
bounds: bounds,
state: state,
runtime: runtime,
children: children
} = this,
{
key: key
} = item,
nextSibling = void 0 === before ? this.marker : before.firstNode(),
elementStack = NewElementBuilder.forInitialRender(runtime.env, {
element: bounds.parentElement(),
nextSibling: nextSibling
});
state.resume(runtime, elementStack).execute(vm => {
vm.pushUpdating();
let opcode = vm.enterItem(item);
opcode.index = children.length, children.push(opcode), opcodeMap.set(key, opcode), associateDestroyableChild(this, opcode);
});
}
moveItem(opcode, item, before) {
let currentSibling,
nextSibling,
{
children: children
} = this;
updateRef(opcode.memo, item.memo), updateRef(opcode.value, item.value), opcode.retained = !0, void 0 === before ? move(opcode, this.marker) : (currentSibling = opcode.lastNode().nextSibling, nextSibling = before.firstNode(),
// Items are moved throughout the algorithm, so there are cases where the
// the items already happen to be siblings (e.g. an item in between was
// moved before this move happened). Check to see if they are siblings
// first before doing the move.
currentSibling !== nextSibling && move(opcode, nextSibling)), opcode.index = children.length, children.push(opcode);
}
deleteItem(opcode) {
destroy(opcode), clear(opcode), this.opcodeMap.delete(opcode.key);
}
}
class UpdatingVMFrame {
current = 0;
constructor(ops, exceptionHandler) {
this.ops = ops, this.exceptionHandler = exceptionHandler;
}
goto(index) {
this.current = index;
}
nextStatement() {
return this.ops[this.current++];
}
handleException() {
this.exceptionHandler && this.exceptionHandler.handleException();
}
}
class RenderResultImpl {
constructor(env, updating, bounds, drop) {
this.env = env, this.updating = updating, this.bounds = bounds, this.drop = drop, associateDestroyableChild(this, drop), registerDestructor(this, () => clear(this.bounds));
}
rerender({
alwaysRevalidate = !1
} = {
alwaysRevalidate: !1
}) {
let {
env: env,
updating: updating
} = this;
new UpdatingVM(env, {
alwaysRevalidate: alwaysRevalidate
}).execute(updating, this);
}
parentElement() {
return this.bounds.parentElement();
}
firstNode() {
return this.bounds.firstNode();
}
lastNode() {
return this.bounds.lastNode();
}
handleException() {
throw "this should never happen";
}
}
class EvaluationStackImpl {
static restore(snapshot) {
return new this(snapshot.slice(), [0, -1, snapshot.length - 1, 0]);
}
[REGISTERS];
// fp -> sp
constructor(stack = [], registers) {
this.stack = stack, this[REGISTERS] = registers;
}
push(value) {
this.stack[++this[REGISTERS][$sp]] = value;
}
dup(position = this[REGISTERS][$sp]) {
this.stack[++this[REGISTERS][$sp]] = this.stack[position];
}
copy(from, to) {
this.stack[to] = this.stack[from];
}
pop(n = 1) {
let top = this.stack[this[REGISTERS][$sp]];
return this[REGISTERS][$sp] -= n, top;
}
peek(offset = 0) {
return this.stack[this[REGISTERS][$sp] - offset];
}
get(offset, base = this[REGISTERS][$fp]) {
return this.stack[base + offset];
}
set(value, offset, base = this[REGISTERS][$fp]) {
this.stack[base + offset] = value;
}
slice(start, end) {
return this.stack.slice(start, end);
}
capture(items) {
let end = this[REGISTERS][$sp] + 1,
start = end - items;
return this.stack.slice(start, end);
}
reset() {
this.stack.length = 0;
}
toArray() {
return this.stack.slice(this[REGISTERS][$fp], this[REGISTERS][$sp] + 1);
}
}
/**
* This interface is used by internal opcodes, and is more stable than
* the implementation of the Append VM itself.
*/
class Stacks {
scope = new StackImpl();
dynamicScope = new StackImpl();
updating = new StackImpl();
cache = new StackImpl();
list = new StackImpl();
}
class VM {
[STACKS] = new Stacks();
[HEAP];
destructor;
[DESTROYABLE_STACK] = new StackImpl();
[CONSTANTS];
[ARGS$1];
[INNER_VM];
get stack() {
return this[INNER_VM].stack;
}
/* Registers */
get pc() {
return this[INNER_VM].fetchRegister($pc);
}
s0 = null;
s1 = null;
t0 = null;
t1 = null;
v0 = null;
// Fetch a value from a register onto the stack
fetch(register) {
let value = this.fetchValue(register);
this.stack.push(value);
}
// Load a value from the stack into a register
load(register) {
let value = this.stack.pop();
this.loadValue(register, value);
}
// Fetch a value from a register
fetchValue(register) {
if (isLowLevelRegister(register)) return this[INNER_VM].fetchRegister(register);
switch (register) {
case $s0:
return this.s0;
case $s1:
return this.s1;
case $t0:
return this.t0;
case $t1:
return this.t1;
case $v0:
return this.v0;
}
}
// Load a value into a register
loadValue(register, value) {
switch (isLowLevelRegister(register) && this[INNER_VM].loadRegister(register, value), register) {
case $s0:
this.s0 = value;
break;
case $s1:
this.s1 = value;
break;
case $t0:
this.t0 = value;
break;
case $t1:
this.t1 = value;
break;
case $v0:
this.v0 = value;
}
}
/**
* Migrated to Inner
*/
// Start a new frame and save $ra and $fp on the stack
pushFrame() {
this[INNER_VM].pushFrame();
}
// Restore $ra, $sp and $fp
popFrame() {
this[INNER_VM].popFrame();
}
// Jump to an address in `program`
goto(offset) {
this[INNER_VM].goto(offset);
}
// Save $pc into $ra, then jump to a new address in `program` (jal in MIPS)
call(handle) {
this[INNER_VM].call(handle);
}
// Put a specific `program` address in $ra
returnTo(offset) {
this[INNER_VM].returnTo(offset);
}
// Return to the `program` address stored in $ra
return() {
this[INNER_VM].return();
}
/**
* End of migrated.
*/
constructor(runtime, {
pc: pc,
scope: scope,
dynamicScope: dynamicScope,
stack: stack
}, elementStack, context) {
this.runtime = runtime, this.elementStack = elementStack, this.context = context, isDevelopingApp() && assertGlobalContextWasSet(), this.resume = initVM(context);
let evalStack = EvaluationStackImpl.restore(stack);
debugAssert("number" == typeof pc, "pc is a number"), evalStack[REGISTERS][$pc] = pc, evalStack[REGISTERS][$sp] = stack.length - 1, evalStack[REGISTERS][$fp] = -1, this[HEAP] = this.program.heap, this[CONSTANTS] = this.program.constants, this.elementStack = elementStack, this[STACKS].scope.push(scope), this[STACKS].dynamicScope.push(dynamicScope), this[ARGS$1] = new VMArgumentsImpl(), this[INNER_VM] = new LowLevelVM(evalStack, this[HEAP], runtime.program, {
debugBefore: opcode => APPEND_OPCODES.debugBefore(this, opcode),
debugAfter: state => {
APPEND_OPCODES.debugAfter(this, state);
}
}, evalStack[REGISTERS]), this.destructor = {}, this[DESTROYABLE_STACK].push(this.destructor);
}
static initial(runtime, context, {
handle: handle,
self: self,
dynamicScope: dynamicScope,
treeBuilder: treeBuilder,
numSymbols: numSymbols,
owner: owner
}) {
let scope = PartialScopeImpl.root(self, numSymbols, owner),
state = vmState(runtime.program.heap.getaddr(handle), scope, dynamicScope),
vm = initVM(context)(runtime, state, treeBuilder);
return vm.pushUpdating(), vm;
}
static empty(runtime, {
handle: handle,
treeBuilder: treeBuilder,
dynamicScope: dynamicScope,
owner: owner
}, context) {
let vm = initVM(context)(runtime, vmState(runtime.program.heap.getaddr(handle), PartialScopeImpl.root(UNDEFINED_REFERENCE, 0, owner), dynamicScope), treeBuilder);
return vm.pushUpdating(), vm;
}
resume;
compile(block) {
return unwrapHandle(block.compile(this.context));
}
get program() {
return this.runtime.program;
}
get env() {
return this.runtime.env;
}
captureState(args, pc = this[INNER_VM].fetchRegister($pc)) {
return {
pc: pc,
scope: this.scope(),
dynamicScope: this.dynamicScope(),
stack: this.stack.capture(args)
};
}
capture(args, pc = this[INNER_VM].fetchRegister($pc)) {
return new ResumableVMStateImpl(this.captureState(args, pc), this.resume);
}
beginCacheGroup(name) {
let opcodes = this.updating(),
guard = new JumpIfNotModifiedOpcode();
opcodes.push(guard), opcodes.push(new BeginTrackFrameOpcode(name)), this[STACKS].cache.push(guard), beginTrackFrame(name);
}
commitCacheGroup() {
let opcodes = this.updating(),
guard = expect(this[STACKS].cache.pop(), "VM BUG: Expected a cache group"),
tag = endTrackFrame();
opcodes.push(new EndTrackFrameOpcode(guard)), guard.finalize(tag, opcodes.length);
}
enter(args) {
let state = this.capture(args),
block = this.elements().pushUpdatableBlock(),
tryOpcode = new TryOpcode(state, this.runtime, block, []);
this.didEnter(tryOpcode);
}
enterItem({
key: key,
value: value,
memo: memo
}) {
let {
stack: stack
} = this,
valueRef = createIteratorItemRef(value),
memoRef = createIteratorItemRef(memo);
stack.push(valueRef), stack.push(memoRef);
let state = this.capture(2),
block = this.elements().pushUpdatableBlock(),
opcode = new ListItemOpcode(state, this.runtime, block, key, memoRef, valueRef);
return this.didEnter(opcode), opcode;
}
registerItem(opcode) {
this.listBlock().initializeChild(opcode);
}
enterList(iterableRef, offset) {
let updating = [],
addr = this[INNER_VM].target(offset),
state = this.capture(0, addr),
list = this.elements().pushBlockList(updating),
opcode = new ListBlockOpcode(state, this.runtime, list, updating, iterableRef);
this[STACKS].list.push(opcode), this.didEnter(opcode);
}
didEnter(opcode) {
this.associateDestroyable(opcode), this[DESTROYABLE_STACK].push(opcode), this.updateWith(opcode), this.pushUpdating(opcode.children);
}
exit() {
this[DESTROYABLE_STACK].pop(), this.elements().popBlock(), this.popUpdating();
}
exitList() {
this.exit(), this[STACKS].list.pop();
}
pushUpdating(list = []) {
this[STACKS].updating.push(list);
}
popUpdating() {
return expect(this[STACKS].updating.pop(), "can't pop an empty stack");
}
updateWith(opcode) {
this.updating().push(opcode);
}
listBlock() {
return expect(this[STACKS].list.current, "expected a list block");
}
associateDestroyable(child) {
let parent = expect(this[DESTROYABLE_STACK].current, "Expected destructor parent");
associateDestroyableChild(parent, child);
}
tryUpdating() {
return this[STACKS].updating.current;
}
updating() {
return expect(this[STACKS].updating.current, "expected updating opcode on the updating opcode stack");
}
elements() {
return this.elementStack;
}
scope() {
return expect(this[STACKS].scope.current, "expected scope on the scope stack");
}
dynamicScope() {
return expect(this[STACKS].dynamicScope.current, "expected dynamic scope on the dynamic scope stack");
}
pushChildScope() {
this[STACKS].scope.push(this.scope().child());
}
pushDynamicScope() {
let child = this.dynamicScope().child();
return this[STACKS].dynamicScope.push(child), child;
}
pushRootScope(size, owner) {
let scope = PartialScopeImpl.sized(size, owner);
return this[STACKS].scope.push(scope), scope;
}
pushScope(scope) {
this[STACKS].scope.push(scope);
}
popScope() {
this[STACKS].scope.pop();
}
popDynamicScope() {
this[STACKS].dynamicScope.pop();
}
/// SCOPE HELPERS
getOwner() {
return this.scope().owner;
}
getSelf() {
return this.scope().getSelf();
}
referenceForSymbol(symbol) {
return this.scope().getSymbol(symbol);
}
/// EXECUTION
execute(initialize) {
if (!isDevelopingApp()) return this._execute(initialize);
{
let hasErrored = !0;
try {
let value = this._execute(initialize);
// using a boolean here to avoid breaking ergonomics of "pause on uncaught exceptions"
// which would happen with a `catch` + `throw`
return hasErrored = !1, value;
} finally {
if (hasErrored) {
// If any existing blocks are open, due to an error or something like
// that, we need to close them all and clean things up properly.
let elements = this.elements();
for (; elements.hasBlocks;) elements.popBlock();
// eslint-disable-next-line no-console
console.error(`\n\nError occurred:\n\n${resetTracking()}\n\n`);
}
}
}
}
_execute(initialize) {
let result;
initialize && initialize(this);
do {
result = this.next();
} while (!result.done);
return result.value;
}
next() {
let result,
{
env: env,
elementStack: elementStack
} = this,
opcode = this[INNER_VM].nextStatement();
return null !== opcode ? (this[INNER_VM].evaluateOuter(opcode, this), result = {
done: !1,
value: null
}) : (
// Unload the stack
this.stack.reset(), result = {
done: !0,
value: new RenderResultImpl(env, this.popUpdating(), elementStack.popBlock(), this.destructor)
}), result;
}
bindDynamicScope(names) {
let scope = this.dynamicScope();
for (const name of reverse(names)) scope.set(name, this.stack.pop());
}
}
function vmState(pc, scope, dynamicScope) {
return {
pc: pc,
scope: scope,
dynamicScope: dynamicScope,
stack: []
};
}
function initVM(context) {
return (runtime, state, builder) => new VM(runtime, state, builder, context);
}
class TemplateIteratorImpl {
constructor(vm) {
this.vm = vm;
}
next() {
return this.vm.next();
}
sync() {
return isDevelopingApp() ? debug.runInTrackingTransaction(() => this.vm.execute(), "- While rendering:") : this.vm.execute();
}
}
function renderSync(env, iterator) {
let result;
return inTransaction(env, () => result = iterator.sync()), result;
}
function renderMain(runtime, context, owner, self, treeBuilder, layout, dynamicScope = new DynamicScopeImpl()) {
let handle = unwrapHandle(layout.compile(context)),
numSymbols = layout.symbolTable.symbols.length,
vm = VM.initial(runtime, context, {
self: self,
dynamicScope: dynamicScope,
treeBuilder: treeBuilder,
handle: handle,
numSymbols: numSymbols,
owner: owner
});
return new TemplateIteratorImpl(vm);
}
function renderComponent(runtime, treeBuilder, context, owner, definition, args = {}, dynamicScope = new DynamicScopeImpl()) {
return function (vm, context, owner, definition, args) {
// Get a list of tuples of argument names and references, like
// [['title', reference], ['name', reference]]
const argList = Object.keys(args).map(key => [key, args[key]]),
blockNames = ["main", "else", "attrs"],
argNames = argList.map(([name]) => `@${name}`);
let reified = vm[CONSTANTS].component(definition, owner);
vm.pushFrame();
// Push blocks on to the stack, three stack values per block
for (let i = 0; i < 3 * blockNames.length; i++) vm.stack.push(null);
vm.stack.push(null),
// For each argument, push its backing reference on to the stack
argList.forEach(([, reference]) => {
vm.stack.push(reference);
}),
// Configure VM based on blocks and args just pushed on to the stack.
vm[ARGS$1].setup(vm.stack, argNames, blockNames, 0, !0);
const compilable = expect(reified.compilable, "BUG: Expected the root component rendered with renderComponent to have an associated template, set with setComponentTemplate"),
invocation = {
handle: unwrapHandle(compilable.compile(context)),
symbolTable: compilable.symbolTable
};
// Needed for the Op.Main opcode: arguments, component invocation object, and
// component definition.
return vm.stack.push(vm[ARGS$1]), vm.stack.push(invocation), vm.stack.push(reified), new TemplateIteratorImpl(vm);
}(VM.empty(runtime, {
treeBuilder: treeBuilder,
handle: context.stdlib.main,
dynamicScope: dynamicScope,
owner: owner
}, context), context, owner, definition, function (record) {
const root = createConstRef(record, "args");
return Object.keys(record).reduce((acc, key) => (acc[key] = childRefFor(root, key), acc), {});
}(args));
}
const SERIALIZATION_FIRST_NODE_STRING = "%+b:0%";
function isSerializationFirstNode(node) {
return "%+b:0%" === node.nodeValue;
}
class RehydratingCursor extends CursorImpl {
candidate = null;
openBlockDepth;
injectedOmittedNode = !1;
constructor(element, nextSibling, startingBlockDepth) {
super(element, nextSibling), this.startingBlockDepth = startingBlockDepth, this.openBlockDepth = startingBlockDepth - 1;
}
}
class RehydrateBuilder extends NewElementBuilder {
unmatchedAttributes = null;
// Hides property on base class
blockDepth = 0;
startingBlockOffset;
constructor(env, parentNode, nextSibling) {
if (super(env, parentNode, nextSibling), nextSibling) throw new Error("Rehydration with nextSibling not supported");
let node = this.currentCursor.element.firstChild;
for (; null !== node && !isOpenBlock(node);) node = node.nextSibling;
debugAssert(node, "Must have opening comment for rehydration."), this.candidate = node;
const startingBlockOffset = getBlockDepth(node);
if (0 !== startingBlockOffset) {
// We are rehydrating from a partial tree and not the root component
// We need to add an extra block before the first block to rehydrate correctly
// The extra block is needed since the renderComponent API creates a synthetic component invocation which generates the extra block
const newBlockDepth = startingBlockOffset - 1,
newCandidate = this.dom.createComment(`%+b:${newBlockDepth}%`);
node.parentNode.insertBefore(newCandidate, this.candidate);
let closingNode = node.nextSibling;
for (; null !== closingNode && (!isCloseBlock(closingNode) || getBlockDepth(closingNode) !== startingBlockOffset);) closingNode = closingNode.nextSibling;
debugAssert(closingNode, "Must have closing comment for starting block comment");
const newClosingBlock = this.dom.createComment(`%-b:${newBlockDepth}%`);
node.parentNode.insertBefore(newClosingBlock, closingNode.nextSibling), this.candidate = newCandidate, this.startingBlockOffset = newBlockDepth;
} else this.startingBlockOffset = 0;
}
get currentCursor() {
return this[CURSOR_STACK].current;
}
get candidate() {
return this.currentCursor ? this.currentCursor.candidate : null;
}
set candidate(node) {
this.currentCursor.candidate = node;
}
disableRehydration(nextSibling) {
const currentCursor = this.currentCursor;
// rehydration will be disabled until we either:
// * hit popElement (and return to using the parent elements cursor)
// * hit closeBlock and the next sibling is a close block comment
// matching the expected openBlockDepth
currentCursor.candidate = null, currentCursor.nextSibling = nextSibling;
}
enableRehydration(candidate) {
const currentCursor = this.currentCursor;
currentCursor.candidate = candidate, currentCursor.nextSibling = null;
}
pushElement(element, nextSibling = null) {
const cursor = new RehydratingCursor(element, nextSibling, this.blockDepth || 0);
/**
* <div> <--------------- currentCursor.element
* <!--%+b:1%--> <------- would have been removed during openBlock
* <div> <--------------- currentCursor.candidate -> cursor.element
* <!--%+b:2%--> <----- currentCursor.candidate.firstChild -> cursor.candidate
* Foo
* <!--%-b:2%-->
* </div>
* <!--%-b:1%--> <------ becomes currentCursor.candidate
*/
null !== this.candidate && (cursor.candidate = element.firstChild, this.candidate = element.nextSibling), this[CURSOR_STACK].push(cursor);
}
// clears until the end of the current container
// either the current open block or higher
clearMismatch(candidate) {
let current = candidate;
const currentCursor = this.currentCursor;
if (null !== currentCursor) {
const openBlockDepth = currentCursor.openBlockDepth;
if (openBlockDepth >= currentCursor.startingBlockDepth) for (; current && !(isCloseBlock(current) && openBlockDepth >= getBlockDepthWithOffset(current, this.startingBlockOffset));) current = this.remove(current);else for (; null !== current;) current = this.remove(current);
// current cursor parentNode should be openCandidate if element
// or openCandidate.parentNode if comment
this.disableRehydration(current);
}
}
__openBlock() {
const {
currentCursor: currentCursor
} = this;
if (null === currentCursor) return;
const blockDepth = this.blockDepth;
this.blockDepth++;
const {
candidate: candidate
} = currentCursor;
if (null === candidate) return;
const {
tagName: tagName
} = currentCursor.element;
isOpenBlock(candidate) && getBlockDepthWithOffset(candidate, this.startingBlockOffset) === blockDepth ? (this.candidate = this.remove(candidate), currentCursor.openBlockDepth = blockDepth) : "TITLE" !== tagName && "SCRIPT" !== tagName && "STYLE" !== tagName && this.clearMismatch(candidate);
}
__closeBlock() {
const {
currentCursor: currentCursor
} = this;
if (null === currentCursor) return;
// openBlock is the last rehydrated open block
const openBlockDepth = currentCursor.openBlockDepth;
// this currently is the expected next open block depth
this.blockDepth--;
const {
candidate: candidate
} = currentCursor;
let isRehydrating = !1;
if (null !== candidate)
//assert(
// openBlockDepth === this.blockDepth,
// 'when rehydrating, openBlockDepth should match this.blockDepth here'
//);
if (isRehydrating = !0, isCloseBlock(candidate) && getBlockDepthWithOffset(candidate, this.startingBlockOffset) === openBlockDepth) {
const nextSibling = this.remove(candidate);
this.candidate = nextSibling, currentCursor.openBlockDepth--;
} else
// close the block and clear mismatch in parent container
// we will be either at the end of the element
// or at the end of our containing block
this.clearMismatch(candidate), isRehydrating = !1;
if (!1 === isRehydrating) {
// check if nextSibling matches our expected close block
// if so, we remove the close block comment and
// restore rehydration after clearMismatch disabled
const nextSibling = currentCursor.nextSibling;
if (null !== nextSibling && isCloseBlock(nextSibling) && getBlockDepthWithOffset(nextSibling, this.startingBlockOffset) === this.blockDepth) {
// restore rehydration state
const candidate = this.remove(nextSibling);
this.enableRehydration(candidate), currentCursor.openBlockDepth--;
}
}
}
__appendNode(node) {
const {
candidate: candidate
} = this;
// This code path is only used when inserting precisely one node. It needs more
// comparison logic, but we can probably lean on the cases where this code path
// is actually used.
return candidate || super.__appendNode(node);
}
__appendHTML(html) {
const candidateBounds = this.markerBounds();
if (candidateBounds) {
const first = candidateBounds.firstNode(),
last = candidateBounds.lastNode(),
newBounds = new ConcreteBounds(this.element, first.nextSibling, last.previousSibling),
possibleEmptyMarker = this.remove(first);
return this.remove(last), null !== possibleEmptyMarker && isEmpty(possibleEmptyMarker) && (this.candidate = this.remove(possibleEmptyMarker), null !== this.candidate && this.clearMismatch(this.candidate)), newBounds;
}
return super.__appendHTML(html);
}
remove(node) {
const element = expect(node.parentNode, "cannot remove a detached node"),
next = node.nextSibling;
return element.removeChild(node), next;
}
markerBounds() {
const _candidate = this.candidate;
if (_candidate && isMarker(_candidate)) {
const first = _candidate;
let last = expect(first.nextSibling, "BUG: serialization markers must be paired");
for (; last && !isMarker(last);) last = expect(last.nextSibling, "BUG: serialization markers must be paired");
return new ConcreteBounds(this.element, first, last);
}
return null;
}
__appendText(string) {
const {
candidate: candidate
} = this;
return candidate ? 3 === candidate.nodeType ? (candidate.nodeValue !== string && (candidate.nodeValue = string), this.candidate = candidate.nextSibling, candidate) : 8 === (node = candidate).nodeType && "%|%" === node.nodeValue || isEmpty(candidate) && "" === string ? (this.candidate = this.remove(candidate), this.__appendText(string)) : (this.clearMismatch(candidate), super.__appendText(string)) : super.__appendText(string);
var node;
}
__appendComment(string) {
const _candidate = this.candidate;
return _candidate && 8 === _candidate.nodeType ? (_candidate.nodeValue !== string && (_candidate.nodeValue = string), this.candidate = _candidate.nextSibling, _candidate) : (_candidate && this.clearMismatch(_candidate), super.__appendComment(string));
}
__openElement(tag) {
const _candidate = this.candidate;
if (_candidate && isElement(_candidate) && function (candidate, tag) {
return candidate.namespaceURI === NS_SVG ? candidate.tagName === tag : candidate.tagName === tag.toUpperCase();
}(_candidate, tag)) return this.unmatchedAttributes = [].slice.call(_candidate.attributes), _candidate;
if (_candidate) {
if (isElement(_candidate) && "TBODY" === _candidate.tagName) return this.pushElement(_candidate, null), this.currentCursor.injectedOmittedNode = !0, this.__openElement(tag);
this.clearMismatch(_candidate);
}
return super.__openElement(tag);
}
__setAttribute(name, value, namespace) {
const unmatched = this.unmatchedAttributes;
if (unmatched) {
const attr = findByName(unmatched, name);
if (attr) return attr.value !== value && (attr.value = value), void unmatched.splice(unmatched.indexOf(attr), 1);
}
return super.__setAttribute(name, value, namespace);
}
__setProperty(name, value) {
const unmatched = this.unmatchedAttributes;
if (unmatched) {
const attr = findByName(unmatched, name);
if (attr) return attr.value !== value && (attr.value = value), void unmatched.splice(unmatched.indexOf(attr), 1);
}
return super.__setProperty(name, value);
}
__flushElement(parent, constructing) {
const {
unmatchedAttributes: unmatched
} = this;
if (unmatched) {
for (const attr of unmatched) this.constructing.removeAttribute(attr.name);
this.unmatchedAttributes = null;
} else super.__flushElement(parent, constructing);
}
willCloseElement() {
const {
candidate: candidate,
currentCursor: currentCursor
} = this;
null !== candidate && this.clearMismatch(candidate), currentCursor && currentCursor.injectedOmittedNode && this.popElement(), super.willCloseElement();
}
getMarker(element, guid) {
const marker = element.querySelector(`script[glmr="${guid}"]`);
return marker ? castToSimple(marker) : null;
}
__pushRemoteElement(element, cursorId, insertBefore) {
const marker = this.getMarker(castToBrowser(element, "HTML"), cursorId);
// when insertBefore is not present, we clear the element
if (debugAssert(!marker || marker.parentNode === element, "expected remote element marker's parent node to match remote element"), void 0 === insertBefore) {
for (; null !== element.firstChild && element.firstChild !== marker;) this.remove(element.firstChild);
insertBefore = null;
}
const cursor = new RehydratingCursor(element, null, this.blockDepth);
this[CURSOR_STACK].push(cursor), null === marker ? this.disableRehydration(insertBefore) : this.candidate = this.remove(marker);
const block = new RemoteLiveBlock(element);
return this.pushLiveBlock(block, !0);
}
didAppendBounds(bounds) {
if (super.didAppendBounds(bounds), this.candidate) {
const last = bounds.lastNode();
this.candidate = last && last.nextSibling;
}
return bounds;
}
}
function isOpenBlock(node) {
return node.nodeType === COMMENT_NODE && 0 === node.nodeValue.lastIndexOf("%+b:", 0);
}
function isCloseBlock(node) {
return node.nodeType === COMMENT_NODE && 0 === node.nodeValue.lastIndexOf("%-b:", 0);
}
function getBlockDepth(node) {
return parseInt(node.nodeValue.slice(4), 10);
}
function getBlockDepthWithOffset(node, offset) {
return getBlockDepth(node) - offset;
}
function isElement(node) {
return 1 === node.nodeType;
}
function isMarker(node) {
return 8 === node.nodeType && "%glmr%" === node.nodeValue;
}
function isEmpty(node) {
return 8 === node.nodeType && "% %" === node.nodeValue;
}
function findByName(array, name) {
for (const attr of array) if (attr.name === name) return attr;
}
function rehydrationBuilder(env, cursor) {
return RehydrateBuilder.forInitialRender(env, cursor);
}
export { ConcreteBounds, CurriedValue, CursorImpl, DOMChanges, DOMTreeConstruction, DynamicAttribute, DynamicScopeImpl, EMPTY_ARGS, EMPTY_NAMED, EMPTY_POSITIONAL, EnvironmentImpl, DOMChangesImpl as IDOMChanges, VM as LowLevelVM, NewElementBuilder, PartialScopeImpl, RehydrateBuilder, RemoteLiveBlock, SERIALIZATION_FIRST_NODE_STRING, SimpleDynamicAttribute, TEMPLATE_ONLY_COMPONENT_MANAGER, TemplateOnlyComponentDefinition as TemplateOnlyComponent, TemplateOnlyComponentManager, UpdatableBlockImpl, UpdatingVM, array, clear, clientBuilder, concat, createCapturedArgs, curry, destroy, dynamicAttribute, fn, get, hash, inTransaction, invokeHelper, isDestroyed, isDestroying, isSerializationFirstNode, isWhitespace, normalizeProperty, on, registerDestructor, rehydrationBuilder, reifyArgs, reifyNamed, reifyPositional, renderComponent, renderMain, renderSync, resetDebuggerCallback, runtimeContext, setDebuggerCallback, templateOnlyComponent };