Skip to content

Instantly share code, notes, and snippets.

@elct9620
Last active November 12, 2023 18:42
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save elct9620/44db8ce0beb7e08cd588e9cdecafbd56 to your computer and use it in GitHub Desktop.
Save elct9620/44db8ce0beb7e08cd588e9cdecafbd56 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
@AndrusGerman
Copy link

extends?

@elct9620
Copy link
Author

extends?

extends?

@AndrusGerman
Copy link

@elct9620 I'm trying to do this in golang webassembly, but I can't find a way to do it

class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

@elct9620
Copy link
Author

elct9620 commented Jun 9, 2022

You mean you want to define a class in Golang and extend a React component?

@AndrusGerman
Copy link

@elct9620 Yes, I want to define a JavaScript class in golang and extend it for example with React componet

@elct9620
Copy link
Author

elct9620 commented Jun 9, 2022

I didn't suggest you do this.

  1. How to access the React.Component? React usually does not expose it to window therefore you are unable to extend it.
  2. The Golang cannot work correctly with React's binding, you need to re-implement it in your project, it is not suggested.

The most reasonable way is to define a JavaScript object under window and access it in your React component.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment