Diff
Added: trunk/rcov4jr/README.txt (0 => 683)
--- trunk/rcov4jr/README.txt (rev 0)
+++ trunk/rcov4jr/README.txt 2007-08-08 04:02:46 UTC (rev 683)
@@ -0,0 +1,7 @@
+This gem provides JRuby support for Rcov.
+
+Right now, this project can only be built from JRuby.
+
+There seems to be problem getting this to work when it's installed as a gem
+
+The tests are part of rcov's package. Use rcov-0.8.0.2.
Added: trunk/rcov4jr/Rakefile.rb (0 => 683)
--- trunk/rcov4jr/Rakefile.rb (rev 0)
+++ trunk/rcov4jr/Rakefile.rb 2007-08-08 04:02:46 UTC (rev 683)
@@ -0,0 +1,81 @@
+# rcovrt4j: This builds a JRuby extension for Rcov.
+#
+# Compiles java and makes rcov service jar. It will
+# also package it as a gem for release into the wild.
+require 'rake'
+require 'rubygems'
+require 'rake/gempackagetask'
+require 'rake/testtask'
+require 'rake/rdoctask'
+
+GEM_NAME='rcov4jr'
+GEM_VERSION='0.0.1'
+
+def java_classpath_arg
+ begin
+ require 'java'
+ classpath = java.lang.System.getProperty('java.class.path')
+ rescue LoadError
+ end
+
+ unless classpath
+ classpath = FileList["#{ENV['JRUBY_HOME']}/lib/*.jar"].join(File::PATH_SEPARATOR)
+ end
+
+ classpath ? "-cp #{classpath}" : ""
+end
+
+def compile_java
+ mkdir_p "java/classes"
+ sh "javac -g -target 1.4 -source 1.4 -d java/classes #{java_classpath_arg} #{FileList['java/src/**/*.java'].join(' ')}"
+end
+
+def make_jar
+ require 'fileutils'
+ lib = File.join(File.dirname(__FILE__), 'lib')
+ FileUtils.mkdir(lib) unless File.exists? lib
+ sh "jar cf lib/rcovrt.jar -C java/classes/ ."
+end
+
+file 'lib/rcovrt.jar' => FileList["java/src/*.java"] do
+ compile_java
+ make_jar
+end
+
+spec = Gem::Specification.new do |s|
+ s.platform = "java"
+ s.summary = "Java implementation of Rcov's native extension."
+ s.name = GEM_NAME
+ s.version = GEM_VERSION
+ s.require_path = 'lib'
+ s.files = FileList['Rakefile.rb', 'README.txt', 'lib/rcovrt.jar']
+ s.description = "Java extension to make rcov run on JRuby"
+ #s.add_dependency 'rcov', '>= 0.8.0.2'
+ s.author = 'Ryan Bell'
+ s.email = '[EMAIL PROTECTED]'
+ s.homepage = 'http://rubyforge.org/projects/jruby-extras'
+ s.has_rdoc = true
+end
+
+Rake::GemPackageTask.new(spec).define
+
+Rake::RDocTask.new do |t|
+ t.main = 'README.txt'
+ t.rdoc_files.include 'README.txt'
+end
+
+desc "Install the gem file #{GEM_NAME}-#{GEM_VERSION}-java.gem"
+task :install_gem do
+ gem = File.join(File.dirname(__FILE__), 'pkg', "#{GEM_NAME}-#{GEM_VERSION}-java.gem")
+ ruby '-S', 'gem', 'install', gem
+end
+
+desc "Uninstall the gem #{GEM_NAME}"
+task :uninstall_gem do
+ ruby '-S', 'gem', 'uninstall', GEM_NAME
+end
+
+desc "Create the Java extension."
+task :compile => ['lib/rcovrt.jar']
+task :gem => [:compile]
+task :install_gem => [:gem]
Added: trunk/rcov4jr/java/src/CallsiteHook.java (0 => 683)
--- trunk/rcov4jr/java/src/CallsiteHook.java (rev 0)
+++ trunk/rcov4jr/java/src/CallsiteHook.java 2007-08-08 04:02:46 UTC (rev 683)
@@ -0,0 +1,129 @@
+import jregex.Matcher;
+import jregex.Pattern;
+
+import org.jruby.Ruby;
+import org.jruby.RubyArray;
+import org.jruby.RubyHash;
+import org.jruby.RubySymbol;
+import org.jruby.runtime.Frame;
+import org.jruby.runtime.ThreadContext;
+import org.jruby.runtime.builtin.IRubyObject;
+
+public class CallsiteHook implements RcovHook {
+
+ private static CallsiteHook callsiteHook;
+
+ public static CallsiteHook getCallsiteHook() {
+ if (callsiteHook == null) {
+ callsiteHook = new CallsiteHook();
+ }
+ return callsiteHook;
+ }
+
+ private boolean active;
+ private RubyHash defsites;
+ private RubyHash callsites;
+ private Pattern backtracePattern;
+
+ private CallsiteHook() {
+ super();
+ backtracePattern = new Pattern("^([^:]*):(\\d+)(:in `(.*)')?$");
+ }
+
+ public boolean isActive() {
+ return active;
+ }
+
+ public boolean isInterestedInEvent(int event) {
+ return event == RUBY_EVENT_CALL;
+ }
+
+ public RubyArray getCallsiteInfo(Ruby runtime) {
+ RubyArray info = runtime.newArray();
+ info.add(getCallsites(runtime));
+ info.add(getDefsites(runtime));
+ return info;
+ }
+
+ public void setActive(boolean active) {
+ this.active = active;
+ }
+
+ public RubyHash resetDefsites() {
+ defsites.clear();
+ return defsites;
+ }
+
+ public void event(ThreadContext context, int event, String file, int line,
+ String name, IRubyObject type) {
+ RubyArray currentMethod = context.getRuntime().newArray();
+ currentMethod.add(context.getFrameKlazz());
+ currentMethod.add(context.getRuntime().newSymbol(name));
+
+ RubyArray fileLoc = context.getRuntime().newArray();
+ fileLoc.add(file);
+ fileLoc.add(Long.valueOf(line));
+
+ RubyHash defsites = getDefsites(context.getRuntime());
+ defsites.put(currentMethod, fileLoc);
+
+ RubyHash callsites = getCallsites(context.getRuntime());
+ if (!callsites.containsKey(currentMethod)) {
+ callsites.put(currentMethod, RubyHash.newHash(context.getRuntime()));
+ }
+ RubyHash hash = (RubyHash) callsites.get(currentMethod);
+
+ RubyArray callerArray = customBacktrace(context);
+ if (!hash.containsKey(callerArray)) {
+ hash.put(callerArray, Long.valueOf(0));
+ }
+ Long count = (Long) hash.get(callerArray);
+ long itCount = count.longValue() + 1L;
+ hash.put(callerArray, Long.valueOf(itCount));
+ }
+
+ private RubyArray customBacktrace(ThreadContext context) {
+ Frame[] frames = context.createBacktrace(1, false);
+ RubyArray backtrace = (RubyArray) ThreadContext
+ .createBacktraceFromFrames(context.getRuntime(), frames);
+
+ RubyArray ary = context.getRuntime().newArray();
+ ary.add(frames[frames.length - 1].getKlazz());
+ ary.addAll(formatBacktrace(context.getRuntime(), (String) backtrace.get(1)));
+
+ return context.getRuntime().newArray((IRubyObject) ary);
+ }
+
+ private RubyArray formatBacktrace(Ruby runtime, String backtrace) {
+ Matcher matcher = backtracePattern.matcher(backtrace);
+
+ RubyArray ary = runtime.newArray();
+ if (matcher.matches()) {
+ String method = matcher.group(4);
+ String file = matcher.group(1);
+ String line = matcher.group(2);
+ Long lineNum = (line == null ? Long.valueOf(0) : Long.valueOf(line));
+
+ ary.add((method == null ? runtime.getNil() : runtime
+ .newSymbol(method)));
+ ary.add(file);
+ ary.add(lineNum);
+ }
+ return ary;
+ }
+
+ private RubyHash getCallsites(Ruby runtime) {
+ if (this.callsites == null) {
+ this.callsites = RubyHash.newHash(runtime);
+ }
+ return this.callsites;
+ }
+
+ private RubyHash getDefsites(Ruby runtime) {
+ if (this.defsites == null) {
+ this.defsites = RubyHash.newHash(runtime);
+ }
+ return this.defsites;
+ }
+
+}
Added: trunk/rcov4jr/java/src/CoverageHook.java (0 => 683)
--- trunk/rcov4jr/java/src/CoverageHook.java (rev 0)
+++ trunk/rcov4jr/java/src/CoverageHook.java 2007-08-08 04:02:46 UTC (rev 683)
@@ -0,0 +1,104 @@
+import org.jruby.Ruby;
+import org.jruby.RubyArray;
+import org.jruby.RubyHash;
+import org.jruby.runtime.ThreadContext;
+import org.jruby.runtime.builtin.IRubyObject;
+
+
+public class CoverageHook implements RcovHook {
+
+ private static CoverageHook hook;
+
+ public static CoverageHook getCoverageHook() {
+ if (hook == null) {
+ hook = new CoverageHook();
+ }
+ return hook;
+ }
+
+ private boolean active;
+ private RubyHash cover;
+
+ private CoverageHook() {
+ super();
+ }
+
+ public boolean isActive() {
+ return active;
+ }
+
+ public void setActive(boolean active) {
+ this.active = active;
+ }
+
+ public void event(ThreadContext context, int event, String file, int line,
+ String name, IRubyObject type) {
+
+ // Make sure that we have SCRIPT_LINES__ and it's a hash
+ RubyHash scriptLines = getScriptLines(context.getRuntime());
+ if (scriptLines == null || !scriptLines.containsKey(file)) {
+ return;
+ }
+
+ // make sure the file's source lines are in SCRIPT_LINES__
+ RubyHash cover = getCover(context.getRuntime());
+ RubyArray lines = (RubyArray) scriptLines.get(file);
+ if (lines == null || cover == null){
+ return;
+ }
+
+ // make sure file's counts are in COVER and set to zero
+ RubyArray counts = (RubyArray) cover.get(file);
+ if (counts == null) {
+ RubyArray ary = context.getRuntime().newArray();
+ for (int i = 0; i < lines.size(); i++) {
+ ary.add(Long.valueOf(0));
+ }
+ cover.put(file, ary);
+ counts = ary;
+ }
+
+ // update counts in COVER
+ Long count = (Long) counts.get(line);
+ if (count == null) {
+ System.out.println("Null count in " + file + ":" + line + ":" + name);
+ count = Long.valueOf(0);
+ }
+ count = Long.valueOf(count.longValue() + 1);
+ counts.set(line , count);
+ }
+
+ public boolean isInterestedInEvent(int event) {
+// return (event != RUBY_EVENT_C_CALL &&
+// event != RUBY_EVENT_C_RETURN &&
+// event != RUBY_EVENT_CLASS);
+ return event == RUBY_EVENT_CALL || event == RUBY_EVENT_LINE;
+ }
+
+ /**
+ * Returns the COVER hash, setting up the COVER constant if necessary.
+ * @param runtime
+ * @return
+ */
+ public RubyHash getCover(Ruby runtime) {
+ if (cover == null) {
+ cover = RubyHash.newHash(runtime);
+ }
+ return cover;
+ }
+
+ public RubyHash getScriptLines(Ruby runtime) {
+ IRubyObject scriptLines = runtime.getObject().getConstantAt("SCRIPT_LINES__");
+ if (scriptLines instanceof RubyHash) {
+ return (RubyHash) scriptLines;
+ } else {
+ return null;
+ }
+ }
+
+ public IRubyObject resetCoverage(Ruby runtime) {
+ getCover(runtime).clear();
+ return runtime.getNil();
+ }
+
+}
Added: trunk/rcov4jr/java/src/RcovHook.java (0 => 683)
--- trunk/rcov4jr/java/src/RcovHook.java (rev 0)
+++ trunk/rcov4jr/java/src/RcovHook.java 2007-08-08 04:02:46 UTC (rev 683)
@@ -0,0 +1,11 @@
+import org.jruby.runtime.EventHook;
+
+
+public interface RcovHook extends EventHook {
+
+ /** returns true if the hook is set */
+ boolean isActive();
+
+ /** used to mark the hook set or unset */
+ void setActive(boolean active);
+}
Added: trunk/rcov4jr/java/src/RcovrtService.java (0 => 683)
--- trunk/rcov4jr/java/src/RcovrtService.java (rev 0)
+++ trunk/rcov4jr/java/src/RcovrtService.java 2007-08-08 04:02:46 UTC (rev 683)
@@ -0,0 +1,121 @@
+import org.jruby.Ruby;
+import org.jruby.RubyArray;
+import org.jruby.RubyFixnum;
+import org.jruby.RubyHash;
+import org.jruby.RubyModule;
+import org.jruby.RubySymbol;
+import org.jruby.exceptions.RaiseException;
+import org.jruby.runtime.CallbackFactory;
+import org.jruby.runtime.builtin.IRubyObject;
+import org.jruby.runtime.load.BasicLibraryService;
+
+public class RcovrtService implements BasicLibraryService {
+
+ public boolean basicLoad(Ruby runtime) {
+ RubyModule rcov = runtime.getModule("Rcov");
+ if (rcov == null) {
+ rcov = runtime.defineModule("Rcov");
+ }
+ RubyModule rcov__ = (RubyModule) rcov.getConstant("RCOV__");
+ if (rcov__ == null) {
+ rcov__ = rcov.defineModuleUnder("RCOV__");
+ }
+ IRubyObject sl = runtime.getObject().getConstantAt("SCRIPT_LINES__");
+ if (sl == null)
+ runtime.getObject().setConstant("SCRIPT_LINES__", RubyHash.newHash(runtime));
+
+ CallbackFactory rcovrt_cb = runtime
+ .callbackFactory(RcovrtService.class);
+ rcov__.getMetaClass().defineMethod("ABI", rcovrt_cb.getFastSingletonMethod("getAbi"));
+ rcov__.getMetaClass().defineMethod("generate_callsite_info", rcovrt_cb.getFastSingletonMethod("generateCallsiteInfo"));
+ rcov__.getMetaClass().defineMethod("install_callsite_hook", rcovrt_cb.getFastSingletonMethod("installCallsiteHook"));
+ rcov__.getMetaClass().defineMethod("remove_callsite_hook", rcovrt_cb.getFastSingletonMethod("removeCallsiteHook"));
+ rcov__.getMetaClass().defineMethod("generate_coverage_info", rcovrt_cb.getFastSingletonMethod("generateCoverageInfo"));
+ rcov__.getMetaClass().defineMethod("install_coverage_hook", rcovrt_cb.getFastSingletonMethod("installCoverageHook"));
+ rcov__.getMetaClass().defineMethod("remove_coverage_hook", rcovrt_cb.getFastSingletonMethod("removeCoverageHook"));
+ rcov__.getMetaClass().defineMethod("reset_coverage", rcovrt_cb.getFastSingletonMethod("resetCoverage"));
+ rcov__.getMetaClass().defineMethod("reset_callsite", rcovrt_cb.getFastSingletonMethod("resetCallsite"));
+
+ return true;
+ }
+
+ public static IRubyObject resetCallsite(IRubyObject recv) {
+ CallsiteHook hook = CallsiteHook.getCallsiteHook();
+ if (hook.isActive()) {
+ throw RaiseException.createNativeRaiseException(
+ recv.getRuntime(),
+ new RuntimeException("Cannot reset the callsite info in the middle of a traced run."));
+ }
+ return hook.resetDefsites();
+ }
+
+ public static IRubyObject resetCoverage(IRubyObject recv) {
+ CoverageHook hook = CoverageHook.getCoverageHook();
+ if (hook.isActive()) {
+ throw RaiseException.createNativeRaiseException(
+ recv.getRuntime(),
+ new RuntimeException("Cannot reset the coverage info in the middle of a traced run."));
+ }
+ return hook.resetCoverage(recv.getRuntime());
+ }
+
+ public static IRubyObject removeCoverageHook(IRubyObject recv) {
+ return removeRcovHook(recv, CoverageHook.getCoverageHook());
+ }
+
+ public static IRubyObject installCoverageHook(IRubyObject recv) {
+ return installRcovHook(recv, CoverageHook.getCoverageHook());
+ }
+
+ public static IRubyObject generateCoverageInfo(IRubyObject recv) {
+ IRubyObject cover = CoverageHook.getCoverageHook().getCover(recv.getRuntime()).dup();
+ RubyModule rcov__ = (RubyModule) recv.getRuntime().getModule("Rcov").getConstant("RCOV__");
+ if (rcov__.const_defined(RubySymbol.newSymbol(recv.getRuntime(), "COVER")).isTrue()) {
+ rcov__.remove_const(recv.getRuntime().newString("COVER"));
+ }
+ rcov__.defineConstant("COVER", cover);
+
+ return cover;
+ }
+
+ public static IRubyObject removeCallsiteHook(IRubyObject recv) {
+ return removeRcovHook(recv, CallsiteHook.getCallsiteHook());
+ }
+
+ public static IRubyObject installCallsiteHook(IRubyObject recv) {
+ return installRcovHook(recv, CallsiteHook.getCallsiteHook());
+ }
+
+ public static IRubyObject generateCallsiteInfo(IRubyObject recv) {
+ return CallsiteHook.getCallsiteHook().getCallsiteInfo(recv.getRuntime()).dup();
+ }
+
+ public static IRubyObject getAbi(IRubyObject recv) {
+ RubyArray ary = recv.getRuntime().newArray();
+ ary.add(RubyFixnum.int2fix(recv.getRuntime(), 2L));
+ ary.add(RubyFixnum.int2fix(recv.getRuntime(), 0L));
+ ary.add(RubyFixnum.int2fix(recv.getRuntime(), 0L));
+ return ary;
+ }
+
+ private static IRubyObject removeRcovHook(IRubyObject recv, RcovHook hook) {
+ if (!hook.isActive()) {
+ hook.setActive(true);
+ recv.getRuntime().addEventHook(hook);
+ return recv.getRuntime().getTrue();
+ } else {
+ return recv.getRuntime().getFalse();
+ }
+ }
+
+ private static IRubyObject installRcovHook(IRubyObject recv, RcovHook hook) {
+ if (!hook.isActive()) {
+ hook.setActive(true);
+ recv.getRuntime().addEventHook(hook);
+ return recv.getRuntime().getTrue();
+ } else {
+ return recv.getRuntime().getFalse();
+ }
+ }
+
+}