[macruby-changes] [3287] MacRuby/trunk

source_changes at macosforge.org source_changes at macosforge.org
Sat Jan 16 20:37:53 PST 2010


Revision: 3287
          http://trac.macosforge.org/projects/ruby/changeset/3287
Author:   lsansonetti at apple.com
Date:     2010-01-16 20:37:52 -0800 (Sat, 16 Jan 2010)
Log Message:
-----------
experimental debugger

Modified Paths:
--------------
    MacRuby/trunk/array.c
    MacRuby/trunk/compiler.cpp
    MacRuby/trunk/compiler.h
    MacRuby/trunk/dispatcher.cpp
    MacRuby/trunk/include/ruby/ruby.h
    MacRuby/trunk/main.cpp
    MacRuby/trunk/rakelib/builder.rb
    MacRuby/trunk/ruby.c
    MacRuby/trunk/vm.cpp
    MacRuby/trunk/vm.h
    MacRuby/trunk/vm_eval.c

Added Paths:
-----------
    MacRuby/trunk/MacRubyDebuggerConnector.h
    MacRuby/trunk/MacRubyDebuggerConnector.m
    MacRuby/trunk/bin/rubyd
    MacRuby/trunk/debugger.cpp
    MacRuby/trunk/debugger.h

Added: MacRuby/trunk/MacRubyDebuggerConnector.h
===================================================================
--- MacRuby/trunk/MacRubyDebuggerConnector.h	                        (rev 0)
+++ MacRuby/trunk/MacRubyDebuggerConnector.h	2010-01-17 04:37:52 UTC (rev 3287)
@@ -0,0 +1,59 @@
+/*
+ * MacRuby Debugger Connector.
+ *
+ * This is an interface to the MacRuby runtime when running in debug mode.
+ * It is used by MacRuby debugger clients, such as macrubyd or Xcode.
+ * This file when compiled separately must be compiled with garbage collection
+ * enabled.
+ *
+ * This file is covered by the Ruby license. See COPYING for more details.
+ * 
+ * Copyright (C) 2010, Apple Inc. All rights reserved.
+ */
+
+#import <Foundation/Foundation.h>
+
+typedef unsigned int breakpoint_t;
+
+ at interface MacRubyDebuggerConnector : NSObject
+{
+    NSString *_socketPath;
+    NSMutableArray *_arguments;
+    NSTask *_task;
+    NSFileHandle *_socket;
+    NSString *_location;
+}
+
+- (id)initWithArguments:(NSArray *)arguments;
+
+// Execution control.
+
+- (void)startExecution;
+- (void)continueExecution;
+- (void)stepExecution;
+- (void)stopExecution;
+
+// Current state.
+
+- (NSString *)location;
+- (NSArray *)localVariables;
+- (NSArray *)backtrace;
+- (void)setFrame:(unsigned int)frame;
+
+// Breakpoints.
+
+- (breakpoint_t)addBreakPointAtPath:(NSString *)path line:(unsigned int)line
+    condition:(NSString *)condition;
+
+- (void)enableBreakPoint:(breakpoint_t)bp;
+- (void)disableBreakPoint:(breakpoint_t)bp;
+- (void)deleteBreakPoint:(breakpoint_t)bp;
+- (void)setCondition:(NSString *)condition forBreakPoint:(breakpoint_t)bp;
+
+- (NSArray *)allBreakPoints;
+
+// Context evaluation.
+
+- (NSString *)evaluateExpression:(NSString *)expression;
+
+ at end

