From e50f66b8756d642e99d702e607b525d8c0791477 Mon Sep 17 00:00:00 2001 From: drexin Date: Fri, 21 Jan 2011 11:07:08 +0100 Subject: [PATCH 1/2] added support for agents --- .gitignore | 5 + Rakefile | 4 +- examples/agents.rb | 13 ++ lib/cloby_ext.jar | Bin 4688 -> 10788 bytes src/org/jruby/clojure/ClojureAgent.java | 110 ++++++++++ src/org/jruby/clojure/ClojureFunction.java | 240 +++++++++++++++++++++ src/org/jruby/clojure/ClojureLibrary.java | 1 + 7 files changed, 371 insertions(+), 2 deletions(-) create mode 100644 .gitignore create mode 100644 examples/agents.rb create mode 100644 src/org/jruby/clojure/ClojureAgent.java create mode 100644 src/org/jruby/clojure/ClojureFunction.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0f46c00 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.DS_Store +pkg +doc +*.gem +build diff --git a/Rakefile b/Rakefile index a00aa4d..08e2b7e 100644 --- a/Rakefile +++ b/Rakefile @@ -12,12 +12,12 @@ desc "Compile the extension" task :compile => "pkg/classes" do |t| ant.javac :srcdir => "src", :destdir => t.prerequisites.first, :source => "1.5", :target => "1.5", :debug => true, - :classpath => "${java.class.path}:${sun.boot.class.path}:lib/clojure-1.2.0.jar" + :classpath => "${java.class.path}:${sun.boot.class.path}:javalib/clojure-1.2.0.jar" end desc "Build the jar" task :jar => :compile do - ant.jar :basedir => "pkg/classes", :destfile => "javalib/cloby_ext.jar", :includes => "**/*.class" + ant.jar :basedir => "pkg/classes", :destfile => "lib/cloby_ext.jar", :includes => "**/*.class" end task :package => :jar diff --git a/examples/agents.rb b/examples/agents.rb new file mode 100644 index 0000000..f522446 --- /dev/null +++ b/examples/agents.rb @@ -0,0 +1,13 @@ +require 'cloby' + +ag = Agent.new 42 + +puts ag.deref # => 42 + +ag.dispatch 19 do |a,b| + a-b +end + +puts ag.deref # => 23 + +Agent.shutdown # IMPORTANT! program will not exit if agents are not shutdown \ No newline at end of file diff --git a/lib/cloby_ext.jar b/lib/cloby_ext.jar index 0141f4a2056746c97880a4f4080296b9fe2528b3..71f04820f121d0b643265bd562a6055c6b1598f9 100644 GIT binary patch literal 10788 zcmb7K1z23kvL?8@6WlepOK^7xGC0BA-Gc{r3&GtT28W;lGH7sj4epPaOnom1H3x;laRQ!N43fIRwG}t-ygng2{`k2{TG7NW6I(1_M+2jTIKm z;f0krPZ(SOh284qi}~{XgH>KwL0UpwRgFnr;#_`oL{65G=^K(PBi-2Oc%2IK6x-^S z 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 MzKpjT%3uEd53sEcv;Y7A delta 3583 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 X0=dv&Au1|z06Bo_*G?kTzlnbV*^871 diff --git a/src/org/jruby/clojure/ClojureAgent.java b/src/org/jruby/clojure/ClojureAgent.java new file mode 100644 index 0000000..6628e8c --- /dev/null +++ b/src/org/jruby/clojure/ClojureAgent.java @@ -0,0 +1,110 @@ +package org.jruby.clojure; + + +import org.jruby.Ruby; +import org.jruby.RubyClass; +import org.jruby.RubyModule; +import org.jruby.RubyObject; +import org.jruby.anno.JRubyMethod; +import org.jruby.anno.JRubyClass; +import org.jruby.runtime.ObjectAllocator; +import org.jruby.runtime.ThreadContext; +import org.jruby.runtime.builtin.IRubyObject; +import clojure.lang.Agent; +import clojure.lang.ISeq; +import clojure.lang.RT; +import clojure.lang.ArraySeq; +import org.jruby.javasupport.JavaUtil; +import org.jruby.runtime.Block; +import org.jruby.RubyBoolean; +import static org.jruby.runtime.Visibility.*; + +@JRubyClass(name = "Agent") +public class ClojureAgent extends RubyObject { + + public static RubyClass createAgentClass(Ruby runtime) { + RubyClass agentc = runtime.defineClass("Agent", runtime.getObject(), AGENT_ALLOCATOR); + agentc.setReifiedClass(ClojureAgent.class); + agentc.kindOf = new RubyModule.KindOf() { + @Override + public boolean isKindOf(IRubyObject obj, RubyModule type) { + return obj instanceof ClojureAgent; + } + }; + agentc.defineAnnotatedMethods(ClojureAgent.class); + return agentc; + } + + private static ObjectAllocator AGENT_ALLOCATOR = new ObjectAllocator() { + public IRubyObject allocate(Ruby runtime, RubyClass klass) { + return new ClojureAgent(runtime, klass); + } + }; + + Ruby ruby; + Agent agent; + + ClojureAgent(Ruby runtime, RubyClass klass) { + super(runtime, klass); + ruby = null; + agent = null; + } + + @JRubyMethod(name = "shutdown", meta = true) + public static IRubyObject shutdown(ThreadContext context, IRubyObject recv) { + Agent.shutdown(); + return context.getRuntime().getNil(); + } + + @JRubyMethod(visibility = PRIVATE) + public IRubyObject initialize(final ThreadContext context, final IRubyObject state, Block block) { + ruby = context.getRuntime(); + try { + agent = new Agent(state); + this.setErrorHandler(context, block); + } catch (Exception e) { + throw ruby.newConcurrencyError(e.getLocalizedMessage()); + } + return this; + } + + @JRubyMethod(name = "dispatch", rest = true) + public IRubyObject dispatch(final ThreadContext context, final IRubyObject[] args, final Block block) { + agent.dispatch( new ClojureFunction(context, block), ArraySeq.create((Object[])args), true); + return ruby.getNil(); + } + + @JRubyMethod + public Object deref(final ThreadContext context) { + try { + return agent.deref(); + } catch (Exception e) { + throw context.getRuntime().newConcurrencyError(e.getLocalizedMessage()); + } + } + + @JRubyMethod(name = "get_error") + public Object getError(final ThreadContext context) { + return agent.getError(); + } + + @JRubyMethod + public Object restart(final ThreadContext context, final IRubyObject newState) { + return restart(context, newState, new RubyBoolean(context.getRuntime(), true)); + } + + @JRubyMethod + public Object restart(final ThreadContext context, final IRubyObject newState, final IRubyObject clearActions) { + try { + return agent.restart(newState, clearActions.isTrue()); + } catch (Exception e) { + throw context.getRuntime().newConcurrencyError(e.getLocalizedMessage()); + } + } + + @JRubyMethod(name = "set_error_handler") + public IRubyObject setErrorHandler(final ThreadContext context, final Block block) { + agent.setErrorHandler(new ClojureFunction(context, block)); + return context.getRuntime().getNil(); + } +} \ No newline at end of file diff --git a/src/org/jruby/clojure/ClojureFunction.java b/src/org/jruby/clojure/ClojureFunction.java new file mode 100644 index 0000000..4df265f --- /dev/null +++ b/src/org/jruby/clojure/ClojureFunction.java @@ -0,0 +1,240 @@ +package org.jruby.clojure; + +import org.jruby.Ruby; +import org.jruby.runtime.Block; +import org.jruby.runtime.ThreadContext; +import clojure.lang.IFn; +import clojure.lang.ISeq; +import clojure.lang.RT; +import org.jruby.javasupport.JavaUtil; + +public class ClojureFunction implements IFn { + + final Block block; + final Ruby ruby; + + public ClojureFunction(final ThreadContext context, final Block block) { + this.block = block; + this.ruby = context.getRuntime(); + } + + @Override + public Object call() throws Exception { + return invoke(); + } + + public void run() { + try { + invoke(); + } catch(Exception e) { + throw new RuntimeException(e); + } + } + + public Object invoke() throws Exception { + return block.call(ruby.getCurrentContext()); + } + + public Object invoke(Object arg1) throws Exception { + return block.call(ruby.getCurrentContext(), + JavaUtil.convertJavaToRuby(ruby,arg1) + ); + } + + public Object invoke(Object arg1, Object arg2) throws Exception { + return block.call(ruby.getCurrentContext(), + JavaUtil.convertJavaToRuby(ruby,arg1), + JavaUtil.convertJavaToRuby(ruby,arg2) + ); + } + + public Object invoke(Object arg1, Object arg2, Object arg3) throws Exception { + return block.call(ruby.getCurrentContext(), + JavaUtil.convertJavaToRuby(ruby,arg1), + JavaUtil.convertJavaToRuby(ruby,arg2), + JavaUtil.convertJavaToRuby(ruby,arg3) + ); + } + + public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4) throws Exception { + Object[] args = { + arg1, arg2, arg3, arg4 + }; + return block.call(ruby.getCurrentContext(), JavaUtil.convertJavaArrayToRuby(ruby, args)); + } + + public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5) throws Exception { + Object[] args = { + arg1, arg2, arg3, arg4, arg5 + }; + return block.call(ruby.getCurrentContext(), JavaUtil.convertJavaArrayToRuby(ruby, args)); + } + + public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6) throws Exception { + Object[] args = { + arg1, arg2, arg3, arg4, arg5, arg6 + }; + return block.call(ruby.getCurrentContext(), JavaUtil.convertJavaArrayToRuby(ruby, args)); + } + + public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7) + throws Exception { + Object[] args = { + arg1, arg2, arg3, arg4, arg5, arg6, arg7 + }; + return block.call(ruby.getCurrentContext(), JavaUtil.convertJavaArrayToRuby(ruby, args)); + } + + public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, + Object arg8) throws Exception { + Object[] args = { + arg1, arg2, arg3, arg4, arg5, arg6, arg7, + arg8 + }; + return block.call(ruby.getCurrentContext(), JavaUtil.convertJavaArrayToRuby(ruby, args)); + } + + public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, + Object arg8, Object arg9) throws Exception { + Object[] args = { + arg1, arg2, arg3, arg4, arg5, arg6, arg7, + arg8, arg9 + }; + return block.call(ruby.getCurrentContext(), JavaUtil.convertJavaArrayToRuby(ruby, args)); + } + + public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, + Object arg8, Object arg9, Object arg10) throws Exception { + Object[] args = { + arg1, arg2, arg3, arg4, arg5, arg6, arg7, + arg8, arg9, arg10 + }; + return block.call(ruby.getCurrentContext(), JavaUtil.convertJavaArrayToRuby(ruby, args)); + } + + public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, + Object arg8, Object arg9, Object arg10, Object arg11) throws Exception { + Object[] args = { + arg1, arg2, arg3, arg4, arg5, arg6, arg7, + arg8, arg9, arg10, arg11 + }; + return block.call(ruby.getCurrentContext(), JavaUtil.convertJavaArrayToRuby(ruby, args)); + } + + public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, + Object arg8, Object arg9, Object arg10, Object arg11, Object arg12) throws Exception { + Object[] args = { + arg1, arg2, arg3, arg4, arg5, arg6, arg7, + arg8, arg9, arg10, arg11, arg12 + }; + return block.call(ruby.getCurrentContext(), JavaUtil.convertJavaArrayToRuby(ruby, args)); + } + + public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, + Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13) throws Exception { + Object[] args = { + arg1, arg2, arg3, arg4, arg5, arg6, arg7, + arg8, arg9, arg10, arg11, arg12, arg13 + }; + return block.call(ruby.getCurrentContext(), JavaUtil.convertJavaArrayToRuby(ruby, args)); + } + + public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, + Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14) + throws Exception { + Object[] args = { + arg1, arg2, arg3, arg4, arg5, arg6, arg7, + arg8, arg9, arg10, arg11, arg12, arg13, arg14 + }; + return block.call(ruby.getCurrentContext(), JavaUtil.convertJavaArrayToRuby(ruby, args)); + } + + public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, + Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14, + Object arg15) throws Exception { + Object[] args = { + arg1, arg2, arg3, arg4, arg5, arg6, arg7, + arg8, arg9, arg10, arg11, arg12, arg13, arg14, + arg15 + }; + return block.call(ruby.getCurrentContext(), JavaUtil.convertJavaArrayToRuby(ruby, args)); + } + + public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, + Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14, + Object arg15, Object arg16) throws Exception { + Object[] args = { + arg1, arg2, arg3, arg4, arg5, arg6, arg7, + arg8, arg9, arg10, arg11, arg12, arg13, arg14, + arg15, arg16 + }; + return block.call(ruby.getCurrentContext(), JavaUtil.convertJavaArrayToRuby(ruby, args)); + } + + public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, + Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14, + Object arg15, Object arg16, Object arg17) throws Exception { + Object[] args = { + arg1, arg2, arg3, arg4, arg5, arg6, arg7, + arg8, arg9, arg10, arg11, arg12, arg13, arg14, + arg15, arg16, arg17 + }; + return block.call(ruby.getCurrentContext(), JavaUtil.convertJavaArrayToRuby(ruby, args)); + } + + public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, + Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14, + Object arg15, Object arg16, Object arg17, Object arg18) throws Exception { + Object[] args = { + arg1, arg2, arg3, arg4, arg5, arg6, arg7, + arg8, arg9, arg10, arg11, arg12, arg13, arg14, + arg15, arg16, arg17, arg18 + }; + return block.call(ruby.getCurrentContext(), JavaUtil.convertJavaArrayToRuby(ruby, args)); + } + + public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, + Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14, + Object arg15, Object arg16, Object arg17, Object arg18, Object arg19) throws Exception { + Object[] args = { + arg1, arg2, arg3, arg4, arg5, arg6, arg7, + arg8, arg9, arg10, arg11, arg12, arg13, arg14, + arg15, arg16, arg17, arg18, arg19 + }; + return block.call(ruby.getCurrentContext(), JavaUtil.convertJavaArrayToRuby(ruby, args)); + } + + public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, + Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14, + Object arg15, Object arg16, Object arg17, Object arg18, Object arg19, Object arg20) + throws Exception { + Object[] args = { + arg1, arg2, arg3, arg4, arg5, arg6, arg7, + arg8, arg9, arg10, arg11, arg12, arg13, arg14, + arg15, arg16, arg17, arg18, arg19, arg20 + }; + return block.call(ruby.getCurrentContext(), JavaUtil.convertJavaArrayToRuby(ruby, args)); + } + + public Object invoke(Object arg1, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7, + Object arg8, Object arg9, Object arg10, Object arg11, Object arg12, Object arg13, Object arg14, + Object arg15, Object arg16, Object arg17, Object arg18, Object arg19, Object arg20, + Object... args) throws Exception { + Object[] args1 = { + arg1, arg2, arg3, arg4, arg5, arg6, arg7, + arg8, arg9, arg10, arg11, arg12, arg13, arg14, + arg15, arg16, arg17, arg18, arg19, arg20 + }; + + Object[] conArgs = new Object[args1.length+args.length]; + System.arraycopy(args1, 0, conArgs, 0, args1.length); + System.arraycopy(args, 0, conArgs, 0, args.length); + + return block.call(ruby.getCurrentContext(), JavaUtil.convertJavaArrayToRuby(ruby,conArgs)); + } + + @Override + public Object applyTo(final ISeq arglist) throws Exception{ + return block.call(ruby.getCurrentContext(), JavaUtil.convertJavaArrayToRuby(ruby, RT.seqToArray(arglist))); + } +} \ No newline at end of file diff --git a/src/org/jruby/clojure/ClojureLibrary.java b/src/org/jruby/clojure/ClojureLibrary.java index 26e48f1..4195c2b 100644 --- a/src/org/jruby/clojure/ClojureLibrary.java +++ b/src/org/jruby/clojure/ClojureLibrary.java @@ -23,6 +23,7 @@ public void load(Ruby runtime, boolean wrap) throws IOException { runtime.defineClassUnder("Object", runtime.getObject(), new ClojureObjectAllocator(), cljModule); runtime.getKernel().defineAnnotatedMethods(ClojureDosync.class); + ClojureAgent.createAgentClass(runtime); } public static class ClojureObjectAllocator implements ObjectAllocator { From c6c7547b85bd7c1865084741ebeb0b845a3cb963 Mon Sep 17 00:00:00 2001 From: drexin Date: Mon, 31 Jan 2011 01:57:31 +0100 Subject: [PATCH 2/2] added support for atoms --- examples/atoms.rb | 18 ++++ lib/cloby_ext.jar | Bin 10788 -> 13991 bytes src/org/jruby/clojure/ClojureAgent.java | 4 +- src/org/jruby/clojure/ClojureAtom.java | 115 ++++++++++++++++++++++ src/org/jruby/clojure/ClojureLibrary.java | 1 + 5 files changed, 135 insertions(+), 3 deletions(-) create mode 100644 examples/atoms.rb create mode 100644 src/org/jruby/clojure/ClojureAtom.java diff --git a/examples/atoms.rb b/examples/atoms.rb new file mode 100644 index 0000000..30ec1c1 --- /dev/null +++ b/examples/atoms.rb @@ -0,0 +1,18 @@ +require 'cloby' + +atom = Atom.new 5 + +atom.swap{|a| a + 6} +puts atom.deref # => 11 + +puts atom.compare_and_set(42, 11) # => false +puts atom.deref # => 11 + +puts atom.compare_and_set(11, 42) # => true +puts atom.deref # => 42 + +puts atom.swap(1,2,3) {|a,b,c,d| a+b+c+d} # => 48 + +atom.reset 13 + +puts atom.deref # => 13 \ No newline at end of file diff --git a/lib/cloby_ext.jar b/lib/cloby_ext.jar index 71f04820f121d0b643265bd562a6055c6b1598f9..cc6a223375ce56e4bff9691f155caba5a029a107 100644 GIT binary patch delta 9492 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