Skip to content

Commit 9731958

Browse files
committed
Implement a mechanism to realloc array buffers; Trap when trying to allocate more than max size; Test allocators in CI
1 parent dcc0e28 commit 9731958

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+2849
-1962
lines changed

.travis.yml

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ language: node_js
22
notifications:
33
email: false
44
stages:
5-
- name: check-pr
6-
if: type = pull_request
5+
- name: check-pr
6+
if: type = pull_request
77
jobs:
88
include:
99

@@ -24,6 +24,13 @@ jobs:
2424
- node_js: node
2525
script: npm run clean && node bin/asc -v && npm test
2626
env: Tests the sources on latest stable node.js
27+
- node_js: lts/*
28+
script:
29+
- npm run clean
30+
- cd $TRAVIS_BUILD_DIR/tests/allocators/arena && npm run build && cd .. && npm test arena
31+
- cd $TRAVIS_BUILD_DIR/tests/allocators/buddy && npm run build && cd .. && npm test buddy
32+
- cd $TRAVIS_BUILD_DIR/tests/allocators/tlsf && npm run build && cd .. && npm test tlsf
33+
env: Tests the allocators on latest node.js LTS
2734

2835
- stage: build
2936
node_js: lts/*

bin/asc.js

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -279,10 +279,12 @@ exports.main = function main(argv, options, callback) {
279279
sourceText = readFile(path.join(dir, plainName + ".ts"));
280280
if (sourceText !== null) {
281281
sourcePath = exports.libraryPrefix + plainName + ".ts";
282+
break;
282283
} else {
283284
sourceText = readFile(path.join(dir, indexName + ".ts"));
284285
if (sourceText !== null) {
285286
sourcePath = exports.libraryPrefix + indexName + ".ts";
287+
break;
286288
}
287289
}
288290
}
@@ -312,10 +314,12 @@ exports.main = function main(argv, options, callback) {
312314
sourceText = readFile(path.join(dir, plainName + ".ts"));
313315
if (sourceText !== null) {
314316
sourcePath = exports.libraryPrefix + plainName + ".ts";
317+
break;
315318
} else {
316319
sourceText = readFile(path.join(dir, indexName + ".ts"));
317320
if (sourceText !== null) {
318321
sourcePath = exports.libraryPrefix + indexName + ".ts";
322+
break;
319323
}
320324
}
321325
}
@@ -530,12 +534,17 @@ exports.main = function main(argv, options, callback) {
530534
sourceMap.sources.forEach((name, index) => {
531535
let text = null;
532536
if (name.startsWith(exports.libraryPrefix)) {
533-
for (let i = 0, k = customLibDirs.length; i < k; ++i) {
534-
text = readFile(path.join(
535-
customLibDirs[i],
536-
name.substring(exports.libraryPrefix.length))
537-
);
538-
if (text !== null) break;
537+
let stdName = name.substring(exports.libraryPrefix.length).replace(/\.ts$/, "");
538+
if (exports.libraryFiles.hasOwnProperty(stdName)) {
539+
text = exports.libraryFiles[stdName];
540+
} else {
541+
for (let i = 0, k = customLibDirs.length; i < k; ++i) {
542+
text = readFile(path.join(
543+
customLibDirs[i],
544+
name.substring(exports.libraryPrefix.length))
545+
);
546+
if (text !== null) break;
547+
}
539548
}
540549
} else {
541550
text = readFile(path.join(baseDir, name));

dist/asc.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/asc.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

std/assembly/allocator/arena.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,15 @@
77
* @module std/assembly/allocator/arena
88
*//***/
99

10-
import { AL_MASK } from "../internal/allocator";
10+
import { AL_MASK, MAX_SIZE_32 } from "../internal/allocator";
1111

1212
var startOffset: usize = (HEAP_BASE + AL_MASK) & ~AL_MASK;
1313
var offset: usize = startOffset;
1414

