ember-app-scheduler
Version:
Ember addon to schedule work at different phases of app life cycle.
211 lines • 6.82 kB
JavaScript
import { CONSTANT_TAG, isConst, isModified, ReferenceCache, ConstReference } from '@glimmer/reference';
import { initializeGuid } from '@glimmer/util';
import { APPEND_OPCODES, UpdatingOpcode } from '../../opcodes';
import { FALSE_REFERENCE, NULL_REFERENCE, PrimitiveReference, TRUE_REFERENCE, UNDEFINED_REFERENCE } from '../../references';
APPEND_OPCODES.add(20 /* ChildScope */, vm => vm.pushChildScope());
APPEND_OPCODES.add(21 /* PopScope */, vm => vm.popScope());
APPEND_OPCODES.add(39 /* PushDynamicScope */, vm => vm.pushDynamicScope());
APPEND_OPCODES.add(40 /* PopDynamicScope */, vm => vm.popDynamicScope());
APPEND_OPCODES.add(12 /* Immediate */, (vm, { op1: number }) => {
vm.stack.push(number);
});
APPEND_OPCODES.add(13 /* Constant */, (vm, { op1: other }) => {
vm.stack.push(vm.constants.getOther(other));
});
APPEND_OPCODES.add(14 /* PrimitiveReference */, (vm, { op1: primitive }) => {
let stack = vm.stack;
let flag = (primitive & 3 << 30) >>> 30;
let value = primitive & ~(3 << 30);
switch (flag) {
case 0:
stack.push(PrimitiveReference.create(value));
break;
case 1:
stack.push(PrimitiveReference.create(vm.constants.getFloat(value)));
break;
case 2:
stack.push(PrimitiveReference.create(vm.constants.getString(value)));
break;
case 3:
switch (value) {
case 0:
stack.push(FALSE_REFERENCE);
break;
case 1:
stack.push(TRUE_REFERENCE);
break;
case 2:
stack.push(NULL_REFERENCE);
break;
case 3:
stack.push(UNDEFINED_REFERENCE);
break;
}
break;
}
});
APPEND_OPCODES.add(15 /* Dup */, (vm, { op1: register, op2: offset }) => {
let position = vm.fetchValue(register) - offset;
vm.stack.dup(position);
});
APPEND_OPCODES.add(16 /* Pop */, (vm, { op1: count }) => vm.stack.pop(count));
APPEND_OPCODES.add(17 /* Load */, (vm, { op1: register }) => vm.load(register));
APPEND_OPCODES.add(18 /* Fetch */, (vm, { op1: register }) => vm.fetch(register));
APPEND_OPCODES.add(38 /* BindDynamicScope */, (vm, { op1: _names }) => {
let names = vm.constants.getArray(_names);
vm.bindDynamicScope(names);
});
APPEND_OPCODES.add(47 /* PushFrame */, vm => vm.pushFrame());
APPEND_OPCODES.add(48 /* PopFrame */, vm => vm.popFrame());
APPEND_OPCODES.add(49 /* Enter */, (vm, { op1: args }) => vm.enter(args));
APPEND_OPCODES.add(50 /* Exit */, vm => vm.exit());
APPEND_OPCODES.add(41 /* CompileDynamicBlock */, vm => {
let stack = vm.stack;
let block = stack.pop();
stack.push(block ? block.compileDynamic(vm.env) : null);
});
APPEND_OPCODES.add(42 /* InvokeStatic */, (vm, { op1: _block }) => {
let block = vm.constants.getBlock(_block);
let compiled = block.compileStatic(vm.env);
vm.call(compiled.handle);
});
APPEND_OPCODES.add(43 /* InvokeDynamic */, (vm, { op1: _invoker }) => {
let invoker = vm.constants.getOther(_invoker);
let block = vm.stack.pop();
invoker.invoke(vm, block);
});
APPEND_OPCODES.add(44 /* Jump */, (vm, { op1: target }) => vm.goto(target));
APPEND_OPCODES.add(45 /* JumpIf */, (vm, { op1: target }) => {
let reference = vm.stack.pop();
if (isConst(reference)) {
if (reference.value()) {
vm.goto(target);
}
} else {
let cache = new ReferenceCache(reference);
if (cache.peek()) {
vm.goto(target);
}
vm.updateWith(new Assert(cache));
}
});
APPEND_OPCODES.add(46 /* JumpUnless */, (vm, { op1: target }) => {
let reference = vm.stack.pop();
if (isConst(reference)) {
if (!reference.value()) {
vm.goto(target);
}
} else {
let cache = new ReferenceCache(reference);
if (!cache.peek()) {
vm.goto(target);
}
vm.updateWith(new Assert(cache));
}
});
APPEND_OPCODES.add(22 /* Return */, vm => vm.return());
APPEND_OPCODES.add(23 /* ReturnTo */, (vm, { op1: relative }) => {
vm.returnTo(relative);
});
export const ConstTest = function (ref, _env) {
return new ConstReference(!!ref.value());
};
export const SimpleTest = function (ref, _env) {
return ref;
};
export const EnvironmentTest = function (ref, env) {
return env.toConditionalReference(ref);
};
APPEND_OPCODES.add(51 /* Test */, (vm, { op1: _func }) => {
let stack = vm.stack;
let operand = stack.pop();
let func = vm.constants.getFunction(_func);
stack.push(func(operand, vm.env));
});
export class Assert extends UpdatingOpcode {
constructor(cache) {
super();
this.type = 'assert';
this.tag = cache.tag;
this.cache = cache;
}
evaluate(vm) {
let { cache } = this;
if (isModified(cache.revalidate())) {
vm.throw();
}
}
toJSON() {
let { type, _guid, cache } = this;
let expected;
try {
expected = JSON.stringify(cache.peek());
} catch (e) {
expected = String(cache.peek());
}
return {
args: [],
details: { expected },
guid: _guid,
type
};
}
}
export class JumpIfNotModifiedOpcode extends UpdatingOpcode {
constructor(tag, target) {
super();
this.target = target;
this.type = 'jump-if-not-modified';
this.tag = tag;
this.lastRevision = tag.value();
}
evaluate(vm) {
let { tag, target, lastRevision } = this;
if (!vm.alwaysRevalidate && tag.validate(lastRevision)) {
vm.goto(target);
}
}
didModify() {
this.lastRevision = this.tag.value();
}
toJSON() {
return {
args: [JSON.stringify(this.target.inspect())],
guid: this._guid,
type: this.type
};
}
}
export class DidModifyOpcode extends UpdatingOpcode {
constructor(target) {
super();
this.target = target;
this.type = 'did-modify';
this.tag = CONSTANT_TAG;
}
evaluate() {
this.target.didModify();
}
}
export class LabelOpcode {
constructor(label) {
this.tag = CONSTANT_TAG;
this.type = 'label';
this.label = null;
this.prev = null;
this.next = null;
initializeGuid(this);
this.label = label;
}
evaluate() {}
inspect() {
return `${this.label} [${this._guid}]`;
}
toJSON() {
return {
args: [JSON.stringify(this.inspect())],
guid: this._guid,
type: this.type
};
}
}