Skip to content

Commit

Permalink
Merge pull request #798 from glimmerjs/fix-array-growth
Browse files Browse the repository at this point in the history
[BUGFIX] Grow the Uint16Array if the page size overflows 1MB
  • Loading branch information
rwjblue committed Apr 16, 2018
2 parents f62132c + f628372 commit a28b777
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 16 deletions.
44 changes: 28 additions & 16 deletions packages/@glimmer/program/lib/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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<number>;
private heap: Uint16Array;
private placeholders: Placeholder[] = [];
private table: number[];
private offset = 0;
private handle = 0;
private capacity = PAGE_SIZE;

constructor(serializedHeap?: SerializedHeap) {
if (serializedHeap) {
Expand All @@ -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];
}
Expand Down Expand Up @@ -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]);
Expand All @@ -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,
Expand Down Expand Up @@ -250,20 +266,16 @@ export class Program<Locator> extends WriteOnlyProgram {
public constants: Constants<Locator>;
}

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;
}
38 changes: 38 additions & 0 deletions packages/@glimmer/program/test/heap-test.ts
Original file line number Diff line number Diff line change
@@ -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);
});

0 comments on commit a28b777

Please sign in to comment.