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