wasm: add support for js.FuncOf
Этот коммит содержится в:
родитель
3313decb68
коммит
eb1d834dd4
9 изменённых файлов: 129 добавлений и 27 удалений
|
@ -389,7 +389,20 @@ func (c *Compiler) markAsyncFunctions() (needsScheduler bool, err error) {
|
|||
|
||||
c.builder.SetInsertPointBefore(inst)
|
||||
|
||||
parentHandle := f.LastParam()
|
||||
var parentHandle llvm.Value
|
||||
if f.Linkage() == llvm.ExternalLinkage {
|
||||
// Exported function.
|
||||
// Note that getTaskPromisePtr will panic if it is called with
|
||||
// a nil pointer, so blocking exported functions that try to
|
||||
// return anything will not work.
|
||||
parentHandle = llvm.ConstPointerNull(c.i8ptrType)
|
||||
} else {
|
||||
parentHandle = f.LastParam()
|
||||
if parentHandle.IsNil() || parentHandle.Name() != "parentHandle" {
|
||||
// sanity check
|
||||
panic("trying to make exported function async")
|
||||
}
|
||||
}
|
||||
|
||||
// Store return values.
|
||||
switch inst.OperandsCount() {
|
||||
|
@ -417,7 +430,7 @@ func (c *Compiler) markAsyncFunctions() (needsScheduler bool, err error) {
|
|||
// behavior somehow (with the unreachable instruction).
|
||||
continuePoint := c.builder.CreateCall(coroSuspendFunc, []llvm.Value{
|
||||
llvm.ConstNull(c.ctx.TokenType()),
|
||||
llvm.ConstInt(c.ctx.Int1Type(), 1, false),
|
||||
llvm.ConstInt(c.ctx.Int1Type(), 0, false),
|
||||
}, "ret")
|
||||
sw := c.builder.CreateSwitch(continuePoint, frame.suspendBlock, 2)
|
||||
sw.AddCase(llvm.ConstInt(c.ctx.Int8Type(), 0, false), frame.unreachableBlock)
|
||||
|
@ -488,7 +501,7 @@ func (c *Compiler) markAsyncFunctions() (needsScheduler bool, err error) {
|
|||
c.builder.SetInsertPointBefore(deadlockCall)
|
||||
continuePoint := c.builder.CreateCall(coroSuspendFunc, []llvm.Value{
|
||||
llvm.ConstNull(c.ctx.TokenType()),
|
||||
llvm.ConstInt(c.ctx.Int1Type(), 1, false), // final suspend
|
||||
llvm.ConstInt(c.ctx.Int1Type(), 0, false),
|
||||
}, "")
|
||||
c.splitBasicBlock(deadlockCall, llvm.NextBasicBlock(c.builder.GetInsertBlock()), "task.wakeup.dead")
|
||||
c.builder.SetInsertPointBefore(deadlockCall)
|
||||
|
|
|
@ -3,6 +3,11 @@ export: clean wasm_exec
|
|||
cp ./export/wasm.js ./html/
|
||||
cp ./export/index.html ./html/
|
||||
|
||||
callback: clean wasm_exec
|
||||
tinygo build -o ./html/wasm.wasm -target wasm ./callback/wasm.go
|
||||
cp ./callback/wasm.js ./html/
|
||||
cp ./callback/index.html ./html/
|
||||
|
||||
main: clean wasm_exec
|
||||
tinygo build -o ./html/wasm.wasm -target wasm -no-debug ./main/main.go
|
||||
cp ./main/index.html ./html/
|
||||
|
|
19
src/examples/wasm/callback/index.html
Обычный файл
19
src/examples/wasm/callback/index.html
Обычный файл
|
@ -0,0 +1,19 @@
|
|||
<!DOCTYPE html>
|
||||
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Go WebAssembly</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<script src="wasm_exec.js" defer></script>
|
||||
<script src="wasm.js" defer></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>WebAssembly</h1>
|
||||
<p>Add two numbers, using WebAssembly:</p>
|
||||
<input type="number" id="a" value="0" /> + <input type="number" id="b" value="0" /> = <input type="number" id="result" readonly />
|
||||
</body>
|
||||
|
||||
</html>
|
27
src/examples/wasm/callback/wasm.go
Обычный файл
27
src/examples/wasm/callback/wasm.go
Обычный файл
|
@ -0,0 +1,27 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"syscall/js"
|
||||
)
|
||||
|
||||
var a, b int
|
||||
|
||||
func main() {
|
||||
document := js.Global().Get("document")
|
||||
document.Call("getElementById", "a").Set("oninput", updater(&a))
|
||||
document.Call("getElementById", "b").Set("oninput", updater(&b))
|
||||
update()
|
||||
}
|
||||
|
||||
func updater(n *int) js.Func {
|
||||
return js.FuncOf(func(this js.Value, args []js.Value) interface{} {
|
||||
*n, _ = strconv.Atoi(this.Get("value").String())
|
||||
update()
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func update() {
|
||||
js.Global().Get("document").Call("getElementById", "result").Set("value", a+b)
|
||||
}
|
26
src/examples/wasm/callback/wasm.js
Обычный файл
26
src/examples/wasm/callback/wasm.js
Обычный файл
|
@ -0,0 +1,26 @@
|
|||
'use strict';
|
||||
|
||||
const WASM_URL = 'wasm.wasm';
|
||||
|
||||
var wasm;
|
||||
|
||||
function init() {
|
||||
const go = new Go();
|
||||
if ('instantiateStreaming' in WebAssembly) {
|
||||
WebAssembly.instantiateStreaming(fetch(WASM_URL), go.importObject).then(function (obj) {
|
||||
wasm = obj.instance;
|
||||
go.run(wasm);
|
||||
})
|
||||
} else {
|
||||
fetch(WASM_URL).then(resp =>
|
||||
resp.arrayBuffer()
|
||||
).then(bytes =>
|
||||
WebAssembly.instantiate(bytes, go.importObject).then(function (obj) {
|
||||
wasm = obj.instance;
|
||||
go.run(wasm);
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
init();
|
|
@ -49,3 +49,7 @@ func lookupPanic() {
|
|||
func slicePanic() {
|
||||
runtimePanic("slice out of range")
|
||||
}
|
||||
|
||||
func blockingPanic() {
|
||||
runtimePanic("trying to do blocking operation in exported function")
|
||||
}
|
||||
|
|
|
@ -37,9 +37,16 @@ func putchar(c byte) {
|
|||
resource_write(stdout, &c, 1)
|
||||
}
|
||||
|
||||
var handleEvent func()
|
||||
|
||||
//go:linkname setEventHandler syscall/js.setEventHandler
|
||||
func setEventHandler(fn func()) {
|
||||
// TODO
|
||||
handleEvent = fn
|
||||
}
|
||||
|
||||
//go:export resume
|
||||
func resume() {
|
||||
handleEvent()
|
||||
}
|
||||
|
||||
//go:export go_scheduler
|
||||
|
|
|
@ -120,6 +120,9 @@ func setTaskPromisePtr(task *coroutine, value unsafe.Pointer) {
|
|||
// getTaskPromisePtr is a helper function to get the current .ptr field from a
|
||||
// coroutine promise.
|
||||
func getTaskPromisePtr(task *coroutine) unsafe.Pointer {
|
||||
if task == nil {
|
||||
blockingPanic()
|
||||
}
|
||||
return task.promise().ptr
|
||||
}
|
||||
|
||||
|
|
|
@ -240,9 +240,9 @@
|
|||
},
|
||||
|
||||
// func valueIndex(v ref, i int) ref
|
||||
//"syscall/js.valueIndex": (sp) => {
|
||||
// storeValue(sp + 24, Reflect.get(loadValue(sp + 8), getInt64(sp + 16)));
|
||||
//},
|
||||
"syscall/js.valueIndex": (ret_addr, v_addr, i) => {
|
||||
storeValue(ret_addr, Reflect.get(loadValue(v_addr), i));
|
||||
},
|
||||
|
||||
// valueSetIndex(v ref, i int, x ref)
|
||||
//"syscall/js.valueSetIndex": (sp) => {
|
||||
|
@ -291,9 +291,9 @@
|
|||
},
|
||||
|
||||
// func valueLength(v ref) int
|
||||
//"syscall/js.valueLength": (sp) => {
|
||||
// setInt64(sp + 16, parseInt(loadValue(sp + 8).length));
|
||||
//},
|
||||
"syscall/js.valueLength": (v_addr) => {
|
||||
return loadValue(v_addr).length;
|
||||
},
|
||||
|
||||
// valuePrepareString(v ref) (ref, int)
|
||||
"syscall/js.valuePrepareString": (ret_addr, v_addr) => {
|
||||
|
@ -352,25 +352,23 @@
|
|||
}
|
||||
}
|
||||
|
||||
static _makeCallbackHelper(id, pendingCallbacks, go) {
|
||||
return function () {
|
||||
pendingCallbacks.push({ id: id, args: arguments });
|
||||
go._resolveCallbackPromise();
|
||||
};
|
||||
_resume() {
|
||||
if (this.exited) {
|
||||
throw new Error("Go program has already exited");
|
||||
}
|
||||
this._inst.exports.resume();
|
||||
if (this.exited) {
|
||||
this._resolveExitPromise();
|
||||
}
|
||||
}
|
||||
|
||||
static _makeEventCallbackHelper(preventDefault, stopPropagation, stopImmediatePropagation, fn) {
|
||||
return function (event) {
|
||||
if (preventDefault) {
|
||||
event.preventDefault();
|
||||
}
|
||||
if (stopPropagation) {
|
||||
event.stopPropagation();
|
||||
}
|
||||
if (stopImmediatePropagation) {
|
||||
event.stopImmediatePropagation();
|
||||
}
|
||||
fn(event);
|
||||
_makeFuncWrapper(id) {
|
||||
const go = this;
|
||||
return function () {
|
||||
const event = { id: id, this: this, args: arguments };
|
||||
go._pendingEvent = event;
|
||||
go._resume();
|
||||
return event.result;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче