Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

syscall/js: possibility of using String.prototype.* methods on strings #35917

Open
dmitshur opened this issue Dec 1, 2019 · 4 comments
Open

syscall/js: possibility of using String.prototype.* methods on strings #35917

dmitshur opened this issue Dec 1, 2019 · 4 comments

Comments

@dmitshur
Copy link
Member

@dmitshur dmitshur commented Dec 1, 2019

With GopherJS, it was possible to call the String.prototype.toLowerCase method on a JavaScript String:

https://gopherjs.github.io/playground/#/ekZpm7tuW4

It's possible I'm overlooking something trivial, but this doesn't seem possible with syscall/js API of WebAssembly. Consider the same program modified to use syscall/js:

package main

import (
	"fmt"
	"syscall/js"
)

func main() {
	body := js.Global().Get("document").Get("body")

	fmt.Println(body.Get("nodeName"))
	fmt.Println(body.Get("nodeName").Call("toLowerCase"))
	fmt.Println(body.Get("nodeName").Get("toLowerCase").Invoke())
}

Its output with Go 1.13.4 is:

BODY
panic: syscall/js: call of Value.Get on string

goroutine 1 [running]:
syscall/js.Value.Get(0x7ff800010000000e, 0x34d12, 0xb, 0x7ff800010000000e)
	/usr/local/go/src/syscall/js/js.go:252 +0x39
main.main()
	/tmp/tmp.CQimpW7L/main.go:12 +0xb

Depending on whether a JavaScript String type is considered a "JavaScript object" or not, this may be consistent with js.Value.Get documentation, which says:

Get returns the JavaScript property p of value v. It panics if v is not a JavaScript object.

I'm wondering if it's possible to use toLowerCase with syscall/js API? If not, should it be possible?

/cc @neelance

@slimsag

This comment has been minimized.

Copy link

@slimsag slimsag commented Dec 1, 2019

Workaround: you can call JS String methods via the String.prototype itself, i.e. in JS:

String.prototype.toLowerCase.call("FOO")

In Go:

js.Global().Get("String").Get("prototype").Get("toLowerCase").Call("call", js.ValueOf("FOO"))

I have used this in gopherjs/vecty#251 in order to workaround this problem.

@agnivade

This comment has been minimized.

Copy link
Contributor

@agnivade agnivade commented Dec 1, 2019

If not, should it be possible?

I think it should. The current API relies on Reflect.get which only works on objects, and therefore fails on normal strings. With a little bit of code, and now with the argument spread syntax available, I was able to make it work.

fmt.Println(body.Get("nodeName").Call("toLowerCase"))
fmt.Println(body.Get("nodeName").Call("charAt", 3))

Not pasting the code here. But I can send a CL if Richard is okay with it.

@neelance

This comment has been minimized.

Copy link
Member

@neelance neelance commented Dec 1, 2019

This is because of JavaScript's autoboxing. string and String are two distinct types. The first is the primitive type, the second one is the boxed object type and only the second has methods. Autoboxing makes it so the expression

"foo".toUpperCase()

gets interpreted as

new String("foo").toUpperCase()

I do not yet see why syscall/js should emulate autoboxing. Is there any proper use case? In my opinion, for the examples above the proper solution is to use the .String() method to get the Go string and then use Go's functions for manipulating the string.

@dmitshur

This comment has been minimized.

Copy link
Member Author

@dmitshur dmitshur commented Dec 1, 2019

Thanks for the explanation.

I do not yet see why syscall/js should emulate autoboxing.

I agree, I don't think that would be a good change.

In my opinion, for the examples above the proper solution is to use the .String() method to get the Go string and then use Go's functions for manipulating the string.

It should also be possible to use JavaScript's toLowerCase explicitly, if one wants to (e.g., to avoid having to import strings package, or for other JavaScript functions where there isn't equivalent Go functionality).

It sounds like it is possible with one of these two ways:

var str string = "FOO"

js.Global().Get("String").New(str).Call("toLowerCase")

// or

js.Global().Get("String").Get("prototype").Get("toLowerCase").Call("call", str)

So nothing needs to be done. I'll close this if there aren't objections.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
4 participants
You can’t perform that action at this time.