1515
@global
1616
export function allocate_memory(size: usize): usize {
17-
const MAX_SIZE: usize = 1 << 30;
18-
if (size && size < MAX_SIZE) {
17+
if (size) {
18+
if (size > MAX_SIZE_32) unreachable();
1919
let ptr = offset;
2020
let newPtr = (ptr + size + AL_MASK) & ~AL_MASK;
2121
let pagesBefore = current_memory();

std/assembly/allocator/buddy.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -347,9 +347,7 @@ export function allocate_memory(request: usize): usize {
347347
* a hard-coded limit on the maximum allocation size because of the way this
348348
* allocator works.
349349
*/
350-
if (request > MAX_ALLOC - HEADER_SIZE) {
351-
return 0;
352-
}
350+
if (request > MAX_ALLOC - HEADER_SIZE) unreachable();
353351

354352
/*
355353
* Initialize our global state if this is the first call to "malloc". At the

std/assembly/allocator/tlsf.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -457,7 +457,8 @@ export function allocate_memory(size: usize): usize {
457457

458458
// search for a suitable block
459459
var data: usize = 0;
460-
if (size && size <= Block.MAX_SIZE) {
460+
if (size) {
461+
if (size > Block.MAX_SIZE) unreachable();
461462
// 32-bit MAX_SIZE is 1 << 30 and itself aligned, hence the following can't overflow MAX_SIZE
462463
size = max<usize>((size + AL_MASK) & ~AL_MASK, Block.MIN_SIZE);
463464

std/assembly/arraybuffer.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import {
22
HEADER_SIZE,
33
MAX_BLENGTH,
4-
allocate
4+
allocUnsafe
55
} from "./internal/arraybuffer";
66

77
@sealed
@@ -11,7 +11,7 @@ export class ArrayBuffer {
1111

1212
constructor(length: i32) {
1313
if (<u32>length > <u32>MAX_BLENGTH) throw new RangeError("Invalid array buffer length");
14-
var buffer = allocate(length);
14+
var buffer = allocUnsafe(length);
1515
set_memory(changetype<usize>(buffer) + HEADER_SIZE, 0, <usize>length);
1616
return buffer;
1717
}
@@ -23,7 +23,7 @@ export class ArrayBuffer {
2323
if (end < 0) end = max(len + end, 0);
2424
else end = min(end, len);
2525
var newLen = max(end - begin, 0);
26-
var buffer = allocate(newLen);
26+
var buffer = allocUnsafe(newLen);
2727
move_memory(changetype<usize>(buffer) + HEADER_SIZE, changetype<usize>(this) + HEADER_SIZE + begin, newLen);
2828
return buffer;
2929
}

std/assembly/internal/arraybuffer.ts

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,44 @@ export function computeSize(byteLength: i32): usize {
1717
return <usize>1 << <usize>(<u32>32 - clz<u32>(byteLength + HEADER_SIZE - 1));
1818
}
1919

20-
/** Allocates a raw ArrayBuffer with uninitialized contents. */
21-
export function allocate(byteLength: i32): ArrayBuffer {
20+
/** Allocates a raw ArrayBuffer. Contents remain uninitialized. */
21+
export function allocUnsafe(byteLength: i32): ArrayBuffer {
2222
assert(<u32>byteLength <= <u32>MAX_BLENGTH);
2323
var buffer = allocate_memory(computeSize(byteLength));
2424
store<i32>(buffer, byteLength, offsetof<ArrayBuffer>("byteLength"));
2525
return changetype<ArrayBuffer>(buffer);
2626
}
2727

28+
/** Reallocates an ArrayBuffer, resizing it as requested. Tries to modify the buffer in place. */
29+
export function reallocUnsafe(buffer: ArrayBuffer, newByteLength: i32): ArrayBuffer {
30+
var oldByteLength = buffer.byteLength;
31+
if (newByteLength > oldByteLength) {
32+
assert(newByteLength <= MAX_BLENGTH);
33+
let oldSize = computeSize(oldByteLength);
34+
if (<i32>(oldSize - HEADER_SIZE) <= newByteLength) { // fast path: zero out additional space
35+
store<i32>(changetype<usize>(buffer), newByteLength, offsetof<ArrayBuffer>("byteLength"));
36+
set_memory(
37+
changetype<usize>(buffer) + HEADER_SIZE + oldByteLength,
38+
0,
39+
<usize>(newByteLength - oldByteLength)
40+
);
41+
} else { // slow path: copy to new buffer
42+
let newBuffer = allocUnsafe(newByteLength);
43+
move_memory(
44+
changetype<usize>(newBuffer) + HEADER_SIZE,
45+
changetype<usize>(buffer) + HEADER_SIZE,
46+
<usize>newByteLength
47+
);
48+
return newBuffer;
49+
}
50+
} else if (newByteLength < oldByteLength) { // fast path: override size
51+
// TBD: worth to copy and release if size is significantly less than before?
52+
assert(newByteLength >= 0);
53+
store<i32>(changetype<usize>(buffer), newByteLength, offsetof<ArrayBuffer>("byteLength"));
54+
}
55+
return buffer;
56+
}
57+
2858
/** Common typed array interface. Not a global object. */
2959
// export declare interface ArrayBufferView<T> {
3060
// readonly buffer: ArrayBuffer;

std/assembly/internal/typedarray.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {
2+
HEADER_SIZE,
23
MAX_BLENGTH,
3-
allocate
4+
allocUnsafe
45
// ArrayBufferView
56
} from "./arraybuffer";
67

@@ -15,7 +16,9 @@ export abstract class TypedArray<T> /* implements ArrayBufferView<T> */ {
1516
const MAX_LENGTH = <u32>MAX_BLENGTH / sizeof<T>();
1617
if (<u32>length > MAX_LENGTH) throw new RangeError("Invalid typed array length");
1718
var byteLength = length << alignof<T>();
18-
this.buffer = allocate(byteLength);
19+
var buffer = allocUnsafe(byteLength);
20+
set_memory(changetype<usize>(buffer) + HEADER_SIZE, 0, <usize>byteLength);
21+
this.buffer = buffer;
1922
this.byteOffset = 0;
2023
this.byteLength = byteLength;
2124
}

0 commit comments

Comments
 (0)