Calls between JavaScript and WebAssembly are finally fast
hacks.mozilla.org
hacks.mozilla.org
I think the thing I found most exciting is at the end: “WebAssembly is getting more flexible types very soon. Experimental support for the current proposal is already landed in Firefox Nightly.” Implication being that WASM will have native access to the DOM.
I think at that point WASM could be used to fully work with the DOM with no negative costs, right?
The DOM builtins should be helped by the Reference Types Proposal for WebAssembly. [1]
`The host bindings proposal` is going to help speed up new and getter/setter calls. [2]
1: `Currently the only built-ins that we support this for are mostly limited to the math built-ins. That’s because WebAssembly currently only has support for integers and floats as value types.
That works well for the math functions because they work with numbers, but it doesn’t work out so well for other things like the DOM built-ins. So currently when you want to call one of those functions, you have to go through JavaScript. That’s what wasm-bindgen does for you.
But WebAssembly is getting more flexible types very soon. Experimental support for the current proposal is already landed in Firefox Nightly behind the pref javascript.options.wasm_gc. Once these types are in place, you will be able to call these other built-ins directly from WebAssembly without having to go through JS.`
-- https://github.com/WebAssembly/reference-types
2: `there are still a couple of built-ins where you will need to go through JavaScript. For example, if those built-ins are called as if they were using new or if they’re using a getter or setter. These remaining built-ins will be addressed with the host-bindings proposal.`
-- https://github.com/WebAssembly/host-bindings
(BTW I could be mistaken, just quoting the article)
"A new proposal has been made to the WebAssembly specification committee a few months ago: to add reference types to the type system. Reference types are a new way to represent a reference to any host values. In a Web environment, this means being capable of playing with JavaScript values within WebAssembly. This is a huge difference with the existing type system, which only contains primitive types: integers represented on 32 or 64 bits, IEEE754 floating-point numbers represented on 32 or 64 bits. This is also a first step for implementing garbage collection (GC) integration within WebAssembly: since these reference values have been allocated on the GC heap in JavaScript, they need to be traced during wasm execution."
https://blog.benj.me/2018/07/04/mozilla-2018-faster-calls-an...
And though it's a matter of opinion, I disagree. The GPU is easier for me to use for things like screen-space shading, alpha blending, sprite transformations and distortions, and it all performs much faster than the equivalent CPU render code.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
It doesn't read like this is an explicit goal of the project, but are we going to get this by accident? Being able to use the same X code (where X is your favourite statically-compilable language) to generate both a WASM version that runs on the web and a statically-compiled version that links to the Y browser source (where Y can be whichever browser works for you because they all support the same native API) would be awesome.
WebAssembly is one of those technologies where we haven't seen the true extent of its capabilities yet. This is an exciting time to be working in browsers.
JS developers that don't have experience in lower level languages or some CS concepts. In other words, probably a good portion of the expected audience given it's about JS and possible improvements, and is likely to trickle into the news sources to cater to those people.
https://medium.com/@donhopkins/the-shape-of-psiber-space-oct...
Was the idea with the direct stack manipulation dragging you describe to provide visual analogues to PostScript commands like roll/pop/etc?
It was very dangerous since you were editing the live data structures of the window system! The most complicated program I used it to debug was itself.
It was integrated with NeWS's multithreaded debugger, so when some process hit a bug or a breakpoint, you could "enter" the process and see what was on its stack, peek and poke at the objects, rearrange the stack and edit the objects, then send it on its way.
Since PostScript is homoiconic like Lisp, code and objects are made of arrays and dictionaries, which you could visually browse, edit, and execute. Very much inspired by Smalltalk, but with a twist of Lisp and FORTH!
The primary target audience is Web developers interested in new technologies. I'd wager most of them don't know what a stack frame is and would appreciate the explanation.
Probably a lot of JavaScript developers who started in web land and are only now starting to be exposed to the underlying architecture.
99% of all the JS developers I've ever met?
[1] On the right: https://2r4s9p1yi1fa2jd7j43zph8r-wpengine.netdna-ssl.com/fil...
Firefox WASM: 1809ms
Chrome WASM: 2855ms
Edge WASM: 12872ms
Firefox JS: 5413ms
Chrome JS: 4779ms
Edge JS: 8512ms
Firefox linux (v62.0.3)1904
From article: "This means that in the latest version of Firefox * Beta *, calls between JS and WebAssembly are faster than non-inlined JS to JS function calls."
Firefox linux 2132
E.g. if userscript replaces the built-in window.fetch() API to modify page behavior will wasm also be intercepted?
> We took the code that C++ was running — the entry stub — and made it directly callable from JIT code. When the engine goes from JavaScript to WebAssembly, the entry stub un-boxes the values and places them in the right place. With this, we got rid of the C++ trampolining.
So they replaced unboxing C++ trampoline with entry stub. But isn't that stub technically written in C++ too?
Specifically, see the GenerateJitEntry bits in https://bug1319203.bmoattachments.org/attachment.cgi?id=8949... -- the masm.whatever() calls are calls into an architecture-dependent assembler that will output the relevant machine instructions.
There is still an out-of-line codepath that does call into a C++-implemented entry stub in some cases when the argument conversions involved are too complicated to do them directly in the JIT-generated code.
I don't know what I want more, DOM calls from WASM, or C++ modules. A web without javascript would be welcome.
Yeah, Wasm => JS calls used to be reasonably fast because we had optimized that before. The work described here made that path much nicer, though (a more unified stack layout for JIT/Wasm frames) and also a bit faster still.
JS => Wasm calls being slow was one of the big performance cliffs in SpiderMonkey and I'm really glad that's fixed now.
A 3 GHz CPU would do 3 ticks per nanoseconds, doing such a bridging call in 23 or 15 CPU ticks is quite good IMHO (I just hope I didn't blunder the math, but function call overhead from WASM to JS isn't much of an issue, it was already fast after the previous round of optimizations).
Why? What is it that wasm or c++ provides you in making a DOM call that modern JS does not?
I have high hopes for Kotlin here. With Kotlin native they are targeting native IOS and native Android. Being able drive browsers from the same code-base via WASM seems like it will be a useful feature. Also, they seem to be really serious about tool support with Kotlin; providing dom/webgl and other bindings, and having decent end to end support with IDEs and other tools.
I imagine, Microsoft might have similar plans for C#. And it wouldn't surprise me if Apple was working on some Swift WASM support either.
Changing the game into "browsers run WASM" and JS is a built-in JIT compiler for it promises to make everyone's lives much easier.
The JavaScript object model is likely to remain fundamental for cross-language within-browser interop, just like C api's are fundamental for foreign function calls in other environments. (Or Java API's in a JVM.)
all assumptions here
It can be minified if both the web assembly and foreign code pass through the same minifier, but source code still has to be readable.
The ability to use existing non-JS code
The ability to write loops involving non-trivial computations without spamming a hundred million heap objects
etc.
How does being able to call the DOM from webasm give you that? That's what webasm is for in the first place.
> > The ability to write loops involving non-trivial computations without spamming a hundred million heap objects
> How does being able to call the DOM from webasm give you that? That's what webasm is for in the first place.
The tell parent comment that, not me.
And heck, even without that, there are languages that transpile directly to JavaScript.
Also, for several languages, specifically those based on LLVM, the path to wasm is much clearer than compiling to JS.
Typescript, Coffeescript and a host of similar options have essentially the same data model. What makes other languages, like Java, Rust or Python actually useful is their way of handling data. Which is why you can't transcribe these languages to Javascript trivially.
Still, the ecosystem of javascript is a lot better than the languages which compile to WASM as of right now. I guess that will change...
Some devs are treating WASM as an entire runtime and outputing canvas/webGL code. I think that is mostly a mistake, and I'll be very disappointed if that becomes the defacto approach for new web devs.
However, the cool thing about WASM is that it's just the code part. So you can build an app that's still normal CSS/HTML, just like every web-app should be, and then you can replace all of your JS with WASM (with the current exception of a little bit of JS-to-DOM binding code which Mozilla has promised to eventually get rid of).
In the world of Flash/Java you're essentially working in a completely separate platform, it just happens to be launched from a web browser. So shortcuts don't work, accessibility is awful, users can't customize the page, extensions are broken, etc, etc...
With WASM, you don't have to be in that position. You can say, "I'm a web developer, I treat the web like a medium, I write responsive HTML/CSS. I just use C instead of Javascript."
True, but until Web development catches up with the state of Flash and other native RAD GUI designers, it is a very attractive proposition.
One thing I hate when doing web development is playing around with CSS and tag soup while trying to make it work the same way across all target platforms required by the customer, that in native code would be a couple of graphical calls to the visualization engine and platform widgets, which also provide layout managers.
Stuff like Web Components and Houdini would make it better, but who knows when they will be widely available.
This goes back to what I was talking about with treating the web like a medium, not a distribution platform. Fundamentally, the web is about giving you a "controlled" way to give up control. The native paradigm is "I want control over what each individual pixel looks like", and the web paradigm is, "heck off, well-written apps don't do that."
I understand why it's attractive, and why it'll continue to be attractive. And I highly agree about Houdini being an exciting development (I don't personally think that Web Components are offering much, but whatever). I'm really looking forward to being able to polyfill CSS. But that difference you're talking about is probably not ever going to go away completely, because the web is optimizing to solve different problems than most native frameworks.
Not to say one approach is better than the other (Flash had some legitimate use cases), but I find that there is an actual cultural difference between how web-apps and native apps are designed and how they balance between user control and developer control. It's not just technology.
I worked with many of them and they are struggling in the post Flash world. This led to a removal of designers from the implementation and a rise of quality of applications, at least on the technical side.
Hopefully with design tools lile Pagedraw and FramerX, we will get tools as powerful as Flash, with better integration behavior and more control over the code quality.
Realistically, that would be not much different than current sites using JS, or Flash when that was a thing, or even Java applets. None of those deprecated or replaced HTML, and WASM won't either. At worse, it will be just another thing you can block with a script blocker.
Ongoing POC of running Unity games, .NET, Go, Qt and many others already show it is the direction many are moving into.
That is the day where the open web is a little closer to dead. Possibly a lot closer. The move away from flash was a move towards empowering users. That did cost a little bit for developers, as they had worse tools to control the experience, but that also meant that it was more likely (eventually) that the experience they did develop would work in more cases. How many flash sites dealt with different size displays well, or dynamically resized and flowed correctly? How many worked with screen readers?
That's one of the benefits we've reaped by pushing the framework to the browser, and using open standards. Items that traditionally would haven't gotten much attention were also seen as important, and saw advances. I don't know how much some random React-like framework that just draws on the canvas would focus on screen reader support, but I suspect it wouldn't be high on their priority list. And even if it is on theirs, what about the 5 other main competitors that will be sharing market space with it?
I just hope that the sites that opt for these types of system are few and far between, and have specific operating needs that make it worthwhile. I think it's good that we can, but usually we shouldn't.
As a simple example, it's not impossible that Adobe could literally take the flash VM code, compile to WASM with emscripten, and ship it along with an SWF file and a little setup code. This completely bypasses the need for a browser plugin for flash. Now, I suspect the flash VM might be large enough to make this noticeable, but it probably wouldn't be hard for Adobe to streamline it, and then re-purpose a lot of those tools used to build flash that everyone loves to rave about...
I believe there are financial incentives for this to happen, therefore I think it's only a matter of time until it does.
But I saw far to many flash websites in the past, because sometimes people really just want a powerpoint presentation for a website because they have a "vision", and usability is a foreign concept.
I think it's highly likely some service like Squarespace will deploy sites using something like this, because it both lets them more finely control some aspects and features, as well as make it harder for people to move. All marketed under "website plagiarism protection" or something similar. "Want to stop scraping? Just use this middleware package..."
You can easily test it by creating a WebAssembly module in C with a function that overwrites an internal memory buffer, placed besides other data, which is used to parametrize behaviour of another function, for example a boolean stating that the user is authenticated.
This kind of thing is a risk for NodeJS, but that's only because NodeJS inexplicably allows host access by default. I believe Dahl is looking to address this in his next project.
> for example a boolean stating that the user is authenticated.
You should never do authentication clientside. Clients are untrustworthy.
> You should never do authentication clientside. Clients are untrustworthy.
Nice way of picking up on my example, that was just an idea for a quick POC.
I wasn't trying to cherry-pick your example, but I see your example as indicative of the only types of bugs that are exposed by allowing unsafe memory access.
If you're writing a web app today, any unvalidated code you run on the client is a risk. WASM changes nothing about that. The only reason clientside memory access in a sandbox would ever be a security issue is if you were relying on a client not being able to access itself (ie, for stuff like authentication). And since you should never, ever trust the client anyway... I don't see why anyone should care about that class of issues. Don't run unvalidated code, and don't put any serverside logic with security implications on the client.
If you are going to run unvalidated code, you can put it in a WASM sandbox with its own dedicated chunk of memory. That's actually easier to do in WASM than it is in Javascript, since you don't need to worry about references across iframes anymore.
If you can think of a valid POC that's not already a security risk using current technologies, I'm open to it, but I can't think of one.
You can use a language that provides them and targets WebAssembly, without taking away the thing that makes the platform uniquely useful.
It might have been compiled from Ada, Rust or plain C, just as an example.
Telling just to use a language we trust is not an option if one is just using other people libraries.
Thus bringing to the web the same security level as depending on regular native libraries, because a regular user is not going to check what a web page depends on.
Note that this is considerably better than Javascript, because without going out of your way to make an iframe, separate Javascript scripts aren't isolated from each other, so you don't know if one of them is modifying a prototype or hooking around other variables in the global scope.
By default, separate WASM modules don't have access to the same memory.
Linux Security Summit 2018 had a report that 68% of kernel exploits are caused by out-of-bounds errors.
User space doesn't write on kernel level, but can call syscalls, which happen to trigger such exploits.
WebAssembly is no different, regardless how they are sandboxed between modules.
Why wouldn't you just use JS to remove the WASM module, replace it with a malicious module you wrote, and then overwrite the exported functions? Safe pointers won't protect you from a malicious host environment.
And like the original parent said, if you have code where you want safe pointers, use a language that requires them. Your module will be protected from any other WASM dependencies you bring in because they run in separate contexts and can't access each other. Don't give them access to critical memory. WASM makes this easy. You're just not protected from a compromised host environment -- but that's not new, that's the case for everything on the web.
WASM protects the page from your module, not the other way around. Imagine that I'm running Linux, and I put Windows in a VM inside of Linux. Is it a problem that Linux could compromise or mess with my Windows VM? Of course not, that's intended -- the point of a VM is not to protect the system running inside the VM, it's to protect the system running outside of it.
Maybe I don't understand what your threat model is. Are you worried about serverside code? I don't know that many people are really looking into running WASM on the serverside, and I don't see how the threat model is any different from writing a server in something like C++.
So assuming nothing changes and my understanding is correct, you'll just have to split your memory into two chunks; one that's safe to share and one that you keep private to your module.
And of course, unless you're writing WASM yourself by hand (which is possible, but I don't know how many people are doing it), your compiler should handle all of this for you -- so at the point where Rust or whatever says, "we want to allow you to split your code into multiple WASM modules instead of just bundling it all into one", it would have to come up with some kind of allocation strategy for this.
...
In the MVP, linear memory cannot be shared between threads of execution. The addition of threads :unicorn: will allow this.
To me that sounds like, "define multiple linear memories, then import them after you instantiate the module." But I could be misinterpreting.
> In the current version of WebAssembly, at most one memory may be defined or imported in a single module, and all constructs implicitly reference this memory 0. This restriction may be lifted in future versions.
But I don't get the impression that it's completely unplanned. Modules still take a vector of linear memories, not just one[0], which would be pointless if it wasn't planned to let you access the others at some point. Data segments also explicitly allow referencing a memidx[1] (even if right now 0 is the only one that's allowed).
Still, I've been under the impression that this was going in as part of threads, and I was all excited since threads were making such good progress. I'm a little disappointed to find out it's not.
It's also possible globals[2] might solve some of the same problems? But I haven't dug into them enough to know what their memory model looks like or how well they work across multiple modules, and in any case, sharing multiple globals is probably going to be less convenient than just sharing memory, so I'd still like to see the ability to import multiple chunks of memory.
[0]: https://webassembly.github.io/threads/syntax/modules.html#
[1]: https://webassembly.github.io/threads/syntax/modules.html#sy...
[2]: https://developer.mozilla.org/en-US/docs/WebAssembly/Using_t...
If I'm remembering correctly, I wouldn't put that in the same category as Unity and Qt.
[0]: https://blogs.msdn.microsoft.com/webdev/2018/03/22/get-start...
Can I do all that in Wasm?
For practical purposes of reverse engineering, minified JS is far better than Wasm. Wasm is the new Flash.
I mean, yes, there are things like Blazor or similar things for Rust or Nim, but these aren't yet compelling to me.
(I don’t think this is where wasm will be used as much but I’m still interested!)
There is something like this for Rust, I think.
But Python for the Browser is not compelling unless we both have a faithful implementation of the language and data model and a productive and beautiful frontend framework. Call it a React for Python or a Django for the frontend.
Similar goals apply for other languages which have an edge on Javascript in some regard.
(And yeah, there are several of these in Rust now, but they're extremely early days, and not really ready for anything serious at all.)
Also we don't need performance parity. We just need enough performance for such a framework to not suck. Interpreting Python bytecode in Javascript already doesn't suck that much, compiling bytecode to WASM and then to native machine code would suck even less. And there are always ways to improve the performance of critical parts.
https://blog.benj.me/2018/07/04/mozilla-2018-faster-calls-an...
(Maybe it’s functionally easy but getting the heuristics right is hard? Curious what the primary obstacles are for Mozilla or others.)
The WASM spec already tries to avoid pretty much every other big problem that would've confounded attempts to cross-language inline. For example, WASM requires structured control flow (IOW, WASM doesn't have `goto`), avoids type punning in favor of a reinterpret primitive, uses function addresses that are orthogonal to memory addresses, and allows non-deterministic floating point bit representations, all to match up closer with the way existing JavaScript JITs already work. But they can't do anything about boxing without either breaking the web or making WebAssembly essentially a binary encoding of the JavaScript language.
The point of UnityJS is to tightly and efficiently integrate Unity3D and JavaScript, so it does a lot of JavaScript <=> C# calls, and I'm looking forward to it getting even faster!
https://github.com/SimHacker/UnityJS
You can pass delegates to C# functions that are directly callable into JavaScript using some magic PInvoke attributes and the Unity Runtime.dynCall function.
Declare a delegate that describes the signature of your C# function you want to call from JavaScript:
https://github.com/SimHacker/UnityJS/blob/master/UnityJS/Ass...
public delegate int AllocateTextureDelegate(int width, int height);
Then declare a C# static method with the MonoPInvokeCallback attribute, to implement you C# function:https://github.com/SimHacker/UnityJS/blob/master/UnityJS/Ass...
[MonoPInvokeCallback(typeof(AllocateTextureDelegate))]
public static int AllocateTexture(int width, int height) { ... }
Then pass those specially marked delegates to JavaScript and stash them in JS variables when you initialize (it doesn't work unless you use the magic MonoPInvokeCallback attribute):https://github.com/SimHacker/UnityJS/blob/master/UnityJS/Ass...
[DllImport(PLUGIN_DLL)]
public static extern void _UnityJS_HandleAwake(AllocateTextureDelegate allocateTextureCallback, FreeTextureDelegate freeTextureCallback, LockTextureDelegate lockTextureCallback, UnlockTextureDelegate unlockTextureCallback);
https://github.com/SimHacker/UnityJS/blob/master/UnityJS/Ass... public override void HandleAwake()
{
//Debug.Log("BridgeTransportWebGL: HandleAwake: this: " + this + " bridge: " + bridge);
_UnityJS_HandleAwake(
AllocateTexture,
FreeTexture,
LockTexture,
UnlockTexture);
}
In the awake function on the JavaScript side of your Unity WebGL extension (a .jslib file), wrap the C# delegate in a JavaScript thunk that calls into it via Runtime.dynCall:https://github.com/SimHacker/UnityJS/blob/master/UnityJS/Ass...
// Called by Unity when awakened.
_UnityJS_HandleAwake: function _UnityJS_HandleAwake(allocateTextureCallback, freeTextureCallback, lockTextureCallback, unlockTextureCallback)
{ [...]
function _UnityJS_AllocateTexture(width, height)
{
//console.log("UnityJS.jslib: _UnityJS_AllocateTexture: width: " + width + " height: " + height + " allocateTextureCallback: " + allocateTextureCallback);
var result = Runtime.dynCall('iii', allocateTextureCallback, [width, height]);
//console.log("UnityJS.jslib: _UnityJS_AllocateTexture: result: " + result);
return result;
};
window.bridge._UnityJS_AllocateTexture = _UnityJS_AllocateTexture;
Then you can call the C# method from JavaScript:https://github.com/SimHacker/UnityJS/blob/f0a4e1fb07fd9e8aab...
params.cache.backgroundSharedTextureID = id =
window.bridge._UnityJS_AllocateTexture(params.width, params.height);
This is zillions of time faster and more flexible than using Unity's terrible SendMessage technique to send messages from JS=>C#, whose only parameter is a single string, and which inefficiently dispatches messages by looking up Unity objects by name, and is asynchronous and can't return a result.I use this technique to efficiently copy binary textures and arrays of numbers between JavaScript and C#. MUCH better than serializing it as JSON, or base 64 encoded PNG files in a data: url (yuck!).
https://github.com/SimHacker/UnityJS/blob/674eda49be12a70812...
function DrawToCanvas(params, drawer, success, error)
{ [...]
var id = params.pie.backgroundSharedTextureID;
if (!id) {
params.pie.backgroundSharedTextureID = id =
window.bridge._UnityJS_AllocateTexture(params.width, params.height);
//console.log("game.js: DrawToCanvas: WebGL: AllocateTexture: width: " + params.width + " height: " + params.height + " id: " + id);
}
var imageData =
context.getImageData(0, 0, params.width, params.height);
window.bridge._UnityJS_UpdateTexture(id, imageData);
texture = {
type: 'sharedtexture',
id: id
};
success(texture, params);
canvasNode.parentNode.removeChild(canvasNode);
This lets me draw 2D user interface stuff, pie charts, diagrams, data visualizations, etc, in JavaScript with canvas, d3, or whatever library I like, and then efficiently use those images in Unity3D as user interface overlays, 3D textures, etc. It works great, and it's smooth and interactive, mixing up 2D canvas graphics with 3D Unity stuff!Unity is sorely lacking a decent 2D drawing library like canvas, not to mention fancy stuff built on top of it like d3.
I'm currently working on the plumbing to send binary arrays of floats from JavaScript to Unity, so I can pass them right into shaders!
Here's some discussion about the magic MonoPInvokeCallback attribute:
https://forum.unity.com/threads/monopinvokecallback-in-unity...
And about Unity.dyncall and the WebGL runtime:
https://forum.unity.com/threads/c-jslib-2-way-communication....
and:
https://forum.unity.com/threads/super-fast-javascript-intera...
Good note taken.