From 14759c157151cfa9da265ebf2d1ea0faa158d3d6 Mon Sep 17 00:00:00 2001 From: Chad Hietala Date: Thu, 12 Apr 2018 08:42:21 -0400 Subject: [PATCH 1/4] [BUGFIX] Grow the Uint16Array if the page size overflows 1MB --- packages/@glimmer/program/lib/program.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/packages/@glimmer/program/lib/program.ts b/packages/@glimmer/program/lib/program.ts index dc7226143..3521b337d 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 @@ -63,6 +65,7 @@ export class Heap implements CompileTimeHeap { private table: number[]; private offset = 0; private handle = 0; + private pageSize = 0; constructor(serializedHeap?: SerializedHeap) { if (serializedHeap) { @@ -72,13 +75,24 @@ export class Heap implements CompileTimeHeap { this.offset = this.heap.length; this.handle = handle; } else { - this.heap = new Uint16Array(0x100000); + this.heap = new Uint16Array(PAGE_SIZE); this.table = []; } } push(item: number): void { this.heap[this.offset++] = item; + this.pageSize++; + if (this.pageSize === PAGE_SIZE) { + this.grow(); + } + } + + private grow() { + let heap = this.heap.slice(0, this.pageSize); + this.heap = new Uint16Array(heap.length + PAGE_SIZE); + this.heap.set(heap); + this.pageSize = 0; } getbyaddr(address: number): number { From 6b8fdedb702ecdf19a1b9d0e2ba69fd740a4925b Mon Sep 17 00:00:00 2001 From: Chad Hietala Date: Thu, 12 Apr 2018 09:35:28 -0400 Subject: [PATCH 2/4] Add test --- packages/@glimmer/program/lib/program.ts | 48 ++++++++++----------- packages/@glimmer/program/test/heap-test.ts | 32 ++++++++++++++ 2 files changed, 55 insertions(+), 25 deletions(-) create mode 100644 packages/@glimmer/program/test/heap-test.ts diff --git a/packages/@glimmer/program/lib/program.ts b/packages/@glimmer/program/lib/program.ts index 3521b337d..489ef7685 100644 --- a/packages/@glimmer/program/lib/program.ts +++ b/packages/@glimmer/program/lib/program.ts @@ -60,12 +60,12 @@ const PAGE_SIZE = 0x100000; * 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 pageSize = 0; + private capacity = PAGE_SIZE; constructor(serializedHeap?: SerializedHeap) { if (serializedHeap) { @@ -74,6 +74,7 @@ export class Heap implements CompileTimeHeap { this.table = table; this.offset = this.heap.length; this.handle = handle; + this.capacity = 0; } else { this.heap = new Uint16Array(PAGE_SIZE); this.table = []; @@ -81,18 +82,18 @@ export class Heap implements CompileTimeHeap { } push(item: number): void { + this.sizeCheck(); this.heap[this.offset++] = item; - this.pageSize++; - if (this.pageSize === PAGE_SIZE) { - this.grow(); - } } - private grow() { - let heap = this.heap.slice(0, this.pageSize); - this.heap = new Uint16Array(heap.length + PAGE_SIZE); - this.heap.set(heap); - this.pageSize = 0; + 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 { @@ -194,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]); @@ -210,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, @@ -264,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..8fa82e17b --- /dev/null +++ b/packages/@glimmer/program/test/heap-test.ts @@ -0,0 +1,32 @@ +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); + assert.equal(serializedHeap.length, size * 2); + assert.equal(serializedHeap[size], 11); +}); From eddb9321450e5b1660dc204ae9166e08a7382887 Mon Sep 17 00:00:00 2001 From: Chad Hietala Date: Fri, 13 Apr 2018 10:45:58 -0400 Subject: [PATCH 3/4] Check byteLength --- packages/@glimmer/program/test/heap-test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/@glimmer/program/test/heap-test.ts b/packages/@glimmer/program/test/heap-test.ts index 8fa82e17b..cee4ceb6d 100644 --- a/packages/@glimmer/program/test/heap-test.ts +++ b/packages/@glimmer/program/test/heap-test.ts @@ -20,13 +20,13 @@ QUnit.test('Can grow', (assert) => { // 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.byteLength, size * 2); assert.equal(serializedHeap[size - 1], 10); heap.push(11); serialized = heap.capture(Number.MAX_SAFE_INTEGER); serializedHeap = new Uint16Array(serialized.buffer); - assert.equal(serializedHeap.length, size * 2); + assert.equal(serializedHeap.byteLength, size * 4); assert.equal(serializedHeap[size], 11); }); From f628372245a08c8876353df10d0f3c8ea1bab6c5 Mon Sep 17 00:00:00 2001 From: Chad Hietala Date: Fri, 13 Apr 2018 11:04:19 -0400 Subject: [PATCH 4/4] Check for ie11 --- packages/@glimmer/program/test/heap-test.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/@glimmer/program/test/heap-test.ts b/packages/@glimmer/program/test/heap-test.ts index cee4ceb6d..cf47d5b29 100644 --- a/packages/@glimmer/program/test/heap-test.ts +++ b/packages/@glimmer/program/test/heap-test.ts @@ -20,13 +20,19 @@ QUnit.test('Can grow', (assert) => { // we get the whole thing out let serialized = heap.capture(Number.MAX_SAFE_INTEGER); let serializedHeap = new Uint16Array(serialized.buffer); - assert.equal(serializedHeap.byteLength, size * 2); + 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); - assert.equal(serializedHeap.byteLength, size * 4); + + 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); });