-
-
Notifications
You must be signed in to change notification settings - Fork 920
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Pull all initialize logic up and out into initializer classes.
- Loading branch information
Showing
20 changed files
with
1,300 additions
and
1,112 deletions.
There are no files selected for viewing
1,136 changes: 25 additions & 1,111 deletions
1,136
core/src/main/java/org/jruby/javasupport/JavaClass.java
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
15 changes: 15 additions & 0 deletions
15
core/src/main/java/org/jruby/javasupport/binding/AssignedName.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package org.jruby.javasupport.binding; | ||
|
||
/** | ||
* Created by headius on 2/26/15. | ||
*/ | ||
public class AssignedName { | ||
String name; | ||
Priority type; | ||
|
||
public AssignedName() {} | ||
public AssignedName(String name, Priority type) { | ||
this.name = name; | ||
this.type = type; | ||
} | ||
} |
235 changes: 235 additions & 0 deletions
235
core/src/main/java/org/jruby/javasupport/binding/ClassInitializer.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,235 @@ | ||
package org.jruby.javasupport.binding; | ||
|
||
import org.jruby.Ruby; | ||
import org.jruby.RubyClass; | ||
import org.jruby.RubyModule; | ||
import org.jruby.javasupport.Java; | ||
import org.jruby.javasupport.JavaClass; | ||
|
||
import java.lang.reflect.Constructor; | ||
import java.lang.reflect.Field; | ||
import java.lang.reflect.Method; | ||
import java.lang.reflect.Modifier; | ||
import java.util.Collections; | ||
import java.util.Map; | ||
|
||
/** | ||
* Created by headius on 2/26/15. | ||
*/ | ||
public class ClassInitializer extends Initializer { | ||
public ClassInitializer(Ruby runtime, Class<?> javaClass) { | ||
super(runtime, javaClass); | ||
} | ||
|
||
public void initialize(JavaClass javaClassObject, RubyModule proxy) { | ||
RubyClass proxyClass = (RubyClass)proxy; | ||
Class<?> superclass = javaClass.getSuperclass(); | ||
|
||
final State state = new State(runtime, superclass); | ||
|
||
super.initializeBase(proxy); | ||
|
||
if ( javaClass.isArray() || javaClass.isPrimitive() ) { | ||
// see note below re: 2-field kludge | ||
javaClassObject.setProxyClass(proxyClass); | ||
javaClassObject.setProxyModule(proxy); | ||
return; | ||
} | ||
|
||
setupClassFields(javaClass, state); | ||
setupClassMethods(javaClass, state); | ||
setupClassConstructors(javaClass, state); | ||
|
||
javaClassObject.staticAssignedNames = Collections.unmodifiableMap(state.staticNames); | ||
javaClassObject.instanceAssignedNames = Collections.unmodifiableMap(state.instanceNames); | ||
|
||
proxyClass.setReifiedClass(javaClass); | ||
|
||
assert javaClassObject.proxyClass == null; | ||
javaClassObject.unfinishedProxyClass = proxyClass; | ||
|
||
// flag the class as a Java class proxy. | ||
proxy.setJavaProxy(true); | ||
proxy.getSingletonClass().setJavaProxy(true); | ||
|
||
// set parent to either package module or outer class | ||
final RubyModule parent; | ||
final Class<?> enclosingClass = javaClass.getEnclosingClass(); | ||
if ( enclosingClass != null ) { | ||
parent = Java.getProxyClass(runtime, enclosingClass); | ||
} else { | ||
parent = Java.getJavaPackageModule(runtime, javaClass.getPackage()); | ||
} | ||
proxy.setParent(parent); | ||
|
||
// set the Java class name and package | ||
if ( javaClass.isAnonymousClass() ) { | ||
String baseName = ""; // javaClass.getSimpleName() returns "" for anonymous | ||
if ( enclosingClass != null ) { | ||
// instead of an empty name anonymous classes will have a "conforming" | ||
// although not valid (by Ruby semantics) RubyClass name e.g. : | ||
// 'Java::JavaUtilConcurrent::TimeUnit::1' for $1 anonymous enum class | ||
// NOTE: if this turns out suitable shall do the same for method etc. | ||
final String className = javaClass.getName(); | ||
final int length = className.length(); | ||
final int offset = enclosingClass.getName().length(); | ||
if ( length > offset && className.charAt(offset) != '$' ) { | ||
baseName = className.substring( offset ); | ||
} | ||
else if ( length > offset + 1 ) { // skip '$' | ||
baseName = className.substring( offset + 1 ); | ||
} | ||
} | ||
proxy.setBaseName( baseName ); | ||
} | ||
else { | ||
proxy.setBaseName( javaClass.getSimpleName() ); | ||
} | ||
|
||
installClassFields(proxyClass, state); | ||
installClassInstanceMethods(proxyClass, state); | ||
installClassConstructors(proxyClass, state); | ||
installClassClasses(javaClass, proxyClass); | ||
|
||
// FIXME: bit of a kludge here (non-interface classes assigned to both | ||
// class and module fields). simplifies proxy extender code, will go away | ||
// when JI is overhauled (and proxy extenders are deprecated). | ||
javaClassObject.setProxyClass(proxyClass); | ||
javaClassObject.setProxyModule(proxy); | ||
|
||
javaClassObject.applyProxyExtenders(); | ||
|
||
// TODO: we can probably release our references to the constantFields | ||
// array and static/instance callback hashes at this point. | ||
} | ||
|
||
private static void installClassInstanceMethods(final RubyClass proxy, final Initializer.State state) { | ||
installClassStaticMethods(proxy, state); | ||
//assert state.instanceInstallers != null; | ||
for ( Map.Entry<String, NamedInstaller> entry : state.instanceInstallers.entrySet() ) { | ||
entry.getValue().install(proxy); | ||
} | ||
} | ||
|
||
private static void setupClassFields(Class<?> javaClass, Initializer.State state) { | ||
Field[] fields = JavaClass.getFields(javaClass); | ||
|
||
for (int i = fields.length; --i >= 0;) { | ||
Field field = fields[i]; | ||
if (javaClass != field.getDeclaringClass()) continue; | ||
|
||
if (ConstantField.isConstant(field)) { | ||
state.constantFields.add(new ConstantField(field)); | ||
continue; | ||
} | ||
|
||
int modifiers = field.getModifiers(); | ||
if (Modifier.isStatic(modifiers)) { | ||
addField(state.staticInstallers, state.staticNames, field, Modifier.isFinal(modifiers), true); | ||
} else { | ||
addField(state.instanceInstallers, state.instanceNames, field, Modifier.isFinal(modifiers), false); | ||
} | ||
} | ||
} | ||
|
||
private void setupClassMethods(Class<?> javaClass, State state) { | ||
// TODO: protected methods. this is going to require a rework of some of the mechanism. | ||
Method[] methods = getMethods(javaClass); | ||
|
||
for (int i = methods.length; --i >= 0;) { | ||
// we need to collect all methods, though we'll only | ||
// install the ones that are named in this class | ||
Method method = methods[i]; | ||
String name = method.getName(); | ||
|
||
if (Modifier.isStatic(method.getModifiers())) { | ||
prepareStaticMethod(javaClass, state, method, name); | ||
} else { | ||
prepareInstanceMethod(javaClass, state, method, name); | ||
} | ||
} | ||
|
||
// try to wire up Scala singleton logic if present | ||
handleScalaSingletons(javaClass, state); | ||
|
||
// now iterate over all installers and make sure they also have appropriate aliases | ||
assignStaticAliases(state); | ||
assignInstanceAliases(state); | ||
} | ||
|
||
private void setupClassConstructors(final Class<?> javaClass, final State state) { | ||
// TODO: protected methods. this is going to require a rework | ||
// of some of the mechanism. | ||
final Constructor[] constructors = JavaClass.getConstructors(javaClass); | ||
|
||
// create constructorInstaller; if there are no constructors, it will disable construction | ||
ConstructorInvokerInstaller constructorInstaller = new ConstructorInvokerInstaller("__jcreate!"); | ||
|
||
for ( int i = constructors.length; --i >= 0; ) { | ||
// we need to collect all methods, though we'll only | ||
// install the ones that are named in this class | ||
constructorInstaller.addConstructor(constructors[i], javaClass); | ||
} | ||
|
||
state.constructorInstaller = constructorInstaller; | ||
} | ||
|
||
private void prepareInstanceMethod(Class<?> javaClass, State state, Method method, String name) { | ||
AssignedName assignedName = state.instanceNames.get(name); | ||
|
||
// For JRUBY-4505, restore __method methods for reserved names | ||
if (INSTANCE_RESERVED_NAMES.containsKey(method.getName())) { | ||
setupInstanceMethods(state.instanceInstallers, javaClass, method, name + METHOD_MANGLE); | ||
return; | ||
} | ||
|
||
if (assignedName == null) { | ||
state.instanceNames.put(name, new AssignedName(name, Priority.METHOD)); | ||
} else { | ||
if (Priority.METHOD.lessImportantThan(assignedName)) return; | ||
if (!Priority.METHOD.asImportantAs(assignedName)) { | ||
state.instanceInstallers.remove(name); | ||
state.instanceInstallers.remove(name + '='); | ||
state.instanceNames.put(name, new AssignedName(name, Priority.METHOD)); | ||
} | ||
} | ||
setupInstanceMethods(state.instanceInstallers, javaClass, method, name); | ||
} | ||
|
||
private static void setupInstanceMethods(Map<String, NamedInstaller> methodCallbacks, Class<?> javaClass, Method method, String name) { | ||
MethodInstaller invoker = (MethodInstaller) methodCallbacks.get(name); | ||
if (invoker == null) { | ||
invoker = new InstanceMethodInvokerInstaller(name); | ||
methodCallbacks.put(name, invoker); | ||
} | ||
invoker.addMethod(method, javaClass); | ||
} | ||
|
||
private static void assignInstanceAliases(State state) { | ||
for (Map.Entry<String, NamedInstaller> entry : state.instanceInstallers.entrySet()) { | ||
if (entry.getValue().type == NamedInstaller.INSTANCE_METHOD) { | ||
MethodInstaller methodInstaller = (MethodInstaller)entry.getValue(); | ||
|
||
// no aliases for __method methods | ||
if (entry.getKey().endsWith("__method")) continue; | ||
|
||
if (methodInstaller.hasLocalMethod()) { | ||
assignAliases(methodInstaller, state.instanceNames); | ||
} | ||
|
||
// JRUBY-6967: Types with java.lang.Comparable were using Ruby Comparable#== instead of dispatching directly to | ||
// java.lang.Object.equals. We force an alias here to ensure we always use equals. | ||
if (entry.getKey().equals("equals")) { | ||
// we only install "local" methods, but need to force this so it will bind | ||
methodInstaller.setLocalMethod(true); | ||
methodInstaller.addAlias("=="); | ||
} | ||
} | ||
} | ||
} | ||
|
||
private static void installClassConstructors(final RubyModule proxy, final State state) { | ||
if ( state.constructorInstaller != null ) state.constructorInstaller.install(proxy); | ||
} | ||
|
||
} |
38 changes: 38 additions & 0 deletions
38
core/src/main/java/org/jruby/javasupport/binding/ConstantField.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
package org.jruby.javasupport.binding; | ||
|
||
import org.jruby.RubyModule; | ||
import org.jruby.javasupport.JavaUtil; | ||
|
||
import java.lang.reflect.Field; | ||
import java.lang.reflect.Modifier; | ||
|
||
/** | ||
* Created by headius on 2/26/15. | ||
*/ | ||
public class ConstantField { | ||
|
||
final Field field; | ||
|
||
public ConstantField(Field field) { this.field = field; } | ||
|
||
void install(final RubyModule proxy) { | ||
final String name = field.getName(); | ||
if ( proxy.getConstantAt(name) == null ) { | ||
try { | ||
final Object value = field.get(null); | ||
proxy.setConstant(name, JavaUtil.convertJavaToUsableRubyObject(proxy.getRuntime(), value)); | ||
} | ||
catch (IllegalAccessException iae) { | ||
// if we can't read it, we don't set it | ||
} | ||
} | ||
} | ||
|
||
private static final int CONSTANT = Modifier.FINAL | Modifier.PUBLIC | Modifier.STATIC; | ||
|
||
static boolean isConstant(final Field field) { | ||
return (field.getModifiers() & CONSTANT) == CONSTANT && | ||
Character.isUpperCase( field.getName().charAt(0) ); | ||
} | ||
|
||
} |
Oops, something went wrong.