dart-lang / sdk

The Dart SDK, including the VM, JS and Wasm compilers, analysis, core libraries, and more.
https://dart.dev
BSD 3-Clause "New" or "Revised" License
10.06k stars 1.56k forks source link

Provide interop between Dart and WASM #37355

Open bryanrideshark opened 5 years ago

bryanrideshark commented 5 years ago

Would it be possible to enable Web Assembly in Dart?

Were this to be possible, I think it would greatly increase investment in the language as existing codebases would have a common compile target which could be used as a bridge.

For example I'd love to use Dart/Flutter but doing so would be incredibly costly without much benefit. Were I able to use WASM in Dart however the story would be very different- Flutter would become just as easy to use as any other web interface were that the case.

Can we get an up/down on whether or not this is A possible, and B something the team would like to support in the future?

lrhn commented 5 years ago

As I see it, supporting Web Assembly is not a language feature. It doesn't require any new language features, and since it's only available on the web, we would cautious about adding platform specific syntax. All it needs is a way to communicate with JavaScript, which we already have in JavaScript interop.

I'll move this question to the SDK repository and make it a question for the Dart-for-web team, who are in charge of JavaScript interop: Can they include WASM interop in dart:js or package:js?

lexaknyazev commented 5 years ago

A package for accessing raw WASM API from JS exists: https://pub.dev/packages/wasm_interop

common compile target which could be used as a bridge

Emscripten-compiled modules always require additional JS glue to do initialization and to pass any function parameter or return value that is not a number. So an actual interop would be between dart2js compiled JS and emscripten-supplied JS (which is already possible via existing JS-interop options).

Compiling Dart code to WASM may be interesting for some use cases but the final result would look like what emscripten does: a WASM module with auto-generated JS glue. So again, an actual interop would be between two JS codebases.

srawlins commented 5 years ago

CC @yjbanov @mraleph @matanlurey

mraleph commented 5 years ago

Could you clarify what you mean by "Web Assembly Support" because there are multiple different things this could mean, for example:

For example I'd love to use Dart/Flutter but doing so would be incredibly costly without much benefit. Were I able to use WASM in Dart however the story would be very different- Flutter would become just as easy to use as any other web interface were that the case.

It is not really clear to me why any form of WASM support makes it easier for you to use Flutter.

You can already use any native language in your Flutter application, you don't really need any WASM support for that. Of course you need to figure out interoperability story because Flutter framework is written in Dart so it works best when actually used from Dart and not from some other language.

Say you want to use Flutter class StatefulWidget from C++ code - how is this supposed to work? What does it even mean? WASM does not magically answer this question.

vsmenon commented 5 years ago

