Skip to content
31 changes: 19 additions & 12 deletions std/assembly/array.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,22 +6,28 @@ import { joinBooleanArray, joinIntegerArray, joinFloatArray, joinStringArray, jo
import { idof, isArray as builtin_isArray } from "./builtins";
import { E_INDEXOUTOFRANGE, E_INVALIDLENGTH, E_ILLEGALGENTYPE, E_EMPTYARRAY, E_HOLEYARRAY } from "./util/error";

// @ts-ignore: decorator
@inline @lazy const MIN_SIZE: usize = 8;

/** Ensures that the given array has _at least_ the specified backing size. */
function ensureSize(array: usize, minSize: usize, alignLog2: u32): void {
// depends on the fact that Arrays mimic ArrayBufferView
var oldCapacity = changetype<ArrayBufferView>(array).byteLength;
if (minSize > <usize>oldCapacity >>> alignLog2) {
if (minSize > BLOCK_MAXSIZE >>> alignLog2) throw new RangeError(E_INVALIDLENGTH);
function ensureCapacity(array: usize, newSize: usize, alignLog2: u32, canGrow: bool = true): void {
// Depends on the fact that Arrays mimic ArrayBufferView
var oldCapacity = <usize>changetype<ArrayBufferView>(array).byteLength;
if (newSize > oldCapacity >>> alignLog2) {
if (newSize > BLOCK_MAXSIZE >>> alignLog2) throw new RangeError(E_INVALIDLENGTH);
let oldData = changetype<usize>(changetype<ArrayBufferView>(array).buffer);
let newCapacity = minSize << alignLog2;
// Grows old capacity by factor of two.
// Make sure we don't reach BLOCK_MAXSIZE for new growed capacity.
let newCapacity = max(newSize, MIN_SIZE) << alignLog2;
if (canGrow) newCapacity = max(min(oldCapacity << 1, BLOCK_MAXSIZE), newCapacity);
let newData = __renew(oldData, newCapacity);
memory.fill(newData + oldCapacity, 0, newCapacity - oldCapacity);
if (newData !== oldData) { // oldData has been free'd
store<usize>(array, newData, offsetof<ArrayBufferView>("buffer"));
store<usize>(array, newData, offsetof<ArrayBufferView>("dataStart"));
__link(array, changetype<usize>(newData), false);
}
store<u32>(array, newCapacity, offsetof<ArrayBufferView>("byteLength"));
store<u32>(array, <u32>newCapacity, offsetof<ArrayBufferView>("byteLength"));
}
}

Expand Down Expand Up @@ -56,7 +62,8 @@ export class Array<T> {

constructor(length: i32 = 0) {
if (<u32>length > <u32>BLOCK_MAXSIZE >>> alignof<T>()) throw new RangeError(E_INVALIDLENGTH);
var bufferSize = <usize>length << alignof<T>();
// reserve capacity for at least MIN_SIZE elements
var bufferSize = <usize>max(length, MIN_SIZE) << alignof<T>();
var buffer = changetype<ArrayBuffer>(__new(bufferSize, idof<ArrayBuffer>()));
memory.fill(changetype<usize>(buffer), 0, bufferSize);
this.buffer = buffer; // links
Expand All @@ -70,7 +77,7 @@ export class Array<T> {
}

set length(newLength: i32) {
ensureSize(changetype<usize>(this), newLength, alignof<T>());
ensureCapacity(changetype<usize>(this), newLength, alignof<T>(), false);
this.length_ = newLength;
}

Expand Down Expand Up @@ -106,7 +113,7 @@ export class Array<T> {
@operator("[]=") private __set(index: i32, value: T): void {
if (<u32>index >= <u32>this.length_) {
if (index < 0) throw new RangeError(E_INDEXOUTOFRANGE);
ensureSize(changetype<usize>(this), index + 1, alignof<T>());
ensureCapacity(changetype<usize>(this), index + 1, alignof<T>());
this.length_ = index + 1;
}
this.__uset(index, value);
Expand Down Expand Up @@ -204,7 +211,7 @@ export class Array<T> {
push(value: T): i32 {
var length = this.length_;
var newLength = length + 1;
ensureSize(changetype<usize>(this), newLength, alignof<T>());
ensureCapacity(changetype<usize>(this), newLength, alignof<T>());
if (isManaged<T>()) {
store<usize>(this.dataStart + (<usize>length << alignof<T>()), changetype<usize>(value));
__link(changetype<usize>(this), changetype<usize>(value), true);
Expand Down Expand Up @@ -353,7 +360,7 @@ export class Array<T> {

unshift(value: T): i32 {
var newLength = this.length_ + 1;
ensureSize(changetype<usize>(this), newLength, alignof<T>());
ensureCapacity(changetype<usize>(this), newLength, alignof<T>());
var dataStart = this.dataStart;
memory.copy(
dataStart + sizeof<T>(),
Expand Down
6 changes: 3 additions & 3 deletions tests/compiler/assert-nonnull.optimized.wat
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
if
i32.const 1184
i32.const 1248
i32.const 92
i32.const 99
i32.const 42
call $~lib/builtins/abort
unreachable
Expand Down Expand Up @@ -212,7 +212,7 @@
if
i32.const 1184
i32.const 1248
i32.const 92
i32.const 99
i32.const 42
call $~lib/builtins/abort
unreachable
Expand All @@ -228,7 +228,7 @@
if
i32.const 1296
i32.const 1248
i32.const 96
i32.const 103
i32.const 40
call $~lib/builtins/abort
unreachable
Expand Down
6 changes: 3 additions & 3 deletions tests/compiler/assert-nonnull.untouched.wat
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@
if
i32.const 160
i32.const 224
i32.const 92
i32.const 99
i32.const 42
call $~lib/builtins/abort
unreachable
Expand All @@ -334,7 +334,7 @@
if
i32.const 272
i32.const 224
i32.const 96
i32.const 103
i32.const 40
call $~lib/builtins/abort
unreachable
Expand Down Expand Up @@ -365,7 +365,7 @@
if
i32.const 160
i32.const 224
i32.const 92
i32.const 99
i32.const 42
call $~lib/builtins/abort
unreachable
Expand Down
6 changes: 3 additions & 3 deletions tests/compiler/class.optimized.wat
Original file line number Diff line number Diff line change
Expand Up @@ -1820,13 +1820,13 @@
i32.const 0
i32.store offset=12
global.get $~lib/memory/__stack_pointer
i32.const 0
i32.const 32
i32.const 0
call $~lib/rt/itcms/__new
local.tee $1
i32.store offset=4
local.get $1
i32.const 0
i32.const 32
call $~lib/memory/memory.fill
local.get $0
local.get $1
Expand All @@ -1835,7 +1835,7 @@
local.get $1
i32.store offset=4
local.get $0
i32.const 0
i32.const 32
i32.store offset=8
local.get $0
i32.const 0
Expand Down
31 changes: 20 additions & 11 deletions tests/compiler/class.untouched.wat
Original file line number Diff line number Diff line change
Expand Up @@ -2683,6 +2683,8 @@
(local $2 i32)
(local $3 i32)
(local $4 i32)
(local $5 i32)
(local $6 i32)
global.get $~lib/memory/__stack_pointer
i32.const 8
i32.sub
Expand Down Expand Up @@ -2721,44 +2723,51 @@
if
i32.const 432
i32.const 480
i32.const 58
i32.const 64
i32.const 60
call $~lib/builtins/abort
unreachable
end
local.get $1
local.tee $2
i32.const 8
local.tee $3
local.get $2
local.get $3
i32.gt_s
select
i32.const 2
i32.shl
local.set $2
local.set $4
global.get $~lib/memory/__stack_pointer
local.get $2
local.get $4
i32.const 0
call $~lib/rt/itcms/__new
local.tee $3
local.tee $5
i32.store offset=4
local.get $3
local.get $5
i32.const 0
local.get $2
local.get $4
call $~lib/memory/memory.fill
local.get $0
local.get $3
local.get $5
call $~lib/array/Array<i32>#set:buffer
local.get $0
local.get $3
local.get $5
call $~lib/array/Array<i32>#set:dataStart
local.get $0
local.get $2
local.get $4
call $~lib/array/Array<i32>#set:byteLength
local.get $0
local.get $1
call $~lib/array/Array<i32>#set:length_
local.get $0
local.set $4
local.set $6
global.get $~lib/memory/__stack_pointer
i32.const 8
i32.add
global.set $~lib/memory/__stack_pointer
local.get $4
local.get $6
)
(func $class/GenericInitializer<i32>#constructor (param $0 i32) (result i32)
(local $1 i32)
Expand Down
59 changes: 39 additions & 20 deletions tests/compiler/extends-baseaggregate.optimized.wat
Original file line number Diff line number Diff line change
Expand Up @@ -2150,15 +2150,14 @@
i32.store offset=4
i32.const 1180
i32.load
local.tee $9
local.tee $8
i32.const 1
i32.add
local.tee $5
local.set $1
local.get $5
local.tee $9
local.tee $1
i32.const 1176
i32.load
local.tee $6
local.tee $4
i32.const 2
i32.shr_u
i32.gt_u
Expand All @@ -2169,45 +2168,65 @@
if
i32.const 1616
i32.const 1664
i32.const 14
i32.const 17
i32.const 48
call $~lib/builtins/abort
unreachable
end
i32.const 1168
i32.load
local.tee $7
local.tee $6
local.set $0
block $__inlined_func$~lib/rt/itcms/__renew
local.get $4
i32.const 1
i32.shl
local.tee $3
i32.const 1073741820
local.get $3
i32.const 1073741820
i32.lt_u
select
local.tee $3
local.get $1
i32.const 8
local.get $1
i32.const 8
i32.gt_u
select
i32.const 2
i32.shl
local.tee $8
local.tee $1
local.get $1
local.get $3
i32.lt_u
select
local.tee $7
local.tee $3
local.get $7
local.get $6
i32.const 20
i32.sub
local.tee $4
local.tee $5
i32.load
i32.const -4
i32.and
i32.const 16
i32.sub
i32.le_u
if
local.get $4
local.get $5
local.get $3
i32.store offset=16
br $__inlined_func$~lib/rt/itcms/__renew
end
local.get $3
local.get $4
local.get $5
i32.load offset=12
call $~lib/rt/itcms/__new
local.tee $1
local.get $0
local.get $3
local.get $4
local.get $5
i32.load offset=16
local.tee $0
local.get $0
Expand All @@ -2219,14 +2238,14 @@
local.set $0
end
local.get $0
local.get $6
local.get $4
i32.add
local.get $8
local.get $6
local.get $7
local.get $4
i32.sub
call $~lib/memory/memory.fill
local.get $0
local.get $7
local.get $6
i32.ne
if
i32.const 1168
Expand All @@ -2241,12 +2260,12 @@
call $~lib/rt/itcms/__link
end
i32.const 1176
local.get $8
local.get $7
i32.store
end
i32.const 1172
i32.load
local.get $9
local.get $8
i32.const 2
i32.shl
i32.add
Expand All @@ -2257,7 +2276,7 @@
i32.const 1
call $~lib/rt/itcms/__link
i32.const 1180
local.get $5
local.get $9
i32.store
global.get $~lib/memory/__stack_pointer
i32.const 8
Expand Down
Loading