Thanks to visit codestin.com
Credit goes to github.com

Skip to content

All vugu generated files are missing //go:build js && wasm build tags #330

@owenwaller

Description

@owenwaller

Describe the bug

As part of the investigation into issue #328 I was attempting to update the new test-025-valueof example to this:

package main

import (
	"fmt"
	"time"

	sysjs "syscall/js" // <-- new

	js "github.com/vugu/vugu/js"
)

type Root struct {
}

// Make a vugu.js.ValueOf call on an object derived from a JS global object - in this case the global "Date" object.
// Returns the string "Ok" or a panic message
// See Issue #328 https://github.com/vugu/vugu/issues/328
// vugu/js version
func (c *Root) VuguJsValueOf() (s string) {
	// We have to be a little clever here
	// We need to recover from the panic but change the value we return.
	// The simplest way to achieve this is with a named return value.
	// By setting the named return value ot the panic string we will overwrite any previous
	// value (in this case "Ok") that we would otherwise return.
	defer func() {
		if r := recover(); r != nil {
			s = fmt.Sprintf("%s", r) // set the names return value to the panic string
		}
	}()
	c.vuguJsPanicingFunc()
	return "Ok"
}

// Syscall/js version of VuguJsValueOf
func (c *Root) SyscallJsValueOf() (s string) { // <-- new
	defer func() {
		if r := recover(); r != nil {
			s = fmt.Sprintf("%s", r) // set the names return value to the panic string
		}
	}()
	c.syscallJsPanicingFunc()
	return "Ok"
}

// panicingFunc makes the vugu.js.ValueOf call that rill result in a panic
// vugu/js version
func (c *Root) vuguJsPanicingFunc() {
	date := js.Global().Get("Date") // NOTE: use of double quotes not single quotes as in Issue #328 https://github.com/vugu/vugu/issues/328
	timeEndValue := date.New(time.Now().UnixMilli())
	// This line with panic with:
	//
	// ValueOf: invalid value
	//
	// The underlying implementation of js.Value() calls syscall.js.Value.
	// The underlying type of the interface{} that is passed to syscall.js.Value is a vugu.js.Value
	// because a vugu.js.Value is the return type of the js.Global.Get(...) call above.
	// But because a vugu.js.Value is not a known type inside syscall.js.ValueOf the result is a panic from syscall.js.ValueOf.
	// The internal implementation of syscall.js.ValueOf consists of a type switch statement i.e. `switch x := x.(type)`
	// and since a vugu.js.Value is not one of the listed types the syscall.js.ValueOf we reach the default case which panics.
	js.ValueOf(timeEndValue)
}

// syscall/js version of panicingFunc
func (c *Root) syscallJsPanicingFunc() { // <-- new
	date := sysjs.Global().Get("Date") // NOTE: use of double quotes not single quotes as in Issue #328 https://github.com/vugu/vugu/issues/328
	timeEndValue := date.New(time.Now().UnixMilli())
	// This should not panic as it calls syscall.js.ValueOf directly with a parameter with an underlying type of syscall.js.Value
	sysjs.ValueOf(timeEndValue)
}

VuguJsValueOf() and SyscallJsValueOf() are both called directly form the root.vugu file.
As per the comments we would expect only the SyscallJSValueOf() to run without panicing.

But attempting to build this with mage like this:

$ mage testSingleWasmTest github.com/vugu/vugu/wasm-test-suite/test-025-valueof

results in this error

package github.com/vugu/vugu/wasm-test-suite/test-025-valueof
	imports syscall/js: build constraints exclude all Go files in /home/owen/sdk/go1.23.5/src/syscall/js
...
Error: running "go test -v github.com/vugu/vugu/wasm-test-suite/test-025-valueof" failed with exit code 1

This makes sense as syscall/js is imported directly by the Root.go and syscall/js has a build constraint of go:build js && wasm.

mage is also doing the "right thing". It is attempting to build the module, under the directory wasm-test-suite/test-025-valueof, named github.com/vugu/vugu/wasm-test-suite/test-025-valueof.

