Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: jruby/jruby
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: b596b5ae4595
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 0d1c338bdcce
Choose a head ref
  • 5 commits
  • 8 files changed
  • 1 contributor

Commits on Feb 27, 2015

  1. Copy the full SHA
    026d1d9 View commit details
  2. Copy the full SHA
    ac6a9de View commit details
  3. Copy the full SHA
    317e9ba View commit details
  4. Copy the full SHA
    a3d28b8 View commit details
  5. test asserting that concurrent proxy initialization does dispatch met…

    …hods correctly
    
    closing #2014
    kares committed Feb 27, 2015
    Copy the full SHA
    0d1c338 View commit details
9 changes: 5 additions & 4 deletions core/src/main/java/org/jruby/RubyModule.java
Original file line number Diff line number Diff line change
@@ -438,24 +438,25 @@ public String getBaseName() {
*/
public void setBaseName(String name) {
baseName = name;
cachedName = null;
}

/**
* Generate a fully-qualified class name or a #-style name for anonymous and singleton classes.
*
*
* Ruby C equivalent = "classname"
*
*
* @return The generated class name
*/
public String getName() {
if (cachedName != null) return cachedName;
return calculateName();
}

/**
* Get the "simple" name for the class, which is either the "base" name or
* the "anonymous" class name.
*
*
* @return the "simple" name of the class
*/
public String getSimpleName() {
2 changes: 0 additions & 2 deletions core/src/main/java/org/jruby/javasupport/JavaClass.java
Original file line number Diff line number Diff line change
@@ -63,9 +63,7 @@
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@JRubyClass(name="Java::JavaClass", parent="Java::JavaObject")
public class JavaClass extends JavaObject {
60 changes: 35 additions & 25 deletions core/src/main/java/org/jruby/javasupport/JavaMethod.java
Original file line number Diff line number Diff line change
@@ -21,7 +21,7 @@
* Copyright (C) 2004 David Corbin <dcorbin@users.sourceforge.net>
* Copyright (C) 2005 Charles O Nutter <headius@headius.com>
* Copyright (C) 2006 Kresten Krab Thorup <krab@gnu.org>
*
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
@@ -68,8 +68,9 @@ public class JavaMethod extends JavaCallable {

private final static boolean USE_HANDLES = RubyInstanceConfig.USE_GENERATED_HANDLES;
private final static boolean HANDLE_DEBUG = false;

private final Method method;
private final Class boxedReturnType;
private final Class<?> boxedReturnType;
private final boolean isFinal;
private final JavaUtil.JavaConverter returnConverter;

@@ -80,12 +81,12 @@ public Object getValue() {
public static RubyClass createJavaMethodClass(Ruby runtime, RubyModule javaModule) {
// TODO: NOT_ALLOCATABLE_ALLOCATOR is probably ok here, since we don't intend for people to monkey with
// this type and it can't be marshalled. Confirm. JRUBY-415
RubyClass result =
RubyClass result =
javaModule.defineClassUnder("JavaMethod", runtime.getObject(), ObjectAllocator.NOT_ALLOCATABLE_ALLOCATOR);

JavaAccessibleObject.registerRubyMethods(runtime, result);
JavaCallable.registerRubyMethods(runtime, result);

result.defineAnnotatedMethods(JavaMethod.class);

return result;
@@ -95,33 +96,31 @@ public JavaMethod(Ruby runtime, Method method) {
super(runtime, runtime.getJavaSupport().getJavaMethodClass(), method.getParameterTypes());
this.method = method;
this.isFinal = Modifier.isFinal(method.getModifiers());
if (method.getReturnType().isPrimitive() && method.getReturnType() != void.class) {
this.boxedReturnType = CodegenUtils.getBoxType(method.getReturnType());
final Class<?> returnType = method.getReturnType();
if (returnType.isPrimitive() && returnType != void.class) {
this.boxedReturnType = CodegenUtils.getBoxType(returnType);
} else {
this.boxedReturnType = method.getReturnType();
this.boxedReturnType = returnType;
}

boolean methodIsPublic = Modifier.isPublic(method.getModifiers());
boolean classIsPublic = Modifier.isPublic(method.getDeclaringClass().getModifiers());

// Special classes like Collections.EMPTY_LIST are inner classes that are private but
// implement public interfaces. Their methods are all public methods for the public
// Special classes like Collections.EMPTY_LIST are inner classes that are private but
// implement public interfaces. Their methods are all public methods for the public
// interface. Let these public methods execute via setAccessible(true).
if (JavaUtil.CAN_SET_ACCESSIBLE) {
// we should be able to setAccessible ok...
try {
if (methodIsPublic &&
!Modifier.isPublic(method.getDeclaringClass().getModifiers())) {
accessibleObject().setAccessible(true);
if ( Modifier.isPublic(method.getModifiers()) &&
! Modifier.isPublic(method.getDeclaringClass().getModifiers()) ) {
method.setAccessible(true);
}
} catch (SecurityException se) {
// we shouldn't get here if JavaClass.CAN_SET_ACCESSIBLE is doing
// what it should, so we warn.
runtime.getWarnings().warn("failed to setAccessible: " + accessibleObject() + ", exception follows: " + se.getMessage());
runtime.getWarnings().warn("failed to setAccessible: " + method + ", exception follows: " + se.getMessage());
}
}
returnConverter = JavaUtil.getJavaConverter(method.getReturnType());

returnConverter = JavaUtil.getJavaConverter(returnType);
}

public static JavaMethod create(Ruby runtime, Method method) {
@@ -158,12 +157,12 @@ public static JavaMethod getMatchingDeclaredMethod(Ruby runtime, Class<?> javaCl
MethodSearch: for (Method method : javaClass.getDeclaredMethods()) {
if (method.getName().equals(methodName)) {
Class<?>[] targetTypes = method.getParameterTypes();

// for zero args case we can stop searching
if (targetTypes.length == 0 && argumentTypes.length == 0) {
return create(runtime, method);
}

TypeScan: for (int i = 0; i < argumentTypes.length; i++) {
if (i >= targetTypes.length) continue MethodSearch;

@@ -183,13 +182,13 @@ public static JavaMethod getMatchingDeclaredMethod(Ruby runtime, Class<?> javaCl
// no matching method found
return null;
}

@Override
public boolean equals(Object other) {
return other instanceof JavaMethod &&
this.method == ((JavaMethod)other).method;
}

@Override
public int hashCode() {
return method.hashCode();
@@ -201,6 +200,7 @@ public RubyString name() {
return getRuntime().newString(method.getName());
}

@Override
public int getArity() {
return parameterTypes.length;
}
@@ -269,7 +269,7 @@ public IRubyObject invoke_static(IRubyObject[] args) {
@JRubyMethod
public IRubyObject return_type() {
Class<?> klass = method.getReturnType();

if (klass.equals(void.class)) {
return getRuntime().getNil();
}
@@ -544,30 +544,37 @@ private void convertArguments(IRubyObject[] argsIn, Object[] argsOut, int from)
}
}

@Override
public Class<?>[] getParameterTypes() {
return parameterTypes;
}

@Override
public Class<?>[] getExceptionTypes() {
return method.getExceptionTypes();
}

@Override
public Type[] getGenericParameterTypes() {
return method.getGenericParameterTypes();
}

@Override
public Type[] getGenericExceptionTypes() {
return method.getGenericExceptionTypes();
}


@Override
public Annotation[][] getParameterAnnotations() {
return method.getParameterAnnotations();
}

@Override
public boolean isVarArgs() {
return method.isVarArgs();
}

@Override
protected String nameOnInspection() {
return "#<" + getType().toString() + "/" + method.getName() + "(";
}
@@ -576,7 +583,7 @@ protected String nameOnInspection() {
public RubyBoolean static_p() {
return getRuntime().newBoolean(isStatic());
}

public RubyBoolean bridge_p() {
return getRuntime().newBoolean(method.isBridge());
}
@@ -585,14 +592,17 @@ private boolean isStatic() {
return Modifier.isStatic(method.getModifiers());
}

@Override
public int getModifiers() {
return method.getModifiers();
}

@Override
public String toGenericString() {
return method.toGenericString();
}

@Override
public AccessibleObject accessibleObject() {
return method;
}
26 changes: 15 additions & 11 deletions core/src/main/java/org/jruby/javasupport/JavaSupport.java
Original file line number Diff line number Diff line change
@@ -78,14 +78,14 @@ public IRubyObject allocateProxy(Object javaObject, RubyClass clazz) {
private static final Constructor<? extends ClassValue> CLASS_VALUE_CONSTRUCTOR;

static {
Constructor<? extends ClassValue> constructor = null;
Constructor constructor = null;

if (Options.INVOKEDYNAMIC_CLASS_VALUES.load()) {
try {
// try to load the ClassValue class. If it succeeds, we can use our
// ClassValue-based cache.
Class.forName("java.lang.ClassValue");
constructor = (Constructor<ClassValue>)Class.forName("org.jruby.util.collections.Java7ClassValue").getConstructor(ClassValueCalculator.class);
constructor = Class.forName("org.jruby.util.collections.Java7ClassValue").getConstructor(ClassValueCalculator.class);
}
catch (Exception ex) {
// fall through to Map version
@@ -95,12 +95,14 @@ public IRubyObject allocateProxy(Object javaObject, RubyClass clazz) {
if (constructor == null) {
try {
constructor = MapBasedClassValue.class.getConstructor(ClassValueCalculator.class);
} catch (Exception ex2) {
throw new RuntimeException(ex2);
}
catch (Exception ex) {
if ( ex instanceof RuntimeException ) throw (RuntimeException) ex;
throw new RuntimeException(ex);
}
}

CLASS_VALUE_CONSTRUCTOR = constructor;
CLASS_VALUE_CONSTRUCTOR = (Constructor<ClassValue>) constructor;
}

private RubyModule javaModule;
@@ -217,15 +219,17 @@ public RubyModule getProxyClassFromCache(Class clazz) {
}

public void handleNativeException(Throwable exception, Member target) {
if (exception instanceof RaiseException) {
if ( exception instanceof RaiseException ) {
// allow RaiseExceptions to propagate
throw (RaiseException) exception;
} else if (exception instanceof Unrescuable) {
}
if (exception instanceof Unrescuable) {
// allow "unrescuable" flow-control exceptions to propagate
if (exception instanceof Error) {
throw (Error)exception;
} else if (exception instanceof RuntimeException) {
throw (RuntimeException)exception;
if ( exception instanceof Error ) {
throw (Error) exception;
}
if ( exception instanceof RuntimeException ) {
throw (RuntimeException) exception;
}
}
throw createRaiseException(exception, target);
Original file line number Diff line number Diff line change
@@ -86,6 +86,8 @@ else if ( length > offset + 1 ) { // skip '$'
installClassConstructors(proxyClass, state);
installClassClasses(javaClass, proxyClass);

proxy.getName(); // trigger calculateName()

return proxy;
}

Original file line number Diff line number Diff line change
@@ -65,6 +65,8 @@ public RubyModule initialize(RubyModule proxy) {
installClassStaticMethods(proxy, state);
installClassClasses(javaClass, proxy);

proxy.getName(); // trigger calculateName()

return proxy;
}

8 changes: 8 additions & 0 deletions core/src/main/java/org/jruby/runtime/callsite/CacheEntry.java
Original file line number Diff line number Diff line change
@@ -21,4 +21,12 @@ public final boolean typeOk(RubyClass incomingType) {
public static final boolean typeOk(CacheEntry entry, RubyClass incomingType) {
return entry.token == incomingType.getGeneration();
}

@Override
public String toString() {
return getClass().getName() + '@' +
Integer.toHexString(System.identityHashCode(this)) +
"<method=" + method + ", token=" + token + ">";
}

}
24 changes: 23 additions & 1 deletion test/test_higher_javasupport.rb
Original file line number Diff line number Diff line change
@@ -861,7 +861,7 @@ def test_no_warnings_on_concurrent_package_const_initialization

def test_no_warnings_on_concurrent_class_const_initialization
Java.send :remove_const, :DefaultPackageClass if Java.const_defined? :DefaultPackageClass

stderr = $stderr; require 'stringio'
begin
$stderr = StringIO.new
@@ -879,4 +879,26 @@ def test_no_warnings_on_concurrent_class_const_initialization
end
end

# reproducing https://github.com/jruby/jruby/issues/2014
def test_concurrent_proxy_class_initialization
abort_on_exception = Thread.abort_on_exception
begin
Thread.abort_on_exception = true
# some strange enough (un-initialized proxy) classes not used elsewhere
threads = (0..10).map do
Thread.new { Java::java.awt.Desktop::Action.valueOf('OPEN') }
end
threads.each { |thread| thread.join }

# same but top (package) level class and using an aliased method :
threads = (0..10).map do
Thread.new { java.lang.management.MemoryType.value_of('HEAP') }
end
threads.each { |thread| thread.join }
ensure
Thread.abort_on_exception = abort_on_exception
end

end

end