Skip to content

Commit eb1d834

Browse files
committed
wasm: add support for js.FuncOf
1 parent 3313dec commit eb1d834

File tree

9 files changed

+129
-27
lines changed

9 files changed

+129
-27
lines changed

compiler/goroutine-lowering.go

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -389,7 +389,20 @@ func (c *Compiler) markAsyncFunctions() (needsScheduler bool, err error) {
389389

390390
c.builder.SetInsertPointBefore(inst)
391391

392-
parentHandle := f.LastParam()
392+
var parentHandle llvm.Value
393+
if f.Linkage() == llvm.ExternalLinkage {
394+
// Exported function.
395+
// Note that getTaskPromisePtr will panic if it is called with
396+
// a nil pointer, so blocking exported functions that try to
397+
// return anything will not work.
398+
parentHandle = llvm.ConstPointerNull(c.i8ptrType)
399+
} else {
400+
parentHandle = f.LastParam()
401+
if parentHandle.IsNil() || parentHandle.Name() != "parentHandle" {
402+
// sanity check
403+
panic("trying to make exported function async")
404+
}
405+
}
393406

394407
// Store return values.
395408
switch inst.OperandsCount() {
@@ -417,7 +430,7 @@ func (c *Compiler) markAsyncFunctions() (needsScheduler bool, err error) {
417430
// behavior somehow (with the unreachable instruction).
418431
continuePoint := c.builder.CreateCall(coroSuspendFunc, []llvm.Value{
419432
llvm.ConstNull(c.ctx.TokenType()),
420-
llvm.ConstInt(c.ctx.Int1Type(), 1, false),
433+
llvm.ConstInt(c.ctx.Int1Type(), 0, false),
421434
}, "ret")
422435
sw := c.builder.CreateSwitch(continuePoint, frame.suspendBlock, 2)
423436
sw.AddCase(llvm.ConstInt(c.ctx.Int8Type(), 0, false), frame.unreachableBlock)
@@ -488,7 +501,7 @@ func (c *Compiler) markAsyncFunctions() (needsScheduler bool, err error) {
488501
c.builder.SetInsertPointBefore(deadlockCall)
489502
continuePoint := c.builder.CreateCall(coroSuspendFunc, []llvm.Value{
490503
llvm.ConstNull(c.ctx.TokenType()),
491-
llvm.ConstInt(c.ctx.Int1Type(), 1, false), // final suspend
504+
llvm.ConstInt(c.ctx.Int1Type(), 0, false),
492505
}, "")
493506
c.splitBasicBlock(deadlockCall, llvm.NextBasicBlock(c.builder.GetInsertBlock()), "task.wakeup.dead")
494507
c.builder.SetInsertPointBefore(deadlockCall)

src/examples/wasm/Makefile

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@ export: clean wasm_exec
33
cp ./export/wasm.js ./html/
44
cp ./export/index.html ./html/
55

6+
callback: clean wasm_exec
7+
tinygo build -o ./html/wasm.wasm -target wasm ./callback/wasm.go
8+
cp ./callback/wasm.js ./html/
9+
cp ./callback/index.html ./html/
10+
611
main: clean wasm_exec
712
tinygo build -o ./html/wasm.wasm -target wasm -no-debug ./main/main.go
813
cp ./main/index.html ./html/
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<!DOCTYPE html>
2+
3+
<html>
4+
5+
<head>
6+
<meta charset="utf-8" />
7+
<title>Go WebAssembly</title>
8+
<meta name="viewport" content="width=device-width, initial-scale=1" />
9+
<script src="wasm_exec.js" defer></script>
10+
<script src="wasm.js" defer></script>
11+
</head>
12+
13+
<body>
14+
<h1>WebAssembly</h1>
15+
<p>Add two numbers, using WebAssembly:</p>
16+
<input type="number" id="a" value="0" /> + <input type="number" id="b" value="0" /> = <input type="number" id="result" readonly />
17+
</body>
18+
19+
</html>

src/examples/wasm/callback/wasm.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package main
2+
3+
import (
4+
"strconv"
5+
"syscall/js"
6+
)
7+
8+
var a, b int
9+
10+
func main() {
11+
document := js.Global().Get("document")
12+
document.Call("getElementById", "a").Set("oninput", updater(&a))
13+
document.Call("getElementById", "b").Set("oninput", updater(&b))
14+
update()
15+
}
16+
17+
func updater(n *int) js.Func {
18+
return js.FuncOf(func(this js.Value, args []js.Value) interface{} {
19+
*n, _ = strconv.Atoi(this.Get("value").String())
20+
update()
21+
return nil
22+
})
23+
}
24+
25+
func update() {
26+
js.Global().Get("document").Call("getElementById", "result").Set("value", a+b)
27+
}

src/examples/wasm/callback/wasm.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
'use strict';
2+
3+
const WASM_URL = 'wasm.wasm';
4+
5+
var wasm;
6+
7+
function init() {
8+
const go = new Go();
9+
if ('instantiateStreaming' in WebAssembly) {
10+
WebAssembly.instantiateStreaming(fetch(WASM_URL), go.importObject).then(function (obj) {
11+
wasm = obj.instance;
12+
go.run(wasm);
13+
})
14+
} else {
15+
fetch(WASM_URL).then(resp =>
16+
resp.arrayBuffer()
17+
).then(bytes =>
18+
WebAssembly.instantiate(bytes, go.importObject).then(function (obj) {
19+
wasm = obj.instance;
20+
go.run(wasm);
21+
})
22+
)
23+
}
24+
}
25+
26+
init();

src/runtime/panic.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,7 @@ func lookupPanic() {
4949
func slicePanic() {
5050
runtimePanic("slice out of range")
5151
}
52+
53+
func blockingPanic() {
54+
runtimePanic("trying to do blocking operation in exported function")
55+
}

src/runtime/runtime_wasm.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,16 @@ func putchar(c byte) {
3737
resource_write(stdout, &c, 1)
3838
}
3939

40+
var handleEvent func()
41+
4042
//go:linkname setEventHandler syscall/js.setEventHandler
4143
func setEventHandler(fn func()) {
42-
// TODO
44+
handleEvent = fn
45+
}
46+
47+
//go:export resume
48+
func resume() {
49+
handleEvent()
4350
}
4451

4552
//go:export go_scheduler

src/runtime/scheduler.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,9 @@ func setTaskPromisePtr(task *coroutine, value unsafe.Pointer) {
120120
// getTaskPromisePtr is a helper function to get the current .ptr field from a
121121
// coroutine promise.
122122
func getTaskPromisePtr(task *coroutine) unsafe.Pointer {
123+
if task == nil {
124+
blockingPanic()
125+
}
123126
return task.promise().ptr
124127
}
125128

targets/wasm_exec.js

Lines changed: 21 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -240,9 +240,9 @@
240240
},
241241

242242
// func valueIndex(v ref, i int) ref
243-
//"syscall/js.valueIndex": (sp) => {
244-
// storeValue(sp + 24, Reflect.get(loadValue(sp + 8), getInt64(sp + 16)));
245-
//},
243+
"syscall/js.valueIndex": (ret_addr, v_addr, i) => {
244+
storeValue(ret_addr, Reflect.get(loadValue(v_addr), i));
245+
},
246246

247247
// valueSetIndex(v ref, i int, x ref)
248248
//"syscall/js.valueSetIndex": (sp) => {
@@ -291,9 +291,9 @@
291291
},
292292

293293
// func valueLength(v ref) int
294-
//"syscall/js.valueLength": (sp) => {
295-
// setInt64(sp + 16, parseInt(loadValue(sp + 8).length));
296-
//},
294+
"syscall/js.valueLength": (v_addr) => {
295+
return loadValue(v_addr).length;
296+
},
297297

298298
// valuePrepareString(v ref) (ref, int)
299299
"syscall/js.valuePrepareString": (ret_addr, v_addr) => {
@@ -352,25 +352,23 @@
352352
}
353353
}
354354

355-
static _makeCallbackHelper(id, pendingCallbacks, go) {
356-
return function () {
357-
pendingCallbacks.push({ id: id, args: arguments });
358-
go._resolveCallbackPromise();
359-
};
355+
_resume() {
356+
if (this.exited) {
357+
throw new Error("Go program has already exited");
358+
}
359+
this._inst.exports.resume();
360+
if (this.exited) {
361+
this._resolveExitPromise();
362+
}
360363
}
361364

362-
static _makeEventCallbackHelper(preventDefault, stopPropagation, stopImmediatePropagation, fn) {
363-
return function (event) {
364-
if (preventDefault) {
365-
event.preventDefault();
366-
}
367-
if (stopPropagation) {
368-
event.stopPropagation();
369-
}
370-
if (stopImmediatePropagation) {
371-
event.stopImmediatePropagation();
372-
}
373-
fn(event);
365+
_makeFuncWrapper(id) {
366+
const go = this;
367+
return function () {
368+
const event = { id: id, this: this, args: arguments };
369+
go._pendingEvent = event;
370+
go._resume();
371+
return event.result;
374372
};
375373
}
376374
}

0 commit comments

Comments
 (0)