Skip to content

compiler fails to handle arbitrary string js tags #778

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

Closed
myitcv opened this issue Mar 24, 2018 · 6 comments
Closed

compiler fails to handle arbitrary string js tags #778

myitcv opened this issue Mar 24, 2018 · 6 comments

Comments

@myitcv
Copy link
Member

myitcv commented Mar 24, 2018

Consider the following test:

type StructWithNonIdentifierJsTags struct {
        object *js.Object
        Name   string `js:"my name"`
}

func TestStructWithNonIdentifierJsTags(t *testing.T) {
        s := StructWithNonIdentifierJsTags{
                object: js.Global.Get("Object").New(),
        }

        const want = "Paul"

        // externalise a value
        s.Name = want

        // internalise again
        got := s.Name

        if want != got {
                t.Errorf("Value via non-identifier js tag field gave %q, want %q", got, want)
        }
}

This currently fails at runtime (gopherjs test) with:

/home/myitcv/.mountpoints/myitcv_io_react/src/myitcv.io/react/_vendor/src/github.com/gopherjs/gopherjs/test.554077738:28596
                s.object.my name = $externalize("Paul", $String);
                            ^^^^

SyntaxError: Unexpected identifier
    at new Script (vm.js:51:7)
    at createScript (vm.js:138:10)
    at Object.runInThisContext (vm.js:199:10)
    at Module._compile (module.js:624:28)
    at Object.Module._extensions..js (module.js:671:10)
    at Module.load (module.js:573:32)
    at tryModuleLoad (module.js:513:12)
    at Function.Module._load (module.js:505:3)
    at Function.Module.runMain (module.js:701:10)
    at startup (bootstrap_node.js:193:16)
FAIL    github.com/gopherjs/gopherjs/js 0.185s

This is because the compiler currently translates field access (getting or setting) via the dot notation:

s.object.my name

Where a string contains a non-identifier value (e.g. space above) this is destined to fail.

Some external libraries (e.g. React) require properties to be set that are composed of non-identifier strings, e.g. https://reactjs.org/docs/dom-elements.html, where "data-*" and "aria-*" are used for attribute names.

An equivalent way to set properties is via the bracket notation:

s.object.["my name"]

Hence it probably makes more sense in general to use this for js tag compilation.

For more details see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Property_Accessors

PR that addresses this will follow shortly.