Since we have an issue ( #32894 ) for compilation, let's use this for interop. We may want to split the native and web cases ( @mraleph 's first two bullets) at some point, but one use case might be deploying / sharing C++ components between Web and native targets.

sffc commented 5 years ago

We are interested in Google i18n about using WASM in native environments with Dart. See https://github.com/dart-lang/sdk/issues/32894#issuecomment-513325349

kevmoo commented 5 years ago

@nex3 – do you already have an issue open on this?

nex3 commented 5 years ago

@kevmoo I don't think so.

Using WASM from Dart is something I'm interested in seeing as someone with a vested interest in Dart's success. WASM is extremely portable (any language could add support for it, and it's usable in the browser), it will provide a clearly-defined interop interface for high-level languages, and it's likely to be quite performant for algorithmically-intensive code. This combination positions it very well for becoming the de facto standard platform for self-contained libraries. Library authors are motivated to target WASM because it'll make their libraries easy for downstream users across all manner of languages and platforms to use, and languages will be motivated to support it because so many libraries exist. The normal bootstrapping problem a network effect like this will be smashed by having support from the two most widely-used platforms, Node.js and the browser, from the word go.

If Dart gets ahead of the curve and starts adding support for WASM now, it'll have access to (and input into) this ecosystem as it grows, making it easier and more attractive for new users to move to Dart. But if you wait until WASM support is de rigueur for any serious language, you'll have to exert the same energy playing catch-up that could have instead been a competitive advantage.

bryanrideshark commented 4 years ago

I was more wanting to use WASM inside Dart, in a mobile Flutter app. I maintain a large enterprise codebase which must be able to work in the browser; Moving to Flutter is simply not feasible, because as it stands, it would require an entire rewrite of important business logic, etc.

If I could use WASM to contain my business logic etc. then I could easily move to a Flutter app as I would need to do, is import the appropriate WASM module.

sachaarbonel commented 4 years ago

Actually @bryanrideshark as far I can tell Dart will support wasmerio for loading web assembly modules, so you don't have to be worry about that. I think it is behind an experimental flag on master.

maks commented 4 years ago

I'm guessing that @sachaarbonel you mean #37882 so this is very exciting to see support for wasm libraries coming to Dart and I assume eventually to Flutter?

sachaarbonel commented 4 years ago

Yep exactly @maks

orangeagain commented 4 years ago

you mean wasmvm on dart? i need this too

orangeagain commented 4 years ago

https://github.com/drydart/wasm.dart

mraleph commented 4 years ago

For everybody tracking this issue. We are currently evaluating how to move multiplatform Dart<->WASM interop forward. Similarly to our C FFI work we would like to collect some examples of code that people would like to use in their Dart applications via WASM. So if you have one that is open source please leave a comment here or on #37882 with a link to the source and which platforms you would like to use it on (Flutter on iOS/Android/Desktop/Web, Dart Web), please also include information if you have ever tried running this library in WASM before. Thank you.

sffc commented 4 years ago

The i18n Engineering team at Google is building a cross-platform standard i18n library, which we intend to make available via WASM (internal link http://go/omnicu, public link http://bit.ly/omnicu-charter). Dart supporting WASM would be a big boost.


Another use case separate from i18n that I'm interested in is to use 2D graphics libraries, such as Cairo. I have not done it personally, but a quick Google search brings up a few different examples of how to use Cairo on the web via WASM.

syrusakbary commented 4 years ago

That's awesome @sffc. We are working on bringing universality to the WebAssembly modules, integrating Wasm into multiple languages (we already have integrations for Python, PHP, Rust, Ruby, Elixir, C#, Go, C, C++...)

Would be great to chat and see how we can make omnicu usable from all this languages, including Dart as well!

Here's my email, in case is useful for following up syrus@wasmer.io :)

codemasterover9000 commented 4 years ago

Would be nice to use in combination with skia canvaskit so that we can have flutter support for web based designops tools like storybook.

See https://github.com/storybookjs/storybook/issues/9183

yjbanov commented 4 years ago

@codemasterover9000 FWIW you can use CanvasKit as the rendering backend in Flutter on the Web already (still experimental though). All you need to do is pass --dart-define=FLUTTER_WEB_USE_SKIA=true to flutter run or flutter build web. When you use the normal Canvas and SceneBuilder we'll use CanvasKit behind the scenes.

insinfo commented 3 years ago

@mraleph

I would like to use libraries written in "rust" compiled to wasm, directly in dart projects (AngularDart,Flutter for Web,...), like the lib "photon_rs" https://silvia-odwyer.github.io/photon/ without the need to create javascript glue files, a way for the dart compiler to create the glue would be excellent.

https://github.com/insinfo/photon-test

Something like that

lib.rs

mod utils;
use wasm_bindgen::prelude::*;
use web_sys::{HtmlCanvasElement};
//for production use: wasm-pack build --target no-modules --release
//for dev use: wasm-pack build --target no-modules --debug

// When the `wee_alloc` feature is enabled, use `wee_alloc` as the global
// allocator.
#[cfg(feature = "wee_alloc")]
#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;

#[wasm_bindgen]
extern "C" {
    fn alert(s: &str);
}

#[wasm_bindgen]
extern "C" {
    // Use `js_namespace` here to bind `console.log(..)` instead of just
    // `log(..)`
    #[wasm_bindgen(js_namespace = console)]
    fn log(s: &str);

    // The `console.log` is quite polymorphic, so we can bind it with multiple
    // signatures. Note that we need to use `js_name` to ensure we always call
    // `log` in JS.
    #[wasm_bindgen(js_namespace = console, js_name = log)]
    fn log_u32(a: u32);

    // Multiple arguments too!
    #[wasm_bindgen(js_namespace = console, js_name = log)]
    fn log_many(a: &str, b: &str);
}

macro_rules! console_log {
    // Note that this is using the `log` function imported above during
    // `bare_bones`
    ($($t:tt)*) => (log(&format_args!($($t)*).to_string()))
}

#[wasm_bindgen]
pub fn greet(canvas: HtmlCanvasElement) {
    //alert("Hello, teste!");
    console_log!("greet {:?}", canvas.width());
}

Cargo.toml

[package]
name = "teste"
version = "0.1.0"
authors = ["insinfo <insinfo2008@gmail.com>"]
edition = "2018"

[lib]
crate-type = ["cdylib", "rlib"]

[features]
default = ["console_error_panic_hook"]

[dependencies]
wasm-bindgen = "0.2.74" #0.2.63
js-sys = "0.3.51"

# The `console_error_panic_hook` crate provides better debugging of panics by
# logging them with `console.error`. This is great for development, but requires
# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for
# code size when deploying.
console_error_panic_hook = { version = "0.1.6", optional = true }

# `wee_alloc` is a tiny allocator for wasm that is only ~1K in code size
# compared to the default allocator's ~10K. It is slower than the default
# allocator, however.
#
# Unfortunately, `wee_alloc` requires nightly Rust when targeting wasm for now.
wee_alloc = { version = "0.4.5", optional = true }

[dev-dependencies]
wasm-bindgen-test = "0.3.24"
wasm-bindgen-futures = "0.4.24"

[dependencies.web-sys]
version = "0.3.51"
features = [
  "Document",
  "Element",
  "HtmlElement",
  "Node",
  "Window",
  "CanvasRenderingContext2d",
  "ImageData",
  "HtmlCanvasElement",
  "HtmlImageElement",
  "console",
  'CssStyleDeclaration',
  'EventTarget',
]

[profile.release]
# Tell `rustc` to optimize for small code size.
opt-level = "s"

index.dart


import 'dart:wasm';
import 'dart:html';

//maybe analyze this at compile time
external import * as wasm from './lib.wasm';

@wasm('greet')
external void greet(CanvasElement canvas);

is there any way to do something like this today?

tsavo-vdb-knott commented 2 years ago

@insinfo

This Gentleman is doing something interesting https://dev.to/sunshine-chain/dart-meets-rust-a-match-made-in-heaven-9f5

insinfo commented 2 years ago

@tsavo-vdb-knott although I appreciate the article written at this link, the article itself only shows dart ffi interacting with shared dynamic libraries c, it seems out of the topic covered here, dart ffi does not have support yet for libraries written in c compiled for web assembly, if if dart ffi were web assembly compatible it would be fantastic.

maks commented 2 years ago

@insinfo if all you want is to do this with Dart in Webbrowsers, then no, I don't think this is possible yet.

But if what you mean by "dart projects" also includes Dart on desktop then the dart:wasm package does allow you to use wasm modules from Dart. I believe support for Flutter is coming soon too. Perhaps you could use this as a starting point to enhance that package to work on Web too.

I also wrote up my experience of using the package to make use of a codebase written in Assemblyscript so it's definitely already usable for codebases originally written to be used within webbrowsers.

cedvdb commented 2 years ago

I'm personally interested in using a C library on mobile and web. One way I'm thinking about going about this is:

However this would be cumbersome. It would be easier if I could compile to wasm for all platforms.

fzyzcjy commented 1 year ago

By the way, https://github.com/fzyzcjy/flutter_rust_bridge already supports Flutter Web to call WASM (compiled from Rust) with safety etc.

kevmoo commented 1 year ago

Sweet @fzyzcjy !!

fzyzcjy commented 1 year ago

Thanks! @kevmoo

fzyzcjy commented 1 year ago

Oh forget to say, thanks @Desdaemon for contributing the implementation!

osa1 commented 3 months ago

We've shipped the Wasm backend as experimental with Dart 3.3 and as stable with 3.4 (it's still experimental). Closing as completed.

nex3 commented 3 months ago

Does Dart support loading WASM libraries, or only emitting them? If the latter and not the former, I don't think this should be closed yet.

osa1 commented 3 months ago

We can compile Dart to Wasm, and interact with other Wasm modules in Dart compiled to Wasm.

The latter is done via Native. AFAIK this is how Flutter interacts with SkWasm (e.g. in https://github.com/flutter/engine/tree/main/lib/web_ui/lib/src/engine/skwasm/skwasm_impl/raw).

nex3 commented 3 months ago

Good to know, thanks. It's also worth noting that in 3.4 dart compile doesn't list wasm as an option, and dart compile wasm explicitly says *NOTE*: Compilation to WasmGC is experimental. Can you clarify whether or not this is in fact considered stable, and if so whether these should be considered UI bugs in the CLI?

osa1 commented 3 months ago

Ah, my bad, I read the release notes wrong. It looks like we still advertise it as experimental. I've updated my original comment.

osa1 commented 3 months ago

The latter is done via Native. AFAIK this is how Flutter interacts with SkWasm

We discussed this a little bit today, Native works today but we will limit its usage to SDK and Flutter repo. For users that want to interact with any Wasm module we will design and implement a separate mechanism.

mkustermann commented 3 months ago

Good to know, thanks. It's also worth noting that in 3.4 dart compile doesn't list wasm as an option, and dart compile wasm explicitly says NOTE: Compilation to WasmGC is experimental. Can you clarify whether or not this is in fact considered stable, and if so whether these should be considered UI bugs in the CLI?

This has been removed in 004d6a3fad85e53c0b6f36d7b4e84993db9e1050 and the Compile Dart to Wasm bug was closed.

Cross-linking other bugs:

We could consider closing this issue in favor of the more concrete ones listed above. wdyt?

iapicca commented 3 months ago
  • Running dart2wasm-compiled code in non-JS environments: Issue 53884

We could consider closing this issue in favor of the more concrete ones listed above. wdyt?

@mkustermann I feel that the issue above + this one would easily translate in WASI support

nmfisher commented 2 months ago

The latter is done via Native. AFAIK this is how Flutter interacts with SkWasm

We discussed this a little bit today, Native works today but we will limit its usage to SDK and Flutter repo. For users that want to interact with any Wasm module we will design and implement a separate mechanism.

@osa1 seeking a bit of clarification on the direction here:

1) Does this mean that, at some point in future, it won't be possible for me to create any Dart library that uses @ffi.Native to invoke a method in some external C library on any platform? Or just that we won't be able to do so when compiling to WASM? Or is that something Flutter-specific?

2) Why is dart:ffi specifically not suitable for WASM? Is it because it's based around the 'native' memory paradigm (e.g. single shared linear memory), which doesn't accommodate WasmGC or multiple linear memory instances? Or is there some other reason?

osa1 commented 2 months ago

@nmfisher

Does this mean that, at some point in future, it won't be possible for me to create any Dart library that uses @ffi.Native to invoke a method in some external C library on any platform? Or just that we won't be able to do so when compiling to WASM? Or is that something Flutter-specific?

This is when compiling to Wasm. Other platforms/targets will work as before.

Why is dart:ffi specifically not suitable for WASM? Is it because it's based around the 'native' memory paradigm (e.g. single shared linear memory), which doesn't accommodate WasmGC or multiple linear memory instances? Or is there some other reason?

I think most (probably even all) of dart:ffi can be supported with the current Wasm + GC instructions, and I think we will need to support it at some point to allow using emscripten-generated modules with dart2wasm-generated modules. The main problem, as I see it, is that it needs more work, and we may have to do breaking changes to the currently supported (only allowed internally in Flutter) version of dart:ffi. Current version was implemented based on what we need in Flutter. It's easier to disallow it for now and release it when it's properly supported.

I think we can't properly support multiple linear memories (and we certainly don't need to support it) for dart:ffi. The API assumes there's one shared memory with all the FFI code.

