1/* 2 * gomacro - A Go interpreter with Lisp-like macros 3 * 4 * Copyright (C) 2017-2019 Massimiliano Ghilardi 5 * 6 * This Source Code Form is subject to the terms of the Mozilla Public 7 * License, v. 2.0. If a copy of the MPL was not distributed with this 8 * file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 * 10 * 11 * statement.go 12 * 13 * Created on Apr 01, 2017 14 * Author Massimiliano Ghilardi 15 */ 16 17package fast 18 19import ( 20 "go/ast" 21 "go/token" 22 r "reflect" 23 "sort" 24 25 . "github.com/cosmos72/gomacro/base" 26 "github.com/cosmos72/gomacro/base/output" 27 "github.com/cosmos72/gomacro/gls" 28) 29 30func stmtNop(env *Env) (Stmt, *Env) { 31 ip := env.IP + 1 32 env.IP = ip 33 return env.Code[ip], env 34} 35 36func popEnv(env *Env) (Stmt, *Env) { 37 outer := env.Outer 38 outer.IP = env.IP + 1 39 env.FreeEnv() 40 return outer.Code[outer.IP], outer 41} 42 43func (c *Comp) Stmt(in ast.Stmt) { 44 var labels []string 45 // DebugSource // codelen := len(c.Code.List) 46 for { 47 if in != nil { 48 c.Pos = in.Pos() 49 if isBreakpoint(in) { 50 c.append(c.breakpoint()) 51 break 52 } 53 } 54 switch node := in.(type) { 55 case nil: 56 case *ast.AssignStmt: 57 c.Assign(node) 58 case *ast.BlockStmt: 59 c.Block(node) 60 case *ast.BranchStmt: 61 c.Branch(node) 62 case *ast.CaseClause: 63 c.misplacedCase(node, node.List == nil) 64 case *ast.CommClause: 65 c.misplacedCase(node, node.Comm == nil) 66 case *ast.DeclStmt: 67 c.Decl(node.Decl) 68 case *ast.DeferStmt: 69 c.Defer(node) 70 case *ast.EmptyStmt: 71 // nothing to do 72 case *ast.ExprStmt: 73 expr := c.expr(node.X, nil) 74 if !expr.Const() { 75 c.Append(expr.AsStmt(c), in.Pos()) 76 } 77 case *ast.ForStmt: 78 c.For(node, labels) 79 case *ast.GoStmt: 80 c.Go(node) 81 case *ast.IfStmt: 82 c.If(node) 83 case *ast.IncDecStmt: 84 c.IncDec(node) 85 case *ast.LabeledStmt: 86 label := node.Label.Name 87 labels = append(labels, label) 88 ip := c.Code.Len() 89 if c.Labels == nil { 90 c.Labels = map[string]*int{label: &ip} 91 } else if addr := c.Labels[label]; addr != nil { 92 *addr = ip 93 } else { 94 c.Labels[label] = &ip 95 } 96 in = node.Stmt 97 continue 98 case *ast.RangeStmt: 99 c.Range(node, labels) 100 case *ast.ReturnStmt: 101 c.Return(node) 102 case *ast.SelectStmt: 103 c.Select(node, labels) 104 case *ast.SendStmt: 105 c.Send(node) 106 case *ast.SwitchStmt: 107 c.Switch(node, labels) 108 case *ast.TypeSwitchStmt: 109 c.TypeSwitch(node, labels) 110 default: 111 c.Errorf("unimplemented statement: %v // %T", node, node) 112 } 113 break 114 } 115 // DebugSource // c.showStatementsSource(in, codelen) 116} 117 118/* DebugSource */ 119func (c *Comp) showStatementsSource(in ast.Stmt, startIP int) { 120 n1, n2 := len(c.Code.List), len(c.Code.DebugPos) 121 if n1 != n2 { 122 c.Warnf("code mismatch: len(c.Code.List) = %d differs from len(c.Code.DebugPos) = %d", 123 n1, n2) 124 } 125 g := &c.Globals 126 g.Fprintf(g.Stdout, "source for statement: %v // %T\n", in, in) 127 for ip := startIP; ip < n2; ip++ { 128 c.showStatementSource(ip) 129 } 130} 131 132/* DebugSource */ 133func (c *Comp) showStatementSource(ip int) { 134 code := c.Code 135 list := code.List 136 debugp := code.DebugPos 137 g := &c.Globals 138 if ip < len(debugp) && g.Fileset != nil { 139 p := debugp[ip] 140 source, pos := g.Fileset.Source(p) 141 if ip < len(list) { 142 g.Fprintf(g.Stdout, "IP = % 3d: statement %p at [% 3d] %s\n", ip, list[ip], p, pos) 143 } else { 144 g.Fprintf(g.Stdout, "IP = % 3d: unknown statement at [% 3d] %s\n", ip, p, pos) 145 } 146 if len(source) != 0 { 147 g.Fprintf(g.Stdout, "%s\n", source) 148 c.showCaret(source, pos.Column) 149 } 150 } 151} 152 153var spaces = []byte(" ") 154 155func (c *Comp) showCaret(source string, col int) { 156 col-- 157 n := len(source) 158 if col >= 0 && col < n && n >= 3 { 159 out := c.Globals.Stdout 160 chunk := len(spaces) 161 for col >= chunk { 162 out.Write(spaces) 163 col -= chunk 164 } 165 out.Write(spaces[:col]) 166 out.Write([]byte("^^^\n")) 167 } 168} 169 170// Block compiles a block statement, i.e. { ... } 171func (c *Comp) Block(block *ast.BlockStmt) { 172 if block == nil || len(block.List) == 0 { 173 return 174 } 175 c.List(block.List) 176} 177 178// List compiles a slice of statements 179func (c *Comp) List(list []ast.Stmt) { 180 if len(list) == 0 { 181 c.Errorf("List invoked on empty statement list") 182 } 183 var nbinds [2]int // # of binds in the block 184 185 c2, locals := c.pushEnvIfLocalBinds(&nbinds, list...) 186 187 for _, node := range list { 188 c2.Stmt(node) 189 } 190 191 c2.popEnvIfLocalBinds(locals, &nbinds, list...) 192 193 // c.Debugf("List compiled. inner *Comp = %#v", c2) 194} 195 196// Branch compiles a break, continue, fallthrough or goto statement 197func (c *Comp) Branch(node *ast.BranchStmt) { 198 switch node.Tok { 199 case token.BREAK: 200 c.Break(node) 201 case token.CONTINUE: 202 c.Continue(node) 203 case token.FALLTHROUGH: 204 c.misplacedFallthrough() 205 case token.GOTO: 206 c.Goto(node) 207 default: 208 c.Errorf("unimplemented branch statement: %v <%v>", node, r.TypeOf(node)) 209 } 210} 211 212// Break compiles a "break" statement 213func (c *Comp) Break(node *ast.BranchStmt) { 214 label := "" 215 if node.Label != nil { 216 label = node.Label.Name 217 } 218 upn := 0 219 // do not cross function boundaries 220 for o := c; o != nil && o.Func == nil; o = o.Outer { 221 if o.Loop != nil && o.Loop.Break != nil { 222 if len(label) == 0 || o.Loop.HasLabel(label) { 223 // only keep a reference to the jump target, NOT TO THE WHOLE *Comp! 224 c.jumpOut(upn, o.Loop.Break) 225 return 226 } 227 } 228 upn += o.UpCost // count how many Env:s we must exit at runtime 229 } 230 if len(label) != 0 { 231 c.Errorf("break label not defined: %v", label) 232 } else { 233 c.Errorf("break outside for/switch") 234 } 235} 236 237// Continue compiles a "continue" statement 238func (c *Comp) Continue(node *ast.BranchStmt) { 239 label := "" 240 if node.Label != nil { 241 label = node.Label.Name 242 } 243 upn := 0 244 // do not cross function boundaries 245 for o := c; o != nil && o.Func == nil; o = o.Outer { 246 if o.Loop != nil && o.Loop.Continue != nil { 247 if len(label) == 0 || o.Loop.HasLabel(label) { 248 // only keep a reference to the jump target, NOT TO THE WHOLE *Comp! 249 c.jumpOut(upn, o.Loop.Continue) 250 return 251 } 252 } 253 upn += o.UpCost // count how many Env:s we must exit at runtime 254 } 255 if len(label) != 0 { 256 c.Errorf("continue label not defined: %v", label) 257 } else { 258 c.Errorf("continue outside for") 259 } 260} 261 262// Goto compiles a "goto" statement 263func (c *Comp) Goto(node *ast.BranchStmt) { 264 if node.Label == nil { 265 c.Errorf("goto without label: %v", node) 266 } 267 label := node.Label.Name 268 upn := 0 269 // do not cross function boundaries 270 for o := c; o != nil && o.Func == nil; o = o.Outer { 271 if ip := o.Labels[label]; ip != nil { 272 // only keep a reference to the jump target, NOT TO THE WHOLE *Comp! 273 c.jumpOut(upn, ip) 274 return 275 } 276 upn += o.UpCost // count how many Env:s we must exit at runtime 277 } 278 c.Errorf("goto label not found: %v", label) 279} 280 281// Defer compiles a "defer" statement 282func (c *Comp) Defer(node *ast.DeferStmt) { 283 call := c.prepareCall(node.Call, nil) 284 fun := call.Fun.AsX1() 285 argfuns := call.MakeArgfunsX1() 286 ellipsis := call.Ellipsis 287 c.Append(func(env *Env) (Stmt, *Env) { 288 // Go specs: arguments of a defer call are evaluated immediately. 289 // the call itself is executed when the function containing defer returns, 290 // either normally or with a panic 291 f := fun(env) 292 if f.CanSet() { 293 f = f.Convert(f.Type()) // make a copy 294 } 295 args := make([]r.Value, len(argfuns)) 296 for i, argfun := range argfuns { 297 v := argfun(env) 298 if v.CanSet() { 299 v = v.Convert(v.Type()) // make a copy 300 } 301 args[i] = v 302 } 303 env.IP++ 304 run := env.Run 305 if ellipsis { 306 run.InstallDefer = func() { 307 f.CallSlice(args) 308 } 309 } else { 310 run.InstallDefer = func() { 311 f.Call(args) 312 } 313 } 314 run.Signals.Sync = SigDefer 315 return run.Interrupt, env 316 }, node.Pos()) 317 c.Code.WithDefers = true 318} 319 320// jumpOut compiles a break or continue statement 321// ip is a pointer because the jump target may not be known yet... it will be filled later 322func (c *Comp) jumpOut(upn int, ip *int) { 323 var stmt Stmt 324 switch upn { 325 case 0: 326 stmt = func(env *Env) (Stmt, *Env) { 327 ip := *ip 328 env.IP = ip 329 return env.Code[ip], env 330 } 331 case 1: 332 stmt = func(env *Env) (Stmt, *Env) { 333 env = env.Outer 334 ip := *ip 335 env.IP = ip 336 return env.Code[ip], env 337 } 338 case 2: 339 stmt = func(env *Env) (Stmt, *Env) { 340 env = env.Outer.Outer 341 ip := *ip 342 env.IP = ip 343 return env.Code[ip], env 344 } 345 default: 346 stmt = func(env *Env) (Stmt, *Env) { 347 env = env.Outer.Outer.Outer 348 for i := 3; i < upn; i++ { 349 env = env.Outer 350 } 351 ip := *ip 352 env.IP = ip 353 return env.Code[ip], env 354 } 355 } 356 c.append(stmt) 357} 358 359// For compiles a "for" statement 360func (c *Comp) For(node *ast.ForStmt, labels []string) { 361 initLocals := false 362 var initBinds [2]int 363 364 c, initLocals = c.pushEnvIfLocalBinds(&initBinds, node.Init) 365 if node.Init != nil { 366 c.Stmt(node.Init) 367 } 368 flag, fun, err := true, (func(*Env) bool)(nil), false // "for { }" without a condition means "for true { }" 369 if node.Cond != nil { 370 pred := c.Expr(node.Cond, nil) 371 flag, fun, err = pred.TryAsPred() 372 if err { 373 c.invalidPred(node.Cond, pred) 374 return 375 } 376 } 377 var jump struct{ Cond, Post, Break int } 378 sort.Strings(labels) 379 // we need a fresh Comp here... created above by c.pushEnvIfLocalBinds() 380 c.Loop = &LoopInfo{ 381 Continue: &jump.Post, 382 Break: &jump.Break, 383 ThisLabels: labels, 384 } 385 386 // compile the condition, if not a constant 387 jump.Cond = c.Code.Len() 388 if fun != nil { 389 stmt := func(env *Env) (Stmt, *Env) { 390 var ip int 391 if fun(env) { 392 ip = env.IP + 1 393 // Debugf("for: condition = true, iterating. IntBinds = %v", env.IntBinds) 394 } else { 395 // Debugf("for: condition = false, exiting. IntBinds = %v", env.IntBinds) 396 ip = jump.Break 397 } 398 env.IP = ip 399 return env.Code[ip], env 400 } 401 c.Append(stmt, node.Cond.Pos()) 402 } 403 // compile the body 404 c.Block(node.Body) 405 // compile the post 406 if node.Post == nil { 407 jump.Post = jump.Cond // no post statement. "continue" jumps to the condition 408 } else { 409 jump.Post = c.Code.Len() 410 if containLocalBinds(node.Post) { 411 c.Errorf("invalid for: cannot declare new variables in post statement: %v", node.Post) 412 } 413 c.Stmt(node.Post) 414 } 415 c.Append(func(env *Env) (Stmt, *Env) { 416 // jump back to the condition 417 // Debugf("for: body executed, jumping back to condition. IntBinds = %v", env.IntBinds) 418 // time.Sleep(time.Second / 10) 419 ip := jump.Cond 420 env.IP = ip 421 return env.Code[ip], env 422 }, node.End()-1) 423 if fun == nil && !flag { 424 // "for false { }" means that body, post and jump back to condition are never executed... 425 // still compiled above (to check for errors) but drop the generated code 426 c.Code.Truncate(jump.Cond) 427 } 428 jump.Break = c.Code.Len() 429 430 c = c.popEnvIfLocalBinds(initLocals, &initBinds, node.Init) 431} 432 433// Go compiles a "go" statement i.e. a goroutine 434func (c *Comp) Go(node *ast.GoStmt) { 435 // we must create a new ThreadGlobals with a new Pool. 436 // Ideally, the new ThreadGlobals could be created inside the call, 437 // but that requires modifying the function being executed. 438 // Instead, we create the new ThreadGlobals here and wrap it into an "unnecessary" Env 439 // Thus we must create a corresponding "unnecessary" Comp and use it to compile the call 440 c2 := NewComp(c, &c.Code) 441 442 call := c2.prepareCall(node.Call, nil) 443 exprfun := call.Fun.AsX1() 444 argfunsX1 := call.MakeArgfunsX1() 445 446 var debugC *Comp 447 if c2.Globals.Options&OptDebugger != 0 { 448 // keep a reference to c2 only if needed 449 debugC = c2 450 } 451 452 stmt := func(env *Env) (Stmt, *Env) { 453 tg := env.Run 454 // create a new Env to hold the new ThreadGlobals (created in the goroutine below) and (initially empty) Pool 455 env2 := newEnv(tg, env, 0, 0) 456 env2.DebugComp = debugC 457 458 // env2.MarkUsedByClosure() // redundant, done by exprfun(env2) below 459 460 // function and arguments are evaluated in the caller's goroutine 461 // using the new Env: we compiled them with c2 => execute them with env2 462 funv := exprfun(env2) 463 argv := make([]r.Value, len(argfunsX1)) 464 for i, argfun := range argfunsX1 { 465 argv[i] = argfun(env2) 466 } 467 // the call is executed in a new goroutine. 468 // make it easy and do not try to optimize this call. 469 go func() { 470 tg2 := tg.new(gls.GoID()) 471 env2.Run = tg2 472 tg2.glsStore() 473 defer tg2.glsDel() 474 475 funv.Call(argv) 476 }() 477 478 env.IP++ 479 return env.Code[env.IP], env 480 } 481 c2.Append(stmt, node.Pos()) 482 483 // propagate back the compiled code 484 c.Code = c2.Code 485} 486 487// If compiles an "if" statement 488func (c *Comp) If(node *ast.IfStmt) { 489 var jump struct{ Then, Else, End int } 490 491 initLocals := false 492 var initBinds [2]int 493 c, initLocals = c.pushEnvIfLocalBinds(&initBinds, node.Init) 494 if node.Init != nil { 495 c.Stmt(node.Init) 496 } 497 pred := c.Expr(node.Cond, nil) 498 flag, fun, err := pred.TryAsPred() 499 if err { 500 c.invalidPred(node.Cond, pred) 501 return 502 } 503 if fun != nil { 504 stmt := func(env *Env) (Stmt, *Env) { 505 var ip int 506 if fun(env) { 507 ip = jump.Then 508 } else { 509 ip = jump.Else 510 } 511 env.IP = ip 512 return env.Code[ip], env 513 } 514 c.Append(stmt, node.Cond.Pos()) 515 } 516 // compile 'then' branch 517 jump.Then = c.Code.Len() 518 c.Block(node.Body) 519 if fun == nil && !flag { 520 // 'then' branch is never executed... 521 // still compiled above (to check for errors) but drop the generated code 522 c.Code.Truncate(jump.Then) 523 } 524 // compile a 'goto' between 'then' and 'else' branches 525 if fun != nil && node.Else != nil { 526 c.Append(func(env *Env) (Stmt, *Env) { 527 // after executing 'then' branch, we must skip 'else' branch 528 env.IP = jump.End 529 return env.Code[jump.End], env 530 }, node.Else.Pos()) 531 } 532 // compile 'else' branch 533 jump.Else = c.Code.Len() 534 if node.Else != nil { 535 // parser should guarantee Else to be a block or another "if" 536 // but macroexpansion can optimize away the block if it contains no declarations. 537 // still, better be safe and wrap the Else again in a block because: 538 // 1) catches improper macroexpander optimizations 539 // 2) there is no runtime performance penalty 540 xelse := node.Else 541 _, ok1 := xelse.(*ast.BlockStmt) 542 _, ok2 := xelse.(*ast.IfStmt) 543 if ok1 || ok2 { 544 c.Stmt(xelse) 545 } else { 546 c.Block(&ast.BlockStmt{List: []ast.Stmt{xelse}}) 547 } 548 if fun == nil && flag { 549 // 'else' branch is never executed... 550 // still compiled above (to check for errors) but drop the generated code 551 c.Code.Truncate(jump.Else) 552 } 553 } 554 jump.End = c.Code.Len() 555 556 c = c.popEnvIfLocalBinds(initLocals, &initBinds, node.Init) 557} 558 559// IncDec compiles a "place++" or "place--" statement 560func (c *Comp) IncDec(node *ast.IncDecStmt) { 561 place := c.Place(node.X) 562 op := node.Tok 563 if op == token.DEC { 564 op = token.SUB 565 } else { 566 op = token.ADD 567 } 568 one := c.exprUntypedLit(untypedOne.Kind, untypedOne.Val) 569 c.SetPlace(place, op, one) 570} 571 572// Return compiles a "return" statement 573func (c *Comp) Return(node *ast.ReturnStmt) { 574 var cinfo *FuncInfo 575 var upn int 576 var cf *Comp 577 for cf = c; cf != nil; cf = cf.Outer { 578 if cf.Func != nil { 579 cinfo = cf.Func 580 break 581 } 582 upn += cf.UpCost // count how many Env:s we must exit at runtime 583 } 584 if cinfo == nil { 585 c.Errorf("return outside function") 586 return 587 } 588 589 resultBinds := cinfo.Result 590 resultExprs := node.Results 591 n := len(resultBinds) 592 switch len(resultExprs) { 593 case n: 594 // ok 595 case 1: 596 if n == 0 { 597 c.Errorf("return: expecting %d expressions, found %d: %v", n, len(resultExprs), node) 598 } 599 c.returnMultiValues(node, resultBinds, upn, resultExprs) 600 return 601 case 0: 602 if !cinfo.NamedResults { 603 // naked return requires results to have names 604 c.Errorf("return: expecting %d expressions, found %d: %v", n, len(resultExprs), node) 605 return 606 } 607 n = 0 // naked return. results are already set 608 default: 609 c.Errorf("return: expecting %d expressions, found %d: %v", n, len(resultExprs), node) 610 return 611 } 612 613 exprs := c.exprs(resultExprs) 614 for i := 0; i < n; i++ { 615 c.Pos = resultExprs[i].Pos() 616 c.SetVar(resultBinds[i].AsVar(upn, PlaceSettable), token.ASSIGN, exprs[i]) 617 } 618 c.Append(stmtReturn, node.Pos()) 619} 620 621// returnMultiValues compiles a "return foo()" statement where foo() returns multiple values 622func (c *Comp) returnMultiValues(node *ast.ReturnStmt, resultBinds []*Bind, upn int, exprs []ast.Expr) { 623 n := len(resultBinds) 624 e := c.ExprsMultipleValues(exprs, n)[0] 625 fun := e.AsXV(COptDefaults) 626 assigns := make([]func(*Env, r.Value), n) 627 for i := 0; i < n; i++ { 628 texpected := resultBinds[i].Type 629 tactual := e.Out(i) 630 if !tactual.AssignableTo(texpected) { 631 c.Errorf("incompatible types in assignment: %v = %v", texpected, tactual) 632 } 633 assigns[i] = c.varSetValue(resultBinds[i].AsVar(upn, PlaceSettable)) 634 } 635 c.Append(func(env *Env) (Stmt, *Env) { 636 // no risk in evaluating fun() first: return binds are plain variables, not places with side effects 637 _, vals := fun(env) 638 for i, assign := range assigns { 639 assign(env, vals[i]) 640 } 641 // append the return epilogue 642 env.IP++ 643 g := env.Run 644 g.Signals.Sync = SigReturn 645 return g.Interrupt, env 646 }, node.Pos()) 647} 648 649func stmtReturn(env *Env) (Stmt, *Env) { 650 env.IP++ 651 g := env.Run 652 g.Signals.Sync = SigReturn 653 return g.Interrupt, env 654} 655 656// containLocalBinds return true if one or more of the given statements (but not their contents: 657// blocks are not examined) contain some function/variable declaration. 658// ignores types, constants and anything named "_" 659func containLocalBinds(list ...ast.Stmt) bool { 660 if len(list) == 0 { 661 output.Errorf("internal error: containLocalBinds() invoked on empty statement list") 662 } 663 for _, node := range list { 664 switch node := node.(type) { 665 case *ast.AssignStmt: 666 if node.Tok == token.DEFINE { 667 return true 668 } 669 case *ast.DeclStmt: 670 switch decl := node.Decl.(type) { 671 case *ast.FuncDecl: 672 // Go compiler forbids local functions... we allow them 673 if decl.Name != nil && decl.Name.Name != "_" { 674 return true 675 } 676 case *ast.GenDecl: 677 if decl.Tok != token.VAR { 678 continue 679 } 680 // found local variables... bail out unless they are all named "_" 681 for _, spec := range decl.Specs { 682 switch spec := spec.(type) { 683 case *ast.ValueSpec: 684 for _, ident := range spec.Names { 685 if ident.Name != "_" { 686 return true 687 } 688 } 689 } 690 } 691 } 692 case *ast.SelectStmt: 693 // Comp.Select() creates an unnamed bind 694 // to store the value received from channel. 695 if node.Body != nil && len(node.Body.List) != 0 { 696 return true 697 } 698 case nil: 699 } 700 } 701 return false 702} 703 704// pushEnvIfLocalBinds compiles a PushEnv statement if list contains local binds 705// returns the *Comp to use to compile statement list. 706func (c *Comp) pushEnvIfLocalBinds(nbind *[2]int, list ...ast.Stmt) (inner *Comp, locals bool) { 707 if len(list) == 0 { 708 inner.Errorf("internal error: pushEnvIfLocalBinds() invoked on empty statement list") 709 } 710 // 2. optimization: examine statements. if none of them is a function/variable declaration, 711 // no need to create a new *Env at runtime 712 // note: we still create a new *Comp at compile time to handle constant/type declarations 713 locals = containLocalBinds(list...) 714 return c.pushEnvIfFlag(nbind, locals) 715} 716 717// pushEnvIfDefine compiles a PushEnv statement if tok is token.DEFINE 718// returns the *Comp to use to compile statement list. 719func (c *Comp) pushEnvIfDefine(nbind *[2]int, tok token.Token) (inner *Comp, locals bool) { 720 return c.pushEnvIfFlag(nbind, tok == token.DEFINE) 721} 722 723// pushEnvIfFlag compiles a PushEnv statement if flag is true 724// returns the *Comp to use to compile statement list. 725func (c *Comp) pushEnvIfFlag(nbind *[2]int, flag bool) (*Comp, bool) { 726 var debugC *Comp 727 if flag { 728 // push new *Env at runtime. we will know # of binds in the block only later, so use a closure on them 729 c.append(func(env *Env) (Stmt, *Env) { 730 inner := NewEnv(env, nbind[0], nbind[1]) 731 inner.DebugComp = debugC 732 inner.IP++ 733 // Debugf("PushEnv(%p->%p), IP = %d of %d, pushed %d binds and %d intbinds", env, inner, inner.IP, nbinds[0], nbinds[1]) 734 return inner.Code[inner.IP], inner 735 }) 736 } 737 innerC := NewComp(c, &c.Code) 738 if flag { 739 if c.Globals.Options&OptDebugger != 0 { 740 // for debugger, inject the inner *Comp into the inner *Env 741 debugC = innerC 742 } 743 } else { 744 innerC.UpCost = 0 745 innerC.Depth-- 746 } 747 return innerC, flag 748} 749 750// popEnvIfLocalBinds compiles a PopEnv statement if locals is true. also sets *nbinds and *nintbinds 751func (inner *Comp) popEnvIfLocalBinds(locals bool, nbinds *[2]int, list ...ast.Stmt) *Comp { 752 if len(list) == 0 { 753 inner.Errorf("internal error: popEnvIfLocalBinds() invoked on empty statement list") 754 } 755 c := inner.Outer 756 c.Code = inner.Code // copy back accumulated code 757 nbinds[0] = inner.BindNum // we finally know these 758 nbinds[1] = inner.IntBindNum 759 760 if locals != (inner.BindNum != 0 || inner.IntBindNum != 0) { 761 c.Errorf(`internal error: containLocalBinds() returned %t, but block actually defined %d Binds and %d IntBinds: 762 Binds = %v 763 Block = 764%v`, locals, inner.BindNum, inner.IntBindNum, inner.Binds, &ast.BlockStmt{List: list}) 765 return nil 766 } 767 768 if locals { 769 // pop *Env at runtime 770 c.append(popEnv) 771 } 772 return c 773} 774 775// popEnvIfLocalBinds compiles a PopEnv statement if flag is true. also sets *nbinds and *nintbinds 776func (inner *Comp) popEnvIfFlag(nbinds *[2]int, flag bool) *Comp { 777 c := inner.Outer 778 c.Code = inner.Code // copy back accumulated code 779 nbinds[0] = inner.BindNum // we finally know these 780 nbinds[1] = inner.IntBindNum 781 782 if flag && nbinds[0] == 0 && nbinds[1] == 0 { 783 c.Debugf(`redundant popEnvIfFlag: flag is %t, but block actually defined %d Binds and %d IntBinds: 784 Binds = %v`, flag, nbinds[0], nbinds[1], inner.Binds) 785 } else if !flag && (nbinds[0] != 0 || nbinds[1] != 0) { 786 c.Errorf(`popEnvIfFlag internal error: flag is %t, but block actually defined %d Binds and %d IntBinds: 787 Binds = %v`, flag, nbinds[0], nbinds[1], inner.Binds) 788 return nil 789 } 790 791 if flag { 792 // pop *Env at runtime 793 c.append(popEnv) 794 } 795 return c 796} 797 798func (c *Comp) misplacedCase(node ast.Node, isdefault bool) { 799 label := "case" 800 if isdefault { 801 label = "default" 802 } 803 c.Errorf("misplaced %s: not inside switch or select: %v <%v>", label, node, r.TypeOf(node)) 804} 805 806func (c *Comp) misplacedFallthrough() { 807 c.Errorf("misplaced fallthrough: not inside switch") 808} 809