-
Notifications
You must be signed in to change notification settings - Fork 570
[WIP] js: tighten up internalize and externalize #618
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
Conversation
0bbcb74
to
a8c3aed
Compare
js/js_test.go
Outdated
type S struct { | ||
*js.Object | ||
|
||
string `js:"string"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this is a test of internalization logic (as I understand from our conversation in #617), I would suggest rewriting it to avoid using js
struct field tags. I think it's simpler to use Object.String
method, it's lower level and more explicit, making it more clear what the test is testing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@shurcooL I think for completeness we'll need to test String()
as well, but in order that we can cover all Go types I think we need to follow the struct field tags approach... unless I'm missing something?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
but in order that we can cover all Go types I think we need to follow the struct field tags approach...
Are methods like String
, Float
, Bool
Uint64
, etc., not sufficient? What's not possible without struct field tags?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All the types that do not have corresponding methods on Object
. e.g. the specific numeric types like int8
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can Interface().(int8)
be done for those, or is that not equivalent?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think Interface()
is another test case altogether because it uses the Javascript type of the value to decide which Go type to resolve to. So in the context of this discussion, I suspect use of Interface()
wouldn't actually be a problem per se.
What you've proposed transpiles to:
$assertType($internalize(o.A, $emptyInterface), $Int8)
So we still need to have test cases for all the Go types... because it's $internalize
that we're ultimately testing here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see, thanks. Then it's okay to use the struct field tags, but my request is to document that what's being tested is internalize behavior, not the struct field tag behavior.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah I see; 100% agree on that point.
a8c3aed
to
1b130f5
Compare
1b130f5
to
0d5cce0
Compare
d23fda2
to
93b99d9
Compare
// | ||
// When values pass from Go to Javascript, a process known as externalization, | ||
// the following conversion table is applied: | ||
// |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Table missing?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See next line.
js/js.go
Outdated
// | ||
// Externalization | ||
// | ||
// When values pass from Go to Javascript, a process known as externalization, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Incomplete sentence
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @flimzy - I should have mentioned this entire PR is still very much WIP.
In particular, I haven't even started on the $externalize
side of things. Had some fairly interesting edge cases on the $internalize
side of things
@flimzy @shurcooL - thanks both for taking a look at this. I should have been clearer in my update message in #617 because I value your time... This is still WIP so I will give you a shout when it's essentially "ready" for review. There are still too many rough edges. My plan is to get a couple of types (probably Just wanted to post an update to confirm I'm still "on" this, albeit after a significant period of inactivity! |
No problem. I suggest using Go code review convention for achieving this, put a "DO NOT REVIEW" text anywhere in the PR description to signal that. (See https://groups.google.com/d/msg/golang-dev/YU56eKYiXJg/K1CkXo7Iy0gJ.) |
Done. I've switched from using my Thanks again |
f9d8435
to
517a713
Compare
Given there's quite a lot to work through in this PR, I've updated the description in this PR with, amongst other things, the following:
Would welcome your thoughts on the TODO list, in terms of content but also approach (i.e. using the TODO list as a means of communicating how I plan to go about things). At this point I think I've got to the end of step 1, hence am marking this for a review. All thoughts/feedback welcomed. Thanks |
Worth pointing out that at this stage, not least because of point 2 from the TODO list, I'm not expecting the build to pass. |
// | bool | none | null | false | | ||
// | | | undefined | false | | ||
// | | | false † | false | | ||
// | | | new Boolean(false) † | false | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it worth including true
examples here, for the sake of completeness, similar to the non-zero value examples for strings?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We would end up adding two lines: true
and new Boolean(true)
- hence why I opted for the † callout.
@@ -467,7 +467,7 @@ func TestCallWithNull(t *testing.T) { | |||
func TestReflection(t *testing.T) { | |||
o := js.Global.Call("eval", "({ answer: 42 })") | |||
if reflect.ValueOf(o).Interface().(*js.Object) != o { | |||
t.Fail() | |||
t.Fatal() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AFAIK, Fatal()
is for reporting a failure message. Maybe FailNow()
would be more semantically correct?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it not possible to include a helpful error message in the t.Fatal
call? If possible, that's better.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To be honest I was being a little lazy here in just flipping the existing t.Fail()
to t.Fatal()
to help with debugging step 2 from the TODO.
I'm sure we could add helpful messages to the t.Fatal()
calls... because the existing t.Fail()
calls are equally lossy. Could even be something we do in another PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's usually easier to do these minor things as you're working in a relevant context than later. But this is completely up to you, and not critical for getting the PR into a mergeable state.
fieldName := "string" | ||
zero := "" | ||
|
||
s := &Internalize{Object: js.Global.Get("Object").New()} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Stylistic note: I wonder if a table-based test would make sense here? It may make the tests more readable, reduce the need for the // ***
comment blocks, and if coupled with t.Run(...)
calls for each test, could make the output more readable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You raise a good point. I half considered this myself, but postponed giving it much thought until we've confirmed we're actually testing the right stuff... at which point extracting to table tests should be relatively easy.
The little thought I did give it however amount to the following: I'm not convinced we can actually use table tests. We need to address specific fields of the Internalize
struct and do so in such a way that the gopherjs
compiler sees us doing so and compiles to the corresponding Javascript. Put another way, the code we write in these tests needs to correspond exactly to the code that will be written in the wild.
Therefore the one approach I have considered fairly seriously is code generating the tests. Because they do follow a formula that's for sure.
But as I said, I haven't given this much time/though so would very much welcome any suggestions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@flimzy just pushed up a code-generated version of the tests which seems to work well. Let me know what you think.
Just pushed up a new commit per #617 (comment) |
0a359b9
to
19bf589
Compare
dabda69
to
ab5ceb8
Compare
Nearly 4 years later, @nevkontakte and I have decided to close this PR. We like the effort here, and some of it will be very informative if we address these issues in the future, but with limited bandwidth, and the author having left GopherJS development, we feel this PR is at a dead end. |
!! DO NOT REVIEW !!
NEEDS REBASE AFTER #687 IS MERGED
Status
Step 1 and 2 (see below) review.
Description
A work-in-progress PR to complement the discussion in #617
Tighten up
$internalize
and$externalize
such that:undefined
andnull
is well definedSee impact of this PR on
honnef.co/go/js/dom
in this comparisonDocumentation
Proposed human-readable definitions of
$internalize
and$externalize
can be seen here.Scope of
$internalize
testingThe domain of source values we need to consider is:
null
undefined
(there is no need to distinguish between a field/variable being explicitly assigned the value ofundefined
and it being so by virtue of not having been initialised)Overall PR TODO list
string
andbool
TestReflection
failing injs
tests)$internalize
and$externalize
for all types to allow for testing that code in real-life code bases (not just tests)*js.Object
-special struct types injs
package (unless we work this into the tables for$internalize
and$externalize
Consideration for later PRs
$internalize
and$externalize
into separate, smaller type-specific functionsnull
andundefined
do not internalize as(*T)(nil)
for a*js.Object
-special struct typeT