myitcv added a commit to myitcv/gopherjs that referenced this issue Mar 24, 2018
Javascript allows for arbitrary strings to be used as index values on an
Object. A good example of where this comes up is React where "data-*"
and "aria-*" indexes are used on objects
(https://reactjs.org/docs/dom-elements.html). By switching from property
selectors to the index operator for *js.Object special field access we
can support arbitrary string values in js tags.

Fixes gopherjs#778
myitcv added a commit to myitcv/gopherjs that referenced this issue Mar 25, 2018
Javascript allows for arbitrary strings to be used as index values on an
Object. A good example of where this comes up is React where "data-*"
and "aria-*" indexes are used on objects
(https://reactjs.org/docs/dom-elements.html). By switching from property
selectors to the index operator for *js.Object special field access we
can support arbitrary string values in js tags.

Fixes gopherjs#778
myitcv added a commit to myitcv/gopherjs that referenced this issue Mar 25, 2018
Javascript allows for arbitrary strings to be used as index values on an
Object. A good example of where this comes up is React where "data-*"
and "aria-*" indexes are used on objects
(https://reactjs.org/docs/dom-elements.html). By switching from property
selectors to the index operator for *js.Object special field access we
can support arbitrary string values in js tags.

Fixes gopherjs#778
myitcv added a commit to myitcv/gopherjs that referenced this issue Apr 12, 2018
Javascript allows for arbitrary strings to be used as index values on an
Object. A good example of where this comes up is React where "data-*"
and "aria-*" indexes are used on objects
(https://reactjs.org/docs/dom-elements.html). By switching from property
selectors to the index operator for *js.Object special field access we
can support arbitrary string values in js tags.

Fixes gopherjs#778
myitcv added a commit to myitcv/gopherjs that referenced this issue Apr 14, 2018
Javascript allows for arbitrary strings to be used as index values on an
Object. A good example of where this comes up is React where "data-*"
and "aria-*" indexes are used on objects
(https://reactjs.org/docs/dom-elements.html). By switching from property
selectors to the index operator for *js.Object special field access we
can support arbitrary string values in js tags.

Fixes gopherjs#778
myitcv added a commit to myitcv/gopherjs that referenced this issue Apr 14, 2018
Javascript allows for arbitrary strings to be used as index values on an
Object. A good example of where this comes up is React where `"data-*"`
and `"aria-*"` indexes are used on objects
(https://reactjs.org/docs/dom-elements.html).

By switching from property selectors to the index operator (where
required) we can support arbitrary string values in js-tagged fields for
`*js.Object` special structs.

Fixes gopherjs#778
myitcv added a commit to myitcv/gopherjs that referenced this issue Apr 14, 2018
Javascript allows for arbitrary strings to be used as index values on an
Object. A good example of where this comes up is React where `"data-*"`
and `"aria-*"` indexes are used on objects
(https://reactjs.org/docs/dom-elements.html).

By switching from property selectors to the index operator (where
required) we can support arbitrary string values in js-tagged fields for
`*js.Object` special structs.

Fixes gopherjs#778
myitcv added a commit to myitcv/gopherjs that referenced this issue Apr 14, 2018
Javascript allows for arbitrary strings to be used as index values on an
Object. A good example of where this comes up is React where `"data-*"`
and `"aria-*"` indexes are used on objects
(https://reactjs.org/docs/dom-elements.html).

By switching from property selectors to the index operator (where
required) we can support arbitrary string values in js-tagged fields for
`*js.Object` special structs.

Fixes gopherjs#778
myitcv added a commit to myitcv/gopherjs that referenced this issue Apr 14, 2018
Javascript allows for arbitrary strings to be used as index values on an
Object. A good example of where this comes up is React where `"data-*"`
and `"aria-*"` indexes are used on objects
(https://reactjs.org/docs/dom-elements.html).

By switching from property selectors to the index operator (where
required) we can support arbitrary string values in js-tagged fields for
`*js.Object` special structs.

Fixes gopherjs#778
myitcv added a commit to myitcv/gopherjs that referenced this issue Apr 14, 2018
Javascript allows for arbitrary strings to be used as index values on an
Object. A good example of where this comes up is React where `"data-*"`
and `"aria-*"` indexes are used on objects
(https://reactjs.org/docs/dom-elements.html).

By switching from property selectors to the index operator (where
required) we can support arbitrary string values in js-tagged fields for
`*js.Object` special structs.

Fixes gopherjs#778
dmitshur pushed a commit that referenced this issue Apr 16, 2018
JavaScript allows for arbitrary strings to be used as index values on an
Object. A good example of where this comes up is React where `"data-*"`
and `"aria-*"` indexes are used on objects (https://reactjs.org/docs/dom-elements.html).

By switching from property selectors to index operator (where required),
we support arbitrary string values in js-tagged fields for *js.Object
special structs.

Fixes #778.
@pbrown12303
Copy link

pbrown12303 commented Jul 10, 2018

I think there may still be an issue here. I have the latest GopherJS 1.10-4 and still have a problem with getting an attribute whose name begins with a period, e.g. ".label-text". If I have a *js.Object named foo, foo.Get(".label-text") always returns undefined. I can look at the actual javascript object (in the browser debugger) and confirm that the entry is, indeed, there. I can also get any entry that does not have the period. FYI, the name was not my idea - it's a name style used in jointjs.

@myitcv
Copy link
Member Author

myitcv commented Jul 11, 2018

@pbrown12303 do you have a reproducer? Because this appears to work for me:

$ cat main.go
package main

import (
	"github.com/gopherjs/gopherjs/js"
)

type S struct {
	*js.Object
	Name    string `js:"@&\"'<>//my name"`
	DotName string `js:".label-text"`
}

func main() {
	s := S{Object: js.Global.Get("Object").New()}

	s.Name = "Paul"
	s.DotName = "DotPaul"

	js.Global.Get("console").Call("log", s)
}
$ gopherjs run main.go
{ '@&"\'<>//my name': 'Paul', '.label-text': 'DotPaul' }

@flimzy
Copy link
Member

flimzy commented Jul 11, 2018

I think @pbrown12303 is talking about something else, since he mentions using Get() rather than js tags. But I can't reproduce that either.

package main

import (
	"github.com/gopherjs/gopherjs/js"
)

func main() {
	foo := js.Global.Get("Object").New()
	foo.Set(".label-text", "text")

	js.Global.Get("console").Call("log", foo.Get(".label-text"))
}

Output:

$ gopherjs run main.go
text

A reproduction case is indeed necessary.

@myitcv
Copy link
Member Author

myitcv commented Jul 11, 2018

cc @theclapp who has helped on Slack thus far.

@pbrown12303
Copy link

Ah, some more context is warranted. I am not constructing the object - it already exists. The following function creates the data structure that is used to initialize this object, which is a jointjs Element. The function containing the call is bound to the attribute updateRectangles. It is being called by another function also bound to an attribute: initialize. The initialize function is successfully called and it, in turn, successfully calls the updateRectangles function. The problem occurs in the execution of updateRectangles: the calls to .Get(".xxx") all return Undefined as a result.

func NewBePrototypeProperties() js.M {
    // Create the prototype properties
    crlBePrototypeProps := js.M{
        "markup": "<g class=\"rotatable\">" +
            "<g class=\"scalable\">" +
            "<rect class=\"label-rect\"/>" +
            "</g>" +
            "<text class=\"label-text\"/>" +
            "</g>",
        "initialize": js.MakeFunc(func(this *js.Object, arguments []*js.Object) interface{} {
            this.Call("updateRectangles")
            js.Global.Get("joint").Get("shapes").Get("basic").Get("Generic").Get("prototype").Get("initialize").Call("apply", this, arguments)
            return nil
        }),
        "updateRectangles": js.MakeFunc(func(this *js.Object, arguments []*js.Object) interface{} {
            js.Global.Set("UpdateRectanglesThis", this)
            offsetY := 0
            attributes := this.Get("attributes")
            js.Global.Set("UpdateRectanglesAttributes", attributes)
            attrs := attributes.Get("attrs")
            rectHeight := 1*12 + 6
            labelText := this.Get("name")
            labelTextAttr := attrs.Get(".label-text")
            labelTextAttr.Set("text", labelText)
            labelRectAttr := attrs.Get(".label-rect")
            labelRectAttr.Set("height", rectHeight)
            rectWidth := calculateTextWidth(labelText.String()) + 6
            labelRectAttr.Set("transform", "translate(0,"+strconv.Itoa(offsetY)+")")
            this.Call("resize", rectWidth, rectHeight)

            offsetY += rectHeight
            return nil
        })}
    return crlBePrototypeProps
} 

@pbrown12303
Copy link

Argh! Pilot error! There was a typo: attribute name was .label-Text, call was to Get(.label-text). Uppercase vs lowercase T. So sorry to waste everyone's time, but I thank you for your quick and thoughtful responses.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants