mirror of
https://github.com/fluencelabs/assemblyscript
synced 2025-06-17 17:01:37 +00:00
guard, info on never null, more general array rt
This commit is contained in:
@ -1,13 +1,17 @@
|
||||
import { ALLOCATE, REALLOCATE, DISCARD, RETAIN, RELEASE, REGISTER, MAX_BYTELENGTH, ArrayBufferView, MOVE } from "./runtime";
|
||||
import {
|
||||
ALLOCATE, REALLOCATE, DISCARD, RETAIN, RELEASE, REGISTER, MAX_BYTELENGTH, MOVE, MAKEARRAY,
|
||||
ArrayBufferView
|
||||
} from "./runtime";
|
||||
import { ArrayBuffer } from "./arraybuffer";
|
||||
import { COMPARATOR, SORT } from "./util/sort";
|
||||
import { itoa, dtoa, itoa_stream, dtoa_stream, MAX_DOUBLE_LENGTH } from "./util/number";
|
||||
import { isArray as builtin_isArray } from "./builtins";
|
||||
import { E_INDEXOUTOFRANGE, E_INVALIDLENGTH, E_EMPTYARRAY, E_HOLEYARRAY } from "./util/error";
|
||||
|
||||
/** Ensures that the given array has _at least_ the specified capacity. */
|
||||
function ensureCapacity(array: ArrayBufferView, minCapacity: i32, alignLog2: u32): void {
|
||||
if (<u32>minCapacity > <u32>array.dataLength >>> alignLog2) {
|
||||
if (<u32>minCapacity > <u32>(MAX_BYTELENGTH >>> alignLog2)) throw new RangeError("Invalid array length");
|
||||
if (<u32>minCapacity > <u32>(MAX_BYTELENGTH >>> alignLog2)) throw new RangeError(E_INVALIDLENGTH);
|
||||
let oldData = array.data;
|
||||
let newByteLength = minCapacity << alignLog2;
|
||||
let newData = REALLOCATE(changetype<usize>(oldData), <usize>newByteLength); // registers on move
|
||||
@ -26,28 +30,29 @@ export class Array<T> extends ArrayBufferView {
|
||||
// to work with typed and normal arrays interchangeably. Technically, normal arrays do not need
|
||||
// `dataStart` (equals `data`) and `dataLength` (equals computed `data.byteLength`).
|
||||
|
||||
// Also note that Array<T> with non-nullable T must guard against implicit null values whenever
|
||||
// length is modified in a way that a null value would exist. Otherwise, the compiler wouldn't be
|
||||
// able to guarantee type-safety anymore. For lack of a better word, such an array is "holey".
|
||||
|
||||
private length_: i32;
|
||||
|
||||
static isArray<U>(value: U): bool {
|
||||
return builtin_isArray(value) && value !== null;
|
||||
}
|
||||
|
||||
static create<T>(capacity: i32): Array<T> {
|
||||
if (<u32>capacity > <u32>MAX_BYTELENGTH >>> alignof<T>()) throw new RangeError("Invalid length");
|
||||
var buffer = new ArrayBuffer(capacity = capacity << alignof<T>());
|
||||
var out = REGISTER<Array<T>>(ALLOCATE(offsetof<Array<T>>()));
|
||||
out.data = buffer; // links
|
||||
out.dataStart = changetype<usize>(buffer);
|
||||
out.dataLength = capacity;
|
||||
out.length_ = 0;
|
||||
return out;
|
||||
static create<T>(capacity: i32 = 0): Array<T> {
|
||||
if (<u32>capacity > <u32>MAX_BYTELENGTH >>> alignof<T>()) throw new RangeError(E_INVALIDLENGTH);
|
||||
var array = MAKEARRAY<T>(capacity);
|
||||
memory.fill(array.dataStart, 0, <usize>array.dataLength);
|
||||
array.length_ = 0; // !
|
||||
return array;
|
||||
}
|
||||
|
||||
constructor(length: i32 = 0) {
|
||||
super(length, alignof<T>());
|
||||
if (isReference<T>()) {
|
||||
if (!isNullable<T>()) {
|
||||
if (length) throw new Error("T must be nullable if length > 0");
|
||||
if (length) throw new Error(E_HOLEYARRAY);
|
||||
}
|
||||
}
|
||||
this.length_ = length;
|
||||
@ -62,6 +67,11 @@ export class Array<T> extends ArrayBufferView {
|
||||
}
|
||||
|
||||
set length(length: i32) {
|
||||
if (isReference<T>()) {
|
||||
if (!isNullable<T>()) {
|
||||
if (<u32>length > <u32>this.length_) throw new Error(E_HOLEYARRAY);
|
||||
}
|
||||
}
|
||||
ensureCapacity(this, length, alignof<T>());
|
||||
this.length_ = length;
|
||||
}
|
||||
@ -82,24 +92,39 @@ export class Array<T> extends ArrayBufferView {
|
||||
|
||||
@operator("[]") // unchecked is built-in
|
||||
private __get(index: i32): T {
|
||||
if (<u32>index >= <u32>this.dataLength >>> alignof<T>()) throw new RangeError("Offset out of bounds");
|
||||
if (isReference<T>()) {
|
||||
if (!isNullable<T>()) {
|
||||
if (<u32>index >= <u32>this.length_) throw new Error(E_HOLEYARRAY);
|
||||
}
|
||||
}
|
||||
if (<u32>index >= <u32>this.dataLength >>> alignof<T>()) throw new RangeError(E_INDEXOUTOFRANGE);
|
||||
return load<T>(this.dataStart + (<usize>index << alignof<T>()));
|
||||
}
|
||||
|
||||
@operator("[]=") // unchecked is built-in
|
||||
private __set(index: i32, value: T): void {
|
||||
var length = this.length_;
|
||||
if (isReference<T>()) {
|
||||
if (!isNullable<T>()) {
|
||||
if (<u32>index > <u32>length) throw new Error(E_HOLEYARRAY);
|
||||
}
|
||||
}
|
||||
ensureCapacity(this, index + 1, alignof<T>());
|
||||
if (isManaged<T>()) {
|
||||
let offset = this.dataStart + (<usize>index << alignof<T>());
|
||||
let oldValue = load<T>(offset);
|
||||
if (value !== oldValue) {
|
||||
RELEASE<T,this>(oldValue, this);
|
||||
if (isNullable<T>()) {
|
||||
RELEASE<T,this>(oldValue, this); // handles != null
|
||||
} else if (oldValue !== null) {
|
||||
RELEASE<T,this>(oldValue, this); // requires != null
|
||||
}
|
||||
store<T>(offset, RETAIN<T,this>(value, this));
|
||||
}
|
||||
} else {
|
||||
store<T>(this.dataStart + (<usize>index << alignof<T>()), value);
|
||||
}
|
||||
if (index >= this.length_) this.length_ = index + 1;
|
||||
if (index >= length) this.length_ = index + 1;
|
||||
}
|
||||
|
||||
fill(value: T, start: i32 = 0, end: i32 = i32.MAX_VALUE): this {
|
||||
@ -167,7 +192,7 @@ export class Array<T> extends ArrayBufferView {
|
||||
concat(other: Array<T>): Array<T> {
|
||||
var thisLen = this.length_;
|
||||
var otherLen = select(0, other.length_, other === null);
|
||||
var out = new Array<T>(thisLen + otherLen);
|
||||
var out = MAKEARRAY<T>(thisLen + otherLen);
|
||||
var outStart = out.dataStart;
|
||||
var thisSize = <usize>thisLen << alignof<T>();
|
||||
if (isManaged<T>()) {
|
||||
@ -217,7 +242,7 @@ export class Array<T> extends ArrayBufferView {
|
||||
|
||||
pop(): T {
|
||||
var length = this.length_;
|
||||
if (length < 1) throw new RangeError("Array is empty");
|
||||
if (length < 1) throw new RangeError(E_EMPTYARRAY);
|
||||
var element = load<T>(this.dataStart + (<usize>(--length) << alignof<T>()));
|
||||
this.length_ = length;
|
||||
return element;
|
||||
@ -231,7 +256,7 @@ export class Array<T> extends ArrayBufferView {
|
||||
|
||||
map<U>(callbackfn: (value: T, index: i32, array: Array<T>) => U): Array<U> {
|
||||
var length = this.length_;
|
||||
var out = new Array<U>(length);
|
||||
var out = MAKEARRAY<U>(length);
|
||||
var outStart = out.dataStart;
|
||||
for (let index = 0; index < min(length, this.length_); ++index) {
|
||||
let value = load<T>(this.dataStart + (<usize>index << alignof<T>()));
|
||||
@ -246,7 +271,7 @@ export class Array<T> extends ArrayBufferView {
|
||||
}
|
||||
|
||||
filter(callbackfn: (value: T, index: i32, array: Array<T>) => bool): Array<T> {
|
||||
var result = new Array<T>();
|
||||
var result = MAKEARRAY<T>(0);
|
||||
for (let index = 0, length = this.length_; index < min(length, this.length_); ++index) {
|
||||
let value = load<T>(this.dataStart + (<usize>index << alignof<T>()));
|
||||
if (callbackfn(value, index, this)) result.push(value);
|
||||
@ -278,7 +303,7 @@ export class Array<T> extends ArrayBufferView {
|
||||
|
||||
shift(): T {
|
||||
var length = this.length_;
|
||||
if (length < 1) throw new RangeError("Array is empty");
|
||||
if (length < 1) throw new RangeError(E_EMPTYARRAY);
|
||||
var base = this.dataStart;
|
||||
var element = load<T>(base);
|
||||
var lastIndex = length - 1;
|
||||
@ -325,7 +350,7 @@ export class Array<T> extends ArrayBufferView {
|
||||
begin = begin < 0 ? max(begin + length, 0) : min(begin, length);
|
||||
end = end < 0 ? max(end + length, 0) : min(end , length);
|
||||
length = max(end - begin, 0);
|
||||
var slice = new Array<T>(length);
|
||||
var slice = MAKEARRAY<T>(length);
|
||||
var sliceBase = slice.dataStart;
|
||||
var thisBase = this.dataStart + (<usize>begin << alignof<T>());
|
||||
for (let i = 0; i < length; ++i) {
|
||||
@ -344,7 +369,7 @@ export class Array<T> extends ArrayBufferView {
|
||||
var length = this.length_;
|
||||
start = start < 0 ? max<i32>(length + start, 0) : min<i32>(start, length);
|
||||
deleteCount = max<i32>(min<i32>(deleteCount, length - start), 0);
|
||||
var result = new Array<T>(deleteCount);
|
||||
var result = MAKEARRAY<T>(deleteCount);
|
||||
var resultStart = result.dataStart;
|
||||
var thisStart = this.dataStart;
|
||||
var thisBase = thisStart + (<usize>start << alignof<T>());
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { ALLOCATE, REGISTER, HEADER, HEADER_SIZE, MAX_BYTELENGTH } from "./runtime";
|
||||
import { E_INVALIDLENGTH } from "./util/error";
|
||||
|
||||
@sealed export class ArrayBuffer {
|
||||
|
||||
@ -21,7 +22,7 @@ import { ALLOCATE, REGISTER, HEADER, HEADER_SIZE, MAX_BYTELENGTH } from "./runti
|
||||
}
|
||||
|
||||
constructor(length: i32) {
|
||||
if (<u32>length > <u32>MAX_BYTELENGTH) throw new RangeError("Invalid array buffer length");
|
||||
if (<u32>length > <u32>MAX_BYTELENGTH) throw new RangeError(E_INVALIDLENGTH);
|
||||
var buffer = ALLOCATE(<usize>length);
|
||||
memory.fill(changetype<usize>(buffer), 0, <usize>length);
|
||||
return REGISTER<ArrayBuffer>(buffer);
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { MAX_BYTELENGTH } from "./runtime";
|
||||
import { ArrayBuffer } from "./arraybuffer";
|
||||
import { E_INDEXOUTOFRANGE, E_INVALIDLENGTH } from "./util/error";
|
||||
|
||||
// TODO: there is probably a smarter way to check byteOffset for accesses larger than 1 byte
|
||||
|
||||
@ -12,11 +13,13 @@ export class DataView {
|
||||
constructor(
|
||||
buffer: ArrayBuffer,
|
||||
byteOffset: i32 = 0,
|
||||
byteLength: i32 = i32.MIN_VALUE // FIXME
|
||||
byteLength: i32 = i32.MIN_VALUE // FIXME: TS2304: Cannot find name 'buffer'.
|
||||
) {
|
||||
if (byteLength === i32.MIN_VALUE) byteLength = buffer.byteLength - byteOffset; // FIXME
|
||||
if (<u32>byteLength > <u32>MAX_BYTELENGTH) throw new RangeError("Invalid byteLength");
|
||||
if (<u32>byteOffset + byteLength > <u32>buffer.byteLength) throw new RangeError("Invalid length");
|
||||
if (
|
||||
i32(<u32>byteLength > <u32>MAX_BYTELENGTH) |
|
||||
i32(<u32>byteOffset + byteLength > <u32>buffer.byteLength)
|
||||
) throw new RangeError(E_INVALIDLENGTH);
|
||||
this.data = buffer; // links
|
||||
var dataStart = changetype<usize>(buffer) + <usize>byteOffset;
|
||||
this.dataStart = dataStart;
|
||||
@ -39,7 +42,7 @@ export class DataView {
|
||||
if (
|
||||
i32(byteOffset < 0) |
|
||||
i32(byteOffset + 4 > this.dataLength)
|
||||
) throw new Error("Offset out of bounds");
|
||||
) throw new RangeError(E_INDEXOUTOFRANGE);
|
||||
return littleEndian
|
||||
? load<f32>(this.dataStart + <usize>byteOffset)
|
||||
: reinterpret<f32>(
|
||||
@ -53,7 +56,7 @@ export class DataView {
|
||||
if (
|
||||
i32(byteOffset < 0) |
|
||||
i32(byteOffset + 8 > this.dataLength)
|
||||
) throw new Error("Offset out of bounds");
|
||||
) throw new RangeError(E_INDEXOUTOFRANGE);
|
||||
return littleEndian
|
||||
? load<f64>(this.dataStart + <usize>byteOffset)
|
||||
: reinterpret<f64>(
|
||||
@ -64,7 +67,7 @@ export class DataView {
|
||||
}
|
||||
|
||||
getInt8(byteOffset: i32): i8 {
|
||||
if (<u32>byteOffset >= <u32>this.dataLength) throw new Error("Offset out of bounds");
|
||||
if (<u32>byteOffset >= <u32>this.dataLength) throw new RangeError(E_INDEXOUTOFRANGE);
|
||||
return load<i8>(this.dataStart + <usize>byteOffset);
|
||||
}
|
||||
|
||||
@ -72,7 +75,7 @@ export class DataView {
|
||||
if (
|
||||
i32(byteOffset < 0) |
|
||||
i32(byteOffset + 2 > this.dataLength)
|
||||
) throw new Error("Offset out of bounds");
|
||||
) throw new RangeError(E_INDEXOUTOFRANGE);
|
||||
var result: i16 = load<i16>(this.dataStart + <usize>byteOffset);
|
||||
return littleEndian ? result : bswap<i16>(result);
|
||||
}
|
||||
@ -81,13 +84,13 @@ export class DataView {
|
||||
if (
|
||||
i32(byteOffset < 0) |
|
||||
i32(byteOffset + 4 > this.dataLength)
|
||||
) throw new Error("Offset out of bounds");
|
||||
) throw new RangeError(E_INDEXOUTOFRANGE);
|
||||
var result: i32 = load<i32>(this.dataStart + <usize>byteOffset);
|
||||
return littleEndian ? result : bswap<i32>(result);
|
||||
}
|
||||
|
||||
getUint8(byteOffset: i32): u8 {
|
||||
if (<u32>byteOffset >= <u32>this.dataLength) throw new Error("Offset out of bounds");
|
||||
if (<u32>byteOffset >= <u32>this.dataLength) throw new RangeError(E_INDEXOUTOFRANGE);
|
||||
return load<u8>(this.dataStart + <usize>byteOffset);
|
||||
}
|
||||
|
||||
@ -95,7 +98,7 @@ export class DataView {
|
||||
if (
|
||||
i32(byteOffset < 0) |
|
||||
i32(byteOffset + 2 > this.dataLength)
|
||||
) throw new Error("Offset out of bounds");
|
||||
) throw new RangeError(E_INDEXOUTOFRANGE);
|
||||
var result: u16 = load<u16>(this.dataStart + <usize>byteOffset);
|
||||
return littleEndian ? result : bswap<u16>(result);
|
||||
}
|
||||
@ -104,7 +107,7 @@ export class DataView {
|
||||
if (
|
||||
i32(byteOffset < 0) |
|
||||
i32(byteOffset + 4 > this.dataLength)
|
||||
) throw new Error("Offset out of bounds");
|
||||
) throw new RangeError(E_INDEXOUTOFRANGE);
|
||||
var result: u32 = load<u32>(this.dataStart + <usize>byteOffset);
|
||||
return littleEndian ? result : bswap<u32>(result);
|
||||
}
|
||||
@ -113,7 +116,7 @@ export class DataView {
|
||||
if (
|
||||
i32(byteOffset < 0) |
|
||||
i32(byteOffset + 4 > this.dataLength)
|
||||
) throw new Error("Offset out of bounds");
|
||||
) throw new RangeError(E_INDEXOUTOFRANGE);
|
||||
if (littleEndian) store<f32>(this.dataStart + <usize>byteOffset, value);
|
||||
else store<u32>(this.dataStart + <usize>byteOffset, bswap<u32>(reinterpret<u32>(value)));
|
||||
}
|
||||
@ -122,13 +125,13 @@ export class DataView {
|
||||
if (
|
||||
i32(byteOffset < 0) |
|
||||
i32(byteOffset + 8 > this.dataLength)
|
||||
) throw new Error("Offset out of bounds");
|
||||
) throw new RangeError(E_INDEXOUTOFRANGE);
|
||||
if (littleEndian) store<f64>(this.dataStart + <usize>byteOffset, value);
|
||||
else store<u64>(this.dataStart + <usize>byteOffset, bswap<u64>(reinterpret<u64>(value)));
|
||||
}
|
||||
|
||||
setInt8(byteOffset: i32, value: i8): void {
|
||||
if (<u32>byteOffset >= <u32>this.dataLength) throw new Error("Offset out of bounds");
|
||||
if (<u32>byteOffset >= <u32>this.dataLength) throw new RangeError(E_INDEXOUTOFRANGE);
|
||||
store<i8>(this.dataStart + <usize>byteOffset, value);
|
||||
}
|
||||
|
||||
@ -136,7 +139,7 @@ export class DataView {
|
||||
if (
|
||||
i32(byteOffset < 0) |
|
||||
i32(byteOffset + 2 > this.dataLength)
|
||||
) throw new Error("Offset out of bounds");
|
||||
) throw new RangeError(E_INDEXOUTOFRANGE);
|
||||
store<i16>(this.dataStart + <usize>byteOffset, littleEndian ? value : bswap<i16>(value));
|
||||
}
|
||||
|
||||
@ -144,12 +147,12 @@ export class DataView {
|
||||
if (
|
||||
i32(byteOffset < 0) |
|
||||
i32(byteOffset + 4 > this.dataLength)
|
||||
) throw new Error("Offset out of bounds");
|
||||
) throw new RangeError(E_INDEXOUTOFRANGE);
|
||||
store<i32>(this.dataStart + <usize>byteOffset, littleEndian ? value : bswap<i32>(value));
|
||||
}
|
||||
|
||||
setUint8(byteOffset: i32, value: u8): void {
|
||||
if (<u32>byteOffset >= <u32>this.dataLength) throw new Error("Offset out of bounds");
|
||||
if (<u32>byteOffset >= <u32>this.dataLength) throw new RangeError(E_INDEXOUTOFRANGE);
|
||||
store<u8>(this.dataStart + <usize>byteOffset, value);
|
||||
}
|
||||
|
||||
@ -157,7 +160,7 @@ export class DataView {
|
||||
if (
|
||||
i32(byteOffset < 0) |
|
||||
i32(byteOffset + 2 > this.dataLength)
|
||||
) throw new Error("Offset out of bounds");
|
||||
) throw new RangeError(E_INDEXOUTOFRANGE);
|
||||
store<u16>(this.dataStart + <usize>byteOffset, littleEndian ? value : bswap<u16>(value));
|
||||
}
|
||||
|
||||
@ -165,7 +168,7 @@ export class DataView {
|
||||
if (
|
||||
i32(byteOffset < 0) |
|
||||
i32(byteOffset + 4 > this.dataLength)
|
||||
) throw new Error("Offset out of bounds");
|
||||
) throw new RangeError(E_INDEXOUTOFRANGE);
|
||||
store<u32>(this.dataStart + <usize>byteOffset, littleEndian ? value : bswap<u32>(value));
|
||||
}
|
||||
|
||||
@ -175,7 +178,7 @@ export class DataView {
|
||||
if (
|
||||
i32(byteOffset < 0) |
|
||||
i32(byteOffset + 8 > this.dataLength)
|
||||
) throw new Error("Offset out of bounds");
|
||||
) throw new RangeError(E_INDEXOUTOFRANGE);
|
||||
var result: i64 = load<i64>(this.dataStart + <usize>byteOffset);
|
||||
return littleEndian ? result : bswap<i64>(result);
|
||||
}
|
||||
@ -184,7 +187,7 @@ export class DataView {
|
||||
if (
|
||||
i32(byteOffset < 0) |
|
||||
i32(byteOffset + 8 > this.dataLength)
|
||||
) throw new Error("Offset out of bounds");
|
||||
) throw new RangeError(E_INDEXOUTOFRANGE);
|
||||
var result = load<u64>(this.dataStart + <usize>byteOffset);
|
||||
return littleEndian ? result : bswap<u64>(result);
|
||||
}
|
||||
@ -193,7 +196,7 @@ export class DataView {
|
||||
if (
|
||||
i32(byteOffset < 0) |
|
||||
i32(byteOffset + 8 > this.dataLength)
|
||||
) throw new Error("Offset out of bounds");
|
||||
) throw new RangeError(E_INDEXOUTOFRANGE);
|
||||
store<i64>(this.dataStart + <usize>byteOffset, littleEndian ? value : bswap<i64>(value));
|
||||
}
|
||||
|
||||
@ -201,7 +204,7 @@ export class DataView {
|
||||
if (
|
||||
i32(byteOffset < 0) |
|
||||
i32(byteOffset + 8 > this.dataLength)
|
||||
) throw new Error("Offset out of bounds");
|
||||
) throw new RangeError(E_INDEXOUTOFRANGE);
|
||||
store<u64>(this.dataStart + <usize>byteOffset, littleEndian ? value : bswap<u64>(value));
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { ALLOCATE, REGISTER, MAX_BYTELENGTH, HEADER, HEADER_SIZE, RETAIN, RELEASE } from "./runtime";
|
||||
import { E_INDEXOUTOFRANGE, E_INVALIDLENGTH, E_HOLEYARRAY } from "./util/error";
|
||||
|
||||
// NOTE: DO NOT USE YET!
|
||||
|
||||
@ -9,7 +10,12 @@ export class FixedArray<T> {
|
||||
[key: number]: T;
|
||||
|
||||
constructor(length: i32) {
|
||||
if (<u32>length > <u32>MAX_BYTELENGTH >>> alignof<T>()) throw new RangeError("Invalid length");
|
||||
if (<u32>length > <u32>MAX_BYTELENGTH >>> alignof<T>()) throw new RangeError(E_INVALIDLENGTH);
|
||||
if (isReference<T>()) {
|
||||
if (!isNullable<T>()) {
|
||||
if (length) throw new Error(E_HOLEYARRAY);
|
||||
}
|
||||
}
|
||||
var outSize = <usize>length << alignof<T>();
|
||||
var out = ALLOCATE(outSize);
|
||||
memory.fill(out, 0, outSize);
|
||||
@ -21,12 +27,12 @@ export class FixedArray<T> {
|
||||
}
|
||||
|
||||
@operator("[]") private __get(index: i32): T {
|
||||
if (<u32>index >= <u32>this.length) throw new RangeError("Offset out of bounds");
|
||||
if (<u32>index >= <u32>this.length) throw new RangeError(E_INDEXOUTOFRANGE);
|
||||
return this.__unchecked_get(index);
|
||||
}
|
||||
|
||||
@operator("[]=") private __set(index: i32, value: T): void {
|
||||
if (<u32>index >= <u32>this.length) throw new RangeError("Offset out of bounds");
|
||||
if (<u32>index >= <u32>this.length) throw new RangeError(E_INDEXOUTOFRANGE);
|
||||
return this.__unchecked_set(index, value);
|
||||
}
|
||||
|
||||
|
5
std/assembly/index.d.ts
vendored
5
std/assembly/index.d.ts
vendored
@ -1171,12 +1171,15 @@ declare class Float64Array extends TypedArray<f64> {}
|
||||
/** Class representing a sequence of values of type `T`. */
|
||||
declare class Array<T> {
|
||||
|
||||
/** Tests if a value is an array. */
|
||||
static isArray<U>(value: any): value is Array<any>;
|
||||
/** Creates a new array with at least the specified capacity and length zero. */
|
||||
static create<T>(capacity?: i32): Array<T>;
|
||||
|
||||
[key: number]: T;
|
||||
/** Current length of the array. */
|
||||
length: i32;
|
||||
/** Constructs a new array. */
|
||||
/** Constructs a new array. If length is greater than zero and T is a non-nullable reference, use `Array.create` instead.*/
|
||||
constructor(capacity?: i32);
|
||||
|
||||
fill(value: T, start?: i32, end?: i32): this;
|
||||
|
@ -8,68 +8,7 @@
|
||||
|
||||
import { AL_MASK, MAX_SIZE_32 } from "./util/allocator";
|
||||
import { HEAP_BASE, memory } from "./memory";
|
||||
|
||||
// ALLOCATE(size)
|
||||
// --------------
|
||||
// Allocates a runtime object that might eventually make its way into GC'ed userland as a
|
||||
// managed object. Implicitly prepends the common runtime header to the allocation.
|
||||
//
|
||||
// REALLOCATE(ref, size)
|
||||
// ---------------------
|
||||
// Changes the size of a previously allocated, but not yet registered, runtime object, for
|
||||
// example when a pre-allocated buffer turned out to be too small or too large. This works by
|
||||
// aligning dynamic allocations to actual block size internally so in the best case REALLOCATE
|
||||
// only updates payload size while in the worst case moves the object to a larger block.
|
||||
//
|
||||
// DISCARD(ref)
|
||||
// ------------
|
||||
// Discards a runtime object that has not been registed and turned out to be unnecessary.
|
||||
// Essentially undoes the forgoing ALLOCATE. Should be avoided where possible, of course.
|
||||
//
|
||||
// REGISTER<T>(ref)
|
||||
// ----------------
|
||||
// Registers a runtime object of kind T. Sets the internal class id within the runtime header
|
||||
// and asserts that the object hasn't been registered yet. If a tracing garbage collector is
|
||||
// present that requires initial insertion, the macro also forwards a call to it. Once a
|
||||
// runtime object has been registed (makes it into userland), it cannot be DISCARD'ed anymore.
|
||||
//
|
||||
// RETAIN<T,TParent>(ref, parentRef)
|
||||
// ---------------------------------
|
||||
// Introduces a new reference to ref hold by parentRef. A tracing garbage collector will most
|
||||
// likely link the runtime object within its internal graph when RETAIN is called, while a
|
||||
// reference counting collector will increment the reference count.
|
||||
//
|
||||
// RELEASE<T,TParent>(ref, parentRef)
|
||||
// ----------------------------------
|
||||
// Releases a reference to ref hold by parentRef. A tracing garbage collector will most likely
|
||||
// ignore this by design, while a reference counting collector decrements the reference count
|
||||
// and potentially frees the runtime object.
|
||||
//
|
||||
// MOVE<T,TOldParent,TNewParent>(ref, oldParentRef, newParentRef)
|
||||
// --------------------------------------------------------------
|
||||
// Moves a reference to ref hold by oldParentRef to be now hold by newParentRef. This is a
|
||||
// special case of first RELEASE'ing a reference on one and instantly RETAIN'ing the reference
|
||||
// on another parent. A tracing garbage collector will most likely link the runtime object as if
|
||||
// RETAIN'ed on the new parent only, while a reference counting collector can skip increment and
|
||||
// decrement, as decrementing might otherwise involve a costly check for cyclic garbage.
|
||||
//
|
||||
// ALLOCATE_UNMANAGED(size)
|
||||
// ------------------------
|
||||
// Allocates an unmanaged struct-like object. This is used by the compiler as an abstraction
|
||||
// to memory.allocate just in case, and is usually not used directly.
|
||||
//
|
||||
// WRAPARRAY<T>(buffer)
|
||||
// --------------------
|
||||
// Wraps a buffer's data as a standard array of element type T. Used by the compiler when
|
||||
// creating an array from a static data segment, but is usually not used directly.
|
||||
//
|
||||
// HEADER
|
||||
// ------
|
||||
// The common runtime object header prepended to all managed objects. Has a size of 16 bytes in
|
||||
// WASM32 and contains a classId (e.g. for instanceof checks), the allocation size (e.g. for
|
||||
// .byteLength and .length computation) and additional reserved fields to be used by GC. If no
|
||||
// GC is present, the HEADER is cut into half excluding the reserved fields, as indicated by
|
||||
// HEADER_SIZE.
|
||||
import { Array } from "./array";
|
||||
|
||||
/** Whether the memory manager interface is implemented. */
|
||||
// @ts-ignore: decorator, stub
|
||||
@ -79,7 +18,13 @@ import { HEAP_BASE, memory } from "./memory";
|
||||
// @ts-ignore: decorator, stub
|
||||
@lazy export const GC_IMPLEMENTED: bool = isDefined(__gc_register);
|
||||
|
||||
/** Common runtime header. Each managed object has one. */
|
||||
/**
|
||||
* The common runtime object header prepended to all managed objects. Has a size of 16 bytes in
|
||||
* WASM32 and contains a classId (e.g. for instanceof checks), the allocation size (e.g. for
|
||||
* .byteLength and .length computation) and additional reserved fields to be used by GC. If no
|
||||
* GC is present, the HEADER is cut into half excluding the reserved fields, as indicated by
|
||||
* HEADER_SIZE.
|
||||
*/
|
||||
@unmanaged export class HEADER {
|
||||
/** Unique id of the respective class or a magic value if not yet registered.*/
|
||||
classId: u32;
|
||||
@ -120,7 +65,10 @@ export function ADJUSTOBLOCK(payloadSize: usize): usize {
|
||||
return <usize>1 << <usize>(<u32>32 - clz<u32>(payloadSize + HEADER_SIZE - 1));
|
||||
}
|
||||
|
||||
/** Allocates a new object and returns a pointer to its payload. Does not fill. */
|
||||
/**
|
||||
* Allocates a runtime object that might eventually make its way into GC'ed userland as a
|
||||
* managed object. Implicitly prepends the common runtime header to the allocation.
|
||||
*/
|
||||
// @ts-ignore: decorator
|
||||
@unsafe @inline
|
||||
export function ALLOCATE(payloadSize: usize): usize {
|
||||
@ -138,14 +86,22 @@ function doAllocate(payloadSize: usize): usize {
|
||||
return changetype<usize>(header) + HEADER_SIZE;
|
||||
}
|
||||
|
||||
/** Allocates an object explicitly declared as unmanaged and returns a pointer to it. */
|
||||
/**
|
||||
* Allocates an unmanaged struct-like object. This is used by the compiler as an abstraction
|
||||
* to memory.allocate just in case, and is usually not used directly.
|
||||
*/
|
||||
// @ts-ignore: decorator
|
||||
@unsafe @inline
|
||||
export function ALLOCATE_UNMANAGED(size: usize): usize {
|
||||
return memory.allocate(size);
|
||||
}
|
||||
|
||||
/** Reallocates an object if necessary. Returns a pointer to its (moved) payload. */
|
||||
/**
|
||||
* Changes the size of a previously allocated, but not yet registered, runtime object, for
|
||||
* example when a pre-allocated buffer turned out to be too small or too large. This works by
|
||||
* aligning dynamic allocations to actual block size internally so in the best case REALLOCATE
|
||||
* only updates payload size while in the worst case moves the object to a larger block.
|
||||
*/
|
||||
// @ts-ignore: decorator
|
||||
@unsafe @inline
|
||||
export function REALLOCATE(ref: usize, newPayloadSize: usize): usize {
|
||||
@ -195,7 +151,12 @@ function doReallocate(ref: usize, newPayloadSize: usize): usize {
|
||||
return ref;
|
||||
}
|
||||
|
||||
/** Registers a managed object to be tracked by the garbage collector, if present. */
|
||||
/**
|
||||
* Registers a runtime object of kind T. Sets the internal class id within the runtime header
|
||||
* and asserts that the object hasn't been registered yet. If a tracing garbage collector is
|
||||
* present that requires initial insertion, the macro usually forwards a call to it. Once a
|
||||
* runtime object has been registed (makes it into userland), it cannot be DISCARD'ed anymore.
|
||||
*/
|
||||
// @ts-ignore: decorator
|
||||
@unsafe @inline
|
||||
export function REGISTER<T>(ref: usize): T {
|
||||
@ -211,7 +172,12 @@ function doRegister(ref: usize, classId: u32): usize {
|
||||
return ref;
|
||||
}
|
||||
|
||||
/** Retains a registered object. */
|
||||
/**
|
||||
* Introduces a new reference to ref hold by parentRef. A tracing garbage collector will most
|
||||
* likely link the runtime object within its internal graph when RETAIN is called, while a
|
||||
* reference counting collector will increment the reference count. If a reference is moved
|
||||
* from one parent to another, use MOVE instead.
|
||||
*/
|
||||
// @ts-ignore: decorator
|
||||
@unsafe @inline
|
||||
export function RETAIN<T,TParent>(ref: T, parentRef: TParent): T {
|
||||
@ -234,19 +200,21 @@ function doRetain(ref: usize, parentRef: usize): void {
|
||||
if (GC_IMPLEMENTED) __gc_retain(changetype<usize>(ref), changetype<usize>(parentRef));
|
||||
}
|
||||
|
||||
/** Releases a registered object. */
|
||||
/**
|
||||
* Releases a reference to ref hold by parentRef. A tracing garbage collector will most likely
|
||||
* ignore this by design, while a reference counting collector decrements the reference count
|
||||
* and potentially frees the runtime object.
|
||||
*/
|
||||
// @ts-ignore: decorator
|
||||
@unsafe @inline
|
||||
export function RELEASE<T,TParent>(ref: T, parentRef: TParent): void {
|
||||
if (!isManaged<T>()) ERROR("managed reference expected");
|
||||
if (!isManaged<TParent>()) ERROR("managed reference expected");
|
||||
// FIXME: new Array<Ref>(10) has non-nullable elements but still contains `null`s.
|
||||
// In the future, something like this should probably initialize with `new Ref()`s.
|
||||
// if (isNullable<T>()) {
|
||||
if (isNullable<T>()) {
|
||||
if (ref !== null) doRelease(changetype<usize>(ref), changetype<usize>(parentRef));
|
||||
// } else {
|
||||
// doRelease(changetype<usize>(ref), changetype<usize>(parentRef));
|
||||
// }
|
||||
} else {
|
||||
doRelease(changetype<usize>(ref), changetype<usize>(parentRef));
|
||||
}
|
||||
}
|
||||
|
||||
function doRelease(ref: usize, parentRef: usize): void {
|
||||
@ -258,7 +226,13 @@ function doRelease(ref: usize, parentRef: usize): void {
|
||||
if (GC_IMPLEMENTED) __gc_release(changetype<usize>(ref), changetype<usize>(parentRef));
|
||||
}
|
||||
|
||||
/** Moves a registered object from one parent to another. */
|
||||
/**
|
||||
* Moves a reference to ref hold by oldParentRef to be now hold by newParentRef. This is a
|
||||
* special case of first RELEASE'ing a reference on one and instantly RETAIN'ing the reference
|
||||
* on another parent. A tracing garbage collector will most likely link the runtime object as if
|
||||
* RETAIN'ed on the new parent only, while a reference counting collector can skip increment and
|
||||
* decrement, as decrementing might otherwise involve a costly check for cyclic garbage.
|
||||
*/
|
||||
// @ts-ignore: decorator
|
||||
@unsafe @inline
|
||||
export function MOVE<T,TOldParent,TNewParent>(ref: T, oldParentRef: TOldParent, newParentRef: TNewParent): T {
|
||||
@ -293,7 +267,10 @@ function doMove(ref: usize, oldParentRef: usize, newParentRef: usize): void {
|
||||
}
|
||||
}
|
||||
|
||||
/** Discards an unregistered object that turned out to be unnecessary. */
|
||||
/**
|
||||
* Discards a runtime object that has not been registed and turned out to be unnecessary.
|
||||
* Essentially undoes the forgoing ALLOCATE. Should be avoided where possible.
|
||||
*/
|
||||
// @ts-ignore: decorator
|
||||
@unsafe @inline
|
||||
export function DISCARD(ref: usize): void {
|
||||
@ -305,23 +282,27 @@ function doDiscard(ref: usize): void {
|
||||
memory.free(changetype<usize>(ref - HEADER_SIZE));
|
||||
}
|
||||
|
||||
/** Wraps a static buffer within an array by copying its contents. */
|
||||
/**
|
||||
* Makes a new array and optionally initializes is with existing data from source. Used by the
|
||||
* compiler to either wrap static array data in a new instance or pre-initialize the memory used
|
||||
* by an array literal. Does not zero the backing buffer!
|
||||
*/
|
||||
// @ts-ignore: decorator
|
||||
@unsafe @inline
|
||||
export function WRAPARRAY<T>(buffer: ArrayBuffer): T[] {
|
||||
return changetype<T[]>(doWrapArray(buffer, CLASSID<T[]>(), alignof<T>()));
|
||||
export function MAKEARRAY<T>(capacity: i32, source: usize = 0): Array<T> {
|
||||
return changetype<Array<T>>(doMakeArray(capacity, source, CLASSID<T[]>(), alignof<T>()));
|
||||
}
|
||||
|
||||
function doWrapArray(buffer: ArrayBuffer, classId: u32, alignLog2: usize): usize {
|
||||
function doMakeArray(capacity: i32, source: usize, classId: u32, alignLog2: usize): usize {
|
||||
var array = doRegister(doAllocate(offsetof<i32[]>()), classId);
|
||||
var bufferSize = <usize>buffer.byteLength;
|
||||
var newBuffer = doRegister(doAllocate(bufferSize), classId);
|
||||
changetype<ArrayBufferView>(array).data = changetype<ArrayBuffer>(newBuffer); // links
|
||||
changetype<ArrayBufferView>(array).dataStart = changetype<usize>(newBuffer);
|
||||
var bufferSize = <usize>capacity << alignLog2;
|
||||
var buffer = doRegister(doAllocate(<usize>capacity << alignLog2), CLASSID<ArrayBuffer>());
|
||||
changetype<ArrayBufferView>(array).data = changetype<ArrayBuffer>(buffer); // links
|
||||
changetype<ArrayBufferView>(array).dataStart = buffer;
|
||||
changetype<ArrayBufferView>(array).dataLength = bufferSize;
|
||||
store<i32>(changetype<usize>(array), <i32>(bufferSize >>> alignLog2), offsetof<i32[]>("length_"));
|
||||
memory.copy(changetype<usize>(newBuffer), changetype<usize>(buffer), bufferSize);
|
||||
return changetype<usize>(array);
|
||||
store<i32>(changetype<usize>(array), capacity, offsetof<i32[]>("length_"));
|
||||
if (source) memory.copy(buffer, source, bufferSize);
|
||||
return array;
|
||||
}
|
||||
|
||||
// Helpers
|
||||
@ -341,6 +322,7 @@ function assertRegistered(ref: usize): void {
|
||||
}
|
||||
|
||||
import { ArrayBuffer } from "./arraybuffer";
|
||||
import { E_INVALIDLENGTH } from "./util/error";
|
||||
|
||||
/** Maximum byte length of any buffer. */
|
||||
// @ts-ignore: decorator
|
||||
@ -363,7 +345,7 @@ export abstract class ArrayBufferView {
|
||||
dataLength: u32;
|
||||
|
||||
protected constructor(length: i32, alignLog2: i32) {
|
||||
if (<u32>length > <u32>MAX_BYTELENGTH >>> alignLog2) throw new RangeError("Invalid length");
|
||||
if (<u32>length > <u32>MAX_BYTELENGTH >>> alignLog2) throw new RangeError(E_INVALIDLENGTH);
|
||||
var buffer = new ArrayBuffer(length = length << alignLog2);
|
||||
this.data = buffer;
|
||||
this.dataStart = changetype<usize>(buffer);
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { ALLOCATE, REGISTER, HEADER, HEADER_SIZE, ArrayBufferView, RETAIN } from "./runtime";
|
||||
import { ALLOCATE, REGISTER, HEADER, HEADER_SIZE, RETAIN, MAKEARRAY, ArrayBufferView } from "./runtime";
|
||||
import { MAX_SIZE_32 } from "./util/allocator";
|
||||
import { compareImpl, parse, CharCode, isWhiteSpaceOrLineTerminator } from "./util/string";
|
||||
import { E_INVALIDLENGTH } from "./util/error";
|
||||
|
||||
@sealed export abstract class String {
|
||||
|
||||
@ -322,7 +323,7 @@ import { compareImpl, parse, CharCode, isWhiteSpaceOrLineTerminator } from "./ut
|
||||
|
||||
// Most browsers can't handle strings 1 << 28 chars or longer
|
||||
if (count < 0 || <u64>length * count > (1 << 28)) {
|
||||
throw new RangeError("Invalid count value");
|
||||
throw new RangeError(E_INVALIDLENGTH);
|
||||
}
|
||||
|
||||
if (count == 0 || !length) return changetype<String>("");
|
||||
@ -345,16 +346,16 @@ import { compareImpl, parse, CharCode, isWhiteSpaceOrLineTerminator } from "./ut
|
||||
|
||||
split(separator: String | null = null, limit: i32 = i32.MAX_VALUE): String[] {
|
||||
assert(this !== null);
|
||||
if (!limit) return new Array<String>();
|
||||
if (!limit) return MAKEARRAY<String>(0);
|
||||
if (separator === null) return <String[]>[this];
|
||||
var length: isize = this.length;
|
||||
var sepLen: isize = separator.length;
|
||||
if (limit < 0) limit = i32.MAX_VALUE;
|
||||
if (!sepLen) {
|
||||
if (!length) return new Array<String>();
|
||||
if (!length) return MAKEARRAY<String>(0);
|
||||
// split by chars
|
||||
length = min<isize>(length, <isize>limit);
|
||||
let result = new Array<String>(length);
|
||||
let result = MAKEARRAY<String>(length);
|
||||
let resultStart = changetype<ArrayBufferView>(result).dataStart;
|
||||
for (let i: isize = 0; i < length; ++i) {
|
||||
let charStr = REGISTER<String>(ALLOCATE(2));
|
||||
@ -371,11 +372,11 @@ import { compareImpl, parse, CharCode, isWhiteSpaceOrLineTerminator } from "./ut
|
||||
}
|
||||
return result;
|
||||
} else if (!length) {
|
||||
let result = new Array<String>(1);
|
||||
let result = MAKEARRAY<String>(1);
|
||||
store<string>(changetype<ArrayBufferView>(result).dataStart, ""); // no need to register/link
|
||||
return result;
|
||||
}
|
||||
var result = new Array<String>();
|
||||
var result = MAKEARRAY<String>(0);
|
||||
var end = 0, start = 0, i = 0;
|
||||
while ((end = this.indexOf(separator!, start)) != -1) {
|
||||
let len = end - start;
|
||||
@ -390,7 +391,7 @@ import { compareImpl, parse, CharCode, isWhiteSpaceOrLineTerminator } from "./ut
|
||||
start = end + sepLen;
|
||||
}
|
||||
if (!start) {
|
||||
let result = new Array<String>(1);
|
||||
let result = MAKEARRAY<String>(1);
|
||||
unchecked(result[0] = this);
|
||||
return result;
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { ALLOCATE, REGISTER, ArrayBufferView } from "./runtime";
|
||||
import { COMPARATOR, SORT as SORT_IMPL } from "./util/sort";
|
||||
import { E_INDEXOUTOFRANGE } from "./util/error";
|
||||
|
||||
export class Int8Array extends ArrayBufferView {
|
||||
[key: number]: i8;
|
||||
@ -22,13 +23,13 @@ export class Int8Array extends ArrayBufferView {
|
||||
|
||||
@operator("[]") // unchecked is built-in
|
||||
private __get(index: i32): i8 {
|
||||
if (<u32>index >= <u32>this.dataLength) throw new Error("Offset out of bounds");
|
||||
if (<u32>index >= <u32>this.dataLength) throw new RangeError(E_INDEXOUTOFRANGE);
|
||||
return load<i8>(this.dataStart + <usize>index);
|
||||
}
|
||||
|
||||
@operator("[]=") // unchecked is built-in
|
||||
private __set(index: i32, value: native<i8>): void {
|
||||
if (<u32>index >= <u32>this.dataLength) throw new Error("Offset out of bounds");
|
||||
if (<u32>index >= <u32>this.dataLength) throw new RangeError(E_INDEXOUTOFRANGE);
|
||||
store<i8>(this.dataStart + <usize>index, value);
|
||||
}
|
||||
|
||||
@ -104,13 +105,13 @@ export class Uint8Array extends ArrayBufferView {
|
||||
|
||||
@operator("[]") // unchecked is built-in
|
||||
private __get(index: i32): u8 {
|
||||
if (<u32>index >= <u32>this.dataLength) throw new Error("Offset out of bounds");
|
||||
if (<u32>index >= <u32>this.dataLength) throw new RangeError(E_INDEXOUTOFRANGE);
|
||||
return load<u8>(this.dataStart + <usize>index);
|
||||
}
|
||||
|
||||
@operator("[]=") // unchecked is built-in
|
||||
private __set(index: i32, value: native<u8>): void {
|
||||
if (<u32>index >= <u32>this.dataLength) throw new Error("Offset out of bounds");
|
||||
if (<u32>index >= <u32>this.dataLength) throw new RangeError(E_INDEXOUTOFRANGE);
|
||||
store<u8>(this.dataStart + <usize>index, value);
|
||||
}
|
||||
|
||||
@ -186,13 +187,13 @@ export class Uint8ClampedArray extends ArrayBufferView {
|
||||
|
||||
@operator("[]") // unchecked is built-in
|
||||
private __get(index: i32): u8 {
|
||||
if (<u32>index >= <u32>this.dataLength) throw new Error("Offset out of bounds");
|
||||
if (<u32>index >= <u32>this.dataLength) throw new RangeError(E_INDEXOUTOFRANGE);
|
||||
return load<u8>(this.dataStart + <usize>index);
|
||||
}
|
||||
|
||||
@operator("[]=") // unchecked is built-in
|
||||
private __set(index: i32, value: native<u8>): void {
|
||||
if (<u32>index >= <u32>this.dataLength) throw new Error("Offset out of bounds");
|
||||
if (<u32>index >= <u32>this.dataLength) throw new RangeError(E_INDEXOUTOFRANGE);
|
||||
store<u8>(this.dataStart + <usize>index, ~(<i32>value >> 31) & (((255 - value) >> 31) | value));
|
||||
}
|
||||
|
||||
@ -268,13 +269,13 @@ export class Int16Array extends ArrayBufferView {
|
||||
|
||||
@operator("[]") // unchecked is built-in
|
||||
private __get(index: i32): i16 {
|
||||
if (<u32>index >= <u32>this.dataLength >>> alignof<i16>()) throw new Error("Offset out of bounds");
|
||||
if (<u32>index >= <u32>this.dataLength >>> alignof<i16>()) throw new RangeError(E_INDEXOUTOFRANGE);
|
||||
return load<i16>(this.dataStart + (<usize>index << alignof<i16>()));
|
||||
}
|
||||
|
||||
@operator("[]=") // unchecked is built-in
|
||||
private __set(index: i32, value: native<i16>): void {
|
||||
if (<u32>index >= <u32>this.dataLength >>> alignof<i16>()) throw new Error("Offset out of bounds");
|
||||
if (<u32>index >= <u32>this.dataLength >>> alignof<i16>()) throw new RangeError(E_INDEXOUTOFRANGE);
|
||||
store<i16>(this.dataStart + (<usize>index << alignof<i16>()), value);
|
||||
}
|
||||
|
||||
@ -350,13 +351,13 @@ export class Uint16Array extends ArrayBufferView {
|
||||
|
||||
@operator("[]") // unchecked is built-in
|
||||
private __get(index: i32): u16 {
|
||||
if (<u32>index >= <u32>this.dataLength >>> alignof<u16>()) throw new Error("Offset out of bounds");
|
||||
if (<u32>index >= <u32>this.dataLength >>> alignof<u16>()) throw new RangeError(E_INDEXOUTOFRANGE);
|
||||
return load<u16>(this.dataStart + (<usize>index << alignof<u16>()));
|
||||
}
|
||||
|
||||
@operator("[]=") // unchecked is built-in
|
||||
private __set(index: i32, value: native<u16>): void {
|
||||
if (<u32>index >= <u32>this.dataLength >>> alignof<u16>()) throw new Error("Offset out of bounds");
|
||||
if (<u32>index >= <u32>this.dataLength >>> alignof<u16>()) throw new RangeError(E_INDEXOUTOFRANGE);
|
||||
store<u16>(this.dataStart + (<usize>index << alignof<u16>()), value);
|
||||
}
|
||||
|
||||
@ -432,13 +433,13 @@ export class Int32Array extends ArrayBufferView {
|
||||
|
||||
@operator("[]") // unchecked is built-in
|
||||
private __get(index: i32): i32 {
|
||||
if (<u32>index >= <u32>this.dataLength >>> alignof<i32>()) throw new Error("Offset out of bounds");
|
||||
if (<u32>index >= <u32>this.dataLength >>> alignof<i32>()) throw new RangeError(E_INDEXOUTOFRANGE);
|
||||
return load<i32>(this.dataStart + (<usize>index << alignof<i32>()));
|
||||
}
|
||||
|
||||
@operator("[]=") // unchecked is built-in
|
||||
private __set(index: i32, value: i32): void {
|
||||
if (<u32>index >= <u32>this.dataLength >>> alignof<i32>()) throw new Error("Offset out of bounds");
|
||||
if (<u32>index >= <u32>this.dataLength >>> alignof<i32>()) throw new RangeError(E_INDEXOUTOFRANGE);
|
||||
store<i32>(this.dataStart + (<usize>index << alignof<i32>()), value);
|
||||
}
|
||||
|
||||
@ -514,13 +515,13 @@ export class Uint32Array extends ArrayBufferView {
|
||||
|
||||
@operator("[]") // unchecked is built-in
|
||||
private __get(index: i32): u32 {
|
||||
if (<u32>index >= <u32>this.dataLength >>> alignof<u32>()) throw new Error("Offset out of bounds");
|
||||
if (<u32>index >= <u32>this.dataLength >>> alignof<u32>()) throw new RangeError(E_INDEXOUTOFRANGE);
|
||||
return load<u32>(this.dataStart + (<usize>index << alignof<u32>()));
|
||||
}
|
||||
|
||||
@operator("[]=") // unchecked is built-in
|
||||
private __set(index: i32, value: u32): void {
|
||||
if (<u32>index >= <u32>this.dataLength >>> alignof<u32>()) throw new Error("Offset out of bounds");
|
||||
if (<u32>index >= <u32>this.dataLength >>> alignof<u32>()) throw new RangeError(E_INDEXOUTOFRANGE);
|
||||
store<u32>(this.dataStart + (<usize>index << alignof<u32>()), value);
|
||||
}
|
||||
|
||||
@ -596,13 +597,13 @@ export class Int64Array extends ArrayBufferView {
|
||||
|
||||
@operator("[]") // unchecked is built-in
|
||||
private __get(index: i32): i64 {
|
||||
if (<u32>index >= <u32>this.dataLength >>> alignof<i64>()) throw new Error("Offset out of bounds");
|
||||
if (<u32>index >= <u32>this.dataLength >>> alignof<i64>()) throw new RangeError(E_INDEXOUTOFRANGE);
|
||||
return load<i64>(this.dataStart + (<usize>index << alignof<i64>()));
|
||||
}
|
||||
|
||||
@operator("[]=") // unchecked is built-in
|
||||
private __set(index: i32, value: i64): void {
|
||||
if (<u32>index >= <u32>this.dataLength >>> alignof<i64>()) throw new Error("Offset out of bounds");
|
||||
if (<u32>index >= <u32>this.dataLength >>> alignof<i64>()) throw new RangeError(E_INDEXOUTOFRANGE);
|
||||
store<i64>(this.dataStart + (<usize>index << alignof<i64>()), value);
|
||||
}
|
||||
|
||||
@ -678,13 +679,13 @@ export class Uint64Array extends ArrayBufferView {
|
||||
|
||||
@operator("[]") // unchecked is built-in
|
||||
private __get(index: i32): u64 {
|
||||
if (<u32>index >= <u32>this.dataLength >>> alignof<u64>()) throw new Error("Offset out of bounds");
|
||||
if (<u32>index >= <u32>this.dataLength >>> alignof<u64>()) throw new RangeError(E_INDEXOUTOFRANGE);
|
||||
return load<u64>(this.dataStart + (<usize>index << alignof<u64>()));
|
||||
}
|
||||
|
||||
@operator("[]=") // unchecked is built-in
|
||||
private __set(index: i32, value: u64): void {
|
||||
if (<u32>index >= <u32>this.dataLength >>> alignof<u64>()) throw new Error("Offset out of bounds");
|
||||
if (<u32>index >= <u32>this.dataLength >>> alignof<u64>()) throw new RangeError(E_INDEXOUTOFRANGE);
|
||||
store<u64>(this.dataStart + (<usize>index << alignof<u64>()), value);
|
||||
}
|
||||
|
||||
@ -760,13 +761,13 @@ export class Float32Array extends ArrayBufferView {
|
||||
|
||||
@operator("[]") // unchecked is built-in
|
||||
private __get(index: i32): f32 {
|
||||
if (<u32>index >= <u32>this.dataLength >>> alignof<f32>()) throw new Error("Offset out of bounds");
|
||||
if (<u32>index >= <u32>this.dataLength >>> alignof<f32>()) throw new RangeError(E_INDEXOUTOFRANGE);
|
||||
return load<f32>(this.dataStart + (<usize>index << alignof<f32>()));
|
||||
}
|
||||
|
||||
@operator("[]=") // unchecked is built-in
|
||||
private __set(index: i32, value: f32): void {
|
||||
if (<u32>index >= <u32>this.dataLength >>> alignof<f32>()) throw new Error("Offset out of bounds");
|
||||
if (<u32>index >= <u32>this.dataLength >>> alignof<f32>()) throw new RangeError(E_INDEXOUTOFRANGE);
|
||||
store<f32>(this.dataStart + (<usize>index << alignof<f32>()), value);
|
||||
}
|
||||
|
||||
@ -842,13 +843,13 @@ export class Float64Array extends ArrayBufferView {
|
||||
|
||||
@operator("[]") // unchecked is built-in
|
||||
private __get(index: i32): f64 {
|
||||
if (<u32>index >= <u32>this.dataLength >>> alignof<f64>()) throw new Error("Offset out of bounds");
|
||||
if (<u32>index >= <u32>this.dataLength >>> alignof<f64>()) throw new RangeError(E_INDEXOUTOFRANGE);
|
||||
return load<f64>(this.dataStart + (<usize>index << alignof<f64>()));
|
||||
}
|
||||
|
||||
@operator("[]=") // unchecked is built-in
|
||||
private __set(index: i32, value: f64): void {
|
||||
if (<u32>index >= <u32>this.dataLength >>> alignof<f64>()) throw new Error("Offset out of bounds");
|
||||
if (<u32>index >= <u32>this.dataLength >>> alignof<f64>()) throw new RangeError(E_INDEXOUTOFRANGE);
|
||||
store<f64>(this.dataStart + (<usize>index << alignof<f64>()), value);
|
||||
}
|
||||
|
||||
|
18
std/assembly/util/error.ts
Normal file
18
std/assembly/util/error.ts
Normal file
@ -0,0 +1,18 @@
|
||||
// Common error messages for use accross the standard library. Keeping error messages compact
|
||||
// and reusing them where possible ensures minimal static data in binaries.
|
||||
|
||||
// @ts-ignore: decorator
|
||||
@lazy @inline
|
||||
export const E_INDEXOUTOFRANGE: string = "Index out of range";
|
||||
|
||||
// @ts-ignore: decorator
|
||||
@lazy @inline
|
||||
export const E_INVALIDLENGTH: string = "Invalid length";
|
||||
|
||||
// @ts-ignore: decorator
|
||||
@lazy @inline
|
||||
export const E_EMPTYARRAY: string = "Array is empty";
|
||||
|
||||
// @ts-ignore: decorator
|
||||
@lazy @inline
|
||||
export const E_HOLEYARRAY: string = "Element type must be nullable if array is holey";
|
Reference in New Issue
Block a user