Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit d23fda2

Browse filesBrowse files
committed
WIP fixes for internalize and zero val translation of undefined and null
1 parent 2b1d432 commit d23fda2
Copy full SHA for d23fda2

File tree

Expand file treeCollapse file tree

3 files changed

+264
-13
lines changed
Filter options
Expand file treeCollapse file tree

3 files changed

+264
-13
lines changed

‎compiler/expressions.go

Copy file name to clipboardExpand all lines: compiler/expressions.go
-2Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1213,8 +1213,6 @@ func (c *funcContext) internalize(s *expression, t types.Type) *expression {
12131213
switch u := t.Underlying().(type) {
12141214
case *types.Basic:
12151215
switch {
1216-
case isBoolean(u):
1217-
return c.formatExpr("!!(%s)", s)
12181216
case isInteger(u) && !is64Bit(u):
12191217
return c.fixNumber(c.formatExpr("$parseInt(%s)", s), u)
12201218
case isFloat(u):

‎compiler/prelude/jsmapping.go

Copy file name to clipboardExpand all lines: compiler/prelude/jsmapping.go
+41-5Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,13 @@ var $internalize = function(v, t, recv) {
200200
}
201201
switch (t.kind) {
202202
case $kindBool:
203-
return !!v;
203+
if (v === undefined || v == null) {
204+
return false;
205+
}
206+
if (v.constructor !== Boolean) {
207+
$throwRuntimeError("tried to internalize non-bool value of type " + $typeof(v));
208+
}
209+
return Boolean(v);
204210
case $kindInt:
205211
return parseInt(v);
206212
case $kindInt8:
@@ -259,12 +265,9 @@ var $internalize = function(v, t, recv) {
259265
if (t.methods.length !== 0) {
260266
$throwRuntimeError("cannot internalize " + t.string);
261267
}
262-
if (v === null) {
268+
if (v === null || v === undefined) {
263269
return $ifaceNil;
264270
}
265-
if (v === undefined) {
266-
return new $jsObjectPtr(undefined);
267-
}
268271
switch (v.constructor) {
269272
case Int8Array:
270273
return new ($sliceType($Int8))(v);
@@ -321,6 +324,12 @@ var $internalize = function(v, t, recv) {
321324
case $kindSlice:
322325
return new t($mapArray(v, function(e) { return $internalize(e, t.elem); }));
323326
case $kindString:
327+
if (v === undefined || v == null) {
328+
return "";
329+
}
330+
if (v.constructor !== String) {
331+
$throwRuntimeError("tried to internalize non-string value of type " + $typeof(v));
332+
}
324333
v = String(v);
325334
if ($isASCII(v)) {
326335
return v;
@@ -373,6 +382,33 @@ var $internalize = function(v, t, recv) {
373382
$throwRuntimeError("cannot internalize " + t.string);
374383
};
375384
385+
var $typeof = function(v) {
386+
if (v === undefined) {
387+
return "undefined";
388+
}
389+
390+
if (v === null) {
391+
return "null";
392+
}
393+
394+
var to = typeof v;
395+
396+
switch (to) {
397+
case "boolean":
398+
return to;
399+
case "number":
400+
return to;
401+
case "string":
402+
return to;
403+
case "symbol":
404+
return to;
405+
case "function":
406+
return to;
407+
default:
408+
return v.constructor.name;
409+
}
410+
}
411+
376412
/* $isASCII reports whether string s contains only ASCII characters. */
377413
var $isASCII = function(s) {
378414
for (var i = 0; i < s.length; i++) {

‎js/js_test.go

Copy file name to clipboardExpand all lines: js/js_test.go
+223-6Lines changed: 223 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -467,7 +467,7 @@ func TestCallWithNull(t *testing.T) {
467467
func TestReflection(t *testing.T) {
468468
o := js.Global.Call("eval", "({ answer: 42 })")
469469
if reflect.ValueOf(o).Interface().(*js.Object) != o {
470-
t.Fail()
470+
t.Fatal()
471471
}
472472

473473
type S struct {
@@ -477,18 +477,19 @@ func TestReflection(t *testing.T) {
477477

478478
v := reflect.ValueOf(&s).Elem()
479479
if v.Field(0).Interface().(*js.Object).Get("answer").Int() != 42 {
480-
t.Fail()
480+
t.Fatal()
481481
}
482482
if v.Field(0).MethodByName("Get").Call([]reflect.Value{reflect.ValueOf("answer")})[0].Interface().(*js.Object).Int() != 42 {
483-
t.Fail()
483+
t.Fatal()
484484
}
485485
v.Field(0).Set(reflect.ValueOf(js.Global.Call("eval", "({ answer: 100 })")))
486486
if s.Field.Get("answer").Int() != 100 {
487-
t.Fail()
487+
t.Fatal()
488488
}
489489

490-
if fmt.Sprintf("%+v", s) != "{Field:[object Object]}" {
491-
t.Fail()
490+
expFmt := "{Field:[object Object]}"
491+
if v := fmt.Sprintf("%+v", s); v != expFmt {
492+
t.Fatalf("fmt out was: %q; expected %q", v, expFmt)
492493
}
493494
}
494495

@@ -571,3 +572,219 @@ func TestUint8Array(t *testing.T) {
571572
t.Errorf("Non-empty byte array is not externalized as a Uint8Array")
572573
}
573574
}
575+
576+
type Internalize struct {
577+
*js.Object
578+
579+
string string `js:"string"`
580+
bool bool `js:"bool"`
581+
}
582+
583+
func TestInternalizeString(t *testing.T) {
584+
fieldName := "string"
585+
zero := ""
586+
587+
s := &Internalize{Object: js.Global.Get("Object").New()}
588+
589+
// *************
590+
// undefined
591+
// *************
592+
s.Object.Set(fieldName, jsundefined())
593+
594+
// via struct field
595+
if v := s.string; v != zero {
596+
t.Fatalf("expected string field to be %q, got %q", zero, v)
597+
}
598+
599+
// via *js.Object.String()
600+
if v := s.Object.Get(fieldName).String(); v != zero {
601+
t.Fatalf("expected string field via *js.Object.String() to be %q, got %q", zero, v)
602+
}
603+
604+
// *************
605+
// null
606+
// *************
607+
s.Object.Set(fieldName, jsnull())
608+
609+
// via struct field
610+
if v := s.string; v != zero {
611+
t.Fatalf("expected string field to be %q, got %q", zero, v)
612+
}
613+
614+
// via *js.Object.String()
615+
if v := s.Object.Get(fieldName).String(); v != zero {
616+
t.Fatalf("expected string field via *js.Object.String() to be %q, got %q", zero, v)
617+
}
618+
619+
// *************
620+
// valid primitive string
621+
// *************
622+
exp := "ok"
623+
s.Object.Set(fieldName, jsval(`"`+exp+`"`))
624+
625+
// via struct field
626+
if v := s.string; v != exp {
627+
t.Fatalf("expected string field to be %q, got %q", zero, v)
628+
}
629+
630+
// via *js.Object.String()
631+
if v := s.Object.Get(fieldName).String(); v != exp {
632+
t.Fatalf("expected string field via *js.Object.String() to be %q, got %q", zero, v)
633+
}
634+
635+
// *************
636+
// valid String object
637+
// *************
638+
s.Object.Set(fieldName, jsval(`new String("`+exp+`")`))
639+
640+
// via struct field
641+
if v := s.string; v != exp {
642+
t.Fatalf("expected string field to be %q, got %q", zero, v)
643+
}
644+
645+
// via *js.Object.String()
646+
if v := s.Object.Get(fieldName).String(); v != exp {
647+
t.Fatalf("expected string field via *js.Object.String() to be %q, got %q", zero, v)
648+
}
649+
650+
// *************
651+
// invalid
652+
// *************
653+
s.Object.Set(fieldName, jsval("5"))
654+
655+
shouldRuntimePanic(t, "runtime error: tried to internalize non-string value of type number", func() {
656+
_ = s.string
657+
})
658+
659+
shouldRuntimePanic(t, "runtime error: tried to internalize non-string value of type number", func() {
660+
_ = s.Object.Get(fieldName).String()
661+
})
662+
}
663+
664+
func TestInternalizeBool(t *testing.T) {
665+
fieldName := "bool"
666+
zero := false
667+
668+
s := &Internalize{Object: js.Global.Get("Object").New()}
669+
670+
// *************
671+
// undefined
672+
// *************
673+
s.Object.Set(fieldName, jsundefined())
674+
675+
// via struct field
676+
if v := s.bool; v != zero {
677+
t.Fatalf("expected bool field to be %q, got %q", zero, v)
678+
}
679+
680+
// via *js.Object.Bool()
681+
if v := s.Object.Get(fieldName).Bool(); v != zero {
682+
t.Fatalf("expected bool field via *js.Object.Bool() to be %q, got %q", zero, v)
683+
}
684+
685+
// *************
686+
// null
687+
// *************
688+
s.Object.Set(fieldName, jsnull())
689+
690+
// via struct field
691+
if v := s.bool; v != zero {
692+
t.Fatalf("expected bool field to be %q, got %q", zero, v)
693+
}
694+
695+
// via *js.Object.Bool()
696+
if v := s.Object.Get(fieldName).Bool(); v != zero {
697+
t.Fatalf("expected bool field via *js.Object.Bool() to be %q, got %q", zero, v)
698+
}
699+
700+
// *************
701+
// valid primitive bool
702+
// *************
703+
exp := true
704+
s.Object.Set(fieldName, jsval(`true`))
705+
706+
// via struct field
707+
if v := s.bool; v != exp {
708+
t.Fatalf("expected bool field to be %v, got %v", exp, v)
709+
}
710+
711+
// via *js.Object.Bool()
712+
if v := s.Object.Get(fieldName).Bool(); v != exp {
713+
t.Fatalf("expected bool field via *js.Object.Bool() to be %q, got %q", zero, v)
714+
}
715+
716+
// *************
717+
// valid Bool object
718+
// *************
719+
s.Object.Set(fieldName, jsval(`new Boolean(true)`))
720+
721+
// via struct field
722+
if v := s.bool; v != exp {
723+
t.Fatalf("expected bool field to be %q, got %q", zero, v)
724+
}
725+
726+
// via *js.Object.Bool()
727+
if v := s.Object.Get(fieldName).Bool(); v != exp {
728+
t.Fatalf("expected bool field via *js.Object.Bool() to be %q, got %q", zero, v)
729+
}
730+
731+
// *************
732+
// invalid
733+
// *************
734+
s.Object.Set(fieldName, jsval("5"))
735+
736+
shouldRuntimePanic(t, "runtime error: tried to internalize non-bool value of type number", func() {
737+
_ = s.bool
738+
})
739+
740+
shouldRuntimePanic(t, "runtime error: tried to internalize non-bool value of type number", func() {
741+
_ = s.Object.Get(fieldName).Bool()
742+
})
743+
}
744+
745+
func jsval(v string) *js.Object {
746+
return js.Global.Call("eval", v)
747+
}
748+
749+
func jsnull() *js.Object {
750+
return js.Global.Call("eval", "null")
751+
}
752+
753+
func jsundefined() *js.Object {
754+
return js.Global.Call("eval", "undefined")
755+
}
756+
757+
func consolelog(args ...interface{}) {
758+
js.Global.Get("console").Call("log", args...)
759+
}
760+
761+
func shouldRuntimePanic(t *testing.T, msg string, f func()) {
762+
defer func() {
763+
err, ok := recover().(error)
764+
if !ok {
765+
t.Fatalf("expected to have had to handle panic; we didn't see a panic")
766+
}
767+
768+
if err.Error() != msg {
769+
t.Fatalf("expected error %q, got %q", msg, err)
770+
}
771+
}()
772+
773+
f()
774+
}
775+
776+
// |----------------+-------------------------+---------------+--------|
777+
// | Go target type | Javascript source value | Translation | Result |
778+
// |----------------+-------------------------+---------------+--------|
779+
// | string | null | UTF16 -> UTF8 | "" |
780+
// | | undefined | | "" |
781+
// | | "" | | "" |
782+
// | | new String("") | | "" |
783+
// | | "ok" † | | "ok" |
784+
// | | new String("ok") † | | "ok" |
785+
// |----------------+-------------------------+---------------+--------|
786+
// | bool | null | none | false |
787+
// | | undefined | | false |
788+
// | | false † | | false |
789+
// | | new Boolean(false) † | | false |
790+
// |----------------+-------------------------+---------------+--------|

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.