diff --git a/build.sh b/build.sh index e39e526..4df119d 100755 --- a/build.sh +++ b/build.sh @@ -21,10 +21,10 @@ if [ -n "$wasm" ]; then cpp_compiler="clang++" c_compiler="clang" - flags="-target wasm32 -nostdlib -Wl,--no-entry -c -static -mbulk-memory -mbulk-memory-opt -matomics -msimd128 -mno-gc" + flags="-target wasm32 -nostdlib -c -static -mbulk-memory -mbulk-memory-opt -matomics -msimd128 -mno-gc" files="${ext_path}/tinyalloc/tinyalloc.c" includes="-I${ext_path}/tinyalloc" - out="" + out="-obuild/dlibincludes.wasm" $c_compiler $flags $includes $files $out else diff --git a/aliases.d b/dlib/aliases.d similarity index 99% rename from aliases.d rename to dlib/aliases.d index 135bf5c..07e86e3 100644 --- a/aliases.d +++ b/dlib/aliases.d @@ -39,9 +39,7 @@ alias f64 = double; alias b32 = uint; -alias intptr = i64; -alias uintptr = u64; - +alias uintptr = size_t; alias usize = size_t; alias Vec2 = Vector!(f32, 2); diff --git a/alloc.d b/dlib/alloc.d similarity index 99% rename from alloc.d rename to dlib/alloc.d index 0b13654..07bd62a 100644 --- a/alloc.d +++ b/dlib/alloc.d @@ -72,6 +72,12 @@ WasmGrow(u32 size) return grow(0, size); } +extern(C) u32 +WasmGrow2(u32 index, u32 size) +{ + return grow(0, size); +} + extern(C) u32 WasmSize() { diff --git a/assets.d b/dlib/assets.d similarity index 100% rename from assets.d rename to dlib/assets.d diff --git a/dlibincludes.c b/dlib/dlibincludes.c similarity index 100% rename from dlibincludes.c rename to dlib/dlibincludes.c diff --git a/external.d b/dlib/externdecl.d similarity index 100% rename from external.d rename to dlib/externdecl.d diff --git a/fonts.d b/dlib/fonts.d similarity index 100% rename from fonts.d rename to dlib/fonts.d diff --git a/math.d b/dlib/math.d similarity index 99% rename from math.d rename to dlib/math.d index aa99932..98bbec3 100644 --- a/math.d +++ b/dlib/math.d @@ -5,7 +5,7 @@ import dlib.util; import std.traits; import std.math.traits; -import std.meta; +import core.internal.traits : allSatisfy; public import std.math.rounding : round; public import core.math : sqrt, cos, sin; @@ -815,7 +815,7 @@ align(16) struct Matrix(T, int D) this(U...)(U values) { - static if((U.length == N) && allSatisfy!(IsTypeAssignable, U)) + static if((U.length == N) && (allSatisfy!(IsTypeAssignable, U) || allSatisfy!(isFloatingPoint, U))) { static foreach(i, x; values) { diff --git a/package.d b/dlib/package.d similarity index 52% rename from package.d rename to dlib/package.d index 6b42911..85fc176 100644 --- a/package.d +++ b/dlib/package.d @@ -11,7 +11,28 @@ public import dlib.externdecl; version(WebAssembly) { + version(unittest) + { + extern(C) void _start() + { + import wasm; + import std.format; + char[512] buffer; + u32[5] arr = [3, 55, 123, 528, 3943]; + string str = Str(sformat(buffer, "Nigger %s pppp %s", "faggot", arr)); + + Console(str, true); + + static foreach(test_fn; __traits(getUnitTests, __traits(parent, _start))) + { + test_fn(); + } + + + Console("success", true); + } + } } else { diff --git a/platform.d b/dlib/platform.d similarity index 100% rename from platform.d rename to dlib/platform.d diff --git a/util.d b/dlib/util.d similarity index 99% rename from util.d rename to dlib/util.d index 827a926..d30fd40 100644 --- a/util.d +++ b/dlib/util.d @@ -12,7 +12,7 @@ import core.stdc.string; static if(NativeTarget) { import std.format : sformat; - import std.stdio : write, writeln, writef, writefln, stderr; + import std.stdio : writeln, writef, writefln; import dlib.platform; } diff --git a/external/arsd-webassembly/arsd/color.d b/external/arsd-webassembly/arsd/color.d deleted file mode 100644 index b453f72..0000000 --- a/external/arsd-webassembly/arsd/color.d +++ /dev/null @@ -1,224 +0,0 @@ -module arsd.color; - -char toHex(int a) { - if(a < 10) - return cast(char) (a + '0'); - else - return cast(char) (a - 10 + 'a'); -} - -struct Color { - int r, g, b, a; - this(int r, int g, int b, int a = 255) { - this.r = r; - this.g = g; - this.b = b; - this.a = a; - } - - void toTempString(char[] data) { - data[0] = '#'; - data[1] = toHex(r >> 4); - data[2] = toHex(r & 0x0f); - data[3] = toHex(g >> 4); - data[4] = toHex(g & 0x0f); - data[5] = toHex(b >> 4); - data[6] = toHex(b & 0x0f); - } - - static Color fromHsl(double h, double s, double l, double a = 255) { - h = h % 360; - - double C = (1 - absInternal(2 * l - 1)) * s; - - double hPrime = h / 60; - - double X = C * (1 - absInternal(hPrime % 2 - 1)); - - double r, g, b; - - if(h is double.nan) - r = g = b = 0; - else if (hPrime >= 0 && hPrime < 1) { - r = C; - g = X; - b = 0; - } else if (hPrime >= 1 && hPrime < 2) { - r = X; - g = C; - b = 0; - } else if (hPrime >= 2 && hPrime < 3) { - r = 0; - g = C; - b = X; - } else if (hPrime >= 3 && hPrime < 4) { - r = 0; - g = X; - b = C; - } else if (hPrime >= 4 && hPrime < 5) { - r = X; - g = 0; - b = C; - } else if (hPrime >= 5 && hPrime < 6) { - r = C; - g = 0; - b = X; - } - - double m = l - C / 2; - - r += m; - g += m; - b += m; - - return Color( - cast(int)(r * 255), - cast(int)(g * 255), - cast(int)(b * 255), - cast(int)(a)); - } - - static immutable Color white = Color(255, 255, 255, 255); - static immutable Color black = Color(0, 0, 0, 255); - static immutable Color red = Color(255, 0, 0, 255); - static immutable Color blue = Color(0, 0, 255, 255); - static immutable Color green = Color(0, 255, 0, 255); - static immutable Color yellow = Color(255, 255, 0, 255); - static immutable Color teal = Color(0, 255, 255, 255); - static immutable Color purple = Color(255, 0, 255, 255); - static immutable Color gray = Color(127, 127, 127, 255); - static immutable Color transparent = Color(0, 0, 0, 0); -} - -struct Point { - int x; - int y; - - pure const nothrow @safe: - - Point opBinary(string op)(in Point rhs) @nogc { - return Point(mixin("x" ~ op ~ "rhs.x"), mixin("y" ~ op ~ "rhs.y")); - } - - Point opBinary(string op)(int rhs) @nogc { - return Point(mixin("x" ~ op ~ "rhs"), mixin("y" ~ op ~ "rhs")); - } - -} - -struct Size { - int width; - int height; -} - - -nothrow @safe @nogc pure -double absInternal(double a) { return a < 0 ? -a : a; } - -struct Rectangle { - int left; /// - int top; /// - int right; /// - int bottom; /// - - pure const nothrow @safe @nogc: - - /// - this(int left, int top, int right, int bottom) { - this.left = left; - this.top = top; - this.right = right; - this.bottom = bottom; - } - - /// - this(in Point upperLeft, in Point lowerRight) { - this(upperLeft.x, upperLeft.y, lowerRight.x, lowerRight.y); - } - - /// - this(in Point upperLeft, in Size size) { - this(upperLeft.x, upperLeft.y, upperLeft.x + size.width, upperLeft.y + size.height); - } - - /// - @property Point upperLeft() { - return Point(left, top); - } - - /// - @property Point upperRight() { - return Point(right, top); - } - - /// - @property Point lowerLeft() { - return Point(left, bottom); - } - - /// - @property Point lowerRight() { - return Point(right, bottom); - } - - /// - @property Point center() { - return Point((right + left) / 2, (bottom + top) / 2); - } - - /// - @property Size size() { - return Size(width, height); - } - - /// - @property int width() { - return right - left; - } - - /// - @property int height() { - return bottom - top; - } - - /// Returns true if this rectangle entirely contains the other - bool contains(in Rectangle r) { - return contains(r.upperLeft) && contains(r.lowerRight); - } - - /// ditto - bool contains(in Point p) { - return (p.x >= left && p.x < right && p.y >= top && p.y < bottom); - } - - /// Returns true of the two rectangles at any point overlap - bool overlaps(in Rectangle r) { - // the -1 in here are because right and top are exclusive - return !((right-1) < r.left || (r.right-1) < left || (bottom-1) < r.top || (r.bottom-1) < top); - } - - /++ - Returns a Rectangle representing the intersection of this and the other given one. - - History: - Added July 1, 2021 - +/ - Rectangle intersectionOf(in Rectangle r) { - auto tmp = Rectangle(max(left, r.left), max(top, r.top), min(right, r.right), min(bottom, r.bottom)); - if(tmp.left >= tmp.right || tmp.top >= tmp.bottom) - tmp = Rectangle.init; - - return tmp; - } -} - -private int max(int a, int b) @nogc nothrow pure @safe { - return a >= b ? a : b; -} -private int min(int a, int b) @nogc nothrow pure @safe { - return a <= b ? a : b; -} - - -enum arsd_jsvar_compatible = "arsd_jsvar_compatible"; -class MemoryImage {} diff --git a/external/arsd-webassembly/arsd/simpleaudio.d b/external/arsd-webassembly/arsd/simpleaudio.d deleted file mode 100644 index 47099cc..0000000 --- a/external/arsd-webassembly/arsd/simpleaudio.d +++ /dev/null @@ -1,8 +0,0 @@ -module arsd.simpleaudio; - -struct AudioOutputThread { - this(int) {} - void start() {} - void beep(int = 0) {} - void boop(int = 0) {} -} diff --git a/external/arsd-webassembly/arsd/simpledisplay.d b/external/arsd-webassembly/arsd/simpledisplay.d deleted file mode 100644 index de2a941..0000000 --- a/external/arsd-webassembly/arsd/simpledisplay.d +++ /dev/null @@ -1,315 +0,0 @@ -module arsd.simpledisplay; - -public import arsd.color; - -import arsd.webassembly; - -//shared static this() { eval("hi there"); } - -// the js bridge is SO EXPENSIVE we have to minimize using it. - -class SimpleWindow { - this(int width, int height, string title = "D Application") { - this.width = width; - this.height = height; - - element = eval!NativeHandle(q{ - var s = document.getElementById("screen"); - var canvas = document.createElement("canvas"); - canvas.addEventListener("contextmenu", function(event) { event.preventDefault(); }); - canvas.setAttribute("width", $0); - canvas.setAttribute("height", $1); - canvas.setAttribute("title", $2); - s.appendChild(canvas); - return canvas; - }, width, height, title); - - canvasContext = eval!NativeHandle(q{ - return $0.getContext("2d"); - }, element); - } - - NativeHandle element; - NativeHandle canvasContext; - int width; - int height; - - void close() { - eval(q{ clearInterval($0); }, intervalId); - intervalId = 0; - } - - void delegate() onClosing; - - ScreenPainter draw() { - return ScreenPainter(this); - } - - int intervalId; - - void eventLoop(T...)(int timeout, T t) { - foreach(arg; t) { - static if(is(typeof(arg) : void delegate())) { - sdpy_timer = arg; - } else static if(is(typeof(arg) : void delegate(KeyEvent))) { - sdpy_key = arg; - } else static if(is(typeof(arg) : void delegate(MouseEvent))) { - sdpy_mouse = arg; - } else static assert(0, typeof(arg).stringof); - } - - if(timeout) - intervalId = eval!int(q{ - return setInterval(function(a) { exports.sdpy_timer_trigger(); }, $0); - }, timeout); - - eval(q{ - function translate(key) { - var k = 0; - switch(key) { - case "[": k = 1; break; - case "]": k = 2; break; - case "Left": case "ArrowLeft": k = 3; break; - case "Right": case "ArrowRight": k = 4; break; - case "Down": case "ArrowDown": k = 5; break; - case "Up": case "ArrowUp": k = 6; break; - case " ": k = 7; break; - // "Enter", "Esc" / "Escape" - default: k = 0; - } - return k; - } - document.body.addEventListener("keydown", function(event) { - exports.sdpy_key_trigger(1, translate(event.key)); - event.preventDefault(); - }, true); - document.body.addEventListener("keyup", function(event) { - exports.sdpy_key_trigger(0, translate(event.key)); - event.preventDefault(); - }, true); - $0.addEventListener("mousedown", function(event) { - exports.sdpy_mouse_trigger(1, event.button, event.offsetX, event.offsetY); - }, true); - $0.addEventListener("mouseup", function(event) { - exports.sdpy_mouse_trigger(0, event.button); - }, true); - }, element); - - } -} - -void delegate() sdpy_timer; -void delegate(KeyEvent) sdpy_key; -void delegate(MouseEvent) sdpy_mouse; - -export extern(C) void sdpy_timer_trigger() { - sdpy_timer(); -} -export extern(C) void sdpy_key_trigger(int pressed, int key) { - KeyEvent ke; - ke.pressed = pressed ? true : false; - ke.key = key; - if(sdpy_key) - sdpy_key(ke); -} -export extern(C) void sdpy_mouse_trigger(int pressed, int button, int x, int y) { - MouseEvent me; - me.type = pressed ? MouseEventType.buttonPressed : MouseEventType.buttonReleased; - switch(button) { - case 0: - me.button = MouseButton.left; - break; - case 1: - me.button = MouseButton.middle; - break; - case 2: - me.button = MouseButton.right; - break; - default: - } - me.x = x; - me.y = y; - if(sdpy_mouse) - sdpy_mouse(me); - -} - - -// push arguments in reverse order then push the command -enum canvasRender = q{ -}; - -struct ScreenPainter { - this(SimpleWindow window) { - // no need to arc here tbh - this.w = window.width; - this.h = window.height; - - this.element = NativeHandle(window.element.handle, false); - this.context = NativeHandle(window.canvasContext.handle, false); - } - @disable this(this); // for now... - NativeHandle element; - NativeHandle context; - - private int w, h; - - void clear() { - addCommand(1); - } - - void outlineColor(Color c) { - char[7] data; - c.toTempString(data[]); - addCommand(2, 7, data[0], data[1], data[2], data[3], data[4], data[5], data[6]); - return; - //context.properties.strokeStyle!string = cast(immutable)(data[]); - } - void fillColor(Color c) { - char[7] data; - c.toTempString(data[]); - addCommand(3, 7, data[0], data[1], data[2], data[3], data[4], data[5], data[6]); - return; - //context.properties.fillStyle!string = cast(immutable)(data[]); - } - - void drawPolygon(Point[] points) { - addCommand(8); - addCommand(cast(double) points.length); - foreach(point; points) { - push(cast(double) point.x); - push(cast(double) point.y); - } - } - - void drawRectangle(Point p, int w, int h) { - addCommand(4, p.x, p.y, w, h); - } - void drawRectangle(Point p, Size s) { - drawRectangle(p, s.width, s.height); - } - void drawText(Point p, in char[] txt, Point lowerRight = Point(0, 0), uint alignment = 0) { - // FIXME use the new system - addCommand(5, p.x, p.y + 16, txt.length); - foreach(c; txt) - push(cast(double) c); - return; - eval(q{ - var context = $0; - context.font = "18px sans-serif"; - context.strokeText($1, $2, $3 + 16); - }, context, txt, p.x, p.y); - } - - void drawCircle(Point upperLeft, int diameter) { - addCommand(6, upperLeft.x + diameter / 2, upperLeft.y + diameter / 2, diameter / 2); - } - - void drawLine(Point p1, Point p2) { - drawLine(p1.x, p1.y, p2.x, p2.y); - } - - void drawLine(int x1, int y1, int x2, int y2) { - addCommand(7, x1, y1, x2, y2); - } - - private: - void addCommand(T...)(int cmd, T args) { - push(cmd); - foreach(arg; args) { - push(arg); - } - } - - // 50ish % on ronaroids total cpu without this - // with it, we at like 16% - static __gshared double[] commandStack; - size_t commandStackPosition; - - void push(T)(T t) { - if(commandStackPosition == commandStack.length) { - commandStack.length = commandStack.length + 1024; - commandStack.assumeUniqueReference(); - } - - commandStack[commandStackPosition++] = t; - } - - ~this() { - executeCanvasCommands(this.context.handle, this.commandStack.ptr, commandStackPosition); - } -} - -extern(C) void executeCanvasCommands(int handle, double* start, size_t len); - -struct KeyEvent { - int key; - bool pressed; -} - -enum MouseEventType : int { - motion = 0, /// The mouse moved inside the window - buttonPressed = 1, /// A mouse button was pressed or the wheel was spun - buttonReleased = 2, /// A mouse button was released -} - -struct MouseEvent { - MouseEventType type; - int x; - int y; - int dx; - int dy; - - MouseButton button; - int modifierState; -} - -enum MouseButton : int { - none = 0, - left = 1, /// - right = 2, /// - middle = 4, /// - wheelUp = 8, /// - wheelDown = 16, /// - backButton = 32, /// often found on the thumb and used for back in browsers - forwardButton = 64, /// often found on the thumb and used for forward in browsers -} - -enum TextAlignment : uint { - Left = 0, /// - Center = 1, /// - Right = 2, /// - - VerticalTop = 0, /// - VerticalCenter = 4, /// - VerticalBottom = 8, /// -} - -enum Key { - LeftBracket = 1, - RightBracket, - Left, - Right, - Down, - Up, - Space -} - -enum MouseCursor { cross } - -class OperatingSystemFont {} -enum UsingSimpledisplayX11 = false; -enum SimpledisplayTimerAvailable = false; - - -class Sprite{} - -enum bool OpenGlEnabled = false; - -alias ScreenPainterImplementation = ScreenPainter; - -mixin template ExperimentalTextComponent() { - class TextLayout { - - } -} diff --git a/external/arsd-webassembly/arsd/webassembly.d b/external/arsd-webassembly/arsd/webassembly.d deleted file mode 100644 index 6bbfb41..0000000 --- a/external/arsd-webassembly/arsd/webassembly.d +++ /dev/null @@ -1,178 +0,0 @@ -/+ - This is the D interface to my webassembly javascript bridge. -+/ -module arsd.webassembly; - -struct AcquireArgument { - int type; - const(void)* ptr; - int length; -} - -// the basic bridge functions defined in webassembly-core.js { - -@trusted @nogc pure nothrow -{ - extern(C) void retain(int); - extern(C) void release(int); - extern(C) int acquire(int returnType, string callingModuleName, string code, AcquireArgument[] arguments); - extern(C) void abort(); - extern(C) int monotimeNow(); -} - - -// } - -export extern(C) int invoke_d_array_delegate(size_t ptr, size_t funcptr, ubyte[] arg) { - void delegate(in ubyte[] arr) dg; - - dg.ptr = cast(void*) ptr; - dg.funcptr = cast(typeof(dg.funcptr)) funcptr; - - dg(arg); - return 0; - -}; - -/++ - Evaluates the given code in Javascript. The arguments are available in JS as $0, $1, $2, .... - The `this` object in the evaluated code is set to an object representing the D module that - you can store some stuff in across calls without having to hit the global namespace. - - Note that if you want to return a value from javascript, you MUST use the return keyword - in the script string. - - Wrong: `eval!NativeHandle("document");` - - Right: `eval!NativeHandle("return document");` -+/ -template eval(T = void) { - T eval(Args...)(string code, Args args, string callingModuleName = __MODULE__) @trusted @nogc pure { - AcquireArgument[Args.length] aa; - foreach(idx, ref arg; args) { - // FIXME: some other type for unsigned.... - static if(is(typeof(arg) : const int)) { - aa[idx].type = 0; - aa[idx].ptr = cast(void*) arg; - aa[idx].length = arg.sizeof; - } else static if(is(immutable typeof(arg) == immutable string)) { - aa[idx].type = 1; - aa[idx].ptr = arg.ptr; - aa[idx].length = arg.length; - } else static if(is(immutable typeof(arg) == immutable NativeHandle)) { - aa[idx].type = 2; - aa[idx].ptr = cast(void*) arg.handle; - aa[idx].length = NativeHandle.sizeof; - } else static if(is(typeof(arg) : const float)) { - aa[idx].type = 3; - aa[idx].ptr = cast(void*) &arg; - aa[idx].length = arg.sizeof; - } else static if(is(immutable typeof(arg) == immutable ubyte[])) { - aa[idx].type = 4; - aa[idx].ptr = arg.ptr; - aa[idx].length = arg.length; - /* - } else static if(is(typeof(arg) == delegate)) { - aa[idx].type = 5; - aa[idx].ptr = cast(void*) &arg; - aa[idx].length = arg.sizeof; - */ - } else { - static assert(0); - } - } - static if(is(T == void)) - acquire(0, callingModuleName, code, aa[]); - else static if(is(T == int)) - return acquire(1, callingModuleName, code, aa[]); - else static if(is(T == float)) - return *cast(float*) cast(void*) acquire(2, callingModuleName, code, aa[]); - else static if(is(T == NativeHandle)) - return NativeHandle(acquire(3, callingModuleName, code, aa[])); - else static if(is(T == string)) { - auto ptr = cast(int*) acquire(7, callingModuleName, code, aa[]); - auto len = *ptr; - ptr++; - return (cast(immutable(char)*) ptr)[0 .. len]; - } - else static assert(0); - } -} - -// and do some opDispatch on the native things to call their methods and it should look p cool - -struct NativeHandle { - @trusted @nogc pure: - - int handle; - bool arc; - this(int handle, bool arc = true) { - this.handle = handle; - this.arc = arc; - } - - this(this) { - if(arc) retain(handle); - } - - ~this() { - if(arc) release(handle); - } - - // never store these, they don't affect the refcount - PropertiesHelper properties() { - return PropertiesHelper(handle); - } - - // never store these, they don't affect the refcount - MethodsHelper methods() { - return MethodsHelper(handle); - - } -} - -struct MethodsHelper { - @trusted @nogc pure: - @disable this(); - @disable this(this); - - int handle; - private this(int handle) { this.handle = handle; } - - template opDispatch(string name) { - template opDispatch(T = NativeHandle) - { - T opDispatch(Args...)(Args args, string callingModuleName = __MODULE__) @trusted @nogc pure - { - return eval!T(q{ - return $0[$1].apply($0, Array.prototype.slice.call(arguments, 2)); - }, NativeHandle(this.handle, false), name, args, callingModuleName); - } - } - } - -} -struct PropertiesHelper { - @trusted @nogc pure: - @disable this(); - @disable this(this); - - int handle; - private this(int handle) { this.handle = handle; } - - template opDispatch(string name) { - template opDispatch(T = NativeHandle) { - T opDispatch() { - return eval!T(q{ - return $0[$1]; - }, NativeHandle(this.handle, false), name); - } - - void opDispatch(T value) { - return eval!void(q{ - return $0[$1] = $2; - }, NativeHandle(this.handle, false), name, value); - } - } - } -} diff --git a/external/arsd-webassembly/std/stdio.d b/external/arsd-webassembly/std/stdio.d deleted file mode 100644 index 5a9275b..0000000 --- a/external/arsd-webassembly/std/stdio.d +++ /dev/null @@ -1,17 +0,0 @@ -module std.stdio; - -import arsd.webassembly; - -void writeln(T...)(T t) { - eval(q{ - var str = ""; - for(var i = 0; i < arguments.length; i++) - str += arguments[i]; - - str += "\n"; - - var txt = document.createTextNode(str); - var fd = document.getElementById("stdout"); - fd.appendChild(txt); - }, t); -} \ No newline at end of file diff --git a/wasm.d b/external/wasmbuild.c similarity index 100% rename from wasm.d rename to external/wasmbuild.c diff --git a/test.sh b/test.sh index 7b6e69f..460b512 100755 --- a/test.sh +++ b/test.sh @@ -2,16 +2,19 @@ name="Test_Runner" -shared_src="package.d platform.d fonts.d aliases.d math.d util.d alloc.d assets.d external.d" +shared_src="dlib/package.d dlib/platform.d dlib/fonts.d dlib/aliases.d dlib/math.d dlib/util.d dlib/alloc.d dlib/assets.d dlib/externdecl.d" if [ "$1" == "wasm" ]; then - flags="-c -mtriple=wasm32-unknown-unknown-wasm --Xcc=-DBUILD_WASM -Iexternal/arsd-webassembly -L--no-entry -L--allow-undefined -i=core -i=std -i=. --of=build/dlibmain.wasm --d-version=inline_concat -verrors=90" + flags="-c -vgc -mtriple=wasm32-unknown-unknown-wasm -fvisibility=public -dllimport=all --unittest --Xcc=-DBUILD_WASM -Iwasm/runtime -L--no-entry -i=core -i=std -i=. -g --real-precision=double --of=build/dlibmain.wasm --d-version=inline_concat -verrors=90" + wasm_src="wasm/runtime/object.d" /bin/bash ./build.sh build wasm - ldc2 $flags $shared_src external/arsd-webassembly/object.d external/arsd-webassembly/core/stdc/string.d + ldc2 $flags $shared_src $wasm_src - wasm-ld "build/dlibmain.wasm build/dlibincludes.wasm -obuild/dlib.wasm" + wasm-ld build/dlibmain.wasm build/dlibincludes.wasm --error-limit=0 --export-memory -obuild/dlib.wasm + + cp build/dlib.wasm wasm/dlib.wasm else flags="-P-I/usr/include/freetype2 -L-lfreetype --main --unittest -g --of=$name" diff --git a/tinyalloc.o b/tinyalloc.o deleted file mode 100644 index 654325c..0000000 Binary files a/tinyalloc.o and /dev/null differ diff --git a/wasm/dlib.wasm b/wasm/dlib.wasm new file mode 100755 index 0000000..d89ea37 Binary files /dev/null and b/wasm/dlib.wasm differ diff --git a/wasm/dlibmain.wasm b/wasm/dlibmain.wasm new file mode 100644 index 0000000..31ee0b7 Binary files /dev/null and b/wasm/dlibmain.wasm differ diff --git a/wasm/index.html b/wasm/index.html new file mode 100644 index 0000000..7977f39 --- /dev/null +++ b/wasm/index.html @@ -0,0 +1,12 @@ + + + + + + Test page + + + + diff --git a/external/arsd-webassembly/core/arsd/aa.d b/wasm/runtime/core/arsd/aa.d similarity index 100% rename from external/arsd-webassembly/core/arsd/aa.d rename to wasm/runtime/core/arsd/aa.d diff --git a/external/arsd-webassembly/core/arsd/memory_allocation.d b/wasm/runtime/core/arsd/memory_allocation.d similarity index 100% rename from external/arsd-webassembly/core/arsd/memory_allocation.d rename to wasm/runtime/core/arsd/memory_allocation.d diff --git a/external/arsd-webassembly/core/arsd/objectutils.d b/wasm/runtime/core/arsd/objectutils.d similarity index 100% rename from external/arsd-webassembly/core/arsd/objectutils.d rename to wasm/runtime/core/arsd/objectutils.d diff --git a/external/arsd-webassembly/core/arsd/utf_decoding.d b/wasm/runtime/core/arsd/utf_decoding.d similarity index 100% rename from external/arsd-webassembly/core/arsd/utf_decoding.d rename to wasm/runtime/core/arsd/utf_decoding.d diff --git a/wasm/runtime/core/checkedint.d b/wasm/runtime/core/checkedint.d new file mode 100644 index 0000000..33a9fa1 --- /dev/null +++ b/wasm/runtime/core/checkedint.d @@ -0,0 +1,83 @@ +module core.checkedint; + +version(LDC) +{ + import ldc.intrinsics; +} + +pragma(inline, true) +uint addu()(uint x, uint y, ref bool overflow) +{ + version (LDC) + { + if (!__ctfe) + { + auto res = llvm_uadd_with_overflow(x, y); + overflow |= res.overflow; + return res.result; + } + } + immutable uint r = x + y; + immutable bool o = r < x; + assert(o == (r < y)); + if (o) + overflow = true; + return r; +} + +uint mulu()(uint x, uint y, ref bool overflow) +{ + version (D_InlineAsm_X86) enum useAsm = true; + else version (D_InlineAsm_X86_64) enum useAsm = true; + else enum useAsm = false; + + version (LDC) + { + if (!__ctfe) + { + auto res = llvm_umul_with_overflow(x, y); + overflow |= res.overflow; + return res.result; + } + } + else static if (useAsm) + { + if (!__ctfe) + { + uint r; + bool o; + asm pure nothrow @nogc @trusted + { + mov EAX, x; + mul y; // EDX:EAX = EAX * y + mov r, EAX; + setc o; + } + overflow |= o; + return r; + } + } + + immutable ulong r = ulong(x) * ulong(y); + if (r >> 32) + overflow = true; + return cast(uint) r; +} + +pragma(inline, true) +int adds()(int x, int y, ref bool overflow) +{ + version (LDC) + { + if (!__ctfe) + { + auto res = llvm_sadd_with_overflow(x, y); + overflow |= res.overflow; + return res.result; + } + } + long r = cast(long)x + cast(long)y; + if (r < int.min || r > int.max) + overflow = true; + return cast(int)r; +} diff --git a/external/arsd-webassembly/core/demangle.d b/wasm/runtime/core/demangle.d similarity index 99% rename from external/arsd-webassembly/core/demangle.d rename to wasm/runtime/core/demangle.d index b6117cb..89a7429 100644 --- a/external/arsd-webassembly/core/demangle.d +++ b/wasm/runtime/core/demangle.d @@ -2326,17 +2326,6 @@ private struct BufSlice this.to = to; } - invariant - { - if (buf is null) - { - assert(from == 0); - assert(to == 0); - } - - assert(from <= to); - } - auto getSlice() inout nothrow scope { return buf[from .. to]; } size_t length() const scope { return to - from; } } diff --git a/external/arsd-webassembly/core/internal/cast_.d b/wasm/runtime/core/internal/cast_.d similarity index 100% rename from external/arsd-webassembly/core/internal/cast_.d rename to wasm/runtime/core/internal/cast_.d diff --git a/wasm/runtime/core/internal/convert.d b/wasm/runtime/core/internal/convert.d new file mode 100644 index 0000000..0dcf0d3 --- /dev/null +++ b/wasm/runtime/core/internal/convert.d @@ -0,0 +1,694 @@ +/** + * Written in the D programming language. + * This module provides functions to converting different values to const(ubyte)[] + * + * Copyright: Copyright Igor Stepanov 2013-2013. + * License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). + * Authors: Igor Stepanov + * Source: $(DRUNTIMESRC core/internal/_convert.d) + */ +module core.internal.convert; + +/+ +A @nogc function can allocate memory during CTFE. ++/ +@nogc nothrow pure @trusted +private ubyte[] ctfe_alloc(size_t n) +{ + if (!__ctfe) + { + assert(0, "CTFE only"); + } + else + { + static ubyte[] alloc(size_t x) nothrow pure + { + if (__ctfe) // Needed to prevent _d_newarray from appearing in compiled prorgam. + return new ubyte[x]; + else + assert(0); + } + return (cast(ubyte[] function(size_t) @nogc nothrow pure) &alloc)(n); + } +} + +@trusted pure nothrow @nogc +const(ubyte)[] toUbyte(T)(const scope ref T val) if (__traits(isFloating, T) && (is(T : real) || is(T : ireal))) +{ + if (__ctfe) + { + static if (floatFormat!T == FloatFormat.Float || floatFormat!T == FloatFormat.Double) + { + static if (is(T : ireal)) // https://issues.dlang.org/show_bug.cgi?id=19932 + const f = val.im; + else + alias f = val; + static if (T.sizeof == uint.sizeof) + uint bits = *cast(const uint*) &f; + else static if (T.sizeof == ulong.sizeof) + ulong bits = *cast(const ulong*) &f; + ubyte[] result = ctfe_alloc(T.sizeof); + version (BigEndian) + { + foreach_reverse (ref b; result) + { + b = cast(ubyte) bits; + bits >>= 8; + } + } + else + { + foreach (ref b; result) + { + b = cast(ubyte) bits; + bits >>= 8; + } + } + return result; + } + else static if (floatFormat!T == FloatFormat.DoubleDouble) + { + // Parse DoubleDoubles as a pair of doubles. + // The layout of the type is: + // + // [1| 11 | 52 ][1| 11 | 52 ] + // [S| Exponent | Fraction (hi) ][S| Exponent | Fraction (low) ] + // + // We can get the least significant bits by subtracting the IEEE + // double precision portion from the real value. + + import core.math : toPrec; + + ubyte[] buff = ctfe_alloc(T.sizeof); + enum msbSize = double.sizeof; + + static if (is(T : ireal)) + double hi = toPrec!double(val.im); + else + double hi = toPrec!double(val); + buff[0 .. msbSize] = toUbyte(hi)[]; + + if (val is cast(T)0.0 || val is cast(T)-0.0 || + val is T.nan || val is -T.nan || + val is T.infinity || val > T.max || + val is -T.infinity || val < -T.max) + { + // Zero, NaN, and Inf are all representable as doubles, so the + // least significant part can be 0.0. + buff[msbSize .. $] = 0; + } + else + { + static if (is(T : ireal)) + double low = toPrec!double(val.im - hi); + else + double low = toPrec!double(val - hi); + buff[msbSize .. $] = toUbyte(low)[]; + } + + // Arrays don't index differently between little and big-endian targets. + return buff; + } + else + { + auto parsed = parse(val); + + ulong mantissa = parsed.mantissa; + uint exp = parsed.exponent; + uint sign = parsed.sign; + + ubyte[] buff = ctfe_alloc(T.sizeof); + size_t off_bytes = 0; + size_t off_bits = 0; + // Quadruples won't fit in one ulong, so check for that. + enum mantissaMax = FloatTraits!T.MANTISSA < ulong.sizeof*8 ? + FloatTraits!T.MANTISSA : ulong.sizeof*8; + + for (; off_bytes < mantissaMax/8; ++off_bytes) + { + buff[off_bytes] = cast(ubyte)mantissa; + mantissa >>= 8; + } + + static if (floatFormat!T == FloatFormat.Quadruple) + { + ulong mantissa2 = parsed.mantissa2; + off_bytes--; // go back one, since mantissa only stored data in 56 + // bits, ie 7 bytes + for (; off_bytes < FloatTraits!T.MANTISSA/8; ++off_bytes) + { + buff[off_bytes] = cast(ubyte)mantissa2; + mantissa2 >>= 8; + } + } + else + { + off_bits = FloatTraits!T.MANTISSA%8; + buff[off_bytes] = cast(ubyte)mantissa; + } + + for (size_t i=0; i>= 8; + buff[off_bytes] |= (cur_exp << off_bits); + ++off_bytes; + buff[off_bytes] |= cur_exp >> 8 - off_bits; + } + + + exp <<= 8 - FloatTraits!T.EXPONENT%8 - 1; + buff[off_bytes] |= exp; + sign <<= 7; + buff[off_bytes] |= sign; + + version (BigEndian) + { + for (size_t left = 0, right = buff.length - 1; left < right; left++, right--) + { + const swap = buff[left]; + buff[left] = buff[right]; + buff[right] = swap; + } + } + return buff; + } + } + else + { + return (cast(const(ubyte)*)&val)[0 .. T.sizeof]; + } +} + +@safe pure nothrow @nogc +private Float parse(bool is_denormalized = false, T:ireal)(T x) +{ + return parse(x.im); +} + +@safe pure nothrow @nogc +private Float parse(bool is_denormalized = false, T:real)(T x_) if (floatFormat!T != FloatFormat.Real80) +{ + import core.internal.traits : Unqual; + Unqual!T x = x_; + static assert(floatFormat!T != FloatFormat.DoubleDouble, + "doubledouble float format not supported in CTFE"); + if (x is cast(T)0.0) return FloatTraits!T.ZERO; + if (x is cast(T)-0.0) return FloatTraits!T.NZERO; + if (x is T.nan) return FloatTraits!T.NAN; + if (x is -T.nan) return FloatTraits!T.NNAN; + if (x is T.infinity || x > T.max) return FloatTraits!T.INF; + if (x is -T.infinity || x < -T.max) return FloatTraits!T.NINF; + + uint sign = x < 0; + x = sign ? -x : x; + int e = binLog2(x); + real x2 = x; + uint exp = cast(uint)(e + (2^^(FloatTraits!T.EXPONENT-1) - 1)); + + if (!exp) + { + if (is_denormalized) + return Float(0, 0, sign); + else + return denormalizedMantissa(x, sign); + } + + x2 /= binPow2(e); + + static if (!is_denormalized) + x2 -= 1.0; + + static if (floatFormat!T == FloatFormat.Quadruple) + { + // Store the 112-bit mantissa in two ulongs, specifically the lower 56 + // bits of each, with the most significant bits in mantissa2. There's + // an edge case exposed by the labeled test below, where only a subnormal + // with the highest bit set being the 57th bit will "overflow" to the + // 57th bit in mantissa2 with the following logic, but that special case + // is handled by an additional check in denormalizedMantissa for + // Quadruples below. + + x2 *= 2UL<<(FloatTraits!T.MANTISSA - (ulong.sizeof - 1)*8 - 1); + ulong mant2 = cast(ulong) x2; + x2 -= mant2; + + x2 *= 2UL<<((ulong.sizeof - 1)*8 - 1); + ulong mant = cast(ulong) x2; + return Float(mant, exp, sign, mant2); + } + else + { + x2 *= 2UL<<(FloatTraits!T.MANTISSA); + ulong mant = shiftrRound(cast(ulong)x2); + return Float(mant, exp, sign); + } +} + +@safe pure nothrow @nogc +private Float parse(bool _ = false, T:real)(T x_) if (floatFormat!T == FloatFormat.Real80) +{ + import core.internal.traits : Unqual; + Unqual!T x = x_; + //HACK @@@3632@@@ + + if (x == 0.0L) + { + real y = 1.0L/x; + if (y == real.infinity) // -0.0 + return FloatTraits!T.ZERO; + else + return FloatTraits!T.NZERO; //0.0 + } + + if (x != x) //HACK: should be if (x is real.nan) and if (x is -real.nan) + { + auto y = cast(double)x; + if (y is double.nan) + return FloatTraits!T.NAN; + else + return FloatTraits!T.NNAN; + } + + if (x == real.infinity) return FloatTraits!T.INF; + if (x == -real.infinity) return FloatTraits!T.NINF; + + enum EXPONENT_MED = (2^^(FloatTraits!T.EXPONENT-1) - 1); + uint sign = x < 0; + x = sign ? -x : x; + + int e = binLog2(x); + uint exp = cast(uint)(e + EXPONENT_MED); + if (!exp) + { + return denormalizedMantissa(x, sign); + } + int pow = (FloatTraits!T.MANTISSA-1-e); + x *= binPow2((pow / EXPONENT_MED)*EXPONENT_MED); //To avoid overflow in 2.0L ^^ pow + x *= binPow2(pow % EXPONENT_MED); + ulong mant = cast(ulong)x; + return Float(mant, exp, sign); +} + +private struct Float +{ + ulong mantissa; + uint exponent; + uint sign; + ulong mantissa2; +} + +private template FloatTraits(T) if (floatFormat!T == FloatFormat.Float) +{ + enum DATASIZE = 4; + enum EXPONENT = 8; + enum MANTISSA = 23; + enum ZERO = Float(0, 0, 0); + enum NZERO = Float(0, 0, 1); + enum NAN = Float(0x400000UL, 0xff, 0); + enum NNAN = Float(0x400000UL, 0xff, 1); + enum INF = Float(0, 255, 0); + enum NINF = Float(0, 255, 1); +} + +private template FloatTraits(T) if (floatFormat!T == FloatFormat.Double) +{ + enum DATASIZE = 8; + enum EXPONENT = 11; + enum MANTISSA = 52; + enum ZERO = Float(0, 0, 0); + enum NZERO = Float(0, 0, 1); + enum NAN = Float(0x8000000000000UL, 0x7ff, 0); + enum NNAN = Float(0x8000000000000UL, 0x7ff, 1); + enum INF = Float(0, 0x7ff, 0); + enum NINF = Float(0, 0x7ff, 1); +} + +private template FloatTraits(T) if (floatFormat!T == FloatFormat.Real80) +{ + enum DATASIZE = 10; + enum EXPONENT = 15; + enum MANTISSA = 64; + enum ZERO = Float(0, 0, 0); + enum NZERO = Float(0, 0, 1); + enum NAN = Float(0xC000000000000000UL, 0x7fff, 0); + enum NNAN = Float(0xC000000000000000UL, 0x7fff, 1); + enum INF = Float(0x8000000000000000UL, 0x7fff, 0); + enum NINF = Float(0x8000000000000000UL, 0x7fff, 1); +} + +private template FloatTraits(T) if (floatFormat!T == FloatFormat.DoubleDouble) //Unsupported in CTFE +{ + enum DATASIZE = 16; + enum EXPONENT = 11; + enum MANTISSA = 106; + enum ZERO = Float(0, 0, 0); + enum NZERO = Float(0, 0, 1); + enum NAN = Float(0x8000000000000UL, 0x7ff, 0); + enum NNAN = Float(0x8000000000000UL, 0x7ff, 1); + enum INF = Float(0, 0x7ff, 0); + enum NINF = Float(0, 0x7ff, 1); +} + +private template FloatTraits(T) if (floatFormat!T == FloatFormat.Quadruple) +{ + enum DATASIZE = 16; + enum EXPONENT = 15; + enum MANTISSA = 112; + enum ZERO = Float(0, 0, 0); + enum NZERO = Float(0, 0, 1); + enum NAN = Float(0, 0x7fff, 0, 0x80000000000000UL); + enum NNAN = Float(0, 0x7fff, 1, 0x80000000000000UL); + enum INF = Float(0, 0x7fff, 0); + enum NINF = Float(0, 0x7fff, 1); +} + + +@safe pure nothrow @nogc +private real binPow2(int pow) +{ + static real binPosPow2(int pow) @safe pure nothrow @nogc + { + assert(pow > 0); + + if (pow == 1) return 2.0L; + + int subpow = pow/2; + real p = binPosPow2(subpow); + real ret = p*p; + + if (pow%2) + { + ret *= 2.0L; + } + + return ret; + } + + if (!pow) return 1.0L; + if (pow > 0) return binPosPow2(pow); + return 1.0L/binPosPow2(-pow); +} + + +//Need in CTFE, because CTFE float and double expressions computed more precisely that run-time expressions. +@safe pure nothrow @nogc +private ulong shiftrRound(ulong x) +{ + return (x >> 1) + (x & 1); +} + +@safe pure nothrow @nogc +private uint binLog2(T)(const T x) +{ + assert(x > 0); + int max = 2 ^^ (FloatTraits!T.EXPONENT-1)-1; + int min = -max+1; + int med = (min + max) / 2; + + if (x < T.min_normal) return -max; + + while ((max - min) > 1) + { + if (binPow2(med) > x) + { + max = med; + } + else + { + min = med; + } + med = (min + max) / 2; + } + + if (x < binPow2(max)) + return min; + return max; +} + +@safe pure nothrow @nogc +private Float denormalizedMantissa(T)(T x, uint sign) if (floatFormat!T == FloatFormat.Real80) +{ + x *= 2.0L^^FloatTraits!T.MANTISSA; + auto fl = parse(x); + uint pow = FloatTraits!T.MANTISSA - fl.exponent + 1; + return Float(fl.mantissa >> pow, 0, sign); +} + +@safe pure nothrow @nogc +private Float denormalizedMantissa(T)(T x, uint sign) + if (floatFormat!T == FloatFormat.Float || floatFormat!T == FloatFormat.Double) +{ + x *= 2.0L^^FloatTraits!T.MANTISSA; + auto fl = parse!true(x); + ulong mant = fl.mantissa >> (FloatTraits!T.MANTISSA - fl.exponent); + return Float(shiftrRound(mant), 0, sign); +} + +@safe pure nothrow @nogc +private Float denormalizedMantissa(T)(T x, uint sign) if (floatFormat!T == FloatFormat.Quadruple) +{ + x *= 2.0L^^FloatTraits!T.MANTISSA; + auto fl = parse!true(x); + uint offset = FloatTraits!T.MANTISSA - fl.exponent + 1; + enum mantissaSize = (ulong.sizeof - 1) * 8; + + if (offset < mantissaSize) + { // Create a new mantissa ulong with the trailing mantissa2 bits that + // need to be shifted into mantissa, by shifting the needed bits left, + // zeroing out the first byte, and then ORing it with mantissa shifted + // right by offset. + + ulong shiftedMantissa = ((fl.mantissa2 << (mantissaSize - offset)) & + 0x00FFFFFFFFFFFFFFUL) | fl.mantissa >> offset; + return Float(shiftedMantissa, 0, sign, fl.mantissa2 >> offset); + } + else if (offset > mantissaSize) + return Float(fl.mantissa2 >> offset - mantissaSize , 0, sign, 0); + else + // Handle special case mentioned in parse() above by zeroing out the + // 57'th bit of mantissa2, "shifting" it into mantissa, and setting the + // first bit of mantissa2. + return Float(fl.mantissa2 & 0x00FFFFFFFFFFFFFFUL , 0, sign, 1); +} + +private enum FloatFormat +{ + Float, + Double, + Real80, + DoubleDouble, + Quadruple +} + +template floatFormat(T) if (is(T:real) || is(T:ireal)) +{ + static if (T.mant_dig == 24) + enum floatFormat = FloatFormat.Float; + else static if (T.mant_dig == 53) + { + // Double precision, or real == double + static if (T.sizeof == double.sizeof) + enum floatFormat = FloatFormat.Double; + // 80-bit real with rounding precision set to 53 bits. + else static if (T.sizeof == real.sizeof) + enum floatFormat = FloatFormat.Real80; + } + else static if (T.mant_dig == 64) + enum floatFormat = FloatFormat.Real80; + else static if (T.mant_dig == 106) + enum floatFormat = FloatFormat.DoubleDouble; + else static if (T.mant_dig == 113) + enum floatFormat = FloatFormat.Quadruple; + else + static assert(0); + +} + +package template floatSize(T) if (is(T:real) || is(T:ireal)) +{ + enum floatSize = FloatTraits!(T).DATASIZE; +} + +// all toUbyte functions must be evaluable at compile time +@trusted pure nothrow @nogc +const(ubyte)[] toUbyte(T)(return scope const T[] arr) if (T.sizeof == 1) +{ + pragma(inline, true); + return cast(const(ubyte)[])arr; +} + +private const(ubyte)[] toUbyte_array_ctfe(T)(return scope const T[] arr) +{ + pragma(inline, false); + ubyte[] ret = ctfe_alloc(T.sizeof * arr.length); + static if (is(T EType == enum)) // Odd style is to avoid template instantiation in most cases. + alias E = OriginalType!EType; + else + alias E = T; + static if (is(E == struct) || is(E == union) || __traits(isStaticArray, E) || !is(typeof(arr[0] is null))) + { + size_t offset = 0; + foreach (ref cur; arr) + { + ret[offset .. offset + T.sizeof] = toUbyte(cur)[0 .. T.sizeof]; + offset += T.sizeof; + } + } + else + { + foreach (cur; arr) + assert(cur is null, "Unable to compute byte representation of non-null pointer at compile time"); + } + return ret; +} + +@trusted pure nothrow @nogc +const(ubyte)[] toUbyte(T)(return scope const T[] arr) if (T.sizeof > 1) +{ + pragma(inline, true); + return __ctfe ? toUbyte_array_ctfe(arr) + : (cast(const(ubyte)*)(arr.ptr))[0 .. T.sizeof*arr.length]; +} + +private const(ubyte)[] toUbyte_integral_ctfe(T)(const return ref scope T val) +{ + pragma(inline, false); + static if (T.sizeof == 1) + { + ubyte[] result = ctfe_alloc(1); + result[0] = cast(ubyte) val; + return result; + } + else + { + import core.internal.traits : Unqual; + ubyte[] tmp = ctfe_alloc(T.sizeof); + Unqual!T val_ = val; + for (size_t i = 0; i < T.sizeof; ++i) + { + size_t idx; + version (LittleEndian) idx = i; + else idx = T.sizeof-i-1; + tmp[idx] = cast(ubyte)(val_&0xff); + val_ >>= 8; + } + return tmp; + } +} + +@trusted pure nothrow @nogc +const(ubyte)[] toUbyte(T)(const ref scope T val) if (__traits(isIntegral, T) && !is(T == enum) && !is(T == __vector)) +{ + pragma(inline, true); + return __ctfe ? toUbyte_integral_ctfe(val) + : (cast(const ubyte*) &val)[0 .. T.sizeof]; +} + +private const(ubyte)[] toUbyte_vector_ctfe(T)(const return ref scope T val) +{ + pragma(inline, false); + static if (is(typeof(val[0]) : void)) + assert(0, "Unable to compute byte representation of " ~ T.stringof ~ " at compile time."); + else + { + // This code looks like it should work in CTFE but it segfaults: + // auto a = val.array; + // return toUbyte(a); + alias E = typeof(val[0]); + ubyte[] result = ctfe_alloc(T.sizeof); + for (size_t i = 0, j = 0; i < T.sizeof; i += E.sizeof, ++j) + { + result[i .. i + E.sizeof] = toUbyte(val[j]); + } + return result; + } +} + +@trusted pure nothrow @nogc +const(ubyte)[] toUbyte(T)(const ref scope T val) if (is(T == __vector)) +{ + pragma(inline, true); + return __ctfe ? toUbyte_vector_ctfe(val) + : (cast(const ubyte*) &val)[0 .. T.sizeof]; +} + +private const(ubyte)[] toUbyte_enum_ctfe(T)(const return ref scope T val) +{ + pragma(inline, false); + static if (is(T V == enum)){} + return toUbyte(*cast(const V*) &val); +} + +@trusted pure nothrow @nogc +const(ubyte)[] toUbyte(T)(const ref return scope T val) if (is(T == enum)) +{ + pragma(inline, true); + return __ctfe ? toUbyte_enum_ctfe(val) + : (cast(const(ubyte)*)&val)[0 .. T.sizeof]; +} + +nothrow pure @safe unittest +{ + // Issue 19008 - check toUbyte works on enums. + enum Month : uint { jan = 1} + Month m = Month.jan; + const bytes = toUbyte(m); + enum ctfe_works = (() { Month x = Month.jan; return toUbyte(x).length > 0; })(); +} + +private const(ubyte)[] toUbyte_delegate_ctfe(T)(const return ref scope T val) +{ + pragma(inline, false); + if (val !is null) assert(0, "Unable to compute byte representation of non-null pointer at compile time"); + return ctfe_alloc(T.sizeof); +} + +@trusted pure nothrow @nogc +const(ubyte)[] toUbyte(T)(const ref T val) if (is(T == delegate) || is(T : V*, V) && __traits(getAliasThis, T).length == 0) +{ + pragma(inline, true); + return __ctfe ? toUbyte_delegate_ctfe(val) + : (cast(const(ubyte)*)&val)[0 .. T.sizeof]; +} + +private const(ubyte)[] toUbyte_aggregate_ctfe(T)(const return ref scope T val) +{ + pragma(inline, false); + ubyte[] bytes = ctfe_alloc(T.sizeof); + foreach (key, ref cur; val.tupleof) + { + static if (is(typeof(cur) EType == enum)) // Odd style is to avoid template instantiation in most cases. + alias CurType = OriginalType!EType; + else + alias CurType = typeof(cur); + static if (is(CurType == struct) || is(CurType == union) || __traits(isStaticArray, CurType) || !is(typeof(cur is null))) + { + bytes[val.tupleof[key].offsetof .. val.tupleof[key].offsetof + CurType.sizeof] = toUbyte(cur)[]; + } + else + { + assert(cur is null, "Unable to compute byte representation of non-null reference field at compile time"); + //skip, because val bytes are zeros + } + } + return bytes; +} + +@trusted pure nothrow @nogc +const(ubyte)[] toUbyte(T)(const return ref scope T val) if (is(T == struct) || is(T == union)) +{ + pragma(inline, true); + return __ctfe ? toUbyte_aggregate_ctfe(val) + : (cast(const(ubyte)*)&val)[0 .. T.sizeof]; +} + +// Strips off all `enum`s from type `T`. +// Perhaps move to core.internal.types. +private template OriginalType(T) +{ + static if (is(T EType == enum)) + alias OriginalType = .OriginalType!EType; + else + alias OriginalType = T; +} diff --git a/external/arsd-webassembly/core/internal/lifetime.d b/wasm/runtime/core/internal/lifetime.d similarity index 100% rename from external/arsd-webassembly/core/internal/lifetime.d rename to wasm/runtime/core/internal/lifetime.d diff --git a/external/arsd-webassembly/core/internal/string.d b/wasm/runtime/core/internal/string.d similarity index 100% rename from external/arsd-webassembly/core/internal/string.d rename to wasm/runtime/core/internal/string.d diff --git a/wasm/runtime/core/internal/traits.d b/wasm/runtime/core/internal/traits.d new file mode 100644 index 0000000..52b3a96 --- /dev/null +++ b/wasm/runtime/core/internal/traits.d @@ -0,0 +1,123 @@ +module core.internal.traits; + +alias AliasSeq(T...) = T; + +template allSatisfy(alias F, T...) +{ + static foreach (Ti; T) + { + static if (!is(typeof(allSatisfy) == bool) && // not yet defined + !F!(Ti)) + { + enum allSatisfy = false; + } + } + static if (!is(typeof(allSatisfy) == bool)) // if not yet defined + { + enum allSatisfy = true; + } +} + +template Unqual(T : const U, U) +{ + static if (is(U == shared V, V)) + alias Unqual = V; + else + alias Unqual = U; +} + +alias Unconst(T : const U, U) = U; + +template hasElaborateMove(S) +{ + static if (__traits(isStaticArray, S)) + { + enum bool hasElaborateMove = S.sizeof && hasElaborateMove!(BaseElemOf!S); + } + else static if (is(S == struct)) + { + enum hasElaborateMove = (is(typeof(S.init.opPostMove(lvalueOf!S))) && + !is(typeof(S.init.opPostMove(rvalueOf!S)))) || + anySatisfy!(.hasElaborateMove, Fields!S); + } + else + { + enum bool hasElaborateMove = false; + } +} + +template substInout(T) +{ + static if (is(T == immutable)) + { + alias substInout = T; + } + else static if (is(T : shared const U, U) || is(T : const U, U)) + { + // U is top-unqualified + mixin("alias substInout = " + ~ (is(T == shared) ? "shared " : "") + ~ (is(T == const) || is(T == inout) ? "const " : "") // substitute inout to const + ~ "substInoutForm!U;"); + } + else + static assert(0); +} + +template anySatisfy(alias F, Ts...) +{ + static foreach (T; Ts) + { + static if (!is(typeof(anySatisfy) == bool) && // not yet defined + F!T) + { + enum anySatisfy = true; + } + } + static if (!is(typeof(anySatisfy) == bool)) // if not yet defined + { + enum anySatisfy = false; + } +} + +template hasElaborateCopyConstructor(S) +{ + static if (__traits(isStaticArray, S)) + { + enum bool hasElaborateCopyConstructor = S.sizeof && hasElaborateCopyConstructor!(BaseElemOf!S); + } + else static if (is(S == struct)) + { + enum hasElaborateCopyConstructor = __traits(hasCopyConstructor, S) || __traits(hasPostblit, S); + } + else + { + enum bool hasElaborateCopyConstructor = false; + } +} + +template hasElaborateDestructor(S) +{ + static if (__traits(isStaticArray, S)) + { + enum bool hasElaborateDestructor = S.sizeof && hasElaborateDestructor!(BaseElemOf!S); + } + else static if (is(S == struct)) + { + // Once https://issues.dlang.org/show_bug.cgi?id=24865 is fixed, then + // this should be the implementation, but until that's fixed, we need the + // uncommented code. + // enum hasElaborateDestructor = __traits(hasMember, S, "__xdtor"); + + enum hasElaborateDestructor = hasDtor([__traits(allMembers, S)]); + } + else + { + enum bool hasElaborateDestructor = false; + } +} + +enum bool isAggregateType(T) = is(T == struct) || is(T == union) || + is(T == class) || is(T == interface); + +enum bool isPointer(T) = is(T == U*, U) && !isAggregateType!T; diff --git a/external/arsd-webassembly/core/internal/utf.d b/wasm/runtime/core/internal/utf.d similarity index 99% rename from external/arsd-webassembly/core/internal/utf.d rename to wasm/runtime/core/internal/utf.d index 43aab77..6639754 100644 --- a/external/arsd-webassembly/core/internal/utf.d +++ b/wasm/runtime/core/internal/utf.d @@ -436,7 +436,7 @@ unittest { debug(utf) printf("utf.encode.unittest\n"); - char[] s = "abcd".dup; + char[] s = r"abcd"; encode(s, cast(dchar)'a'); assert(s.length == 5); assert(s == "abcda"); diff --git a/external/arsd-webassembly/core/lifetime.d b/wasm/runtime/core/lifetime.d similarity index 70% rename from external/arsd-webassembly/core/lifetime.d rename to wasm/runtime/core/lifetime.d index 41aaafa..2d45218 100644 --- a/external/arsd-webassembly/core/lifetime.d +++ b/wasm/runtime/core/lifetime.d @@ -71,3 +71,41 @@ void copyEmplace(S, T)(ref S source, ref T target) @system *cast(Unconst!(T)*) &target = *cast(Unconst!(T)*) &source; } } + +T* emplace(T)(T* chunk) @safe pure nothrow +{ + import core.internal.lifetime : emplaceRef; + + emplaceRef!T(*chunk); + return chunk; +} + +template forward(args...) +{ + import core.internal.traits : AliasSeq; + + template fwd(alias arg) + { + // by ref || lazy || const/immutable + static if (__traits(isRef, arg) || + __traits(isOut, arg) || + __traits(isLazy, arg) || + !is(typeof(move(arg)))) + alias fwd = arg; + // (r)value + else + @property auto fwd() + { + version (DigitalMars) { /* @@BUG 23890@@ */ } else pragma(inline, true); + return move(arg); + } + } + + alias Result = AliasSeq!(); + static foreach (arg; args) + Result = AliasSeq!(Result, fwd!arg); + static if (Result.length == 1) + alias forward = Result[0]; + else + alias forward = Result; +} diff --git a/external/arsd-webassembly/core/stdc/string.d b/wasm/runtime/core/stdc/string.d similarity index 100% rename from external/arsd-webassembly/core/stdc/string.d rename to wasm/runtime/core/stdc/string.d diff --git a/external/arsd-webassembly/math/package.d b/wasm/runtime/math/package.d similarity index 61% rename from external/arsd-webassembly/math/package.d rename to wasm/runtime/math/package.d index 76f5581..58431c4 100644 --- a/external/arsd-webassembly/math/package.d +++ b/wasm/runtime/math/package.d @@ -6,5 +6,5 @@ float llvm_sqrt(float x) pure nothrow @nogc @safe; pragma(LDC_intrinsic, "llvm.sqrt.f64") double llvm_sqrt(double x) pure nothrow @nogc @safe; -float sqrt(float x ) => x < 0 ? float.nan : llvm_sqrt(x); -double sqrt(double x) => x < 0 ? double.nan : llvm_sqrt(x); +float sqrt(float x ) => x < 0.0f ? float.nan : llvm_sqrt(x); +double sqrt(double x) => x < 0.0f ? double.nan : llvm_sqrt(x); diff --git a/external/arsd-webassembly/object.d b/wasm/runtime/object.d similarity index 93% rename from external/arsd-webassembly/object.d rename to wasm/runtime/object.d index 64c952a..60a456e 100644 --- a/external/arsd-webassembly/object.d +++ b/wasm/runtime/object.d @@ -1,17 +1,21 @@ -// Minimal druntime for webassembly. Assumes your program has a main function. +// ************************************************** +// ** VALID FOR 1.42.0 (DMD v2.112.1, LLVM 21.1.8) ** +// ************************************************** + + module object; public import core.internal.cast_ : _d_cast; -static import arsd.webassembly; - -import std.meta; +alias AliasSeq(TList...) = TList; // import core.arsd.memory_allocation; import core.stdc.string; import dlib.externdecl; +import wasm; + version(CarelessAlocation) { version = inline_concat; @@ -34,6 +38,102 @@ alias sizediff_t = ptrdiff_t; enum immutable(void)* rtinfoNoPointers = null; enum immutable(void)* rtinfoHasPointers = cast(void*)1; +int __cmp(C1, C2)(C1 lhs, C2 rhs) +if ((is(C1 : const(Object)) || (is(C1 == interface) && (__traits(getLinkage, C1) == "D"))) && + (is(C2 : const(Object)) || (is(C2 == interface) && (__traits(getLinkage, C2) == "D")))) +{ + static if (is(C1 == typeof(null)) && is(C2 == typeof(null))) + { + return 0; + } + else static if (is(C1 == typeof(null))) + { + // Regard null references as always being "less than" + return -1; + } + else static if (is(C2 == typeof(null))) + { + return 1; + } + else + { + if (lhs is rhs) + return 0; + if (lhs is null) + return -1; + if (rhs is null) + return 1; + return lhs.opCmp(rhs); + } +} + +class TypeInfo_AssociativeArray : TypeInfo +{ + override string toString() const + { + return MakeString(value.toString(), "[", key.toString(), "]"); + } + + override bool opEquals(Object o) + { + if (this is o) + return true; + auto c = cast(const TypeInfo_AssociativeArray)o; + return c && this.key == c.key && + this.value == c.value; + } + + override bool equals(in void* p1, in void* p2) @trusted const + { + return xopEquals(p1, p2); + } + + override hash_t getHash(scope const void* p) nothrow @trusted const + { + return xtoHash(p); + } + + // BUG: need to add the rest of the functions + + override @property size_t tsize() nothrow pure const + { + return (char[int]).sizeof; + } + + override const(void)[] initializer() const @trusted + { + return (cast(void *)null)[0 .. (char[int]).sizeof]; + } + + override @property inout(TypeInfo) next() nothrow pure inout { return value; } + override @property uint flags() nothrow pure const { return 1; } + + // TypeInfo entry is generated from the type of this template to help rt/aaA.d + // private static import core.internal.newaa; + // alias Entry(K, V) = core.internal.newaa.Entry!(K, V); + + TypeInfo value; + TypeInfo key; + TypeInfo entry; + + bool function(scope const void* p1, scope const void* p2) nothrow @safe xopEquals; + hash_t function(scope const void*) nothrow @safe xtoHash; + + alias aaOpEqual(K, V) = core.internal.newaa._aaOpEqual!(K, V); + alias aaGetHash(K, V) = core.internal.newaa._aaGetHash!(K, V); + + override @property size_t talign() nothrow pure const + { + return (char[int]).alignof; + } + + version (WithArgTypes) override int argTypes(out TypeInfo arg1, out TypeInfo arg2) + { + arg1 = typeid(void*); + return 0; + } +} + template RTInfoImpl(size_t[] pointerBitmap) { immutable size_t[pointerBitmap.length] RTInfoImpl = pointerBitmap[]; @@ -90,70 +190,62 @@ void reserve(T)(ref T[] arr, size_t length) @trusted { arr = (cast(T*) (malloc(length * T.sizeof)))[0 .. 0]; } +// TODO: fix these to use sprintf -extern(C) void _d_arraybounds(string file, size_t line) { - arsd.webassembly.eval( - q{ console.error("Range error: " + $0 + ":" + $1 )}, - file, line); - arsd.webassembly.abort(); +extern(C) void _d_arraybounds(string file, size_t line) +{ + Abort("Range Error"); } /// Called when an out of range slice of an array is created extern(C) void _d_arraybounds_slice(string file, uint line, size_t lwr, size_t upr, size_t length) { - arsd.webassembly.eval( - q{ console.error("Range error: " + $0 + ":" + $1 + " [" + $2 + ".." + $3 + "] <> " + $4)}, - file, line, lwr, upr, length); - arsd.webassembly.abort(); + Abort("Range error on slice creation"); } /// Called when an out of range array index is accessed extern(C) void _d_arraybounds_index(string file, uint line, size_t index, size_t length) { - arsd.webassembly.eval( - q{ console.error("Array index " + $0 + " out of bounds '[0.."+$1+"]' " + $2 + ":" + $3)}, - index, length, file, line); - arsd.webassembly.abort(); + Abort("Range error on indexing array"); } // } extern(C) void _d_assert(string file, uint line) @trusted @nogc pure { - arsd.webassembly.eval(q{ console.error("Assert failure: " + $0 + ":" + $1); /*, "[" + $2 + ".." + $3 + "] <> " + $4);*/ }, file, line);//, lwr, upr, length); - arsd.webassembly.abort(); -} -void _d_assertp(immutable(char)* file, uint line) -{ - // import core.stdc.string : strlen; - size_t sz = 0; - while(file[sz] != '\0') sz++; - arsd.webassembly.eval(q{ console.error("Assert failure: " + $0 + ":" + $1 + "(" + $2 + ")"); /*, "[" + $2 + ".." + $3 + "] <> " + $4);*/ }, file[0 .. sz], line);//, lwr, upr, length); - arsd.webassembly.abort(); + Abort("Assertion failure"); } +void _d_assertp(immutable(char)* file, uint line) +{ + Abort("Assertion failure"); +} extern(C) void _d_assert_msg(string msg, string file, uint line) @trusted @nogc pure { - arsd.webassembly.eval(q{ console.error("Assert failure: " + $0 + ":" + $1 + "(" + $2 + ")"); /*, "[" + $2 + ".." + $3 + "] <> " + $4);*/ }, file, line, msg);//, lwr, upr, length); - arsd.webassembly.abort(); + Abort("Assertion failure with msg"); } void __switch_error(string file, size_t line) @trusted @nogc pure { - _d_assert_msg("final switch error",file, line); + _d_assert_msg("final switch error", file, line); } bool __equals(T1, T2)(scope const T1[] lhs, scope const T2[] rhs) { - if (lhs.length != rhs.length) { + if (lhs.length != rhs.length) + { return false; } - foreach(i; 0..lhs.length) { - if (lhs[i] != rhs[i]) { + + foreach(i; 0..lhs.length) + { + if (lhs[i] != rhs[i]) + { return false; } } + return true; } @@ -581,13 +673,6 @@ class TypeInfo return __cmp(this.toString(), ti.toString()); } - @system unittest - { - assert(typeid(void) <= typeid(void)); - assert(typeid(void).opCmp(null)); - assert(!typeid(void).opCmp(typeid(void))); - } - override bool opEquals(Object o) { return opEquals(cast(TypeInfo) o); @@ -604,14 +689,6 @@ class TypeInfo return ti && this.toString() == ti.toString(); } - @system unittest - { - auto anotherObj = new Object(); - - assert(typeid(void).opEquals(typeid(void))); - assert(typeid(void) != anotherObj); // calling .opEquals here directly is a type mismatch - } - /** * Computes a hash of the instance of a type. * Params: @@ -662,36 +739,6 @@ class TypeInfo } } - @system unittest - { - class _TypeInfo_Dummy : TypeInfo - { - override const(void)[] initializer() const { return []; } - @property override size_t tsize() nothrow pure const @safe @nogc { return tsize_val; } - - size_t tsize_val; - } - auto dummy = new _TypeInfo_Dummy(); - cast(void)dummy.initializer(); // For coverage completeness - - int a = 2, b = -2; - dummy.swap(&a, &b); - // does nothing because tsize is 0 - assert(a == 2); - assert(b == -2); - - dummy.tsize_val = int.sizeof; - dummy.swap(&a, &b); - assert(a == -2); - assert(b == 2); - - void* ptr_a = null, ptr_b = cast(void*)1; - dummy.tsize_val = (void*).sizeof; - dummy.swap(&ptr_a, &ptr_b); - assert(ptr_a is cast(void*)1); - assert(ptr_b is null); - } - /** Get TypeInfo for 'next' type, as defined by what kind of type this is, null if none. */ @property inout(TypeInfo) next() nothrow pure inout @nogc { return null; } @@ -746,6 +793,7 @@ else */ @property immutable(void)* rtInfo() nothrow pure const @trusted @nogc { return rtinfoHasPointers; } // better safe than sorry } + class TypeInfo_Class : TypeInfo { override string toString() const pure { return name; } @@ -1610,114 +1658,22 @@ class TypeInfo_Enum : TypeInfo this.base == c.base; } - @system unittest - { - enum E { A, B, C } - enum EE { A, B, C } - - assert(typeid(E).opEquals(typeid(E))); - assert(!typeid(E).opEquals(typeid(EE))); - } - override size_t getHash(scope const void* p) const { return base.getHash(p); } - @system unittest - { - enum E { A, B, C } - E e1 = E.A; - E e2 = E.B; - - assert(typeid(E).getHash(&e1) == hashOf(E.A)); - assert(typeid(E).getHash(&e2) == hashOf(E.B)); - - enum ES : string { A = "foo", B = "bar" } - ES es1 = ES.A; - ES es2 = ES.B; - - assert(typeid(ES).getHash(&es1) == hashOf("foo")); - assert(typeid(ES).getHash(&es2) == hashOf("bar")); - } - override bool equals(in void* p1, in void* p2) const { return base.equals(p1, p2); } - @system unittest - { - enum E { A, B, C } - - E e1 = E.A; - E e2 = E.B; - - assert(typeid(E).equals(&e1, &e1)); - assert(!typeid(E).equals(&e1, &e2)); - } - override int compare(in void* p1, in void* p2) const { return base.compare(p1, p2); } - @system unittest - { - enum E { A, B, C } - - E e1 = E.A; - E e2 = E.B; - - assert(typeid(E).compare(&e1, &e1) == 0); - assert(typeid(E).compare(&e1, &e2) < 0); - assert(typeid(E).compare(&e2, &e1) > 0); - } - override @property size_t tsize() nothrow pure const { return base.tsize; } - @safe unittest - { - enum E { A, B, C } - enum ES : string { A = "a", B = "b", C = "c"} - - assert(typeid(E).tsize == E.sizeof); - assert(typeid(ES).tsize == ES.sizeof); - assert(typeid(E).tsize != ES.sizeof); - } - override void swap(void* p1, void* p2) const { return base.swap(p1, p2); } - @system unittest - { - enum E { A, B, C } - - E e1 = E.A; - E e2 = E.B; - - typeid(E).swap(&e1, &e2); - assert(e1 == E.B); - assert(e2 == E.A); - } - override @property inout(TypeInfo) next() nothrow pure inout { return base.next; } - @system unittest - { - enum E { A, B, C } - - assert(typeid(E).next is null); - } - override @property uint flags() nothrow pure const { return base.flags; } - @safe unittest - { - enum E { A, B, C } - - assert(typeid(E).flags == 0); - } - override const(OffsetTypeInfo)[] offTi() const { return base.offTi; } - @system unittest - { - enum E { A, B, C } - - assert(typeid(E).offTi is null); - } - override void destroy(void* p) const { return base.destroy(p); } override void postblit(void* p) const { return base.postblit(p); } @@ -2222,7 +2178,6 @@ extern (C) void[] _d_arrayappendcd(ref byte[] x, dchar c) /* TODO: see if needed -alias AliasSeq(T...) = T; static foreach(type; AliasSeq!(byte, char, dchar, double, float, int, long, short, ubyte, uint, ulong, ushort, void, wchar)) { mixin(q{ class TypeInfo_}~type.mangleof~q{ : TypeInfo { @@ -2363,27 +2318,6 @@ class TypeInfo_Delegate : TypeInfo return null; } - @safe unittest - { - double sqr(double x) { return x * x; } - sqr(double.init); // for coverage completeness - - auto delegate_str = "double delegate(double) pure nothrow @nogc @safe"; - - assert(typeid(typeof(&sqr)).toString() == delegate_str); - assert(delegate_str.hashOf() == typeid(typeof(&sqr)).hashOf()); - assert(typeid(typeof(&sqr)).toHash() == typeid(typeof(&sqr)).hashOf()); - - int g; - - alias delegate_type = typeof((int a, int b) => a + b + g); - delegate_str = "int delegate(int, int) pure nothrow @nogc @safe"; - - assert(typeid(delegate_type).toString() == delegate_str); - assert(delegate_str.hashOf() == typeid(delegate_type).hashOf()); - assert(typeid(delegate_type).toHash() == typeid(delegate_type).hashOf()); - } - override bool opEquals(Object o) { if (this is o) @@ -2392,19 +2326,6 @@ class TypeInfo_Delegate : TypeInfo return c && this.deco == c.deco; } - @system unittest - { - double sqr(double x) { return x * x; } - int dbl(int x) { return x + x; } - sqr(double.init); // for coverage completeness - dbl(int.init); // for coverage completeness - - Object obj = typeid(typeof(&sqr)); - assert(obj.opEquals(typeid(typeof(&sqr)))); - assert(typeid(typeof(&sqr)) == typeid(typeof(&sqr))); - assert(typeid(typeof(&dbl)) != typeid(typeof(&sqr))); - } - override size_t getHash(scope const void* p) @trusted const { return hashOf(*cast(const void delegate() *)p); @@ -2612,7 +2533,7 @@ class TypeInfo_Const : TypeInfo } ///For some reason, getHash for interfaces wanted that -pragma(mangle, "_D9invariant12_d_invariantFC6ObjectZv") +pragma(mangle, "_D2rt10invariant_12_d_invariantFC6ObjectZv") extern(D) void _d_invariant(Object o) { TypeInfo_Class c; @@ -2886,7 +2807,6 @@ TTo[] __ArrayCast(TFrom, TTo)(return scope TFrom[] from) nothrow if ((fromSize % TTo.sizeof) != 0) { //onArrayCastError(TFrom.stringof, fromSize, TTo.stringof, toLength * TTo.sizeof); - import arsd.webassembly; abort(); } @@ -2966,7 +2886,8 @@ V[K] dup(T : V[K], K, V)(T aa) /** ditto */ V[K] dup(T : V[K], K, V)(T* aa) { - return (*aa).dup; + assert(false, "Not implemented for platform"); + return (*aa).dup; } T[] dup(T)(scope T[] array) pure nothrow @trusted if (__traits(isPOD, T) && !is(const(T) : T)) diff --git a/wasm/runtime/std/format.d b/wasm/runtime/std/format.d new file mode 100644 index 0000000..b080860 --- /dev/null +++ b/wasm/runtime/std/format.d @@ -0,0 +1,72 @@ +module std.format; + +import dlib.util; +import std.traits; +import wasm; + +char[] +sformat(Char, Args...)(scope return char[] buf, scope const(Char)[] fmt, Args args) +{ + SprintfType type = SprintfType.None; + static foreach(i; 0 .. Args.length) + { + static if(isArray!(Args[i]) || is(Args[i] == string)) + { + { + alias ElementType = typeof(*Args[i].init.ptr); + + with(SprintfType) + { + static if(is(Args[i] == string)) type = String; + else static if(is(ElementType == ubyte)) type = U8Array; + else static if(is(ElementType == byte)) type = I8Array; + else static if(is(ElementType == ushort)) type = U16Array; + else static if(is(ElementType == short)) type = I16Array; + else static if(is(ElementType == uint)) type = U32Array; + else static if(is(ElementType == int)) type = I32Array; + else static if(is(ElementType == ulong)) type = U64Array; + else static if(is(ElementType == long)) type = I64Array; + else static if(is(ElementType == float)) type = F32Array; + else static if(is(ElementType == double)) type = F64Array; + else static if(is(ElementType == char)) type = CharArray; + else static assert(false, "Type unsupported"); + } + + SprintfLoadArray(args[i].length, args[i].ptr, type); + } + } + else + { + with(SprintfType) + { + static if(is(Args[i] == ubyte)) type = U8; + else static if(is(Args[i] == byte)) type = I8; + else static if(is(Args[i] == ushort)) type = U16; + else static if(is(Args[i] == short)) type = I16; + else static if(is(Args[i] == uint)) type = U32; + else static if(is(Args[i] == int)) type = I32; + else static if(is(Args[i] == ulong)) type = U64; + else static if(is(Args[i] == long)) type = I64; + else static if(is(Args[i] == size_t)) type = SizeT; + else static if(is(Args[i] == float)) type = F32; + else static if(is(Args[i] == double)) type = F64; + else static if(is(Args[i] == bool)) type = Bool; + else static if(is(Args[i] == char)) type = Char; + else static assert(false, "Type unsupported"); + } + + SprintfLoadValue(&args[i], type); + } + } + + size_t length = SprintfEnd(buf, Str(fmt)); + + return buf[0 .. length]; +} + +char[] +sformat(alias fmt, Args...)(char[] buf, Args args) if(isSomeString!(typeof(fmt))) +{ + return .sformat(buf, fmt, args); +} + diff --git a/external/arsd-webassembly/std/math/rounding.d b/wasm/runtime/std/math/rounding.d similarity index 100% rename from external/arsd-webassembly/std/math/rounding.d rename to wasm/runtime/std/math/rounding.d diff --git a/wasm/runtime/std/math/traits.d b/wasm/runtime/std/math/traits.d new file mode 100644 index 0000000..c98eda5 --- /dev/null +++ b/wasm/runtime/std/math/traits.d @@ -0,0 +1,258 @@ +module std.math.traits; + +import std.traits; + +enum RealFormat +{ + ieeeHalf, + ieeeSingle, + ieeeDouble, + ieeeExtended, // x87 80-bit real + ieeeExtended53, // x87 real rounded to precision of double. + ibmExtended, // IBM 128-bit extended + ieeeQuadruple, +} + +template floatTraits(T) +{ + import std.traits : Unqual; + + // EXPMASK is a ushort mask to select the exponent portion (without sign) + // EXPSHIFT is the number of bits the exponent is left-shifted by in its ushort + // EXPBIAS is the exponent bias - 1 (exp == EXPBIAS yields ×2^-1). + // EXPPOS_SHORT is the index of the exponent when represented as a ushort array. + // SIGNPOS_BYTE is the index of the sign when represented as a ubyte array. + // RECIP_EPSILON is the value such that (smallest_subnormal) * RECIP_EPSILON == T.min_normal + enum Unqual!T RECIP_EPSILON = (1/T.epsilon); + static if (T.mant_dig == 24) + { + // Single precision float + enum ushort EXPMASK = 0x7F80; + enum ushort EXPSHIFT = 7; + enum ushort EXPBIAS = 0x3F00; + enum uint EXPMASK_INT = 0x7F80_0000; + enum uint MANTISSAMASK_INT = 0x007F_FFFF; + enum realFormat = RealFormat.ieeeSingle; + version (LittleEndian) + { + enum EXPPOS_SHORT = 1; + enum SIGNPOS_BYTE = 3; + } + else + { + enum EXPPOS_SHORT = 0; + enum SIGNPOS_BYTE = 0; + } + } + else static if (T.mant_dig == 53) + { + static if (T.sizeof == 8) + { + // Double precision float, or real == double + enum ushort EXPMASK = 0x7FF0; + enum ushort EXPSHIFT = 4; + enum ushort EXPBIAS = 0x3FE0; + enum uint EXPMASK_INT = 0x7FF0_0000; + enum uint MANTISSAMASK_INT = 0x000F_FFFF; // for the MSB only + enum ulong MANTISSAMASK_LONG = 0x000F_FFFF_FFFF_FFFF; + enum realFormat = RealFormat.ieeeDouble; + version (LittleEndian) + { + enum EXPPOS_SHORT = 3; + enum SIGNPOS_BYTE = 7; + } + else + { + enum EXPPOS_SHORT = 0; + enum SIGNPOS_BYTE = 0; + } + } + else static if (T.sizeof == 12) + { + // Intel extended real80 rounded to double + enum ushort EXPMASK = 0x7FFF; + enum ushort EXPSHIFT = 0; + enum ushort EXPBIAS = 0x3FFE; + enum realFormat = RealFormat.ieeeExtended53; + version (LittleEndian) + { + enum EXPPOS_SHORT = 4; + enum SIGNPOS_BYTE = 9; + } + else + { + enum EXPPOS_SHORT = 0; + enum SIGNPOS_BYTE = 0; + } + } + else + static assert(false, "No traits support for " ~ T.stringof); + } + else static if (T.mant_dig == 64) + { + // Intel extended real80 + enum ushort EXPMASK = 0x7FFF; + enum ushort EXPSHIFT = 0; + enum ushort EXPBIAS = 0x3FFE; + enum realFormat = RealFormat.ieeeExtended; + version (LittleEndian) + { + enum EXPPOS_SHORT = 4; + enum SIGNPOS_BYTE = 9; + } + else + { + enum EXPPOS_SHORT = 0; + enum SIGNPOS_BYTE = 0; + } + } + else static if (T.mant_dig == 113) + { + // Quadruple precision float + enum ushort EXPMASK = 0x7FFF; + enum ushort EXPSHIFT = 0; + enum ushort EXPBIAS = 0x3FFE; + enum realFormat = RealFormat.ieeeQuadruple; + version (LittleEndian) + { + enum EXPPOS_SHORT = 7; + enum SIGNPOS_BYTE = 15; + } + else + { + enum EXPPOS_SHORT = 0; + enum SIGNPOS_BYTE = 0; + } + } + else static if (T.mant_dig == 106) + { + // IBM Extended doubledouble + enum ushort EXPMASK = 0x7FF0; + enum ushort EXPSHIFT = 4; + enum realFormat = RealFormat.ibmExtended; + + // For IBM doubledouble the larger magnitude double comes first. + // It's really a double[2] and arrays don't index differently + // between little and big-endian targets. + enum DOUBLEPAIR_MSB = 0; + enum DOUBLEPAIR_LSB = 1; + + // The exponent/sign byte is for most significant part. + version (LittleEndian) + { + enum EXPPOS_SHORT = 3; + enum SIGNPOS_BYTE = 7; + } + else + { + enum EXPPOS_SHORT = 0; + enum SIGNPOS_BYTE = 0; + } + } + else + static assert(false, "No traits support for " ~ T.stringof); +} + +bool isNaN(X)(X x) @nogc @trusted pure nothrow +if (isFloatingPoint!(X)) +{ + version (all) + { + return x != x; + } + else + { + /* + Code kept for historical context. At least on Intel, the simple test + x != x uses one dedicated instruction (ucomiss/ucomisd) that runs in one + cycle. Code for 80- and 128-bits is larger but still smaller than the + integrals-based solutions below. Future revisions may enable the code + below conditionally depending on hardware. + */ + alias F = floatTraits!(X); + static if (F.realFormat == RealFormat.ieeeSingle) + { + const uint p = *cast(uint *)&x; + // Sign bit (MSB) is irrelevant so mask it out. + // Next 8 bits should be all set. + // At least one bit among the least significant 23 bits should be set. + return (p & 0x7FFF_FFFF) > 0x7F80_0000; + } + else static if (F.realFormat == RealFormat.ieeeDouble) + { + const ulong p = *cast(ulong *)&x; + // Sign bit (MSB) is irrelevant so mask it out. + // Next 11 bits should be all set. + // At least one bit among the least significant 52 bits should be set. + return (p & 0x7FFF_FFFF_FFFF_FFFF) > 0x7FF0_0000_0000_0000; + } + else static if (F.realFormat == RealFormat.ieeeExtended || + F.realFormat == RealFormat.ieeeExtended53) + { + const ushort e = F.EXPMASK & (cast(ushort *)&x)[F.EXPPOS_SHORT]; + const ulong ps = *cast(ulong *)&x; + return e == F.EXPMASK && + ps & 0x7FFF_FFFF_FFFF_FFFF; // not infinity + } + else static if (F.realFormat == RealFormat.ieeeQuadruple) + { + const ushort e = F.EXPMASK & (cast(ushort *)&x)[F.EXPPOS_SHORT]; + const ulong psLsb = (cast(ulong *)&x)[MANTISSA_LSB]; + const ulong psMsb = (cast(ulong *)&x)[MANTISSA_MSB]; + return e == F.EXPMASK && + (psLsb | (psMsb& 0x0000_FFFF_FFFF_FFFF)) != 0; + } + else + { + return x != x; + } + } +} + +bool isInfinity(X)(X x) @nogc @trusted pure nothrow +if (isFloatingPoint!(X)) +{ + import std.math.traits : floatTraits, RealFormat, MANTISSA_MSB, MANTISSA_LSB; + + alias F = floatTraits!(X); + static if (F.realFormat == RealFormat.ieeeSingle) + { + return ((*cast(uint *)&x) & 0x7FFF_FFFF) == 0x7F80_0000; + } + else static if (F.realFormat == RealFormat.ieeeDouble) + { + return ((*cast(ulong *)&x) & 0x7FFF_FFFF_FFFF_FFFF) + == 0x7FF0_0000_0000_0000; + } + else static if (F.realFormat == RealFormat.ieeeExtended || + F.realFormat == RealFormat.ieeeExtended53) + { + const ushort e = cast(ushort)(F.EXPMASK & (cast(ushort *)&x)[F.EXPPOS_SHORT]); + const ulong ps = *cast(ulong *)&x; + + // On Motorola 68K, infinity can have hidden bit = 1 or 0. On x86, it is always 1. + return e == F.EXPMASK && (ps & 0x7FFF_FFFF_FFFF_FFFF) == 0; + } + else static if (F.realFormat == RealFormat.ieeeQuadruple) + { + const long psLsb = (cast(long *)&x)[MANTISSA_LSB]; + const long psMsb = (cast(long *)&x)[MANTISSA_MSB]; + return (psLsb == 0) + && (psMsb & 0x7FFF_FFFF_FFFF_FFFF) == 0x7FFF_0000_0000_0000; + } + else + { + return (x < -X.max) || (X.max < x); + } +} + +version (LittleEndian) +{ + enum MANTISSA_LSB = 0; + enum MANTISSA_MSB = 1; +} +else +{ + enum MANTISSA_LSB = 1; + enum MANTISSA_MSB = 0; +} diff --git a/external/arsd-webassembly/std/random.d b/wasm/runtime/std/random.d similarity index 100% rename from external/arsd-webassembly/std/random.d rename to wasm/runtime/std/random.d diff --git a/wasm/runtime/std/stdio.d b/wasm/runtime/std/stdio.d new file mode 100644 index 0000000..36b7b43 --- /dev/null +++ b/wasm/runtime/std/stdio.d @@ -0,0 +1,22 @@ +module std.stdio; + +import dlib.util; +import std.traits; + +void +writefln(alias fmt, A...)(A args) if(isSomeString!(typeof(fmt))) +{ + static foreach(i; 0 .. A.length) + { + { + static if() + } + } +} + +void +writefln(Char, A...)(in Char[] fmt, A args) +{ + writefln(Str(fmt), args); +} + diff --git a/wasm/runtime/std/traits.d b/wasm/runtime/std/traits.d new file mode 100644 index 0000000..ace6baf --- /dev/null +++ b/wasm/runtime/std/traits.d @@ -0,0 +1,84 @@ +module std.traits; + +enum bool isFloatingPoint(T) = __traits(isFloating, T) && is(T : real); + +template isIntegral(T) +{ + static if (!__traits(isIntegral, T)) + enum isIntegral = false; + else static if (is(T U == enum)) + enum isIntegral = isIntegral!U; + else + enum isIntegral = __traits(isZeroInit, T) // Not char, wchar, or dchar. + && !is(immutable T == immutable bool) && !is(T == __vector); +} + +template isNumeric(T) +{ + static if (!__traits(isArithmetic, T)) + enum isNumeric = false; + else static if (__traits(isFloating, T)) + enum isNumeric = is(T : real); // Not __vector, imaginary, or complex. + else static if (is(T U == enum)) + enum isNumeric = isNumeric!U; + else + enum isNumeric = __traits(isZeroInit, T) // Not char, wchar, or dchar. + && !is(immutable T == immutable bool) && !is(T == __vector); +} + +enum bool isSigned(T) = __traits(isArithmetic, T) && !__traits(isUnsigned, T) + && is(T : real); + +enum hasMember(T, string name) = __traits(hasMember, T, name); + +enum bool isArray(T) = isStaticArray!T || isDynamicArray!T; + +enum bool isStaticArray(T) = __traits(isStaticArray, T); + +template isDynamicArray(T) +{ + static if (is(T == U[], U)) + enum bool isDynamicArray = true; + else static if (is(T U == enum)) + // BUG: isDynamicArray / isStaticArray considers enums + // with appropriate base types as dynamic/static arrays + // Retain old behaviour for now, see + // https://github.com/dlang/phobos/pull/7574 + enum bool isDynamicArray = isDynamicArray!U; + else + enum bool isDynamicArray = false; +} + +enum isAssignable(Lhs, Rhs = Lhs) = isRvalueAssignable!(Lhs, Rhs) && isLvalueAssignable!(Lhs, Rhs); + +enum isRvalueAssignable(Lhs, Rhs = Lhs) = __traits(compiles, { lvalueOf!Lhs = rvalueOf!Rhs; }); + +enum isLvalueAssignable(Lhs, Rhs = Lhs) = __traits(compiles, { lvalueOf!Lhs = lvalueOf!Rhs; }); + +enum bool isInstanceOf(alias S, T) = is(T == S!Args, Args...); + +template isInstanceOf(alias S, alias T) +{ + enum impl(alias T : S!Args, Args...) = true; + enum impl(alias T) = false; + enum isInstanceOf = impl!T; +} + +import core.internal.traits : CoreUnqual = Unqual; +alias Unqual = CoreUnqual; + +@property T rvalueOf(T)(inout __InoutWorkaroundStruct = __InoutWorkaroundStruct.init); + +@property ref T lvalueOf(T)(inout __InoutWorkaroundStruct = __InoutWorkaroundStruct.init); + +enum bool isIterable(T) = is(typeof({ foreach (elem; T.init) {} })); + +template isAggregateType(T) +{ + static if (is(T == enum)) + enum isAggregateType = isAggregateType!(OriginalType!T); + else + enum isAggregateType = is(T == struct) || is(T == class) || is(T == interface) || is(T == union); +} + +enum bool isSomeString(T) = is(immutable T == immutable C[], C) && (is(C == char) || is(C == wchar) || is(C == dchar)); diff --git a/wasm/runtime/wasm.d b/wasm/runtime/wasm.d new file mode 100644 index 0000000..c53e15e --- /dev/null +++ b/wasm/runtime/wasm.d @@ -0,0 +1,51 @@ +module wasm; + +import ldc.attributes; + +enum SprintfType : size_t +{ + None, + U8, + I8, + U16, + I16, + U32, + I32, + U64, + I64, + SizeT, + F32, + F64, + Bool, + String, + U8Array, + I8Array, + U16Array, + I16Array, + U32Array, + I32Array, + U64Array, + I64Array, + F32Array, + F64Array, + Char, + CharArray, +} + +extern extern(C) @nogc pure @llvmAttr("wasm-import-module", "env"): + +@llvmAttr("wasm-import-name", "Console") void +Console(string str, bool write_line); + +@llvmAttr("wasm-import-name", "Abort") void +Abort(string message); + +@llvmAttr("wasm-import-name", "SprintfLoadValue") void +SprintfLoadValue(const(void)* ptr, SprintfType type); + +@llvmAttr("wasm-import-name", "SprintfLoadArray") void +SprintfLoadArray(size_t length, const(void)* ptr, SprintfType type); + +@llvmAttr("wasm-import-name", "SprintfEnd") size_t +SprintfEnd(char[] buffer, string format); + diff --git a/wasm/wasm.js b/wasm/wasm.js index e69de29..5f4f923 100644 --- a/wasm/wasm.js +++ b/wasm/wasm.js @@ -0,0 +1,285 @@ + +const TYPES = { + U8: 1, + I8: 2, + U16: 3, + I16: 4, + U32: 5, + I32: 6, + U64: 7, + I64: 8, + SizeT: 9, + F32: 10, + F64: 11, + Bool: 12, + String: 13, + U8Array: 14, + I8Array: 15, + U16Array: 16, + I16Array: 17, + U32Array: 18, + I32Array: 19, + U64Array: 20, + I64Array: 21, + F32Array: 22, + F64Array: 23, + Char: 24, + CharArray: 25, +}; + +function assert(condition, message) +{ + if(!condition) + { + console.assert(condition, message); + throw "Assert failure"; + } +} + +class WasmManager +{ + constructor(memory, ptr_size = 4) + { + this.ptr_size = ptr_size; + + this.sprintf_values = []; + + this.stdout_string = ""; + + switch(this.ptr_size) + { + case 8: this.LoadSize = this.LoadU64; break; + case 4: + default: this.LoadSize = this.LoadU32; break; + } + } + + SetMemory(memory) + { + this.memory = memory; + this.data_view = new DataView(this.memory.buffer); + } + + LoadBool = (addr) => Boolean(this.data_view.getUint8(addr, true)); + + LoadF32 = (addr) => this.data_view.getFloat32(addr, true); + LoadF64 = (addr) => this.data_view.getFloat64(addr, true); + + LoadU8 = (addr) => this.data_view.getUint8 (addr, true); + LoadI8 = (addr) => this.data_view.getInt8 (addr, true); + LoadU16 = (addr) => this.data_view.getUint16 (addr, true); + LoadI16 = (addr) => this.data_view.getInt16 (addr, true); + LoadU32 = (addr) => this.data_view.getUint32 (addr, true); + LoadI32 = (addr) => this.data_view.getInt32 (addr, true); + + LoadU64(addr) + { + const lo = this.data_view.getUint32(addr+0, true); + const hi = this.data_view.getUint32(addr+4, true); + return lo + hi*4294967296; + } + + LoadI64(addr) + { + const lo = this.data_view.getUint32(addr+0, true); + const hi = this.data_view.getInt32 (addr+4, true); + return lo + hi*4294967296; + } + + LoadArray = (array_class, length, addr) => new array_class(this.memory.buffer, addr, length); + LoadString = (length, addr) => new TextDecoder().decode(this.LoadArray(Uint8Array, length, addr)); + + LoadCString(ptr) + { + if(!ptr) return null; + + let length = 0; + for(; this.LoadU8(ptr+length); length += 1) {} + + return this.LoadString(ptr, length); + } + + StoreString(ptr, value) + { + const src = new TextEncoder().encode(value); + const dst = new Uint8Array(this.memory.buffer, ptr, src.length); + + dst.set(src); + + return src.length; + } + + Imports() + { + const manager = this; + return { + env: { + Abort(string_length, string_ptr) + { + console.error(manager.LoadString(string_length, string_ptr)); + }, + SprintfLoadValue(ptr, type) + { + let value = null; + switch(type) + { + case TYPES.U8: value = manager.LoadU8(ptr); break; + case TYPES.I8: value = manager.LoadI8(ptr); break; + case TYPES.U16: value = manager.LoadU16(ptr); break; + case TYPES.I16: value = manager.LoadI16(ptr); break; + case TYPES.U32: value = manager.LoadU32(ptr); break; + case TYPES.I32: value = manager.LoadI32(ptr); break; + case TYPES.U64: value = manager.LoadU64(ptr); break; + case TYPES.I64: value = manager.LoadI64(ptr); break; + case TYPES.SizeT: value = manager.LoadSize(ptr); break; + case TYPES.F32: value = manager.LoadF32(ptr); break; + case TYPES.F64: value = manager.LoadF64(ptr); break; + case TYPES.Bool: value = manager.LoadBool(ptr); break; + case TYPES.Char: value = manager.LoadString(1, ptr); break; + default: break; + } + + if(value) + { + if(type === TYPES.Bool) + { + manager.sprintf_values.push(value ? 'true' : 'false'); + } + else + { + manager.sprintf_values.push(''+value); + } + } + }, + SprintfLoadArray(length, ptr, type) + { + let value = null; + switch(type) + { + case TYPES.CharArray: + case TYPES.String: value = manager.LoadString(length, ptr); break; + case TYPES.U8Array: value = manager.LoadArray(Uint8Array, length, ptr); break; + case TYPES.I8Array: value = manager.LoadArray(Int8Array, length, ptr); break; + case TYPES.U16Array: value = manager.LoadArray(Uint16Array, length, ptr); break; + case TYPES.I16Array: value = manager.LoadArray(Int16Array, length, ptr); break; + case TYPES.U32Array: value = manager.LoadArray(Uint32Array, length, ptr); break; + case TYPES.I32Array: value = manager.LoadArray(Int32Array, length, ptr); break; + case TYPES.U64Array: value = manager.LoadArray(BigUint64Array, length, ptr); break; + case TYPES.I64Array: value = manager.LoadArray(BigInt64Array, length, ptr); break; + case TYPES.F32Array: value = manager.LoadArray(Float32Array, length, ptr); break; + case TYPES.F64Array: value = manager.LoadArray(Float64Array, length, ptr); break; + } + + if(value) + { + if(type !== TYPES.String && type !== TYPES.CharArray) + { + value = `[${value.join(", ")}]`; + } + + manager.sprintf_values.push(value); + } + }, + SprintfEnd(buffer_length, buffer_ptr, string_length, string_ptr) + { + let format_string = manager.LoadString(string_length, string_ptr); + + let index = format_string.indexOf("%"); + + let result = format_string.substring(0, index); + let format = format_string.substring(index); + + function Next() + { + index = format.indexOf("%"); + result += format.substring(0, index); + format = format.substring(index); + } + + let value_index = 0; + for(; value_index < manager.sprintf_values.length;) + { + assert(format.length); + + const char = format.charAt(1); + assert(char !== " " && char.length, "Invalid format string " + format_string); + + if(char === "%") + { + result += "%"; + format = format.substring(2); + Next(); + continue; + } + else if(char >= '0' && char <= '9') + { + let digit_index = 2; + for(;;) + { + let next_char = format.charAt(digit_index++); + + assert(next_char !== " " && next_char.length, "Invalid format string " + format_string); + + if(next_char < '0' || next_char > '9') + { + continue; + } + + result += manager.sprintf_values[value_index++]; + format = format.substring(digit_index); + Next(); + + break; + } + } + else if((char >= 'a' && char <= 'z') || (char >= 'A' && char <= 'Z')) + { + result += manager.sprintf_values[value_index++]; + format = format.substring(2); + Next(); + } + else assert(false, "Invalid format string " + format_string); + } + + result += format; + + const bytes_written = manager.StoreString(buffer_ptr, result); + assert(bytes_written <= buffer_length, "Format string is longer than buffer length"); + + manager.sprintf_values = []; + + return bytes_written; + }, + Console(string_length, string_ptr, write_line) + { + manager.stdout_string += manager.LoadString(string_length, string_ptr); + if(write_line || manager.stdout_string.includes("\n")) + { + console.log(manager.stdout_string); + manager.stdout_string = ""; + } + }, + }, + } + } +} + +async function StartWasm(path) +{ + const manager = new WasmManager(); + + const response = await fetch(path); + const data = await response.arrayBuffer(); + const wasm = await WebAssembly.instantiate(data, manager.Imports()); + + const exports = wasm.instance.exports; + + if(exports.memory) + { + manager.SetMemory(exports.memory); + } + + exports?._start(); + + return; +}