lib/sprout/tasks/fdb_task.rb in sprout-as3-bundle-0.2.3 vs lib/sprout/tasks/fdb_task.rb in sprout-as3-bundle-0.2.9

- old
+ new

@@ -1,473 +1,947 @@ \ No newline at end of file + +module Sprout #:nodoc: + + class FDBTaskError < StandardError #:nodoc: + end + + # The FDBTask provides a procedural rake front end to the FDB command line tool + # + # Here is a decent tutorial on using FDB with SWF or HTML content: + # http://installingcats.wordpress.com/tag/adobe-flex/ + # + # You can send the fdb task some debug commands directly or simply + # execute the rake task and interact with the debugger manually. + # + # The FDBTask requires that you have a debug Flash Player installed + # on your system as the default execution application for SWF files. + # + # Following is an example of setting up a breakpoint in + # SomeFile at line 23 + # fdb :debug do |t| + # t.file = 'bin/SomeProject-debug.swf' + # t.run + # t.break = 'SomeFile:23' + # t.continue + # end + # + # You can also point the FDBTask at HTML pages. These pages will be + # launched in your default browswer. You will need to manually install + # a debug Flash Player in that particular browser. + # To use a browser instead of the desktop Flash Player, simply point + # file argument at an HTML document or remote URL. The SWF file loaded + # must be compiled using the -debug flag in order to connect to the + # debugger. + # fdb :debug do |t| + # t.file = 'bin/SomeProject-debug.html' + # t.run + # t.continue + # end + # + class FDBTask < ToolTask + # The SWF file to debug. + attr_accessor :swf + + def initialize_task # :nodoc: + @default_gem_name = 'sprout-flex3sdk-tool' + @default_gem_path = 'bin/fdb' + @queue = [] + end + + def define # :nodoc: + self + end + + def stdout=(out) # :nodoc: + @stdout = out + end + + def stdout # :nodoc: + @stdout ||= $stdout + end + + def execute(*args) # :nodoc: + # TODO: First check the SWF file to ensure that debugging is enabled! + buffer = FDBBuffer.new(get_executable, stdout) + buffer.wait_for_prompt + + @queue.each do |command| + handle_command(buffer, command) + end + + buffer.join + self + end + + def handle_command(buffer, command) # :nodoc: + parts = command.split(' ') + name = parts.shift + value = parts.shift + case name + when "sleep" + buffer.sleep_until value + when "terminate" + buffer.kill + else + buffer.write command + end + end + + def get_executable # :nodoc: + exe = Sprout.get_executable(gem_name, gem_path, gem_version) + User.clean_path(exe) + end + + def command_queue # :nodoc: + @queue + end + + # Print backtrace of all stack frames + def bt + @queue << "bt" + end + + # Set breakpoint at specified line or function + def break=(point) + @queue << "break #{point}" + end + + # Display the name and number of the current file + def cf + @queue << "cf" + end + + # Clear breakpoint at specified line or function + def clear=(point) + @queue << "clear #{point}" + end + + # Apply/remove conditional expression to a breakpoint + def condition=(cond) + @queue << "condition #{cond}" + end + + # Continue execution after stopping at breakpoint + def continue + @queue << "continue" + end + + # Alias for continue + def c + @queue << "continue" + end + + # Sets commands to execute when breakpoint hit + def commands=(cmd) + @queue << "com #{cmd}" + end + + # Delete breakpoints or auto-display expressions + def delete + @queue << "delete" + end + + # Add a directory to the search path for source files + def directory=(dir) + @queue << "directory #{dir}" + end + + # Disable breakpoints or auto-display expressions + def disable + @queue << "disable" + end + + # Disassemble source lines or functions + def disassemble + @queue << "dissassemble" + end + + # Add an auto-display expressions + def display=(disp) + @queue << "disp #{disp}" + end + + # Enable breakpoints or auto-display expressions + def enable + @queue << "enable" + end + + # Enable breakpoints or auto-display expressions + def e + @queue << "enable" + end + + # Specify application to be debugged. + def file=(file) + @prerequisites << file + @queue << "file #{file}" + end + + # Execute until current function returns + def finish + @queue << "finish" + end + + # Specify how to handle a fault + def handle + @queue << "handle" + end + + # Set listing location to where execution is halted + def home + @queue << "home" + end + + # Display information about the program being debugged + def info + @queue << "info" + end + + # Argument variables of current stack frame + def info_arguments + @queue << "i a" + end + + # Status of user-settable breakpoints + def info_breakpoints + @queue << "i b" + end + + # Display list of auto-display expressions + def info_display + @queue << "i d" + end + + # Names of targets and files being debugged + def info_files + @queue << "i f" + end + + # All function names + def info_functions=(value) + @queue << "i fu #{value}" + end + + # How to handle a fault + def info_handle + @queue << "i h" + end + + # Local variables of current stack frame + def info_locals + @queue << "i l" + end + + # Scope chain of current stack frame + def info_scopechain + @queue << "i sc" + end + + # Source files in the program + def info_sources + @queue << "i so" + end + + # Backtrace of the stack + def info_stack + @queue << "i s" + end + + # List of swfs in this session + def info_swfs + @queue << "i sw" + end + + # Application being debugged + def info_targets + @queue << "i t" + end + + # All global and static variable names + def info_variables + @queue << "i v" + end + + # Kill execution of program being debugged + def kill + @queue << "kill" + end + + # List specified function or line + def list + @queue << "list" + end + + # Step program + def next + @queue << "next" + end + + # Print value of variable EXP + def print=(msg) + @queue << "print #{msg}" + end + + # Print working directory + def pwd + @queue << "pwd" + end + + # Exit fdb + def quit + @queue << "quit" + @queue << "y" + @queue << "terminate" + end + + # Start debugged program + def run + @queue << "run" + end + + # Set the value of a variable + def set=(value) + @queue << "set #{value}" + end + + # Sleep until some 'str' String is sent to the output + def sleep_until(str) + @queue << "sleep #{str}" + end + + # Read fdb commands from a file + def source=(file) + @queue << "source #{file}" + end + + # Step program until it reaches a different source line + def step + @queue << "step" + end + + # Force the Flash Debugger and running SWF to close + def terminate + @queue << "terminate" + end + + # Remove an auto-display expression + def undisplay + @queue << "undisplay" + end + + # Set or clear filter for file listing based on swf + def viewswf + @queue << "viewswf" + end + + # Displays the context of a variable + def what=(value) + @queue << "what #{value}" + end + + # Same as bt + def where + @queue << "bt" + end + + end + + # A buffer that provides clean blocking support for the fdb command shell + class FDBBuffer #:nodoc: + PLAYER_TERMINATED = 'Player session terminated' + EXIT_PROMPT = 'The program is running. Exit anyway? (y or n)' + PROMPT = '(fdb) ' + QUIT = 'quit' + + # The constructor expects a buffered input and output + def initialize(exe, output, user_input=nil) + @output = output + @prompted = false + @faulted = false + @user_input = user_input + @found_search = false + @pending_expression = nil + listen exe + end + + def user_input + @user_input ||= $stdin + end + + def create_input(exe) + ProcessRunner.new("#{exe}") + end + + def sleep_until(str) + @found_search = false + @pending_expression = str + while !@found_search do + sleep(0.2) + end + end + + # Listen for messages from the input process + def listen(exe) + @input = nil + @listener = Thread.new do + @input = create_input(exe) + def puts(msg) + $stdout.puts msg + end + + char = '' + line = '' + while true do + begin + char = @input.readpartial 1 + rescue EOFError => e + puts "End of File - Exiting Now" + @prompted = true + break + end + + if(char == "\n") + line = '' + else + line << char + end + + @output.print char + @output.flush + + if(line == PROMPT || line.match(/\(y or n\) $/)) + @prompted = true + line = '' + elsif(@pending_expression && line.match(/#{@pending_expression}/)) + @found_search = true + @pending_expression = nil + elsif(line == PLAYER_TERMINATED) + puts "" + puts "Closed SWF Connection - Exiting Now" + @prompted = true + break + end + end + end + + end + + # Block for the life of the input process + def join + puts ">> Entering FDB interactive mode, type 'help' for more info." + print PROMPT + $stdout.flush + + Thread.new do + while true do + msg = user_input.gets.chomp! + @input.puts msg + wait_for_prompt + end + end + + @listener.join + end + + # Block until prompted returns true + def wait_for_prompt + while !@prompted do + sleep(0.2) + end + end + + # Kill the buffer + def kill + @listener.kill + end + + # Send a message to the buffer input and reset the prompted flag to false + def write(msg) + @prompted = false + @input.puts msg + print msg + "\n" + $stdout.flush + if(msg != "c" && msg != "continue") + wait_for_prompt + end + end + + end +end + +def fdb(args, &block) + Sprout::FDBTask.define_task(args, &block) +end +