Skip to content

Commit

Permalink
Propsed fix for struct and array vals in interface-typed variables
Browse files Browse the repository at this point in the history
  • Loading branch information
myitcv committed Aug 1, 2017
1 parent 2b1d432 commit a217ebf
Show file tree
Hide file tree
Showing 5 changed files with 129 additions and 5 deletions.
3 changes: 2 additions & 1 deletion circle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ machine:
version: 6.2.2
environment:
SOURCE_MAP_SUPPORT: false
GOPHERJS_STACK_SIZE: 10000

dependencies:
pre:
Expand All @@ -18,7 +19,7 @@ test:
- go tool vet *.go # Go package in root directory.
- for d in */; do echo $d; done | grep -v tests/ | grep -v third_party/ | xargs go tool vet # All subdirectories except "tests", "third_party".
- >
gopherjs test --short --minify
ulimit -s $GOPHERJS_STACK_SIZE && ulimit -s && gopherjs test --short --minify
github.com/gopherjs/gopherjs/tests
github.com/gopherjs/gopherjs/tests/main
github.com/gopherjs/gopherjs/js
Expand Down
17 changes: 14 additions & 3 deletions compiler/expressions.go
Original file line number Diff line number Diff line change
Expand Up @@ -1112,6 +1112,12 @@ func (c *funcContext) translateImplicitConversion(expr ast.Expr, desiredType typ
}

exprType := c.p.TypeOf(expr)

// whenever expr has an underlying *types.Interface type we need to $copyIntf
if _, isIntf := exprType.Underlying().(*types.Interface); isIntf {
return c.formatExpr("$copyIntf(%e)", expr)
}

if types.Identical(exprType, desiredType) {
return c.translateExpr(expr)
}
Expand All @@ -1130,12 +1136,17 @@ func (c *funcContext) translateImplicitConversion(expr ast.Expr, desiredType typ
// wrap JS object into js.Object struct when converting to interface
return c.formatExpr("new $jsObjectPtr(%e)", expr)
}

switch exprType.Underlying().(type) {
case *types.Array:
return c.formatExpr("new %1s($clone(%e, %1s))", c.typeName(exprType), expr)
case *types.Struct:
return c.formatExpr("new %1e.constructor.elem($clone(%1e, %s))", expr, c.typeName(exprType))
}

if isWrapped(exprType) {
return c.formatExpr("new %s(%e)", c.typeName(exprType), expr)
}
if _, isStruct := exprType.Underlying().(*types.Struct); isStruct {
return c.formatExpr("new %1e.constructor.elem(%1e)", expr)
}
}

return c.translateExpr(expr)
Expand Down
8 changes: 8 additions & 0 deletions compiler/prelude/prelude.go
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,14 @@ var $clone = function(src, type) {
return clone;
};
var $copyIntf = function(src) {
if (src.constructor.copy) {
return new src.constructor($clone(src.$val, src.constructor));
}
return src;
};
var $pointerOfStructConversion = function(obj, type) {
if(obj.$proxies === undefined) {
obj.$proxies = {};
Expand Down
88 changes: 88 additions & 0 deletions tests/interface_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package tests

import (
"fmt"
"testing"
)

type Struct struct {
Name string
}

func (s Struct) SetName(n string) {
s.Name = n
}

type SetName interface {
SetName(n string)
}

func TestAssignStructValInterface(t *testing.T) {
s := Struct{
Name: "Rob",
}

var i1 interface{} = s
var i2 interface{} = i1

s.Name = "Pike"

ss := fmt.Sprintf("%#v", s)
i1s := fmt.Sprintf("%#v", i1)
i2s := fmt.Sprintf("%#v", i2)

if exp := "tests.Struct{Name:\"Pike\"}"; ss != exp {
t.Fatalf("ss should have been %q; got %q", exp, ss)
}

iexp := "tests.Struct{Name:\"Rob\"}"

if i1s != iexp {
t.Fatalf("is should have been %q; got %q", iexp, i1s)
}

if i2s != iexp {
t.Fatalf("is should have been %q; got %q", iexp, i2s)
}
}

func TestStructValIntfMethodCall(t *testing.T) {
var i SetName = Struct{
Name: "Rob",
}

i.SetName("Pike")

is := fmt.Sprintf("%#v", i)

if exp := "tests.Struct{Name:\"Rob\"}"; is != exp {
t.Fatalf("is should have been %q; got %q", exp, is)
}
}

func TestAssignArrayInterface(t *testing.T) {
a := [2]int{1, 2}

var i1 interface{} = a
var i2 interface{} = i1

a[0] = 0

as := fmt.Sprintf("%#v", a)
i1s := fmt.Sprintf("%#v", i1)
i2s := fmt.Sprintf("%#v", i2)

if exp := "[2]int{0, 2}"; as != exp {
t.Fatalf("ss should have been %q; got %q", exp, as)
}

iexp := "[2]int{1, 2}"

if i1s != iexp {
t.Fatalf("is should have been %q; got %q", iexp, i1s)
}

if i2s != iexp {
t.Fatalf("is should have been %q; got %q", iexp, i2s)
}
}
18 changes: 17 additions & 1 deletion tool.go
Original file line number Diff line number Diff line change
Expand Up @@ -749,8 +749,24 @@ func runNode(script string, args []string, dir string, quiet bool) error {
}
}

// set a default stack size;
//
// Per https://github.com/nodejs/node/issues/14567#issuecomment-319367412
// ulimit -s needs to be at least this value
//
// because this probably isn't critical for everyday usage (it's only a real
// issue with massively recursive code, like the tests in text/template) we
// don't test to see if this is the case. Instead we ensure it's the case
// via GOPHERJS_STACK_SIZE which is set as part of the CI build
//
stackSize := "10000"

if ss, ok := os.LookupEnv("GOPHERJS_STACK_SIZE"); ok {
stackSize = ss
}

if runtime.GOOS != "windows" {
allArgs = append(allArgs, "--stack_size=10000", script)
allArgs = append(allArgs, "--stack_size="+stackSize, script)
}

allArgs = append(allArgs, args...)
Expand Down

0 comments on commit a217ebf

Please sign in to comment.