It is doing this by executing a go test -v github.com/vugu/vugu/wasm-test-suite/test-025-valueof with an implicit GOOS=linux GOARCH=amd64 So as the GOOS and GOARCH don't match the build constraints in syscall/js it is excluded.

But it is more complex that that.

We want the test binary to be built on the native platform (GOOS=linux GOARCH=amd64) because the test will connect to a headless chome running in a docker container. But go test works by compiling the module in addition to the test code. From the go test docs

'Go test' recompiles each package along with any files with names matching
the file pattern "*_test.go".

So in the case of test-025-valueof which has a directory tree like this:

wasm-test-suite/test-025-valueof/
├── go.mod
├── go.sum
├── index.html
├── index.html.tmpl
├── main_test.go
├── main.wasm
├── main_wasm.go
├── root_gen.go
├── root.go
├── root.vugu
└── wasm_exec.js

What is happening is this:

Mage runs and correctly makes the main.wasm using GOOS=js GOARCM=wasm go build -o main.wasm github.com/vugu/vugu/wasm-test-suite/test-025-valueof This will build main_wasm.go. root.go and root_gen.go into the wasm binary main.wasm.

Mage then tries to test the code by executing GOOS=linux GOARCH=amd64 go test -v github.com/vugu/vugu/wasm-test-suite/test-025-valueof.

So as per the docs that will attempt to build main_test.go and main_wasm.go. root.go and root_gen.go but this will fail due to the build constraints in syscall/js

We can't just put the //go:build js && wasm into the root.go file that's not sufficient. Trying this shows the build fails with:

# github.com/vugu/vugu/wasm-test-suite/test-025-valueof [github.com/vugu/vugu/wasm-test-suite/test-025-valueof.test]
./root_gen.go:13:10: undefined: Root
FAIL	github.com/vugu/vugu/wasm-test-suite/test-025-valueof [build failed]
FAIL

Which makes sense as the build constraint means that Root won't be defined because it has been excluded by the build tags.

The only way to escape this is to ensure that the files main_wasm.go. root.go and root_gen.go are only ever build with GOOS=js GOARCM=wasm.

If we do this then we can safely call syscall/js anywhere we require.

Impact of the bug

The test test-025-valueof can't be written in such a way that it builds and runs at the minute.

If the solution to issue #328 involves calling syscall/js directly from a vugu component file ie.a root.go or similar or the solution involves the wholesale replacement of vugu/js with direct calls to syscall/js then I would expect any go tests around that code i.e. _gen.go files to fail to build.

Solution

The solution has two parts. One part that vugu itself must do, and one that the developer must do.

The first part is a change to the vugu generator. We need to ensure that all *_gen.go files are guarded by a build constraint of //go:build js && wasm

This change looks like a change to vugu/gen.ParserGo.visitOverall to ensure the build tag is emitted.

The second change is on the shoulders of developers. All vugu component files i.e. root.go or any file with a matching *_gen.go file need to be similarly guarded with a //go:build js && wasm build tag.

This seems to be acceptable since these component files will only ever be executed by a browser and will only be compiled to wasm as a result.

Impact of the Solution

Although the solution seems small, in terms of the lines of code changed the impact is much larger.

Within vugu we need to update each and every was-test-suite test and each and every example.

We also need to update all of the documentation/wiki pages etc to accurately reflect the need for the developer to add the new build constraint to their component files.

Post this change all *_gen.go files will be different form those previously created. On that basis I would suggest this also causes a vugu version bump so we can identify if a *_gen.go file was generated before or after this change.

Note: I would also strongly suggest that this is the point that the legacy-wasm-test-suite is removed. Issues #278 and #279 must therefore be resolved urgently.

Was the move to mage the reason for this?

In a word no. I don't believe so.

Until now there has never been a test or example that called a function in syscall/js directly. All of the calls to syscall/js are indirect via the vugu/js wrapper package. But as issue #328 shows the vugu/js wrapper (now?) is insufficient.

Also we don't know what future developers may want to do, we can't rule out these future developers needing to call syscall/js directly for some reason.

So we need to make sure this is possible under all reasonable circumstances - and calling go test is one such reasonable circumstance.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions