github.com/goplusjs/gopherjs@v1.2.6-0.20211206034512-f187917453b8/compiler/statements.go (about)

     1  package compiler
     2  
     3  import (
     4  	"fmt"
     5  	"go/ast"
     6  	"go/constant"
     7  	"go/token"
     8  	"go/types"
     9  	"strings"
    10  
    11  	"github.com/goplusjs/gopherjs/compiler/analysis"
    12  	"github.com/goplusjs/gopherjs/compiler/astutil"
    13  	"github.com/goplusjs/gopherjs/compiler/filter"
    14  	"github.com/goplusjs/gopherjs/compiler/typesutil"
    15  )
    16  
    17  func (c *funcContext) translateStmtList(stmts []ast.Stmt) {
    18  	for _, stmt := range stmts {
    19  		c.translateStmt(stmt, nil)
    20  	}
    21  	c.SetPos(token.NoPos)
    22  }
    23  
    24  func (c *funcContext) translateStmt(stmt ast.Stmt, label *types.Label) {
    25  	c.SetPos(stmt.Pos())
    26  
    27  	stmt = filter.IncDecStmt(stmt, c.p.Info.Info)
    28  	stmt = filter.Assign(stmt, c.p.Info.Info, c.p.Info.Pkg)
    29  
    30  	switch s := stmt.(type) {
    31  	case *ast.BlockStmt:
    32  		c.translateStmtList(s.List)
    33  
    34  	case *ast.IfStmt:
    35  		var caseClauses []*ast.CaseClause
    36  		ifStmt := s
    37  		for {
    38  			if ifStmt.Init != nil {
    39  				panic("simplification error")
    40  			}
    41  			caseClauses = append(caseClauses, &ast.CaseClause{List: []ast.Expr{ifStmt.Cond}, Body: ifStmt.Body.List})
    42  			elseStmt, ok := ifStmt.Else.(*ast.IfStmt)
    43  			if !ok {
    44  				break
    45  			}
    46  			ifStmt = elseStmt
    47  		}
    48  		var defaultClause *ast.CaseClause
    49  		if block, ok := ifStmt.Else.(*ast.BlockStmt); ok {
    50  			defaultClause = &ast.CaseClause{Body: block.List}
    51  		}
    52  		c.translateBranchingStmt(caseClauses, defaultClause, false, c.translateExpr, nil, c.Flattened[s])
    53  
    54  	case *ast.SwitchStmt:
    55  		if s.Init != nil || s.Tag != nil || len(s.Body.List) != 1 {
    56  			panic("simplification error")
    57  		}
    58  		clause := s.Body.List[0].(*ast.CaseClause)
    59  		if len(clause.List) != 0 {
    60  			panic("simplification error")
    61  		}
    62  
    63  		prevFlowData := c.flowDatas[nil]
    64  		data := &flowData{
    65  			postStmt:  prevFlowData.postStmt,  // for "continue" of outer loop
    66  			beginCase: prevFlowData.beginCase, // same
    67  		}
    68  		c.flowDatas[nil] = data
    69  		c.flowDatas[label] = data
    70  		defer func() {
    71  			delete(c.flowDatas, label)
    72  			c.flowDatas[nil] = prevFlowData
    73  		}()
    74  
    75  		if c.Flattened[s] {
    76  			data.endCase = c.caseCounter
    77  			c.caseCounter++
    78  
    79  			c.Indent(func() {
    80  				c.translateStmtList(clause.Body)
    81  			})
    82  			c.Printf("case %d:", data.endCase)
    83  			return
    84  		}
    85  
    86  		if label != nil || analysis.HasBreak(clause) {
    87  			if label != nil {
    88  				c.Printf("%s:", label.Name())
    89  			}
    90  			c.Printf("switch (0) { default:")
    91  			c.Indent(func() {
    92  				c.translateStmtList(clause.Body)
    93  			})
    94  			c.Printf("}")
    95  			return
    96  		}
    97  
    98  		c.translateStmtList(clause.Body)
    99  
   100  	case *ast.TypeSwitchStmt:
   101  		if s.Init != nil {
   102  			c.translateStmt(s.Init, nil)
   103  		}
   104  		refVar := c.newVariable("_ref")
   105  		var expr ast.Expr
   106  		switch a := s.Assign.(type) {
   107  		case *ast.AssignStmt:
   108  			expr = a.Rhs[0].(*ast.TypeAssertExpr).X
   109  		case *ast.ExprStmt:
   110  			expr = a.X.(*ast.TypeAssertExpr).X
   111  		}
   112  		c.Printf("%s = %s;", refVar, c.translateExpr(expr))
   113  		translateCond := func(cond ast.Expr) *expression {
   114  			if types.Identical(c.p.TypeOf(cond), types.Typ[types.UntypedNil]) {
   115  				return c.formatExpr("%s === $ifaceNil", refVar)
   116  			}
   117  			return c.formatExpr("$assertType(%s, %s, true)[1]", refVar, c.typeName(c.p.TypeOf(cond)))
   118  		}
   119  		var caseClauses []*ast.CaseClause
   120  		var defaultClause *ast.CaseClause
   121  		for _, cc := range s.Body.List {
   122  			clause := cc.(*ast.CaseClause)
   123  			var bodyPrefix []ast.Stmt
   124  			if implicit := c.p.Implicits[clause]; implicit != nil {
   125  				value := refVar
   126  				if typesutil.IsJsObject(implicit.Type().Underlying()) {
   127  					value += ".$val.object"
   128  				} else if _, ok := implicit.Type().Underlying().(*types.Interface); !ok {
   129  					value += ".$val"
   130  				}
   131  				bodyPrefix = []ast.Stmt{&ast.AssignStmt{
   132  					Lhs: []ast.Expr{c.newIdent(c.objectName(implicit), implicit.Type())},
   133  					Tok: token.DEFINE,
   134  					Rhs: []ast.Expr{c.newIdent(value, implicit.Type())},
   135  				}}
   136  			}
   137  			c := &ast.CaseClause{
   138  				List: clause.List,
   139  				Body: append(bodyPrefix, clause.Body...),
   140  			}
   141  			if len(c.List) == 0 {
   142  				defaultClause = c
   143  				continue
   144  			}
   145  			caseClauses = append(caseClauses, c)
   146  		}
   147  		c.translateBranchingStmt(caseClauses, defaultClause, true, translateCond, label, c.Flattened[s])
   148  
   149  	case *ast.ForStmt:
   150  		if s.Init != nil {
   151  			c.translateStmt(s.Init, nil)
   152  		}
   153  		cond := func() string {
   154  			if s.Cond == nil {
   155  				return "true"
   156  			}
   157  			return c.translateExpr(s.Cond).String()
   158  		}
   159  		c.translateLoopingStmt(cond, s.Body, nil, func() {
   160  			if s.Post != nil {
   161  				c.translateStmt(s.Post, nil)
   162  			}
   163  		}, label, c.Flattened[s])
   164  
   165  	case *ast.RangeStmt:
   166  		refVar := c.newVariable("_ref")
   167  		c.Printf("%s = %s;", refVar, c.translateExpr(s.X))
   168  
   169  		switch t := c.p.TypeOf(s.X).Underlying().(type) {
   170  		case *types.Basic:
   171  			iVar := c.newVariable("_i")
   172  			c.Printf("%s = 0;", iVar)
   173  			runeVar := c.newVariable("_rune")
   174  			c.translateLoopingStmt(func() string { return iVar + " < " + refVar + ".length" }, s.Body, func() {
   175  				c.Printf("%s = $decodeRune(%s, %s);", runeVar, refVar, iVar)
   176  				if !isBlank(s.Key) {
   177  					c.Printf("%s", c.translateAssign(s.Key, c.newIdent(iVar, types.Typ[types.Int]), s.Tok == token.DEFINE))
   178  				}
   179  				if !isBlank(s.Value) {
   180  					c.Printf("%s", c.translateAssign(s.Value, c.newIdent(runeVar+"[0]", types.Typ[types.Rune]), s.Tok == token.DEFINE))
   181  				}
   182  			}, func() {
   183  				c.Printf("%s += %s[1];", iVar, runeVar)
   184  			}, label, c.Flattened[s])
   185  
   186  		case *types.Map:
   187  			iVar := c.newVariable("_i")
   188  			c.Printf("%s = 0;", iVar)
   189  			keysVar := c.newVariable("_keys")
   190  			c.Printf("%s = $keys(%s);", keysVar, refVar)
   191  			c.translateLoopingStmt(func() string { return iVar + " < " + keysVar + ".length" }, s.Body, func() {
   192  				entryVar := c.newVariable("_entry")
   193  				c.Printf("%s = %s[%s[%s]];", entryVar, refVar, keysVar, iVar)
   194  				c.translateStmt(&ast.IfStmt{
   195  					Cond: c.newIdent(entryVar+" === undefined", types.Typ[types.Bool]),
   196  					Body: &ast.BlockStmt{List: []ast.Stmt{&ast.BranchStmt{Tok: token.CONTINUE}}},
   197  				}, nil)
   198  				if !isBlank(s.Key) {
   199  					c.Printf("%s", c.translateAssign(s.Key, c.newIdent(entryVar+".k", t.Key()), s.Tok == token.DEFINE))
   200  				}
   201  				if !isBlank(s.Value) {
   202  					c.Printf("%s", c.translateAssign(s.Value, c.newIdent(entryVar+".v", t.Elem()), s.Tok == token.DEFINE))
   203  				}
   204  			}, func() {
   205  				c.Printf("%s++;", iVar)
   206  			}, label, c.Flattened[s])
   207  
   208  		case *types.Array, *types.Pointer, *types.Slice:
   209  			var length string
   210  			var elemType types.Type
   211  			switch t2 := t.(type) {
   212  			case *types.Array:
   213  				length = fmt.Sprintf("%d", t2.Len())
   214  				elemType = t2.Elem()
   215  			case *types.Pointer:
   216  				length = fmt.Sprintf("%d", t2.Elem().Underlying().(*types.Array).Len())
   217  				elemType = t2.Elem().Underlying().(*types.Array).Elem()
   218  			case *types.Slice:
   219  				length = refVar + ".$length"
   220  				elemType = t2.Elem()
   221  			}
   222  			iVar := c.newVariable("_i")
   223  			c.Printf("%s = 0;", iVar)
   224  			c.translateLoopingStmt(func() string { return iVar + " < " + length }, s.Body, func() {
   225  				if !isBlank(s.Key) {
   226  					c.Printf("%s", c.translateAssign(s.Key, c.newIdent(iVar, types.Typ[types.Int]), s.Tok == token.DEFINE))
   227  				}
   228  				if !isBlank(s.Value) {
   229  					c.Printf("%s", c.translateAssign(s.Value, c.setType(&ast.IndexExpr{
   230  						X:     c.newIdent(refVar, t),
   231  						Index: c.newIdent(iVar, types.Typ[types.Int]),
   232  					}, elemType), s.Tok == token.DEFINE))
   233  				}
   234  			}, func() {
   235  				c.Printf("%s++;", iVar)
   236  			}, label, c.Flattened[s])
   237  
   238  		case *types.Chan:
   239  			okVar := c.newIdent(c.newVariable("_ok"), types.Typ[types.Bool])
   240  			key := s.Key
   241  			tok := s.Tok
   242  			if key == nil {
   243  				key = ast.NewIdent("_")
   244  				tok = token.ASSIGN
   245  			}
   246  			forStmt := &ast.ForStmt{
   247  				Body: &ast.BlockStmt{
   248  					List: []ast.Stmt{
   249  						&ast.AssignStmt{
   250  							Lhs: []ast.Expr{
   251  								key,
   252  								okVar,
   253  							},
   254  							Rhs: []ast.Expr{
   255  								c.setType(&ast.UnaryExpr{X: c.newIdent(refVar, t), Op: token.ARROW}, types.NewTuple(types.NewVar(0, nil, "", t.Elem()), types.NewVar(0, nil, "", types.Typ[types.Bool]))),
   256  							},
   257  							Tok: tok,
   258  						},
   259  						&ast.IfStmt{
   260  							Cond: &ast.UnaryExpr{X: okVar, Op: token.NOT},
   261  							Body: &ast.BlockStmt{List: []ast.Stmt{&ast.BranchStmt{Tok: token.BREAK}}},
   262  						},
   263  						s.Body,
   264  					},
   265  				},
   266  			}
   267  			c.Flattened[forStmt] = true
   268  			c.translateStmt(forStmt, label)
   269  
   270  		default:
   271  			panic("")
   272  		}
   273  
   274  	case *ast.BranchStmt:
   275  		normalLabel := ""
   276  		blockingLabel := ""
   277  		data := c.flowDatas[nil]
   278  		if s.Label != nil {
   279  			normalLabel = " " + s.Label.Name
   280  			blockingLabel = " s" // use explicit label "s", because surrounding loop may not be flattened
   281  			data = c.flowDatas[c.p.Uses[s.Label].(*types.Label)]
   282  		}
   283  		switch s.Tok {
   284  		case token.BREAK:
   285  			c.PrintCond(data.endCase == 0, fmt.Sprintf("break%s;", normalLabel), fmt.Sprintf("$s = %d; continue%s;", data.endCase, blockingLabel))
   286  		case token.CONTINUE:
   287  			data.postStmt()
   288  			c.PrintCond(data.beginCase == 0, fmt.Sprintf("continue%s;", normalLabel), fmt.Sprintf("$s = %d; continue%s;", data.beginCase, blockingLabel))
   289  		case token.GOTO:
   290  			c.PrintCond(false, "goto "+s.Label.Name, fmt.Sprintf("$s = %d; continue;", c.labelCase(c.p.Uses[s.Label].(*types.Label))))
   291  		case token.FALLTHROUGH:
   292  			// handled in CaseClause
   293  		default:
   294  			panic("Unhandled branch statment: " + s.Tok.String())
   295  		}
   296  
   297  	case *ast.ReturnStmt:
   298  		results := s.Results
   299  		if c.resultNames != nil {
   300  			if len(s.Results) != 0 {
   301  				c.translateStmt(&ast.AssignStmt{
   302  					Lhs: c.resultNames,
   303  					Tok: token.ASSIGN,
   304  					Rhs: s.Results,
   305  				}, nil)
   306  			}
   307  			results = c.resultNames
   308  		}
   309  		rVal := c.translateResults(results)
   310  		if len(c.Flattened) != 0 {
   311  			c.Printf("$s = -1; return%s;", rVal)
   312  			return
   313  		}
   314  		c.Printf("return%s;", rVal)
   315  
   316  	case *ast.DeferStmt:
   317  		isBuiltin := false
   318  		isJs := false
   319  		switch fun := s.Call.Fun.(type) {
   320  		case *ast.Ident:
   321  			var builtin *types.Builtin
   322  			builtin, isBuiltin = c.p.Uses[fun].(*types.Builtin)
   323  			if isBuiltin && builtin.Name() == "recover" {
   324  				c.Printf("$deferred.push([$recover, []]);")
   325  				return
   326  			}
   327  		case *ast.SelectorExpr:
   328  			isJs = typesutil.IsJsPackage(c.p.Uses[fun.Sel].Pkg())
   329  		}
   330  		sig := c.p.TypeOf(s.Call.Fun).Underlying().(*types.Signature)
   331  		args := c.translateArgs(sig, s.Call.Args, s.Call.Ellipsis.IsValid())
   332  		if isBuiltin || isJs {
   333  			vars := make([]string, len(s.Call.Args))
   334  			callArgs := make([]ast.Expr, len(s.Call.Args))
   335  			for i, arg := range s.Call.Args {
   336  				v := c.newVariable("_arg")
   337  				vars[i] = v
   338  				callArgs[i] = c.newIdent(v, c.p.TypeOf(arg))
   339  			}
   340  			call := c.translateExpr(&ast.CallExpr{
   341  				Fun:      s.Call.Fun,
   342  				Args:     callArgs,
   343  				Ellipsis: s.Call.Ellipsis,
   344  			})
   345  			c.Printf("$deferred.push([function(%s) { %s; }, [%s]]);", strings.Join(vars, ", "), call, strings.Join(args, ", "))
   346  			return
   347  		}
   348  		c.Printf("$deferred.push([%s, [%s]]);", c.translateExpr(s.Call.Fun), strings.Join(args, ", "))
   349  
   350  	case *ast.AssignStmt:
   351  		if s.Tok != token.ASSIGN && s.Tok != token.DEFINE {
   352  			panic(s.Tok)
   353  		}
   354  
   355  		switch {
   356  		case len(s.Lhs) == 1 && len(s.Rhs) == 1:
   357  			lhs := astutil.RemoveParens(s.Lhs[0])
   358  			if isBlank(lhs) {
   359  				c.Printf("$unused(%s);", c.translateExpr(s.Rhs[0]))
   360  				return
   361  			}
   362  			c.Printf("%s", c.translateAssign(lhs, s.Rhs[0], s.Tok == token.DEFINE))
   363  
   364  		case len(s.Lhs) > 1 && len(s.Rhs) == 1:
   365  			tupleVar := c.newVariable("_tuple")
   366  			c.Printf("%s = %s;", tupleVar, c.translateExpr(s.Rhs[0]))
   367  			tuple := c.p.TypeOf(s.Rhs[0]).(*types.Tuple)
   368  			for i, lhs := range s.Lhs {
   369  				lhs = astutil.RemoveParens(lhs)
   370  				if !isBlank(lhs) {
   371  					c.Printf("%s", c.translateAssign(lhs, c.newIdent(fmt.Sprintf("%s[%d]", tupleVar, i), tuple.At(i).Type()), s.Tok == token.DEFINE))
   372  				}
   373  			}
   374  		case len(s.Lhs) == len(s.Rhs):
   375  			tmpVars := make([]string, len(s.Rhs))
   376  			for i, rhs := range s.Rhs {
   377  				tmpVars[i] = c.newVariable("_tmp")
   378  				if isBlank(astutil.RemoveParens(s.Lhs[i])) {
   379  					c.Printf("$unused(%s);", c.translateExpr(rhs))
   380  					continue
   381  				}
   382  				c.Printf("%s", c.translateAssign(c.newIdent(tmpVars[i], c.p.TypeOf(s.Lhs[i])), rhs, true))
   383  			}
   384  			for i, lhs := range s.Lhs {
   385  				lhs = astutil.RemoveParens(lhs)
   386  				if !isBlank(lhs) {
   387  					c.Printf("%s", c.translateAssign(lhs, c.newIdent(tmpVars[i], c.p.TypeOf(lhs)), s.Tok == token.DEFINE))
   388  				}
   389  			}
   390  
   391  		default:
   392  			panic("Invalid arity of AssignStmt.")
   393  
   394  		}
   395  
   396  	case *ast.DeclStmt:
   397  		decl := s.Decl.(*ast.GenDecl)
   398  		switch decl.Tok {
   399  		case token.VAR:
   400  			for _, spec := range s.Decl.(*ast.GenDecl).Specs {
   401  				valueSpec := spec.(*ast.ValueSpec)
   402  				lhs := make([]ast.Expr, len(valueSpec.Names))
   403  				for i, name := range valueSpec.Names {
   404  					lhs[i] = name
   405  				}
   406  				rhs := valueSpec.Values
   407  				if len(rhs) == 0 {
   408  					rhs = make([]ast.Expr, len(lhs))
   409  					for i, e := range lhs {
   410  						rhs[i] = c.zeroValue(c.p.TypeOf(e))
   411  					}
   412  				}
   413  				c.translateStmt(&ast.AssignStmt{
   414  					Lhs: lhs,
   415  					Tok: token.DEFINE,
   416  					Rhs: rhs,
   417  				}, nil)
   418  			}
   419  		case token.TYPE:
   420  			for _, spec := range decl.Specs {
   421  				o := c.p.Defs[spec.(*ast.TypeSpec).Name].(*types.TypeName)
   422  				c.p.typeNames = append(c.p.typeNames, o)
   423  				c.p.objectNames[o] = c.newVariableWithLevel(o.Name(), true)
   424  				c.p.dependencies[o] = true
   425  			}
   426  		case token.CONST:
   427  			// skip, constants are inlined
   428  		}
   429  
   430  	case *ast.ExprStmt:
   431  		expr := c.translateExpr(s.X)
   432  		if expr != nil && expr.String() != "" {
   433  			c.Printf("%s;", expr)
   434  		}
   435  
   436  	case *ast.LabeledStmt:
   437  		label := c.p.Defs[s.Label].(*types.Label)
   438  		if c.GotoLabel[label] {
   439  			c.PrintCond(false, s.Label.Name+":", fmt.Sprintf("case %d:", c.labelCase(label)))
   440  		}
   441  		c.translateStmt(s.Stmt, label)
   442  
   443  	case *ast.GoStmt:
   444  		c.Printf("$go(%s, [%s]);", c.translateExpr(s.Call.Fun), strings.Join(c.translateArgs(c.p.TypeOf(s.Call.Fun).Underlying().(*types.Signature), s.Call.Args, s.Call.Ellipsis.IsValid()), ", "))
   445  
   446  	case *ast.SendStmt:
   447  		chanType := c.p.TypeOf(s.Chan).Underlying().(*types.Chan)
   448  		call := &ast.CallExpr{
   449  			Fun:  c.newIdent("$send", types.NewSignature(nil, types.NewTuple(types.NewVar(0, nil, "", chanType), types.NewVar(0, nil, "", chanType.Elem())), nil, false)),
   450  			Args: []ast.Expr{s.Chan, c.newIdent(c.translateImplicitConversionWithCloning(s.Value, chanType.Elem()).String(), chanType.Elem())},
   451  		}
   452  		c.Blocking[call] = true
   453  		c.translateStmt(&ast.ExprStmt{X: call}, label)
   454  
   455  	case *ast.SelectStmt:
   456  		selectionVar := c.newVariable("_selection")
   457  		var channels []string
   458  		var caseClauses []*ast.CaseClause
   459  		flattened := false
   460  		hasDefault := false
   461  		for i, cc := range s.Body.List {
   462  			clause := cc.(*ast.CommClause)
   463  			switch comm := clause.Comm.(type) {
   464  			case nil:
   465  				channels = append(channels, "[]")
   466  				hasDefault = true
   467  			case *ast.ExprStmt:
   468  				channels = append(channels, c.formatExpr("[%e]", astutil.RemoveParens(comm.X).(*ast.UnaryExpr).X).String())
   469  			case *ast.AssignStmt:
   470  				channels = append(channels, c.formatExpr("[%e]", astutil.RemoveParens(comm.Rhs[0]).(*ast.UnaryExpr).X).String())
   471  			case *ast.SendStmt:
   472  				chanType := c.p.TypeOf(comm.Chan).Underlying().(*types.Chan)
   473  				channels = append(channels, c.formatExpr("[%e, %s]", comm.Chan, c.translateImplicitConversionWithCloning(comm.Value, chanType.Elem())).String())
   474  			default:
   475  				panic(fmt.Sprintf("unhandled: %T", comm))
   476  			}
   477  
   478  			indexLit := &ast.BasicLit{Kind: token.INT}
   479  			c.p.Types[indexLit] = types.TypeAndValue{Type: types.Typ[types.Int], Value: constant.MakeInt64(int64(i))}
   480  
   481  			var bodyPrefix []ast.Stmt
   482  			if assign, ok := clause.Comm.(*ast.AssignStmt); ok {
   483  				switch rhsType := c.p.TypeOf(assign.Rhs[0]).(type) {
   484  				case *types.Tuple:
   485  					bodyPrefix = []ast.Stmt{&ast.AssignStmt{Lhs: assign.Lhs, Rhs: []ast.Expr{c.newIdent(selectionVar+"[1]", rhsType)}, Tok: assign.Tok}}
   486  				default:
   487  					bodyPrefix = []ast.Stmt{&ast.AssignStmt{Lhs: assign.Lhs, Rhs: []ast.Expr{c.newIdent(selectionVar+"[1][0]", rhsType)}, Tok: assign.Tok}}
   488  				}
   489  			}
   490  
   491  			caseClauses = append(caseClauses, &ast.CaseClause{
   492  				List: []ast.Expr{indexLit},
   493  				Body: append(bodyPrefix, clause.Body...),
   494  			})
   495  
   496  			flattened = flattened || c.Flattened[clause]
   497  		}
   498  
   499  		selectCall := c.setType(&ast.CallExpr{
   500  			Fun:  c.newIdent("$select", types.NewSignature(nil, types.NewTuple(types.NewVar(0, nil, "", types.NewInterface(nil, nil))), types.NewTuple(types.NewVar(0, nil, "", types.Typ[types.Int])), false)),
   501  			Args: []ast.Expr{c.newIdent(fmt.Sprintf("[%s]", strings.Join(channels, ", ")), types.NewInterface(nil, nil))},
   502  		}, types.Typ[types.Int])
   503  		c.Blocking[selectCall] = !hasDefault
   504  		c.Printf("%s = %s;", selectionVar, c.translateExpr(selectCall))
   505  
   506  		if len(caseClauses) != 0 {
   507  			translateCond := func(cond ast.Expr) *expression {
   508  				return c.formatExpr("%s[0] === %e", selectionVar, cond)
   509  			}
   510  			c.translateBranchingStmt(caseClauses, nil, true, translateCond, label, flattened)
   511  		}
   512  
   513  	case *ast.EmptyStmt:
   514  		// skip
   515  
   516  	default:
   517  		panic(fmt.Sprintf("Unhandled statement: %T\n", s))
   518  
   519  	}
   520  }
   521  
   522  func (c *funcContext) translateBranchingStmt(caseClauses []*ast.CaseClause, defaultClause *ast.CaseClause, canBreak bool, translateCond func(ast.Expr) *expression, label *types.Label, flatten bool) {
   523  	var caseOffset, defaultCase, endCase int
   524  	if flatten {
   525  		caseOffset = c.caseCounter
   526  		defaultCase = caseOffset + len(caseClauses)
   527  		endCase = defaultCase
   528  		if defaultClause != nil {
   529  			endCase++
   530  		}
   531  		c.caseCounter = endCase + 1
   532  	}
   533  
   534  	hasBreak := false
   535  	if canBreak {
   536  		prevFlowData := c.flowDatas[nil]
   537  		data := &flowData{
   538  			postStmt:  prevFlowData.postStmt,  // for "continue" of outer loop
   539  			beginCase: prevFlowData.beginCase, // same
   540  			endCase:   endCase,
   541  		}
   542  		c.flowDatas[nil] = data
   543  		c.flowDatas[label] = data
   544  		defer func() {
   545  			delete(c.flowDatas, label)
   546  			c.flowDatas[nil] = prevFlowData
   547  		}()
   548  
   549  		for _, child := range caseClauses {
   550  			if analysis.HasBreak(child) {
   551  				hasBreak = true
   552  				break
   553  			}
   554  		}
   555  		if defaultClause != nil && analysis.HasBreak(defaultClause) {
   556  			hasBreak = true
   557  		}
   558  	}
   559  
   560  	if label != nil && !flatten {
   561  		c.Printf("%s:", label.Name())
   562  	}
   563  
   564  	condStrs := make([]string, len(caseClauses))
   565  	for i, clause := range caseClauses {
   566  		conds := make([]string, len(clause.List))
   567  		for j, cond := range clause.List {
   568  			conds[j] = translateCond(cond).String()
   569  		}
   570  		condStrs[i] = strings.Join(conds, " || ")
   571  		if flatten {
   572  			c.Printf("/* */ if (%s) { $s = %d; continue; }", condStrs[i], caseOffset+i)
   573  		}
   574  	}
   575  
   576  	if flatten {
   577  		c.Printf("/* */ $s = %d; continue;", defaultCase)
   578  	}
   579  
   580  	prefix := ""
   581  	suffix := ""
   582  	if label != nil || hasBreak {
   583  		prefix = "switch (0) { default: "
   584  		suffix = " }"
   585  	}
   586  
   587  	for i, clause := range caseClauses {
   588  		c.SetPos(clause.Pos())
   589  		c.PrintCond(!flatten, fmt.Sprintf("%sif (%s) {", prefix, condStrs[i]), fmt.Sprintf("case %d:", caseOffset+i))
   590  		c.Indent(func() {
   591  			c.translateStmtList(clause.Body)
   592  			if flatten && (i < len(caseClauses)-1 || defaultClause != nil) && !endsWithReturn(clause.Body) {
   593  				c.Printf("$s = %d; continue;", endCase)
   594  			}
   595  		})
   596  		prefix = "} else "
   597  	}
   598  
   599  	if defaultClause != nil {
   600  		c.PrintCond(!flatten, prefix+"{", fmt.Sprintf("case %d:", caseOffset+len(caseClauses)))
   601  		c.Indent(func() {
   602  			c.translateStmtList(defaultClause.Body)
   603  		})
   604  	}
   605  
   606  	c.PrintCond(!flatten, "}"+suffix, fmt.Sprintf("case %d:", endCase))
   607  }
   608  
   609  func (c *funcContext) translateLoopingStmt(cond func() string, body *ast.BlockStmt, bodyPrefix, post func(), label *types.Label, flatten bool) {
   610  	prevFlowData := c.flowDatas[nil]
   611  	data := &flowData{
   612  		postStmt: post,
   613  	}
   614  	if flatten {
   615  		data.beginCase = c.caseCounter
   616  		data.endCase = c.caseCounter + 1
   617  		c.caseCounter += 2
   618  	}
   619  	c.flowDatas[nil] = data
   620  	c.flowDatas[label] = data
   621  	defer func() {
   622  		delete(c.flowDatas, label)
   623  		c.flowDatas[nil] = prevFlowData
   624  	}()
   625  
   626  	if !flatten && label != nil {
   627  		c.Printf("%s:", label.Name())
   628  	}
   629  	c.PrintCond(!flatten, "while (true) {", fmt.Sprintf("case %d:", data.beginCase))
   630  	c.Indent(func() {
   631  		condStr := cond()
   632  		if condStr != "true" {
   633  			c.PrintCond(!flatten, fmt.Sprintf("if (!(%s)) { break; }", condStr), fmt.Sprintf("if(!(%s)) { $s = %d; continue; }", condStr, data.endCase))
   634  		}
   635  
   636  		prevEV := c.p.escapingVars
   637  		c.handleEscapingVars(body)
   638  
   639  		if bodyPrefix != nil {
   640  			bodyPrefix()
   641  		}
   642  		c.translateStmtList(body.List)
   643  		isTerminated := false
   644  		if len(body.List) != 0 {
   645  			switch body.List[len(body.List)-1].(type) {
   646  			case *ast.ReturnStmt, *ast.BranchStmt:
   647  				isTerminated = true
   648  			}
   649  		}
   650  		if !isTerminated {
   651  			post()
   652  		}
   653  
   654  		c.p.escapingVars = prevEV
   655  	})
   656  	c.PrintCond(!flatten, "}", fmt.Sprintf("$s = %d; continue; case %d:", data.beginCase, data.endCase))
   657  }
   658  
   659  func (c *funcContext) translateAssign(lhs, rhs ast.Expr, define bool) string {
   660  	lhs = astutil.RemoveParens(lhs)
   661  	if isBlank(lhs) {
   662  		panic("translateAssign with blank lhs")
   663  	}
   664  
   665  	if l, ok := lhs.(*ast.IndexExpr); ok {
   666  		if t, ok := c.p.TypeOf(l.X).Underlying().(*types.Map); ok {
   667  			if typesutil.IsJsObject(c.p.TypeOf(l.Index)) {
   668  				c.p.errList = append(c.p.errList, types.Error{Fset: c.p.fileSet, Pos: l.Index.Pos(), Msg: "cannot use js.Object as map key"})
   669  			}
   670  			keyVar := c.newVariable("_key")
   671  			return fmt.Sprintf(`%s = %s; (%s || $throwRuntimeError("assignment to entry in nil map"))[%s.keyFor(%s)] = { k: %s, v: %s };`, keyVar, c.translateImplicitConversionWithCloning(l.Index, t.Key()), c.translateExpr(l.X), c.typeName(t.Key()), keyVar, keyVar, c.translateImplicitConversionWithCloning(rhs, t.Elem()))
   672  		}
   673  	}
   674  
   675  	lhsType := c.p.TypeOf(lhs)
   676  	rhsExpr := c.translateImplicitConversion(rhs, lhsType)
   677  	if _, ok := rhs.(*ast.CompositeLit); ok && define {
   678  		return fmt.Sprintf("%s = %s;", c.translateExpr(lhs), rhsExpr) // skip $copy
   679  	}
   680  
   681  	isReflectValue := false
   682  	if named, ok := lhsType.(*types.Named); ok && named.Obj().Pkg() != nil && named.Obj().Pkg().Path() == "reflect" && named.Obj().Name() == "Value" {
   683  		isReflectValue = true
   684  	}
   685  	if !isReflectValue { // this is a performance hack, but it is safe since reflect.Value has no exported fields and the reflect package does not violate this assumption
   686  		switch lhsType.Underlying().(type) {
   687  		case *types.Array, *types.Struct:
   688  			if define {
   689  				return fmt.Sprintf("%s = $clone(%s, %s);", c.translateExpr(lhs), rhsExpr, c.typeName(lhsType))
   690  			}
   691  			return fmt.Sprintf("%s.copy(%s, %s);", c.typeName(lhsType), c.translateExpr(lhs), rhsExpr)
   692  		}
   693  	}
   694  
   695  	switch l := lhs.(type) {
   696  	case *ast.Ident:
   697  		return fmt.Sprintf("%s = %s;", c.objectName(c.p.ObjectOf(l)), rhsExpr)
   698  	case *ast.SelectorExpr:
   699  		sel, ok := c.p.SelectionOf(l)
   700  		if !ok {
   701  			// qualified identifier
   702  			return fmt.Sprintf("%s = %s;", c.objectName(c.p.Uses[l.Sel]), rhsExpr)
   703  		}
   704  		fields, jsTag := c.translateSelection(sel, l.Pos())
   705  		if jsTag != "" {
   706  			return fmt.Sprintf("%s.%s%s = %s;", c.translateExpr(l.X), strings.Join(fields, "."), formatJSStructTagVal(jsTag), c.externalize(rhsExpr.String(), sel.Type()))
   707  		}
   708  		return fmt.Sprintf("%s.%s = %s;", c.translateExpr(l.X), strings.Join(fields, "."), rhsExpr)
   709  	case *ast.StarExpr:
   710  		return fmt.Sprintf("%s.$set(%s);", c.translateExpr(l.X), rhsExpr)
   711  	case *ast.IndexExpr:
   712  		switch t := c.p.TypeOf(l.X).Underlying().(type) {
   713  		case *types.Array, *types.Pointer:
   714  			pattern := rangeCheck("%1e[%2f] = %3s", c.p.Types[l.Index].Value != nil, true)
   715  			if _, ok := t.(*types.Pointer); ok { // check pointer for nil (attribute getter causes a panic)
   716  				pattern = `%1e.nilCheck, ` + pattern
   717  			}
   718  			return c.formatExpr(pattern, l.X, l.Index, rhsExpr).String() + ";"
   719  		case *types.Slice:
   720  			return c.formatExpr(rangeCheck("%1e.$array[%1e.$offset + %2f] = %3s", c.p.Types[l.Index].Value != nil, false), l.X, l.Index, rhsExpr).String() + ";"
   721  		default:
   722  			panic(fmt.Sprintf("Unhandled lhs type: %T\n", t))
   723  		}
   724  	default:
   725  		panic(fmt.Sprintf("Unhandled lhs type: %T\n", l))
   726  	}
   727  }
   728  
   729  func (c *funcContext) translateResults(results []ast.Expr) string {
   730  	tuple := c.sig.Results()
   731  	switch tuple.Len() {
   732  	case 0:
   733  		return ""
   734  	case 1:
   735  		result := c.zeroValue(tuple.At(0).Type())
   736  		if results != nil {
   737  			result = results[0]
   738  		}
   739  		v := c.translateImplicitConversion(result, tuple.At(0).Type())
   740  		c.delayedOutput = nil
   741  		return " " + v.String()
   742  	default:
   743  		if len(results) == 1 {
   744  			resultTuple := c.p.TypeOf(results[0]).(*types.Tuple)
   745  
   746  			if resultTuple.Len() != tuple.Len() {
   747  				panic("invalid tuple return assignment")
   748  			}
   749  
   750  			resultExpr := c.translateExpr(results[0]).String()
   751  
   752  			if types.Identical(resultTuple, tuple) {
   753  				return " " + resultExpr
   754  			}
   755  
   756  			tmpVar := c.newVariable("_returncast")
   757  			c.Printf("%s = %s;", tmpVar, resultExpr)
   758  
   759  			// Not all the return types matched, map everything out for implicit casting
   760  			results = make([]ast.Expr, resultTuple.Len())
   761  			for i := range results {
   762  				results[i] = c.newIdent(fmt.Sprintf("%s[%d]", tmpVar, i), resultTuple.At(i).Type())
   763  			}
   764  		}
   765  		values := make([]string, tuple.Len())
   766  		for i := range values {
   767  			result := c.zeroValue(tuple.At(i).Type())
   768  			if results != nil {
   769  				result = results[i]
   770  			}
   771  			values[i] = c.translateImplicitConversion(result, tuple.At(i).Type()).String()
   772  		}
   773  		c.delayedOutput = nil
   774  		return " [" + strings.Join(values, ", ") + "]"
   775  	}
   776  }
   777  
   778  func (c *funcContext) labelCase(label *types.Label) int {
   779  	labelCase, ok := c.labelCases[label]
   780  	if !ok {
   781  		labelCase = c.caseCounter
   782  		c.caseCounter++
   783  		c.labelCases[label] = labelCase
   784  	}
   785  	return labelCase
   786  }