ember-legacy-class-transform
Version:
The default blueprint for ember-cli addons.
337 lines (336 loc) • 10.4 kB
JavaScript
import { populateBuiltins } from './syntax/functions';
import { Constants } from './environment/constants';
import { UNDEFINED_REFERENCE, ConditionalReference } from './references';
import { defaultManagers } from './dom/attribute-managers';
import { assert, ensureGuid, expect } from '@glimmer/util';
export class Scope {
constructor(
// the 0th slot is `self`
slots, 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.callerScope = callerScope;
this.evalScope = evalScope;
this.partialMap = partialMap;
}
static root(self, size = 0) {
let refs = new Array(size + 1);
for (let i = 0; i <= size; i++) {
refs[i] = UNDEFINED_REFERENCE;
}
return new Scope(refs, null, null, null).init({ self });
}
static sized(size = 0) {
let refs = new Array(size + 1);
for (let i = 0; i <= size; i++) {
refs[i] = UNDEFINED_REFERENCE;
}
return new Scope(refs, null, null, null);
}
init({ self }) {
this.slots[0] = self;
return this;
}
getSelf() {
return this.get(0);
}
getSymbol(symbol) {
return this.get(symbol);
}
getBlock(symbol) {
return this.get(symbol);
}
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 Scope(this.slots.slice(), 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;
}
}
class Transaction {
constructor() {
this.scheduledInstallManagers = [];
this.scheduledInstallModifiers = [];
this.scheduledUpdateModifierManagers = [];
this.scheduledUpdateModifiers = [];
this.createdComponents = [];
this.createdManagers = [];
this.updatedComponents = [];
this.updatedManagers = [];
this.destructors = [];
}
didCreate(component, manager) {
this.createdComponents.push(component);
this.createdManagers.push(manager);
}
didUpdate(component, manager) {
this.updatedComponents.push(component);
this.updatedManagers.push(manager);
}
scheduleInstallModifier(modifier, manager) {
this.scheduledInstallManagers.push(manager);
this.scheduledInstallModifiers.push(modifier);
}
scheduleUpdateModifier(modifier, manager) {
this.scheduledUpdateModifierManagers.push(manager);
this.scheduledUpdateModifiers.push(modifier);
}
didDestroy(d) {
this.destructors.push(d);
}
commit() {
let { createdComponents, createdManagers } = this;
for (let i = 0; i < createdComponents.length; i++) {
let component = createdComponents[i];
let manager = createdManagers[i];
manager.didCreate(component);
}
let { updatedComponents, updatedManagers } = this;
for (let i = 0; i < updatedComponents.length; i++) {
let component = updatedComponents[i];
let manager = updatedManagers[i];
manager.didUpdate(component);
}
let { destructors } = this;
for (let i = 0; i < destructors.length; i++) {
destructors[i].destroy();
}
let { scheduledInstallManagers, scheduledInstallModifiers } = this;
for (let i = 0; i < scheduledInstallManagers.length; i++) {
let manager = scheduledInstallManagers[i];
let modifier = scheduledInstallModifiers[i];
manager.install(modifier);
}
let { scheduledUpdateModifierManagers, scheduledUpdateModifiers } = this;
for (let i = 0; i < scheduledUpdateModifierManagers.length; i++) {
let manager = scheduledUpdateModifierManagers[i];
let modifier = scheduledUpdateModifiers[i];
manager.update(modifier);
}
}
}
export class Opcode {
constructor(heap) {
this.heap = heap;
this.offset = 0;
}
get type() {
return this.heap.getbyaddr(this.offset);
}
get op1() {
return this.heap.getbyaddr(this.offset + 1);
}
get op2() {
return this.heap.getbyaddr(this.offset + 2);
}
get op3() {
return this.heap.getbyaddr(this.offset + 3);
}
}
var TableSlotState;
(function (TableSlotState) {
TableSlotState[TableSlotState["Allocated"] = 0] = "Allocated";
TableSlotState[TableSlotState["Freed"] = 1] = "Freed";
TableSlotState[TableSlotState["Purged"] = 2] = "Purged";
TableSlotState[TableSlotState["Pointer"] = 3] = "Pointer";
})(TableSlotState || (TableSlotState = {}));
export class Heap {
constructor() {
this.heap = [];
this.offset = 0;
this.handle = 0;
/**
* layout:
*
* - pointer into heap
* - size
* - freed (0 or 1)
*/
this.table = [];
}
push(item) {
this.heap[this.offset++] = item;
}
getbyaddr(address) {
return this.heap[address];
}
setbyaddr(address, value) {
this.heap[address] = value;
}
malloc() {
this.table.push(this.offset, 0, 0);
let handle = this.handle;
this.handle += 3;
return handle;
}
finishMalloc(handle) {
let start = this.table[handle];
let finish = this.offset;
this.table[handle + 1] = finish - start;
}
size() {
return this.offset;
}
// It is illegal to close over this address, as compaction
// may move it. However, it is legal to use this address
// multiple times between compactions.
getaddr(handle) {
return this.table[handle];
}
gethandle(address) {
this.table.push(address, 0, TableSlotState.Pointer);
let handle = this.handle;
this.handle += 3;
return handle;
}
sizeof(handle) {
if (false) {
return this.table[handle + 1];
}
return -1;
}
free(handle) {
this.table[handle + 2] = 1;
}
compact() {
let compactedSize = 0;
let { table, table: { length }, heap } = this;
for (let i = 0; i < length; i += 3) {
let offset = table[i];
let size = table[i + 1];
let state = table[i + 2];
if (state === TableSlotState.Purged) {
continue;
} else if (state === TableSlotState.Freed) {
// transition to "already freed"
// a good improvement would be to reuse
// these slots
table[i + 2] = 2;
compactedSize += size;
} else if (state === TableSlotState.Allocated) {
for (let j = offset; j <= i + size; j++) {
heap[j - compactedSize] = heap[j];
}
table[i] = offset - compactedSize;
} else if (state === TableSlotState.Pointer) {
table[i] = offset - compactedSize;
}
}
this.offset = this.offset - compactedSize;
}
}
export class Program {
constructor() {
this.heap = new Heap();
this._opcode = new Opcode(this.heap);
this.constants = new Constants();
}
opcode(offset) {
this._opcode.offset = offset;
return this._opcode;
}
}
export class Environment {
constructor({ appendOperations, updateOperations }) {
this._macros = null;
this._transaction = null;
this.program = new Program();
this.appendOperations = appendOperations;
this.updateOperations = updateOperations;
}
toConditionalReference(reference) {
return new ConditionalReference(reference);
}
getAppendOperations() {
return this.appendOperations;
}
getDOM() {
return this.updateOperations;
}
getIdentity(object) {
return ensureGuid(object) + '';
}
begin() {
assert(!this._transaction, 'a glimmer transaction was begun, but one already exists. You may have a nested transaction');
this._transaction = new Transaction();
}
get transaction() {
return expect(this._transaction, 'must be in a transaction');
}
didCreate(component, manager) {
this.transaction.didCreate(component, manager);
}
didUpdate(component, manager) {
this.transaction.didUpdate(component, manager);
}
scheduleInstallModifier(modifier, manager) {
this.transaction.scheduleInstallModifier(modifier, manager);
}
scheduleUpdateModifier(modifier, manager) {
this.transaction.scheduleUpdateModifier(modifier, manager);
}
didDestroy(d) {
this.transaction.didDestroy(d);
}
commit() {
let transaction = this.transaction;
this._transaction = null;
transaction.commit();
}
attributeFor(element, attr, isTrusting, namespace) {
return defaultManagers(element, attr, isTrusting, namespace === undefined ? null : namespace);
}
macros() {
let macros = this._macros;
if (!macros) {
this._macros = macros = this.populateBuiltins();
}
return macros;
}
populateBuiltins() {
return populateBuiltins();
}
}
export default Environment;