Added: MacRuby/trunk/MacRubyDebuggerConnector.m
===================================================================
--- MacRuby/trunk/MacRubyDebuggerConnector.m	                        (rev 0)
+++ MacRuby/trunk/MacRubyDebuggerConnector.m	2010-01-17 04:37:52 UTC (rev 3287)
@@ -0,0 +1,270 @@
+/*
+ * MacRuby Debugger Connector.
+ *
+ * This file is covered by the Ruby license. See COPYING for more details.
+ * 
+ * Copyright (C) 2010, Apple Inc. All rights reserved.
+ */
+
+#import "MacRubyDebuggerConnector.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+ at implementation MacRubyDebuggerConnector
+
+- (NSData *)_recv
+{
+    assert(_socket != nil);
+    return [_socket availableData];
+}
+
+- (const char *)_recvCString
+{
+    return (const char *)[[self _recv] bytes];
+}
+
+- (NSString *)_recvString
+{
+    return [[NSString alloc] initWithData:[self _recv]
+	encoding:NSASCIIStringEncoding];
+}
+
+- (void)_send:(NSData *)data
+{
+    assert(_socket != nil);
+    [_socket writeData:data]; 
+}
+
+- (void)_sendCString:(const char *)str
+{
+    [self _send:[NSData dataWithBytes:str length:strlen(str)]];
+}
+
+-(void)_readLocation
+{
+    _location = [self _recvString];
+    if ([_location length] == 0) {
+	_location = nil;
+    }
+}
+
+- (id)initWithArguments:(NSArray *)arguments
+{
+    self = [super init];
+    if (self != nil) {
+	// Generate the socket path.
+	const char *tmpdir = getenv("TMPDIR");
+	assert(tmpdir != NULL);
+	char path[PATH_MAX];
+	snprintf(path, sizeof path, "%s/macrubyd-XXXXXX", tmpdir);
+	assert(mktemp(path) != NULL);
+	_socketPath = [[NSFileManager defaultManager]
+	    stringWithFileSystemRepresentation:path length:strlen(path)];
+
+	// Setup arguments.
+	_arguments = [NSMutableArray new];
+	[_arguments addObject:@"--debug-mode"];
+	[_arguments addObject:_socketPath];
+	[_arguments addObjectsFromArray:arguments];
+
+	_task = nil;
+	_socket = nil;
+	_location = nil;
+    }
+    return self;
+}
+
+- (void)finalize
+{
+    [super finalize];
+    [self stopExecution];
+}
+
+- (void)startExecution
+{
+    if (_task == nil || ![_task isRunning]) {
+	// Create task.
+	_task = [NSTask new];
+	[_task setLaunchPath:@"./miniruby"]; // XXX
+	[_task setArguments:_arguments];
+
+	// Launch the remote process.
+	[[NSFileManager defaultManager] removeItemAtPath:_socketPath error:nil];
+	[_task launch];
+
+	// Wait until the socket file is available.
+	while (true) {
+	    if ([[NSFileManager defaultManager] fileExistsAtPath:_socketPath]) {
+		break;
+	    }
+	    if (![_task isRunning]) {
+		// Something wrong happened at the very beginning, most likely
+		// a command-line argument error.
+		_task = nil;
+		return;
+	    }
+	    assert([_task isRunning]); // XXX raise exception
+	    usleep(500000);
+	}
+
+	// Create the socket.
+	const int fd = socket(PF_LOCAL, SOCK_STREAM, 0);
+	if (fd == -1) {
+	    // XXX should raise exception.
+	    perror("socket()");
+	    exit(1);
+	}
+
+	// Prepare the name.
+	struct sockaddr_un name;
+	name.sun_family = PF_LOCAL;
+	strncpy(name.sun_path, [_socketPath fileSystemRepresentation],
+		sizeof(name.sun_path));
+
+	// Connect.
+	if (connect(fd, (struct sockaddr *)&name, SUN_LEN(&name)) == -1) {
+	    // XXX should raise exception.
+	    perror("connect()");
+	    exit(1);
+	}
+
+	// Good!
+	_socket = [[NSFileHandle alloc] initWithFileDescriptor:fd];
+	[self _readLocation];
+    }
+}
+
+- (void)continueExecution
+{
+    [self _sendCString:"continue"];
+    [self _readLocation];
+}
+
+- (void)stepExecution
+{
+    [self _sendCString:"next"];
+    [self _readLocation];
+}
+
+- (void)stopExecution
+{
+    if (_task != nil) {
+	[_task terminate];
+	_task = nil;
+    }
+    _location = nil;
+    [[NSFileManager defaultManager] removeItemAtPath:_socketPath error:nil];
+}
+
+- (NSString *)location
+{
+    return _location;
+}
+
+- (NSArray *)localVariables
+{
+    return nil;
+}
+
+- (NSArray *)backtrace
+{
+    [self _sendCString:"backtrace"];
+    return [[self _recvString] componentsSeparatedByString:@"\n"];
+}
+
+- (void)setFrame:(unsigned int)frame
+{
+    char buf[512];
+    snprintf(buf, sizeof buf, "frame %d", frame);
+    [self _sendCString:buf];
+}
+
+- (breakpoint_t)addBreakPointAtPath:(NSString *)path line:(unsigned int)line
+	condition:(NSString *)condition
+{
+    char buf[512];
+    snprintf(buf, sizeof buf, "break %s:%d", [path fileSystemRepresentation],
+	    line);
+    [self _sendCString:buf];
+    return [[self _recvString] integerValue];
+}
+
+- (void)enableBreakPoint:(breakpoint_t)bp
+{
+    char buf[512];
+    snprintf(buf, sizeof buf, "enable %d", bp);
+    [self _sendCString:buf];
+}
+
+- (void)disableBreakPoint:(breakpoint_t)bp
+{
+    char buf[512];
+    snprintf(buf, sizeof buf, "disable %d", bp);
+    [self _sendCString:buf];
+}
+
+- (void)deleteBreakPoint:(breakpoint_t)bp
+{
+    char buf[512];
+    snprintf(buf, sizeof buf, "delete %d", bp);
+    [self _sendCString:buf];
+}
+
+- (void)setCondition:(NSString *)condition forBreakPoint:(breakpoint_t)bp
+{
+    char buf[512];
+    snprintf(buf, sizeof buf, "condition %d %s", bp, [condition UTF8String]);
+    [self _sendCString:buf];
+}
+
+- (NSDictionary *)_parseBreakPointDescription:(NSString *)desc
+{
+    NSArray *ary = [desc componentsSeparatedByString:@","];
+    if ([ary count] == 0) {
+	return nil;
+    }
+    NSMutableDictionary *dict = [NSMutableDictionary new];
+    unsigned i, count;
+    for (i = 0, count = [ary count]; i < count; i++) {
+	NSArray *fields = [[ary objectAtIndex:i]
+	    componentsSeparatedByString:@"="];
+	if ([fields count] == 2) {
+	    NSString *key = [fields objectAtIndex:0];
+	    NSString *val = [fields objectAtIndex:1];
+	    [dict setObject:val forKey:key];
+	}
+    }
+    if ([dict count] == 0) {
+	return nil;
+    }
+    return dict; 
+}
+
+
+- (NSArray *)allBreakPoints
+{
+    [self _sendCString:"info breakpoints"];
+    NSArray *ary = [[self _recvString] componentsSeparatedByString:@"\n"];
+    NSMutableArray *ary2 = [NSMutableArray new];
+    unsigned i, count;
+    for (i = 0, count = [ary count]; i < count; i++) {
+	NSString *desc = [ary objectAtIndex:i];
+	NSDictionary *dict = [self _parseBreakPointDescription:desc];
+	if (dict != nil) {
+	    [ary2 addObject:dict];
+	}
+    }
+    return ary2;
+}
+
+- (NSString *)evaluateExpression:(NSString *)expression
+{
+    char buf[512];
+    snprintf(buf, sizeof buf, "eval %s", [expression UTF8String]);
+    [self _sendCString:buf];
+    return [self _recvString];
+}
+
+ at end

