| ############################################################################### |
| # Copyright (c) 2005, 2016 IBM Corporation and others. |
| # |
| # This program and the accompanying materials are made available under the |
| # terms of the Eclipse Public License v. 2.0 which is available at |
| # http://www.eclipse.org/legal/epl-2.0. |
| # |
| # SPDX-License-Identifier: EPL-2.0 |
| # |
| ############################################################################### |
| |
| require 'monitor' |
| require 'common/Logger' |
| require 'debugger/AbstractContext' |
| require 'debugger/Exceptions' |
| |
| module Kernel |
| def xored_debugger_current_method |
| caller[0] =~ /\d:in `([^']+)'/ |
| $1 |
| end |
| end |
| |
| module XoredDebugger |
| class BasicContext < AbstractContext |
| include Logger |
| STOP_AT_ANY_DEPTH = -1 |
| |
| def initialize(thread) |
| @levels = [] |
| @thread = thread |
| @suspended = false |
| @monitor = Monitor.new |
| @waitResumed = @monitor.new_cond |
| reset_stepping |
| end |
| |
| def thread |
| @thread |
| end |
| |
| def dead? |
| not @thread.alive? |
| end |
| |
| def suspended? |
| @suspended |
| end |
| |
| # Continuation Commands |
| def run() |
| if (@suspended) |
| @monitor.synchronize do |
| log('Requesting thread ' + @thread.object_id.to_s + ' to resume') |
| @suspended = false |
| @waitResumed.signal |
| end |
| else |
| raise OperationNotAvailableError |
| end |
| end |
| |
| def step_into |
| @stepping = true |
| @stop_depth = STOP_AT_ANY_DEPTH |
| run |
| end |
| |
| def step_over |
| @stepping = true |
| @stop_depth = stack_frames_num |
| run |
| end |
| |
| def step_out |
| @stepping = true |
| @stop_depth = stack_frames_num - 1 |
| run |
| end |
| |
| def stop |
| begin |
| reset_stepping |
| run if @suspended |
| ensure |
| Kernel.exit! |
| end |
| nil |
| end |
| |
| def suspend |
| if (!@suspended) |
| log('Requesting thread ' + @thread.object_id.to_s + ' to suspend') |
| @suspended = true |
| else |
| raise OperationNotAvailableError |
| end |
| end |
| |
| # Evaluation |
| def eval(text, index) |
| frame = @levels[stack_frames_num - 1 - index] |
| if frame.nil? |
| log('frame ' + index.to_s + ' is nil ' + @levels.inspect) |
| return nil |
| end |
| binding = frame[ :binding ] |
| info = frame[ :info ] |
| Kernel.eval(text.to_s, binding, info.file, info.line) |
| end |
| |
| # Stack depth |
| def stack_frames_num |
| @levels.length |
| end |
| |
| # Stack level |
| def stack_frame(index) |
| frame = @levels[stack_frames_num - 1 - index] |
| return frame[ :info ] |
| end |
| |
| |
| # basic debugger specific methods |
| def stack_update(file, line, binding, label) |
| if (@levels.empty?) |
| stack_push(file, line, binding, label) |
| else |
| new_frame = { |
| :binding => binding, |
| :info => StackLevelInfo.new(file, line, label) |
| } |
| @levels[stack_frames_num - 1] = new_frame |
| end |
| end |
| |
| def stack_push(file, line, binding, label) |
| frame = { |
| :binding => binding, |
| :info => StackLevelInfo.new(file, line, label) |
| } |
| @levels.push(frame) |
| end |
| |
| def stack_pop |
| @levels.pop |
| if (@stepping && (@stop_depth >= stack_frames_num)) |
| @stop_depth = STOP_AT_ANY_DEPTH |
| end |
| end |
| |
| def check_suspended |
| if (Thread.current != @thread) |
| raise ThreadError, 'Context.check_suspended called from invalid thread' |
| end |
| |
| @monitor.synchronize do |
| while (@suspended) |
| log('Suspending thread ' + @thread.object_id.to_s) |
| @waitResumed.wait |
| log('Resuming thread ' + @thread.object_id.to_s) |
| end |
| end |
| end |
| |
| def check_stepping |
| depth = stack_frames_num |
| return (@stepping && (@stop_depth == STOP_AT_ANY_DEPTH || depth <= @stop_depth)) |
| end |
| |
| def reset_stepping |
| @stepping = false |
| @stop_depth = STOP_AT_ANY_DEPTH |
| end |
| end |
| end |