1/* 2 * gomacro - A Go interpreter with Lisp-like macros 3 * 4 * Copyright (C) 2018-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 * debug.go 12 * 13 * Created on Apr 20, 2018 14 * Author Massimiliano Ghilardi 15 */ 16 17package fast 18 19import ( 20 "go/ast" 21 "go/token" 22 23 . "github.com/cosmos72/gomacro/base" 24) 25 26type stubDebugger struct{} 27 28func (s stubDebugger) Breakpoint(ir *Interp, env *Env) DebugOp { 29 return DebugOpContinue 30} 31 32func (s stubDebugger) At(ir *Interp, env *Env) DebugOp { 33 return DebugOpContinue 34} 35 36// return true if statement is either "break" or _ = "break" 37func isBreakpoint(stmt ast.Stmt) bool { 38 switch node := stmt.(type) { 39 case *ast.ExprStmt: 40 return isBreakLiteral(node.X) 41 case *ast.AssignStmt: 42 if node.Tok == token.ASSIGN && len(node.Lhs) == 1 && len(node.Rhs) == 1 { 43 return isUnderscore(node.Lhs[0]) && isBreakLiteral(node.Rhs[0]) 44 } 45 } 46 return false 47} 48 49func isUnderscore(node ast.Expr) bool { 50 switch node := node.(type) { 51 case *ast.Ident: 52 return node.Name == "_" 53 } 54 return false 55} 56 57func isBreakLiteral(node ast.Expr) bool { 58 switch node := node.(type) { 59 case *ast.BasicLit: 60 return node.Kind == token.STRING && node.Value == `"break"` 61 } 62 return false 63} 64 65func (c *Comp) breakpoint() Stmt { 66 return func(env *Env) (Stmt, *Env) { 67 ir := Interp{c, env} 68 sig := ir.debug(true) 69 env.IP++ 70 stmt := env.Code[env.IP] 71 if sig != SigNone { 72 run := env.Run 73 stmt = run.Interrupt 74 if run.Options&OptDebugDebugger != 0 { 75 run.Debugf("after breakpoint: single-stepping with stmt = %p, env = %p, IP = %v, execFlags = %v, signals = %#v", stmt, env, env.IP, run.ExecFlags, run.Signals) 76 } 77 } 78 return stmt, env 79 } 80} 81 82func singleStep(env *Env) (Stmt, *Env) { 83 stmt := env.Code[env.IP] 84 run := env.Run 85 if run.Signals.Debug == SigNone { 86 return stmt, env // resume normal execution 87 } 88 89 if env.CallDepth < run.DebugDepth { 90 if run.Options&OptDebugDebugger != 0 { 91 run.Debugf("single-stepping: stmt = %p, env = %p, IP = %v, env.CallDepth = %d, g.DebugDepth = %d", stmt, env, env.IP, env.CallDepth, run.DebugDepth) 92 } 93 c := env.DebugComp 94 if c != nil { 95 ir := Interp{c, env} 96 sig := ir.debug(false) // not a breakpoint 97 if sig != SigNone { 98 run := env.Run 99 run.Signals.Debug = sig 100 } 101 } 102 } 103 104 // single step 105 stmt, env = stmt(env) 106 if run.Signals.Debug != SigNone { 107 stmt = run.Interrupt 108 } 109 return stmt, env 110} 111 112func (ir *Interp) debug(breakpoint bool) Signal { 113 run := ir.env.Run 114 if run.Debugger == nil { 115 ir.Comp.Warnf("// breakpoint: no debugger set with Interp.SetDebugger(), resuming execution (warned only once)") 116 run.Debugger = stubDebugger{} 117 } 118 var op DebugOp 119 if breakpoint { 120 op = run.Debugger.Breakpoint(ir, ir.env) 121 } else { 122 op = run.Debugger.At(ir, ir.env) 123 } 124 if run.Options&OptDebugDebugger != 0 { 125 run.Debugf("Debugger returned op = %v", op) 126 } 127 return run.applyDebugOp(op) 128} 129 130func (run *Run) applyDebugOp(op DebugOp) Signal { 131 if op.Panic != nil { 132 if run.Options&OptDebugDebugger != 0 { 133 run.Debugf("applyDebugOp: op = %v, signaling panic(%v)", op, *op.Panic) 134 } 135 panic(*op.Panic) 136 } 137 saveOp := op 138 var sig Signal 139 if op.Depth > 0 { 140 sig = SigDebug 141 } else { 142 sig = SigNone 143 op.Depth = 0 144 } 145 if run.Options&OptDebugDebugger != 0 { 146 if op == saveOp { 147 run.Debugf("applyDebugOp: op = %v, updated run.DebugDepth from %v to %v", op, run.DebugDepth, op.Depth) 148 } else { 149 run.Debugf("applyDebugOp: op = %v, replaced with %v and updated run.DebugDepth from %v to %v", saveOp, op, run.DebugDepth, op.Depth) 150 } 151 } 152 run.DebugDepth = op.Depth 153 run.ExecFlags.SetDebug(sig != SigNone) 154 run.Signals.Debug = sig 155 return sig 156} 157