github.com/gopherjs/gopherjs@v1.19.0-beta1.0.20240506212314-27071a8796e4/compiler/expressions.go (about)

     1  package compiler
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"go/ast"
     7  	"go/constant"
     8  	"go/token"
     9  	"go/types"
    10  	"sort"
    11  	"strconv"
    12  	"strings"
    13  
    14  	"github.com/gopherjs/gopherjs/compiler/analysis"
    15  	"github.com/gopherjs/gopherjs/compiler/astutil"
    16  	"github.com/gopherjs/gopherjs/compiler/internal/typeparams"
    17  	"github.com/gopherjs/gopherjs/compiler/typesutil"
    18  )
    19  
    20  type expression struct {
    21  	str    string
    22  	parens bool
    23  }
    24  
    25  func (e *expression) String() string {
    26  	return e.str
    27  }
    28  
    29  func (e *expression) StringWithParens() string {
    30  	if e.parens {
    31  		return "(" + e.str + ")"
    32  	}
    33  	return e.str
    34  }
    35  
    36  func (fc *funcContext) translateExpr(expr ast.Expr) *expression {
    37  	exprType := fc.typeOf(expr)
    38  	if value := fc.pkgCtx.Types[expr].Value; value != nil {
    39  		basic := exprType.Underlying().(*types.Basic)
    40  		switch {
    41  		case isBoolean(basic):
    42  			return fc.formatExpr("%s", strconv.FormatBool(constant.BoolVal(value)))
    43  		case isInteger(basic):
    44  			if is64Bit(basic) {
    45  				if basic.Kind() == types.Int64 {
    46  					d, ok := constant.Int64Val(constant.ToInt(value))
    47  					if !ok {
    48  						panic("could not get exact uint")
    49  					}
    50  					return fc.formatExpr("new %s(%s, %s)", fc.typeName(exprType), strconv.FormatInt(d>>32, 10), strconv.FormatUint(uint64(d)&(1<<32-1), 10))
    51  				}
    52  				d, ok := constant.Uint64Val(constant.ToInt(value))
    53  				if !ok {
    54  					panic("could not get exact uint")
    55  				}
    56  				return fc.formatExpr("new %s(%s, %s)", fc.typeName(exprType), strconv.FormatUint(d>>32, 10), strconv.FormatUint(d&(1<<32-1), 10))
    57  			}
    58  			d, ok := constant.Int64Val(constant.ToInt(value))
    59  			if !ok {
    60  				panic("could not get exact int")
    61  			}
    62  			return fc.formatExpr("%s", strconv.FormatInt(d, 10))
    63  		case isFloat(basic):
    64  			f, _ := constant.Float64Val(value)
    65  			return fc.formatExpr("%s", strconv.FormatFloat(f, 'g', -1, 64))
    66  		case isComplex(basic):
    67  			r, _ := constant.Float64Val(constant.Real(value))
    68  			i, _ := constant.Float64Val(constant.Imag(value))
    69  			if basic.Kind() == types.UntypedComplex {
    70  				exprType = types.Typ[types.Complex128]
    71  			}
    72  			return fc.formatExpr("new %s(%s, %s)", fc.typeName(exprType), strconv.FormatFloat(r, 'g', -1, 64), strconv.FormatFloat(i, 'g', -1, 64))
    73  		case isString(basic):
    74  			return fc.formatExpr("%s", encodeString(constant.StringVal(value)))
    75  		default:
    76  			panic("Unhandled constant type: " + basic.String())
    77  		}
    78  	}
    79  
    80  	var inst typeparams.Instance
    81  	switch e := expr.(type) {
    82  	case *ast.SelectorExpr:
    83  		inst = fc.instanceOf(e.Sel)
    84  	case *ast.Ident:
    85  		inst = fc.instanceOf(e)
    86  	}
    87  
    88  	if inst.Object != nil && typesutil.IsJsPackage(inst.Object.Pkg()) {
    89  		switch inst.Object.Name() {
    90  		case "Global":
    91  			return fc.formatExpr("$global")
    92  		case "Module":
    93  			return fc.formatExpr("$module")
    94  		case "Undefined":
    95  			return fc.formatExpr("undefined")
    96  		}
    97  	}
    98  
    99  	switch e := expr.(type) {
   100  	case *ast.CompositeLit:
   101  		if ptrType, isPointer := exprType.Underlying().(*types.Pointer); isPointer {
   102  			// Go automatically treats `[]*T{{}}` as `[]*T{&T{}}`, in which case the
   103  			// inner composite literal `{}` would has a pointer type. To make sure the
   104  			// type conversion is handled correctly, we generate the explicit AST for
   105  			// this.
   106  			var rewritten ast.Expr = fc.setType(&ast.UnaryExpr{
   107  				OpPos: e.Pos(),
   108  				Op:    token.AND,
   109  				X: fc.setType(&ast.CompositeLit{
   110  					Elts: e.Elts,
   111  				}, ptrType.Elem()),
   112  			}, ptrType)
   113  
   114  			if exprType, ok := exprType.(*types.Named); ok {
   115  				// Handle a special case when the pointer type is named, e.g.:
   116  				//   type PS *S
   117  				//   _ = []PS{{}}
   118  				// In that case the value corresponding to the inner literal `{}` is
   119  				// initialized as `&S{}` and then converted to `PS`: `[]PS{PS(&S{})}`.
   120  				typeCast := fc.setType(&ast.CallExpr{
   121  					Fun:    fc.newTypeIdent(exprType.String(), exprType.Obj()),
   122  					Lparen: e.Lbrace,
   123  					Args:   []ast.Expr{rewritten},
   124  					Rparen: e.Rbrace,
   125  				}, exprType)
   126  				rewritten = typeCast
   127  			}
   128  			return fc.translateExpr(rewritten)
   129  		}
   130  
   131  		collectIndexedElements := func(elementType types.Type) []string {
   132  			var elements []string
   133  			i := 0
   134  			zero := fc.translateExpr(fc.zeroValue(elementType)).String()
   135  			for _, element := range e.Elts {
   136  				if kve, isKve := element.(*ast.KeyValueExpr); isKve {
   137  					key, ok := constant.Int64Val(constant.ToInt(fc.pkgCtx.Types[kve.Key].Value))
   138  					if !ok {
   139  						panic("could not get exact int")
   140  					}
   141  					i = int(key)
   142  					element = kve.Value
   143  				}
   144  				for len(elements) <= i {
   145  					elements = append(elements, zero)
   146  				}
   147  				elements[i] = fc.translateImplicitConversionWithCloning(element, elementType).String()
   148  				i++
   149  			}
   150  			return elements
   151  		}
   152  
   153  		switch t := exprType.Underlying().(type) {
   154  		case *types.Array:
   155  			elements := collectIndexedElements(t.Elem())
   156  			if len(elements) == 0 {
   157  				return fc.formatExpr("%s.zero()", fc.typeName(t))
   158  			}
   159  			zero := fc.translateExpr(fc.zeroValue(t.Elem())).String()
   160  			for len(elements) < int(t.Len()) {
   161  				elements = append(elements, zero)
   162  			}
   163  			return fc.formatExpr(`$toNativeArray(%s, [%s])`, typeKind(t.Elem()), strings.Join(elements, ", "))
   164  		case *types.Slice:
   165  			return fc.formatExpr("new %s([%s])", fc.typeName(exprType), strings.Join(collectIndexedElements(t.Elem()), ", "))
   166  		case *types.Map:
   167  			entries := make([]string, len(e.Elts))
   168  			for i, element := range e.Elts {
   169  				kve := element.(*ast.KeyValueExpr)
   170  				entries[i] = fmt.Sprintf("{ k: %s, v: %s }", fc.translateImplicitConversionWithCloning(kve.Key, t.Key()), fc.translateImplicitConversionWithCloning(kve.Value, t.Elem()))
   171  			}
   172  			return fc.formatExpr("$makeMap(%s.keyFor, [%s])", fc.typeName(t.Key()), strings.Join(entries, ", "))
   173  		case *types.Struct:
   174  			elements := make([]string, t.NumFields())
   175  			isKeyValue := true
   176  			if len(e.Elts) != 0 {
   177  				_, isKeyValue = e.Elts[0].(*ast.KeyValueExpr)
   178  			}
   179  			if !isKeyValue {
   180  				for i, element := range e.Elts {
   181  					elements[i] = fc.translateImplicitConversionWithCloning(element, t.Field(i).Type()).String()
   182  				}
   183  			}
   184  			if isKeyValue {
   185  				for i := range elements {
   186  					elements[i] = fc.translateExpr(fc.zeroValue(t.Field(i).Type())).String()
   187  				}
   188  				for _, element := range e.Elts {
   189  					kve := element.(*ast.KeyValueExpr)
   190  					for j := range elements {
   191  						if kve.Key.(*ast.Ident).Name == t.Field(j).Name() {
   192  							elements[j] = fc.translateImplicitConversionWithCloning(kve.Value, t.Field(j).Type()).String()
   193  							break
   194  						}
   195  					}
   196  				}
   197  			}
   198  			return fc.formatExpr("new %s.ptr(%s)", fc.typeName(exprType), strings.Join(elements, ", "))
   199  		default:
   200  			panic(fmt.Sprintf("Unhandled CompositeLit type: %[1]T %[1]v\n", t))
   201  		}
   202  
   203  	case *ast.FuncLit:
   204  		_, fun := translateFunction(e.Type, nil, e.Body, fc, exprType.(*types.Signature), fc.pkgCtx.FuncLitInfos[e], "", typeparams.Instance{})
   205  		if len(fc.pkgCtx.escapingVars) != 0 {
   206  			names := make([]string, 0, len(fc.pkgCtx.escapingVars))
   207  			for obj := range fc.pkgCtx.escapingVars {
   208  				name, ok := fc.assignedObjectName(obj)
   209  				if !ok {
   210  					// This should never happen.
   211  					panic(fmt.Errorf("escaping variable %s hasn't been assigned a JS name", obj))
   212  				}
   213  				names = append(names, name)
   214  			}
   215  			sort.Strings(names)
   216  			list := strings.Join(names, ", ")
   217  			return fc.formatExpr("(function(%s) { return %s; })(%s)", list, fun, list)
   218  		}
   219  		return fc.formatExpr("(%s)", fun)
   220  
   221  	case *ast.UnaryExpr:
   222  		t := fc.typeOf(e.X)
   223  		switch e.Op {
   224  		case token.AND:
   225  			if typesutil.IsJsObject(exprType) {
   226  				return fc.formatExpr("%e.object", e.X)
   227  			}
   228  
   229  			switch t.Underlying().(type) {
   230  			case *types.Struct, *types.Array:
   231  				// JavaScript's pass-by-reference semantics makes passing array's or
   232  				// struct's object semantically equivalent to passing a pointer
   233  				// TODO(nevkontakte): Evaluate if performance gain justifies complexity
   234  				// introduced by the special case.
   235  				return fc.translateExpr(e.X)
   236  			}
   237  
   238  			elemType := exprType.(*types.Pointer).Elem()
   239  
   240  			switch x := astutil.RemoveParens(e.X).(type) {
   241  			case *ast.CompositeLit:
   242  				return fc.formatExpr("$newDataPointer(%e, %s)", x, fc.typeName(fc.typeOf(e)))
   243  			case *ast.Ident:
   244  				obj := fc.pkgCtx.Uses[x].(*types.Var)
   245  				if fc.pkgCtx.escapingVars[obj] {
   246  					name, ok := fc.assignedObjectName(obj)
   247  					if !ok {
   248  						// This should never happen.
   249  						panic(fmt.Errorf("escaping variable %s hasn't been assigned a JS name", obj))
   250  					}
   251  					return fc.formatExpr("(%1s.$ptr || (%1s.$ptr = new %2s(function() { return this.$target[0]; }, function($v) { this.$target[0] = $v; }, %1s)))", name, fc.typeName(exprType))
   252  				}
   253  				return fc.formatExpr(`(%1s || (%1s = new %2s(function() { return %3s; }, function($v) { %4s })))`, fc.varPtrName(obj), fc.typeName(exprType), fc.objectName(obj), fc.translateAssign(x, fc.newIdent("$v", elemType), false))
   254  			case *ast.SelectorExpr:
   255  				sel, ok := fc.selectionOf(x)
   256  				if !ok {
   257  					// qualified identifier
   258  					obj := fc.pkgCtx.Uses[x.Sel].(*types.Var)
   259  					return fc.formatExpr(`(%1s || (%1s = new %2s(function() { return %3s; }, function($v) { %4s })))`, fc.varPtrName(obj), fc.typeName(exprType), fc.objectName(obj), fc.translateAssign(x, fc.newIdent("$v", elemType), false))
   260  				}
   261  				newSel := &ast.SelectorExpr{X: fc.newIdent("this.$target", fc.typeOf(x.X)), Sel: x.Sel}
   262  				fc.setType(newSel, exprType)
   263  				fc.pkgCtx.additionalSelections[newSel] = sel
   264  				return fc.formatExpr("(%1e.$ptr_%2s || (%1e.$ptr_%2s = new %3s(function() { return %4e; }, function($v) { %5s }, %1e)))", x.X, x.Sel.Name, fc.typeName(exprType), newSel, fc.translateAssign(newSel, fc.newIdent("$v", exprType), false))
   265  			case *ast.IndexExpr:
   266  				if _, ok := fc.typeOf(x.X).Underlying().(*types.Slice); ok {
   267  					return fc.formatExpr("$indexPtr(%1e.$array, %1e.$offset + %2e, %3s)", x.X, x.Index, fc.typeName(exprType))
   268  				}
   269  				return fc.formatExpr("$indexPtr(%e, %e, %s)", x.X, x.Index, fc.typeName(exprType))
   270  			case *ast.StarExpr:
   271  				return fc.translateExpr(x.X)
   272  			default:
   273  				panic(fmt.Sprintf("Unhandled: %T\n", x))
   274  			}
   275  
   276  		case token.ARROW:
   277  			call := &ast.CallExpr{
   278  				Fun:  fc.newIdent("$recv", types.NewSignatureType(nil, nil, nil, types.NewTuple(types.NewVar(0, nil, "", t)), types.NewTuple(types.NewVar(0, nil, "", exprType), types.NewVar(0, nil, "", types.Typ[types.Bool])), false)),
   279  				Args: []ast.Expr{e.X},
   280  			}
   281  			fc.Blocking[call] = true
   282  			if _, isTuple := exprType.(*types.Tuple); isTuple {
   283  				return fc.formatExpr("%e", call)
   284  			}
   285  			return fc.formatExpr("%e[0]", call)
   286  		}
   287  
   288  		basic := t.Underlying().(*types.Basic)
   289  		switch e.Op {
   290  		case token.ADD:
   291  			return fc.translateExpr(e.X)
   292  		case token.SUB:
   293  			switch {
   294  			case is64Bit(basic):
   295  				return fc.formatExpr("new %1s(-%2h, -%2l)", fc.typeName(t), e.X)
   296  			case isComplex(basic):
   297  				return fc.formatExpr("new %1s(-%2r, -%2i)", fc.typeName(t), e.X)
   298  			case isUnsigned(basic):
   299  				return fc.fixNumber(fc.formatExpr("-%e", e.X), basic)
   300  			default:
   301  				return fc.formatExpr("-%e", e.X)
   302  			}
   303  		case token.XOR:
   304  			if is64Bit(basic) {
   305  				return fc.formatExpr("new %1s(~%2h, ~%2l >>> 0)", fc.typeName(t), e.X)
   306  			}
   307  			return fc.fixNumber(fc.formatExpr("~%e", e.X), basic)
   308  		case token.NOT:
   309  			return fc.formatExpr("!%e", e.X)
   310  		default:
   311  			panic(e.Op)
   312  		}
   313  
   314  	case *ast.BinaryExpr:
   315  		if e.Op == token.NEQ {
   316  			return fc.formatExpr("!(%s)", fc.translateExpr(&ast.BinaryExpr{
   317  				X:  e.X,
   318  				Op: token.EQL,
   319  				Y:  e.Y,
   320  			}))
   321  		}
   322  
   323  		t := fc.typeOf(e.X)
   324  		t2 := fc.typeOf(e.Y)
   325  		_, isInterface := t2.Underlying().(*types.Interface)
   326  		if isInterface || types.Identical(t, types.Typ[types.UntypedNil]) {
   327  			t = t2
   328  		}
   329  
   330  		if basic, isBasic := t.Underlying().(*types.Basic); isBasic && isNumeric(basic) {
   331  			if is64Bit(basic) {
   332  				switch e.Op {
   333  				case token.MUL:
   334  					return fc.formatExpr("$mul64(%e, %e)", e.X, e.Y)
   335  				case token.QUO:
   336  					return fc.formatExpr("$div64(%e, %e, false)", e.X, e.Y)
   337  				case token.REM:
   338  					return fc.formatExpr("$div64(%e, %e, true)", e.X, e.Y)
   339  				case token.SHL:
   340  					return fc.formatExpr("$shiftLeft64(%e, %f)", e.X, e.Y)
   341  				case token.SHR:
   342  					return fc.formatExpr("$shiftRight%s(%e, %f)", toJavaScriptType(basic), e.X, e.Y)
   343  				case token.EQL:
   344  					return fc.formatExpr("(%1h === %2h && %1l === %2l)", e.X, e.Y)
   345  				case token.LSS:
   346  					return fc.formatExpr("(%1h < %2h || (%1h === %2h && %1l < %2l))", e.X, e.Y)
   347  				case token.LEQ:
   348  					return fc.formatExpr("(%1h < %2h || (%1h === %2h && %1l <= %2l))", e.X, e.Y)
   349  				case token.GTR:
   350  					return fc.formatExpr("(%1h > %2h || (%1h === %2h && %1l > %2l))", e.X, e.Y)
   351  				case token.GEQ:
   352  					return fc.formatExpr("(%1h > %2h || (%1h === %2h && %1l >= %2l))", e.X, e.Y)
   353  				case token.ADD, token.SUB:
   354  					return fc.formatExpr("new %3s(%1h %4t %2h, %1l %4t %2l)", e.X, e.Y, fc.typeName(t), e.Op)
   355  				case token.AND, token.OR, token.XOR:
   356  					return fc.formatExpr("new %3s(%1h %4t %2h, (%1l %4t %2l) >>> 0)", e.X, e.Y, fc.typeName(t), e.Op)
   357  				case token.AND_NOT:
   358  					return fc.formatExpr("new %3s(%1h & ~%2h, (%1l & ~%2l) >>> 0)", e.X, e.Y, fc.typeName(t))
   359  				default:
   360  					panic(e.Op)
   361  				}
   362  			}
   363  
   364  			if isComplex(basic) {
   365  				switch e.Op {
   366  				case token.EQL:
   367  					return fc.formatExpr("(%1r === %2r && %1i === %2i)", e.X, e.Y)
   368  				case token.ADD, token.SUB:
   369  					return fc.formatExpr("new %3s(%1r %4t %2r, %1i %4t %2i)", e.X, e.Y, fc.typeName(t), e.Op)
   370  				case token.MUL:
   371  					return fc.formatExpr("new %3s(%1r * %2r - %1i * %2i, %1r * %2i + %1i * %2r)", e.X, e.Y, fc.typeName(t))
   372  				case token.QUO:
   373  					return fc.formatExpr("$divComplex(%e, %e)", e.X, e.Y)
   374  				default:
   375  					panic(e.Op)
   376  				}
   377  			}
   378  
   379  			switch e.Op {
   380  			case token.EQL:
   381  				return fc.formatParenExpr("%e === %e", e.X, e.Y)
   382  			case token.LSS, token.LEQ, token.GTR, token.GEQ:
   383  				return fc.formatExpr("%e %t %e", e.X, e.Op, e.Y)
   384  			case token.ADD, token.SUB:
   385  				return fc.fixNumber(fc.formatExpr("%e %t %e", e.X, e.Op, e.Y), basic)
   386  			case token.MUL:
   387  				switch basic.Kind() {
   388  				case types.Int32, types.Int:
   389  					return fc.formatParenExpr("$imul(%e, %e)", e.X, e.Y)
   390  				case types.Uint32, types.Uintptr:
   391  					return fc.formatParenExpr("$imul(%e, %e) >>> 0", e.X, e.Y)
   392  				}
   393  				return fc.fixNumber(fc.formatExpr("%e * %e", e.X, e.Y), basic)
   394  			case token.QUO:
   395  				if isInteger(basic) {
   396  					// cut off decimals
   397  					shift := ">>"
   398  					if isUnsigned(basic) {
   399  						shift = ">>>"
   400  					}
   401  					return fc.formatExpr(`(%1s = %2e / %3e, (%1s === %1s && %1s !== 1/0 && %1s !== -1/0) ? %1s %4s 0 : $throwRuntimeError("integer divide by zero"))`, fc.newLocalVariable("_q"), e.X, e.Y, shift)
   402  				}
   403  				if basic.Kind() == types.Float32 {
   404  					return fc.fixNumber(fc.formatExpr("%e / %e", e.X, e.Y), basic)
   405  				}
   406  				return fc.formatExpr("%e / %e", e.X, e.Y)
   407  			case token.REM:
   408  				return fc.formatExpr(`(%1s = %2e %% %3e, %1s === %1s ? %1s : $throwRuntimeError("integer divide by zero"))`, fc.newLocalVariable("_r"), e.X, e.Y)
   409  			case token.SHL, token.SHR:
   410  				op := e.Op.String()
   411  				if e.Op == token.SHR && isUnsigned(basic) {
   412  					op = ">>>"
   413  				}
   414  				if v := fc.pkgCtx.Types[e.Y].Value; v != nil {
   415  					i, _ := constant.Uint64Val(constant.ToInt(v))
   416  					if i >= 32 {
   417  						return fc.formatExpr("0")
   418  					}
   419  					return fc.fixNumber(fc.formatExpr("%e %s %s", e.X, op, strconv.FormatUint(i, 10)), basic)
   420  				}
   421  				if e.Op == token.SHR && !isUnsigned(basic) {
   422  					return fc.fixNumber(fc.formatParenExpr("%e >> $min(%f, 31)", e.X, e.Y), basic)
   423  				}
   424  				y := fc.newLocalVariable("y")
   425  				return fc.fixNumber(fc.formatExpr("(%s = %f, %s < 32 ? (%e %s %s) : 0)", y, e.Y, y, e.X, op, y), basic)
   426  			case token.AND, token.OR:
   427  				if isUnsigned(basic) {
   428  					return fc.formatParenExpr("(%e %t %e) >>> 0", e.X, e.Op, e.Y)
   429  				}
   430  				return fc.formatParenExpr("%e %t %e", e.X, e.Op, e.Y)
   431  			case token.AND_NOT:
   432  				return fc.fixNumber(fc.formatParenExpr("%e & ~%e", e.X, e.Y), basic)
   433  			case token.XOR:
   434  				return fc.fixNumber(fc.formatParenExpr("%e ^ %e", e.X, e.Y), basic)
   435  			default:
   436  				panic(e.Op)
   437  			}
   438  		}
   439  
   440  		switch e.Op {
   441  		case token.ADD, token.LSS, token.LEQ, token.GTR, token.GEQ:
   442  			return fc.formatExpr("%e %t %e", e.X, e.Op, e.Y)
   443  		case token.LAND:
   444  			if fc.Blocking[e.Y] {
   445  				skipCase := fc.caseCounter
   446  				fc.caseCounter++
   447  				resultVar := fc.newLocalVariable("_v")
   448  				fc.Printf("if (!(%s)) { %s = false; $s = %d; continue s; }", fc.translateExpr(e.X), resultVar, skipCase)
   449  				fc.Printf("%s = %s; case %d:", resultVar, fc.translateExpr(e.Y), skipCase)
   450  				return fc.formatExpr("%s", resultVar)
   451  			}
   452  			return fc.formatExpr("%e && %e", e.X, e.Y)
   453  		case token.LOR:
   454  			if fc.Blocking[e.Y] {
   455  				skipCase := fc.caseCounter
   456  				fc.caseCounter++
   457  				resultVar := fc.newLocalVariable("_v")
   458  				fc.Printf("if (%s) { %s = true; $s = %d; continue s; }", fc.translateExpr(e.X), resultVar, skipCase)
   459  				fc.Printf("%s = %s; case %d:", resultVar, fc.translateExpr(e.Y), skipCase)
   460  				return fc.formatExpr("%s", resultVar)
   461  			}
   462  			return fc.formatExpr("%e || %e", e.X, e.Y)
   463  		case token.EQL:
   464  			switch u := t.Underlying().(type) {
   465  			case *types.Array, *types.Struct:
   466  				return fc.formatExpr("$equal(%e, %e, %s)", e.X, e.Y, fc.typeName(t))
   467  			case *types.Interface:
   468  				return fc.formatExpr("$interfaceIsEqual(%s, %s)", fc.translateImplicitConversion(e.X, t), fc.translateImplicitConversion(e.Y, t))
   469  			case *types.Basic:
   470  				if isBoolean(u) {
   471  					if b, ok := analysis.BoolValue(e.X, fc.pkgCtx.Info.Info); ok && b {
   472  						return fc.translateExpr(e.Y)
   473  					}
   474  					if b, ok := analysis.BoolValue(e.Y, fc.pkgCtx.Info.Info); ok && b {
   475  						return fc.translateExpr(e.X)
   476  					}
   477  				}
   478  			}
   479  			return fc.formatExpr("%s === %s", fc.translateImplicitConversion(e.X, t), fc.translateImplicitConversion(e.Y, t))
   480  		default:
   481  			panic(e.Op)
   482  		}
   483  
   484  	case *ast.ParenExpr:
   485  		return fc.formatParenExpr("%e", e.X)
   486  
   487  	case *ast.IndexExpr:
   488  		switch t := fc.typeOf(e.X).Underlying().(type) {
   489  		case *types.Pointer:
   490  			if _, ok := t.Elem().Underlying().(*types.Array); !ok {
   491  				// Should never happen in type-checked code.
   492  				panic(fmt.Errorf("non-array pointers can't be used with index expression"))
   493  			}
   494  			// Rewrite arrPtr[i] → (*arrPtr)[i] to concentrate array dereferencing
   495  			// logic in one place.
   496  			x := &ast.StarExpr{
   497  				Star: e.X.Pos(),
   498  				X:    e.X,
   499  			}
   500  			astutil.SetType(fc.pkgCtx.Info.Info, t.Elem(), x)
   501  			e.X = x
   502  			return fc.translateExpr(e)
   503  		case *types.Array:
   504  			pattern := rangeCheck("%1e[%2f]", fc.pkgCtx.Types[e.Index].Value != nil, true)
   505  			return fc.formatExpr(pattern, e.X, e.Index)
   506  		case *types.Slice:
   507  			return fc.formatExpr(rangeCheck("%1e.$array[%1e.$offset + %2f]", fc.pkgCtx.Types[e.Index].Value != nil, false), e.X, e.Index)
   508  		case *types.Map:
   509  			if typesutil.IsJsObject(fc.typeOf(e.Index)) {
   510  				fc.pkgCtx.errList = append(fc.pkgCtx.errList, types.Error{Fset: fc.pkgCtx.fileSet, Pos: e.Index.Pos(), Msg: "cannot use js.Object as map key"})
   511  			}
   512  			key := fmt.Sprintf("%s.keyFor(%s)", fc.typeName(t.Key()), fc.translateImplicitConversion(e.Index, t.Key()))
   513  			if _, isTuple := exprType.(*types.Tuple); isTuple {
   514  				return fc.formatExpr(
   515  					`(%1s = $mapIndex(%2e,%3s), %1s !== undefined ? [%1s.v, true] : [%4e, false])`,
   516  					fc.newLocalVariable("_entry"),
   517  					e.X,
   518  					key,
   519  					fc.zeroValue(t.Elem()),
   520  				)
   521  			}
   522  			return fc.formatExpr(
   523  				`(%1s = $mapIndex(%2e,%3s), %1s !== undefined ? %1s.v : %4e)`,
   524  				fc.newLocalVariable("_entry"),
   525  				e.X,
   526  				key,
   527  				fc.zeroValue(t.Elem()),
   528  			)
   529  		case *types.Basic:
   530  			return fc.formatExpr("%e.charCodeAt(%f)", e.X, e.Index)
   531  		case *types.Signature:
   532  			return fc.formatExpr("%s", fc.instName(fc.instanceOf(e.X.(*ast.Ident))))
   533  		default:
   534  			panic(fmt.Errorf(`unhandled IndexExpr: %T`, t))
   535  		}
   536  	case *ast.IndexListExpr:
   537  		switch t := fc.typeOf(e.X).Underlying().(type) {
   538  		case *types.Signature:
   539  			return fc.formatExpr("%s", fc.instName(fc.instanceOf(e.X.(*ast.Ident))))
   540  		default:
   541  			panic(fmt.Errorf("unhandled IndexListExpr: %T", t))
   542  		}
   543  	case *ast.SliceExpr:
   544  		if b, isBasic := fc.typeOf(e.X).Underlying().(*types.Basic); isBasic && isString(b) {
   545  			switch {
   546  			case e.Low == nil && e.High == nil:
   547  				return fc.translateExpr(e.X)
   548  			case e.Low == nil:
   549  				return fc.formatExpr("$substring(%e, 0, %f)", e.X, e.High)
   550  			case e.High == nil:
   551  				return fc.formatExpr("$substring(%e, %f)", e.X, e.Low)
   552  			default:
   553  				return fc.formatExpr("$substring(%e, %f, %f)", e.X, e.Low, e.High)
   554  			}
   555  		}
   556  		slice := fc.translateConversionToSlice(e.X, exprType)
   557  		switch {
   558  		case e.Low == nil && e.High == nil:
   559  			return fc.formatExpr("%s", slice)
   560  		case e.Low == nil:
   561  			if e.Max != nil {
   562  				return fc.formatExpr("$subslice(%s, 0, %f, %f)", slice, e.High, e.Max)
   563  			}
   564  			return fc.formatExpr("$subslice(%s, 0, %f)", slice, e.High)
   565  		case e.High == nil:
   566  			return fc.formatExpr("$subslice(%s, %f)", slice, e.Low)
   567  		default:
   568  			if e.Max != nil {
   569  				return fc.formatExpr("$subslice(%s, %f, %f, %f)", slice, e.Low, e.High, e.Max)
   570  			}
   571  			return fc.formatExpr("$subslice(%s, %f, %f)", slice, e.Low, e.High)
   572  		}
   573  
   574  	case *ast.SelectorExpr:
   575  		sel, ok := fc.selectionOf(e)
   576  		if !ok {
   577  			// qualified identifier
   578  			return fc.formatExpr("%s", fc.instName(inst))
   579  		}
   580  
   581  		switch sel.Kind() {
   582  		case types.FieldVal:
   583  			fields, jsTag := fc.translateSelection(sel, e.Pos())
   584  			if jsTag != "" {
   585  				if _, ok := sel.Type().(*types.Signature); ok {
   586  					return fc.formatExpr("$internalize(%1e.%2s%3s, %4s, %1e.%2s)", e.X, strings.Join(fields, "."), formatJSStructTagVal(jsTag), fc.typeName(sel.Type()))
   587  				}
   588  				return fc.internalize(fc.formatExpr("%e.%s%s", e.X, strings.Join(fields, "."), formatJSStructTagVal(jsTag)), sel.Type())
   589  			}
   590  			return fc.formatExpr("%e.%s", e.X, strings.Join(fields, "."))
   591  		case types.MethodVal:
   592  			return fc.formatExpr(`$methodVal(%s, "%s")`, fc.makeReceiver(e), sel.Obj().(*types.Func).Name())
   593  		case types.MethodExpr:
   594  			if !sel.Obj().Exported() {
   595  				fc.pkgCtx.dependencies[sel.Obj()] = true
   596  			}
   597  			if _, ok := sel.Recv().Underlying().(*types.Interface); ok {
   598  				return fc.formatExpr(`$ifaceMethodExpr("%s")`, sel.Obj().(*types.Func).Name())
   599  			}
   600  			return fc.formatExpr(`$methodExpr(%s, "%s")`, fc.typeName(sel.Recv()), sel.Obj().(*types.Func).Name())
   601  		default:
   602  			panic(fmt.Sprintf("unexpected sel.Kind(): %T", sel.Kind()))
   603  		}
   604  
   605  	case *ast.CallExpr:
   606  		plainFun := astutil.RemoveParens(e.Fun)
   607  
   608  		if astutil.IsTypeExpr(plainFun, fc.pkgCtx.Info.Info) {
   609  			return fc.formatExpr("(%s)", fc.translateConversion(e.Args[0], fc.typeOf(plainFun)))
   610  		}
   611  
   612  		sig := fc.typeOf(plainFun).Underlying().(*types.Signature)
   613  
   614  		switch f := plainFun.(type) {
   615  		case *ast.Ident:
   616  			obj := fc.pkgCtx.Uses[f]
   617  			if o, ok := obj.(*types.Builtin); ok {
   618  				return fc.translateBuiltin(o.Name(), sig, e.Args, e.Ellipsis.IsValid())
   619  			}
   620  			if typesutil.IsJsPackage(obj.Pkg()) && obj.Name() == "InternalObject" {
   621  				return fc.translateExpr(e.Args[0])
   622  			}
   623  			return fc.translateCall(e, sig, fc.translateExpr(f))
   624  
   625  		case *ast.SelectorExpr:
   626  			sel, ok := fc.selectionOf(f)
   627  			if !ok {
   628  				// qualified identifier
   629  				obj := fc.pkgCtx.Uses[f.Sel]
   630  				if o, ok := obj.(*types.Builtin); ok {
   631  					return fc.translateBuiltin(o.Name(), sig, e.Args, e.Ellipsis.IsValid())
   632  				}
   633  				if typesutil.IsJsPackage(obj.Pkg()) {
   634  					switch obj.Name() {
   635  					case "Debugger":
   636  						return fc.formatExpr("debugger")
   637  					case "InternalObject":
   638  						return fc.translateExpr(e.Args[0])
   639  					}
   640  				}
   641  				return fc.translateCall(e, sig, fc.translateExpr(f))
   642  			}
   643  
   644  			externalizeExpr := func(e ast.Expr) string {
   645  				t := fc.typeOf(e)
   646  				if types.Identical(t, types.Typ[types.UntypedNil]) {
   647  					return "null"
   648  				}
   649  				return fc.externalize(fc.translateExpr(e).String(), t)
   650  			}
   651  			externalizeArgs := func(args []ast.Expr) string {
   652  				s := make([]string, len(args))
   653  				for i, arg := range args {
   654  					s[i] = externalizeExpr(arg)
   655  				}
   656  				return strings.Join(s, ", ")
   657  			}
   658  
   659  			switch sel.Kind() {
   660  			case types.MethodVal:
   661  				recv := fc.makeReceiver(f)
   662  				declaredFuncRecv := sel.Obj().(*types.Func).Type().(*types.Signature).Recv().Type()
   663  				if typesutil.IsJsObject(declaredFuncRecv) {
   664  					globalRef := func(id string) string {
   665  						if recv.String() == "$global" && id[0] == '$' && len(id) > 1 {
   666  							return id
   667  						}
   668  						return recv.String() + "." + id
   669  					}
   670  					switch sel.Obj().Name() {
   671  					case "Get":
   672  						if id, ok := fc.identifierConstant(e.Args[0]); ok {
   673  							return fc.formatExpr("%s", globalRef(id))
   674  						}
   675  						return fc.formatExpr("%s[$externalize(%e, $String)]", recv, e.Args[0])
   676  					case "Set":
   677  						if id, ok := fc.identifierConstant(e.Args[0]); ok {
   678  							return fc.formatExpr("%s = %s", globalRef(id), externalizeExpr(e.Args[1]))
   679  						}
   680  						return fc.formatExpr("%s[$externalize(%e, $String)] = %s", recv, e.Args[0], externalizeExpr(e.Args[1]))
   681  					case "Delete":
   682  						return fc.formatExpr("delete %s[$externalize(%e, $String)]", recv, e.Args[0])
   683  					case "Length":
   684  						return fc.formatExpr("$parseInt(%s.length)", recv)
   685  					case "Index":
   686  						return fc.formatExpr("%s[%e]", recv, e.Args[0])
   687  					case "SetIndex":
   688  						return fc.formatExpr("%s[%e] = %s", recv, e.Args[0], externalizeExpr(e.Args[1]))
   689  					case "Call":
   690  						if id, ok := fc.identifierConstant(e.Args[0]); ok {
   691  							if e.Ellipsis.IsValid() {
   692  								objVar := fc.newLocalVariable("obj")
   693  								return fc.formatExpr("(%s = %s, %s.%s.apply(%s, %s))", objVar, recv, objVar, id, objVar, externalizeExpr(e.Args[1]))
   694  							}
   695  							return fc.formatExpr("%s(%s)", globalRef(id), externalizeArgs(e.Args[1:]))
   696  						}
   697  						if e.Ellipsis.IsValid() {
   698  							objVar := fc.newLocalVariable("obj")
   699  							return fc.formatExpr("(%s = %s, %s[$externalize(%e, $String)].apply(%s, %s))", objVar, recv, objVar, e.Args[0], objVar, externalizeExpr(e.Args[1]))
   700  						}
   701  						return fc.formatExpr("%s[$externalize(%e, $String)](%s)", recv, e.Args[0], externalizeArgs(e.Args[1:]))
   702  					case "Invoke":
   703  						if e.Ellipsis.IsValid() {
   704  							return fc.formatExpr("%s.apply(undefined, %s)", recv, externalizeExpr(e.Args[0]))
   705  						}
   706  						return fc.formatExpr("%s(%s)", recv, externalizeArgs(e.Args))
   707  					case "New":
   708  						if e.Ellipsis.IsValid() {
   709  							return fc.formatExpr("new ($global.Function.prototype.bind.apply(%s, [undefined].concat(%s)))", recv, externalizeExpr(e.Args[0]))
   710  						}
   711  						return fc.formatExpr("new (%s)(%s)", recv, externalizeArgs(e.Args))
   712  					case "Bool":
   713  						return fc.internalize(recv, types.Typ[types.Bool])
   714  					case "String":
   715  						return fc.internalize(recv, types.Typ[types.String])
   716  					case "Int":
   717  						return fc.internalize(recv, types.Typ[types.Int])
   718  					case "Int64":
   719  						return fc.internalize(recv, types.Typ[types.Int64])
   720  					case "Uint64":
   721  						return fc.internalize(recv, types.Typ[types.Uint64])
   722  					case "Float":
   723  						return fc.internalize(recv, types.Typ[types.Float64])
   724  					case "Interface":
   725  						return fc.internalize(recv, types.NewInterfaceType(nil, nil))
   726  					case "Unsafe":
   727  						return recv
   728  					default:
   729  						panic("Invalid js package object: " + sel.Obj().Name())
   730  					}
   731  				}
   732  
   733  				methodName := sel.Obj().Name()
   734  				if reservedKeywords[methodName] {
   735  					methodName += "$"
   736  				}
   737  				return fc.translateCall(e, sig, fc.formatExpr("%s.%s", recv, methodName))
   738  
   739  			case types.FieldVal:
   740  				fields, jsTag := fc.translateSelection(sel, f.Pos())
   741  				if jsTag != "" {
   742  					call := fc.formatExpr("%e.%s%s(%s)", f.X, strings.Join(fields, "."), formatJSStructTagVal(jsTag), externalizeArgs(e.Args))
   743  					switch sig.Results().Len() {
   744  					case 0:
   745  						return call
   746  					case 1:
   747  						return fc.internalize(call, sig.Results().At(0).Type())
   748  					default:
   749  						fc.pkgCtx.errList = append(fc.pkgCtx.errList, types.Error{Fset: fc.pkgCtx.fileSet, Pos: f.Pos(), Msg: "field with js tag can not have func type with multiple results"})
   750  					}
   751  				}
   752  				return fc.translateCall(e, sig, fc.formatExpr("%e.%s", f.X, strings.Join(fields, ".")))
   753  
   754  			case types.MethodExpr:
   755  				return fc.translateCall(e, sig, fc.translateExpr(f))
   756  
   757  			default:
   758  				panic(fmt.Sprintf("unexpected sel.Kind(): %T", sel.Kind()))
   759  			}
   760  		default:
   761  			return fc.translateCall(e, sig, fc.translateExpr(plainFun))
   762  		}
   763  
   764  	case *ast.StarExpr:
   765  		if typesutil.IsJsObject(fc.typeOf(e.X)) {
   766  			return fc.formatExpr("new $jsObjectPtr(%e)", e.X)
   767  		}
   768  		if c1, isCall := e.X.(*ast.CallExpr); isCall && len(c1.Args) == 1 {
   769  			if c2, isCall := c1.Args[0].(*ast.CallExpr); isCall && len(c2.Args) == 1 && types.Identical(fc.typeOf(c2.Fun), types.Typ[types.UnsafePointer]) {
   770  				if unary, isUnary := c2.Args[0].(*ast.UnaryExpr); isUnary && unary.Op == token.AND {
   771  					return fc.translateExpr(unary.X) // unsafe conversion
   772  				}
   773  			}
   774  		}
   775  		switch exprType.Underlying().(type) {
   776  		case *types.Struct, *types.Array:
   777  			return fc.translateExpr(e.X)
   778  		}
   779  		return fc.formatExpr("%e.$get()", e.X)
   780  
   781  	case *ast.TypeAssertExpr:
   782  		if e.Type == nil {
   783  			return fc.translateExpr(e.X)
   784  		}
   785  		t := fc.typeOf(e.Type)
   786  		if _, isTuple := exprType.(*types.Tuple); isTuple {
   787  			return fc.formatExpr("$assertType(%e, %s, true)", e.X, fc.typeName(t))
   788  		}
   789  		return fc.formatExpr("$assertType(%e, %s)", e.X, fc.typeName(t))
   790  
   791  	case *ast.Ident:
   792  		if e.Name == "_" {
   793  			panic("Tried to translate underscore identifier.")
   794  		}
   795  		switch o := inst.Object.(type) {
   796  		case *types.Var, *types.Const:
   797  			return fc.formatExpr("%s", fc.instName(inst))
   798  		case *types.Func:
   799  			return fc.formatExpr("%s", fc.instName(inst))
   800  		case *types.TypeName:
   801  			return fc.formatExpr("%s", fc.typeName(o.Type()))
   802  		case *types.Nil:
   803  			if typesutil.IsJsObject(exprType) {
   804  				return fc.formatExpr("null")
   805  			}
   806  			switch t := exprType.Underlying().(type) {
   807  			case *types.Basic:
   808  				if t.Kind() != types.UnsafePointer {
   809  					panic("unexpected basic type")
   810  				}
   811  				return fc.formatExpr("0")
   812  			case *types.Slice, *types.Pointer:
   813  				return fc.formatExpr("%s.nil", fc.typeName(exprType))
   814  			case *types.Chan:
   815  				return fc.formatExpr("$chanNil")
   816  			case *types.Map:
   817  				return fc.formatExpr("false")
   818  			case *types.Interface:
   819  				return fc.formatExpr("$ifaceNil")
   820  			case *types.Signature:
   821  				return fc.formatExpr("$throwNilPointerError")
   822  			default:
   823  				panic(fmt.Sprintf("unexpected type: %T", t))
   824  			}
   825  		default:
   826  			panic(fmt.Sprintf("Unhandled object: %T\n", o))
   827  		}
   828  
   829  	case nil:
   830  		return fc.formatExpr("")
   831  
   832  	default:
   833  		panic(fmt.Sprintf("Unhandled expression: %T\n", e))
   834  
   835  	}
   836  }
   837  
   838  func (fc *funcContext) translateCall(e *ast.CallExpr, sig *types.Signature, fun *expression) *expression {
   839  	args := fc.translateArgs(sig, e.Args, e.Ellipsis.IsValid())
   840  	if fc.Blocking[e] {
   841  		resumeCase := fc.caseCounter
   842  		fc.caseCounter++
   843  		returnVar := "$r"
   844  		if sig.Results().Len() != 0 {
   845  			returnVar = fc.newLocalVariable("_r")
   846  		}
   847  		fc.Printf("%[1]s = %[2]s(%[3]s); /* */ $s = %[4]d; case %[4]d: if($c) { $c = false; %[1]s = %[1]s.$blk(); } if (%[1]s && %[1]s.$blk !== undefined) { break s; }", returnVar, fun, strings.Join(args, ", "), resumeCase)
   848  		if sig.Results().Len() != 0 {
   849  			return fc.formatExpr("%s", returnVar)
   850  		}
   851  		return fc.formatExpr("")
   852  	}
   853  	return fc.formatExpr("%s(%s)", fun, strings.Join(args, ", "))
   854  }
   855  
   856  // delegatedCall returns a pair of JS expressions representing a callable function
   857  // and its arguments to be invoked elsewhere.
   858  //
   859  // This function is necessary in conjunction with keywords such as `go` and `defer`,
   860  // where we need to compute function and its arguments at the keyword site,
   861  // but the call itself will happen elsewhere (hence "delegated").
   862  //
   863  // Built-in functions and cetrain `js.Object` methods don't translate into JS
   864  // function calls, and need to be wrapped before they can be delegated, which
   865  // this function handles and returns JS expressions that are safe to delegate
   866  // and behave like a regular JS function and a list of its argument values.
   867  func (fc *funcContext) delegatedCall(expr *ast.CallExpr) (callable *expression, arglist *expression) {
   868  	isBuiltin := false
   869  	isJs := false
   870  	switch fun := expr.Fun.(type) {
   871  	case *ast.Ident:
   872  		_, isBuiltin = fc.pkgCtx.Uses[fun].(*types.Builtin)
   873  	case *ast.SelectorExpr:
   874  		isJs = typesutil.IsJsPackage(fc.pkgCtx.Uses[fun.Sel].Pkg())
   875  	}
   876  	sig := typesutil.Signature{Sig: fc.typeOf(expr.Fun).Underlying().(*types.Signature)}
   877  	args := fc.translateArgs(sig.Sig, expr.Args, expr.Ellipsis.IsValid())
   878  
   879  	if !isBuiltin && !isJs {
   880  		// Normal function calls don't require wrappers.
   881  		callable = fc.translateExpr(expr.Fun)
   882  		arglist = fc.formatExpr("[%s]", strings.Join(args, ", "))
   883  		return callable, arglist
   884  	}
   885  
   886  	// Since some builtins or js.Object methods may not transpile into
   887  	// callable expressions, we need to wrap then in a proxy lambda in order
   888  	// to push them onto the deferral stack.
   889  	vars := make([]string, len(expr.Args))
   890  	callArgs := make([]ast.Expr, len(expr.Args))
   891  	ellipsis := expr.Ellipsis
   892  
   893  	for i := range expr.Args {
   894  		v := fc.newLocalVariable("_arg")
   895  		vars[i] = v
   896  		// Subtle: the proxy lambda argument needs to be assigned with the type
   897  		// that the original function expects, and not with the argument
   898  		// expression result type, or we may do implicit type conversion twice.
   899  		callArgs[i] = fc.newIdent(v, sig.Param(i, ellipsis.IsValid()))
   900  	}
   901  	wrapper := &ast.CallExpr{
   902  		Fun:      expr.Fun,
   903  		Args:     callArgs,
   904  		Ellipsis: expr.Ellipsis,
   905  	}
   906  	callable = fc.formatExpr("function(%s) { %e; }", strings.Join(vars, ", "), wrapper)
   907  	arglist = fc.formatExpr("[%s]", strings.Join(args, ", "))
   908  	return callable, arglist
   909  }
   910  
   911  func (fc *funcContext) makeReceiver(e *ast.SelectorExpr) *expression {
   912  	sel, _ := fc.selectionOf(e)
   913  	if !sel.Obj().Exported() {
   914  		fc.pkgCtx.dependencies[sel.Obj()] = true
   915  	}
   916  
   917  	x := e.X
   918  	recvType := sel.Recv()
   919  	if len(sel.Index()) > 1 {
   920  		for _, index := range sel.Index()[:len(sel.Index())-1] {
   921  			if ptr, isPtr := recvType.(*types.Pointer); isPtr {
   922  				recvType = ptr.Elem()
   923  			}
   924  			s := recvType.Underlying().(*types.Struct)
   925  			recvType = s.Field(index).Type()
   926  		}
   927  
   928  		fakeSel := &ast.SelectorExpr{X: x, Sel: ast.NewIdent("o")}
   929  		fc.pkgCtx.additionalSelections[fakeSel] = typesutil.NewSelection(types.FieldVal, sel.Recv(), sel.Index()[:len(sel.Index())-1], nil, recvType)
   930  		x = fc.setType(fakeSel, recvType)
   931  	}
   932  
   933  	_, isPointer := recvType.Underlying().(*types.Pointer)
   934  	methodsRecvType := sel.Obj().Type().(*types.Signature).Recv().Type()
   935  	_, pointerExpected := methodsRecvType.(*types.Pointer)
   936  	if !isPointer && pointerExpected {
   937  		recvType = types.NewPointer(recvType)
   938  		x = fc.setType(&ast.UnaryExpr{Op: token.AND, X: x}, recvType)
   939  	}
   940  	if isPointer && !pointerExpected {
   941  		x = fc.setType(x, methodsRecvType)
   942  	}
   943  
   944  	recv := fc.translateImplicitConversionWithCloning(x, methodsRecvType)
   945  	if isWrapped(recvType) {
   946  		// Wrap JS-native value to have access to the Go type's methods.
   947  		recv = fc.formatExpr("new %s(%s)", fc.typeName(methodsRecvType), recv)
   948  	}
   949  	return recv
   950  }
   951  
   952  func (fc *funcContext) translateBuiltin(name string, sig *types.Signature, args []ast.Expr, ellipsis bool) *expression {
   953  	switch name {
   954  	case "new":
   955  		t := sig.Results().At(0).Type().(*types.Pointer)
   956  		if fc.pkgCtx.Pkg.Path() == "syscall" && types.Identical(t.Elem().Underlying(), types.Typ[types.Uintptr]) {
   957  			return fc.formatExpr("new Uint8Array(8)")
   958  		}
   959  		switch t.Elem().Underlying().(type) {
   960  		case *types.Struct, *types.Array:
   961  			return fc.formatExpr("%e", fc.zeroValue(t.Elem()))
   962  		default:
   963  			return fc.formatExpr("$newDataPointer(%e, %s)", fc.zeroValue(t.Elem()), fc.typeName(t))
   964  		}
   965  	case "make":
   966  		switch argType := fc.typeOf(args[0]).Underlying().(type) {
   967  		case *types.Slice:
   968  			t := fc.typeName(fc.typeOf(args[0]))
   969  			if len(args) == 3 {
   970  				return fc.formatExpr("$makeSlice(%s, %f, %f)", t, args[1], args[2])
   971  			}
   972  			return fc.formatExpr("$makeSlice(%s, %f)", t, args[1])
   973  		case *types.Map:
   974  			if len(args) == 2 && fc.pkgCtx.Types[args[1]].Value == nil {
   975  				return fc.formatExpr(`((%1f < 0 || %1f > 2147483647) ? $throwRuntimeError("makemap: size out of range") : new $global.Map())`, args[1])
   976  			}
   977  			return fc.formatExpr("new $global.Map()")
   978  		case *types.Chan:
   979  			length := "0"
   980  			if len(args) == 2 {
   981  				length = fc.formatExpr("%f", args[1]).String()
   982  			}
   983  			return fc.formatExpr("new $Chan(%s, %s)", fc.typeName(fc.typeOf(args[0]).Underlying().(*types.Chan).Elem()), length)
   984  		default:
   985  			panic(fmt.Sprintf("Unhandled make type: %T\n", argType))
   986  		}
   987  	case "len":
   988  		switch argType := fc.typeOf(args[0]).Underlying().(type) {
   989  		case *types.Basic:
   990  			return fc.formatExpr("%e.length", args[0])
   991  		case *types.Slice:
   992  			return fc.formatExpr("%e.$length", args[0])
   993  		case *types.Pointer:
   994  			return fc.formatExpr("(%e, %d)", args[0], argType.Elem().(*types.Array).Len())
   995  		case *types.Map:
   996  			return fc.formatExpr("(%e ? %e.size : 0)", args[0], args[0])
   997  		case *types.Chan:
   998  			return fc.formatExpr("%e.$buffer.length", args[0])
   999  		// length of array is constant
  1000  		default:
  1001  			panic(fmt.Sprintf("Unhandled len type: %T\n", argType))
  1002  		}
  1003  	case "cap":
  1004  		switch argType := fc.typeOf(args[0]).Underlying().(type) {
  1005  		case *types.Slice, *types.Chan:
  1006  			return fc.formatExpr("%e.$capacity", args[0])
  1007  		case *types.Pointer:
  1008  			return fc.formatExpr("(%e, %d)", args[0], argType.Elem().(*types.Array).Len())
  1009  		// capacity of array is constant
  1010  		default:
  1011  			panic(fmt.Sprintf("Unhandled cap type: %T\n", argType))
  1012  		}
  1013  	case "panic":
  1014  		return fc.formatExpr("$panic(%s)", fc.translateImplicitConversion(args[0], types.NewInterfaceType(nil, nil)))
  1015  	case "append":
  1016  		if ellipsis || len(args) == 1 {
  1017  			argStr := fc.translateArgs(sig, args, ellipsis)
  1018  			return fc.formatExpr("$appendSlice(%s, %s)", argStr[0], argStr[1])
  1019  		}
  1020  		sliceType := sig.Results().At(0).Type().Underlying().(*types.Slice)
  1021  		return fc.formatExpr("$append(%e, %s)", args[0], strings.Join(fc.translateExprSlice(args[1:], sliceType.Elem()), ", "))
  1022  	case "delete":
  1023  		args = fc.expandTupleArgs(args)
  1024  		keyType := fc.typeOf(args[0]).Underlying().(*types.Map).Key()
  1025  		return fc.formatExpr(
  1026  			`$mapDelete(%1e, %2s.keyFor(%3s))`,
  1027  			args[0],
  1028  			fc.typeName(keyType),
  1029  			fc.translateImplicitConversion(args[1], keyType),
  1030  		)
  1031  	case "copy":
  1032  		args = fc.expandTupleArgs(args)
  1033  		if basic, isBasic := fc.typeOf(args[1]).Underlying().(*types.Basic); isBasic && isString(basic) {
  1034  			return fc.formatExpr("$copyString(%e, %e)", args[0], args[1])
  1035  		}
  1036  		return fc.formatExpr("$copySlice(%e, %e)", args[0], args[1])
  1037  	case "print":
  1038  		args = fc.expandTupleArgs(args)
  1039  		return fc.formatExpr("$print(%s)", strings.Join(fc.translateExprSlice(args, nil), ", "))
  1040  	case "println":
  1041  		args = fc.expandTupleArgs(args)
  1042  		return fc.formatExpr("console.log(%s)", strings.Join(fc.translateExprSlice(args, nil), ", "))
  1043  	case "complex":
  1044  		argStr := fc.translateArgs(sig, args, ellipsis)
  1045  		return fc.formatExpr("new %s(%s, %s)", fc.typeName(sig.Results().At(0).Type()), argStr[0], argStr[1])
  1046  	case "real":
  1047  		return fc.formatExpr("%e.$real", args[0])
  1048  	case "imag":
  1049  		return fc.formatExpr("%e.$imag", args[0])
  1050  	case "recover":
  1051  		return fc.formatExpr("$recover()")
  1052  	case "close":
  1053  		return fc.formatExpr(`$close(%e)`, args[0])
  1054  	case "Sizeof":
  1055  		return fc.formatExpr("%d", sizes32.Sizeof(fc.typeOf(args[0])))
  1056  	case "Alignof":
  1057  		return fc.formatExpr("%d", sizes32.Alignof(fc.typeOf(args[0])))
  1058  	case "Offsetof":
  1059  		sel, _ := fc.selectionOf(astutil.RemoveParens(args[0]).(*ast.SelectorExpr))
  1060  		return fc.formatExpr("%d", typesutil.OffsetOf(sizes32, sel))
  1061  	default:
  1062  		panic(fmt.Sprintf("Unhandled builtin: %s\n", name))
  1063  	}
  1064  }
  1065  
  1066  func (fc *funcContext) identifierConstant(expr ast.Expr) (string, bool) {
  1067  	val := fc.pkgCtx.Types[expr].Value
  1068  	if val == nil {
  1069  		return "", false
  1070  	}
  1071  	s := constant.StringVal(val)
  1072  	if len(s) == 0 {
  1073  		return "", false
  1074  	}
  1075  	for i, c := range s {
  1076  		if !((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (i > 0 && c >= '0' && c <= '9') || c == '_' || c == '$') {
  1077  			return "", false
  1078  		}
  1079  	}
  1080  	return s, true
  1081  }
  1082  
  1083  func (fc *funcContext) translateExprSlice(exprs []ast.Expr, desiredType types.Type) []string {
  1084  	parts := make([]string, len(exprs))
  1085  	for i, expr := range exprs {
  1086  		parts[i] = fc.translateImplicitConversion(expr, desiredType).String()
  1087  	}
  1088  	return parts
  1089  }
  1090  
  1091  func (fc *funcContext) translateConversion(expr ast.Expr, desiredType types.Type) *expression {
  1092  	exprType := fc.typeOf(expr)
  1093  	if types.Identical(exprType, desiredType) {
  1094  		return fc.translateExpr(expr)
  1095  	}
  1096  
  1097  	if fc.pkgCtx.Pkg.Path() == "reflect" || fc.pkgCtx.Pkg.Path() == "internal/reflectlite" {
  1098  		if call, isCall := expr.(*ast.CallExpr); isCall && types.Identical(fc.typeOf(call.Fun), types.Typ[types.UnsafePointer]) {
  1099  			if ptr, isPtr := desiredType.(*types.Pointer); isPtr {
  1100  				if named, isNamed := ptr.Elem().(*types.Named); isNamed {
  1101  					switch named.Obj().Name() {
  1102  					case "arrayType", "chanType", "funcType", "interfaceType", "mapType", "ptrType", "sliceType", "structType":
  1103  						return fc.formatExpr("%e.kindType", call.Args[0]) // unsafe conversion
  1104  					default:
  1105  						return fc.translateExpr(expr)
  1106  					}
  1107  				}
  1108  			}
  1109  		}
  1110  	}
  1111  
  1112  	switch t := desiredType.Underlying().(type) {
  1113  	case *types.Basic:
  1114  		switch {
  1115  		case isInteger(t):
  1116  			basicExprType := exprType.Underlying().(*types.Basic)
  1117  			switch {
  1118  			case is64Bit(t):
  1119  				if !is64Bit(basicExprType) {
  1120  					if basicExprType.Kind() == types.Uintptr { // this might be an Object returned from reflect.Value.Pointer()
  1121  						return fc.formatExpr("new %1s(0, %2e.constructor === Number ? %2e : 1)", fc.typeName(desiredType), expr)
  1122  					}
  1123  					return fc.formatExpr("new %s(0, %e)", fc.typeName(desiredType), expr)
  1124  				}
  1125  				return fc.formatExpr("new %1s(%2h, %2l)", fc.typeName(desiredType), expr)
  1126  			case is64Bit(basicExprType):
  1127  				if !isUnsigned(t) && !isUnsigned(basicExprType) {
  1128  					return fc.fixNumber(fc.formatParenExpr("%1l + ((%1h >> 31) * 4294967296)", expr), t)
  1129  				}
  1130  				return fc.fixNumber(fc.formatExpr("%s.$low", fc.translateExpr(expr)), t)
  1131  			case types.Identical(exprType, types.Typ[types.UnsafePointer]):
  1132  				return fc.translateExpr(expr)
  1133  			default:
  1134  				return fc.fixNumber(fc.translateExpr(expr), t)
  1135  			}
  1136  		case isFloat(t):
  1137  			if t.Kind() == types.Float32 && exprType.Underlying().(*types.Basic).Kind() == types.Float64 {
  1138  				return fc.formatExpr("$fround(%e)", expr)
  1139  			}
  1140  			return fc.formatExpr("%f", expr)
  1141  		case isComplex(t):
  1142  			return fc.formatExpr("new %1s(%2r, %2i)", fc.typeName(desiredType), expr)
  1143  		case isString(t):
  1144  			value := fc.translateExpr(expr)
  1145  			switch et := exprType.Underlying().(type) {
  1146  			case *types.Basic:
  1147  				if is64Bit(et) {
  1148  					value = fc.formatExpr("%s.$low", value)
  1149  				}
  1150  				if isNumeric(et) {
  1151  					return fc.formatExpr("$encodeRune(%s)", value)
  1152  				}
  1153  				return value
  1154  			case *types.Slice:
  1155  				if types.Identical(et.Elem().Underlying(), types.Typ[types.Rune]) {
  1156  					return fc.formatExpr("$runesToString(%s)", value)
  1157  				}
  1158  				return fc.formatExpr("$bytesToString(%s)", value)
  1159  			default:
  1160  				panic(fmt.Sprintf("Unhandled conversion: %v\n", et))
  1161  			}
  1162  		case t.Kind() == types.UnsafePointer:
  1163  			if unary, isUnary := expr.(*ast.UnaryExpr); isUnary && unary.Op == token.AND {
  1164  				if indexExpr, isIndexExpr := unary.X.(*ast.IndexExpr); isIndexExpr {
  1165  					return fc.formatExpr("$sliceToNativeArray(%s)", fc.translateConversionToSlice(indexExpr.X, types.NewSlice(types.Typ[types.Uint8])))
  1166  				}
  1167  				if ident, isIdent := unary.X.(*ast.Ident); isIdent && ident.Name == "_zero" {
  1168  					return fc.formatExpr("new Uint8Array(0)")
  1169  				}
  1170  			}
  1171  			if ptr, isPtr := fc.typeOf(expr).(*types.Pointer); fc.pkgCtx.Pkg.Path() == "syscall" && isPtr {
  1172  				if s, isStruct := ptr.Elem().Underlying().(*types.Struct); isStruct {
  1173  					array := fc.newLocalVariable("_array")
  1174  					target := fc.newLocalVariable("_struct")
  1175  					fc.Printf("%s = new Uint8Array(%d);", array, sizes32.Sizeof(s))
  1176  					fc.Delayed(func() {
  1177  						fc.Printf("%s = %s, %s;", target, fc.translateExpr(expr), fc.loadStruct(array, target, s))
  1178  					})
  1179  					return fc.formatExpr("%s", array)
  1180  				}
  1181  			}
  1182  			if call, ok := expr.(*ast.CallExpr); ok {
  1183  				if id, ok := call.Fun.(*ast.Ident); ok && id.Name == "new" {
  1184  					return fc.formatExpr("new Uint8Array(%d)", int(sizes32.Sizeof(fc.typeOf(call.Args[0]))))
  1185  				}
  1186  			}
  1187  		}
  1188  
  1189  	case *types.Slice:
  1190  		switch et := exprType.Underlying().(type) {
  1191  		case *types.Basic:
  1192  			if isString(et) {
  1193  				if types.Identical(t.Elem().Underlying(), types.Typ[types.Rune]) {
  1194  					return fc.formatExpr("new %s($stringToRunes(%e))", fc.typeName(desiredType), expr)
  1195  				}
  1196  				return fc.formatExpr("new %s($stringToBytes(%e))", fc.typeName(desiredType), expr)
  1197  			}
  1198  		case *types.Array, *types.Pointer:
  1199  			return fc.formatExpr("new %s(%e)", fc.typeName(desiredType), expr)
  1200  		}
  1201  
  1202  	case *types.Pointer:
  1203  		if types.Identical(exprType, types.Typ[types.UntypedNil]) {
  1204  			// Fall through to the fc.translateImplicitConversionWithCloning(), which
  1205  			// handles conversion from untyped nil to a pointer type.
  1206  			break
  1207  		}
  1208  
  1209  		switch ptrElType := t.Elem().Underlying().(type) {
  1210  		case *types.Array: // (*[N]T)(expr) — converting expr to a pointer to an array.
  1211  			if _, ok := exprType.Underlying().(*types.Slice); ok {
  1212  				return fc.formatExpr("$sliceToGoArray(%e, %s)", expr, fc.typeName(desiredType))
  1213  			}
  1214  			// TODO(nevkontakte): Is this just for aliased types (e.g. `type a [4]byte`)?
  1215  			return fc.translateExpr(expr)
  1216  		case *types.Struct: // (*StructT)(expr) — converting expr to a pointer to a struct.
  1217  			if fc.pkgCtx.Pkg.Path() == "syscall" && types.Identical(exprType, types.Typ[types.UnsafePointer]) {
  1218  				// Special case: converting an unsafe pointer to a byte array into a
  1219  				// struct pointer when handling syscalls.
  1220  				// TODO(nevkontakte): Add a runtime assertion that the unsafe.Pointer is
  1221  				// indeed pointing at a byte array.
  1222  				array := fc.newLocalVariable("_array")
  1223  				target := fc.newLocalVariable("_struct")
  1224  				return fc.formatExpr("(%s = %e, %s = %e, %s, %s)", array, expr, target, fc.zeroValue(t.Elem()), fc.loadStruct(array, target, ptrElType), target)
  1225  			}
  1226  			// Convert between structs of different types but identical layouts,
  1227  			// for example:
  1228  			// type A struct { foo int }; type B A; var a *A = &A{42}; var b *B = (*B)(a)
  1229  			//
  1230  			// TODO(nevkontakte): Should this only apply when exprType is a pointer to a
  1231  			// struct as well?
  1232  			return fc.formatExpr("$pointerOfStructConversion(%e, %s)", expr, fc.typeName(desiredType))
  1233  		}
  1234  
  1235  		if types.Identical(exprType, types.Typ[types.UnsafePointer]) {
  1236  			// TODO(nevkontakte): Why do we fall through to the implicit conversion here?
  1237  			// Conversion from unsafe.Pointer() requires explicit type conversion: https://play.golang.org/p/IQxtmpn1wgc.
  1238  			// Possibly related to https://github.com/gopherjs/gopherjs/issues/1001.
  1239  			break // Fall through to fc.translateImplicitConversionWithCloning() below.
  1240  		}
  1241  		// Handle remaining cases, for example:
  1242  		// type iPtr *int; var c int = 42; println((iPtr)(&c));
  1243  		// TODO(nevkontakte): Are there any other cases that fall into this case?
  1244  		exprTypeElem := exprType.Underlying().(*types.Pointer).Elem()
  1245  		ptrVar := fc.newLocalVariable("_ptr")
  1246  		getterConv := fc.translateConversion(fc.setType(&ast.StarExpr{X: fc.newIdent(ptrVar, exprType)}, exprTypeElem), t.Elem())
  1247  		setterConv := fc.translateConversion(fc.newIdent("$v", t.Elem()), exprTypeElem)
  1248  		return fc.formatExpr("(%1s = %2e, new %3s(function() { return %4s; }, function($v) { %1s.$set(%5s); }, %1s.$target))", ptrVar, expr, fc.typeName(desiredType), getterConv, setterConv)
  1249  
  1250  	case *types.Interface:
  1251  		if types.Identical(exprType, types.Typ[types.UnsafePointer]) {
  1252  			return fc.translateExpr(expr)
  1253  		}
  1254  	}
  1255  
  1256  	return fc.translateImplicitConversionWithCloning(expr, desiredType)
  1257  }
  1258  
  1259  func (fc *funcContext) translateImplicitConversionWithCloning(expr ast.Expr, desiredType types.Type) *expression {
  1260  	switch desiredType.Underlying().(type) {
  1261  	case *types.Struct, *types.Array:
  1262  		return fc.formatExpr("$clone(%e, %s)", expr, fc.typeName(desiredType))
  1263  	}
  1264  
  1265  	return fc.translateImplicitConversion(expr, desiredType)
  1266  }
  1267  
  1268  func (fc *funcContext) translateImplicitConversion(expr ast.Expr, desiredType types.Type) *expression {
  1269  	if desiredType == nil {
  1270  		return fc.translateExpr(expr)
  1271  	}
  1272  
  1273  	exprType := fc.typeOf(expr)
  1274  	if types.Identical(exprType, desiredType) {
  1275  		return fc.translateExpr(expr)
  1276  	}
  1277  
  1278  	basicExprType, isBasicExpr := exprType.Underlying().(*types.Basic)
  1279  	if isBasicExpr && basicExprType.Kind() == types.UntypedNil {
  1280  		return fc.formatExpr("%e", fc.zeroValue(desiredType))
  1281  	}
  1282  
  1283  	switch desiredType.Underlying().(type) {
  1284  	case *types.Slice:
  1285  		return fc.formatExpr("$convertSliceType(%1e, %2s)", expr, fc.typeName(desiredType))
  1286  
  1287  	case *types.Interface:
  1288  		if typesutil.IsJsObject(exprType) {
  1289  			// wrap JS object into js.Object struct when converting to interface
  1290  			return fc.formatExpr("new $jsObjectPtr(%e)", expr)
  1291  		}
  1292  		if isWrapped(exprType) {
  1293  			return fc.formatExpr("new %s(%e)", fc.typeName(exprType), expr)
  1294  		}
  1295  		if _, isStruct := exprType.Underlying().(*types.Struct); isStruct {
  1296  			return fc.formatExpr("new %1e.constructor.elem(%1e)", expr)
  1297  		}
  1298  	}
  1299  
  1300  	return fc.translateExpr(expr)
  1301  }
  1302  
  1303  func (fc *funcContext) translateConversionToSlice(expr ast.Expr, desiredType types.Type) *expression {
  1304  	switch fc.typeOf(expr).Underlying().(type) {
  1305  	case *types.Array, *types.Pointer:
  1306  		return fc.formatExpr("new %s(%e)", fc.typeName(desiredType), expr)
  1307  	}
  1308  	return fc.translateExpr(expr)
  1309  }
  1310  
  1311  func (fc *funcContext) loadStruct(array, target string, s *types.Struct) string {
  1312  	view := fc.newLocalVariable("_view")
  1313  	code := fmt.Sprintf("%s = new DataView(%s.buffer, %s.byteOffset)", view, array, array)
  1314  	var fields []*types.Var
  1315  	var collectFields func(s *types.Struct, path string)
  1316  	collectFields = func(s *types.Struct, path string) {
  1317  		for i := 0; i < s.NumFields(); i++ {
  1318  			field := s.Field(i)
  1319  			if fs, isStruct := field.Type().Underlying().(*types.Struct); isStruct {
  1320  				collectFields(fs, path+"."+fieldName(s, i))
  1321  				continue
  1322  			}
  1323  			fields = append(fields, types.NewVar(0, nil, path+"."+fieldName(s, i), field.Type()))
  1324  		}
  1325  	}
  1326  	collectFields(s, target)
  1327  	offsets := sizes32.Offsetsof(fields)
  1328  	for i, field := range fields {
  1329  		switch t := field.Type().Underlying().(type) {
  1330  		case *types.Basic:
  1331  			if isNumeric(t) {
  1332  				if is64Bit(t) {
  1333  					code += fmt.Sprintf(", %s = new %s(%s.getUint32(%d, true), %s.getUint32(%d, true))", field.Name(), fc.typeName(field.Type()), view, offsets[i]+4, view, offsets[i])
  1334  					break
  1335  				}
  1336  				code += fmt.Sprintf(", %s = %s.get%s(%d, true)", field.Name(), view, toJavaScriptType(t), offsets[i])
  1337  			}
  1338  		case *types.Array:
  1339  			code += fmt.Sprintf(`, %s = new ($nativeArray(%s))(%s.buffer, $min(%s.byteOffset + %d, %s.buffer.byteLength))`, field.Name(), typeKind(t.Elem()), array, array, offsets[i], array)
  1340  		}
  1341  		// TODO(nevkontakte): Explicitly panic if unsupported field type is encountered?
  1342  	}
  1343  	return code
  1344  }
  1345  
  1346  func (fc *funcContext) fixNumber(value *expression, basic *types.Basic) *expression {
  1347  	switch basic.Kind() {
  1348  	case types.Int8:
  1349  		return fc.formatParenExpr("%s << 24 >> 24", value)
  1350  	case types.Uint8:
  1351  		return fc.formatParenExpr("%s << 24 >>> 24", value)
  1352  	case types.Int16:
  1353  		return fc.formatParenExpr("%s << 16 >> 16", value)
  1354  	case types.Uint16:
  1355  		return fc.formatParenExpr("%s << 16 >>> 16", value)
  1356  	case types.Int32, types.Int, types.UntypedInt:
  1357  		return fc.formatParenExpr("%s >> 0", value)
  1358  	case types.Uint32, types.Uint, types.Uintptr:
  1359  		return fc.formatParenExpr("%s >>> 0", value)
  1360  	case types.Float32:
  1361  		return fc.formatExpr("$fround(%s)", value)
  1362  	case types.Float64:
  1363  		return value
  1364  	default:
  1365  		panic(fmt.Sprintf("fixNumber: unhandled basic.Kind(): %s", basic.String()))
  1366  	}
  1367  }
  1368  
  1369  func (fc *funcContext) internalize(s *expression, t types.Type) *expression {
  1370  	if typesutil.IsJsObject(t) {
  1371  		return s
  1372  	}
  1373  	switch u := t.Underlying().(type) {
  1374  	case *types.Basic:
  1375  		switch {
  1376  		case isBoolean(u):
  1377  			return fc.formatExpr("!!(%s)", s)
  1378  		case isInteger(u) && !is64Bit(u):
  1379  			return fc.fixNumber(fc.formatExpr("$parseInt(%s)", s), u)
  1380  		case isFloat(u):
  1381  			return fc.formatExpr("$parseFloat(%s)", s)
  1382  		}
  1383  	}
  1384  	return fc.formatExpr("$internalize(%s, %s)", s, fc.typeName(t))
  1385  }
  1386  
  1387  func (fc *funcContext) formatExpr(format string, a ...interface{}) *expression {
  1388  	return fc.formatExprInternal(format, a, false)
  1389  }
  1390  
  1391  func (fc *funcContext) formatParenExpr(format string, a ...interface{}) *expression {
  1392  	return fc.formatExprInternal(format, a, true)
  1393  }
  1394  
  1395  func (fc *funcContext) formatExprInternal(format string, a []interface{}, parens bool) *expression {
  1396  	processFormat := func(f func(uint8, uint8, int)) {
  1397  		n := 0
  1398  		for i := 0; i < len(format); i++ {
  1399  			b := format[i]
  1400  			if b == '%' {
  1401  				i++
  1402  				k := format[i]
  1403  				if k >= '0' && k <= '9' {
  1404  					n = int(k - '0' - 1)
  1405  					i++
  1406  					k = format[i]
  1407  				}
  1408  				f(0, k, n)
  1409  				n++
  1410  				continue
  1411  			}
  1412  			f(b, 0, 0)
  1413  		}
  1414  	}
  1415  
  1416  	counts := make([]int, len(a))
  1417  	processFormat(func(b, k uint8, n int) {
  1418  		switch k {
  1419  		case 'e', 'f', 'h', 'l', 'r', 'i':
  1420  			counts[n]++
  1421  		}
  1422  	})
  1423  
  1424  	out := bytes.NewBuffer(nil)
  1425  	vars := make([]string, len(a))
  1426  	hasAssignments := false
  1427  	for i, e := range a {
  1428  		if counts[i] <= 1 {
  1429  			continue
  1430  		}
  1431  		if _, isIdent := e.(*ast.Ident); isIdent {
  1432  			continue
  1433  		}
  1434  		if val := fc.pkgCtx.Types[e.(ast.Expr)].Value; val != nil {
  1435  			continue
  1436  		}
  1437  		if !hasAssignments {
  1438  			hasAssignments = true
  1439  			out.WriteByte('(')
  1440  			parens = false
  1441  		}
  1442  		v := fc.newLocalVariable("x")
  1443  		out.WriteString(v + " = " + fc.translateExpr(e.(ast.Expr)).String() + ", ")
  1444  		vars[i] = v
  1445  	}
  1446  
  1447  	processFormat(func(b, k uint8, n int) {
  1448  		writeExpr := func(suffix string) {
  1449  			if vars[n] != "" {
  1450  				out.WriteString(vars[n] + suffix)
  1451  				return
  1452  			}
  1453  			out.WriteString(fc.translateExpr(a[n].(ast.Expr)).StringWithParens() + suffix)
  1454  		}
  1455  		switch k {
  1456  		case 0:
  1457  			out.WriteByte(b)
  1458  		case 's':
  1459  			if e, ok := a[n].(*expression); ok {
  1460  				out.WriteString(e.StringWithParens())
  1461  				return
  1462  			}
  1463  			out.WriteString(a[n].(string))
  1464  		case 'd':
  1465  			fmt.Fprintf(out, "%d", a[n])
  1466  		case 't':
  1467  			out.WriteString(a[n].(token.Token).String())
  1468  		case 'e':
  1469  			e := a[n].(ast.Expr)
  1470  			if val := fc.pkgCtx.Types[e].Value; val != nil {
  1471  				out.WriteString(fc.translateExpr(e).String())
  1472  				return
  1473  			}
  1474  			writeExpr("")
  1475  		case 'f':
  1476  			e := a[n].(ast.Expr)
  1477  			if val := fc.pkgCtx.Types[e].Value; val != nil {
  1478  				d, _ := constant.Int64Val(constant.ToInt(val))
  1479  				out.WriteString(strconv.FormatInt(d, 10))
  1480  				return
  1481  			}
  1482  			if is64Bit(fc.typeOf(e).Underlying().(*types.Basic)) {
  1483  				out.WriteString("$flatten64(")
  1484  				writeExpr("")
  1485  				out.WriteString(")")
  1486  				return
  1487  			}
  1488  			writeExpr("")
  1489  		case 'h':
  1490  			e := a[n].(ast.Expr)
  1491  			if val := fc.pkgCtx.Types[e].Value; val != nil {
  1492  				d, _ := constant.Uint64Val(constant.ToInt(val))
  1493  				if fc.typeOf(e).Underlying().(*types.Basic).Kind() == types.Int64 {
  1494  					out.WriteString(strconv.FormatInt(int64(d)>>32, 10))
  1495  					return
  1496  				}
  1497  				out.WriteString(strconv.FormatUint(d>>32, 10))
  1498  				return
  1499  			}
  1500  			writeExpr(".$high")
  1501  		case 'l':
  1502  			if val := fc.pkgCtx.Types[a[n].(ast.Expr)].Value; val != nil {
  1503  				d, _ := constant.Uint64Val(constant.ToInt(val))
  1504  				out.WriteString(strconv.FormatUint(d&(1<<32-1), 10))
  1505  				return
  1506  			}
  1507  			writeExpr(".$low")
  1508  		case 'r':
  1509  			if val := fc.pkgCtx.Types[a[n].(ast.Expr)].Value; val != nil {
  1510  				r, _ := constant.Float64Val(constant.Real(val))
  1511  				out.WriteString(strconv.FormatFloat(r, 'g', -1, 64))
  1512  				return
  1513  			}
  1514  			writeExpr(".$real")
  1515  		case 'i':
  1516  			if val := fc.pkgCtx.Types[a[n].(ast.Expr)].Value; val != nil {
  1517  				i, _ := constant.Float64Val(constant.Imag(val))
  1518  				out.WriteString(strconv.FormatFloat(i, 'g', -1, 64))
  1519  				return
  1520  			}
  1521  			writeExpr(".$imag")
  1522  		case '%':
  1523  			out.WriteRune('%')
  1524  		default:
  1525  			panic(fmt.Sprintf("formatExpr: %%%c%d", k, n))
  1526  		}
  1527  	})
  1528  
  1529  	if hasAssignments {
  1530  		out.WriteByte(')')
  1531  	}
  1532  	return &expression{str: out.String(), parens: parens}
  1533  }