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 }