The Wasm dev compiler has three major parts:
pkg/dart2wasm/lib/dev): compiles Dart to kernel to CFG IR to Wasm. Outputs wasm dev modules that each import the runtime.pkg/wasm_dev_rt): a dart2wasm-compiled app that holds shared module state.pkg/dart2wasm/bin/run_wasm_dev.js): instantiates the runtime, passes it to the app's modules, then calls main.All of the app's modules import the runtime. Apps define and look up state-bearing elements (like classes, members, and statics) via the runtime.
rt = instantiate(wasm_dev_rt); app = instantiate(app_module, {wasm_dev_rt: rt}); app.main();
Statics (top-level functions, static methods, and constructors) are represented by a fully resolved label (like package:foo/foo.dart::Foo.bar). The runtime maps labels to IDs and IDs to functions. Each call looks up its target by ID.
// package:foo/foo.dart class Foo { static int bar() => 1; } void main() => Foo.bar();
compiles to:
// Once on module init
barId = rt.staticId('package:foo/foo.dart::Foo.bar');
rt.defineStatic(barId, Foo.bar);
// main
rt.lookupStatic(barId)();
The runtime maps class labels, member names, and shapes to IDs. A shape is a member‘s positional parameter count and named parameters (sorted), like 1,a,b for foo(x, {b, a}). Each class defines its superclass and members by name and shape. Calls are dispatched by resolving a member ID and shape ID from the receiver’s class, walking its superclass hierarchy.
// package:foo/foo.dart class Greeter { String message() => 'Hello!'; void greet() => print(message()); } class WorldGreeter extends Greeter { @override String message() => 'Hello, world!'; } void main() => WorldGreeter().greet();
compiles to:
// Once on module init
// Greeter
greeterId = rt.classId('package:foo/foo.dart::Greeter');
objectId = rt.classId('dart:core::Object');
rt.defineClassSupers(greeterId, objectId);
greeterNewId = rt.staticId('package:foo/foo.dart::Greeter.new');
rt.defineStatic(greeterNewId, Greeter.new);
messageId = rt.instanceMemberId('message');
greetId = rt.instanceMemberId('greet');
shape0Id = rt.shapeId('0');
rt.startInstanceMemberDefinitions(greeterId);
rt.defineInstanceMember(greeterId, messageId, shape0Id, Greeter.message);
rt.defineInstanceMember(greeterId, greetId, shape0Id, Greeter.greet);
rt.endInstanceMemberDefinitions(greeterId);
// WorldGreeter
worldGreeterId = rt.classId('package:foo/foo.dart::WorldGreeter');
rt.defineClassSupers(worldGreeterId, greeterId);
worldGreeterNewId = rt.staticId('package:foo/foo.dart::WorldGreeter.new');
rt.defineStatic(worldGreeterNewId, WorldGreeter.new);
rt.startInstanceMemberDefinitions(worldGreeterId);
rt.defineInstanceMember(
worldGreeterId, messageId, shape0Id, WorldGreeter.message);
rt.endInstanceMemberDefinitions(worldGreeterId);
// main
o = {classId: worldGreeterId};
rt.lookupStatic(worldGreeterNewId)(o);
rt.findTarget(o.classId, greetId, shape0Id)(o);
// Greeter.greet
print(rt.findTarget(this.classId, messageId, shape0Id)(this));
Every function accepts its parameters: the receiver (for instance members and constructors), positional parameters (in order), then named parameters (sorted). Omitted optional parameters are passed a sentinel, which callees replace with their default value.
Besides the receiver, all arguments and return values are top-typed (and so boxed when necessary).
class Greeter { String greet(String name, {String? title = 'Dr.', bool loud = false}) => name; } String staticGreet(String name, {String? title = 'Dr.', bool loud = false}) => name; void greetAll(Greeter g) { g.greet('Jekyll', title: null); staticGreet('Jekyll', title: null); }
compiles to:
// Once on module init
greetShapeId = rt.shapeId('1,loud,title');
// Greeter.greet(this, name, loud, title)
final loud_ = identical(loud, sentinel) ? false : unbox(loud);
title = identical(title, sentinel) ? box('Dr.') : title;
// staticGreet(name, loud, title)
final loud_ = identical(loud, sentinel) ? false : unbox(loud);
title = identical(title, sentinel) ? box('Dr.') : title;
// greetAll
rt.findTarget(g.classId, greetId, greetShapeId)(
g, box('Jekyll'), sentinel, null);
rt.lookupStatic(staticGreetId)(box('Jekyll'), sentinel, null);