gitlab.com/Raven-IO/raven-delve@v1.22.4/pkg/proc/evalop/evalcompile.go (about) 1 package evalop 2 3 import ( 4 "bytes" 5 "errors" 6 "fmt" 7 "go/ast" 8 "go/constant" 9 "go/parser" 10 "go/printer" 11 "go/scanner" 12 "go/token" 13 "strconv" 14 "strings" 15 16 "gitlab.com/Raven-IO/raven-delve/pkg/dwarf/godwarf" 17 "gitlab.com/Raven-IO/raven-delve/pkg/dwarf/reader" 18 ) 19 20 var ( 21 ErrFuncCallNotAllowed = errors.New("function calls not allowed without using 'call'") 22 ) 23 24 type compileCtx struct { 25 evalLookup 26 ops []Op 27 allowCalls bool 28 curCall int 29 } 30 31 type evalLookup interface { 32 FindTypeExpr(ast.Expr) (godwarf.Type, error) 33 HasLocal(string) bool 34 HasGlobal(string, string) bool 35 HasBuiltin(string) bool 36 LookupRegisterName(string) (int, bool) 37 } 38 39 // CompileAST compiles the expression t into a list of instructions. 40 func CompileAST(lookup evalLookup, t ast.Expr) ([]Op, error) { 41 ctx := &compileCtx{evalLookup: lookup, allowCalls: true} 42 err := ctx.compileAST(t) 43 if err != nil { 44 return nil, err 45 } 46 47 err = ctx.depthCheck(1) 48 if err != nil { 49 return ctx.ops, err 50 } 51 return ctx.ops, nil 52 } 53 54 // Compile compiles the expression expr into a list of instructions. 55 // If canSet is true expressions like "x = y" are also accepted. 56 func Compile(lookup evalLookup, expr string, canSet bool) ([]Op, error) { 57 t, err := parser.ParseExpr(expr) 58 if err != nil { 59 if canSet { 60 eqOff, isAs := isAssignment(err) 61 if isAs { 62 return CompileSet(lookup, expr[:eqOff], expr[eqOff+1:]) 63 } 64 } 65 return nil, err 66 } 67 return CompileAST(lookup, t) 68 } 69 70 func isAssignment(err error) (int, bool) { 71 el, isScannerErr := err.(scanner.ErrorList) 72 if isScannerErr && el[0].Msg == "expected '==', found '='" { 73 return el[0].Pos.Offset, true 74 } 75 return 0, false 76 } 77 78 // CompileSet compiles the expression setting lhexpr to rhexpr into a list of 79 // instructions. 80 func CompileSet(lookup evalLookup, lhexpr, rhexpr string) ([]Op, error) { 81 lhe, err := parser.ParseExpr(lhexpr) 82 if err != nil { 83 return nil, err 84 } 85 rhe, err := parser.ParseExpr(rhexpr) 86 if err != nil { 87 return nil, err 88 } 89 90 ctx := &compileCtx{evalLookup: lookup, allowCalls: true} 91 err = ctx.compileAST(rhe) 92 if err != nil { 93 return nil, err 94 } 95 96 if isStringLiteral(rhe) { 97 ctx.compileAllocLiteralString() 98 } 99 100 err = ctx.compileAST(lhe) 101 if err != nil { 102 return nil, err 103 } 104 105 ctx.pushOp(&SetValue{lhe: lhe, Rhe: rhe}) 106 107 err = ctx.depthCheck(0) 108 if err != nil { 109 return ctx.ops, err 110 } 111 return ctx.ops, nil 112 } 113 114 func (ctx *compileCtx) compileAllocLiteralString() { 115 jmp := &Jump{When: JumpIfAllocStringChecksFail} 116 ctx.pushOp(jmp) 117 118 ctx.compileSpecialCall("runtime.mallocgc", []ast.Expr{ 119 &ast.BasicLit{Kind: token.INT, Value: "0"}, 120 &ast.Ident{Name: "nil"}, 121 &ast.Ident{Name: "false"}, 122 }, []Op{ 123 &PushLen{}, 124 &PushNil{}, 125 &PushConst{constant.MakeBool(false)}, 126 }) 127 128 ctx.pushOp(&ConvertAllocToString{}) 129 jmp.Target = len(ctx.ops) 130 } 131 132 func (ctx *compileCtx) compileSpecialCall(fnname string, argAst []ast.Expr, args []Op) { 133 id := ctx.curCall 134 ctx.curCall++ 135 ctx.pushOp(&CallInjectionStartSpecial{ 136 id: id, 137 FnName: fnname, 138 ArgAst: argAst}) 139 ctx.pushOp(&CallInjectionSetTarget{id: id}) 140 141 for i := range args { 142 ctx.pushOp(args[i]) 143 ctx.pushOp(&CallInjectionCopyArg{id: id, ArgNum: i}) 144 } 145 146 ctx.pushOp(&CallInjectionComplete{id: id}) 147 } 148 149 func (ctx *compileCtx) pushOp(op Op) { 150 ctx.ops = append(ctx.ops, op) 151 } 152 153 // depthCheck validates the list of instructions produced by Compile and 154 // CompileSet by performing a stack depth check. 155 // It calculates the depth of the stack at every instruction in ctx.ops and 156 // checks that they have enough arguments to execute. For instructions that 157 // can be reached through multiple paths (because of a jump) it checks that 158 // all paths reach the instruction with the same stack depth. 159 // Finally it checks that the stack depth after all instructions have 160 // executed is equal to endDepth. 161 func (ctx *compileCtx) depthCheck(endDepth int) error { 162 depth := make([]int, len(ctx.ops)+1) // depth[i] is the depth of the stack before i-th instruction 163 for i := range depth { 164 depth[i] = -1 165 } 166 depth[0] = 0 167 168 var err error 169 checkAndSet := func(j, d int) { // sets depth[j] to d after checking that we can 170 if depth[j] < 0 { 171 depth[j] = d 172 } 173 if d != depth[j] { 174 err = fmt.Errorf("internal debugger error: depth check error at instruction %d: expected depth %d have %d (jump target)\n%s", j, d, depth[j], Listing(depth, ctx.ops)) 175 } 176 } 177 178 for i, op := range ctx.ops { 179 npop, npush := op.depthCheck() 180 if depth[i] < npop { 181 return fmt.Errorf("internal debugger error: depth check error at instruction %d: expected at least %d have %d\n%s", i, npop, depth[i], Listing(depth, ctx.ops)) 182 } 183 d := depth[i] - npop + npush 184 checkAndSet(i+1, d) 185 if jmp, _ := op.(*Jump); jmp != nil { 186 checkAndSet(jmp.Target, d) 187 } 188 if err != nil { 189 return err 190 } 191 } 192 193 if depth[len(ctx.ops)] != endDepth { 194 return fmt.Errorf("internal debugger error: depth check failed: depth at the end is not %d (got %d)\n%s", depth[len(ctx.ops)], endDepth, Listing(depth, ctx.ops)) 195 } 196 return nil 197 } 198 199 func (ctx *compileCtx) compileAST(t ast.Expr) error { 200 switch node := t.(type) { 201 case *ast.CallExpr: 202 return ctx.compileTypeCastOrFuncCall(node) 203 204 case *ast.Ident: 205 return ctx.compileIdent(node) 206 207 case *ast.ParenExpr: 208 // otherwise just eval recursively 209 return ctx.compileAST(node.X) 210 211 case *ast.SelectorExpr: // <expression>.<identifier> 212 switch x := node.X.(type) { 213 case *ast.Ident: 214 switch { 215 case x.Name == "runtime" && node.Sel.Name == "curg": 216 ctx.pushOp(&PushCurg{}) 217 218 case x.Name == "runtime" && node.Sel.Name == "frameoff": 219 ctx.pushOp(&PushFrameoff{}) 220 221 case x.Name == "runtime" && node.Sel.Name == "threadid": 222 ctx.pushOp(&PushThreadID{}) 223 224 case ctx.HasLocal(x.Name): 225 ctx.pushOp(&PushLocal{Name: x.Name}) 226 ctx.pushOp(&Select{node.Sel.Name}) 227 228 case ctx.HasGlobal(x.Name, node.Sel.Name): 229 ctx.pushOp(&PushPackageVar{x.Name, node.Sel.Name}) 230 231 default: 232 return ctx.compileUnary(node.X, &Select{node.Sel.Name}) 233 } 234 235 case *ast.CallExpr: 236 ident, ok := x.Fun.(*ast.SelectorExpr) 237 if ok { 238 f, ok := ident.X.(*ast.Ident) 239 if ok && f.Name == "runtime" && ident.Sel.Name == "frame" { 240 switch arg := x.Args[0].(type) { 241 case *ast.BasicLit: 242 fr, err := strconv.ParseInt(arg.Value, 10, 8) 243 if err != nil { 244 return err 245 } 246 // Push local onto the stack to be evaluated in the new frame context. 247 ctx.pushOp(&PushLocal{Name: node.Sel.Name, Frame: fr}) 248 return nil 249 default: 250 return fmt.Errorf("expected integer value for frame, got %v", arg) 251 } 252 } 253 } 254 return ctx.compileUnary(node.X, &Select{node.Sel.Name}) 255 256 case *ast.BasicLit: // try to accept "package/path".varname syntax for package variables 257 s, err := strconv.Unquote(x.Value) 258 if err != nil { 259 return err 260 } 261 if ctx.HasGlobal(s, node.Sel.Name) { 262 ctx.pushOp(&PushPackageVar{s, node.Sel.Name}) 263 return nil 264 } 265 return ctx.compileUnary(node.X, &Select{node.Sel.Name}) 266 267 default: 268 return ctx.compileUnary(node.X, &Select{node.Sel.Name}) 269 270 } 271 272 case *ast.TypeAssertExpr: // <expression>.(<type>) 273 return ctx.compileTypeAssert(node) 274 275 case *ast.IndexExpr: 276 return ctx.compileBinary(node.X, node.Index, nil, &Index{node}) 277 278 case *ast.SliceExpr: 279 if node.Slice3 { 280 return fmt.Errorf("3-index slice expressions not supported") 281 } 282 return ctx.compileReslice(node) 283 284 case *ast.StarExpr: 285 // pointer dereferencing *<expression> 286 return ctx.compileUnary(node.X, &PointerDeref{node}) 287 288 case *ast.UnaryExpr: 289 // The unary operators we support are +, - and & (note that unary * is parsed as ast.StarExpr) 290 switch node.Op { 291 case token.AND: 292 return ctx.compileUnary(node.X, &AddrOf{node}) 293 default: 294 return ctx.compileUnary(node.X, &Unary{node}) 295 } 296 297 case *ast.BinaryExpr: 298 switch node.Op { 299 case token.INC, token.DEC, token.ARROW: 300 return fmt.Errorf("operator %s not supported", node.Op.String()) 301 } 302 // short circuits logical operators 303 var sop *Jump 304 switch node.Op { 305 case token.LAND: 306 sop = &Jump{When: JumpIfFalse, Node: node.X} 307 case token.LOR: 308 sop = &Jump{When: JumpIfTrue, Node: node.X} 309 } 310 err := ctx.compileBinary(node.X, node.Y, sop, &Binary{node}) 311 if err != nil { 312 return err 313 } 314 if sop != nil { 315 sop.Target = len(ctx.ops) 316 ctx.pushOp(&BoolToConst{}) 317 } 318 319 case *ast.BasicLit: 320 ctx.pushOp(&PushConst{constant.MakeFromLiteral(node.Value, node.Kind, 0)}) 321 322 default: 323 return fmt.Errorf("expression %T not implemented", t) 324 } 325 return nil 326 } 327 328 func (ctx *compileCtx) compileTypeCastOrFuncCall(node *ast.CallExpr) error { 329 if len(node.Args) != 1 { 330 // Things that have more or less than one argument are always function calls. 331 return ctx.compileFunctionCall(node) 332 } 333 334 ambiguous := func() error { 335 // Ambiguous, could be a function call or a type cast, if node.Fun can be 336 // evaluated then try to treat it as a function call, otherwise try the 337 // type cast. 338 ctx2 := &compileCtx{evalLookup: ctx.evalLookup} 339 err0 := ctx2.compileAST(node.Fun) 340 if err0 == nil { 341 return ctx.compileFunctionCall(node) 342 } 343 return ctx.compileTypeCast(node, err0) 344 } 345 346 fnnode := node.Fun 347 for { 348 fnnode = removeParen(fnnode) 349 n, _ := fnnode.(*ast.StarExpr) 350 if n == nil { 351 break 352 } 353 fnnode = n.X 354 } 355 356 switch n := fnnode.(type) { 357 case *ast.BasicLit: 358 // It can only be a ("type string")(x) type cast 359 return ctx.compileTypeCast(node, nil) 360 case *ast.ArrayType, *ast.StructType, *ast.FuncType, *ast.InterfaceType, *ast.MapType, *ast.ChanType: 361 return ctx.compileTypeCast(node, nil) 362 case *ast.SelectorExpr: 363 if _, isident := n.X.(*ast.Ident); isident { 364 if typ, _ := ctx.FindTypeExpr(n); typ != nil { 365 return ctx.compileTypeCast(node, nil) 366 } 367 return ambiguous() 368 } 369 return ctx.compileFunctionCall(node) 370 case *ast.Ident: 371 if ctx.HasBuiltin(n.Name) { 372 return ctx.compileFunctionCall(node) 373 } 374 if ctx.HasGlobal("", n.Name) || ctx.HasLocal(n.Name) { 375 return ctx.compileFunctionCall(node) 376 } 377 return ctx.compileTypeCast(node, fmt.Errorf("could not find symbol value for %s", n.Name)) 378 case *ast.IndexExpr: 379 // Ambiguous, could be a parametric type 380 switch n.X.(type) { 381 case *ast.Ident, *ast.SelectorExpr: 382 // Do the type-cast first since evaluating node.Fun could be expensive. 383 err := ctx.compileTypeCast(node, nil) 384 if err == nil || err != reader.ErrTypeNotFound { 385 return err 386 } 387 return ctx.compileFunctionCall(node) 388 default: 389 return ctx.compileFunctionCall(node) 390 } 391 case *ast.IndexListExpr: 392 return ctx.compileTypeCast(node, nil) 393 default: 394 // All other expressions must be function calls 395 return ctx.compileFunctionCall(node) 396 } 397 } 398 399 func (ctx *compileCtx) compileTypeCast(node *ast.CallExpr, ambiguousErr error) error { 400 err := ctx.compileAST(node.Args[0]) 401 if err != nil { 402 return err 403 } 404 405 fnnode := node.Fun 406 407 // remove all enclosing parenthesis from the type name 408 fnnode = removeParen(fnnode) 409 410 targetTypeStr := exprToString(removeParen(node.Fun)) 411 styp, err := ctx.FindTypeExpr(fnnode) 412 if err != nil { 413 switch targetTypeStr { 414 case "[]byte", "[]uint8": 415 styp = godwarf.FakeSliceType(godwarf.FakeBasicType("uint", 8)) 416 case "[]int32", "[]rune": 417 styp = godwarf.FakeSliceType(godwarf.FakeBasicType("int", 32)) 418 default: 419 if ambiguousErr != nil && err == reader.ErrTypeNotFound { 420 return fmt.Errorf("could not evaluate function or type %s: %v", exprToString(node.Fun), ambiguousErr) 421 } 422 return err 423 } 424 } 425 426 ctx.pushOp(&TypeCast{DwarfType: styp, Node: node}) 427 return nil 428 } 429 430 func (ctx *compileCtx) compileBuiltinCall(builtin string, args []ast.Expr) error { 431 for _, arg := range args { 432 err := ctx.compileAST(arg) 433 if err != nil { 434 return err 435 } 436 } 437 ctx.pushOp(&BuiltinCall{builtin, args}) 438 return nil 439 } 440 441 func (ctx *compileCtx) compileIdent(node *ast.Ident) error { 442 switch { 443 case ctx.HasLocal(node.Name): 444 ctx.pushOp(&PushLocal{Name: node.Name}) 445 case ctx.HasGlobal("", node.Name): 446 ctx.pushOp(&PushPackageVar{"", node.Name}) 447 case node.Name == "true" || node.Name == "false": 448 ctx.pushOp(&PushConst{constant.MakeBool(node.Name == "true")}) 449 case node.Name == "nil": 450 ctx.pushOp(&PushNil{}) 451 default: 452 found := false 453 if regnum, ok := ctx.LookupRegisterName(node.Name); ok { 454 ctx.pushOp(&PushRegister{regnum, node.Name}) 455 found = true 456 } 457 if !found { 458 return fmt.Errorf("could not find symbol value for %s", node.Name) 459 } 460 } 461 return nil 462 } 463 464 func (ctx *compileCtx) compileUnary(expr ast.Expr, op Op) error { 465 err := ctx.compileAST(expr) 466 if err != nil { 467 return err 468 } 469 ctx.pushOp(op) 470 return nil 471 } 472 473 func (ctx *compileCtx) compileTypeAssert(node *ast.TypeAssertExpr) error { 474 err := ctx.compileAST(node.X) 475 if err != nil { 476 return err 477 } 478 // Accept .(data) as a type assertion that always succeeds, so that users 479 // can access the data field of an interface without actually having to 480 // type the concrete type. 481 if idtyp, isident := node.Type.(*ast.Ident); !isident || idtyp.Name != "data" { 482 typ, err := ctx.FindTypeExpr(node.Type) 483 if err != nil { 484 return err 485 } 486 ctx.pushOp(&TypeAssert{typ, node}) 487 return nil 488 } 489 ctx.pushOp(&TypeAssert{nil, node}) 490 return nil 491 } 492 493 func (ctx *compileCtx) compileBinary(a, b ast.Expr, sop *Jump, op Op) error { 494 err := ctx.compileAST(a) 495 if err != nil { 496 return err 497 } 498 if sop != nil { 499 ctx.pushOp(sop) 500 } 501 err = ctx.compileAST(b) 502 if err != nil { 503 return err 504 } 505 ctx.pushOp(op) 506 return nil 507 } 508 509 func (ctx *compileCtx) compileReslice(node *ast.SliceExpr) error { 510 err := ctx.compileAST(node.X) 511 if err != nil { 512 return err 513 } 514 515 trustLen := true 516 hasHigh := false 517 if node.High != nil { 518 hasHigh = true 519 err = ctx.compileAST(node.High) 520 if err != nil { 521 return err 522 } 523 _, isbasiclit := node.High.(*ast.BasicLit) 524 trustLen = trustLen && isbasiclit 525 } else { 526 trustLen = false 527 } 528 529 if node.Low != nil { 530 err = ctx.compileAST(node.Low) 531 if err != nil { 532 return err 533 } 534 _, isbasiclit := node.Low.(*ast.BasicLit) 535 trustLen = trustLen && isbasiclit 536 } else { 537 ctx.pushOp(&PushConst{constant.MakeInt64(0)}) 538 } 539 540 ctx.pushOp(&Reslice{Node: node, HasHigh: hasHigh, TrustLen: trustLen}) 541 return nil 542 } 543 544 func (ctx *compileCtx) compileFunctionCall(node *ast.CallExpr) error { 545 if fnnode, ok := node.Fun.(*ast.Ident); ok { 546 if ctx.HasBuiltin(fnnode.Name) { 547 return ctx.compileBuiltinCall(fnnode.Name, node.Args) 548 } 549 } 550 if !ctx.allowCalls { 551 return ErrFuncCallNotAllowed 552 } 553 554 id := ctx.curCall 555 ctx.curCall++ 556 557 oldAllowCalls := ctx.allowCalls 558 oldOps := ctx.ops 559 ctx.allowCalls = false 560 err := ctx.compileAST(node.Fun) 561 ctx.allowCalls = oldAllowCalls 562 hasFunc := false 563 if err != nil { 564 ctx.ops = oldOps 565 if err != ErrFuncCallNotAllowed { 566 return err 567 } 568 } else { 569 hasFunc = true 570 } 571 ctx.pushOp(&CallInjectionStart{HasFunc: hasFunc, id: id, Node: node}) 572 573 // CallInjectionStart pushes true on the stack if it needs the function argument re-evaluated 574 var jmpif *Jump 575 if hasFunc { 576 jmpif = &Jump{When: JumpIfFalse, Pop: true} 577 ctx.pushOp(jmpif) 578 } 579 ctx.pushOp(&Pop{}) 580 err = ctx.compileAST(node.Fun) 581 if err != nil { 582 return err 583 } 584 if jmpif != nil { 585 jmpif.Target = len(ctx.ops) 586 } 587 588 ctx.pushOp(&CallInjectionSetTarget{id: id}) 589 590 for i, arg := range node.Args { 591 err := ctx.compileAST(arg) 592 if err != nil { 593 return fmt.Errorf("error evaluating %q as argument %d in function %s: %v", exprToString(arg), i+1, exprToString(node.Fun), err) 594 } 595 if isStringLiteral(arg) { 596 ctx.compileAllocLiteralString() 597 } 598 ctx.pushOp(&CallInjectionCopyArg{id: id, ArgNum: i, ArgExpr: arg}) 599 } 600 601 ctx.pushOp(&CallInjectionComplete{id: id}) 602 603 return nil 604 } 605 606 func Listing(depth []int, ops []Op) string { 607 if depth == nil { 608 depth = make([]int, len(ops)+1) 609 } 610 buf := new(strings.Builder) 611 for i, op := range ops { 612 fmt.Fprintf(buf, " %3d (%2d->%2d) %#v\n", i, depth[i], depth[i+1], op) 613 } 614 return buf.String() 615 } 616 617 func isStringLiteral(expr ast.Expr) bool { 618 switch expr := expr.(type) { 619 case *ast.BasicLit: 620 return expr.Kind == token.STRING 621 case *ast.BinaryExpr: 622 if expr.Op == token.ADD { 623 return isStringLiteral(expr.X) && isStringLiteral(expr.Y) 624 } 625 case *ast.ParenExpr: 626 return isStringLiteral(expr.X) 627 } 628 return false 629 } 630 631 func removeParen(n ast.Expr) ast.Expr { 632 for { 633 p, ok := n.(*ast.ParenExpr) 634 if !ok { 635 break 636 } 637 n = p.X 638 } 639 return n 640 } 641 642 func exprToString(t ast.Expr) string { 643 var buf bytes.Buffer 644 printer.Fprint(&buf, token.NewFileSet(), t) 645 return buf.String() 646 }