(@mkustermann may have more thoughts on this)

mkustermann commented 2 months ago

@nmfisher As @osa1 alluded to

nmfisher commented 2 months ago

Thanks for the responses @osa1 and @mkustermann. I've been posting about this on Twitter too, but I figure it's better to move it here for a more fruitful discussion :)

By way of background, I was using dart:ffi, package:ffi and package:ffigen so that my Dart library could load symbols from native libraries across all platforms via ffi.Nativesee here for an example.

This works exceptionally well on desktop/mobile. On web/WASM, I can compile the native library via emscripten, generate the native<->Dart bindings with ffigen, compile the Dart library to WASM, pass the emscripten library to the dart2wasm module in JS at instantiation time, and now both Dart and emscripten libraries have access to the same address space (there's a lot of passing pointers around).

This required some hacks to get around missing dart:ffi functionality. I had previously raised the problems here, and the response was that they wouldn't be fixed because dart:ffi wouldn't be used on web going forward. That's fine - I understand that, no complaint.

However, pulling WASM support for dart:ffi before the replacement was in place is a bit frustrating. I know it wasn't 100% complete/stable, but a subset was - after all, that's what Flutter relies on. What was the downside to just keeping it publicly available? There may have been a few GitHub issues filed saying "dart:ffi isn't working on WASM", but couldn't those be adequately addressed with "dart:ffi isn't officially supported on WASM, that won't be fixed until we have a replacement"?

It also feels a bit strange to grant the Flutter engine access to something that other apps can't use (I mean, if Flutter needs dart:ffi to run on web wasm, other apps probably do too, right?). I'm fully behind the decision to create something like dart:wasm (and I know the current "official" approach now is to use JS interop), but for continuity's sake, it didn't really seem like dart:ffi was causing that many problems that it had to be nuked from the WASM target.  

mkustermann commented 2 months ago

However, pulling WASM support for dart:ffi before the replacement was in place is a bit frustrating. I know it wasn't 100% complete/stable, but a subset was - after all, that's what Flutter relies on. What was the downside to just keeping it publicly available? There may have been a few GitHub issues filed saying "dart:ffi isn't working on WASM", but couldn't those be adequately addressed with "dart:ffi isn't officially supported on WASM, that won't be fixed until we have a replacement"?

It also feels a bit strange to grant the Flutter engine access to something that other apps can't use (I mean, if Flutter needs dart:ffi to run on web wasm, other apps probably do too, right?). I'm fully behind the decision to create something like dart:wasm (and I know the current "official" approach now is to use JS interop), but for continuity's sake, it didn't really seem like dart:ffi was causing that many problems that it had to be nuked from the WASM target.

Happy to explain the multitude of reasons that led to this decision:

It's fantastic to see the community so eager to get this and build on top of it - it's very motivating for us. We'll get there, stay tuned!

nmfisher commented 2 months ago

Appreciate the response @mkustermann. I understand why dart:ffi isn't stable, and also why the Dart team wouldn't want to open the door to support something known to be unstable (even if personally I'm happy with the "not officially supported, fix it yourself" response to any issue).

Why the exception for Flutter internals though? Rightly or wrongly, many people see Dart as the "Flutter language" rather than a fantastic language/platform in its own right. Giving Flutter "trusted" access to SDK internals doesn't help dispel that view.

mkustermann commented 2 months ago

Why the exception for Flutter internals though? Rightly or wrongly, many people see Dart as the "Flutter language" rather than a fantastic language/platform in its own right. Giving Flutter "trusted" access to SDK internals doesn't help dispel that view.

From a technical point of view, we allow any dart:* library to import other internal dart:* libraries - it's not specific to flutter in that sense. Flutter happens to have two dart-colon libraries (dart:ui and dart:web_ui) which can access dart:ffi. But the flutter framework for example cannot use dart:ffi. So if a 3rd party would come along and make a flutter-like framework, adds more dart:* libraries, those would be allowed to import it as well.

From a practical standpoint: We e.g. don't have to maintain API stability. We can break the API at any point, update the corresponding flutter engine (i.e. dart:ui / dart:web_ui) code - do so within a day or two without causing widespread breakages throughout our ecosystem. The bar for public APIs is much higher.

(In hindsight we should have started out with dart:_ffi (just like we have dart:_wasm, dart:_internal, dart:_...) and graduate it to dart:ffi (or similar library) once it's ready)

nmfisher commented 2 months ago

dart:ui is a Flutter SDK package, though. dart:ffi isn't, it is a public Dart API that you can still call on any Dart (VM) platform. It sounds like the WASM implementation fell into a grey area where it was developed specifically for Flutter compatibility, published, but was never intended for public consumption (because it was intentionally incomplete). Understandable, but given it was published, and that Flutter still relies on it for WASM compilation, a graceful deprecation would have made things easier.

I don't want to take up any more of your time, though - you've acknowledged my feedback and there's probably nothing further to add. I'm sure you have many better things to spend your time on! I look forward to the next iteration of WASM interop on Dart.

jezell commented 2 months ago

Not to minimize how useful hot reload is, and not to discount other issues which are blocking ffi on web, but while better support for hot reload on web would definitely be useful, in practice it's not really working that well in web, and probably shouldn't block any dart features from shipping if it's not blocking Flutter itself from shipping web support.