Modified: MacRuby/trunk/array.c
===================================================================
--- MacRuby/trunk/array.c	2010-01-17 04:36:20 UTC (rev 3286)
+++ MacRuby/trunk/array.c	2010-01-17 04:37:52 UTC (rev 3287)
@@ -1498,10 +1498,8 @@
 static VALUE
 rb_ary_each_imp(VALUE ary, SEL sel)
 {
-    long i;
-
     RETURN_ENUMERATOR(ary, 0, 0);
-    for (i = 0; i < RARRAY_LEN(ary); i++) {
+    for (long i = 0; i < RARRAY_LEN(ary); i++) {
 	rb_yield(RARRAY_AT(ary, i));
 	RETURN_IF_BROKEN();
     }
@@ -1532,10 +1530,9 @@
 static VALUE
 rb_ary_each_index(VALUE ary, SEL sel)
 {
-    long i, n;
     RETURN_ENUMERATOR(ary, 0, 0);
 
-    for (i = 0, n = RARRAY_LEN(ary); i < n; i++) {
+    for (long i = 0, n = RARRAY_LEN(ary); i < n; i++) {
 	rb_yield(LONG2NUM(i));
 	RETURN_IF_BROKEN();
     }
@@ -1560,14 +1557,13 @@
 static VALUE
 rb_ary_reverse_each(VALUE ary, SEL sel)
 {
-    long n, len;
-
     RETURN_ENUMERATOR(ary, 0, 0);
-    len = RARRAY_LEN(ary);
+
+    long len = RARRAY_LEN(ary);
     while (len--) {
 	rb_yield(RARRAY_AT(ary, len));
 	RETURN_IF_BROKEN();
-	n = RARRAY_LEN(ary);
+	const long n = RARRAY_LEN(ary);
 	if (n < len) {
 	    len = n;
 	}
@@ -1707,7 +1703,7 @@
 	    taint = Qtrue;
 	}
 	if (OBJ_UNTRUSTED(tmp)) {
-        untrust = Qtrue;
+	    untrust = Qtrue;
 	}
     }
 

Added: MacRuby/trunk/bin/rubyd
===================================================================
--- MacRuby/trunk/bin/rubyd	                        (rev 0)
+++ MacRuby/trunk/bin/rubyd	2010-01-17 04:37:52 UTC (rev 3287)
@@ -0,0 +1,214 @@
+#!/usr/bin/ruby
+# MacRuby Debugger.
+#
+# This file is covered by the Ruby license.
+#
+# Copyright (C) 2010, Apple Inc
+
+require 'readline'
+
+class Debugger
+  def initialize(args)
+    @terran_mode = args.delete('--terran')
+    raise "need at least one argument" if args.empty?
+    @connector = ::MacRubyDebuggerConnector.alloc.initWithArguments(args)
+  end
+
+  def run
+    $stdout.puts "Starting program."
+    @connector.startExecution
+    running = true
+
+    while true do
+      if @connector.location == nil and running
+        # Looks like the program terminated.
+        $stdout.puts "Program exited."
+        running = false
+      end
+
+      # Grab a command from the prompt.
+      command = prompt
+      break unless command
+      command.strip!
+      next if command.empty?
+
+      case command
+        when 'c', 'continue'
+          if running
+            @connector.continueExecution
+          else
+            $stdout.puts "Program not running."
+          end
+        when 'n', 'next'
+          if running
+            @connector.stepExecution
+          else
+            $stdout.puts "Program not running."
+          end
+        when 'r', 'run'
+          if running
+            $stdout.print <<EOS.chomp
+The program being debugged has been started already.
+Start it from the beginning? (y or n) 
+EOS
+            if $stdin.gets.chomp == 'y'
+              @connector.stopExecution
+              @connector.startExecution
+              running = true 
+            else
+              $stdout.puts "Program not restarted."
+            end
+          else
+            $stdout.puts "Starting program."
+            @connector.startExecution
+            running = true 
+          end
+        when 'stop'
+          if running
+            @connector.stopExecution        
+          else
+            $stdout.puts "Program not running."
+          end
+        when /^b(?:reak)?\s+(.+):(\d+)(?:\s+if\s+(.+))?/
+          if running
+            file = $1.strip
+            line = $2.to_i
+            cond = $3
+            bp = @connector.addBreakPointAtPath(file, line: line,
+              condition: nil)
+            if cond
+              cond.strip!
+              unless cond.empty?
+                @connector.setCondition(cond, forBreakPoint: bp)
+              end
+            end
+            $stdout.puts "Added breakpoint #{bp}."
+          else
+            $stdout.puts "Program not running."
+          end
+        when /^enabled\s+(\d+)/
+          if running
+            bp = $1.to_i
+            @connector.enableBreakPoint(bp)
+            $stdout.puts "Enabled breakpoint #{bp}."
+          else
+            $stdout.puts "Program not running."
+          end
+        when /^disable\s+(\d+)/
+          if running
+            bp = $1.to_i
+            @connector.disableBreakPoint(bp)
+            $stdout.puts "Disabled breakpoint #{bp}."
+          else
+            $stdout.puts "Program not running."
+          end
+        when /^delete\s+(\d+)/
+          if running
+            bp = $1.to_i
+            @connector.deleteBreakPoint(bp)
+            $stdout.puts "Deleted breakpoint #{bp}."
+          else
+            $stdout.puts "Program not running."
+          end
+        when /^condition\s+(\d+)\s+(.+)/
+          if running
+            bp = $1.to_i
+            cond = $2.strip
+            @connector.setCondition(cond, forBreakPoint: bp)
+          else
+            $stdout.puts "Program not running."
+          end
+        when 'bt', 'backtrace'
+          if running
+            @connector.backtrace.each_with_index do |frame, n|
+              $stdout.puts "#%d\t%s" % [n, frame]
+            end
+          else
+            $stdout.puts "Program not running."
+          end
+        when /^fr(?:ame)?\s+(\d+)/
+          frid = $1.to_i
+          @connector.setFrame(frid)
+        when /^info\s+breakpoints/
+          if running
+            $stdout.puts "Program not running." and next unless running
+            bps = @connector.allBreakPoints
+            if bps.empty?
+              $stdout.puts "No breakpoint."
+            else
+              $stdout.puts "ID\t\tSTATE\t\tLOCATION"
+              bps.each do |bp|
+                $stdout.puts "%s\t\t%s\t\t%s" % [bp['id'],
+                  bp['enabled'] == '1' ? "enabled" : "disabled",
+                  bp['location']]
+              end
+  	    end
+          else
+            $stdout.puts "Program not running."
+          end
+        when /^info\s+threads/, /^thread\s+(\d+)/
+          $stdout.puts "Not yet implemented."
+        when /^p\s+(.+)/
+          if running
+            expr = $1.strip
+            $stdout.puts "=> #{@connector.evaluateExpression(expr)}"
+          else
+            $stdout.puts "Program not running."
+          end
+        when 'help'
+          help
+        when 'quit', 'exit'
+          @connector.stopExecution
+          exit
+        else
+          $stdout.puts "Unknown command `#{command}', type `help' for help."
+      end
+    end
+  end
+
+  private
+
+  def help
+    $stdout.puts <<EOS
+break <file:line>          Set up and enable a breakpoint at given file/line.
+break <...> if <expr>      Same as above but also set up a given condition.
+delete <bp-id>             Delete the given breakpoint.
+disable <bp-id>            Disable the given breakpoint.
+enable <bp-id>             Enable the given breakpoint.
+condition <bp-id> <expr>   Set up a condition for the given breakpoint.
+info breakpoints           List all breakpoints.
+info threads               List all threads.
+next                       Go to the next expression.
+continue                   Continue the program's execution.
+run                        Start the program's execution.
+stop                       Stop the program's execution.
+backtrace                  Show current backtrace.
+backtrace full             Show a full backtrace (for every thread).
+thread <thread-id>         Switch to the given thread.
+frame <frame-id>           Switch to the given frame.
+p <expression>             Evaluate the given expression and print the result.
+help                       Print this message.
+quit                       Terminate the debugged program and exit.
+EOS
+  end
+
+  PROMPTS = [
+    "Explorer reporting.",
+    "Ah, greetings command!",
+    "Transmit orders.",
+    "Receiving headquarters!",
+    "We have you on visual.",
+    "Let's roll!",
+    "Excellent!",
+    "Commencing!",
+    "Affirmative, sir."
+  ]
+  def prompt
+    if @terran_mode
+      $stdout.puts PROMPTS.sample
+    end
+    Readline.readline("#{@connector.location}> ", true)
+  end
+end
+
+Debugger.new(ARGV).run

Modified: MacRuby/trunk/compiler.cpp
===================================================================
--- MacRuby/trunk/compiler.cpp	2010-01-17 04:36:20 UTC (rev 3286)
+++ MacRuby/trunk/compiler.cpp	2010-01-17 04:37:52 UTC (rev 3287)
@@ -29,13 +29,15 @@
 llvm::Module *RoxorCompiler::module = NULL;
 RoxorCompiler *RoxorCompiler::shared = NULL;
 
-RoxorCompiler::RoxorCompiler(void)
+RoxorCompiler::RoxorCompiler(bool _debug_mode)
 {
     assert(RoxorCompiler::module != NULL);
     debug_info = new DIFactory(*RoxorCompiler::module);
 
+    debug_mode = _debug_mode;
     fname = "";
     inside_eval = false;
+    current_line = 0;
 
     bb = NULL;
     entry_bb = NULL;
@@ -141,6 +143,7 @@
     setScopeFunc = NULL;
     setCurrentClassFunc = NULL;
     getCacheFunc = NULL;
+    debugTrapFunc = NULL;
 
     VoidTy = Type::getVoidTy(context);
     Int1Ty = Type::getInt1Ty(context);
@@ -182,7 +185,7 @@
 }
 
 RoxorAOTCompiler::RoxorAOTCompiler(void)
-: RoxorCompiler()
+: RoxorCompiler(false)
 {
     cObject_gvar = NULL;
     cStandardError_gvar = NULL;
@@ -3055,7 +3058,12 @@
     }
     printf("... %s\n", ruby_node_name(nd_type(node)));
 #endif
-    current_line = nd_line(node);
+    if (current_line != nd_line(node)) {
+	current_line = nd_line(node);
+	if (debug_mode) {
+	    compile_debug_trap();
+	}
+    }
 
     switch (nd_type(node)) {
 	case NODE_SCOPE:
@@ -4301,13 +4309,14 @@
 		// object, let's create it.
 		// (Note: this won't work if the method is aliased, but we can
 		//  live with that for now)
-		if (!super_call
+		if (debug_mode
+		    || (!super_call
 			&& (sel == selEval
 			    || sel == selInstanceEval
 			    || sel == selClassEval
 			    || sel == selModuleEval
 			    || sel == selLocalVariables
-			    || sel == selBinding)) {
+			    || sel == selBinding))) {
 		    compile_binding();
 		}
 

Modified: MacRuby/trunk/compiler.h
===================================================================
--- MacRuby/trunk/compiler.h	2010-01-17 04:36:20 UTC (rev 3286)
+++ MacRuby/trunk/compiler.h	2010-01-17 04:37:52 UTC (rev 3287)
@@ -32,7 +32,7 @@
 	static llvm::Module *module;
 	static RoxorCompiler *shared;
 
-	RoxorCompiler(void);
+	RoxorCompiler(bool debug_mode);
 	virtual ~RoxorCompiler(void) { }
 
 	void set_fname(const char *_fname);
@@ -66,6 +66,7 @@
 	DICompileUnit debug_compile_unit;
 	DISubprogram debug_subprogram;
 
+	bool debug_mode;
 	const char *fname;
 	bool inside_eval;
 
@@ -191,6 +192,7 @@
 	Function *setScopeFunc;
 	Function *setCurrentClassFunc;
 	Function *getCacheFunc;
+	Function *debugTrapFunc;
 
 	Constant *zeroVal;
 	Constant *oneVal;
@@ -337,6 +339,7 @@
 				       Value *slot);
 	Value *compile_conversion_to_ruby(const char *type,
 					  const Type *llvm_type, Value *val);
+	void compile_debug_trap(void);
 
 	Value *compile_slot_cache(ID id);
 	virtual Value *gen_slot_cache(ID id);

Added: MacRuby/trunk/debugger.cpp
===================================================================
--- MacRuby/trunk/debugger.cpp	                        (rev 0)
+++ MacRuby/trunk/debugger.cpp	2010-01-17 04:37:52 UTC (rev 3287)
@@ -0,0 +1,422 @@
+/*
+ * MacRuby debugger.
+ *
+ * This file is covered by the Ruby license. See COPYING for more details.
+ * 
+ * Copyright (C) 2010, Apple Inc. All rights reserved.
+ */
+
+#include <llvm/Module.h>
+#include <llvm/DerivedTypes.h>
+#include <llvm/Constants.h>
+#include <llvm/CallingConv.h>
+#include <llvm/Instructions.h>
+#include <llvm/ModuleProvider.h>
+#include <llvm/Intrinsics.h>
+#include <llvm/Analysis/DebugInfo.h>
+#include <llvm/ExecutionEngine/JIT.h>
+#include <llvm/PassManager.h>
+#include <llvm/Target/TargetData.h>
+using namespace llvm;
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include "ruby/ruby.h"
+#include "ruby/node.h"
+#include "vm.h"
+#include "compiler.h"
+#include "debugger.h"
+
+void
+RoxorCompiler::compile_debug_trap(void)
+{
+    if (bb == NULL || inside_eval) {
+	return;
+    }
+
+    if (debugTrapFunc == NULL) {
+	// void rb_vm_debug_trap(const char *file, int line,
+	//	VALUE self, rb_vm_block_t *current_block, int lvars_size, ...);
+	std::vector<const Type *> types;
+	types.push_back(PtrTy);
+	types.push_back(Int32Ty);
+	types.push_back(RubyObjTy);
+	types.push_back(PtrTy);
+	types.push_back(Int32Ty);
+	FunctionType *ft = FunctionType::get(VoidTy, types, true);
+	debugTrapFunc = cast<Function>
+	    (module->getOrInsertFunction( "rb_vm_debug_trap", ft));
+    }
+
+    std::vector<Value *> params;
+    params.push_back(compile_const_pointer((void *)fname));
+    params.push_back(ConstantInt::get(Int32Ty, current_line));
+    params.push_back(current_self);
+    params.push_back(running_block == NULL
+	    ? compile_const_pointer(NULL) : running_block);
+
+    // Lvars.
+    params.push_back(ConstantInt::get(Int32Ty, (int)lvars.size()));
+    for (std::map<ID, Value *>::iterator iter = lvars.begin();
+	 iter != lvars.end(); ++iter) {
+
+	ID lvar = iter->first;
+	params.push_back(compile_id(lvar));
+	params.push_back(iter->second);
+    }
+
+    CallInst::Create(debugTrapFunc, params.begin(), params.end(), "", bb);
+}
+
+extern "C"
+void
+rb_vm_debug_trap(const char *file, const int line, VALUE self,
+	rb_vm_block_t *current_block, int lvars_size, ...)
+{
+    if (RoxorDebugger::shared == NULL) {
+	RoxorDebugger::shared = RoxorDebugger::unix_server();
+    }
+
+    va_list args;
+    va_start(args, lvars_size);
+    RoxorDebugger::shared->trap(file, line, self, current_block,
+	    lvars_size, args);
+    va_end(args);
+}
+
+unsigned int RoxorBreakPoint::IDs = 0;
+RoxorDebugger *RoxorDebugger::shared = NULL;
+
+RoxorDebugger *
+RoxorDebugger::unix_server(void)
+{
+    // Create the socket.
+    const int fd = socket(PF_LOCAL, SOCK_STREAM, 0);
+    if (fd < 0) {
+	perror("socket()");
+	exit(1);
+    }
+
+    // Bind it to the given path.
+    assert(ruby_debug_socket_path != Qfalse);
+    const char *path = RSTRING_PTR(ruby_debug_socket_path);
+    struct sockaddr_un name;
+    name.sun_family = PF_LOCAL;
+    strncpy(name.sun_path, path, sizeof(name.sun_path));
+    if (bind(fd, (struct sockaddr *)&name, SUN_LEN(&name)) != 0) {
+	perror("bind()");
+	exit(1);
+    }
+
+    // Prepare for listening, backlog of 1 connection.
+    if (listen(fd, 1) != 0) {
+	perror("listen()");
+	exit(1);
+    }
+
+    // Accept first connection.
+    struct sockaddr_un remote;
+    socklen_t remotelen = sizeof(remote);
+    const int pipe = accept(fd, (struct sockaddr *)&remote, &remotelen);
+    if (socket < 0) {
+	perror("accept()");
+	exit(1);
+    }
+
+//printf("DBG: connected with fd %d\n", pipe);
+
+    return new RoxorDebugger(pipe);
+}
+
+RoxorDebugger::RoxorDebugger(int _pipe)
+{
+    pipe = _pipe;
+    binding = NULL;
+    breakpoint = NULL;
+    frame = 0;
+
+    // We want to break at the very first line.
+    break_at_next = true;
+}
+
+RoxorDebugger::~RoxorDebugger()
+{
+    close(pipe);
+}
+
+bool
+RoxorDebugger::send(std::string &data)
+{
+//printf("DBG: send %s\n", data.c_str());
+    const ssize_t len = ::send(pipe, data.c_str(), data.length(), 0);
+    if (len != (ssize_t)data.length()) {
+	if (len == -1) {
+	    perror("send()");
+	}
+	else {
+	    fprintf(stderr, "expected to send %ld bytes instead of %ld\n",
+		    data.length(), len);
+	}
+	return false;	
+    }
+    return true;
+}
+
+bool
+RoxorDebugger::recv(std::string &data)
+{
+    char buf[512];
+    const ssize_t len = ::recv(pipe, buf, sizeof buf, 0);
+    if (len == -1) {
+	perror("recv()");
+	return false;
+    }
+    data.clear();
+    data.append(buf, len);
+//printf("DBG: recv %s\n", data.c_str());
+    return true;
+}
+
+void 
+RoxorDebugger::trap(const char *file, const int line, VALUE self,
+	rb_vm_block_t *block, int lvars_size, va_list lvars)
+{
+    // Compute location.
+    char buf[100];
+    snprintf(buf, sizeof buf, "%s:%d", file, line);
+    std::string loc(buf);
+
+    // Should we break here?
+    bool should_break = false;
+    RoxorBreakPoint *bp = NULL;
+    if (break_at_next) {
+	should_break = true;
+	break_at_next = false;
+    }
+    else {
+	std::map<std::string, RoxorBreakPoint *>::iterator iter =
+	    breakpoints.find(loc);
+	if (iter != breakpoints.end()) {
+	    bp = iter->second;
+	    if (bp->enabled) {
+		if (bp->condition.length() > 0) {
+		    VALUE obj = evaluate_expression(self, block, lvars_size,
+			    lvars, bp->condition);
+		    if (obj != Qnil && obj != Qfalse) {
+			should_break = true;
+		    }
+		}
+		else {
+		    should_break = true;
+		}
+	    }
+	}
+    }
+
+    if (!should_break) {
+	return;
+    }
+
+    // Send the current location to the client.
+    if (!send(loc)) {
+	return;
+    }
+
+    // Invalidate latest binding.
+    if (binding != NULL) {
+	GC_RELEASE(binding);
+	binding = NULL;
+    }
+    frame = 0;
+
+    // Enter the main loop.
+    std::string data;
+    while (true) {
+	// Receive command.
+	if (!recv(data)) {
+	    return;
+	}
+	const char *cmd = data.c_str();
+
+	// Handle command.
+	if (strcmp(cmd, "exit") == 0) {
+	    exit(0);
+	}
+	if (strcmp(cmd, "continue") == 0) {
+	    break;
+	}
+	if (strcmp(cmd, "next") == 0) {
+	    break_at_next = true;
+	    break;
+	}
+	if (strncmp(cmd, "break ", 6) == 0) {
+	    std::string location = data.substr(6);
+	    if (location.length() >= 3) {
+		unsigned int id = add_breakpoint(location);
+		char buf[10];
+		snprintf(buf, sizeof buf, "%d", id);
+		send(buf);
+		continue;
+	    }
+	}
+	if (strncmp(cmd, "enable ", 7) == 0) {
+	    const unsigned int bpid = (unsigned int)strtol(cmd + 7, NULL, 10);
+	    RoxorBreakPoint *bp = find_breakpoint(bpid);
+	    if (bp != NULL) {
+		bp->enabled = true;
+	    }
+	    continue;
+	}
+	if (strncmp(cmd, "disable ", 8) == 0) {
+	    const unsigned int bpid = (unsigned int)strtol(cmd + 8, NULL, 10);
+	    RoxorBreakPoint *bp = find_breakpoint(bpid);
+	    if (bp != NULL) {
+		bp->enabled = false;
+	    }
+	    continue;
+	}
+	if (strncmp(cmd, "delete ", 7) == 0) {
+	    const unsigned int bpid = (unsigned int)strtol(cmd + 7, NULL, 10);
+	    delete_breakpoint(bpid);
+	    continue;
+	}
+	if (strncmp(cmd, "condition ", 10) == 0) {
+	    const char *p = strchr(cmd + 10, ' ');
+	    if (p != NULL) {
+		std::string bpstr = data.substr(10, p - cmd - 10);
+		const unsigned int bpid = (unsigned int)strtol(bpstr.c_str(),
+			NULL, 10);
+		std::string condstr = data.substr(10 + bpstr.length());
+		RoxorBreakPoint *bp = find_breakpoint(bpid);
+		if (bp != NULL) {
+		    bp->condition = condstr;
+		}
+		continue;
+	    }
+	} 
+	if (strcmp(cmd, "info breakpoints") == 0) {
+	    std::string resp;
+	    for (std::map<std::string, RoxorBreakPoint *>::iterator iter
+		    = breakpoints.begin();
+		    iter != breakpoints.end();
+		    ++iter) {
+		char buf[100];
+		snprintf(buf, sizeof buf, "id=%d,location=%s,enabled=%d\n",
+			iter->second->id, iter->first.c_str(),
+			iter->second->enabled);
+		resp.append(buf);
+	    }
+	    if (resp.length() == 0) {
+		resp.append("nil");
+	    }
+	    send(resp);
+	    continue;
+	}
+	if (strcmp(cmd, "backtrace") == 0) {
+	    VALUE bt = rb_vm_backtrace(100);
+	    VALUE str = rb_ary_join(bt, rb_str_new2("\n"));
+	    send(RSTRING_PTR(str));
+	    continue;
+	}
+	if (strncmp(cmd, "frame ", 6) == 0) {
+	    unsigned int frid = (unsigned int)strtol(cmd + 6, NULL, 10);
+	    if (frame != frid) {
+		frame = frid;
+		if (binding != NULL) {
+		    GC_RELEASE(binding);
+		    binding = NULL;
+		}
+		// TODO update location
+	    }
+	    continue;
+	}
+	if (strncmp(cmd, "eval ", 5) == 0) {
+	    std::string expr = data.substr(5);
+	    if (expr.length() > 0) {
+		VALUE obj = evaluate_expression(self, block, lvars_size, lvars,
+			expr);
+		send(RSTRING_PTR(rb_inspect(obj)));
+		continue;
+	    }
+	} 
+
+	// Unknown command, let's exit the program. This should never happen!
+	fprintf(stderr, "unknown command: %s\n", cmd);
+	exit(1); 
+    }
+}
+
+unsigned int
+RoxorDebugger::add_breakpoint(std::string &location)
+{
+    std::map<std::string, RoxorBreakPoint *>::iterator iter
+	= breakpoints.find(location);
+
+    RoxorBreakPoint *bp;
+    if (iter == breakpoints.end()) {
+	bp = new RoxorBreakPoint();
+	breakpoints[location] = bp;
+    }
+    else {
+	bp = iter->second;
+    }
+
+    return bp->id;
+}
+
+VALUE
+RoxorDebugger::evaluate_expression(VALUE self, rb_vm_block_t *block,
+	int lvars_size, va_list lvars, std::string expr)
+{
+    if (binding == NULL) {
+	if (frame == 0) {
+	    binding = rb_vm_create_binding(self, block, lvars_size, lvars,
+		    false);
+	}
+	else {
+	    binding = GET_VM()->get_binding(frame - 1);
+	    assert(binding != NULL);
+	}
+	GC_RETAIN(binding);
+    }
+
+    try {
+	return rb_vm_eval_string(self, 0, rb_str_new2(expr.c_str()), binding,
+		"(eval)", 1);
+    }
+    catch (...) {
+	rb_vm_print_current_exception();
+	return Qnil;
+    }
+}
+
+RoxorBreakPoint *
+RoxorDebugger::find_breakpoint(unsigned int bpid)
+{
+    for (std::map<std::string, RoxorBreakPoint *>::iterator iter
+	    = breakpoints.begin();
+	    iter != breakpoints.end();
+	    ++iter) {
+	if (iter->second->id == bpid) {
+	    return iter->second;
+	}
+    }
+    return NULL;
+}
+
+bool
+RoxorDebugger::delete_breakpoint(unsigned int bpid)
+{
+    for (std::map<std::string, RoxorBreakPoint *>::iterator iter
+	    = breakpoints.begin();
+	    iter != breakpoints.end();
+	    ++iter) {
+	if (iter->second->id == bpid) {
+	    breakpoints.erase(iter);
+	    return true;
+	}
+    }
+    return false;
+}

Added: MacRuby/trunk/debugger.h
===================================================================
--- MacRuby/trunk/debugger.h	                        (rev 0)
+++ MacRuby/trunk/debugger.h	2010-01-17 04:37:52 UTC (rev 3287)
@@ -0,0 +1,65 @@
+/*
+ * MacRuby debugger.
+ *
+ * This file is covered by the Ruby license. See COPYING for more details.
+ * 
+ * Copyright (C) 2010, Apple Inc. All rights reserved.
+ */
+
+#ifndef __DEBUGGER_H_
+#define __DEBUGGER_H_
+
+#if defined(__cplusplus)
+
+class RoxorBreakPoint {
+    public:
+	static unsigned int IDs;
+	unsigned int id;
+	bool enabled;
+	std::string condition;
+
+	RoxorBreakPoint(void) {
+	    id = ++IDs;
+	    enabled = true;
+	}
+};
+
+class RoxorDebugger {
+    private:
+	std::map<std::string, RoxorBreakPoint *> breakpoints;
+	bool break_at_next;
+	std::string location;
+	RoxorBreakPoint *breakpoint;
+	rb_vm_binding_t *binding;
+	unsigned int frame;
+	int pipe;
+
+	bool send(std::string &data);
+	bool send(const char *str) {
+	    std::string s(str);
+	    return send(s);
+	}
+	bool recv(std::string &data);
+
+	unsigned int add_breakpoint(std::string &location);
+	RoxorBreakPoint *find_breakpoint(unsigned int bpid);
+	bool delete_breakpoint(unsigned int bpid);
+
+	VALUE evaluate_expression(VALUE self, rb_vm_block_t *block,
+		int lvars_size, va_list lvars, std::string expr);
+	
+    public:
+	static RoxorDebugger *shared;
+
+	static RoxorDebugger *unix_server(void);
+
+	RoxorDebugger(int pipe);
+	~RoxorDebugger();
+
+	void trap(const char *file, const int line, VALUE self,
+		rb_vm_block_t *block, int lvars_size, va_list lvars);
+};
+
+#endif /* __cplusplus */
+
+#endif /* __DEBUGGER_H_ */

Modified: MacRuby/trunk/dispatcher.cpp
===================================================================
--- MacRuby/trunk/dispatcher.cpp	2010-01-17 04:36:20 UTC (rev 3286)
+++ MacRuby/trunk/dispatcher.cpp	2010-01-17 04:37:52 UTC (rev 3287)
@@ -1053,11 +1053,15 @@
 
     RoxorVM *vm = GET_VM();
 
+    struct Finally {
+	RoxorVM *vm;
+	Finally(RoxorVM *_vm) { vm = _vm; }
+	~Finally() { vm->pop_current_binding(); }
+    } finalizer(vm);
+
     VALUE retval = __rb_vm_dispatch(vm, cache, top, self, NULL, sel, block,
 	    opt, argc, argv);
 
-    vm->pop_current_binding();
-
     return retval;
 }
 

Modified: MacRuby/trunk/include/ruby/ruby.h
===================================================================
--- MacRuby/trunk/include/ruby/ruby.h	2010-01-17 04:36:20 UTC (rev 3286)
+++ MacRuby/trunk/include/ruby/ruby.h	2010-01-17 04:37:52 UTC (rev 3287)
@@ -1000,9 +1000,15 @@
 
 VALUE rb_equal(VALUE,VALUE);
 
+// Flags.
 RUBY_EXTERN VALUE ruby_verbose, ruby_debug;
+
+// AOT compiler.
 RUBY_EXTERN VALUE ruby_aot_compile, ruby_aot_init_func;
 
+// Debugger.
+RUBY_EXTERN VALUE ruby_debug_socket_path;
+
 PRINTF_ARGS(NORETURN(void rb_raise(VALUE, const char*, ...)), 2, 3);
 PRINTF_ARGS(NORETURN(void rb_fatal(const char*, ...)), 1, 2);
 PRINTF_ARGS(NORETURN(void rb_bug(const char*, ...)), 1, 2);

Modified: MacRuby/trunk/main.cpp
===================================================================
--- MacRuby/trunk/main.cpp	2010-01-17 04:36:20 UTC (rev 3286)
+++ MacRuby/trunk/main.cpp	2010-01-17 04:37:52 UTC (rev 3287)
@@ -5,6 +5,8 @@
  * Copyright (C) 1993-2007 Yukihiro Matsumoto
  */
 
+#include "llvm.h"
+
 #undef RUBY_EXPORT
 #include "ruby.h"
 #include "ruby/node.h"
@@ -12,11 +14,7 @@
 #include <locale.h>
 #endif
 
-extern "C" {
-    void rb_vm_print_current_exception(void);
-    void rb_vm_aot_compile(NODE *);
-    void rb_vm_init_compiler(void);
-}
+#include "vm.h"
 
 extern bool ruby_is_miniruby;
 

Modified: MacRuby/trunk/rakelib/builder.rb
===================================================================
--- MacRuby/trunk/rakelib/builder.rb	2010-01-17 04:36:20 UTC (rev 3286)
+++ MacRuby/trunk/rakelib/builder.rb	2010-01-17 04:37:52 UTC (rev 3287)
@@ -115,7 +115,7 @@
   ruby signal sprintf st string struct time transcode util variable version
   thread id objc bs encoding main dln dmyext marshal gcd
   vm_eval prelude miniprelude gc-stub bridgesupport compiler dispatcher vm
-  MacRuby
+  debugger MacRuby MacRubyDebuggerConnector
 }
 
 OBJS_CFLAGS = {

Modified: MacRuby/trunk/ruby.c
===================================================================
--- MacRuby/trunk/ruby.c	2010-01-17 04:36:20 UTC (rev 3286)
+++ MacRuby/trunk/ruby.c	2010-01-17 04:37:52 UTC (rev 3287)
@@ -44,6 +44,7 @@
 /* TODO: move to VM */
 VALUE ruby_debug = Qfalse;
 VALUE ruby_verbose = Qfalse;
+VALUE ruby_debug_socket_path = Qfalse;
 VALUE ruby_aot_compile = Qfalse;
 VALUE ruby_aot_init_func = Qfalse;
 VALUE rb_parser_get_yydebug(VALUE);
@@ -769,6 +770,18 @@
 		argc--; argv++;
 		argc--; argv++;
 	    }
+	    else if (strcmp("debug-mode", s) == 0) {
+		// This option is not documented and only used by macrubyd.
+		// Users should use macrubyd and never call this option
+		// directly.
+		if (argc < 2) {
+		    rb_raise(rb_eRuntimeError,
+			    "expected 1 argument (unix socket path) for --debug-mode");
+		}
+		ruby_debug_socket_path = rb_str_new2(argv[1]);
+		GC_RETAIN(ruby_debug_socket_path);
+		argc--; argv++;
+	    }
 	    else {
 		rb_raise(rb_eRuntimeError,
 			 "invalid option --%s  (-h will show valid options)", s);

Modified: MacRuby/trunk/vm.cpp
===================================================================
--- MacRuby/trunk/vm.cpp	2010-01-17 04:36:20 UTC (rev 3286)
+++ MacRuby/trunk/vm.cpp	2010-01-17 04:37:52 UTC (rev 3287)
@@ -47,6 +47,7 @@
 #include "id.h"
 #include "vm.h"
 #include "compiler.h"
+#include "debugger.h"
 #include "objc.h"
 #include "dtrace.h"
 
@@ -3000,10 +3001,9 @@
 }
 
 extern "C"
-void
-rb_vm_push_binding(VALUE self, rb_vm_block_t *current_block,
-		   rb_vm_var_uses **parent_var_uses,
-		   int lvars_size, ...)
+rb_vm_binding_t *
+rb_vm_create_binding(VALUE self, rb_vm_block_t *current_block, int lvars_size,
+	va_list lvars, bool vm_push)
 {
     rb_vm_binding_t *binding =
 	(rb_vm_binding_t *)xmalloc(sizeof(rb_vm_binding_t));
@@ -3017,24 +3017,36 @@
 	}
     }
 
-    va_list ar;
-    va_start(ar, lvars_size);
     for (int i = 0; i < lvars_size; ++i) {
-	ID name = va_arg(ar, ID);
-	VALUE *value = va_arg(ar, VALUE *);
+	ID name = va_arg(lvars, ID);
+	VALUE *value = va_arg(lvars, VALUE *);
 	l = push_local(l, name, value);
     }
-    va_end(ar);
 
-    rb_vm_add_binding_lvar_use(binding, current_block, parent_var_uses);
-
     RoxorVM *vm = GET_VM();
     GC_WB(&binding->block, vm->current_block());
+    if (vm_push) {
+	vm->push_current_binding(binding);
+    }
 
-    vm->push_current_binding(binding);
+    return binding;
 }
 
 extern "C"
