Skip to content

Instantly share code, notes, and snippets.

@j100002ben
Forked from elct9620/app-tinygo.js
Created October 5, 2020 15:10
Show Gist options
  • Save j100002ben/2c40e5e6b57302ca337d08f7dccbfc0a to your computer and use it in GitHub Desktop.
Save j100002ben/2c40e5e6b57302ca337d08f7dccbfc0a to your computer and use it in GitHub Desktop.
Define JavaScript class inside Golang (WebAssembly)
import 'vendor/tinygo'
const go = new Go();
go.importObject.env['main.defineClass'] = function(namePtr, nameLen, cPtr, cGcPtr, pPtr/*, pGcPtr*/) {
const mem = new DataView(go._inst.exports.memory.buffer)
const decoder = new TextDecoder("utf-8");
const name = decoder.decode(new DataView(go._inst.exports.memory.buffer, namePtr, nameLen));
const constructorID = mem.getUint32(cPtr, true)
const constructor = go._values[constructorID]
const prototypeID = mem.getUint32(pPtr, true)
const prototypes = go._values[prototypeID]
window[name] = (new Function(
'constructor', 'prototypes',
`
function ${name}() {
if(!(this instanceof ${name})) {
throw new TypeError("Cannot call a class as a function")
}
return constructor.apply(this, arguments)
}
prototypes.call(${name}.prototype)
return ${name}
`
))(constructor, prototypes);
}
WebAssembly
.instantiateStreaming(
fetch(require('./main.wasm')),
go.importObject,
)
.then(result => {
go.run(result.instance);
});
import 'vendor/golang'
const go = new Go();
const decoder = new TextDecoder("utf-8");
const getInt64 = (mem, addr) => {
const low = mem.getUint32(addr + 0, true);
const high = mem.getInt32(addr + 4, true);
return low + high * 4294967296;
}
const loadString = (mem, addr) => {
const saddr = getInt64(mem, addr + 0);
const len = getInt64(mem, addr + 8);
return decoder.decode(new DataView(mem.buffer, saddr, len));
}
const loadValue = (mem, addr) => {
const f = mem.getFloat64(addr, true);
if (f === 0) {
return undefined;
}
if (!isNaN(f)) {
return f;
}
const id = mem.getUint32(addr, true);
return go._values[id];
}
go.importObject.go['main.defineClass'] = function(sp) {
const mem = new DataView(go._inst.exports.mem.buffer)
const name = loadString(mem, sp + 8);
const constructor = loadValue(mem, sp + 24)
const prototypes = loadValue(mem, sp + 40)
window[name] = (new Function(
'constructor', 'prototypes',
`
function ${name}() {
if(!(this instanceof ${name})) {
throw new TypeError("Cannot call a class as a function")
}
return constructor.apply(this, arguments)
}
prototypes.call(${name}.prototype)
return ${name}
`
))(constructor, prototypes);
}
WebAssembly
.instantiateStreaming(
fetch(require('./main.wasm')),
go.importObject,
)
.then(result => {
go.run(result.instance);
});
package main
func defineClass(name string, constructor js.Value)
func pointGetX(this js.Value, []inputs js.Value) interface{} {
return this.Get("x")
}
func pointConstructor(this js.Value, []inputs js.Value) interface{} {
this.Set("x", inputs[0])
this.Set("y", inputs[1])
return this;
}
func pointPrototype(this js.Value, []inputs js.Value) interface{} {
this.Set("getX", js.FuncOf(pointGetX))
return nil
}
func main() {
defineClass("Point", js.FuncOf(pointConstructor).Value)
// Keep Golang running
<-make(chan bool)
}
#include "textflag.h"
TEXT ·defineClass(SB), NOSPLIT, $0
CallImport
RET
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment