package evalop import ( "go/ast" "go/constant" "github.com/go-delve/delve/pkg/dwarf/godwarf" ) // Op is a stack machine opcode type Op interface { depthCheck() (npop, npush int) } // PushCurg pushes the current goroutine on the stack. type PushCurg struct { } func (*PushCurg) depthCheck() (npop, npush int) { return 0, 1 } // PushFrameoff pushes the frame offset for the current frame on the stack. type PushFrameoff struct { } func (*PushFrameoff) depthCheck() (npop, npush int) { return 0, 1 } // PushThreadID pushes the ID of the current thread on the stack. type PushThreadID struct { } func (*PushThreadID) depthCheck() (npop, npush int) { return 0, 1 } // PushRangeParentOffset pushes the frame offset of the range-over-func closure body parent. type PushRangeParentOffset struct { } func (*PushRangeParentOffset) depthCheck() (npop, npush int) { return 0, 1 } // PushConst pushes a constant on the stack. type PushConst struct { Value constant.Value } func (*PushConst) depthCheck() (npop, npush int) { return 0, 1 } // PushLocal pushes the local variable with the given name on the stack. type PushLocal struct { Name string Frame int64 } func (*PushLocal) depthCheck() (npop, npush int) { return 0, 1 } // PushIdent pushes a local or global variable or a predefined identifier // (true, false, etc) or the value of a register on the stack. type PushIdent struct { Name string } func (*PushIdent) depthCheck() (npop, npush int) { return 0, 1 } // PushPackageVarOrSelect pushes the value of Name.Sel on the stack, which // could either be a global variable (with the package name specified), or a // field of a local variable. type PushPackageVarOrSelect struct { Name, Sel string NameIsString bool } func (*PushPackageVarOrSelect) depthCheck() (npop, npush int) { return 0, 1 } // PushNil pushes an untyped nil on the stack. type PushNil struct { } func (*PushNil) depthCheck() (npop, npush int) { return 0, 1 } // PushLen pushes the length of the variable at the top of the stack into // the stack. type PushLen struct { } func (*PushLen) depthCheck() (npop, npush int) { return 1, 2 } // Select replaces the topmost stack variable v with v.Name. type Select struct { Name string } func (*Select) depthCheck() (npop, npush int) { return 1, 1 } // TypeAssert replaces the topmost stack variable v with v.(DwarfType). type TypeAssert struct { DwarfType godwarf.Type Node *ast.TypeAssertExpr } func (*TypeAssert) depthCheck() (npop, npush int) { return 1, 1 } // PointerDeref replaces the topmost stack variable v with *v. type PointerDeref struct { Node *ast.StarExpr } func (*PointerDeref) depthCheck() (npop, npush int) { return 1, 1 } // Unary applies the given unary operator to the topmost stack variable. type Unary struct { Node *ast.UnaryExpr } func (*Unary) depthCheck() (npop, npush int) { return 1, 1 } // AddrOf replaces the topmost stack variable v with &v. type AddrOf struct { Node *ast.UnaryExpr } func (*AddrOf) depthCheck() (npop, npush int) { return 1, 1 } // TypeCast replaces the topmost stack variable v with (DwarfType)(v). type TypeCast struct { DwarfType godwarf.Type Node *ast.CallExpr } func (*TypeCast) depthCheck() (npop, npush int) { return 1, 1 } // Reslice implements a reslice operation. // If HasHigh is set it pops three variables, low, high and v, and pushes // v[low:high]. // Otherwise it pops two variables, low and v, and pushes v[low:]. // If TrustLen is set when the variable resulting from the reslice is loaded it will be fully loaded. type Reslice struct { HasHigh bool TrustLen bool Node *ast.SliceExpr } func (op *Reslice) depthCheck() (npop, npush int) { if op.HasHigh { return 3, 1 } else { return 2, 1 } } // Index pops two variables, idx and v, and pushes v[idx]. type Index struct { Node *ast.IndexExpr } func (*Index) depthCheck() (npop, npush int) { return 2, 1 } // Jump looks at the topmost stack variable and if it satisfies the // condition specified by When it jumps to the stack machine instruction at // Target+1. // If Pop is set the topmost stack variable is also popped. type Jump struct { When JumpCond Pop bool Target int Node ast.Expr } func (jmpif *Jump) depthCheck() (npop, npush int) { switch jmpif.When { case JumpIfTrue, JumpIfFalse, JumpIfAllocStringChecksFail: if jmpif.Pop { return 1, 0 } else { return 1, 1 } } return 0, 0 } // JumpCond specifies a condition for the Jump instruction. type JumpCond uint8 const ( JumpIfFalse JumpCond = iota JumpIfTrue JumpIfAllocStringChecksFail JumpAlways JumpIfPinningDone ) // Binary pops two variables from the stack, applies the specified binary // operator to them and pushes the result back on the stack. type Binary struct { Node *ast.BinaryExpr } func (*Binary) depthCheck() (npop, npush int) { return 2, 1 } // BoolToConst pops the topmost variable from the stack, which must be a // boolean variable, and converts it to a constant. type BoolToConst struct { } func (*BoolToConst) depthCheck() (npop, npush int) { return 1, 1 } // Pop removes the topmost variable from the stack. type Pop struct { } func (*Pop) depthCheck() (npop, npush int) { return 1, 0 } // Roll removes the n-th element of the stack and pushes it back in at the top type Roll struct { N int } func (*Roll) depthCheck() (npop, npush int) { return 1, 1 } // BuiltinCall pops len(Args) argument from the stack, calls the specified // builtin on them and pushes the result back on the stack. type BuiltinCall struct { Name string Args []ast.Expr } func (bc *BuiltinCall) depthCheck() (npop, npush int) { return len(bc.Args), 1 } // CallInjectionStart starts call injection by calling // runtime.debugCallVn. type CallInjectionStart struct { id int // identifier for all the call injection instructions that belong to the same sequence, this only exists to make reading listings easier HasFunc bool // target function already pushed on the stack Node *ast.CallExpr } func (*CallInjectionStart) depthCheck() (npop, npush int) { return 0, 1 } // CallInjectionSetTarget starts the call injection, after // runtime.debugCallVn set up the stack for us, by copying the entry point // of the function, setting the closure register and copying the receiver. type CallInjectionSetTarget struct { id int } func (*CallInjectionSetTarget) depthCheck() (npop, npush int) { return 1, 0 } // CallInjectionCopyArg copies one argument for call injection. type CallInjectionCopyArg struct { id int ArgNum int ArgExpr ast.Expr } func (*CallInjectionCopyArg) depthCheck() (npop, npush int) { return 1, 0 } // CallInjectionComplete resumes target execution so that the injected call can run. // If DoPinning is true it stops after the call is completed without undoing // the call injection frames so that address pinning for the return value // can be performed, see CallInjectionComplete2. type CallInjectionComplete struct { id int DoPinning bool } func (op *CallInjectionComplete) depthCheck() (npop, npush int) { if op.DoPinning { return 0, 0 } else { return 0, 1 } } // CallInjectionComplete2 if DoPinning was passed to CallInjectionComplete // this will finish the call injection protocol and push the evaluation // result on the stack. type CallInjectionComplete2 struct { id int } func (*CallInjectionComplete2) depthCheck() (npop, npush int) { return 0, 1 } // CallInjectionStartSpecial starts call injection for a function with a // name and arguments known at compile time. type CallInjectionStartSpecial struct { id int FnName string ArgAst []ast.Expr } func (*CallInjectionStartSpecial) depthCheck() (npop, npush int) { return 0, 1 } // ConvertAllocToString pops two variables from the stack, a constant string // and the return value of runtime.mallocgc (mallocv), copies the contents // of the string at the address in mallocv and pushes on the stack a new // string value that uses the backing storage of mallocv. type ConvertAllocToString struct { } func (*ConvertAllocToString) depthCheck() (npop, npush int) { return 2, 1 } // SetValue pops to variables from the stack, lhv and rhv, and sets lhv to // rhv. type SetValue struct { lhe, Rhe ast.Expr } func (*SetValue) depthCheck() (npop, npush int) { return 2, 0 } // SetDebugPinner pops one variable from the stack and uses it as the saved debug pinner. type SetDebugPinner struct { } func (*SetDebugPinner) depthCheck() (npop, npush int) { return 1, 0 } // PushDebugPinner pushes the debug pinner on the stack. type PushDebugPinner struct { } func (*PushDebugPinner) depthCheck() (npop, npush int) { return 0, 1 } // PushPinAddress pushes an address to pin on the stack (as an // unsafe.Pointer) and removes it from the list of addresses to pin. type PushPinAddress struct { } func (*PushPinAddress) depthCheck() (npop, npush int) { return 0, 1 }