UNPKG

ember-source

Version:

A JavaScript framework for creating ambitious web applications

180 lines (173 loc) 4.85 kB
import './debug-to-string-BsFOvUtQ.js'; import { u as unwrap } from './collections-B8me-ZlQ.js'; import '@embroider/macros'; import { O as OPERAND_LEN_MASK, A as ARG_SHIFT, a as MACHINE_MASK, b as TYPE_MASK } from './flags-BsZlvEeR.js'; class RuntimeOpImpl { offset = 0; constructor(heap) { this.heap = heap; } get size() { let rawType = this.heap.getbyaddr(this.offset); return ((rawType & OPERAND_LEN_MASK) >> ARG_SHIFT) + 1; } get isMachine() { let rawType = this.heap.getbyaddr(this.offset); return rawType & MACHINE_MASK ? 1 : 0; } get type() { return this.heap.getbyaddr(this.offset) & TYPE_MASK; } 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); } } const ALLOCATED = 0; const FREED = 1; const PURGED = 2; const POINTER = 3; const PAGE_SIZE = 0x100000; /** * The Program Heap is responsible for dynamically allocating * memory in which we read/write the VM's instructions * from/to. When we malloc we pass out a VMHandle, which * is used as an indirect way of accessing the memory during * execution of the VM. Internally we track the different * regions of the memory in an int array known as the table. * * The table 32-bit aligned and has the following layout: * * | ... | hp (u32) | info (u32) | size (u32) | * | ... | Handle | Scope Size | State | Size | * | ... | 32bits | 30bits | 2bits | 32bit | * * With this information we effectively have the ability to * control when we want to free memory. That being said you * can not free during execution as raw address are only * valid during the execution. This means you cannot close * over them as you will have a bad memory access exception. */ class ProgramHeapImpl { offset = 0; heap; handleTable; handleState; handle = 0; constructor() { this.heap = new Int32Array(PAGE_SIZE); this.handleTable = []; this.handleState = []; } entries() { return this.offset; } pushRaw(value) { this.sizeCheck(); this.heap[this.offset++] = value; } pushOp(item) { this.pushRaw(item); } pushMachine(item) { this.pushRaw(item | MACHINE_MASK); } sizeCheck() { let { heap } = this; if (this.offset === this.heap.length) { let newHeap = new Int32Array(heap.length + PAGE_SIZE); newHeap.set(heap, 0); this.heap = newHeap; } } getbyaddr(address) { return unwrap(this.heap[address]); } setbyaddr(address, value) { this.heap[address] = value; } malloc() { // push offset, info, size this.handleTable.push(this.offset); return this.handleTable.length - 1; } finishMalloc(handle) { } 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 unwrap(this.handleTable[handle]); } sizeof(handle) { return sizeof(this.handleTable); } free(handle) { this.handleState[handle] = FREED; } /** * The heap uses the [Mark-Compact Algorithm](https://en.wikipedia.org/wiki/Mark-compact_algorithm) to shift * reachable memory to the bottom of the heap and freeable * memory to the top of the heap. When we have shifted all * the reachable memory to the top of the heap, we move the * offset to the next free position. */ compact() { let compactedSize = 0; let { handleTable, handleState, heap } = this; for (let i = 0; i < length; i++) { let offset = unwrap(handleTable[i]); let size = unwrap(handleTable[i + 1]) - unwrap(offset); let state = handleState[i]; if (state === PURGED) { continue; } else if (state === FREED) { // transition to "already freed" aka "purged" // a good improvement would be to reuse // these slots handleState[i] = PURGED; compactedSize += size; } else if (state === ALLOCATED) { for (let j = offset; j <= i + size; j++) { heap[j - compactedSize] = unwrap(heap[j]); } handleTable[i] = offset - compactedSize; } else if (state === POINTER) { handleTable[i] = offset - compactedSize; } } this.offset = this.offset - compactedSize; } } class ProgramImpl { _opcode; constructor(constants, heap) { this.constants = constants; this.heap = heap; this._opcode = new RuntimeOpImpl(this.heap); } opcode(offset) { this._opcode.offset = offset; return this._opcode; } } function sizeof(table, handle) { { return -1; } } export { ProgramHeapImpl as P, RuntimeOpImpl as R, ProgramImpl as a };