1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

js: support WASM interoperability using wasm_import/wasm_export fn tags (#12212)

This commit is contained in:
playX 2021-10-18 10:56:21 +03:00 committed by GitHub
parent 2070839722
commit a3de67de28
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 107 additions and 8 deletions

View File

@ -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) {
res := g.fn_gen_type(it)
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
}
if g.inside_builtin {
@ -535,12 +546,39 @@ fn (mut g JsGen) gen_method_decl(it ast.FnDecl, typ FnGenType) {
// g.write(')')
}
g.writeln('')
for attr in it.attrs {
match attr.name {
'export' {
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 {}
}
}

View File

@ -84,6 +84,8 @@ mut:
defer_ifdef string
out strings.Builder = strings.new_builder(128)
array_sort_fn map[string]bool
wasm_export map[string][]string
wasm_import map[string][]string
}
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()
// resolve imports
deps_resolved := graph.resolve()
nodes := deps_resolved.nodes
// deps_resolved := graph.resolve()
// nodes := deps_resolved.nodes
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
// TODO: Fix msvc bug that's preventing $embed_file('fast_deep_equal.js')
// unsafe {
@ -226,12 +252,12 @@ pub fn gen(files []&ast.File, table &ast.Table, pref &pref.Preferences) string {
// out += eq_fn.data().vstring()
//}
out += fast_deep_eq_fn
/*
if pref.is_shared {
// Export, through CommonJS, the module of the entry file if `-shared` was passed
export := nodes[nodes.len - 1].name
out += 'if (typeof module === "object" && module.exports) module.exports = $export;\n'
}
}*/
out += '\n'
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) {
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))

View File

@ -195,6 +195,7 @@ fn (mut p Parser) fn_decl() ast.FnDecl {
'direct_array_access' { is_direct_arr = true }
'keep_args_alive' { is_keep_alive = true }
'export' { is_exported = true }
'wasm_export' { is_exported = true }
'unsafe' { is_unsafe = true }
'trusted' { is_trusted = true }
'c2v_variadic' { is_c2v_variadic = true }

View File

@ -1502,7 +1502,7 @@ fn (mut p Parser) attributes() {
for p.tok.kind != .rsbr {
start_pos := p.tok.position()
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()))
return
}