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