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)
|
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.
|
// Store return values.
|
||||||
switch inst.OperandsCount() {
|
switch inst.OperandsCount() {
|
||||||
|
@ -417,7 +430,7 @@ func (c *Compiler) markAsyncFunctions() (needsScheduler bool, err error) {
|
||||||
// behavior somehow (with the unreachable instruction).
|
// behavior somehow (with the unreachable instruction).
|
||||||
continuePoint := c.builder.CreateCall(coroSuspendFunc, []llvm.Value{
|
continuePoint := c.builder.CreateCall(coroSuspendFunc, []llvm.Value{
|
||||||
llvm.ConstNull(c.ctx.TokenType()),
|
llvm.ConstNull(c.ctx.TokenType()),
|
||||||
llvm.ConstInt(c.ctx.Int1Type(), 1, false),
|
llvm.ConstInt(c.ctx.Int1Type(), 0, false),
|
||||||
}, "ret")
|
}, "ret")
|
||||||
sw := c.builder.CreateSwitch(continuePoint, frame.suspendBlock, 2)
|
sw := c.builder.CreateSwitch(continuePoint, frame.suspendBlock, 2)
|
||||||
sw.AddCase(llvm.ConstInt(c.ctx.Int8Type(), 0, false), frame.unreachableBlock)
|
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)
|
c.builder.SetInsertPointBefore(deadlockCall)
|
||||||
continuePoint := c.builder.CreateCall(coroSuspendFunc, []llvm.Value{
|
continuePoint := c.builder.CreateCall(coroSuspendFunc, []llvm.Value{
|
||||||
llvm.ConstNull(c.ctx.TokenType()),
|
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.splitBasicBlock(deadlockCall, llvm.NextBasicBlock(c.builder.GetInsertBlock()), "task.wakeup.dead")
|
||||||
c.builder.SetInsertPointBefore(deadlockCall)
|
c.builder.SetInsertPointBefore(deadlockCall)
|
||||||
|
|
|
@ -3,6 +3,11 @@ export: clean wasm_exec
|
||||||
cp ./export/wasm.js ./html/
|
cp ./export/wasm.js ./html/
|
||||||
cp ./export/index.html ./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
|
main: clean wasm_exec
|
||||||
tinygo build -o ./html/wasm.wasm -target wasm -no-debug ./main/main.go
|
tinygo build -o ./html/wasm.wasm -target wasm -no-debug ./main/main.go
|
||||||
cp ./main/index.html ./html/
|
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() {
|
func slicePanic() {
|
||||||
runtimePanic("slice out of range")
|
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)
|
resource_write(stdout, &c, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var handleEvent func()
|
||||||
|
|
||||||
//go:linkname setEventHandler syscall/js.setEventHandler
|
//go:linkname setEventHandler syscall/js.setEventHandler
|
||||||
func setEventHandler(fn func()) {
|
func setEventHandler(fn func()) {
|
||||||
// TODO
|
handleEvent = fn
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:export resume
|
||||||
|
func resume() {
|
||||||
|
handleEvent()
|
||||||
}
|
}
|
||||||
|
|
||||||
//go:export go_scheduler
|
//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
|
// getTaskPromisePtr is a helper function to get the current .ptr field from a
|
||||||
// coroutine promise.
|
// coroutine promise.
|
||||||
func getTaskPromisePtr(task *coroutine) unsafe.Pointer {
|
func getTaskPromisePtr(task *coroutine) unsafe.Pointer {
|
||||||
|
if task == nil {
|
||||||
|
blockingPanic()
|
||||||
|
}
|
||||||
return task.promise().ptr
|
return task.promise().ptr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -240,9 +240,9 @@
|
||||||
},
|
},
|
||||||
|
|
||||||
// func valueIndex(v ref, i int) ref
|
// func valueIndex(v ref, i int) ref
|
||||||
//"syscall/js.valueIndex": (sp) => {
|
"syscall/js.valueIndex": (ret_addr, v_addr, i) => {
|
||||||
// storeValue(sp + 24, Reflect.get(loadValue(sp + 8), getInt64(sp + 16)));
|
storeValue(ret_addr, Reflect.get(loadValue(v_addr), i));
|
||||||
//},
|
},
|
||||||
|
|
||||||
// valueSetIndex(v ref, i int, x ref)
|
// valueSetIndex(v ref, i int, x ref)
|
||||||
//"syscall/js.valueSetIndex": (sp) => {
|
//"syscall/js.valueSetIndex": (sp) => {
|
||||||
|
@ -291,9 +291,9 @@
|
||||||
},
|
},
|
||||||
|
|
||||||
// func valueLength(v ref) int
|
// func valueLength(v ref) int
|
||||||
//"syscall/js.valueLength": (sp) => {
|
"syscall/js.valueLength": (v_addr) => {
|
||||||
// setInt64(sp + 16, parseInt(loadValue(sp + 8).length));
|
return loadValue(v_addr).length;
|
||||||
//},
|
},
|
||||||
|
|
||||||
// valuePrepareString(v ref) (ref, int)
|
// valuePrepareString(v ref) (ref, int)
|
||||||
"syscall/js.valuePrepareString": (ret_addr, v_addr) => {
|
"syscall/js.valuePrepareString": (ret_addr, v_addr) => {
|
||||||
|
@ -352,25 +352,23 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static _makeCallbackHelper(id, pendingCallbacks, go) {
|
_resume() {
|
||||||
return function () {
|
if (this.exited) {
|
||||||
pendingCallbacks.push({ id: id, args: arguments });
|
throw new Error("Go program has already exited");
|
||||||
go._resolveCallbackPromise();
|
}
|
||||||
};
|
this._inst.exports.resume();
|
||||||
|
if (this.exited) {
|
||||||
|
this._resolveExitPromise();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static _makeEventCallbackHelper(preventDefault, stopPropagation, stopImmediatePropagation, fn) {
|
_makeFuncWrapper(id) {
|
||||||
return function (event) {
|
const go = this;
|
||||||
if (preventDefault) {
|
return function () {
|
||||||
event.preventDefault();
|
const event = { id: id, this: this, args: arguments };
|
||||||
}
|
go._pendingEvent = event;
|
||||||
if (stopPropagation) {
|
go._resume();
|
||||||
event.stopPropagation();
|
return event.result;
|
||||||
}
|
|
||||||
if (stopImmediatePropagation) {
|
|
||||||
event.stopImmediatePropagation();
|
|
||||||
}
|
|
||||||
fn(event);
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче