vecty icon indicating copy to clipboard operation
vecty copied to clipboard

Improving TinyGo support

Open slimsag opened this issue 4 years ago • 14 comments

Experimental TinyGo support in Vecty has landed: https://github.com/tinygo-org/tinygo/issues/1282#issuecomment-674475054

For details on what that means, its limitations, and how to use it - see: https://github.com/hexops/vecty/pull/243#issue-295065853

This issue is for tracking improvements to TinyGo support that would get it out of experimental status.

  • [ ] TinyGo cannot compile example/markdown's dependencies.
  • [ ] TinyGo cannot compile example/todomvc's dependencies.
  • [x] TinyGo has no reflect.New support, meaning vecty.Copier must be implemented for all components. https://github.com/tinygo-org/tinygo/issues/1087
  • [ ] Debug symbols / information is not included which means panics with component names %T show just T instead of my/package.MyComponent
  • [ ] There is no wasmserve equivilent for TinyGo yet

slimsag avatar Aug 16 '20 04:08 slimsag

Thanks for your work on TinyGo support.

I've created equivalent of wasmserve for TinyGo. https://github.com/dImrich/tinygo-wasmserve

dImrich avatar Aug 16 '20 22:08 dImrich

@dlmrich that’s great to hear! I’ll check it out and update the instructions for using that soon

slimsag avatar Aug 17 '20 00:08 slimsag

@slimsag Great!

I have a little problem with vecty.List. It's not working under TinyGo while both examples works fine in regular Go.

Error 1:

panic: runtime error: type assert failed

Uncaught (in promise) RuntimeError: unreachable
    at runtime.runtimePanic (<anonymous>:wasm-function[64]:0x454d)
    at runtime.interfaceTypeAssert (<anonymous>:wasm-function[89]:0x5f2d)
    at (github.com/hexops/vecty.jsObject).Equal (<anonymous>:wasm-function[179]:0xdabd)
    at (*github.com/hexops/vecty.HTML).reconcileChildren.resume (<anonymous>:wasm-function[209]:0x133f1)
func (p *PageView) Render() vecty.ComponentOrHTML {
	items := make(vecty.List, 2)
	items = append(items, elem.Div(vecty.Text("Item 1")))
	items = append(items, elem.Div(vecty.Text("Item 2")))

	return elem.Body(
		elem.Div(vecty.Text("Hello Vecty!")),
		items,
	)
}

Error 2:

panic: runtime error: slice out of range

Uncaught (in promise) RuntimeError: unreachable
    at runtime.runtimePanic (<anonymous>:wasm-function[64]:0x454d)
    at runtime.slicePanic (<anonymous>:wasm-function[112]:0x74cb)
    at (*github.com/hexops/vecty.HTML).reconcileChildren.resume (<anonymous>:wasm-function[209]:0x136b9)
func (p *PageView) Render() vecty.ComponentOrHTML {
	items := make(vecty.List, 2)
	items = append(items, elem.Div(vecty.Text("Item 1")))
	items = append(items, elem.Div(vecty.Text("Item 2")))

	return elem.Body(
		elem.Div(vecty.Text("Hello Vecty!")),
		elem.Div(items),
	)
}

dImrich avatar Aug 17 '20 15:08 dImrich

Hey @slimsag , hope you're well. I'm curious if there has been any updates here or if there's anything that TinyGo is pending on to make improvements in Vecty? Thanks!

marwan-at-work avatar Feb 23 '21 15:02 marwan-at-work

Things here have improved quite considerably on the TinyGo side:

  • Much of the reflection API support has been improved: https://github.com/tinygo-org/tinygo/pull/1727
  • reflect.New support has been added: https://github.com/tinygo-org/tinygo/pull/1787

I haven't yet had a chance to try out this, but if someone wants to:

  1. Remove dom_tinygo.go in this repository.
  2. Remove this line from dom_no_tinygo.go: https://github.com/hexops/vecty/blob/main/dom_no_tinygo.go#L1
  3. Try running the examples in this repository with latest TinyGo.

I suspect many things will 'just work' now.

slimsag avatar May 28 '21 12:05 slimsag

I suspect many things will 'just work' now.

Has anyone had a chance to try this recently? I would agree with the above statement! :smile_cat:

deadprogram avatar Dec 07 '21 14:12 deadprogram

I just tried running vecty's tests with tinygo dev (well, with my setenv branch) after making the two changes above. Six tests fail:

TestCore: panic: runtime error: nil pointer dereference
TestHTML_reconcile_nil: panic: vecty: internal error (only one of HTML.tag or HTML.text may be set)
TestRenderBody_ExpectsBody: panic: vecty: RenderBody: expected Component.Render to return a "body", found ""
TestRenderBody_RenderSkipper_Skip: panic: vecty: RenderBody: Component.SkipRender illegally returned true
TestRerender_nil: panic: vecty: Rerender illegally called with a nil Component argument
TestRerender_no_prevRender: panic: vecty: Rerender invoked on Component that has never been rendered

The next step, if someone has the time, might be to create minimal test cases for one or more of those & file them individually in https://github.com/tinygo-org/tinygo/issues.

dkegel-fastly avatar Dec 12 '21 03:12 dkegel-fastly

TestCore (at least) seems to be testing that things panic in the appropriate places, which means we should retest with a tinygo branch with https://github.com/tinygo-org/tinygo/pull/2331 applied to it (since otherwise all panics are fatal.)

dgryski avatar Dec 12 '21 16:12 dgryski

Seemed to die pretty quickly on branch recover3:

(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGABRT
  * frame #0: 0x00007fff204d492e libsystem_kernel.dylib`__pthread_kill + 10
    frame #1: 0x00007fff205035bd libsystem_pthread.dylib`pthread_kill + 263
    frame #2: 0x00007fff20458406 libsystem_c.dylib`abort + 125
    frame #3: 0x000000010000678d vecty.test`runtime.runtimePanic + 46
    frame #4: 0x0000000100006a97 vecty.test`runtime.nilPanic + 16
    frame #5: 0x00000001000276e5 vecty.test`github.com/hexops/vecty.TestCore$3$1 + 47
    frame #6: 0x0000000100027646 vecty.test`github.com/hexops/vecty.recoverStr + 144
    frame #7: 0x0000000100027436 vecty.test`github.com/hexops/vecty.TestCore$3 + 118
    frame #8: 0x00000001000156b6 vecty.test`(*testing.T).Run + 227
    frame #9: 0x0000000100027321 vecty.test`github.com/hexops/vecty.TestCore + 83
    frame #10: 0x000000010003217e vecty.test`github.com/hexops/vecty.TestMain + 2422
    frame #11: 0x00000001000322ac vecty.test`main.main + 17

Also, there was this warning first; my tinygo was built using "make", fwiw.

ld: warning: PIE disabled. Absolute addressing (perhaps -mdynamic-no-pic) not allowed in code signed PIE, but used in _(*sync.Once).Do from /var/folders/v0/0k9hwftd29b18z9z7s2g2sfc0000gn/T/tinygo198488412/main.o. To fix this warning, don't compile with -mdynamic-no-pic or link with -Wl,-no_pie

dkegel-fastly avatar Dec 14 '21 03:12 dkegel-fastly

It may be the case that TinyGo just doesn't support recover() very well, has anyone tried just disabling tests that rely on recover() and seeing if the remainder pass - and better, the examples?

slimsag avatar Dec 14 '21 05:12 slimsag

The recover support is still draft, fwiw.
Quite a few tests do pass, the exceptions are listed above. I have not tried examples yet, leaving that to people who actually use vecty; mine was just a drive-by "which tests fail" check.

dkegel-fastly avatar Dec 14 '21 17:12 dkegel-fastly

TestCore (at least) seems to be testing that things panic in the appropriate places, which means we should retest with a tinygo branch with tinygo-org/tinygo#2331 applied to it (since otherwise all panics are fatal.)

Unfortunately, #2331 is only for non-WebAssembly instruction sets. WebAssembly is kind of a pain to support... Just FYI.

aykevl avatar Dec 17 '21 11:12 aykevl

@aykevl makes sense, thanks for that info! We don't use recover() outside of tests anyway (it's literally just to check if we panic under the right (wrong) conditions) :)

