func()

in tool/instrument/inst_func.go [128:262]


func (rp *RuleProcessor) insertTJump(t *resource.InstFuncRule,
	funcDecl *dst.FuncDecl) error {
	util.Assert(t.OnEnter != "" || t.OnExit != "", "sanity check")

	var retVals []dst.Expr // nil by default
	if retList := funcDecl.Type.Results; retList != nil {
		retVals = make([]dst.Expr, 0)
		// If return values are named, collect their names, otherwise we try to
		// name them manually for further use
		for _, field := range retList.List {
			if field.Names != nil {
				for _, name := range field.Names {
					retVals = append(retVals, dst.NewIdent(name.Name))
				}
			} else {
				retValIdent := dst.NewIdent("retVal" + util.RandomString(5))
				field.Names = []*dst.Ident{retValIdent}
				retVals = append(retVals, dst.Clone(retValIdent).(*dst.Ident))
			}
		}
	}

	// Arguments for onEnter trampoline
	args := make([]dst.Expr, 0)
	// Receiver as argument for trampoline func, if any
	if util.HasReceiver(funcDecl) {
		if recv := funcDecl.Recv.List; recv != nil {
			receiver := recv[0].Names[0].Name
			args = append(args, util.AddressOf(util.Ident(receiver)))
		} else {
			util.Unimplemented()
		}
	}
	// Original function arguments as arguments for trampoline func
	for _, field := range funcDecl.Type.Params.List {
		for _, name := range field.Names {
			args = append(args, util.AddressOf(util.Ident(name.Name)))
		}
	}

	varSuffix := util.RandomString(5)
	rp.rule2Suffix[t] = varSuffix

	// Generate the trampoline-jump-if. N.B. Note that future optimization pass
	// heavily depends on the structure of trampoline-jump-if. Any change in it
	// should be carefully examined.
	onEnterCall := util.CallTo(rp.makeName(t, rp.rawFunc, true), args)
	onExitCall := util.CallTo(rp.makeName(t, rp.rawFunc, false), func() []dst.Expr {
		// NB. DST framework disallows duplicated node in the
		// AST tree, we need to replicate the return values
		// as they are already used in return statement above
		clone := make([]dst.Expr, len(retVals)+1)
		clone[0] = util.Ident(TrampolineCallContextName + varSuffix)
		for i := 1; i < len(clone); i++ {
			clone[i] = util.AddressOf(retVals[i-1])
		}
		return clone
	}())
	tjumpInit := util.DefineStmts(
		util.Exprs(
			util.Ident(TrampolineCallContextName+varSuffix),
			util.Ident(TrampolineSkipName+varSuffix),
		),
		util.Exprs(onEnterCall),
	)
	tjumpCond := util.Ident(TrampolineSkipName + varSuffix)
	tjumpBody := util.BlockStmts(
		util.ExprStmt(onExitCall),
		util.ReturnStmt(retVals),
	)
	tjumpElse := util.Block(util.DeferStmt(onExitCall))
	tjump := util.IfStmt(tjumpInit, tjumpCond, tjumpBody, tjumpElse)
	// Add this trampoline-jump-if as optimization candidates
	rp.trampolineJumps = append(rp.trampolineJumps, &TJump{
		target: funcDecl,
		ifStmt: tjump,
		rule:   t,
	})
	// Add label for trampoline-jump-if. Note that the label will be cleared
	// during optimization pass, to make it pretty in the generated code
	tjump.Decs.If.Append(TJumpLabel)
	// Find if there is already a trampoline-jump-if, insert new tjump if so,
	// otherwise prepend to block body
	found := false
	if len(funcDecl.Body.List) > 0 {
		firstStmt := funcDecl.Body.List[0]
		if ifStmt, ok := firstStmt.(*dst.IfStmt); ok {
			point := findJumpPoint(ifStmt)
			if point != nil {
				point.List = append(point.List, util.EmptyStmt())
				point.List = append(point.List, tjump)
				found = true
			}
		}
	}
	if !found {
		// Tag the trampoline-jump-if with a special line directive so that
		// debugger can show the correct line number
		tjump.Decs.Before = dst.NewLine
		tjump.Decs.Start.Append("//line <generated>:1")
		pos := rp.parser.FindPosition(funcDecl.Body)
		if len(funcDecl.Body.List) > 0 {
			// It does happens because we may insert raw code snippets at the
			// function entry. These dynamically generated nodes do not have
			// corresponding node positions. We need to keep looking downward
			// until we find a node that contains position information, and then
			// annotate it with a line directive.
			for i := 0; i < len(funcDecl.Body.List); i++ {
				stmt := funcDecl.Body.List[i]
				pos = rp.parser.FindPosition(stmt)
				if !pos.IsValid() {
					continue
				}
				tag := fmt.Sprintf("//line %s", pos.String())
				stmt.Decorations().Before = dst.NewLine
				stmt.Decorations().Start.Append(tag)
			}
		} else {
			pos = rp.parser.FindPosition(funcDecl.Body)
			tag := fmt.Sprintf("//line %s", pos.String())
			empty := util.EmptyStmt()
			empty.Decs.Before = dst.NewLine
			empty.Decs.Start.Append(tag)
			funcDecl.Body.List = append(funcDecl.Body.List, empty)
		}
		funcDecl.Body.List = append([]dst.Stmt{tjump}, funcDecl.Body.List...)
	}

	// Generate corresponding trampoline code
	err := rp.generateTrampoline(t)
	if err != nil {
		return err
	}
	return nil
}