Weitere ähnliche Inhalte Ähnlich wie Rust ⇋ JavaScript (20) Mehr von Ingvar Stepanyan (7) Kürzlich hochgeladen (20) Rust ⇋ JavaScript17. Emscripten + Rust
Rust CFG/SSA
LLVM
bitcode
Native
objects
Native
executable
C/C++ CFG/SSA
LLVM
bitcode
JavaScript /
WASM
18. Emscripten + Rust
Rust CFG/SSA
LLVM
bitcode
Native
objects
Native
executable
C/C++ CFG/SSA
LLVM
bitcode
JavaScript /
WASM
28. Exporting functions
#[no_mangle]
pub extern fn add_integers(x: i32, y: i32) -> i32 {
return x + y;
}
extern {
fn emscripten_exit_with_live_runtime();
}
fn main() {
unsafe {
emscripten_exit_with_live_runtime();
}
}
29. $ rustc --target=asmjs-unknown-emscripten hello.rs
$ node
> require('./hello')
exit(0) implicitly called by end of main(), but
noExitRuntime, so not exiting the runtime (you can
use emscripten_force_exit, if you want to force a
true shutdown)
[Emscripten Module object]
> require('./hello')._add_integers(10, 20)
30
30. Exporting functions
$ emcc ... "hello.0.o" "-o" "hello.js" "-s"
"EXPORTED_FUNCTIONS=["_add_integers","_main",
"_rust_eh_personality"]" ...
31. Exporting functions
var real__add_integers = asm["_add_integers"];
asm["_add_integers"] = function() {
assert(runtimeInitialized, 'you need to wait for the runtime
to be ready (e.g. wait for main() to be called)');
assert(!runtimeExited, 'the runtime was exited (use
NO_EXIT_RUNTIME to keep it alive after main() exits)');
return real__add_integers.apply(null, arguments);
};
32. Generated code (asm.js)
function _add_integers($0,$1) {
$0 = $0|0;
$1 = $1|0;
var $$arith = 0, $$ispos = 0, $$negcheck = 0, $$negtemp = 0, $$poscheck = 0, $
$postemp = 0, $$select = 0, $2 = 0, label = 0, sp = 0;
sp = STACKTOP;
$$arith = (($0) + ($1))|0;
$$postemp = (($0) + -2147483648)|0;
$$negtemp = (($0) + 2147483647)|0;
$$poscheck = ($$arith|0)<($$postemp|0);
$$negcheck = ($$arith|0)>($$negtemp|0);
$$ispos = ($0|0)>=(0);
$$select = $$ispos ? $$poscheck : $$negcheck;
$2 = $$select;
if ($2) {
__ZN4core9panicking5panic17h0c8c35aaab94c092E(2160);
// unreachable;
} else {
return ($$arith|0);
}
return (0)|0;
}
35. asm.js <-> wasm
x+y|0
(i32.add (get_local $x)
(get_local $y))
+(x+y)
(i32.add (get_local $x)
(get_local $y))
f()|0 (call $f)
HEAP32[ptr>>2]|0
(i32.load (get_local
$ptr))
36. Memory model
var HEAP8 = new global.Int8Array(buffer);
var HEAP16 = new global.Int16Array(buffer);
var HEAP32 = new global.Int32Array(buffer);
var HEAPU8 = new global.Uint8Array(buffer);
var HEAPU16 = new global.Uint16Array(buffer);
var HEAPU32 = new global.Uint32Array(buffer);
var HEAPF32 = new global.Float32Array(buffer);
var HEAPF64 = new global.Float64Array(buffer);
59. > module = require('./hello')
[Emscripten Module object]
> myObj = new module.MyClass(10)
MyClass {}
> myObj.x
10
62. EMSCRIPTEN_ALWAYS_INLINE explicit class_(const char* name) {
using namespace internal;
BaseSpecifier::template verify<ClassType>();
auto _getActualType = &getActualType<ClassType>;
auto upcast = BaseSpecifier::template getUpcaster<ClassType>();
auto downcast = BaseSpecifier::template getDowncaster<ClassType>();
auto destructor = &raw_destructor<ClassType>;
_embind_register_class(
TypeID<ClassType>::get(),
TypeID<AllowedRawPointer<ClassType>>::get(),
TypeID<AllowedRawPointer<const ClassType>>::get(),
BaseSpecifier::get(),
getSignature(_getActualType),
reinterpret_cast<GenericFunction>(_getActualType),
getSignature(upcast),
reinterpret_cast<GenericFunction>(upcast),
getSignature(downcast),
reinterpret_cast<GenericFunction>(downcast),
name,
getSignature(destructor),
reinterpret_cast<GenericFunction>(destructor));
}
63. EMSCRIPTEN_ALWAYS_INLINE explicit class_(const char* name) {
using namespace internal;
BaseSpecifier::template verify<ClassType>();
auto _getActualType = &getActualType<ClassType>;
auto upcast = BaseSpecifier::template getUpcaster<ClassType>();
auto downcast = BaseSpecifier::template getDowncaster<ClassType>();
auto destructor = &raw_destructor<ClassType>;
_embind_register_class(
TypeID<ClassType>::get(),
TypeID<AllowedRawPointer<ClassType>>::get(),
TypeID<AllowedRawPointer<const ClassType>>::get(),
BaseSpecifier::get(),
getSignature(_getActualType),
reinterpret_cast<GenericFunction>(_getActualType),
getSignature(upcast),
reinterpret_cast<GenericFunction>(upcast),
getSignature(downcast),
reinterpret_cast<GenericFunction>(downcast),
name,
getSignature(destructor),
reinterpret_cast<GenericFunction>(destructor));
}
65. // Implemented in JavaScript. Don't call these directly.
extern "C" {
...
void _embind_register_class(
...
);
...
}
66. // Implemented in JavaScript. Don't call these directly.
extern "C" {
...
void _embind_register_class(
TYPEID classType,
TYPEID pointerType,
TYPEID constPointerType,
TYPEID baseClassType,
const char* getActualTypeSignature,
GenericFunction getActualType,
const char* upcastSignature,
GenericFunction upcast,
const char* downcastSignature,
GenericFunction downcast,
const char* className,
const char* destructorSignature,
GenericFunction destructor);
...
}
69. // Implemented in JavaScript. Don't call these directly.
extern "C" {
...
void _embind_register_class(
TYPEID classType,
TYPEID pointerType,
TYPEID constPointerType,
TYPEID baseClassType,
const char* getActualTypeSignature,
GenericFunction getActualType,
const char* upcastSignature,
GenericFunction upcast,
const char* downcastSignature,
GenericFunction downcast,
const char* className,
const char* destructorSignature,
GenericFunction destructor);
...
}
71. // Implemented in JavaScript. Don't call these directly.
extern "C" {
...
void _embind_register_class(
TYPEID classType,
TYPEID pointerType,
TYPEID constPointerType,
TYPEID baseClassType,
const char* getActualTypeSignature,
GenericFunction getActualType,
const char* upcastSignature,
GenericFunction upcast,
const char* downcastSignature,
GenericFunction downcast,
const char* className,
const char* destructorSignature,
GenericFunction destructor);
...
}
74. // Implemented in JavaScript. Don't call these directly.
extern "C" {
...
void _embind_register_class(
TYPEID classType,
TYPEID pointerType,
TYPEID constPointerType,
TYPEID baseClassType,
const char* getActualTypeSignature,
GenericFunction getActualType,
const char* upcastSignature,
GenericFunction upcast,
const char* downcastSignature,
GenericFunction downcast,
const char* className,
const char* destructorSignature,
GenericFunction destructor);
...
}
75. // Implemented in JavaScript. Don't call these directly.
extern "C" {
...
void _embind_register_class(
TYPEID classType,
TYPEID pointerType,
TYPEID constPointerType,
TYPEID baseClassType,
const char* getActualTypeSignature,
GenericFunction getActualType,
const char* upcastSignature,
GenericFunction upcast,
const char* downcastSignature,
GenericFunction downcast,
const char* className,
const char* destructorSignature,
GenericFunction destructor);
...
}
78. // Implemented in JavaScript. Don't call these directly.
extern "C" {
...
void _embind_register_class(
TYPEID classType,
TYPEID pointerType,
TYPEID constPointerType,
TYPEID baseClassType,
const char* getActualTypeSignature,
GenericFunction getActualType,
const char* upcastSignature,
GenericFunction upcast,
const char* downcastSignature,
GenericFunction downcast,
const char* className,
const char* destructorSignature,
GenericFunction destructor);
...
}
80. // Implemented in JavaScript. Don't call these directly.
extern "C" {
...
void _embind_register_class(
TYPEID classType,
TYPEID pointerType,
TYPEID constPointerType,
TYPEID baseClassType,
const char* getActualTypeSignature,
GenericFunction getActualType,
const char* upcastSignature,
GenericFunction upcast,
const char* downcastSignature,
GenericFunction downcast,
const char* className,
const char* destructorSignature,
GenericFunction destructor);
...
}
81. unsafe {
_embind_register_class(
type_id::<T>(),
type_id::<*mut T>(),
type_id::<*const T>(),
null(),
b"ii0".as_ptr(),
get_actual_type::<T>,
b"v0".as_ptr(),
noop,
b"v0".as_ptr(),
noop,
CString::new(type_name::<T>()).unwrap().as_ptr(),
b"vi0".as_ptr(),
destructure::<T>
)
}
82. unsafe {
_embind_register_class(
type_id::<T>(),
type_id::<*mut T>(),
type_id::<*const T>(),
null(),
cstr!("ii"),
get_actual_type::<T>,
cstr!("v"),
noop,
cstr!("v"),
noop,
CString::new(type_name::<T>()).unwrap().as_ptr(),
cstr!("vi"),
destructure::<T>
)
}
87. unsafe {
let arg_types = [type_id::<*mut T>()];
_embind_register_class_constructor(
type_id::<T>(),
arg_types.len(),
arg_types.as_ptr(),
cstr!("ii"),
invoker,
::std::mem::transmute(
Box::<T>::default as fn () -> Box<_>
)
)
}
94. #[link_args = "--js-library rustlib.js"]
extern {
fn _embind_register_rust_string(type_id: u32);
}
...
_embind_register_rust_string(type_id::<&str>());
103. let s = S {
x: "hello, world",
y: 42,
z: [123.456, 789.]
};
let val = s.serialize(Serializer).unwrap();
107. Useful links
• https://github.com/RReverser/asmjs-experiments - safe
bindings for Embind
• https://kripken.github.io/emscripten-site/docs/
api_reference/emscripten.h.html - Emscripten APIs
• https://github.com/rust-lang/rust/pull/41409 - allow
linking JS libraries as normal libs
• https://github.com/rust-lang/cargo/pull/3954 - let Cargo
run binaries, tests, benchmarks on Emscripten target
via Node.js