+void
+rb_vm_push_binding(VALUE self, rb_vm_block_t *current_block,
+	rb_vm_var_uses **parent_var_uses, int lvars_size, ...)
+{
+    va_list lvars;
+    va_start(lvars, lvars_size);
+    rb_vm_binding_t *binding = rb_vm_create_binding(self, current_block,
+	    lvars_size, lvars, true);
+    va_end(lvars);
+
+    rb_vm_add_binding_lvar_use(binding, current_block, parent_var_uses);
+}
+
+extern "C"
 rb_vm_binding_t *
 rb_vm_current_binding(void)
 {
@@ -3725,9 +3737,13 @@
 void
 rb_vm_init_compiler(void)
 {
+    if (ruby_aot_compile && ruby_debug_socket_path) {
+	fprintf(stderr, "cannot run in both AOT and debug mode\n");
+	exit(1);
+    }
     RoxorCompiler::shared = ruby_aot_compile
 	? new RoxorAOTCompiler()
-	: new RoxorCompiler();
+	: new RoxorCompiler(ruby_debug_socket_path);
 }
 
 extern "C" void rb_node_release(NODE *node);
@@ -4798,7 +4814,7 @@
     GET_VM()->set_current_class(NULL);
 
     VALUE top_self = rb_obj_alloc(rb_cTopLevel);
-    rb_objc_retain((void *)top_self);
+    GC_RETAIN(top_self);
     GET_VM()->set_current_top_object(top_self);
 
     rb_vm_set_current_scope(rb_cNSObject, SCOPE_PRIVATE);

Modified: MacRuby/trunk/vm.h
===================================================================
--- MacRuby/trunk/vm.h	2010-01-17 04:36:20 UTC (rev 3286)
+++ MacRuby/trunk/vm.h	2010-01-17 04:37:52 UTC (rev 3287)
@@ -250,10 +250,12 @@
     return NEW_FBODY(NEW_METHOD(node, klass, noex), 0);
 }
 
