This commit is contained in:
dcode 2019-03-13 22:35:47 +01:00
parent 6f70826e45
commit e38f627c8b
10 changed files with 326 additions and 276 deletions

View File

@ -6602,7 +6602,7 @@ export class Compiler extends DiagnosticEmitter {
}
// make a static array if possible
if (isStatic) return this.ensureStaticArray(elementType, constantValues);
// if (isStatic) return this.ensureStaticArray(elementType, constantValues); // TODO
// otherwise obtain the array type
var arrayPrototype = assert(this.program.arrayPrototype);

View File

@ -33,7 +33,7 @@ export class Array<T> extends ArrayBufferView {
var oldData = this.data;
var oldCapacity = oldData.byteLength >>> alignof<T>();
if (<u32>length > <u32>oldCapacity) {
const MAX_LENGTH = ArrayBuffer.MAX_BYTELENGTH >>> alignof<T>();
const MAX_LENGTH = ArrayBufferView.MAX_BYTELENGTH >>> alignof<T>();
if (<u32>length > <u32>MAX_LENGTH) throw new RangeError("Invalid array length");
let newCapacity = <usize>length << alignof<T>();
let newData = REALLOC(changetype<usize>(oldData), newCapacity); // registers on move
@ -373,140 +373,150 @@ export class Array<T> extends ArrayBufferView {
return this;
}
// FIXME: refactor into multiple functions?
join(separator: string = ","): string {
if (isInteger<T>()) {
if (value instanceof bool) return this.join_bool(separator);
return this.join_int(separator);
}
if (isFloat<T>()) return this.join_flt(separator);
if (isString<T>()) return this.join_str(separator);
if (isArray<T>()) return this.join_arr(separator);
if (isReference<T>()) return this.join_ref(separator);
ERROR("unspported element type");
return <string>unreachable();
}
private join_bool(separator: string = ","): string {
var lastIndex = this.length_ - 1;
if (lastIndex < 0) return "";
var result = "";
var value: T;
var base = this.dataStart;
// var buffer = this.buffer_;
var sepLen = separator.length;
var hasSeparator = sepLen != 0;
if (value instanceof bool) {
if (!lastIndex) return select("true", "false", load<T>(base));
var dataStart = this.dataStart;
if (!lastIndex) return select("true", "false", load<bool>(dataStart));
let valueLen = 5; // max possible length of element len("false")
let estLen = (valueLen + sepLen) * lastIndex + valueLen;
let result = ALLOC(estLen << 1);
let offset = 0;
for (let i = 0; i < lastIndex; ++i) {
value = load<T>(base + i);
valueLen = 4 + <i32>(!value);
memory.copy(
result + (<usize>offset << 1),
changetype<usize>(select("true", "false", value)),
<usize>valueLen << 1
);
offset += valueLen;
if (hasSeparator) {
memory.copy(
result + (<usize>offset << 1),
changetype<usize>(separator),
<usize>sepLen << 1
);
offset += sepLen;
}
}
value = load<T>(base + <usize>lastIndex);
var sepLen = separator.length;
var valueLen = 5; // max possible length of element len("false")
var estLen = (valueLen + sepLen) * lastIndex + valueLen;
var result = ALLOC(estLen << 1);
var offset = 0;
var value: bool;
for (let i = 0; i < lastIndex; ++i) {
value = load<bool>(dataStart + i);
valueLen = 4 + <i32>(!value);
memory.copy(
result + (<usize>offset << 1),
changetype<usize>(select("true", "false", value)),
valueLen << 1
<usize>valueLen << 1
);
offset += valueLen;
if (estLen > offset) {
let trimmed = changetype<string>(result).substring(0, offset);
FREE(result);
return trimmed; // registered in .substring
if (sepLen) {
memory.copy(
result + (<usize>offset << 1),
changetype<usize>(separator),
<usize>sepLen << 1
);
offset += sepLen;
}
return REGISTER<string>(result);
} else if (isInteger<T>()) {
if (!lastIndex) return changetype<string>(itoa<T>(load<T>(base)));
}
value = load<bool>(dataStart + <usize>lastIndex);
valueLen = 4 + <i32>(!value);
memory.copy(
result + (<usize>offset << 1),
changetype<usize>(select("true", "false", value)),
valueLen << 1
);
offset += valueLen;
const valueLen = (sizeof<T>() <= 4 ? 10 : 20) + <i32>isSigned<T>();
let estLen = (valueLen + sepLen) * lastIndex + valueLen;
let result = ALLOC(estLen << 1);
let offset = 0;
for (let i = 0; i < lastIndex; ++i) {
value = load<T>(base + (<usize>i << alignof<T>()));
offset += itoa_stream<T>(result, offset, value);
if (hasSeparator) {
memory.copy(
result + (<usize>offset << 1),
changetype<usize>(separator),
<usize>sepLen << 1
);
offset += sepLen;
}
}
value = load<T>(base + (<usize>lastIndex << alignof<T>()));
if (estLen > offset) {
let trimmed = changetype<string>(result).substring(0, offset);
FREE(result);
return trimmed; // registered in .substring
}
return REGISTER<string>(result);
}
private join_int(separator: string = ","): string {
var lastIndex = this.length_ - 1;
if (lastIndex < 0) return "";
var dataStart = this.dataStart;
if (!lastIndex) return changetype<string>(itoa<T>(load<T>(dataStart)));
var sepLen = separator.length;
const valueLen = (sizeof<T>() <= 4 ? 10 : 20) + <i32>isSigned<T>();
var estLen = (valueLen + sepLen) * lastIndex + valueLen;
var result = ALLOC(estLen << 1);
var offset = 0;
var value: T;
for (let i = 0; i < lastIndex; ++i) {
value = load<T>(dataStart + (<usize>i << alignof<T>()));
offset += itoa_stream<T>(result, offset, value);
if (estLen > offset) {
let trimmed = changetype<string>(result).substring(0, offset);
FREE(result);
return trimmed; // registered in .substring
if (sepLen) {
memory.copy(
result + (<usize>offset << 1),
changetype<usize>(separator),
<usize>sepLen << 1
);
offset += sepLen;
}
return REGISTER<string>(result);
} else if (isFloat<T>()) {
if (!lastIndex) return changetype<string>(dtoa(load<T>(base)));
}
value = load<T>(dataStart + (<usize>lastIndex << alignof<T>()));
offset += itoa_stream<T>(result, offset, value);
if (estLen > offset) {
let trimmed = changetype<string>(result).substring(0, offset);
FREE(result);
return trimmed; // registered in .substring
}
return REGISTER<string>(result);
}
const valueLen = MAX_DOUBLE_LENGTH;
let estLen = (valueLen + sepLen) * lastIndex + valueLen;
let result = ALLOC(estLen << 1);
let offset = 0;
for (let i = 0; i < lastIndex; ++i) {
value = load<T>(base + (<usize>i << alignof<T>()));
offset += dtoa_stream(result, offset, value);
if (hasSeparator) {
memory.copy(
result + (<usize>offset << 1),
changetype<usize>(separator),
<usize>sepLen << 1
);
offset += sepLen;
}
}
value = load<T>(base + (<usize>lastIndex << alignof<T>()));
private join_flt(separator: string = ","): string {
var lastIndex = this.length_ - 1;
if (lastIndex < 0) return "";
var dataStart = this.dataStart;
if (!lastIndex) return changetype<string>(dtoa(load<T>(dataStart)));
const valueLen = MAX_DOUBLE_LENGTH;
var sepLen = separator.length;
var estLen = (valueLen + sepLen) * lastIndex + valueLen;
var result = ALLOC(estLen << 1);
var offset = 0;
var value: T;
for (let i = 0; i < lastIndex; ++i) {
value = load<T>(dataStart + (<usize>i << alignof<T>()));
offset += dtoa_stream(result, offset, value);
if (estLen > offset) {
let trimmed = changetype<string>(result).substring(0, offset);
FREE(result);
return trimmed; // registered in .substring
if (sepLen) {
memory.copy(
result + (<usize>offset << 1),
changetype<usize>(separator),
<usize>sepLen << 1
);
offset += sepLen;
}
return REGISTER<string>(result);
} else if (isString<T>()) {
if (!lastIndex) return load<string>(base);
}
value = load<T>(dataStart + (<usize>lastIndex << alignof<T>()));
offset += dtoa_stream(result, offset, value);
if (estLen > offset) {
let trimmed = changetype<string>(result).substring(0, offset);
FREE(result);
return trimmed; // registered in .substring
}
return REGISTER<string>(result);
}
let estLen = 0;
for (let i = 0, len = lastIndex + 1; i < len; ++i) {
estLen += load<string>(base + (<usize>i << alignof<T>())).length;
}
let offset = 0;
let result = ALLOC((estLen + sepLen * lastIndex) << 1);
for (let i = 0; i < lastIndex; ++i) {
value = load<string>(base + (<usize>i << alignof<T>()));
if (value) {
let valueLen = changetype<string>(value).length;
memory.copy(
result + (<usize>offset << 1),
changetype<usize>(value),
<usize>valueLen << 1
);
offset += valueLen;
}
if (hasSeparator) {
memory.copy(
result + (<usize>offset << 1),
changetype<usize>(separator),
<usize>sepLen << 1
);
offset += sepLen;
}
}
value = load<string>(base + (<usize>lastIndex << alignof<T>()));
private join_str(separator: string = ","): string {
var lastIndex = this.length_ - 1;
if (lastIndex < 0) return "";
var dataStart = this.dataStart;
if (!lastIndex) return load<string>(dataStart);
var sepLen = separator.length;
var estLen = 0;
for (let i = 0, len = lastIndex + 1; i < len; ++i) {
estLen += load<string>(dataStart + (<usize>i << alignof<T>())).length;
}
var offset = 0;
var result = ALLOC((estLen + sepLen * lastIndex) << 1);
var value: String;
for (let i = 0; i < lastIndex; ++i) {
value = load<string>(dataStart + (<usize>i << alignof<T>()));
if (value) {
let valueLen = changetype<string>(value).length;
memory.copy(
@ -514,47 +524,66 @@ export class Array<T> extends ArrayBufferView {
changetype<usize>(value),
<usize>valueLen << 1
);
offset += valueLen;
}
return REGISTER<string>(result);
} else if (isArray<T>()) {
if (!lastIndex) {
value = load<T>(base);
return value ? value.join(separator) : "";
if (sepLen) {
memory.copy(
result + (<usize>offset << 1),
changetype<usize>(separator),
<usize>sepLen << 1
);
offset += sepLen;
}
for (let i = 0; i < lastIndex; ++i) {
value = load<T>(base + (<usize>i << alignof<T>()));
if (value) result += value.join(separator);
if (hasSeparator) result += separator;
}
value = load<T>(base + (<usize>lastIndex << alignof<T>()));
}
value = load<string>(dataStart + (<usize>lastIndex << alignof<T>()));
if (value) {
let valueLen = changetype<string>(value).length;
memory.copy(
result + (<usize>offset << 1),
changetype<usize>(value),
<usize>valueLen << 1
);
}
return REGISTER<string>(result);
}
private join_arr(separator: string = ","): string {
var lastIndex = this.length_ - 1;
if (lastIndex < 0) return "";
var result = "";
var sepLen = separator.length;
var base = this.dataStart;
var value: T;
if (!lastIndex) {
value = load<T>(base);
return value ? value.join(separator) : "";
}
for (let i = 0; i < lastIndex; ++i) {
value = load<T>(base + (<usize>i << alignof<T>()));
if (value) result += value.join(separator);
return result; // registered by concatenation (FIXME: lots of garbage)
} else if (isReference<T>()) { // References
if (!lastIndex) return "[object Object]";
const valueLen = 15; // max possible length of element len("[object Object]")
let estLen = (valueLen + sepLen) * lastIndex + valueLen;
let result = ALLOC(estLen << 1);
let offset = 0;
for (let i = 0; i < lastIndex; ++i) {
value = load<T>(base + (<usize>i << alignof<T>()));
if (value) {
memory.copy(
result + (<usize>offset << 1),
changetype<usize>("[object Object]"),
<usize>valueLen << 1
);
offset += valueLen;
}
if (hasSeparator) {
memory.copy(
result + (<usize>offset << 1),
changetype<usize>(separator),
<usize>sepLen << 1
);
offset += sepLen;
}
}
if (load<T>(base + (<usize>lastIndex << alignof<T>()))) {
if (sepLen) result += separator;
}
value = load<T>(base + (<usize>lastIndex << alignof<T>()));
if (value) result += value.join(separator);
return result; // registered by concatenation (FIXME: lots of garbage)
}
private join_ref(separator: string = ","): string {
var lastIndex = this.length_ - 1;
if (lastIndex < 0) return "";
var base = this.dataStart;
if (!lastIndex) return "[object Object]";
const valueLen = 15; // max possible length of element len("[object Object]")
var sepLen = separator.length;
var estLen = (valueLen + sepLen) * lastIndex + valueLen;
var result = ALLOC(estLen << 1);
var offset = 0;
var value: T;
for (let i = 0; i < lastIndex; ++i) {
value = load<T>(base + (<usize>i << alignof<T>()));
if (value) {
memory.copy(
result + (<usize>offset << 1),
changetype<usize>("[object Object]"),
@ -562,16 +591,29 @@ export class Array<T> extends ArrayBufferView {
);
offset += valueLen;
}
if (estLen > offset) {
let out = changetype<string>(result).substring(0, offset);
FREE(result);
return out; // registered in .substring
if (sepLen) {
memory.copy(
result + (<usize>offset << 1),
changetype<usize>(separator),
<usize>sepLen << 1
);
offset += sepLen;
}
return REGISTER<string>(result);
} else {
ERROR("unspported type");
assert(false);
}
if (load<T>(base + (<usize>lastIndex << alignof<T>()))) {
memory.copy(
result + (<usize>offset << 1),
changetype<usize>("[object Object]"),
<usize>valueLen << 1
);
offset += valueLen;
}
if (estLen > offset) {
let out = changetype<string>(result).substring(0, offset);
FREE(result);
return out; // registered in .substring
}
return REGISTER<string>(result);
}
@inline

View File

@ -1,6 +1,6 @@
import { ALLOC_RAW, REGISTER, ArrayBufferBase } from "./runtime";
import { HEADER, ALLOC_RAW, ALLOC, REGISTER, ArrayBufferView } from "./runtime";
@sealed export class ArrayBuffer extends ArrayBufferBase {
@sealed export class ArrayBuffer {
@inline static isView<T>(value: T): bool {
if (value) {
@ -20,7 +20,16 @@ import { ALLOC_RAW, REGISTER, ArrayBufferBase } from "./runtime";
return false;
}
slice(begin: i32 = 0, end: i32 = ArrayBuffer.MAX_BYTELENGTH): ArrayBuffer {
constructor(length: i32) {
if (<u32>length > <u32>ArrayBufferView.MAX_BYTELENGTH) throw new RangeError("Invalid array buffer length");
return REGISTER<ArrayBuffer>(ALLOC(<usize>length));
}
get byteLength(): i32 {
return changetype<HEADER>(changetype<usize>(this) - HEADER_SIZE).payloadSize;
}
slice(begin: i32 = 0, end: i32 = ArrayBufferView.MAX_BYTELENGTH): ArrayBuffer {
var length = this.byteLength;
begin = begin < 0 ? max(length + begin, 0) : min(begin, length);
end = end < 0 ? max(length + end , 0) : min(end , length);

View File

@ -500,5 +500,3 @@ export namespace f64x2 {
export namespace v8x16 {
@builtin export declare function shuffle(a: v128, b: v128, l0: u8, l1: u8, l2: u8, l3: u8, l4: u8, l5: u8, l6: u8, l7: u8, l8: u8, l9: u8, l10: u8, l11: u8, l12: u8, l13: u8, l14: u8, l15: u8): v128;
}
@builtin export declare function start(): void;

31
std/assembly/gc.ts Normal file
View File

@ -0,0 +1,31 @@
/** Garbage collector interface. */
export namespace gc {
/** Gets the computed unique class id of a class type. */
@builtin @unsafe export declare function classId<T>(): u32;
/** Iterates reference root objects. */
@builtin @unsafe export declare function iterateRoots(fn: (ref: usize) => void): void;
/** Registers a managed object to be tracked by the garbage collector. */
@stub @unsafe export function register(ref: usize): void {
ERROR("stub: missing garbage collector");
}
/** Links a registered object with the registered object now referencing it. */
@stub @unsafe export function link(ref: usize, parentRef: usize): void {
ERROR("stub: missing garbage collector");
}
/** Marks an object as being reachable. */
@stub @unsafe export function mark(ref: usize): void {
ERROR("stub: missing garbage collector");
}
/** Performs a full garbage collection cycle. */
@stub export function collect(): void {
WARNING("stub: missing garbage collector");
}
}
// TODO: move marking into userspace using builtins like iterateFields?

62
std/assembly/memory.ts Normal file
View File

@ -0,0 +1,62 @@
import { memcmp, memmove, memset } from "./util/memory";
/** Memory manager interface. */
export namespace memory {
/** Gets the size of the memory in pages. */
@builtin export declare function size(): i32;
/** Grows the memory by the given size in pages and returns the previous size in pages. */
@builtin @unsafe export declare function grow(pages: i32): i32;
/** Fills a section in memory with the specified byte value. */
@builtin @unsafe @inline export function fill(dst: usize, c: u8, n: usize): void {
memset(dst, c, n); // fallback if "bulk-memory" isn't enabled
}
/** Copies a section of memory to another. Has move semantics. */
@builtin @unsafe @inline export function copy(dst: usize, src: usize, n: usize): void {
memmove(dst, src, n); // fallback if "bulk-memory" isn't enabled
}
/** Initializes a memory segment. */
@unsafe export function init(segmentIndex: u32, srcOffset: usize, dstOffset: usize, n: usize): void {
ERROR("not implemented");
}
/** Drops a memory segment. */
@unsafe export function drop(segmentIndex: u32): void {
ERROR("not implemented");
}
/** Dynamically allocates a section of memory and returns its address. */
@stub @inline export function allocate(size: usize): usize {
ERROR("stub: missing memory manager");
return <usize>unreachable();
}
/** Dynamically frees a section of memory by the previously allocated address. */
@stub @unsafe @inline export function free(ptr: usize): void {
ERROR("stub: missing memory manager");
}
/** Resets the memory to its initial state. Arena allocator only. */
@stub @unsafe @inline export function reset(): void {
ERROR("stub: not supported by memory manager");
}
/** Compares a section of memory to another. */
@inline export function compare(vl: usize, vr: usize, n: usize): i32 {
return memcmp(vl, vr, n);
}
/** Repeats a section of memory at a specific address. */
@unsafe export function repeat(dst: usize, src: usize, srcLength: usize, count: usize): void {
var index: usize = 0;
var total = srcLength * count;
while (index < total) {
memory.copy(dst + index, src, srcLength);
index += srcLength;
}
}
}

View File

@ -25,8 +25,7 @@ export function bswap<T>(value: T): T {
return value;
}
@inline
export function bswap16<T>(value: T): T {
@inline export function bswap16<T>(value: T): T {
if (isInteger<T>() && sizeof<T>() <= 4) {
if (sizeof<T>() == 2) {
return <T>((value << 8) | ((value >> 8) & <T>0x00FF));

View File

@ -19,7 +19,7 @@ import { AL_MASK, MAX_SIZE_32 } from "./util/allocator";
// runtime will most likely change significantly once reftypes and WASM GC are a thing.
/** Whether a GC is present or not. */
@inline export const GC = isImplemented(gc.register) && isImplemented(gc.link);
@inline export const GC = isImplemented(gc.register);
/** Size of the common runtime header. */
@inline export const HEADER_SIZE: usize = GC
@ -132,20 +132,9 @@ function unref(ref: usize): HEADER {
if (GC) gc.link(changetype<usize>(ref), changetype<usize>(parentRef)); // tslint:disable-line
}
export abstract class ArrayBufferBase {
export abstract class ArrayBufferView {
@lazy static readonly MAX_BYTELENGTH: i32 = MAX_SIZE_32 - HEADER_SIZE;
constructor(length: i32) {
if (<u32>length > <u32>ArrayBufferBase.MAX_BYTELENGTH) throw new RangeError("Invalid array buffer length");
return REGISTER<ArrayBuffer>(ALLOC(<usize>length));
}
get byteLength(): i32 {
return changetype<HEADER>(changetype<usize>(this) - HEADER_SIZE).payloadSize;
}
}
export abstract class ArrayBufferView {
[key: number]: number;
@unsafe data: ArrayBuffer;
@ -153,9 +142,8 @@ export abstract class ArrayBufferView {
@unsafe dataEnd: usize;
constructor(length: i32, alignLog2: i32) {
if (<u32>length > <u32>ArrayBufferBase.MAX_BYTELENGTH >>> alignLog2) throw new RangeError("Invalid length");
var byteLength = length << alignLog2;
var buffer = new ArrayBuffer(byteLength);
if (<u32>length > <u32>ArrayBufferView.MAX_BYTELENGTH >>> alignLog2) throw new RangeError("Invalid length");
var buffer = new ArrayBuffer(length << alignLog2);
this.data = buffer;
this.dataStart = changetype<usize>(buffer);
this.dataEnd = changetype<usize>(buffer) + <usize>length;
@ -170,83 +158,7 @@ export abstract class ArrayBufferView {
}
get length(): i32 {
ERROR("not implemented");
ERROR("concrete implementation must provide this");
return unreachable();
}
}
export abstract class StringBase {
@lazy static readonly MAX_LENGTH: i32 = (MAX_SIZE_32 - HEADER_SIZE) >> 1;
get length(): i32 {
return changetype<HEADER>(changetype<usize>(this) - HEADER_SIZE).payloadSize >> 1;
}
}
import { memcmp, memmove, memset } from "./util/memory";
export namespace memory {
@builtin export declare function size(): i32;
@builtin @unsafe export declare function grow(pages: i32): i32;
@builtin @unsafe @inline export function fill(dst: usize, c: u8, n: usize): void {
memset(dst, c, n); // fallback if "bulk-memory" isn't enabled
}
@builtin @unsafe @inline export function copy(dst: usize, src: usize, n: usize): void {
memmove(dst, src, n); // fallback if "bulk-memory" isn't enabled
}
@unsafe export function init(segmentIndex: u32, srcOffset: usize, dstOffset: usize, n: usize): void {
ERROR("not implemented");
}
@unsafe export function drop(segmentIndex: u32): void {
ERROR("not implemented");
}
@stub @inline export function allocate(size: usize): usize {
ERROR("stub: missing memory manager");
return <usize>unreachable();
}
@stub @unsafe @inline export function free(ptr: usize): void {
ERROR("stub: missing memory manager");
}
@stub @unsafe @inline export function reset(): void {
ERROR("stub: not supported by memory manager");
}
@inline export function compare(vl: usize, vr: usize, n: usize): i32 {
return memcmp(vl, vr, n);
}
@unsafe export function repeat(dst: usize, src: usize, srcLength: usize, count: usize): void {
var index: usize = 0;
var total = srcLength * count;
while (index < total) {
memory.copy(dst + index, src, srcLength);
index += srcLength;
}
}
}
export namespace gc {
@builtin @unsafe export declare function classId<T>(): u32;
@builtin @unsafe export declare function iterateRoots(fn: (ref: usize) => void): void;
@stub @unsafe export function register(ref: usize): void {
ERROR("stub: missing garbage collector");
}
@stub @unsafe export function link(ref: usize, parentRef: usize): void {
ERROR("stub: missing garbage collector");
}
@stub export function collect(): void {
WARNING("stub: missing garbage collector");
}
}

View File

@ -1,17 +1,13 @@
import {
ALLOC,
REGISTER,
StringBase
} from "./runtime";
import { HEADER, HEADER_SIZE, ALLOC, REGISTER, ArrayBufferView } from "./runtime";
import { MAX_SIZE_32 } from "./util/allocator";
import { compareImpl, parse, CharCode, isWhiteSpaceOrLineTerminator } from "./util/string";
import {
compareImpl,
parse,
CharCode,
isWhiteSpaceOrLineTerminator
} from "./util/string";
@sealed export abstract class String {
@lazy static readonly MAX_LENGTH: i32 = (MAX_SIZE_32 - HEADER_SIZE) >> alignof<u16>();
@sealed export class String extends StringBase {
get length(): i32 {
return changetype<HEADER>(changetype<usize>(this) - HEADER_SIZE).payloadSize >> 1;
}
// TODO Add and handle second argument
static fromCharCode(code: i32): String {

View File

@ -1,2 +1,3 @@
/** Vector abstraction. */
@sealed export class V128 {
}