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;
+}