Skip to content

Instantly share code, notes, and snippets.

@headius
Created April 28, 2015 18:57
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save headius/5a511cedc4194f0f8968 to your computer and use it in GitHub Desktop.
Make JRuby classes and modules update their constant transactionally
diff --git a/build.xml b/build.xml
index 64c66cb..f347790 100644
--- a/build.xml
+++ b/build.xml
@@ -307,6 +307,7 @@
<zipfileset src="${build.lib.dir}/joda-time-1.6.1.jar"/>
<zipfileset src="${build.lib.dir}/yydebug.jar"/>
<zipfileset src="${build.lib.dir}/nailgun-0.7.1.jar"/>
+ <zipfileset src="${build.lib.dir}/clojure.jar"/>
<metainf dir="${base.dir}/spi">
<include name="services/**"/>
</metainf>
diff --git a/build_lib/clj-ds.jar b/build_lib/clj-ds.jar
deleted file mode 100644
index c5f26e4..0000000
Binary files a/build_lib/clj-ds.jar and /dev/null differ
diff --git a/build_lib/clojure.jar b/build_lib/clojure.jar
new file mode 100644
index 0000000..82b549d
Binary files /dev/null and b/build_lib/clojure.jar differ
diff --git a/nbproject/project.xml b/nbproject/project.xml
index c1f3260..ada8aa1 100644
--- a/nbproject/project.xml
+++ b/nbproject/project.xml
@@ -225,7 +225,7 @@
<java-data xmlns="http://www.netbeans.org/ns/freeform-project-java/2">
<compilation-unit>
<package-root>${src.dir}</package-root>
- <classpath mode="compile">build_lib/junit.jar:build_lib/jline-0.9.93.jar:build_lib/jna.jar:build_lib/nailgun-0.7.1.jar:build_lib/joni.jar:build_lib/dynalang-0.3.jar:build_lib/invokedynamic.jar:build_lib/jcodings.jar:build_lib/constantine.jar:build_lib/bytelist.jar:build_lib/jffi.jar:build_lib/yydebug.jar:build_lib/bsf.jar:build_lib/jaffl.jar:build_lib/asm-3.2.jar:build_lib/asm-analysis-3.2.jar:build_lib/asm-commons-3.2.jar:build_lib/asm-tree-3.2.jar:build_lib/asm-util-3.2.jar:build_lib/jsr292-mock.jar:build_lib/jgrapht-jdk1.5.jar:build_lib/jnr-netdb.jar:build_lib/jnr-posix.jar:build_lib/joda-time-1.6.1.jar:build_lib/livetribe-jsr223-2.0.6.jar:build_lib/clj-ds.jar</classpath>
+ <classpath mode="compile">build_lib/junit.jar:build_lib/jline-0.9.93.jar:build_lib/jna.jar:build_lib/nailgun-0.7.1.jar:build_lib/joni.jar:build_lib/dynalang-0.3.jar:build_lib/invokedynamic.jar:build_lib/jcodings.jar:build_lib/constantine.jar:build_lib/bytelist.jar:build_lib/jffi.jar:build_lib/yydebug.jar:build_lib/bsf.jar:build_lib/jaffl.jar:build_lib/asm-3.2.jar:build_lib/asm-analysis-3.2.jar:build_lib/asm-commons-3.2.jar:build_lib/asm-tree-3.2.jar:build_lib/asm-util-3.2.jar:build_lib/jsr292-mock.jar:build_lib/jgrapht-jdk1.5.jar:build_lib/jnr-netdb.jar:build_lib/jnr-posix.jar:build_lib/joda-time-1.6.1.jar:build_lib/livetribe-jsr223-2.0.6.jar:build_lib/clojure.jar</classpath>
<built-to>${jruby.classes.dir}</built-to>
<built-to>${lib.dir}/jruby.jar</built-to>
<javadoc-built-to>docs/api</javadoc-built-to>
diff --git a/src/org/jruby/Ruby.java b/src/org/jruby/Ruby.java
index 815d268..6c67524 100644
--- a/src/org/jruby/Ruby.java
+++ b/src/org/jruby/Ruby.java
@@ -3206,7 +3206,9 @@ public RaiseException newIllegalSequence(String message) {
}
public RaiseException newNoMethodError(String message, String name, IRubyObject args) {
- return new RaiseException(new RubyNoMethodError(this, getNoMethodError(), message, name, args), true);
+ RaiseException exception = new RaiseException(new RubyNoMethodError(this, getNoMethodError(), message, name, args), true);
+ exception.preRaise(getCurrentContext());
+ return exception;
}
public RaiseException newNameError(String message, String name) {
@@ -3221,12 +3223,18 @@ public RaiseException newNameError(String message, String name, Throwable origEx
if (printWhenVerbose && origException != null && this.isVerbose()) {
origException.printStackTrace(getErrorStream());
}
- return new RaiseException(new RubyNameError(
+
+ RaiseException exception = new RaiseException(new RubyNameError(
this, getNameError(), message, name), false);
+ exception.preRaise(getCurrentContext());
+
+ return exception;
}
public RaiseException newLocalJumpError(RubyLocalJumpError.Reason reason, IRubyObject exitValue, String message) {
- return new RaiseException(new RubyLocalJumpError(this, getLocalJumpError(), message, reason, exitValue), true);
+ RaiseException exception = new RaiseException(new RubyLocalJumpError(this, getLocalJumpError(), message, reason, exitValue), true);
+ exception.preRaise(getCurrentContext());
+ return exception;
}
public RaiseException newLocalJumpErrorNoBlock() {
@@ -3234,7 +3242,7 @@ public RaiseException newLocalJumpErrorNoBlock() {
}
public RaiseException newRedoLocalJumpError() {
- return new RaiseException(new RubyLocalJumpError(this, getLocalJumpError(), "unexpected redo", RubyLocalJumpError.Reason.REDO, getNil()), true);
+ return newLocalJumpError(RubyLocalJumpError.Reason.REDO, getNil(), "unexpected redo");
}
public RaiseException newLoadError(String message) {
@@ -3258,7 +3266,9 @@ public RaiseException newSystemStackError(String message, StackOverflowError soe
}
public RaiseException newSystemExit(int status) {
- return new RaiseException(RubySystemExit.newInstance(this, status));
+ RaiseException exception = new RaiseException(RubySystemExit.newInstance(this, status));
+ exception.preRaise(getCurrentContext());
+ return exception;
}
public RaiseException newIOError(String message) {
diff --git a/src/org/jruby/RubyKernel.java b/src/org/jruby/RubyKernel.java
index 689c02e..aa45324 100644
--- a/src/org/jruby/RubyKernel.java
+++ b/src/org/jruby/RubyKernel.java
@@ -267,7 +267,9 @@ private static IRubyObject methodMissing(ThreadContext context, IRubyObject recv
exArgs = new IRubyObject[]{msg, symbol};
}
- throw new RaiseException((RubyException)exc.newInstance(context, exArgs, Block.NULL_BLOCK));
+ RaiseException exception = new RaiseException((RubyException)exc.newInstance(context, exArgs, Block.NULL_BLOCK));
+ exception.preRaise(context);
+ throw exception;
}
@JRubyMethod(name = "open", required = 1, optional = 2, frame = true, module = true, visibility = PRIVATE)
diff --git a/src/org/jruby/RubyModule.java b/src/org/jruby/RubyModule.java
index d508014..d7b4df6 100644
--- a/src/org/jruby/RubyModule.java
+++ b/src/org/jruby/RubyModule.java
@@ -37,6 +37,11 @@
***** END LICENSE BLOCK *****/
package org.jruby;
+import clojure.lang.APersistentMap;
+import clojure.lang.IPersistentMap;
+import clojure.lang.LockingTransaction;
+import clojure.lang.PersistentHashMap;
+import clojure.lang.Ref;
import static org.jruby.anno.FrameField.VISIBILITY;
import static org.jruby.runtime.Visibility.MODULE_FUNCTION;
import static org.jruby.runtime.Visibility.PRIVATE;
@@ -55,6 +60,7 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import org.jruby.anno.JRubyClass;
@@ -182,11 +188,15 @@ public boolean isInstance(IRubyObject object) {
}
public Map<String, IRubyObject> getConstantMap() {
- return constants;
+ return (Map<String, IRubyObject>)constants.deref();
+ }
+
+ public IPersistentMap getPersistentConstants() {
+ return (IPersistentMap)constants.deref();
}
public synchronized Map<String, IRubyObject> getConstantMapForWrite() {
- return constants == Collections.EMPTY_MAP ? constants = new ConcurrentHashMap<String, IRubyObject>(4, 0.9f, 1) : constants;
+ return (Map<String, IRubyObject>)constants.deref();
}
public void addIncludingHierarchy(IncludedModuleWrapper hierarchy) {
@@ -207,6 +217,11 @@ protected RubyModule(Ruby runtime, RubyClass metaClass, boolean objectSpace) {
// if (parent == null) parent = runtime.getObject();
setFlag(USER7_F, !isClass());
generation = runtime.getNextModuleGeneration();
+ try {
+ constants = new Ref(PersistentHashMap.EMPTY);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
}
/** used by MODULE_ALLOCATOR and RubyClass constructors
@@ -1069,19 +1084,28 @@ public synchronized void defineAliases(List<String> aliases, String oldName) {
}
invalidateCacheDescendants();
}
-
+
/** this method should be used only by interpreter or compiler
*
*/
public RubyClass defineOrGetClassUnder(String name, RubyClass superClazz) {
+ return defineOrGetClassUnder(name, superClazz, null);
+ }
+
+ public interface ModuleCallback {
+ public void call(RubyModule cls);
+ }
+
+ /** this method should be used only by interpreter or compiler
+ *
+ */
+ public RubyClass defineOrGetClassUnder(final String name, final RubyClass superClazz, final ModuleCallback body) {
// This method is intended only for defining new classes in Ruby code,
// so it uses the allocator of the specified superclass or default to
// the Object allocator. It should NOT be used to define classes that require a native allocator.
-
- Ruby runtime = getRuntime();
+ final Ruby runtime = getRuntime();
IRubyObject classObj = getConstantAt(name);
RubyClass clazz;
-
if (classObj != null) {
if (!(classObj instanceof RubyClass)) throw runtime.newTypeError(name + " is not a class");
clazz = (RubyClass)classObj;
@@ -1095,26 +1119,48 @@ public RubyClass defineOrGetClassUnder(String name, RubyClass superClazz) {
}
if (runtime.getSafeLevel() >= 4) throw runtime.newTypeError("extending class prohibited");
+ return clazz;
} else if (classProviders != null && (clazz = searchProvidersForClass(name, superClazz)) != null) {
// reopen a java class
} else {
- if (superClazz == null) superClazz = runtime.getObject();
- if (superClazz == runtime.getObject() && RubyInstanceConfig.REIFY_RUBY_CLASSES) {
- clazz = RubyClass.newClass(runtime, superClazz, name, REIFYING_OBJECT_ALLOCATOR, this, true);
- } else {
- clazz = RubyClass.newClass(runtime, superClazz, name, superClazz.getAllocator(), this, true);
+ try {
+ clazz = (RubyClass)LockingTransaction.runInTransaction(new Callable() {
+ public Object call() throws Exception {
+ RubyClass superCls = superClazz;
+ RubyClass localClazz;
+ if (superCls == null) superCls = runtime.getObject();
+ if (superCls == runtime.getObject() && RubyInstanceConfig.REIFY_RUBY_CLASSES) {
+ localClazz = RubyClass.newClass(runtime, superCls, name, REIFYING_OBJECT_ALLOCATOR, RubyModule.this, true);
+ } else {
+ localClazz = RubyClass.newClass(runtime, superCls, name, superCls.getAllocator(), RubyModule.this, true);
+ }
+ if (body != null) {
+ body.call(localClazz);
+ }
+ return localClazz;
+ }
+ });
+ } catch (Exception e) {
+ throw new RuntimeException(e);
}
- }
+ }
return clazz;
}
+ /** this method should be used only by interpreter or compiler
+ *
+ */
+ public RubyModule defineOrGetModuleUnder(String name) {
+ return defineOrGetModuleUnder(name, null);
+ }
+
/** this method should be used only by interpreter or compiler
*
*/
- public RubyModule defineOrGetModuleUnder(String name) {
+ public RubyModule defineOrGetModuleUnder(final String name, final ModuleCallback body) {
// This method is intended only for defining new modules in Ruby code
- Ruby runtime = getRuntime();
+ final Ruby runtime = getRuntime();
IRubyObject moduleObj = getConstantAt(name);
RubyModule module;
if (moduleObj != null) {
@@ -1124,7 +1170,19 @@ public RubyModule defineOrGetModuleUnder(String name) {
} else if (classProviders != null && (module = searchProvidersForModule(name)) != null) {
// reopen a java module
} else {
- module = RubyModule.newModule(runtime, name, this, true);
+ try {
+ module = (RubyModule)LockingTransaction.runInTransaction(new Callable() {
+ public Object call() throws Exception {
+ RubyModule module = RubyModule.newModule(runtime, name, RubyModule.this, true);
+ if (body != null) {
+ body.call(module);
+ }
+ return module;
+ }
+ });
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
}
return module;
}
@@ -1447,9 +1505,15 @@ public IRubyObject initialize_copy(IRubyObject original) {
return this;
}
- public void syncConstants(RubyModule other) {
- if (other.getConstantMap() != Collections.EMPTY_MAP) {
- getConstantMapForWrite().putAll(other.getConstantMap());
+ public void syncConstants(final RubyModule other) {
+ try {
+ LockingTransaction.runInTransaction(new Callable() {
+ public Object call() throws Exception {
+ return constants.set(getPersistentConstants().cons(other.getPersistentConstants()));
+ }
+ });
+ } catch (Exception e) {
+ throw new RuntimeException(e);
}
}
@@ -3082,18 +3146,33 @@ protected IRubyObject constantTableFastFetch(String internedName) {
return getConstantMap().get(internedName);
}
- protected IRubyObject constantTableStore(String name, IRubyObject value) {
- getConstantMapForWrite().put(name, value);
+ protected IRubyObject constantTableStore(final String name, final IRubyObject value) {
+ try {
+ LockingTransaction.runInTransaction(new Callable() {
+ public Object call() throws Exception {
+ return constants.set(((IPersistentMap)constants.deref()).assoc(name, value));
+ }
+ });
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
return value;
}
- protected IRubyObject constantTableFastStore(String internedName, IRubyObject value) {
- getConstantMapForWrite().put(internedName, value);
- return value;
+ protected IRubyObject constantTableFastStore(final String internedName, final IRubyObject value) {
+ return constantTableStore(internedName, value);
}
- protected IRubyObject constantTableRemove(String name) {
- return getConstantMapForWrite().remove(name);
+ protected IRubyObject constantTableRemove(final String name) {
+ try {
+ return (IRubyObject)LockingTransaction.runInTransaction(new Callable() {
+ public Object call() throws Exception {
+ return constants.set(((IPersistentMap)constants.deref()).without(name));
+ }
+ });
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
}
private static void define(RubyModule module, JavaMethodDescriptor desc, DynamicMethod dynamicMethod) {
@@ -3182,7 +3261,7 @@ private static void define(RubyModule module, JavaMethodDescriptor desc, Dynamic
// If it is null, then it an anonymous class.
protected String classId;
- private volatile Map<String, IRubyObject> constants = Collections.EMPTY_MAP;
+ private final Ref constants;
private volatile Map<String, DynamicMethod> methods = Collections.EMPTY_MAP;
private Map<String, CacheEntry> cachedMethods = Collections.EMPTY_MAP;
protected int generation;
diff --git a/src/org/jruby/ast/ClassNode.java b/src/org/jruby/ast/ClassNode.java
index 03f2a61..974e8c3 100644
--- a/src/org/jruby/ast/ClassNode.java
+++ b/src/org/jruby/ast/ClassNode.java
@@ -118,7 +118,7 @@ public Node getSuperNode() {
}
@Override
- public IRubyObject interpret(Ruby runtime, ThreadContext context, IRubyObject self, Block aBlock) {
+ public IRubyObject interpret(final Ruby runtime, final ThreadContext context, final IRubyObject self, final Block aBlock) {
RubyModule enclosingClass = cpath.getEnclosingModule(runtime, context, self, aBlock);
// TODO: Figure out how this can happen and possibly remove
@@ -132,14 +132,15 @@ public IRubyObject interpret(Ruby runtime, ThreadContext context, IRubyObject se
superClass = (RubyClass)superObj;
}
- boolean definedAlready = enclosingClass.isConstantDefined(cpath.getName());
-
- RubyClass clazz = enclosingClass.defineOrGetClassUnder(cpath.getName(), superClass);
-
- scope.setModule(clazz);
+ final IRubyObject[] classBodyResult = new IRubyObject[1];
+ RubyClass clazz = enclosingClass.defineOrGetClassUnder(cpath.getName(), superClass, new RubyModule.ModuleCallback() {
+ public void call(RubyModule cls) {
+ scope.setModule(cls);
- IRubyObject classBodyResult = ASTInterpreter.evalClassDefinitionBody(runtime, context, scope, bodyNode, clazz, self, aBlock);
+ classBodyResult[0] = ASTInterpreter.evalClassDefinitionBody(runtime, context, scope, bodyNode, cls, self, aBlock);
+ }
+ });
- return classBodyResult;
+ return classBodyResult[0];
}
}
diff --git a/src/org/jruby/ast/ModuleNode.java b/src/org/jruby/ast/ModuleNode.java
index ec3df28..ca6b613 100644
--- a/src/org/jruby/ast/ModuleNode.java
+++ b/src/org/jruby/ast/ModuleNode.java
@@ -106,17 +106,21 @@ public Colon3Node getCPath() {
}
@Override
- public IRubyObject interpret(Ruby runtime, ThreadContext context, IRubyObject self, Block aBlock) {
+ public IRubyObject interpret(final Ruby runtime, final ThreadContext context, final IRubyObject self, final Block aBlock) {
RubyModule enclosingModule = cpath.getEnclosingModule(runtime, context, self, aBlock);
+ // TODO: Figure out how this can happen and possibly remove
if (enclosingModule == null) throw runtime.newTypeError("no outer class/module");
- String name = cpath.getName();
+ final IRubyObject[] moduleBodyResult = new IRubyObject[1];
+ RubyModule module = enclosingModule.defineOrGetModuleUnder(cpath.getName(), new RubyModule.ModuleCallback() {
+ public void call(RubyModule module) {
+ scope.setModule(module);
- RubyModule module = enclosingModule.defineOrGetModuleUnder(name);
+ moduleBodyResult[0] = ASTInterpreter.evalClassDefinitionBody(runtime, context, scope, bodyNode, module, self, aBlock);
+ }
+ });
- scope.setModule(module);
-
- return ASTInterpreter.evalClassDefinitionBody(runtime, context, scope, bodyNode, module, self, aBlock);
+ return moduleBodyResult[0];
}
}
diff --git a/src/org/jruby/embed/ScriptingContainer.java b/src/org/jruby/embed/ScriptingContainer.java
index d218263..ae90bc1 100644
--- a/src/org/jruby/embed/ScriptingContainer.java
+++ b/src/org/jruby/embed/ScriptingContainer.java
@@ -1463,7 +1463,7 @@ public void setReader(Reader reader) {
RubyIO io = new RubyIO(runtime, istream);
io.getOpenFile().getMainStream().setSync(true);
runtime.defineVariable(new InputGlobalVariable(runtime, "$stdin", io));
- runtime.getObject().getConstantMapForWrite().put("STDIN", io);
+ runtime.getObject().setConstant("STDIN", io);
}
/**
@@ -1521,7 +1521,7 @@ private void setOutputStream(PrintStream pstream) {
RubyIO io = new RubyIO(runtime, pstream);
io.getOpenFile().getMainStream().setSync(true);
runtime.defineVariable(new OutputGlobalVariable(runtime, "$stdout", io));
- runtime.getObject().getConstantMapForWrite().put("STDOUT", io);
+ runtime.getObject().setConstant("STDOUT", io);
runtime.getGlobalVariables().alias("$>", "$stdout");
runtime.getGlobalVariables().alias("$defout", "$stdout");
}
@@ -1586,7 +1586,7 @@ private void setErrorStream(PrintStream error) {
RubyIO io = new RubyIO(runtime, error);
io.getOpenFile().getMainStream().setSync(true);
runtime.defineVariable(new OutputGlobalVariable(runtime, "$stderr", io));
- runtime.getObject().getConstantMapForWrite().put("STDERR", io);
+ runtime.getObject().setConstant("STDERR", io);
runtime.getGlobalVariables().alias("$deferr", "$stderr");
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment