diff --git a/packages/@glimmer/program/lib/program.ts b/packages/@glimmer/program/lib/program.ts index dc7226143..489ef7685 100644 --- a/packages/@glimmer/program/lib/program.ts +++ b/packages/@glimmer/program/lib/program.ts @@ -37,6 +37,8 @@ export interface SerializedHeap { export type Placeholder = [number, () => number]; +const PAGE_SIZE = 0x100000; + /** * The Heap is responsible for dynamically allocating * memory in which we read/write the VM's instructions @@ -58,11 +60,12 @@ export type Placeholder = [number, () => number]; * over them as you will have a bad memory access exception. */ export class Heap implements CompileTimeHeap { - private heap: Uint16Array | Array; + private heap: Uint16Array; private placeholders: Placeholder[] = []; private table: number[]; private offset = 0; private handle = 0; + private capacity = PAGE_SIZE; constructor(serializedHeap?: SerializedHeap) { if (serializedHeap) { @@ -71,16 +74,28 @@ export class Heap implements CompileTimeHeap { this.table = table; this.offset = this.heap.length; this.handle = handle; + this.capacity = 0; } else { - this.heap = new Uint16Array(0x100000); + this.heap = new Uint16Array(PAGE_SIZE); this.table = []; } } push(item: number): void { + this.sizeCheck(); this.heap[this.offset++] = item; } + private sizeCheck() { + if (this.capacity === 0) { + let heap = slice(this.heap, 0, this.offset); + this.heap = new Uint16Array(heap.length + PAGE_SIZE); + this.heap.set(heap, 0); + this.capacity = PAGE_SIZE; + } + this.capacity--; + } + getbyaddr(address: number): number { return this.heap[address]; } @@ -180,6 +195,7 @@ export class Heap implements CompileTimeHeap { } pushPlaceholder(valueFunc: () => number): void { + this.sizeCheck(); let address = this.offset++; this.heap[address] = Size.MAX_SIZE; this.placeholders.push([address, valueFunc]); @@ -196,11 +212,11 @@ export class Heap implements CompileTimeHeap { } } - capture(): SerializedHeap { + capture(offset = this.offset): SerializedHeap { this.patchPlaceholders(); // Only called in eager mode - let buffer = slice(this.heap, 0, this.offset); + let buffer = slice(this.heap, 0, offset).buffer; return { handle: this.handle, table: this.table, @@ -250,20 +266,16 @@ export class Program extends WriteOnlyProgram { public constants: Constants; } -function slice(arr: Uint16Array | number[], start: number, end: number) { - if (arr instanceof Uint16Array) { - if (arr.slice !== undefined) { - return arr.slice(start, end).buffer; - } - - let ret = new Uint16Array(end); +function slice(arr: Uint16Array, start: number, end: number): Uint16Array { + if (arr.slice !== undefined) { + return arr.slice(start, end); + } - for (; start < end; start++) { - ret[start] = arr[start]; - } + let ret = new Uint16Array(end); - return ret.buffer; + for (; start < end; start++) { + ret[start] = arr[start]; } - return null; + return ret; } diff --git a/packages/@glimmer/program/test/heap-test.ts b/packages/@glimmer/program/test/heap-test.ts new file mode 100644 index 000000000..cf47d5b29 --- /dev/null +++ b/packages/@glimmer/program/test/heap-test.ts @@ -0,0 +1,38 @@ +import { Heap } from '@glimmer/program'; + +QUnit.module('Heap'); + +QUnit.test('Can grow', (assert) => { + let size = 0x100000; + let heap = new Heap(); + + let i = 0; + + while (i !== (size - 1)) { + heap.push(1); + i++; + } + + // Should grow here + heap.push(10); + + // Slices the buffer. Passing MAX_SAFE_INTEGER ensures + // we get the whole thing out + let serialized = heap.capture(Number.MAX_SAFE_INTEGER); + let serializedHeap = new Uint16Array(serialized.buffer); + assert.equal(serializedHeap.length, size); + assert.equal(serializedHeap[size - 1], 10); + + heap.push(11); + + serialized = heap.capture(Number.MAX_SAFE_INTEGER); + serializedHeap = new Uint16Array(serialized.buffer); + + if (serializedHeap.slice) { + assert.equal(serializedHeap.length, size * 2); + } else { + // IE11 only gives you a buffer with residents in the slots + assert.equal(serializedHeap.length, size + 1); + } + assert.equal(serializedHeap[size], 11); +});