+VALUE rb_vm_eval_string(VALUE self, VALUE klass, VALUE src,
+	rb_vm_binding_t *binding, const char *file, const int line);
 VALUE rb_vm_run(const char *fname, NODE *node, rb_vm_binding_t *binding,
-		bool inside_eval);
+	bool inside_eval);
 VALUE rb_vm_run_under(VALUE klass, VALUE self, const char *fname, NODE *node,
-		      rb_vm_binding_t *binding, bool inside_eval);
+	rb_vm_binding_t *binding, bool inside_eval);
 void rb_vm_aot_compile(NODE *node);
 
 void rb_vm_init_compiler(void);
@@ -385,6 +387,8 @@
     }
 }
 
+rb_vm_binding_t *rb_vm_create_binding(VALUE self, rb_vm_block_t *current_block,
+	int lvars_size, va_list lvars, bool vm_push);
 rb_vm_binding_t *rb_vm_current_binding(void);
 void rb_vm_add_binding(rb_vm_binding_t *binding);
 void rb_vm_pop_binding();
@@ -442,6 +446,7 @@
 VALUE rb_vm_current_exception(void);
 void rb_vm_set_current_exception(VALUE exception);
 VALUE rb_vm_backtrace(int level);
+void rb_vm_print_current_exception(void);
 
 #define TEST_THREAD_CANCEL() (pthread_testcancel())
 
