js: support WASM interoperability using `wasm_import`/`wasm_export` fn tags (#12212)
parent
2070839722
commit
a3de67de28
|
@ -405,6 +405,17 @@ fn (mut g JsGen) is_used_by_main(node ast.FnDecl) bool {
|
||||||
fn (mut g JsGen) gen_fn_decl(it ast.FnDecl) {
|
fn (mut g JsGen) gen_fn_decl(it ast.FnDecl) {
|
||||||
res := g.fn_gen_type(it)
|
res := g.fn_gen_type(it)
|
||||||
if it.language == .js {
|
if it.language == .js {
|
||||||
|
for attr in it.attrs {
|
||||||
|
match attr.name {
|
||||||
|
'wasm_import' {
|
||||||
|
mut x := g.wasm_export[attr.arg] or { []string{} }
|
||||||
|
x << it.name
|
||||||
|
g.wasm_import[attr.arg] = x
|
||||||
|
}
|
||||||
|
else {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if g.inside_builtin {
|
if g.inside_builtin {
|
||||||
|
@ -535,12 +546,39 @@ fn (mut g JsGen) gen_method_decl(it ast.FnDecl, typ FnGenType) {
|
||||||
// g.write(')')
|
// g.write(')')
|
||||||
}
|
}
|
||||||
g.writeln('')
|
g.writeln('')
|
||||||
|
|
||||||
for attr in it.attrs {
|
for attr in it.attrs {
|
||||||
match attr.name {
|
match attr.name {
|
||||||
'export' {
|
'export' {
|
||||||
g.writeln('globalThis.$attr.arg = ${g.js_name(it.name)};')
|
g.writeln('globalThis.$attr.arg = ${g.js_name(it.name)};')
|
||||||
}
|
}
|
||||||
|
'wasm_export' {
|
||||||
|
mut x := g.wasm_export[attr.arg] or { []string{} }
|
||||||
|
g.write('function \$wasm${g.js_name(it.name)}(')
|
||||||
|
g.fn_args(args, it.is_variadic)
|
||||||
|
g.writeln(') {')
|
||||||
|
g.write('\treturn $name (')
|
||||||
|
for i, arg in args {
|
||||||
|
is_varg := i == args.len - 1 && it.is_variadic
|
||||||
|
arg_name := g.js_name(arg.name)
|
||||||
|
if is_varg {
|
||||||
|
g.write('...$arg_name')
|
||||||
|
} else {
|
||||||
|
g.gen_cast_tmp(arg_name, arg.typ)
|
||||||
|
}
|
||||||
|
if i != args.len - 1 {
|
||||||
|
g.write(',')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g.writeln(').valueOf();')
|
||||||
|
g.writeln('}')
|
||||||
|
x << it.name
|
||||||
|
g.wasm_export[attr.arg] = x
|
||||||
|
}
|
||||||
|
'wasm_import' {
|
||||||
|
mut x := g.wasm_export[attr.arg] or { []string{} }
|
||||||
|
x << name
|
||||||
|
g.wasm_import[attr.arg] = x
|
||||||
|
}
|
||||||
else {}
|
else {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,6 +84,8 @@ mut:
|
||||||
defer_ifdef string
|
defer_ifdef string
|
||||||
out strings.Builder = strings.new_builder(128)
|
out strings.Builder = strings.new_builder(128)
|
||||||
array_sort_fn map[string]bool
|
array_sort_fn map[string]bool
|
||||||
|
wasm_export map[string][]string
|
||||||
|
wasm_import map[string][]string
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut g JsGen) write_tests_definitions() {
|
fn (mut g JsGen) write_tests_definitions() {
|
||||||
|
@ -211,14 +213,38 @@ pub fn gen(files []&ast.File, table &ast.Table, pref &pref.Preferences) string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
g.write('js_main();')
|
if !g.pref.is_shared {
|
||||||
|
g.write('loadRoutine().then(_ => js_main());')
|
||||||
|
}
|
||||||
g.escape_namespace()
|
g.escape_namespace()
|
||||||
// resolve imports
|
// resolve imports
|
||||||
deps_resolved := graph.resolve()
|
// deps_resolved := graph.resolve()
|
||||||
nodes := deps_resolved.nodes
|
// nodes := deps_resolved.nodes
|
||||||
|
|
||||||
mut out := g.definitions.str() + g.hashes()
|
mut out := g.definitions.str() + g.hashes()
|
||||||
|
out += '\nlet wasmExportObject;\n'
|
||||||
|
out += 'const loadRoutine = async () => {\n'
|
||||||
|
for mod, functions in g.wasm_import {
|
||||||
|
if g.pref.backend == .js_browser {
|
||||||
|
out += '\nawait fetch("$mod").then(respone => respone.arrayBuffer()).then(bytes => '
|
||||||
|
out += 'WebAssembly.instantiate(bytes,'
|
||||||
|
exports := g.wasm_export[mod]
|
||||||
|
out += '{ imports: { \n'
|
||||||
|
for i, exp in exports {
|
||||||
|
out += g.js_name(exp) + ':' + '\$wasm' + g.js_name(exp)
|
||||||
|
if i != exports.len - 1 {
|
||||||
|
out += ',\n'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out += '}})).then(obj => wasmExportObject = obj.instance.exports);\n'
|
||||||
|
for fun in functions {
|
||||||
|
out += 'globalThis.${g.js_name(fun)} = wasmExportObject.${g.js_name(fun)};\n'
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
verror('WebAssembly export is supported only for browser backend at the moment')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out += '}\n'
|
||||||
// equality check for js objects
|
// equality check for js objects
|
||||||
// TODO: Fix msvc bug that's preventing $embed_file('fast_deep_equal.js')
|
// TODO: Fix msvc bug that's preventing $embed_file('fast_deep_equal.js')
|
||||||
// unsafe {
|
// unsafe {
|
||||||
|
@ -226,12 +252,12 @@ pub fn gen(files []&ast.File, table &ast.Table, pref &pref.Preferences) string {
|
||||||
// out += eq_fn.data().vstring()
|
// out += eq_fn.data().vstring()
|
||||||
//}
|
//}
|
||||||
out += fast_deep_eq_fn
|
out += fast_deep_eq_fn
|
||||||
|
/*
|
||||||
if pref.is_shared {
|
if pref.is_shared {
|
||||||
// Export, through CommonJS, the module of the entry file if `-shared` was passed
|
// Export, through CommonJS, the module of the entry file if `-shared` was passed
|
||||||
export := nodes[nodes.len - 1].name
|
export := nodes[nodes.len - 1].name
|
||||||
out += 'if (typeof module === "object" && module.exports) module.exports = $export;\n'
|
out += 'if (typeof module === "object" && module.exports) module.exports = $export;\n'
|
||||||
}
|
}*/
|
||||||
out += '\n'
|
out += '\n'
|
||||||
|
|
||||||
out += g.out.str()
|
out += g.out.str()
|
||||||
|
@ -2982,6 +3008,40 @@ fn (mut g JsGen) gen_typeof_expr(it ast.TypeOf) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn (mut g JsGen) gen_cast_tmp(tmp string, typ_ ast.Type) {
|
||||||
|
// Skip cast if type is the same as the parrent caster
|
||||||
|
tsym := g.table.get_final_type_symbol(typ_)
|
||||||
|
if tsym.kind == .i64 || tsym.kind == .u64 {
|
||||||
|
g.write('new ')
|
||||||
|
|
||||||
|
g.write('$tsym.kind.str()')
|
||||||
|
g.write('(BigInt(')
|
||||||
|
g.write(tmp)
|
||||||
|
g.write('n))')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
g.cast_stack << typ_
|
||||||
|
typ := g.typ(typ_)
|
||||||
|
|
||||||
|
if typ_.is_ptr() {
|
||||||
|
g.write('new \$ref(')
|
||||||
|
}
|
||||||
|
|
||||||
|
g.write('new ')
|
||||||
|
g.write('${typ}(')
|
||||||
|
g.write(tmp)
|
||||||
|
if typ == 'string' {
|
||||||
|
g.write('.toString()')
|
||||||
|
}
|
||||||
|
|
||||||
|
g.write(')')
|
||||||
|
if typ_.is_ptr() {
|
||||||
|
g.write(')')
|
||||||
|
}
|
||||||
|
|
||||||
|
g.cast_stack.delete_last()
|
||||||
|
}
|
||||||
|
|
||||||
fn (mut g JsGen) gen_type_cast_expr(it ast.CastExpr) {
|
fn (mut g JsGen) gen_type_cast_expr(it ast.CastExpr) {
|
||||||
is_literal := ((it.expr is ast.IntegerLiteral && it.typ in ast.integer_type_idxs)
|
is_literal := ((it.expr is ast.IntegerLiteral && it.typ in ast.integer_type_idxs)
|
||||||
|| (it.expr is ast.FloatLiteral && it.typ in ast.float_type_idxs))
|
|| (it.expr is ast.FloatLiteral && it.typ in ast.float_type_idxs))
|
||||||
|
|
|
@ -195,6 +195,7 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
|
||||||
'direct_array_access' { is_direct_arr = true }
|
'direct_array_access' { is_direct_arr = true }
|
||||||
'keep_args_alive' { is_keep_alive = true }
|
'keep_args_alive' { is_keep_alive = true }
|
||||||
'export' { is_exported = true }
|
'export' { is_exported = true }
|
||||||
|
'wasm_export' { is_exported = true }
|
||||||
'unsafe' { is_unsafe = true }
|
'unsafe' { is_unsafe = true }
|
||||||
'trusted' { is_trusted = true }
|
'trusted' { is_trusted = true }
|
||||||
'c2v_variadic' { is_c2v_variadic = true }
|
'c2v_variadic' { is_c2v_variadic = true }
|
||||||
|
|
|
@ -1502,7 +1502,7 @@ fn (mut p Parser) attributes() {
|
||||||
for p.tok.kind != .rsbr {
|
for p.tok.kind != .rsbr {
|
||||||
start_pos := p.tok.position()
|
start_pos := p.tok.position()
|
||||||
attr := p.parse_attr()
|
attr := p.parse_attr()
|
||||||
if p.attrs.contains(attr.name) {
|
if p.attrs.contains(attr.name) && attr.name != 'wasm_export' {
|
||||||
p.error_with_pos('duplicate attribute `$attr.name`', start_pos.extend(p.prev_tok.position()))
|
p.error_with_pos('duplicate attribute `$attr.name`', start_pos.extend(p.prev_tok.position()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue