Skip to content

Add WASI abort, trace and seed implementations #1159

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

Merged
merged 16 commits into from
Mar 13, 2020
Merged
Show file tree
Hide file tree
Changes from 5 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
6 changes: 0 additions & 6 deletions cli/asc.js
Original file line number Diff line number Diff line change
Expand Up @@ -248,12 +248,6 @@ exports.main = function main(argv, options, callback) {
assemblyscript.setNoUnsafe(compilerOptions, args.noUnsafe);
assemblyscript.setPedantic(compilerOptions, args.pedantic);

// Initialize default aliases
assemblyscript.setGlobalAlias(compilerOptions, "Math", "NativeMath");
assemblyscript.setGlobalAlias(compilerOptions, "Mathf", "NativeMathf");
assemblyscript.setGlobalAlias(compilerOptions, "abort", "~lib/builtins/abort");
assemblyscript.setGlobalAlias(compilerOptions, "trace", "~lib/builtins/trace");

// Add or override aliases if specified
if (args.use) {
let aliases = args.use;
Expand Down
9 changes: 6 additions & 3 deletions lib/loader/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,15 @@ function preInstantiate(imports) {
const env = (imports.env = imports.env || {});
env.abort = env.abort || function abort(mesg, file, line, colm) {
const memory = baseModule.memory || env.memory; // prefer exported, otherwise try imported
throw Error("abort: " + getString(memory, mesg) + " at " + getString(memory, file) + ":" + line + ":" + colm);
}
throw Error("abort: " + getString(memory, mesg) + " in " + getString(memory, file) + "(" + line + ":" + colm + ")");
};
env.trace = env.trace || function trace(mesg, n) {
const memory = baseModule.memory || env.memory;
console.log("trace: " + getString(memory, mesg) + (n ? " " : "") + Array.prototype.slice.call(arguments, 2, 2 + n).join(", "));
}
};
env.seed = env.seed || function seed() {
return Date.now();
};
imports.Math = imports.Math || Math;
imports.Date = imports.Date || Date;

Expand Down
9 changes: 9 additions & 0 deletions src/builtins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,10 @@ export namespace BuiltinNames {
export const setArgumentsLength = "~setArgumentsLength";

// std/builtins.ts
export const abort = "~lib/builtins/abort";
export const trace = "~lib/builtins/trace";
export const seed = "~lib/builtins/seed";

export const isInteger = "~lib/builtins/isInteger";
export const isFloat = "~lib/builtins/isFloat";
export const isBoolean = "~lib/builtins/isBoolean";
Expand Down Expand Up @@ -586,6 +590,11 @@ export namespace BuiltinNames {
export const Uint64Array = "~lib/typedarray/Uint64Array";
export const Float32Array = "~lib/typedarray/Float32Array";
export const Float64Array = "~lib/typedarray/Float64Array";

// std/bindings/wasi.ts
export const wasiAbort = "~lib/bindings/wasi/abort";
export const wasiTrace = "~lib/bindings/wasi/trace";
export const wasiSeed = "~lib/bindings/wasi/seed";
}

/** Builtin compilation context. */
Expand Down
4 changes: 4 additions & 0 deletions src/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,8 @@ export namespace CommonNames {
export const ArrayBuffer = "ArrayBuffer";
export const Math = "Math";
export const Mathf = "Mathf";
export const NativeMath = "NativeMath";
export const NativeMathf = "NativeMathf";
export const Int8Array = "Int8Array";
export const Int16Array = "Int16Array";
export const Int32Array = "Int32Array";
Expand All @@ -203,6 +205,8 @@ export namespace CommonNames {
export const Error = "Error";
// runtime
export const abort = "abort";
export const trace = "trace";
export const seed = "seed";
export const pow = "pow";
export const mod = "mod";
export const alloc = "__alloc";
Expand Down
58 changes: 42 additions & 16 deletions src/program.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ import {
import {
Parser
} from "./parser";
import { BuiltinNames } from "./builtins";

/** Represents a yet unresolved `import`. */
class QueuedImport {
Expand Down Expand Up @@ -980,23 +981,48 @@ export class Program extends DiagnosticEmitter {
// set up global aliases
{
let globalAliases = options.globalAliases;
if (globalAliases) {
// TODO: for (let [alias, name] of globalAliases) {
for (let _keys = Map_keys(globalAliases), i = 0, k = _keys.length; i < k; ++i) {
let alias = unchecked(_keys[i]);
let name = assert(globalAliases.get(alias));
if (!name.length) continue; // explicitly disabled
let firstChar = name.charCodeAt(0);
if (firstChar >= CharCode._0 && firstChar <= CharCode._9) {
this.registerConstantInteger(alias, Type.i32, i64_new(<i32>parseInt(name, 10)));
if (!globalAliases) globalAliases = new Map();
if (!globalAliases.has(CommonNames.abort)) {
globalAliases.set(CommonNames.abort,
this.elementsByName.has(BuiltinNames.wasiAbort)
? BuiltinNames.wasiAbort
: BuiltinNames.abort
);
}
if (!globalAliases.has(CommonNames.trace)) {
globalAliases.set(CommonNames.trace,
this.elementsByName.has(BuiltinNames.wasiTrace)
? BuiltinNames.wasiTrace
: BuiltinNames.trace
);
}
if (!globalAliases.has(CommonNames.seed)) {
globalAliases.set(CommonNames.seed,
this.elementsByName.has(BuiltinNames.wasiSeed)
? BuiltinNames.wasiSeed
: BuiltinNames.seed
);
}
if (!globalAliases.has(CommonNames.Math)) {
globalAliases.set(CommonNames.Math, CommonNames.NativeMath);
}
if (!globalAliases.has(CommonNames.Mathf)) {
globalAliases.set(CommonNames.Mathf, CommonNames.NativeMathf);
}
// TODO: for (let [alias, name] of globalAliases) {
for (let _keys = Map_keys(globalAliases), i = 0, k = _keys.length; i < k; ++i) {
let alias = unchecked(_keys[i]);
let name = assert(globalAliases.get(alias));
if (!name.length) continue; // explicitly disabled
let firstChar = name.charCodeAt(0);
if (firstChar >= CharCode._0 && firstChar <= CharCode._9) {
this.registerConstantInteger(alias, Type.i32, i64_new(<i32>parseInt(name, 10)));
} else {
let elementsByName = this.elementsByName;
if (elementsByName.has(name)) {
elementsByName.set(alias, assert(elementsByName.get(name)));
} else {
let elementsByName = this.elementsByName;
let element = elementsByName.get(name);
if (element) {
if (elementsByName.has(alias)) throw new Error("duplicate global element: " + name);
elementsByName.set(alias, element);
}
else throw new Error("no such global element: " + name);
throw new Error("no such global element: " + name);
}
}
}
Expand Down
119 changes: 119 additions & 0 deletions std/assembly/bindings/wasi.ts
Original file line number Diff line number Diff line change
@@ -1 +1,120 @@
import {
proc_exit,
fd_write,
iovec,
random_get
} from "./wasi_snapshot_preview1";

import {
MAX_DOUBLE_LENGTH,
decimalCount32,
dtoa_stream
} from "util/number";

export * from "./wasi_snapshot_preview1";

/** A WASI-aware abort implementation. */
function abort(
message: string | null = null,
fileName: string | null = null,
lineNumber: u32 = 0,
columnNumber: u32 = 0
): void {
// 0: iov.buf
// 4: iov.buf_len
// 8: len
// 12: buf...
const iovPtr: usize = 0;
const bufPtr: usize = iovPtr + offsetof<iovec>() + sizeof<usize>();
changetype<iovec>(iovPtr).buf = bufPtr;
var ptr = bufPtr;
store<u64>(ptr, 0x203A74726F6261); ptr += 7; // 'abort: '
if (message !== null) {
ptr += String.UTF8.encodeUnsafe(changetype<usize>(message), message.length, ptr);
}
store<u32>(ptr, 0x206E6920); ptr += 4; // ' in '
if (fileName !== null) {
ptr += String.UTF8.encodeUnsafe(changetype<usize>(fileName), fileName.length, ptr);
}
store<u8>(ptr++, 0x28); // (
var len = decimalCount32(lineNumber); ptr += len;
do {
let t = lineNumber / 10;
store<u8>(--ptr, 0x30 + lineNumber % 10);
lineNumber = t;
} while (lineNumber); ptr += len;
store<u8>(ptr++, 0x3A); // :
len = decimalCount32(columnNumber); ptr += len;
do {
let t = columnNumber / 10;
store<u8>(--ptr, 0x30 + columnNumber % 10);
columnNumber = t;
} while (columnNumber); ptr += len;
store<u16>(ptr, 0x0A29); ptr += 2; // )\n
changetype<iovec>(iovPtr).buf_len = ptr - bufPtr;
fd_write(2, iovPtr, 1, offsetof<iovec>());
proc_exit(255);
}

/** A WASI-aware trace implementation. */
function trace(
message: string,
n: i32 = 0,
a0: f64 = 0,
a1: f64 = 0,
a2: f64 = 0,
a3: f64 = 0,
a4: f64 = 0
): void {
// 0: iov.buf
// 4: iov.buf_len
// 8: len
// 12: buf...
var iovPtr = __alloc(offsetof<iovec>() + sizeof<usize>() + 1 + <usize>(max(String.UTF8.byteLength(message), MAX_DOUBLE_LENGTH << 1)), 0);
var lenPtr = iovPtr + offsetof<iovec>();
var bufPtr = lenPtr + sizeof<usize>();
changetype<iovec>(iovPtr).buf = bufPtr;
store<u64>(bufPtr, 0x203A6563617274); // 'trace: '
changetype<iovec>(iovPtr).buf_len = 7;
fd_write(1, iovPtr, 1, lenPtr);
changetype<iovec>(iovPtr).buf_len = String.UTF8.encodeUnsafe(changetype<usize>(message), message.length, bufPtr);
fd_write(1, iovPtr, 1, lenPtr);
if (n) {
store<u8>(bufPtr++, 0x20); // space
changetype<iovec>(iovPtr).buf_len = 1 + String.UTF8.encodeUnsafe(bufPtr, dtoa_stream(bufPtr, 0, a0), bufPtr);
fd_write(1, iovPtr, 1, lenPtr);
if (n > 1) {
changetype<iovec>(iovPtr).buf_len = 1 + String.UTF8.encodeUnsafe(bufPtr, dtoa_stream(bufPtr, 0, a1), bufPtr);
fd_write(1, iovPtr, 1, lenPtr);
if (n > 2) {
changetype<iovec>(iovPtr).buf_len = 1 + String.UTF8.encodeUnsafe(bufPtr, dtoa_stream(bufPtr, 0, a2), bufPtr);
fd_write(1, iovPtr, 1, lenPtr);
if (n > 3) {
changetype<iovec>(iovPtr).buf_len = 1 + String.UTF8.encodeUnsafe(bufPtr, dtoa_stream(bufPtr, 0, a3), bufPtr);
fd_write(1, iovPtr, 1, lenPtr);
if (n > 4) {
changetype<iovec>(iovPtr).buf_len = 1 + String.UTF8.encodeUnsafe(bufPtr, dtoa_stream(bufPtr, 0, a4), bufPtr);
fd_write(1, iovPtr, 1, lenPtr);
}
}
}
}
--bufPtr;
}
store<u8>(bufPtr, 0x0A); // \n
changetype<iovec>(iovPtr).buf_len = 1;
fd_write(1, iovPtr, 1, lenPtr);
__free(iovPtr);
}

/** A WASI-aware seed implementation. */
function seed(): f64 {
var temp = load<u64>(0);
var rand: u64;
do {
random_get(0, 8); // to be sure
rand = load<u64>(0);
} while (!rand);
store<u64>(0, temp);
return reinterpret<f64>(rand);
}
4 changes: 4 additions & 0 deletions std/assembly/builtins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1939,3 +1939,7 @@ declare function trace(
a3?: f64,
a4?: f64
): void;

// @ts-ignore: decorator
@external("env", "seed")
declare function seed(): f64;
10 changes: 9 additions & 1 deletion std/assembly/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1460,6 +1460,8 @@ declare namespace String {
export function byteLength(str: string, nullTerminated?: bool): i32;
/** Encodes the specified string to UTF-8 bytes, optionally null terminated. */
export function encode(str: string, nullTerminated?: bool): ArrayBuffer;
/** Encodes the specified raw string to UTF-8 bytes, opionally null terminated. Returns the number of bytes written. */
export function encodeUnsafe(str: usize, len: i32, buf: usize, nullTerminated?: bool): usize;
/** Decodes the specified buffer from UTF-8 bytes to a string, optionally null terminated. */
export function decode(buf: ArrayBuffer, nullTerminated?: bool): string;
/** Decodes raw UTF-8 bytes to a string, optionally null terminated. */
Expand All @@ -1471,6 +1473,8 @@ declare namespace String {
export function byteLength(str: string): i32;
/** Encodes the specified string to UTF-16 bytes. */
export function encode(str: string): ArrayBuffer;
/** Encodes the specified raw string to UTF-16 bytes. Returns the number of bytes written. */
export function encodeUnsafe(str: usize, len: i32, buf: usize): usize;
/** Decodes the specified buffer from UTF-16 bytes to a string. */
export function decode(buf: ArrayBuffer): string;
/** Decodes raw UTF-16 bytes to a string. */
Expand Down Expand Up @@ -1707,8 +1711,12 @@ declare const Math: IMath<f64>;
/** Alias of {@link NativeMathf} or {@link JSMath} respectively. Defaults to `NativeMathf`. */
declare const Mathf: IMath<f32>;

/** Environmental tracing function for debugging purposes. */
/** Environmental abort function. */
declare function abort(msg?: string | null, fileName?: string | null, lineNumber?: i32, columnNumber?: i32): never;
/** Environmental tracing function. */
declare function trace(msg: string, n?: i32, a0?: f64, a1?: f64, a2?: f64, a3?: f64, a4?: f64): void;
/** Environmental seeding function. */
declare function seed(): f64;

// Decorators

Expand Down
4 changes: 2 additions & 2 deletions std/assembly/math.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1410,7 +1410,7 @@ export namespace NativeMath {
}

export function random(): f64 { // see: v8/src/base/utils/random-number-generator.cc
if (!random_seeded) throw new Error("PRNG must be seeded.");
if (!random_seeded) seedRandom(reinterpret<i64>(seed()));
var s1 = random_state0_64;
var s0 = random_state1_64;
random_state0_64 = s0;
Expand Down Expand Up @@ -2603,7 +2603,7 @@ export namespace NativeMathf {

// Using xoroshiro64starstar from http://xoshiro.di.unimi.it/xoroshiro64starstar.c
export function random(): f32 {
if (!random_seeded) throw new Error("PRNG must be seeded.");
if (!random_seeded) seedRandom(reinterpret<i64>(seed()));

var s0 = random_state0_32;
var s1 = random_state1_32;
Expand Down
Loading