Пример #1
0
func (v *visitor) wrapSwitch(_switch *ast.SwitchStmt, identList []*ast.Ident) (block *ast.BlockStmt) {
	block = astPrintf(`
		{
			godebug.Line(ctx, %s, %s)
			%s
			scope := %s.EnteringNewChildScope()
			_ = scope // placeholder
			_ = scope // placeholder
		}`, v.scopeVar, pos2lineString(_switch.Pos()), _switch.Init, v.scopeVar)[0].(*ast.BlockStmt)
	block.List[3] = newDeclareCall(idents.scope, identList)
	_switch.Init = nil
	block.List[4] = _switch
	return block
}
Пример #2
0
func (a *stmtCompiler) compileSwitchStmt(s *ast.SwitchStmt) {
	// Create implicit scope around switch
	bc := a.enterChild()
	defer bc.exit()

	// Compile init statement, if any
	if s.Init != nil {
		bc.compileStmt(s.Init)
	}

	// Compile condition, if any, and extract its effects
	var cond *expr
	condbc := bc.enterChild()
	if s.Tag != nil {
		e := condbc.compileExpr(condbc.block, false, s.Tag)
		if e != nil {
			var effect func(*Thread)
			effect, cond = e.extractEffect(condbc.block, "switch")
			a.push(effect)
		}
	}

	// Count cases
	ncases := 0
	hasDefault := false
	for _, c := range s.Body.List {
		clause, ok := c.(*ast.CaseClause)
		if !ok {
			a.diagAt(clause.Pos(), "switch statement must contain case clauses")
			continue
		}
		if clause.List == nil {
			if hasDefault {
				a.diagAt(clause.Pos(), "switch statement contains more than one default case")
			}
			hasDefault = true
		} else {
			ncases += len(clause.List)
		}
	}

	// Compile case expressions
	cases := make([]func(*Thread) bool, ncases)
	i := 0
	for _, c := range s.Body.List {
		clause, ok := c.(*ast.CaseClause)
		if !ok {
			continue
		}
		for _, v := range clause.List {
			e := condbc.compileExpr(condbc.block, false, v)
			switch {
			case e == nil:
				// Error reported by compileExpr
			case cond == nil && !e.t.isBoolean():
				a.diagAt(v.Pos(), "'case' condition must be boolean")
			case cond == nil:
				cases[i] = e.asBool()
			case cond != nil:
				// Create comparison
				// TOOD(austin) This produces bad error messages
				compare := e.compileBinaryExpr(token.EQL, cond, e)
				if compare != nil {
					cases[i] = compare.asBool()
				}
			}
			i++
		}
	}

	// Emit condition
	casePCs := make([]*uint, ncases+1)
	endPC := badPC

	a.flow.put(false, false, casePCs)
	a.push(func(t *Thread) {
		for i, c := range cases {
			if c(t) {
				t.pc = *casePCs[i]
				return
			}
		}
		t.pc = *casePCs[ncases]
	})
	condbc.exit()

	// Compile cases
	i = 0
	for _, c := range s.Body.List {
		clause, ok := c.(*ast.CaseClause)
		if !ok {
			continue
		}

		// Save jump PC's
		pc := a.nextPC()
		if clause.List != nil {
			for _ = range clause.List {
				casePCs[i] = &pc
				i++
			}
		} else {
			// Default clause
			casePCs[ncases] = &pc
		}

		// Compile body
		fall := false
		for j, s := range clause.Body {
			if br, ok := s.(*ast.BranchStmt); ok && br.Tok == token.FALLTHROUGH {
				// println("Found fallthrough");
				// It may be used only as the final
				// non-empty statement in a case or
				// default clause in an expression
				// "switch" statement.
				for _, s2 := range clause.Body[j+1:] {
					// XXX(Spec) 6g also considers
					// empty blocks to be empty
					// statements.
					if _, ok := s2.(*ast.EmptyStmt); !ok {
						a.diagAt(s.Pos(), "fallthrough statement must be final statement in case")
						break
					}
				}
				fall = true
			} else {
				bc.compileStmt(s)
			}
		}
		// Jump out of switch, unless there was a fallthrough
		if !fall {
			a.flow.put1(false, &endPC)
			a.push(func(v *Thread) { v.pc = endPC })
		}
	}

	// Get end PC
	endPC = a.nextPC()
	if !hasDefault {
		casePCs[ncases] = &endPC
	}
}