-
-
Notifications
You must be signed in to change notification settings - Fork 925
Commit
Conflicts: truffle/src/main/java/org/jruby/truffle/nodes/core/PrimitiveNodes.java truffle/src/main/java/org/jruby/truffle/nodes/core/StringNodes.java truffle/src/main/java/org/jruby/truffle/nodes/core/SymbolNodes.java truffle/src/main/java/org/jruby/truffle/nodes/core/TrufflePrimitiveNodes.java truffle/src/main/java/org/jruby/truffle/runtime/RubyContext.java truffle/src/main/java/org/jruby/truffle/runtime/core/RubyHash.java truffle/src/main/java/org/jruby/truffle/runtime/core/RubyString.java
- 9.4.12.0
- 9.4.11.0
- 9.4.10.0
- 9.4.9.0
- 9.4.8.0
- 9.4.7.0
- 9.4.6.0
- 9.4.5.0
- 9.4.4.0
- 9.4.3.0
- 9.4.2.0
- 9.4.1.0
- 9.4.0.0
- 9.3.15.0
- 9.3.14.0
- 9.3.13.0
- 9.3.12.0
- 9.3.11.0
- 9.3.10.0
- 9.3.9.0
- 9.3.8.0
- 9.3.7.0
- 9.3.6.0
- 9.3.5.0
- 9.3.4.0
- 9.3.3.0
- 9.3.2.0
- 9.3.1.0
- 9.3.0.0
- 9.2.21.0
- 9.2.20.1
- 9.2.20.0
- 9.2.19.0
- 9.2.18.0
- 9.2.17.0
- 9.2.16.0
- 9.2.15.0
- 9.2.14.0
- 9.2.13.0
- 9.2.12.0
- 9.2.11.1
- 9.2.11.0
- 9.2.10.0
- 9.2.9.0
- 9.2.8.0
- 9.2.7.0
- 9.2.6.0
- 9.2.5.0
- 9.2.4.1
- 9.2.4.0
- 9.2.3.0
- 9.2.2.0
- 9.2.1.0
- 9.2.0.0
- 9.1.17.0
- 9.1.16.0
- 9.1.15.0
- 9.1.14.0
- 9.1.13.0
- 9.1.12.0
- 9.1.11.0
- 9.1.10.0
- 9.1.9.0
- 9.1.8.0
- 9.1.7.0
- 9.1.6.0
- 9.1.5.0
- 9.1.4.0
- 9.1.3.0
- 9.1.2.0
- 9.1.1.0
- 9.1.0.0
- 9.0.5.0
- 9.0.4.0
- 9.0.3.0
- 9.0.1.0
- 9.0.0.0
- 9.0.0.0.rc2
- 9.0.0.0.rc1
There are no files selected for viewing
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
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; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,221 @@ | ||
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.Map; | ||
|
||
/** | ||
* Created by headius on 2/26/15. | ||
*/ | ||
public class ClassInitializer extends Initializer { | ||
public ClassInitializer(Ruby runtime, Class<?> javaClass) { | ||
super(runtime, javaClass); | ||
} | ||
|
||
public RubyModule initialize(RubyModule proxy) { | ||
RubyClass proxyClass = (RubyClass)proxy; | ||
Class<?> superclass = javaClass.getSuperclass(); | ||
|
||
final State state = new State(runtime, superclass); | ||
|
||
super.initializeBase(proxy); | ||
|
||
proxyClass.setReifiedClass(javaClass); | ||
|
||
runtime.getJavaSupport().getUnfinishedProxyClassCache().get(javaClass).set(proxyClass); | ||
|
||
if ( javaClass.isArray() || javaClass.isPrimitive() ) { | ||
return proxy; | ||
} | ||
|
||
setupClassFields(javaClass, state); | ||
setupClassMethods(javaClass, state); | ||
setupClassConstructors(javaClass, state); | ||
|
||
runtime.getJavaSupport().getStaticAssignedNames().get(javaClass).putAll(state.staticNames); | ||
runtime.getJavaSupport().getInstanceAssignedNames().get(javaClass).putAll(state.instanceNames); | ||
|
||
// 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); | ||
|
||
return proxy; | ||
} | ||
|
||
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); | ||
} | ||
|
||
} |
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) ); | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package org.jruby.javasupport.binding; | ||
|
||
import org.jruby.Ruby; | ||
import org.jruby.RubyModule; | ||
import org.jruby.java.invokers.ConstructorInvoker; | ||
import org.jruby.runtime.Block; | ||
import org.jruby.runtime.ThreadContext; | ||
import org.jruby.runtime.builtin.IRubyObject; | ||
|
||
import java.lang.reflect.Constructor; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
import static org.jruby.runtime.Visibility.PUBLIC; | ||
|
||
/** | ||
* Created by headius on 2/26/15. | ||
*/ | ||
public class ConstructorInvokerInstaller extends MethodInstaller { | ||
|
||
protected final List<Constructor> constructors = new ArrayList<Constructor>(4); | ||
private boolean localConstructor; | ||
|
||
public ConstructorInvokerInstaller(String name) { super(name, STATIC_METHOD); } | ||
|
||
// called only by initializing thread; no synchronization required | ||
void addConstructor(final Constructor ctor, final Class<?> clazz) { | ||
if ( ! Ruby.isSecurityRestricted() ) { | ||
try { | ||
ctor.setAccessible(true); | ||
} catch(SecurityException e) {} | ||
} | ||
this.constructors.add(ctor); | ||
localConstructor |= clazz == ctor.getDeclaringClass(); | ||
} | ||
|
||
@Override void install(final RubyModule proxy) { | ||
if ( localConstructor ) { | ||
proxy.addMethod(name, new ConstructorInvoker(proxy, constructors)); | ||
} | ||
else { // if there's no constructor, we must prevent construction | ||
proxy.addMethod(name, new org.jruby.internal.runtime.methods.JavaMethod(proxy, PUBLIC) { | ||
@Override | ||
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject[] args, Block block) { | ||
throw context.runtime.newTypeError("no public constructors for " + clazz); | ||
} | ||
}); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package org.jruby.javasupport.binding; | ||
|
||
import java.lang.reflect.Field; | ||
|
||
/** | ||
* Created by headius on 2/26/15. | ||
*/ | ||
public abstract class FieldInstaller extends NamedInstaller { | ||
|
||
final Field field; | ||
|
||
public FieldInstaller(String name, int type, Field field) { | ||
super(name,type); | ||
this.field = field; | ||
} | ||
} |
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package org.jruby.javasupport.binding; | ||
|
||
import org.jruby.RubyModule; | ||
import org.jruby.java.invokers.InstanceFieldGetter; | ||
|
||
import java.lang.reflect.Field; | ||
import java.lang.reflect.Modifier; | ||
|
||
/** | ||
* Created by headius on 2/26/15. | ||
*/ | ||
public class InstanceFieldGetterInstaller extends FieldInstaller { | ||
|
||
public InstanceFieldGetterInstaller(String name, Field field) { | ||
super(name, INSTANCE_FIELD, field); | ||
} | ||
|
||
@Override void install(final RubyModule proxy) { | ||
if ( Modifier.isPublic(field.getModifiers()) ) { | ||
proxy.addMethod(name, new InstanceFieldGetter(name, proxy, field)); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package org.jruby.javasupport.binding; | ||
|
||
import org.jruby.RubyModule; | ||
import org.jruby.java.invokers.InstanceFieldSetter; | ||
|
||
import java.lang.reflect.Field; | ||
import java.lang.reflect.Modifier; | ||
|
||
/** | ||
* Created by headius on 2/26/15. | ||
*/ | ||
public class InstanceFieldSetterInstaller extends FieldInstaller { | ||
|
||
public InstanceFieldSetterInstaller(String name, Field field) { | ||
super(name, INSTANCE_FIELD, field); | ||
} | ||
|
||
@Override void install(final RubyModule proxy) { | ||
if ( Modifier.isPublic(field.getModifiers()) ) { | ||
proxy.addMethod(name, new InstanceFieldSetter(name, proxy, field)); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package org.jruby.javasupport.binding; | ||
|
||
import org.jruby.RubyModule; | ||
import org.jruby.java.invokers.InstanceMethodInvoker; | ||
|
||
/** | ||
* Created by headius on 2/26/15. | ||
*/ | ||
public class InstanceMethodInvokerInstaller extends MethodInstaller { | ||
|
||
public InstanceMethodInvokerInstaller(String name) { super(name, INSTANCE_METHOD); } | ||
|
||
@Override void install(final RubyModule proxy) { | ||
if ( hasLocalMethod() ) { | ||
proxy.addMethod(name, new InstanceMethodInvoker(proxy, methods)); | ||
if ( aliases != null && isPublic() ) { | ||
proxy.defineAliases(aliases, this.name); | ||
//aliases = null; | ||
} | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
package org.jruby.javasupport.binding; | ||
|
||
import org.jruby.Ruby; | ||
import org.jruby.RubyModule; | ||
import org.jruby.javasupport.JavaClass; | ||
|
||
import java.lang.reflect.Field; | ||
import java.lang.reflect.Method; | ||
import java.lang.reflect.Modifier; | ||
import java.util.Map; | ||
|
||
/** | ||
* Created by headius on 2/26/15. | ||
*/ | ||
public class InterfaceInitializer extends Initializer { | ||
|
||
public InterfaceInitializer(Ruby runtime, Class<?> javaClass) { | ||
super(runtime, javaClass); | ||
} | ||
|
||
public RubyModule initialize(RubyModule proxy) { | ||
final State state = new State(runtime, null); | ||
|
||
super.initializeBase(proxy); | ||
|
||
runtime.getJavaSupport().getUnfinishedProxyClassCache().get(javaClass).set(proxy); | ||
|
||
Field[] fields = JavaClass.getDeclaredFields(javaClass); | ||
|
||
for (int i = fields.length; --i >= 0; ) { | ||
final Field field = fields[i]; | ||
if ( javaClass != field.getDeclaringClass() ) continue; | ||
|
||
if ( ConstantField.isConstant(field) ) { | ||
state.constantFields.add(new ConstantField(field)); | ||
} | ||
|
||
final int mod = field.getModifiers(); | ||
if ( Modifier.isStatic(mod) ) { | ||
addField(state.staticInstallers, state.staticNames, field, Modifier.isFinal(mod), true); | ||
} | ||
} | ||
|
||
setupInterfaceMethods(javaClass, state); | ||
|
||
// Add in any Scala singleton methods | ||
handleScalaSingletons(javaClass, state); | ||
|
||
// Now add all aliases for the static methods (fields) as appropriate | ||
for (Map.Entry<String, NamedInstaller> entry : state.staticInstallers.entrySet()) { | ||
final NamedInstaller installer = entry.getValue(); | ||
if (installer.type == NamedInstaller.STATIC_METHOD && installer.hasLocalMethod()) { | ||
assignAliases((MethodInstaller) installer, state.staticNames); | ||
} | ||
} | ||
|
||
runtime.getJavaSupport().getStaticAssignedNames().get(javaClass).putAll(state.staticNames); | ||
runtime.getJavaSupport().getInstanceAssignedNames().get(javaClass).clear(); | ||
|
||
// flag the class as a Java class proxy. | ||
proxy.setJavaProxy(true); | ||
proxy.getSingletonClass().setJavaProxy(true); | ||
|
||
installClassFields(proxy, state); | ||
installClassStaticMethods(proxy, state); | ||
installClassClasses(javaClass, proxy); | ||
|
||
return proxy; | ||
} | ||
|
||
private static void setupInterfaceMethods(Class<?> javaClass, Initializer.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;) { | ||
// Java 8 introduced static methods on interfaces, so we just look for those | ||
Method method = methods[i]; | ||
String name = method.getName(); | ||
|
||
if (!Modifier.isStatic(method.getModifiers())) continue; | ||
|
||
prepareStaticMethod(javaClass, state, method, name); | ||
} | ||
|
||
// now iterate over all installers and make sure they also have appropriate aliases | ||
assignStaticAliases(state); | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
package org.jruby.javasupport.binding; | ||
|
||
import java.lang.reflect.Method; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
/** | ||
* Created by headius on 2/26/15. | ||
*/ | ||
public abstract class MethodInstaller extends NamedInstaller { | ||
|
||
protected final List<Method> methods = new ArrayList<Method>(4); | ||
protected List<String> aliases; | ||
private boolean localMethod; | ||
|
||
public MethodInstaller(String name, int type) { super(name, type); } | ||
|
||
// called only by initializing thread; no synchronization required | ||
void addMethod(final Method method, final Class<?> clazz) { | ||
this.methods.add(method); | ||
localMethod |= | ||
clazz == method.getDeclaringClass() || | ||
method.getDeclaringClass().isInterface(); | ||
} | ||
|
||
// called only by initializing thread; no synchronization required | ||
void addAlias(final String alias) { | ||
List<String> aliases = this.aliases; | ||
if (aliases == null) { | ||
aliases = this.aliases = new ArrayList<String>(4); | ||
} | ||
if ( ! aliases.contains(alias) ) aliases.add(alias); | ||
} | ||
|
||
@Override | ||
boolean hasLocalMethod () { return localMethod; } | ||
|
||
void setLocalMethod(boolean flag) { localMethod = flag; } | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package org.jruby.javasupport.binding; | ||
|
||
import org.jruby.RubyModule; | ||
import org.jruby.runtime.Visibility; | ||
|
||
/** | ||
* Created by headius on 2/26/15. | ||
*/ | ||
public abstract class NamedInstaller { | ||
static final int STATIC_FIELD = 1; | ||
static final int STATIC_METHOD = 2; | ||
static final int INSTANCE_FIELD = 3; | ||
static final int INSTANCE_METHOD = 4; | ||
|
||
final String name; | ||
final int type; | ||
|
||
Visibility visibility = Visibility.PUBLIC; | ||
|
||
public NamedInstaller(String name, int type) { | ||
this.name = name; | ||
this.type = type; | ||
} | ||
|
||
abstract void install(RubyModule proxy); | ||
|
||
// small hack to save a cast later on | ||
boolean hasLocalMethod() { return true; } | ||
|
||
boolean isPublic() { return visibility == Visibility.PUBLIC; } | ||
|
||
//boolean isProtected() { return visibility == Visibility.PROTECTED; } | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package org.jruby.javasupport.binding; | ||
|
||
import org.jruby.javasupport.JavaClass; | ||
|
||
/** | ||
* Assigned names only override based priority of an assigned type, the type must be less than | ||
* or equal to the assigned type. For example, field name (FIELD) in a subclass will override | ||
* an alias (ALIAS) in a superclass, but not a method (METHOD). | ||
*/ | ||
public enum Priority { | ||
RESERVED(0), METHOD(1), FIELD(2), PROTECTED_METHOD(3), | ||
WEAKLY_RESERVED(4), ALIAS(5), PROTECTED_FIELD(6); | ||
|
||
private int value; | ||
|
||
Priority(int value) { | ||
this.value = value; | ||
} | ||
|
||
public boolean asImportantAs(AssignedName other) { | ||
return other != null && other.type.value == value; | ||
} | ||
|
||
public boolean lessImportantThan(AssignedName other) { | ||
return other != null && other.type.value < value; | ||
} | ||
|
||
public boolean moreImportantThan(AssignedName other) { | ||
return other == null || other.type.value > value; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package org.jruby.javasupport.binding; | ||
|
||
import org.jruby.RubyClass; | ||
import org.jruby.RubyModule; | ||
import org.jruby.internal.runtime.methods.DynamicMethod; | ||
import org.jruby.java.invokers.SingletonMethodInvoker; | ||
|
||
/** | ||
* Created by headius on 2/26/15. | ||
*/ | ||
public class SingletonMethodInvokerInstaller extends StaticMethodInvokerInstaller { | ||
|
||
final Object singleton; | ||
|
||
public SingletonMethodInvokerInstaller(String name, Object singleton) { | ||
super(name); | ||
this.singleton = singleton; | ||
} | ||
|
||
@Override void install(final RubyModule proxy) { | ||
// we don't check haveLocalMethod() here because it's not local and we know | ||
// that we always want to go ahead and install it | ||
final RubyClass singletonClass = proxy.getSingletonClass(); | ||
DynamicMethod method = new SingletonMethodInvoker(this.singleton, singletonClass, methods); | ||
singletonClass.addMethod(name, method); | ||
if ( aliases != null && isPublic() ) { | ||
singletonClass.defineAliases(aliases, name); | ||
//aliases = null; | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package org.jruby.javasupport.binding; | ||
|
||
import org.jruby.RubyModule; | ||
import org.jruby.java.invokers.StaticFieldGetter; | ||
|
||
import java.lang.reflect.Field; | ||
import java.lang.reflect.Modifier; | ||
|
||
/** | ||
* Created by headius on 2/26/15. | ||
*/ | ||
public class StaticFieldGetterInstaller extends FieldInstaller { | ||
|
||
public StaticFieldGetterInstaller(String name, Field field) { | ||
super(name, STATIC_FIELD, field); | ||
} | ||
|
||
@Override void install(final RubyModule proxy) { | ||
if ( Modifier.isPublic(field.getModifiers()) ) { | ||
proxy.getSingletonClass().addMethod(name, new StaticFieldGetter(name, proxy, field)); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package org.jruby.javasupport.binding; | ||
|
||
import org.jruby.RubyModule; | ||
import org.jruby.java.invokers.StaticFieldSetter; | ||
|
||
import java.lang.reflect.Field; | ||
import java.lang.reflect.Modifier; | ||
|
||
/** | ||
* Created by headius on 2/26/15. | ||
*/ | ||
public class StaticFieldSetterInstaller extends FieldInstaller { | ||
|
||
public StaticFieldSetterInstaller(String name, Field field) { | ||
super(name, STATIC_FIELD, field); | ||
} | ||
|
||
@Override void install(final RubyModule proxy) { | ||
if ( Modifier.isPublic(field.getModifiers()) ) { | ||
proxy.getSingletonClass().addMethod(name, new StaticFieldSetter(name, proxy, field)); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package org.jruby.javasupport.binding; | ||
|
||
import org.jruby.RubyClass; | ||
import org.jruby.RubyModule; | ||
import org.jruby.internal.runtime.methods.DynamicMethod; | ||
import org.jruby.java.invokers.StaticMethodInvoker; | ||
|
||
/** | ||
* Created by headius on 2/26/15. | ||
*/ | ||
public class StaticMethodInvokerInstaller extends MethodInstaller { | ||
|
||
public StaticMethodInvokerInstaller(String name) { super(name, STATIC_METHOD); } | ||
|
||
@Override void install(final RubyModule proxy) { | ||
if ( hasLocalMethod() ) { | ||
final RubyClass singletonClass = proxy.getSingletonClass(); | ||
DynamicMethod method = new StaticMethodInvoker(singletonClass, methods); | ||
singletonClass.addMethod(name, method); | ||
if ( aliases != null && isPublic() ) { | ||
singletonClass.defineAliases(aliases, name); | ||
//aliases = null; | ||
} | ||
} | ||
} | ||
} |
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,19 +1,8 @@ | ||
module Java | ||
class << self | ||
def const_missing(sym) | ||
result = JavaUtilities.get_top_level_proxy_or_package(sym) | ||
if const_defined? sym | ||
result | ||
else | ||
const_set(sym, result) | ||
end | ||
end | ||
|
||
def method_missing(sym, *args) | ||
class << self | ||
def method_missing(sym, *args) | ||
raise ArgumentError, "Java package `java' does not have a method `#{sym}'" unless args.empty? | ||
JavaUtilities.get_top_level_proxy_or_package sym | ||
end | ||
end | ||
|
||
end | ||
|
||
JavaUtilities.get_top_level_proxy_or_package sym | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1 @@ | ||
fails:Encoding.aliases has a 'locale' key and its value equals to the name of the encoding finded by the locale charmap | ||
fails:Encoding.aliases only contains valid aliased encodings |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,2 @@ | ||
fails:Encoding.default_external with command line options is not changed by the -U option | ||
fails:Encoding.default_external with command line options returns the encoding specified by '-E external' | ||
fails:Encoding.default_external with command line options returns the encoding specified by '-E external:' | ||
fails:Encoding.default_external= sets the default external encoding | ||
fails:Encoding.default_external= can accept a name of an encoding as a String | ||
fails:Encoding.default_external= calls #to_s on arguments that are neither Strings nor Encodings | ||
fails:Encoding.default_external= raises a TypeError unless the argument is an Encoding or convertible to a String | ||
fails:Encoding.default_external= raises an ArgumentError if the argument is nil |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,2 @@ | ||
fails:Encoding.locale_charmap returns a String | ||
fails:Encoding.locale_charmap returns a value based on the LC_ALL environment variable | ||
fails:Encoding.locale_charmap is unaffected by assigning to ENV['LC_ALL'] in the same process |
This file was deleted.
This file was deleted.
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1 @@ | ||
fails:Regexp.union returns a Regexp with US-ASCII encoding if all arguments are ASCII-only | ||
fails:Regexp.union raises ArgumentError if the arguments include conflicting fixed encoding Regexps | ||
fails:Regexp.union returns a Regexp with the encoding of a String containing non-ASCII-compatible characters and another ASCII-only String |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,72 +1,19 @@ | ||
fails:String#sub with pattern, replacement returns a copy of self with all occurrences of pattern replaced with replacement | ||
fails:String#sub with pattern, replacement ignores a block if supplied | ||
fails:String#sub with pattern, replacement supports \G which matches at the beginning of the string | ||
fails:String#sub with pattern, replacement supports /i for ignoring case | ||
fails:String#sub with pattern, replacement doesn't interpret regexp metacharacters if pattern is a string | ||
fails:String#sub with pattern, replacement replaces \1 sequences with the regexp's corresponding capture | ||
fails:String#sub with pattern, replacement treats \1 sequences without corresponding captures as empty strings | ||
fails:String#sub with pattern, replacement replaces \& and \0 with the complete match | ||
fails:String#sub with pattern, replacement replaces \` with everything before the current match | ||
fails:String#sub with pattern, replacement replaces \' with everything after the current match | ||
fails:String#sub with pattern, replacement replaces \\\+ with \\+ | ||
fails:String#sub with pattern, replacement replaces \+ with the last paren that actually matched | ||
fails:String#sub with pattern, replacement treats \+ as an empty string if there was no captures | ||
fails:String#sub with pattern, replacement maps \\ in replacement to \ | ||
fails:String#sub with pattern, replacement leaves unknown \x escapes in replacement untouched | ||
fails:String#sub with pattern, replacement leaves \ at the end of replacement untouched | ||
fails:String#sub with pattern, replacement taints the result if the original string or replacement is tainted | ||
fails:String#sub with pattern, replacement tries to convert pattern to a string using to_str | ||
fails:String#sub with pattern, replacement raises a TypeError when pattern can't be converted to a string | ||
fails:String#sub with pattern, replacement tries to convert replacement to a string using to_str | ||
fails:String#sub with pattern, replacement raises a TypeError when replacement can't be converted to a string | ||
fails:String#sub with pattern, replacement returns subclass instances when called on a subclass | ||
fails:String#sub with pattern, replacement sets $~ to MatchData of match and nil when there's none | ||
fails:String#sub with pattern, replacement replaces \ with | ||
fails:String#sub with pattern, replacement replaces \\1 with \1 | ||
fails:String#sub with pattern, replacement replaces \\ with \ | ||
fails:String#sub with pattern and block returns a copy of self with the first occurrences of pattern replaced with the block's return value | ||
fails:String#sub with pattern and block sets $~ for access from the block | ||
fails:String#sub with pattern and block sets $~ to MatchData of last match and nil when there's none for access from outside | ||
fails:String#sub with pattern and block doesn't raise a RuntimeError if the string is modified while substituting | ||
fails:String#sub with pattern and block doesn't interpolate special sequences like \1 for the block's return value | ||
fails:String#sub with pattern and block converts the block's return value to a string using to_s | ||
fails:String#sub with pattern and block taints the result if the original string or replacement is tainted | ||
fails:String#sub! with pattern, replacement modifies self in place and returns self | ||
fails:String#sub! with pattern, replacement taints self if replacement is tainted | ||
fails:String#sub! with pattern, replacement returns nil if no modifications were made | ||
fails:String#sub! with pattern, replacement raises a RuntimeError when self is frozen | ||
fails:String#sub! with pattern and block modifies self in place and returns self | ||
fails:String#sub! with pattern and block sets $~ for access from the block | ||
fails:String#sub! with pattern and block taints self if block's result is tainted | ||
fails:String#sub! with pattern and block returns nil if no modifications were made | ||
fails:String#sub! with pattern and block raises a RuntimeError if the string is modified while substituting | ||
fails:String#sub! with pattern and block raises a RuntimeError when self is frozen | ||
fails:String#sub with pattern and Hash returns a copy of self with the first occurrence of pattern replaced with the value of the corresponding hash key | ||
fails:String#sub with pattern and Hash removes keys that don't correspond to matches | ||
fails:String#sub with pattern and Hash ignores non-String keys | ||
fails:String#sub with pattern and Hash uses a key's value only a single time | ||
fails:String#sub with pattern and Hash uses the hash's default value for missing keys | ||
fails:String#sub with pattern and Hash coerces the hash values with #to_s | ||
fails:String#sub with pattern and Hash uses the hash's value set from default_proc for missing keys | ||
fails:String#sub with pattern and Hash sets $~ to MatchData of first match and nil when there's none for access from outside | ||
fails:String#sub with pattern and Hash doesn't interpolate special sequences like \1 for the block's return value | ||
fails:String#sub with pattern and Hash untrusts the result if the original string is untrusted | ||
fails:String#sub with pattern and Hash untrusts the result if a hash value is untrusted | ||
fails:String#sub with pattern and Hash taints the result if the original string is tainted | ||
fails:String#sub with pattern and Hash taints the result if a hash value is tainted | ||
fails:String#sub! with pattern and Hash returns self with the first occurrence of pattern replaced with the value of the corresponding hash key | ||
fails:String#sub! with pattern and Hash removes keys that don't correspond to matches | ||
fails:String#sub! with pattern and Hash ignores non-String keys | ||
fails:String#sub! with pattern and Hash uses a key's value only a single time | ||
fails:String#sub! with pattern and Hash uses the hash's default value for missing keys | ||
fails:String#sub! with pattern and Hash coerces the hash values with #to_s | ||
fails:String#sub! with pattern and Hash uses the hash's value set from default_proc for missing keys | ||
fails:String#sub! with pattern and Hash sets $~ to MatchData of first match and nil when there's none for access from outside | ||
fails:String#sub! with pattern and Hash doesn't interpolate special sequences like \1 for the block's return value | ||
fails:String#sub! with pattern and Hash keeps untrusted state | ||
fails:String#sub! with pattern and Hash untrusts self if a hash value is untrusted | ||
fails:String#sub! with pattern and Hash keeps tainted state | ||
fails:String#sub! with pattern and Hash taints self if a hash value is tainted | ||
fails:String#sub with pattern, replacement raises a TypeError when pattern is a Symbol | ||
fails:String#sub with pattern, replacement raises a TypeError when pattern is an Array | ||
fails:String#sub with pattern, replacement replaces \ with |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
/* | ||
* Copyright (c) 2015 Oracle and/or its affiliates. All rights reserved. This | ||
* code is released under a tri EPL/GPL/LGPL license. You can use it, | ||
* redistribute it and/or modify it under the terms of the: | ||
* | ||
* Eclipse Public License version 1.0 | ||
* GNU General Public License version 2 | ||
* GNU Lesser General Public License version 2.1 | ||
*/ | ||
|
||
|
||
package org.jruby.truffle.nodes.coerce; | ||
|
||
import com.oracle.truffle.api.CompilerDirectives; | ||
import com.oracle.truffle.api.dsl.NodeChild; | ||
import com.oracle.truffle.api.dsl.Specialization; | ||
import com.oracle.truffle.api.frame.VirtualFrame; | ||
import com.oracle.truffle.api.source.SourceSection; | ||
import org.jruby.truffle.nodes.RubyNode; | ||
import org.jruby.truffle.nodes.core.FloatNodes; | ||
import org.jruby.truffle.nodes.core.FloatNodesFactory; | ||
import org.jruby.truffle.nodes.dispatch.CallDispatchHeadNode; | ||
import org.jruby.truffle.nodes.dispatch.DispatchHeadNodeFactory; | ||
import org.jruby.truffle.runtime.RubyContext; | ||
import org.jruby.truffle.runtime.control.RaiseException; | ||
import org.jruby.truffle.runtime.core.RubyBignum; | ||
|
||
@NodeChild(value = "child", type = RubyNode.class) | ||
public abstract class ToIntNode extends RubyNode { | ||
|
||
@Child private CallDispatchHeadNode toIntNode; | ||
@Child private FloatNodes.ToINode floatToIntNode; | ||
|
||
public ToIntNode(RubyContext context, SourceSection sourceSection) { | ||
super(context, sourceSection); | ||
} | ||
|
||
public ToIntNode(ToIntNode prev) { | ||
super(prev); | ||
} | ||
|
||
@Specialization | ||
public int coerceInt(int value) { | ||
return value; | ||
} | ||
|
||
@Specialization | ||
public long coerceLong(long value) { | ||
return value; | ||
} | ||
|
||
@Specialization | ||
public RubyBignum coerceRubyBignum(RubyBignum value) { | ||
return value; | ||
} | ||
|
||
@Specialization | ||
public Object coerceDouble(double value) { | ||
if (floatToIntNode == null) { | ||
CompilerDirectives.transferToInterpreter(); | ||
floatToIntNode = insert(FloatNodesFactory.ToINodeFactory.create(getContext(), getSourceSection(), new RubyNode[]{})); | ||
} | ||
|
||
return floatToIntNode.toI(value); | ||
} | ||
|
||
@Specialization(guards = "!isRubyBignum(object)") | ||
public Object coerceObject(VirtualFrame frame, Object object) { | ||
notDesignedForCompilation(); | ||
|
||
if (toIntNode == null) { | ||
CompilerDirectives.transferToInterpreter(); | ||
toIntNode = insert(DispatchHeadNodeFactory.createMethodCall(getContext())); | ||
} | ||
|
||
final Object coerced; | ||
|
||
try { | ||
coerced = toIntNode.call(frame, object, "to_int", null); | ||
} catch (RaiseException e) { | ||
if (e.getRubyException().getLogicalClass() == getContext().getCoreLibrary().getNoMethodErrorClass()) { | ||
CompilerDirectives.transferToInterpreter(); | ||
|
||
throw new RaiseException( | ||
getContext().getCoreLibrary().typeErrorNoImplicitConversion(object, "Integer", this)); | ||
} else { | ||
throw e; | ||
} | ||
} | ||
|
||
if (getContext().getCoreLibrary().getLogicalClass(coerced) == getContext().getCoreLibrary().getFixnumClass()) { | ||
return coerced; | ||
} else { | ||
CompilerDirectives.transferToInterpreter(); | ||
|
||
throw new RaiseException( | ||
getContext().getCoreLibrary().typeErrorBadCoercion(object, "Integer", "to_int", coerced, this)); | ||
} | ||
} | ||
|
||
@Override | ||
public abstract int executeIntegerFixnum(VirtualFrame frame); | ||
|
||
public abstract int executeIntegerFixnum(VirtualFrame frame, Object object); | ||
|
||
@Override | ||
public abstract long executeLongFixnum(VirtualFrame frame); | ||
|
||
@Override | ||
public abstract RubyBignum executeBignum(VirtualFrame frame); | ||
} |