I think the next step here is for someone to try running the examples with tinygo and/or sending a PR to disable those tests when compiling with TinyGo.

slimsag avatar Dec 17 '21 18:12 slimsag

I've just tried to run the hellovecty example.

Should I have followed these instructions?

  1. Remove dom_tinygo.go in this repository.
  2. Remove this line from dom_no_tinygo.go: https://github.com/hexops/vecty/blob/main/dom_no_tinygo.go#L1
  3. Try running the examples in this repository with latest TinyGo.

Here the browser console output:

23:14:54.856 GEThttp://localhost:8080/
[[HTTP/1.1 304 Not Modified 1ms]]()

23:14:54.894 GEThttp://localhost:8080/wasm_exec.js
[[HTTP/1.1 304 Not Modified 1ms]]()

23:14:54.916 The script from “http://localhost:8080/wasm_exec.js” was loaded even though its MIME type (“text/plain”) is not a valid JavaScript MIME type. [localhost:8080](http://localhost:8080/)
23:14:54.937 XHRGEThttp://localhost:8080/main.wasm
[[HTTP/1.1 200 OK 1ms]]()

23:14:54.967 (19429:0x00017fd0) [wasm_exec.js:268:19](http://localhost:8080/wasm_exec.js)
23:14:54.968 panic: vecty: Component T does not implement vecty.Copier interface [wasm_exec.js:268:19](http://localhost:8080/wasm_exec.js)
23:14:54.968 <empty string> [wasm_exec.js:268:19](http://localhost:8080/wasm_exec.js)
23:14:54.968 TinyGo does not support Vecty components that do not implement the vecty.Copier interface. [wasm_exec.js:268:19](http://localhost:8080/wasm_exec.js)
23:14:54.968 <empty string> [wasm_exec.js:268:19](http://localhost:8080/wasm_exec.js)
23:14:54.968 ## What does this mean? [wasm_exec.js:268:19](http://localhost:8080/wasm_exec.js)
23:14:54.968 <empty string> [wasm_exec.js:268:19](http://localhost:8080/wasm_exec.js)
23:14:54.968 TinyGo currently does not have support for reflect.New: https://github.com/tinygo-org/tinygo/issues/1087 [wasm_exec.js:268:19](http://localhost:8080/wasm_exec.js)
23:14:54.969 This prevents Vecty from automatically copying your component for you using reflection. [wasm_exec.js:268:19](http://localhost:8080/wasm_exec.js)
23:14:54.969 <empty string> [wasm_exec.js:268:19](http://localhost:8080/wasm_exec.js)
23:14:54.969 ## How do I fix this? [wasm_exec.js:268:19](http://localhost:8080/wasm_exec.js)
23:14:54.969 <empty string> [wasm_exec.js:268:19](http://localhost:8080/wasm_exec.js)
23:14:54.969 You will need to implement the 'vecty.Copier' interface on your component, e.g.: [wasm_exec.js:268:19](http://localhost:8080/wasm_exec.js)
23:14:54.969 <empty string> [wasm_exec.js:268:19](http://localhost:8080/wasm_exec.js)
23:14:54.970 	func (c *MyComponent) Copy() vecty.Component { [wasm_exec.js:268:19](http://localhost:8080/wasm_exec.js)
23:14:54.970 		cpy := *c [wasm_exec.js:268:19](http://localhost:8080/wasm_exec.js)
23:14:54.970 		return &cpy [wasm_exec.js:268:19](http://localhost:8080/wasm_exec.js)
23:14:54.970 	} [wasm_exec.js:268:19](http://localhost:8080/wasm_exec.js)
23:14:54.970 <empty string> [wasm_exec.js:268:19](http://localhost:8080/wasm_exec.js)
23:14:54.970 ## Which component? [wasm_exec.js:268:19](http://localhost:8080/wasm_exec.js)
23:14:54.970 <empty string> [wasm_exec.js:268:19](http://localhost:8080/wasm_exec.js)
23:14:54.970 Vecty has printed as much information as it can about the component above. Unfortunately, you will need to hunt it down yourself. [wasm_exec.js:268:19](http://localhost:8080/wasm_exec.js)
23:14:54.970 <empty string> 2 [wasm_exec.js:268:19](http://localhost:8080/wasm_exec.js)
23:14:54.971 Uncaught (in promise) RuntimeError: unreachable executed
    run http://localhost:8080/wasm_exec.js:486
    <anonymous> http://localhost:8080/:11
    promise callback* http://localhost:8080/:10
[main.wasm:4411:1](http://localhost:8080/main.wasm)
23:14:55.011 GEThttp://localhost:8080/favicon.ico
[[HTTP/1.1 200 OK 0ms]]()

VinceJnz avatar Apr 15 '22 11:04 VinceJnz

After removing 1 and 2

The browser displayed "Hello Vecty!"

The browser console output still has some errors:

23:27:14.281 GEThttp://localhost:8080/
[[HTTP/1.1 200 OK 1ms]]()

23:27:14.317 GEThttp://localhost:8080/wasm_exec.js
[[HTTP/1.1 200 OK 1ms]]()

23:27:14.320 The script from “http://localhost:8080/wasm_exec.js” was loaded even though its MIME type (“text/plain”) is not a valid JavaScript MIME type. [localhost:8080](http://localhost:8080/)
23:27:14.364 XHRGEThttp://localhost:8080/main.wasm
[[HTTP/1.1 200 OK 5ms]]()

23:27:14.391 syscall/js.finalizeRef not implemented [wasm_exec.js:313:15](http://localhost:8080/wasm_exec.js)
23:27:14.392 syscall/js.finalizeRef not implemented [wasm_exec.js:313:15](http://localhost:8080/wasm_exec.js)
23:27:14.392 syscall/js.finalizeRef not implemented [wasm_exec.js:313:15](http://localhost:8080/wasm_exec.js)
23:27:14.436 GEThttp://localhost:8080/favicon.ico
[[HTTP/1.1 200 OK 3ms]]()

VinceJnz avatar Apr 15 '22 11:04 VinceJnz

I've tried running the "Markdown" example

Here's the browser console output

23:38:31.097 GEThttp://localhost:8080/
[[HTTP/1.1 304 Not Modified 2ms]]()

23:38:31.138 GEThttp://localhost:8080/wasm_exec.js
[[HTTP/1.1 304 Not Modified 2ms]]()

23:38:31.162 The script from “http://localhost:8080/wasm_exec.js” was loaded even though its MIME type (“text/plain”) is not a valid JavaScript MIME type. [localhost:8080](http://localhost:8080/)
23:38:31.190 XHRGEThttp://localhost:8080/main.wasm
[[HTTP/1.1 200 OK 45ms]]()

23:38:31.254 GEThttp://localhost:8080/favicon.ico
[[HTTP/1.1 200 OK 0ms]]()

23:38:32.317 m���j�������7�panic: runtime error: nil pointer dereference [wasm_exec.js:268:19](http://localhost:8080/wasm_exec.js)
23:38:32.320 Uncaught (in promise) RuntimeError: unreachable executed
    run http://localhost:8080/wasm_exec.js:486
    <anonymous> http://localhost:8080/:11
    promise callback* http://localhost:8080/:10
[main.wasm:39478:1](http://localhost:8080/main.wasm)

VinceJnz avatar Apr 15 '22 11:04 VinceJnz