Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUGFIX] Grow the Uint16Array if the page size overflows 1MB #798

Merged
merged 4 commits into from
Apr 16, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
});