@@ -957,6 +962,15 @@
 		? NULL : bindings.back();
 	}
 
+	rb_vm_binding_t *get_binding(unsigned int index) {
+	    if (!bindings.empty()) {
+		if (index < bindings.size()) {
+		    return bindings[index];
+		}
+	    }
+	    return NULL;
+	}
+
 	void push_current_binding(rb_vm_binding_t *binding, bool retain=true) {
 	    if (retain) {
 		rb_objc_retain(binding);

Modified: MacRuby/trunk/vm_eval.c
===================================================================
--- MacRuby/trunk/vm_eval.c	2010-01-17 04:36:20 UTC (rev 3286)
+++ MacRuby/trunk/vm_eval.c	2010-01-17 04:37:52 UTC (rev 3287)
@@ -291,29 +291,20 @@
     return rb_call(obj, idEach, 0, 0, CALL_FCALL, false);
 }
 
-static VALUE
-eval_string(VALUE self, VALUE klass, VALUE src, VALUE scope, const char *file,
-	    const int line)
+VALUE
+rb_vm_eval_string(VALUE self, VALUE klass, VALUE src, rb_vm_binding_t *binding,
+	const char *file, const int line)
 {
-    rb_vm_binding_t *b = NULL;
-    if (scope != Qnil) {
-	if (!rb_obj_is_kind_of(scope, rb_cBinding)) {
-	    rb_raise(rb_eTypeError, "wrong argument type %s (expected Binding)",
-		     rb_obj_classname(scope));
-	}
-	b = (rb_vm_binding_t *)DATA_PTR(scope);
-    }
-
     bool old_parse_in_eval = rb_vm_parse_in_eval();
     rb_vm_set_parse_in_eval(true);
-    if (b != NULL) {
+    if (binding != NULL) {
 	// Binding must be added because the parser needs it.
-	rb_vm_add_binding(b);
+	rb_vm_add_binding(binding);
     }
 
     NODE *node = rb_compile_string(file, src, line);
 
-    if (b != NULL) {
+    if (binding != NULL) {
 	// We remove the binding now but we still pass it to the VM, which
 	// will use it for compilation.
 	rb_vm_pop_binding();
@@ -330,10 +321,25 @@
 	}
     }
 
-    return rb_vm_run_under(klass, self, file, node, b, true);
+    return rb_vm_run_under(klass, self, file, node, binding, true);
 }
 
 static VALUE
+eval_string(VALUE self, VALUE klass, VALUE src, VALUE scope, const char *file,
+	    const int line)
+{
+    rb_vm_binding_t *binding = NULL;
+    if (scope != Qnil) {
+	if (!rb_obj_is_kind_of(scope, rb_cBinding)) {
+	    rb_raise(rb_eTypeError, "wrong argument type %s (expected Binding)",
+		     rb_obj_classname(scope));
+	}
+	binding = (rb_vm_binding_t *)DATA_PTR(scope);
+    }
+    return rb_vm_eval_string(self, klass, src, binding, file, line);
+}
+
+static VALUE
 specific_eval(int argc, VALUE *argv, VALUE klass, VALUE self)
 {
     VALUE retval;
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/macruby-changes/attachments/20100116/01be3548/attachment-0001.html>


More information about the macruby-changes mailing list