Skip to content

Commit

Permalink
Showing 88 changed files with 730 additions and 824 deletions.
2 changes: 1 addition & 1 deletion core/src/main/java/org/jruby/RubyObjectSpace.java
Original file line number Diff line number Diff line change
@@ -114,7 +114,7 @@ public static IRubyObject id2ref(IRubyObject recv, IRubyObject id) {
return object;
} else {
runtime.getWarnings().warn("ObjectSpace is disabled; _id2ref only supports immediates, pass -X+O to enable");
return runtime.getNil();
throw recv.getRuntime().newRangeError(String.format("0x%016x is not id value", longId));
}
}
}
31 changes: 15 additions & 16 deletions core/src/main/java/org/jruby/java/dispatch/CallableSelector.java
Original file line number Diff line number Diff line change
@@ -529,7 +529,7 @@ public boolean match(Class type, IRubyObject arg) {
};

private static boolean exactMatch(ParameterTypes paramTypes, IRubyObject... args) {
Class[] types = paramTypes.getParameterTypes();
final Class[] types = paramTypes.getParameterTypes();

if (args.length != types.length) return false;

@@ -542,7 +542,7 @@ private static boolean exactMatch(ParameterTypes paramTypes, IRubyObject... args
}

private static boolean assignableAndPrimitivable(ParameterTypes paramTypes, IRubyObject... args) {
Class[] types = paramTypes.getParameterTypes();
final Class[] types = paramTypes.getParameterTypes();

if (args.length != types.length) return false;

@@ -555,7 +555,7 @@ private static boolean assignableAndPrimitivable(ParameterTypes paramTypes, IRub
}

private static boolean assignableOrDuckable(ParameterTypes paramTypes, IRubyObject... args) {
Class[] types = paramTypes.getParameterTypes();
final Class[] types = paramTypes.getParameterTypes();

if (args.length != types.length) return false;

@@ -568,31 +568,30 @@ private static boolean assignableOrDuckable(ParameterTypes paramTypes, IRubyObje
}

private static boolean assignableAndPrimitivableWithVarargs(ParameterTypes paramTypes, IRubyObject... args) {
// bail out if this is not a varargs method
if (!paramTypes.isVarArgs()) return false;
if ( ! paramTypes.isVarArgs() ) return false; // bail out if this is not a varargs method

Class[] types = paramTypes.getParameterTypes();

Class varArgArrayType = types[types.length - 1];
Class varArgType = varArgArrayType.getComponentType();
final Class[] types = paramTypes.getParameterTypes();

// if there's no args, we only match when there's just varargs
if ( args.length == 0 ) return types.length <= 1;

// dig out as many trailing args as will fit, ensuring they match varargs type
int nonVarargs = types.length - 1;
for (int i = args.length - 1; i >= nonVarargs; i--) {
if (!(ASSIGNABLE.match(varArgType, args[i]) || PRIMITIVABLE.match(varArgType, args[i]))) {
final int last = types.length - 1;

if ( args.length < last ) return false; // can't match - paramTypes method is not usable!
for ( int i = 0; i < last; i++ ) { // first check non-vararg argument types match
if (!(ASSIGNABLE.match(types[i], args[i]) || PRIMITIVABLE.match(types[i], args[i]))) {
return false;
}
}

// check remaining args
for (int i = 0; i < nonVarargs; i++) {
if (!(ASSIGNABLE.match(types[i], args[i]) || PRIMITIVABLE.match(types[i], args[i]))) {
final Class varArgType = types[last].getComponentType();
// dig out as many trailing args as will fit, ensuring they match varargs type
for ( int i = last; i < args.length; i++ ) {
if (!(ASSIGNABLE.match(varArgType, args[i]) || PRIMITIVABLE.match(varArgType, args[i]))) {
return false;
}
}

return true;
}

Original file line number Diff line number Diff line change
@@ -16,9 +16,7 @@
public final class ConstructorInvoker extends RubyToJavaInvoker {

public ConstructorInvoker(RubyModule host, List<Constructor> ctors) {
super(host, ctors.toArray(new Constructor[ctors.size()]));

trySetAccessible(getAccessibleObjects());
super(host, setAccessible( ctors.toArray(new Constructor[ctors.size()]) ) );
}

@Override
6 changes: 2 additions & 4 deletions core/src/main/java/org/jruby/java/invokers/MethodInvoker.java
Original file line number Diff line number Diff line change
@@ -11,13 +11,11 @@
public abstract class MethodInvoker extends RubyToJavaInvoker {

MethodInvoker(RubyModule host, List<Method> methods) {
super(host, methods.toArray(new Method[methods.size()]));
trySetAccessible(getAccessibleObjects());
super(host, setAccessible( methods.toArray(new Method[methods.size()]) ) );
}

MethodInvoker(RubyModule host, Method method) {
super(host, new Method[] { method });
trySetAccessible(getAccessibleObjects());
super(host, setAccessible(method));
}

@Override
113 changes: 82 additions & 31 deletions core/src/main/java/org/jruby/java/invokers/RubyToJavaInvoker.java
Original file line number Diff line number Diff line change
@@ -10,7 +10,6 @@
import org.jruby.Ruby;
import org.jruby.RubyModule;
import org.jruby.exceptions.RaiseException;
import org.jruby.internal.runtime.methods.CallConfiguration;
import org.jruby.internal.runtime.methods.JavaMethod;
import org.jruby.java.dispatch.CallableSelector;
import org.jruby.java.proxies.ArrayJavaProxy;
@@ -33,48 +32,73 @@ public abstract class RubyToJavaInvoker extends JavaMethod {
protected final JavaCallable javaCallable; /* null if multiple callable members */
protected final JavaCallable[][] javaCallables; /* != null if javaCallable == null */
protected final JavaCallable[] javaVarargsCallables; /* != null if any var args callables */
protected final int minVarargsArity;

// in case multiple callables (overloaded Java method - same name different args)
// for the invoker exists CallableSelector caches resolution based on args here
final IntHashMap<JavaCallable> cache;

private final Ruby runtime;
private final Member[] members;

RubyToJavaInvoker(RubyModule host, Member member) {
super(host, Visibility.PUBLIC);
this.runtime = host.getRuntime();

final JavaCallable callable;
JavaCallable[] varargsCallables = null;
int minVarArgsArity = -1;

callable = createCallable(runtime, member);
int minArity = callable.getArity();
if ( callable.isVarArgs() ) { // TODO does it need to happen?
varargsCallables = createCallableArray(callable);
minVarArgsArity = getMemberArity(member) - 1;
}

cache = NULL_CACHE; // if there's a single callable - matching (and thus the cache) won't be used

this.javaCallable = callable;
this.javaCallables = null;
this.javaVarargsCallables = varargsCallables;

setArity(minArity, minArity, minVarArgsArity);
setupNativeCall();
}

RubyToJavaInvoker(RubyModule host, Member[] members) {
super(host, Visibility.PUBLIC, CallConfiguration.FrameNoneScopeNone);
this.members = members;
super(host, Visibility.PUBLIC);
this.runtime = host.getRuntime();
// we set all Java methods to optional, since many/most have overloads
setArity(Arity.OPTIONAL);

// initialize all the callables for this method
final JavaCallable callable;
final JavaCallable[][] callables;
JavaCallable[] varargsCallables = null;
int varArgsArity = Integer.MAX_VALUE;
int minVarArgsArity = -1; int maxArity, minArity;

final int length = members.length;
if ( length == 1 ) {
callable = createCallable(runtime, members[0]);
maxArity = minArity = callable.getArity();
if ( callable.isVarArgs() ) {
varargsCallables = createCallableArray(callable);
minVarArgsArity = getMemberArity(members[0]) - 1;
}
callables = null;

cache = NULL_CACHE; // if there's a single callable - matching (and thus the cache) won't be used
}
else {
callable = null;
callable = null; maxArity = -1; minArity = Integer.MAX_VALUE;

IntHashMap<ArrayList<JavaCallable>> arityMap = new IntHashMap<ArrayList<JavaCallable>>(length, 1);

ArrayList<JavaCallable> varArgs = null; int maxArity = 0;
ArrayList<JavaCallable> varArgs = null;
for ( int i = 0; i < length; i++ ) {
final Member method = members[i];
final int currentArity = getMemberParameterTypes(method).length;
final int currentArity = getMemberArity(method);
maxArity = Math.max(currentArity, maxArity);
minArity = Math.min(currentArity, minArity);

final JavaCallable javaMethod = createCallable(runtime, method);

ArrayList<JavaCallable> methodsForArity = arityMap.get(currentArity);
if (methodsForArity == null) {
@@ -84,14 +108,22 @@ public abstract class RubyToJavaInvoker extends JavaMethod {
methodsForArity = new ArrayList<JavaCallable>(length);
arityMap.put(currentArity, methodsForArity);
}

final JavaCallable javaMethod = createCallable(runtime, method);
methodsForArity.add(javaMethod);

if ( isMemberVarArgs(method) ) {
varArgsArity = Math.min(currentArity - 1, varArgsArity);
if ( javaMethod.isVarArgs() ) {
final int usableArity = currentArity - 1;
// (String, Object...) has usable arity == 1 ... (String)
if ((methodsForArity = arityMap.get(usableArity)) == null) {
methodsForArity = new ArrayList<JavaCallable>(length);
arityMap.put(usableArity, methodsForArity);
}
methodsForArity.add(javaMethod);

if (varArgs == null) varArgs = new ArrayList<JavaCallable>(length);
varArgs.add(javaMethod);

if ( minVarArgsArity == -1 ) minVarArgsArity = Integer.MAX_VALUE;
minVarArgsArity = Math.min(usableArity, minVarArgsArity);
}
}

@@ -113,9 +145,26 @@ public abstract class RubyToJavaInvoker extends JavaMethod {
this.javaCallable = callable;
this.javaCallables = callables;
this.javaVarargsCallables = varargsCallables;
this.minVarargsArity = varArgsArity;

// if it's not overloaded, set up a NativeCall
setArity(minArity, maxArity, minVarArgsArity);
setupNativeCall();
}

private void setArity(final int minArity, final int maxArity, final int minVarArgsArity) {
if ( minVarArgsArity == -1 ) { // no var-args
if ( minArity == maxArity ) {
setArity( Arity.fixed(minArity) );
}
else { // multiple overloads
setArity( Arity.required(minArity) ); // but <= maxArity
}
}
else {
setArity( Arity.required(minVarArgsArity < minArity ? minVarArgsArity : minArity) );
}
}

final void setupNativeCall() { // if it's not overloaded, set up a NativeCall
if (javaCallable != null) {
// no constructor support yet
if (javaCallable instanceof org.jruby.javasupport.JavaMethod) {
@@ -142,14 +191,6 @@ private boolean setNativeCallIfPublic(final Method method) {
return false;
}

protected final Member[] getMembers() {
return members;
}

protected AccessibleObject[] getAccessibleObjects() {
return (AccessibleObject[]) getMembers();
}

protected abstract JavaCallable createCallable(Ruby runtime, Member member);

protected abstract JavaCallable[] createCallableArray(JavaCallable callable);
@@ -160,11 +201,12 @@ protected AccessibleObject[] getAccessibleObjects() {

protected abstract Class[] getMemberParameterTypes(Member member);

@Deprecated // no longer used!
protected abstract boolean isMemberVarArgs(Member member);

//final int getMemberArity(Member member) {
// return getMemberParameterTypes(member).length;
//}
final int getMemberArity(Member member) {
return getMemberParameterTypes(member).length;
}

public static Object[] convertArguments(final ParameterTypes method, final IRubyObject[] args) {
final Class<?>[] paramTypes = method.getParameterTypes();
@@ -213,11 +255,20 @@ static JavaProxy castJavaProxy(final IRubyObject self) {
return (JavaProxy) self;
}

static void trySetAccessible(AccessibleObject... accesibles) {
static <T extends AccessibleObject> T setAccessible(T accessible) {
if ( ! Ruby.isSecurityRestricted() ) {
try { accessible.setAccessible(true); }
catch (SecurityException e) {}
}
return accessible;
}

static <T extends AccessibleObject> T[] setAccessible(T[] accessibles) {
if ( ! Ruby.isSecurityRestricted() ) {
try { AccessibleObject.setAccessible(accesibles, true); }
catch(SecurityException e) {}
try { AccessibleObject.setAccessible(accessibles, true); }
catch (SecurityException e) {}
}
return accessibles;
}

protected JavaCallable findCallable(IRubyObject self, String name, IRubyObject[] args, final int arity) {
4 changes: 4 additions & 0 deletions core/src/main/java/org/jruby/javasupport/JavaMethod.java
Original file line number Diff line number Diff line change
@@ -526,6 +526,10 @@ private IRubyObject convertReturn(Object result) {
// return parameterTypes;
//}

public String getName() {
return method.getName();
}

@Override
public Class<?>[] getExceptionTypes() {
return method.getExceptionTypes();
320 changes: 154 additions & 166 deletions core/src/main/java/org/jruby/runtime/ThreadContext.java

Large diffs are not rendered by default.

30 changes: 16 additions & 14 deletions core/src/main/java/org/jruby/runtime/backtrace/TraceType.java
Original file line number Diff line number Diff line change
@@ -246,16 +246,17 @@ public void renderBacktrace(RubyStackTraceElement[] elts, StringBuilder buffer,
}

protected static String printBacktraceMRI(RubyException exception, boolean console) {
Ruby runtime = exception.getRuntime();
ThreadContext context = runtime.getCurrentContext();
IRubyObject backtrace = exception.callMethod(context, "backtrace");
final Ruby runtime = exception.getRuntime();
final ThreadContext context = runtime.getCurrentContext();

final IRubyObject backtrace = exception.callMethod(context, "backtrace");

ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintStream errorStream = new PrintStream(baos);
boolean printedPosition = false;
if (backtrace.isNil() || !(backtrace instanceof RubyArray)) {
if (context.getFile() != null && context.getFile().length() > 0) {
errorStream.print(context.getFile() + ":" + context.getLine());
errorStream.print(context.getFile() + ':' + context.getLine());
printedPosition = true;
} else {
errorStream.print(context.getLine());
@@ -277,11 +278,10 @@ protected static String printBacktraceMRI(RubyException exception, boolean conso
RubyClass type = exception.getMetaClass();
String info = exception.toString();

if (printedPosition) errorStream.print(": ");

if (type == runtime.getRuntimeError() && (info == null || info.length() == 0)) {
errorStream.print(": unhandled exception\n");
} else {
if (printedPosition) errorStream.print(": ");
String path = type.getName();

if (info.length() == 0) {
@@ -292,9 +292,10 @@ protected static String printBacktraceMRI(RubyException exception, boolean conso
}

String tail = null;
if (info.indexOf("\n") != -1) {
tail = info.substring(info.indexOf("\n") + 1);
info = info.substring(0, info.indexOf("\n"));
int idx = info.indexOf('\n');
if (idx != -1) {
tail = info.substring(idx + 1);
info = info.substring(0, idx);
}

errorStream.print(info);
@@ -320,13 +321,14 @@ protected static String printBacktraceMRI(RubyException exception, boolean conso
private static final String CLEAR_COLOR = "\033[0m";

protected static String printBacktraceJRuby(RubyException exception, boolean console) {
Ruby runtime = exception.getRuntime();
final Ruby runtime = exception.getRuntime();
final ThreadContext context = runtime.getCurrentContext();

StringBuilder buffer = new StringBuilder();
boolean color = console && runtime.getInstanceConfig().getBacktraceColor();

// exception line
String message = exception.message(runtime.getCurrentContext()).toString();
String message = exception.message(context).toString();
if (exception.getMetaClass() == runtime.getRuntimeError() && message.length() == 0) {
message = "No current exception";
}
@@ -412,7 +414,7 @@ public static IRubyObject generateMRIBacktrace(Ruby runtime, RubyStackTraceEleme
for (int i = 0; i < trace.length; i++) {
RubyStackTraceElement element = trace[i];

RubyString str = RubyString.newString(runtime, element.getFileName() + ":" + element.getLineNumber() + ":in `" + element.getMethodName() + "'");
RubyString str = RubyString.newString(runtime, element.getFileName() + ':' + element.getLineNumber() + ":in `" + element.getMethodName() + "'");
traceArray.append(str);
}

@@ -422,10 +424,10 @@ public static IRubyObject generateMRIBacktrace(Ruby runtime, RubyStackTraceEleme
private static void printErrorPos(ThreadContext context, PrintStream errorStream) {
if (context.getFile() != null && context.getFile().length() > 0) {
if (context.getFrameName() != null) {
errorStream.print(context.getFile() + ":" + context.getLine());
errorStream.print(context.getFile() + ':' + context.getLine());
errorStream.print(":in '" + context.getFrameName() + '\'');
} else if (context.getLine() != 0) {
errorStream.print(context.getFile() + ":" + context.getLine());
errorStream.print(context.getFile() + ':' + context.getLine());
} else {
errorStream.print(context.getFile());
}
37 changes: 29 additions & 8 deletions spec/java_integration/methods/basics_spec.rb
Original file line number Diff line number Diff line change
@@ -1,18 +1,39 @@
require File.dirname(__FILE__) + "/../spec_helper"

describe "Java instance methods" do
it "should have Ruby arity -1" do
expect do
expect(java.lang.String.instance_method(:toString).arity).to eq(-1)
end.not_to raise_error
it "have correct (fixed) arity" do
expect(java.lang.String.instance_method(:length).arity).to eq(0)
expect(java.lang.String.instance_method(:toString).arity).to eq(0)
expect(java.lang.String.instance_method(:charAt).arity).to eq(1)

expect(java.lang.String.instance_method(:replace).arity).to eq(2)
end
it "(overloads) have correct arity" do
expect(java.lang.String.instance_method(:lastIndexOf).arity).to eq(-2)
expect(java.lang.String.instance_method(:toLowerCase).arity).to eq(-1)

expect(java.lang.StringBuilder.instance_method(:append).arity).to eq(-2)
expect(java.lang.StringBuilder.instance_method(:insert).arity).to eq(-3)

expect(java.util.ArrayList.instance_method(:toArray).arity).to eq(-1)
end
end

describe "Java static methods" do
it "should have Ruby arity -1" do
expect do
expect(java.lang.System.method(:getProperty).arity).to eq(-1)
end.not_to raise_error
it "have correct arity" do
expect(java.lang.System.method(:exit).arity).to eq(1)
expect(java.lang.System.method(:gc).arity).to eq(0)
expect(java.lang.System.method(:arraycopy).arity).to eq(5)

expect(java.util.Arrays.method(:asList).arity).to eq(-1) # (T... a)
end
it "(overloads) have correct arity" do
expect(java.lang.System.method(:getProperty).arity).to eq(-2)
# format(String format, Object... args)
# format(Locale l, String format, Object... args)
expect(java.lang.String.method(:format).arity).to eq(-2)
# not fixed due valueOf(char[] data, int offset, int count)
expect(java.lang.String.method(:valueOf).arity).to eq(-2)
end
end

50 changes: 27 additions & 23 deletions spec/java_integration/methods/dispatch_spec.rb
Original file line number Diff line number Diff line change
@@ -24,7 +24,7 @@
expect(CoreTypeMethods.getType(1.0, obj)).to eq("double,object")
expect(CoreTypeMethods.getType(1.0, obj, obj)).to eq("double,object,object")
expect(CoreTypeMethods.getType(1.0, obj, obj, obj)).to eq("double,object,object,object")

obj = "foo"
expect(CoreTypeMethods.getType(1)).to eq("long")
expect(CoreTypeMethods.getType(1, obj)).to eq("long,string")
@@ -34,14 +34,15 @@
expect(CoreTypeMethods.getType(1.0, obj)).to eq("double,string")
expect(CoreTypeMethods.getType(1.0, obj, obj)).to eq("double,string,string")
expect(CoreTypeMethods.getType(1.0, obj, obj, obj)).to eq("double,string,string,string")
expect(CoreTypeMethods.getType(1.0, obj.to_java)).to eq("double,string")
end

it "should raise error when called with too many args" do
expect do
obj = java.lang.Integer.new(1)
CoreTypeMethods.getType(1, obj, obj, obj, obj)
end.to raise_error(ArgumentError)

expect do
obj = "foo"
CoreTypeMethods.getType(1, obj, obj, obj, obj)
@@ -68,7 +69,7 @@
end
it "should be of the correct type" do
expect(@return_value).to be_an_instance_of(StaticMethodSelection)
end
end
end

describe "An overloaded Java instance method" do
@@ -83,7 +84,7 @@
expect(ctm.getTypeInstance(1.0, obj)).to eq("double,object")
expect(ctm.getTypeInstance(1.0, obj, obj)).to eq("double,object,object")
expect(ctm.getTypeInstance(1.0, obj, obj, obj)).to eq("double,object,object,object")

obj = "foo"
ctm = CoreTypeMethods.new
expect(ctm.getTypeInstance(1)).to eq("long")
@@ -101,7 +102,7 @@
obj = java.lang.Integer.new(1)
CoreTypeMethods.new.getTypeInstance(1, obj, obj, obj, obj)
end.to raise_error(ArgumentError)

expect do
obj = "foo"
CoreTypeMethods.new.getTypeInstance(1, obj, obj, obj, obj)
@@ -159,16 +160,16 @@
obj = ClassWithVarargs.new('foo', 'bar', 'baz', 1, 2, 3, 4)
expect(obj.constructor).to eq("3: [1, 2, 3, 4]")

skip("needs better type-driven ranking of overloads") do
obj = ClassWithVarargs.new('foo')
expect(obj.constructor).to eq("1: []")
#skip("needs better type-driven ranking of overloads") do
obj = ClassWithVarargs.new('foo')
expect(obj.constructor).to eq("1: []")

obj = ClassWithVarargs.new('foo', 'bar')
expect(obj.constructor).to eq("2: []")
obj = ClassWithVarargs.new('foo', 'bar')
expect(obj.constructor).to eq("2: []")

obj = ClassWithVarargs.new('foo', 'bar', 'baz')
expect(obj.constructor).to eq("3: []")
end
obj = ClassWithVarargs.new('foo', 'bar', 'baz')
expect(obj.constructor).to eq("3: []")
#end
end

it "should be callable with an array" do
@@ -204,11 +205,11 @@
expect(obj.varargs('foo', 'bar', 'baz', 1, 2, 3)).to eq("3: [1, 2, 3]");
expect(obj.varargs('foo', 'bar', 'baz', 1, 2, 3, 4)).to eq("3: [1, 2, 3, 4]");

skip("needs better type-driven ranking of overloads") do
expect(obj.varargs('foo')).to eq("1: []")
expect(obj.varargs('foo', 'bar')).to eq("2: []")
expect(obj.varargs('foo', 'bar', 'baz')).to eq("3: []")
end
#skip("needs better type-driven ranking of overloads") do
expect(obj.varargs('foo')).to eq("1: []")
expect(obj.varargs('foo', 'bar')).to eq("2: []")
expect(obj.varargs('foo', 'bar', 'baz')).to eq("3: []")
#end
end

it "should be callable with an array" do
@@ -243,11 +244,14 @@
expect(ClassWithVarargs.varargs_static('foo', 'bar', 'baz', 1, 2, 3)).to eq("3: [1, 2, 3]");
expect(ClassWithVarargs.varargs_static('foo', 'bar', 'baz', 1, 2, 3, 4)).to eq("3: [1, 2, 3, 4]");

skip("needs better type-driven ranking of overloads") do
expect(ClassWithVarargs.varargs_static('foo')).to eq("1: []")
expect(ClassWithVarargs.varargs_static('foo', 'bar')).to eq("2: []")
expect(ClassWithVarargs.varargs_static('foo', 'bar')).to eq("3: []")
end
#skip("needs better type-driven ranking of overloads") do
expect(ClassWithVarargs.varargs_static('foo')).to eq("1: []")
expect(ClassWithVarargs.varargs_static('foo', 'bar')).to eq("2: []")
expect(ClassWithVarargs.varargs_static('foo', 'bar', 'baz')).to eq("3: []")
#end
expect(ClassWithVarargs.varargs_static('foo'.to_java)).to eq("1: []")
expect(ClassWithVarargs.varargs_static('foo'.to_java, 'bar')).to eq("2: []")
expect(ClassWithVarargs.varargs_static('foo'.to_java, 'bar'.to_java)).to eq("2: []")
end

it "should be callable with an array" do
16 changes: 8 additions & 8 deletions spec/java_integration/types/extension_spec.rb
Original file line number Diff line number Diff line change
@@ -98,7 +98,7 @@ def size; 100; end
end.new

expect(ReceivesArrayList.new.receive_array_list(my_arraylist)).to eq(100)

thread = java.lang.Thread.new(my_arraylist)
thread.start
thread.join
@@ -121,19 +121,19 @@ def initialize
expect(my_arraylist.class.superclass).to eq(java.util.ArrayList)
expect(my_arraylist.to_java).to eq(my_arraylist)
end

it "raises argument error when super does not match superclass constructor arity" do
my_arraylist_cls = Class.new(java.util.ArrayList) do
def initialize
super('foo', 'foo', 'foo')
end
end

expect do
my_arraylist_cls.new
end.to raise_error(ArgumentError)
end

# JRUBY-4788
it "raises argument error if no matching arity method has been implemented on class or superclass" do
my_cwam_cls = Class.new(ClassWithAbstractMethods) do
@@ -143,12 +143,12 @@ def foo1
end
end
my_cwam = my_cwam_cls.new

expect do
ClassWithAbstractMethods.callFoo1(my_cwam, "ok")
end.to raise_error(ArgumentError)
end

it "dispatches to other-arity superclass methods if arities mismatch" do
my_cwam_cls = Class.new(ClassWithAbstractMethods) do
# arity should be 2, mismatch is intentional
@@ -157,15 +157,15 @@ def foo2(arg)
end
end
my_cwam = my_cwam_cls.new

expect(ClassWithAbstractMethods.callFoo2(my_cwam, "x", "y")).to eq("ok")
end
end

describe "A final Java class" do
it "should not be allowed as a superclass" do
expect do
substring = Class.new(java.lang.String)
Class.new(java.lang.String)
end.to raise_error(TypeError)
end
end
22 changes: 20 additions & 2 deletions spec/ruby/core/fixnum/left_shift_spec.rb
Original file line number Diff line number Diff line change
@@ -7,6 +7,8 @@

it "returns n shifted left m bits when n < 0, m > 0" do
(-1 << 1).should == -2
(-7 << 1).should == -14
(-42 << 2).should == -168
end

it "returns n shifted right m bits when n > 0, m < 0" do
@@ -29,8 +31,24 @@
(-1 << 0).should == -1
end

it "returns 0 when m < 0 and m == p where 2**p > n >= 2**(p-1)" do
(4 << -3).should == 0
it "returns 0 when n > 0, m < 0 and n < 2**-m" do
(3 << -2).should == 0
(7 << -3).should == 0
(127 << -7).should == 0

# To make sure the exponent is not truncated
(7 << -32).should == 0
(7 << -64).should == 0
end

it "returns -1 when n < 0, m < 0 and n > -(2**-m)" do
(-3 << -2).should == -1
(-7 << -3).should == -1
(-127 << -7).should == -1

# To make sure the exponent is not truncated
(-7 << -32).should == -1
(-7 << -64).should == -1
end

not_compliant_on :rubinius, :jruby do
26 changes: 17 additions & 9 deletions spec/ruby/core/fixnum/right_shift_spec.rb
Original file line number Diff line number Diff line change
@@ -7,6 +7,8 @@

it "returns n shifted right m bits when n < 0, m > 0" do
(-2 >> 1).should == -1
(-7 >> 1).should == -4
(-42 >> 2).should == -11
end

it "returns n shifted left m bits when n > 0, m < 0" do
@@ -29,18 +31,24 @@
(-1 >> 0).should == -1
end

it "returns 0 when m > 0 and m == p where 2**p > n >= 2**(p-1)" do
(4 >> 3).should == 0
end

fixnum_bits = (Math.log(fixnum_max) / Math.log(2)).to_i
it "returns 0 when n > 0, m > 0 and n < 2**m" do
(3 >> 2).should == 0
(7 >> 3).should == 0
(127 >> 7).should == 0

it "returns 0 when m is outside the available bits and n >= 0" do
(2 >> (fixnum_bits + 1)).should == 0
# To make sure the exponent is not truncated
(7 >> 32).should == 0
(7 >> 64).should == 0
end

it "returns -1 when m is outside the available bits and n < 0" do
(-2 >> (fixnum_bits + 1)).should == -1
it "returns -1 when n < 0, m > 0 and n > -(2**m)" do
(-3 >> 2).should == -1
(-7 >> 3).should == -1
(-127 >> 7).should == -1

# To make sure the exponent is not truncated
(-7 >> 32).should == -1
(-7 >> 64).should == -1
end

not_compliant_on :rubinius do
14 changes: 14 additions & 0 deletions spec/ruby/language/case_spec.rb
Original file line number Diff line number Diff line change
@@ -365,4 +365,18 @@ def bar; @calls << :bar; end
"bar"
end.should == "bar"
end

it "calls === even when private" do
klass = Class.new do
def ===(o)
true
end
private :===
end

case 1
when klass.new
:called
end.should == :called
end
end
15 changes: 15 additions & 0 deletions spec/ruby/language/super_spec.rb
Original file line number Diff line number Diff line change
@@ -79,6 +79,21 @@ def foo
lambda {sub_zsuper.new.foo}.should raise_error(NoMethodError, /super/)
end

it "uses given block even if arguments are passed explicitly" do
c1 = Class.new do
def m
yield
end
end
c2 = Class.new(c1) do
def m(v)
super()
end
end

c2.new.m(:dump) { :value }.should == :value
end

it "calls the superclass method when in a block" do
Super::S6.new.here.should == :good
end
1 change: 0 additions & 1 deletion spec/truffle/tags/core/argf/gets_tags.txt
Original file line number Diff line number Diff line change
@@ -2,4 +2,3 @@ fails:ARGF.gets sets $_ global variable with each line read
fails:ARGF.gets modifies the files when in place edit mode is on
fails:ARGF.gets modifies and backups two files when in place edit mode is on
slow:ARGF.gets reads all lines of stdin
fails:ARGF.gets reads all lines of stdin
3 changes: 0 additions & 3 deletions spec/truffle/tags/core/argf/read_tags.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
slow:ARGF.read reads the contents of stdin
slow:ARGF.read reads a number of bytes from stdin
slow:ARGF.read reads the contents of one file and stdin
fails:ARGF.read reads the contents of stdin
fails:ARGF.read reads a number of bytes from stdin
fails:ARGF.read reads the contents of one file and stdin
1 change: 0 additions & 1 deletion spec/truffle/tags/core/argf/readline_tags.txt
Original file line number Diff line number Diff line change
@@ -2,4 +2,3 @@ fails:ARGF.readline sets $_ global variable with each line read
fails:ARGF.readline modifies the files when in place edit mode is on
fails:ARGF.readline modifies and backups two files when in place edit mode is on
slow:ARGF.readline reads all lines of stdin
fails:ARGF.readline reads all lines of stdin
4 changes: 0 additions & 4 deletions spec/truffle/tags/core/argf/set_encoding_tags.txt
Original file line number Diff line number Diff line change
@@ -2,7 +2,3 @@ slow:ARGF.set_encoding sets the external encoding when passed an encoding instan
slow:ARGF.set_encoding sets the external encoding when passed an encoding name
slow:ARGF.set_encoding sets the external, internal encoding when passed two encoding instances
slow:ARGF.set_encoding sets the external, internal encoding when passed 'ext:int' String
fails:ARGF.set_encoding sets the external encoding when passed an encoding instance
fails:ARGF.set_encoding sets the external encoding when passed an encoding name
fails:ARGF.set_encoding sets the external, internal encoding when passed two encoding instances
fails:ARGF.set_encoding sets the external, internal encoding when passed 'ext:int' String
1 change: 0 additions & 1 deletion spec/truffle/tags/core/array/element_set_tags.txt
Original file line number Diff line number Diff line change
@@ -2,6 +2,5 @@ fails:Array#[]= sets elements in the range arguments when passed ranges
fails:Array#[]= does nothing if the section defined by range has negative width and the rhs is an empty array
fails:Array#[]= tries to convert Range elements to Integers using #to_int with [m..n] and [m...n]
fails:Array#[]= calls to_ary on its rhs argument for multi-element sets
fails:Array#[]= with [m..n] replaces the section if m < 0 and n > 0
fails:Array#[]= with [m..n] inserts the other section at m if m > n
fails:Array#[]= with [m..n] accepts Range subclasses
2 changes: 0 additions & 2 deletions spec/truffle/tags/core/dir/entries_tags.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1 @@
fails:Dir.entries returns entries encoded with the filesystem encoding by default
fails:Dir.entries returns entries encoded with the specified encoding
fails:Dir.entries returns entries transcoded to the default internal encoding
1 change: 0 additions & 1 deletion spec/truffle/tags/core/encoding/converter/convert_tags.txt

This file was deleted.

6 changes: 0 additions & 6 deletions spec/truffle/tags/core/encoding/converter/finish_tags.txt

This file was deleted.

Empty file.
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
fails:Encoding::Converter#primitive_convert accepts a nil source buffer
fails:Encoding::Converter#primitive_convert calls #to_int to convert the destination byte size
fails:Encoding::Converter#primitive_convert reuses read-again bytes after the first error
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
fails:Encoding::Converter#primitive_errinfo returns the state, source encoding, target encoding, erroneous bytes, and the read-again bytes when #convert last raised InvalidByteSequenceError
fails:Encoding::Converter#primitive_errinfo returns the state, source encoding, target encoding, erroneous bytes, and the read-again bytes when #finish last raised InvalidByteSequenceError
3 changes: 0 additions & 3 deletions spec/truffle/tags/core/encoding/default_external_tags.txt
Original file line number Diff line number Diff line change
@@ -3,6 +3,3 @@ windows:Encoding.default_external with command line options returns the encoding
slow:Encoding.default_external with command line options is not changed by the -U option
slow:Encoding.default_external with command line options returns the encoding specified by '-E external'
slow:Encoding.default_external with command line options returns the encoding specified by '-E external:'
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:'
3 changes: 0 additions & 3 deletions spec/truffle/tags/core/encoding/default_internal_tags.txt
Original file line number Diff line number Diff line change
@@ -4,6 +4,3 @@ windows:Encoding.default_internal with command line options uses the encoding sp
slow:Encoding.default_internal with command line options returns Encoding::UTF_8 if ruby was invoked with -U
slow:Encoding.default_internal with command line options uses the encoding specified when ruby is invoked with an '-E :internal' argument
slow:Encoding.default_internal with command line options uses the encoding specified when ruby is invoked with an '-E external:internal' argument
fails:Encoding.default_internal with command line options returns Encoding::UTF_8 if ruby was invoked with -U
fails:Encoding.default_internal with command line options uses the encoding specified when ruby is invoked with an '-E :internal' argument
fails:Encoding.default_internal with command line options uses the encoding specified when ruby is invoked with an '-E external:internal' argument

This file was deleted.

1 change: 0 additions & 1 deletion spec/truffle/tags/core/enumerator/lazy/collect_tags.txt

This file was deleted.

1 change: 0 additions & 1 deletion spec/truffle/tags/core/enumerator/lazy/drop_while_tags.txt

This file was deleted.

1 change: 0 additions & 1 deletion spec/truffle/tags/core/enumerator/lazy/enum_for_tags.txt

This file was deleted.

1 change: 0 additions & 1 deletion spec/truffle/tags/core/enumerator/lazy/find_all_tags.txt

This file was deleted.

3 changes: 0 additions & 3 deletions spec/truffle/tags/core/enumerator/lazy/flat_map_tags.txt

This file was deleted.

2 changes: 0 additions & 2 deletions spec/truffle/tags/core/enumerator/lazy/force_tags.txt

This file was deleted.

1 change: 0 additions & 1 deletion spec/truffle/tags/core/enumerator/lazy/grep_tags.txt

This file was deleted.

1 change: 0 additions & 1 deletion spec/truffle/tags/core/enumerator/lazy/map_tags.txt

This file was deleted.

1 change: 0 additions & 1 deletion spec/truffle/tags/core/enumerator/lazy/reject_tags.txt

This file was deleted.

1 change: 0 additions & 1 deletion spec/truffle/tags/core/enumerator/lazy/select_tags.txt

This file was deleted.

2 changes: 0 additions & 2 deletions spec/truffle/tags/core/enumerator/lazy/take_tags.txt

This file was deleted.

3 changes: 0 additions & 3 deletions spec/truffle/tags/core/enumerator/lazy/take_while_tags.txt

This file was deleted.

1 change: 0 additions & 1 deletion spec/truffle/tags/core/enumerator/lazy/to_enum_tags.txt

This file was deleted.

1 change: 0 additions & 1 deletion spec/truffle/tags/core/enumerator/lazy/zip_tags.txt

This file was deleted.

1 change: 0 additions & 1 deletion spec/truffle/tags/core/enumerator/rewind_tags.txt

This file was deleted.

2 changes: 0 additions & 2 deletions spec/truffle/tags/core/enumerator/to_enum_tags.txt

This file was deleted.

1 change: 0 additions & 1 deletion spec/truffle/tags/core/io/bytes_tags.txt

This file was deleted.

2 changes: 0 additions & 2 deletions spec/truffle/tags/core/io/chars_tags.txt

This file was deleted.

3 changes: 0 additions & 3 deletions spec/truffle/tags/core/io/codepoints_tags.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1 @@
fails:IO#codepoints yields each codepoint
fails:IO#codepoints raises an error if reading invalid sequence
fails:IO#codepoints does not change $_
fails:IO#codepoints calls the given block
18 changes: 0 additions & 18 deletions spec/truffle/tags/core/io/copy_stream_tags.txt
Original file line number Diff line number Diff line change
@@ -1,43 +1,25 @@
fails:IO.copy_stream from an IO does not close the source IO
fails:IO.copy_stream from an IO does not change the IO offset when an offset is specified
fails:IO.copy_stream from an IO does change the IO offset when an offset is not specified
fails:IO.copy_stream from an IO to a file name copies the entire IO contents to the file
fails:IO.copy_stream from an IO to a file name returns the number of bytes copied
fails:IO.copy_stream from an IO to a file name copies only length bytes when specified
fails:IO.copy_stream from an IO to a file name calls #to_path to convert on object to a file name
fails:IO.copy_stream from an IO to a file name copies only length bytes from the offset
fails:IO.copy_stream from an IO to an IO copies the entire IO contents to the IO
fails:IO.copy_stream from an IO to an IO returns the number of bytes copied
fails:IO.copy_stream from an IO to an IO starts writing at the destination IO's current position
fails:IO.copy_stream from an IO to an IO leaves the destination IO position at the last write
fails:IO.copy_stream from an IO to an IO does not close the destination IO
fails:IO.copy_stream from an IO to an IO copies only length bytes when specified
fails:IO.copy_stream from an IO to an IO copies only length bytes from the offset
fails:IO.copy_stream from a file name calls #to_path to convert on object to a file name
fails:IO.copy_stream from a file name to a file name copies the entire IO contents to the file
fails:IO.copy_stream from a file name to a file name returns the number of bytes copied
fails:IO.copy_stream from a file name to a file name copies only length bytes when specified
fails:IO.copy_stream from a file name to a file name calls #to_path to convert on object to a file name
fails:IO.copy_stream from a file name to a file name copies only length bytes from the offset
fails:IO.copy_stream from a file name to an IO copies the entire IO contents to the IO
fails:IO.copy_stream from a file name to an IO returns the number of bytes copied
fails:IO.copy_stream from a file name to an IO starts writing at the destination IO's current position
fails:IO.copy_stream from a file name to an IO leaves the destination IO position at the last write
fails:IO.copy_stream from a file name to an IO does not close the destination IO
fails:IO.copy_stream from a file name to an IO copies only length bytes when specified
fails:IO.copy_stream from a file name to an IO copies only length bytes from the offset
fails:IO.copy_stream from a pipe IO does not close the source IO
fails:IO.copy_stream from a pipe IO raises an error when an offset is specified
fails:IO.copy_stream from a pipe IO to a file name copies the entire IO contents to the file
fails:IO.copy_stream from a pipe IO to a file name returns the number of bytes copied
fails:IO.copy_stream from a pipe IO to a file name copies only length bytes when specified
fails:IO.copy_stream from a pipe IO to a file name calls #to_path to convert on object to a file name
fails:IO.copy_stream from a pipe IO to an IO copies the entire IO contents to the IO
fails:IO.copy_stream from a pipe IO to an IO returns the number of bytes copied
fails:IO.copy_stream from a pipe IO to an IO starts writing at the destination IO's current position
fails:IO.copy_stream from a pipe IO to an IO leaves the destination IO position at the last write
fails:IO.copy_stream from a pipe IO to an IO does not close the destination IO
fails:IO.copy_stream from a pipe IO to an IO copies only length bytes when specified
fails:IO.copy_stream with non-IO Objects calls #readpartial on the source Object if defined
fails:IO.copy_stream with non-IO Objects calls #write on the destination Object
fails:IO.copy_stream with non-IO Objects does not call #pos on the source if no offset is given
2 changes: 0 additions & 2 deletions spec/truffle/tags/core/io/each_char_tags.txt

This file was deleted.

4 changes: 0 additions & 4 deletions spec/truffle/tags/core/io/each_codepoint_tags.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1 @@
fails:IO#each_codepoint yields each codepoint
fails:IO#each_codepoint raises an error if reading invalid sequence
fails:IO#each_codepoint does not change $_
fails:IO#each_codepoint calls the given block
fails:IO#each_codepoint returns self
1 change: 0 additions & 1 deletion spec/truffle/tags/core/io/getbyte_tags.txt

This file was deleted.

1 change: 0 additions & 1 deletion spec/truffle/tags/core/io/getc_tags.txt

This file was deleted.

2 changes: 0 additions & 2 deletions spec/truffle/tags/core/io/gets_tags.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,2 @@
fails:IO#gets assigns the returned line to $_
fails:IO#gets reads limit bytes and extra bytes when limit is reached not at character boundary
fails:IO#gets read limit bytes and extra bytes with maximum of 16
fails:IO#gets transcodes to internal encoding if the IO object's external encoding is ASCII-8BIT
5 changes: 0 additions & 5 deletions spec/truffle/tags/core/io/read_nonblock_tags.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,2 @@
fails:IO#read_nonblock raises EAGAIN when there is no data
fails:IO#read_nonblock raises IO::WaitReadable when there is no data
fails:IO#read_nonblock returns at most the number of bytes requested
fails:IO#read_nonblock returns less data if that is all that is available
fails:IO#read_nonblock allows for reading 0 bytes before any write
fails:IO#read_nonblock allows for reading 0 bytes after a write
fails:IO#read_nonblock raises EOFError when the end is reached
5 changes: 0 additions & 5 deletions spec/truffle/tags/core/io/read_tags.txt
Original file line number Diff line number Diff line change
@@ -2,10 +2,5 @@ fails:IO.read from a pipe runs the rest as a subprocess and returns the standard
fails:IO.read from a pipe opens a pipe to a fork if the rest is -
fails:IO.read from a pipe reads only the specified number of bytes requested
fails:IO.read from a pipe raises Errno::ESPIPE if passed an offset
fails:IO.read with BOM reads a file with a utf-8 bom
fails:IO.read with BOM reads a file with a utf-16le bom
fails:IO.read with BOM reads a file with a utf-16be bom
fails:IO.read with BOM reads a file with a utf-32le bom
fails:IO.read with BOM reads a file with a utf-32be bom
fails(windows):IO#read on Windows normalizes line endings in text mode
fails(windows):IO#read on Windows does not normalize line endings in binary mode
1 change: 0 additions & 1 deletion spec/truffle/tags/core/io/readpartial_tags.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
fails:IO#readpartial reads after ungetc with multibyte characters in the buffer
fails:IO#readpartial reads after ungetc without data in the buffer
fails:IO#readpartial discards the existing buffer content upon successful read
fails:IO#readpartial raises EOFError on EOF
14 changes: 0 additions & 14 deletions spec/truffle/tags/core/marshal/load_tags.txt
Original file line number Diff line number Diff line change
@@ -6,27 +6,13 @@ fails:Marshal.load loads an Array with proc
fails:Marshal.load loads a 1..2
fails:Marshal.load loads a 1...2
fails:Marshal.load loads a 'a'..'b'
fails:Marshal.load loads a Fixnum -2**24
fails:Marshal.load loads a Fixnum -4516727
fails:Marshal.load loads a Fixnum -2**16
fails:Marshal.load loads a Fixnum -2**8
fails:Marshal.load loads a Fixnum -123
fails:Marshal.load loads a Fixnum -124
fails:Marshal.load loads a Random
fails:Marshal.load for an Array loads an array containing the same objects
fails:Marshal.load for an Exception loads a marshalled exception with no message
fails:Marshal.load for an Exception loads a marshalled exception with a message
fails:Marshal.load for an Exception loads a marshalled exception with a backtrace
fails:Marshal.load for a user Class raises ArgumentError if the object from an 'o' stream is not dumpable as 'o' type user class
fails:Marshal.load for a user Class that extends a core type other than Object or BasicObject raises ArgumentError if the resulting class does not extend the same type
fails:Marshal.load for a Integer loads and Integer -8
fails:Marshal.load for a Integer loads an Integer 1234
fails:Marshal.load for a Integer loads an Integer -1234
fails:Marshal.load for a Time loads
fails:Marshal.load for a Time loads serialized instance variables
fails:Marshal.load for a Time loads Time objects stored as links
fails:Marshal.load for a Time loads the zone
fails:Marshal.load for a Time loads nanoseconds
fails:Marshal.load for a wrapped C pointer loads
fails:Marshal.load for a wrapped C pointer raises ArgumentError when the local class is a regular object
fails:Marshal.load when a class with the same name as the dumped one exists outside the namespace invokes Module#const_missing
14 changes: 0 additions & 14 deletions spec/truffle/tags/core/marshal/restore_tags.txt
Original file line number Diff line number Diff line change
@@ -6,27 +6,13 @@ fails:Marshal.restore loads an Array with proc
fails:Marshal.restore loads a 1..2
fails:Marshal.restore loads a 1...2
fails:Marshal.restore loads a 'a'..'b'
fails:Marshal.restore loads a Fixnum -2**24
fails:Marshal.restore loads a Fixnum -4516727
fails:Marshal.restore loads a Fixnum -2**16
fails:Marshal.restore loads a Fixnum -2**8
fails:Marshal.restore loads a Fixnum -123
fails:Marshal.restore loads a Fixnum -124
fails:Marshal.restore loads a Random
fails:Marshal.restore for an Array loads an array containing the same objects
fails:Marshal.restore for an Exception loads a marshalled exception with no message
fails:Marshal.restore for an Exception loads a marshalled exception with a message
fails:Marshal.restore for an Exception loads a marshalled exception with a backtrace
fails:Marshal.restore for a user Class raises ArgumentError if the object from an 'o' stream is not dumpable as 'o' type user class
fails:Marshal.restore for a user Class that extends a core type other than Object or BasicObject raises ArgumentError if the resulting class does not extend the same type
fails:Marshal.restore for a Integer loads and Integer -8
fails:Marshal.restore for a Integer loads an Integer 1234
fails:Marshal.restore for a Integer loads an Integer -1234
fails:Marshal.restore for a Time loads
fails:Marshal.restore for a Time loads serialized instance variables
fails:Marshal.restore for a Time loads Time objects stored as links
fails:Marshal.restore for a Time loads the zone
fails:Marshal.restore for a Time loads nanoseconds
fails:Marshal.restore for a wrapped C pointer loads
fails:Marshal.restore for a wrapped C pointer raises ArgumentError when the local class is a regular object
fails:Marshal.restore when a class with the same name as the dumped one exists outside the namespace invokes Module#const_missing
1 change: 0 additions & 1 deletion spec/truffle/tags/core/module/class_eval_tags.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
fails:Module#class_eval uses the optional filename and lineno parameters for error messages
fails:Module#class_eval adds methods respecting the lexical constant scope
1 change: 0 additions & 1 deletion spec/truffle/tags/core/module/module_eval_tags.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
fails:Module#module_eval uses the optional filename and lineno parameters for error messages
fails:Module#module_eval adds methods respecting the lexical constant scope
1 change: 0 additions & 1 deletion spec/truffle/tags/core/module/name_tags.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
fails:Module#name is set with a conditional assignment to a nested constant
fails:Module#name preserves the encoding in which the class was defined
Empty file.
2 changes: 1 addition & 1 deletion test/jruby/test_higher_javasupport.rb
Original file line number Diff line number Diff line change
@@ -1313,7 +1313,7 @@ def test_extend_default_package_class
# JRUBY-3046
def test_java_methods_have_arity
assert_nothing_raised do
assert_equal(-1, java.lang.String.instance_method(:toString).arity)
assert java.lang.String.instance_method(:toString).arity
end
end

2 changes: 1 addition & 1 deletion tool/jt.rb
Original file line number Diff line number Diff line change
@@ -243,7 +243,7 @@ def rebuild

def run(*args)
env_vars = args.first.is_a?(Hash) ? args.shift : {}
jruby_args = %w[-X+T -Xtruffle.core.load_path=truffle/src/main/ruby]
jruby_args = ['-X+T', "-Xtruffle.core.load_path=#{JRUBY_DIR}/truffle/src/main/ruby"]

{ '--asm' => '--graal', '--igv' => '--graal' }.each_pair do |arg, dep|
args.unshift dep if args.include?(arg)
62 changes: 52 additions & 10 deletions tool/truffle/jruby_truffle_runner/README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,53 @@
# jruby+truffle_runner gem

- Use `jt install-tool` or add alias to the gem's executable,
e.g. `~/Workspace/labs/jruby/tool/truffle/jruby_truffle_runner/bin/jruby+truffle`
- `git clone foo` project/gem you would like to run in JRuby+Truffle
- `cd foo`
- You need other Ruby (MRI or JRuby) to run the tool.
- `jruby+truffle setup` to install required gems and prepare environment to run on JRuby+Truffle
- `jruby+truffle setup a_file` to execute files in prepared environment of the gem
- For more information see `jruby+truffle --help`
# JRuby+Truffle Runner

`jruby+truffle_runner` gem is a small command line utility designed to run and test Ruby gems and applications
on JRuby+Truffle Ruby runtime. It uses other Ruby implementation to prepare environment and to execute files, tests
on JRuby+Truffle. It is a temporary tool to make working with JRuby+Truffle easy until it fully supports
`rubygems` and `bundler`

## Installation

There are 3 options.

1. Install the gem from rubygems.org (Not yet published).
Install the gem `gem install jruby+truffle_runner`
2. Use `jt` tool from JRuby's repository.
Run `jt install-tool` to install the gem from the cloned repository.
3. Create alias for the gem's executable. E.g. add
`alias jruby+truffle="~/path-to-jruby/tool/truffle/jruby_truffle_runner/bin/jruby+truffle" to your `.zshrc`.

Then run `jruby+truffle --help` to see available subcommands.

## Setup

There is a `setup` subcommand to create environment for JRuby+Truffle.

- Go to directory of a gem/application you would like to test.
- Run `jruby+truffle setup`

It copies default configuration (part of the gem) if one is available for a given gem (looks for a `gemspec` in current
directory). It installs all required gems (based on `Gemfile`) to local bundle (default path: `.jruby+truffle_bundle`),
and executes other steps defined in configuration file (`.jruby+truffle.yaml`) or as command line options
(see `jruby+truffle setup --help` to learn what additional setup steps are available, or see one of the bundled
configuration files found in `gem_configurations` directory).

## Running

After the environment is set the gem can be used to execute code, files, or gem's executables on JRuby+Truffle
in prepared environment. Examples follows (options after `--` are interpreted by Ruby, options before `--` are options
for this tool):

- `jruby+truffle run -- file.rb` - executes file.rb
- `jruby+truffle run -- -e '1+1'` - evaluates 1+1 expresion
- `jruby+truffle run -- -I test test/a_test_file_test.rb` - runs test_file
- `jruby+truffle run -S rspec -- spec/a_spec_file_spec.rb` - runs a spec file using executable of rspec gem
- `jruby+truffle run --require mocks -- file.rb` - executes file.rb, but requires mocks first.
(mocks can be made to load always by putting the option to configuration file instead)

See `jruby+truffle run --help` to see all available options.

## Clean up

To remove all files added during setup phase run `jruby+truffle clean`, it'll only keep `.jruby+truffle.yaml`
configuration file for future re-setup.

Original file line number Diff line number Diff line change
@@ -7,14 +7,6 @@
:file:
shims.rb: |
require 'minitest'
# mock diff, it calls not fully supported Kernel#system
module Minitest
module Assertions
def self.diff
nil
end
end
end
# mock load_plugins as it loads rubygems
def Minitest.load_plugins
end
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@
:setup:
:file:
shims.rb: |
# stub methods caling #system
# stub methods calling #system
module Concurrent
module Utility
class ProcessorCounter
Original file line number Diff line number Diff line change
@@ -13,6 +13,8 @@
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.api.source.SourceSection;
import com.oracle.truffle.api.utilities.ConditionProfile;

import org.jruby.truffle.nodes.RubyNode;
import org.jruby.truffle.runtime.RubyArguments;
import org.jruby.truffle.runtime.RubyContext;
@@ -31,6 +33,8 @@ public class CheckArityNode extends RubyNode {
private final String[] keywords;
private final boolean keywordsRest;

private final ConditionProfile optimizedKeywordArgsProfile = ConditionProfile.createBinaryProfile();

public CheckArityNode(RubyContext context, SourceSection sourceSection, Arity arity) {
this(context, sourceSection, arity, new String[]{}, false);
}
@@ -48,10 +52,8 @@ public void executeVoid(VirtualFrame frame) {
final int given;
final DynamicObject keywordArguments;

//TODO (MS): Check merge
if (RubyArguments.isKwOptimized(frame.getArguments())) {
given = RubyArguments.getUserArgumentsCount(frame.getArguments())
- arity.getKeywordsCount() - 2;
if (optimizedKeywordArgsProfile.profile(RubyArguments.isKwOptimized(frame.getArguments()))) {
given = RubyArguments.getUserArgumentsCount(frame.getArguments()) - arity.getKeywordsCount() - 2;
} else {
given = RubyArguments.getUserArgumentsCount(frame.getArguments());
}
Original file line number Diff line number Diff line change
@@ -30,8 +30,8 @@ public class ReadKeywordArgumentNode extends RubyNode {
private final int kwIndex;
private final ValueProfile argumentValueProfile = ValueProfile.createPrimitiveProfile();

private ConditionProfile optimizedProfile = ConditionProfile.createBinaryProfile();
private ConditionProfile defaultProfile = ConditionProfile.createBinaryProfile();
private final ConditionProfile optimizedProfile = ConditionProfile.createBinaryProfile();
private final ConditionProfile defaultProfile = ConditionProfile.createBinaryProfile();

@Child private RubyNode defaultValue;

Original file line number Diff line number Diff line change
@@ -17,9 +17,11 @@
import com.oracle.truffle.api.nodes.InvalidAssumptionException;
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.api.source.SourceSection;

import org.jcodings.specific.UTF8Encoding;
import org.jruby.RubyString;
import org.jruby.truffle.nodes.RubyNode;
import org.jruby.truffle.nodes.core.ProcNodes;
import org.jruby.truffle.runtime.RubyArguments;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.layouts.Layouts;
@@ -87,7 +89,7 @@ public void trace(VirtualFrame frame) {
};

try {
callNode.call(frame, RubyArguments.pack(Layouts.PROC.getMethod(traceFunc), Layouts.PROC.getDeclarationFrame(traceFunc), Layouts.PROC.getSelf(traceFunc), Layouts.PROC.getBlock(traceFunc), args));
callNode.call(frame, ProcNodes.packArguments(traceFunc, args));
} finally {
context.getTraceManager().setInTraceFunc(false);
}
Original file line number Diff line number Diff line change
@@ -66,6 +66,11 @@ public double add(double a, DynamicObject b) {
return a + Layouts.BIGNUM.getValue(b).doubleValue();
}

// TODO (pitr 14-Sep-2015): all coerces should be replaced with a CallDispatchHeadNodes to speed up things like `5 + rational`
@Specialization(guards = "!isRubyBignum(b)")
public Object addCoerced(VirtualFrame frame, double a, DynamicObject b) {
return ruby(frame, "redo_coerced :+, b", "b", b);
}
}

@CoreMethod(names = "-", required = 1)
@@ -215,7 +220,7 @@ public Object div(VirtualFrame frame, double a, Object b) {
@CoreMethod(names = "%", required = 1)
public abstract static class ModNode extends CoreMethodArrayArgumentsNode {

private ConditionProfile lessThanZeroProfile = ConditionProfile.createBinaryProfile();
private final ConditionProfile lessThanZeroProfile = ConditionProfile.createBinaryProfile();

public ModNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
@@ -247,6 +252,11 @@ public double mod(double a, DynamicObject b) {
return mod(a, Layouts.BIGNUM.getValue(b).doubleValue());
}

@Specialization(guards = "!isRubyBignum(b)")
public Object modCoerced(VirtualFrame frame, double a, DynamicObject b) {
return ruby(frame, "redo_coerced :mod, b", "b", b);
}

}

@CoreMethod(names = "divmod", required = 1)
@@ -274,6 +284,11 @@ public DynamicObject divMod(double a, DynamicObject b) {
return divModNode.execute(a, Layouts.BIGNUM.getValue(b));
}

@Specialization(guards = "!isRubyBignum(b)")
public Object divModCoerced(VirtualFrame frame, double a, DynamicObject b) {
return ruby(frame, "redo_coerced :divmod, b", "b", b);
}

}

@CoreMethod(names = "<", required = 1)
81 changes: 20 additions & 61 deletions truffle/src/main/java/org/jruby/truffle/nodes/core/KernelNodes.java
Original file line number Diff line number Diff line change
@@ -126,7 +126,7 @@ public DynamicObject backtick(VirtualFrame frame, DynamicObject command) {

try {
// We need to run via bash to get the variable and other expansion we expect
process = Runtime.getRuntime().exec(new String[]{"bash", "-c", command.toString()}, envp.toArray(new String[envp.size()]));
process = Runtime.getRuntime().exec(new String[]{ "bash", "-c", command.toString() }, envp.toArray(new String[envp.size()]));
} catch (IOException e) {
throw new RuntimeException(e);
}
@@ -232,14 +232,14 @@ public boolean notMatch(VirtualFrame frame, Object self, Object other) {

}

@CoreMethod(names = {"<=>"}, required = 1)
@CoreMethod(names = { "<=>" }, required = 1)
public abstract static class CompareNode extends CoreMethodArrayArgumentsNode {

@Child private SameOrEqualNode equalNode;

public CompareNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
equalNode = SameOrEqualNodeFactory.create(context, sourceSection, new RubyNode[]{null, null});
equalNode = SameOrEqualNodeFactory.create(context, sourceSection, new RubyNode[]{ null, null });
}

@Specialization
@@ -326,7 +326,7 @@ public DynamicObject calleeName() {
}
}

@CoreMethod(names = "caller_locations", isModuleFunction = true, optional = 2, lowerFixnumParameters = {0, 1})
@CoreMethod(names = "caller_locations", isModuleFunction = true, optional = 2, lowerFixnumParameters = { 0, 1 })
public abstract static class CallerLocationsNode extends CoreMethodArrayArgumentsNode {

public CallerLocationsNode(RubyContext context, SourceSection sourceSection) {
@@ -498,7 +498,8 @@ public EvalNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}

@CreateCast("source") public RubyNode coerceSourceToString(RubyNode source) {
@CreateCast("source")
public RubyNode coerceSourceToString(RubyNode source) {
return ToStrNodeGen.create(getContext(), getSourceSection(), source);
}

@@ -597,7 +598,7 @@ public Object evalBinding(DynamicObject source, DynamicObject binding, NotProvid
@Specialization(guards = {
"isRubyString(source)",
"isRubyBinding(binding)",
"isRubyString(filename)"})
"isRubyString(filename)" })
public Object evalBindingFilename(DynamicObject source, DynamicObject binding, DynamicObject filename,
NotProvided lineNumber) {
return getContext().eval(Layouts.STRING.getByteList(source), binding, false, filename.toString(), this);
@@ -607,7 +608,7 @@ public Object evalBindingFilename(DynamicObject source, DynamicObject binding, D
@Specialization(guards = {
"isRubyString(source)",
"isRubyBinding(binding)",
"isRubyString(filename)"})
"isRubyString(filename)" })
public Object evalBindingFilenameLine(DynamicObject source, DynamicObject binding, DynamicObject filename,
int lineNumber) {
return getContext().eval(Layouts.STRING.getByteList(source), binding, false, filename.toString(), this);
@@ -616,7 +617,7 @@ public Object evalBindingFilenameLine(DynamicObject source, DynamicObject bindin
@TruffleBoundary
@Specialization(guards = {
"isRubyString(source)",
"!isRubyBinding(badBinding)"})
"!isRubyBinding(badBinding)" })
public Object evalBadBinding(DynamicObject source, DynamicObject badBinding, NotProvided filename,
NotProvided lineNumber) {
throw new RaiseException(getContext().getCoreLibrary().typeErrorWrongArgumentType(badBinding, "binding", this));
@@ -1044,7 +1045,7 @@ public Object instanceVariableSetSymbol(DynamicObject object, DynamicObject name

}

@CoreMethod(names = {"instance_variables", "__instance_variables__"})
@CoreMethod(names = { "instance_variables", "__instance_variables__" })
public abstract static class InstanceVariablesNode extends CoreMethodArrayArgumentsNode {

public InstanceVariablesNode(RubyContext context, SourceSection sourceSection) {
@@ -1072,7 +1073,7 @@ public DynamicObject instanceVariables(DynamicObject self) {

}

@CoreMethod(names = {"is_a?", "kind_of?"}, required = 1)
@CoreMethod(names = { "is_a?", "kind_of?" }, required = 1)
public abstract static class IsANode extends CoreMethodArrayArgumentsNode {

@Child MetaClassNode metaClassNode;
@@ -1084,14 +1085,14 @@ public IsANode(RubyContext context, SourceSection sourceSection) {

public abstract boolean executeIsA(VirtualFrame frame, Object self, DynamicObject rubyClass);

@Specialization(guards = {"isNil(nil)", "!isRubyModule(nil)"})
@Specialization(guards = { "isNil(nil)", "!isRubyModule(nil)" })
public boolean isANil(DynamicObject self, Object nil) {
return false;
}

@Specialization(
limit = "getCacheLimit()",
guards = {"isRubyModule(module)", "getMetaClass(frame, self) == cachedMetaClass", "module == cachedModule"},
guards = { "isRubyModule(module)", "getMetaClass(frame, self) == cachedMetaClass", "module == cachedModule" },
assumptions = "getUnmodifiedAssumption(cachedModule)")
public boolean isACached(VirtualFrame frame,
Object self,
@@ -1288,7 +1289,7 @@ public RubyNode coerceToBoolean(RubyNode regular) {

@Specialization(guards = "regular")
public DynamicObject methodsRegular(VirtualFrame frame, Object self, boolean regular,
@Cached("createMetaClassNode()") MetaClassNode metaClassNode) {
@Cached("createMetaClassNode()") MetaClassNode metaClassNode) {
final DynamicObject metaClass = metaClassNode.executeMetaClass(frame, self);

CompilerDirectives.transferToInterpreter();
@@ -1298,7 +1299,7 @@ public DynamicObject methodsRegular(VirtualFrame frame, Object self, boolean reg

@Specialization(guards = "!regular")
public DynamicObject methodsSingleton(VirtualFrame frame, Object self, boolean regular,
@Cached("createSingletonMethodsNode()") SingletonMethodsNode singletonMethodsNode) {
@Cached("createSingletonMethodsNode()") SingletonMethodsNode singletonMethodsNode) {
return singletonMethodsNode.executeSingletonMethods(frame, self, false);
}

@@ -1871,7 +1872,7 @@ public Long block() throws InterruptedException {

}

@CoreMethod(names = {"format", "sprintf"}, isModuleFunction = true, rest = true, required = 1, taintFromParameter = 0)
@CoreMethod(names = { "format", "sprintf" }, isModuleFunction = true, rest = true, required = 1, taintFromParameter = 0)
@ImportStatic(StringCachingGuards.class)
public abstract static class FormatNode extends CoreMethodArrayArgumentsNode {

@@ -1891,7 +1892,7 @@ public DynamicObject formatCached(
final PackResult result;

try {
result = (PackResult) callPackNode.call(frame, new Object[] { arguments, arguments.length });
result = (PackResult) callPackNode.call(frame, new Object[]{ arguments, arguments.length });
} catch (PackException e) {
CompilerDirectives.transferToInterpreter();
throw handleException(e);
@@ -1909,7 +1910,7 @@ public DynamicObject formatUncached(
final PackResult result;

try {
result = (PackResult) callPackNode.call(frame, compileFormat(format), new Object[] { arguments, arguments.length });
result = (PackResult) callPackNode.call(frame, compileFormat(format), new Object[]{ arguments, arguments.length });
} catch (PackException e) {
CompilerDirectives.transferToInterpreter();
throw handleException(e);
@@ -1983,48 +1984,6 @@ protected CallTarget compileFormat(DynamicObject format) {

}

@CoreMethod(names = "system", isModuleFunction = true, needsSelf = false, required = 1)
public abstract static class SystemNode extends CoreMethodArrayArgumentsNode {

@Child private CallDispatchHeadNode toHashNode;

public SystemNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}

@Specialization(guards = "isRubyString(command)")
public boolean system(VirtualFrame frame, DynamicObject command) {
if (toHashNode == null) {
CompilerDirectives.transferToInterpreter();
toHashNode = insert(DispatchHeadNodeFactory.createMethodCall(getContext()));
}

CompilerDirectives.transferToInterpreter();

// TODO(CS 5-JAN-15): very simplistic implementation

final DynamicObject env = getContext().getCoreLibrary().getENV();
final DynamicObject envAsHash = (DynamicObject) toHashNode.call(frame, env, "to_hash", null);

final List<String> envp = new ArrayList<>();

// TODO(CS): cast
for (Map.Entry<Object, Object> keyValue : HashOperations.iterableKeyValues(envAsHash)) {
envp.add(keyValue.getKey().toString() + "=" + keyValue.getValue().toString());
}

// We need to run via bash to get the variable and other expansion we expect
try {
Runtime.getRuntime().exec(new String[]{"bash", "-c", command.toString()}, envp.toArray(new String[envp.size()]));
} catch (IOException e) {
throw new RuntimeException(e);
}

return true;
}

}

@CoreMethod(names = "taint")
public abstract static class KernelTaintNode extends CoreMethodArrayArgumentsNode {

@@ -2100,8 +2059,8 @@ public abstract static class ToSNode extends CoreMethodArrayArgumentsNode {
public ToSNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
classNode = ClassNodeGen.create(context, sourceSection, null);
objectIDNode = ObjectPrimitiveNodesFactory.ObjectIDPrimitiveNodeFactory.create(context, sourceSection, new RubyNode[] { null });
toHexStringNode = KernelNodesFactory.ToHexStringNodeFactory.create(context, sourceSection, new RubyNode[]{null});
objectIDNode = ObjectPrimitiveNodesFactory.ObjectIDPrimitiveNodeFactory.create(context, sourceSection, new RubyNode[]{ null });
toHexStringNode = KernelNodesFactory.ToHexStringNodeFactory.create(context, sourceSection, new RubyNode[]{ null });
}

public abstract DynamicObject executeToS(VirtualFrame frame, Object self);
Original file line number Diff line number Diff line change
@@ -47,15 +47,19 @@
@CoreClass(name = "Proc")
public abstract class ProcNodes {

public static Object rootCall(DynamicObject proc, Object... args) {
assert RubyGuards.isRubyProc(proc);

return Layouts.PROC.getCallTargetForType(proc).call(RubyArguments.pack(
public static Object[] packArguments(DynamicObject proc, Object... args) {
return RubyArguments.pack(
Layouts.PROC.getMethod(proc),
Layouts.PROC.getDeclarationFrame(proc),
Layouts.PROC.getSelf(proc),
Layouts.PROC.getBlock(proc),
args));
args);
}

public static Object rootCall(DynamicObject proc, Object... args) {
assert RubyGuards.isRubyProc(proc);

return Layouts.PROC.getCallTargetForType(proc).call(packArguments(proc, args));
}

public static DynamicObject createRubyProc(DynamicObject procClass, Type type, SharedMethodInfo sharedMethodInfo, CallTarget callTargetForProcs,
Original file line number Diff line number Diff line change
@@ -1660,7 +1660,7 @@ public int size(DynamicObject string) {
@CoreMethod(names = "squeeze!", rest = true, raiseIfFrozenSelf = true)
public abstract static class SqueezeBangNode extends CoreMethodArrayArgumentsNode {

private ConditionProfile singleByteOptimizableProfile = ConditionProfile.createBinaryProfile();
private final ConditionProfile singleByteOptimizableProfile = ConditionProfile.createBinaryProfile();

@Child private ToStrNode toStrNode;

Original file line number Diff line number Diff line change
@@ -22,6 +22,7 @@
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.api.source.Source;
import com.oracle.truffle.api.source.SourceSection;
import jnr.posix.SpawnFileAction;
import org.jcodings.specific.UTF8Encoding;
import org.jruby.Ruby;
import org.jruby.RubyGC;
@@ -38,6 +39,7 @@
import org.jruby.truffle.runtime.control.RaiseException;
import org.jruby.truffle.runtime.core.ArrayOperations;
import org.jruby.truffle.runtime.core.CoreLibrary;
import org.jruby.truffle.runtime.core.StringOperations;
import org.jruby.truffle.runtime.hash.BucketsStrategy;
import org.jruby.truffle.runtime.layouts.Layouts;
import org.jruby.truffle.runtime.methods.InternalMethod;
@@ -47,6 +49,12 @@
import org.jruby.util.StringSupport;

import java.util.*;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Collections;
import java.util.Arrays;

@CoreClass(name = "Truffle::Primitive")
public abstract class TrufflePrimitiveNodes {
@@ -626,7 +634,62 @@ public ObjectTypeOfNode(RubyContext context, SourceSection sourceSection) {
public DynamicObject objectTypeOf(DynamicObject value) {
return getSymbol(value.getShape().getObjectType().getClass().getSimpleName());
}
}

@CoreMethod(names = "spawn_process", onSingleton = true, required = 3)
public abstract static class SpawnProcess extends CoreMethodArrayArgumentsNode {

public SpawnProcess(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}

@Specialization(guards = {
"isRubyString(command)",
"isRubyArray(arguments)",
"isRubyArray(environmentVariables)" })
public int spawn(DynamicObject command,
DynamicObject arguments,
DynamicObject environmentVariables) {

final long longPid = call(
StringOperations.getString(command),
toStringArray(arguments),
toStringArray(environmentVariables));
assert longPid <= Integer.MAX_VALUE;
// VMWaitPidPrimitiveNode accepts only int
final int pid = (int) longPid;

if (pid == -1) {
// TODO (pitr 07-Sep-2015): needs compatibility improvements
throw new RaiseException(getContext().getCoreLibrary().errnoError(getContext().getPosix().errno(), this));
}

return pid;
}

private String[] toStringArray(DynamicObject rubyStrings) {
final int size = Layouts.ARRAY.getSize(rubyStrings);
final Object[] unconvertedStrings = ArrayOperations.toObjectArray(rubyStrings);
final String[] strings = new String[size];

for (int i = 0; i < size; i++) {
assert Layouts.STRING.isString(unconvertedStrings[i]);
strings[i] = StringOperations.getString((DynamicObject) unconvertedStrings[i]);
}

return strings;
}

@TruffleBoundary
private long call(String command, String[] arguments, String[] environmentVariables) {
// TODO (pitr 04-Sep-2015): only simple implementation, does not support file actions or other options
return getContext().getPosix().posix_spawnp(
command,
Collections.<SpawnFileAction>emptyList(),
Arrays.asList(arguments),
Arrays.asList(environmentVariables));

}
}

}
Original file line number Diff line number Diff line change
@@ -18,10 +18,12 @@
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.api.source.SourceSection;
import com.oracle.truffle.api.utilities.BranchProfile;

import org.jcodings.specific.USASCIIEncoding;
import org.jruby.RubyString;
import org.jruby.truffle.nodes.RubyNode;
import org.jruby.truffle.nodes.core.*;
import org.jruby.truffle.nodes.core.fixnum.FixnumNodesFactory.RightShiftNodeFactory;
import org.jruby.truffle.nodes.dispatch.CallDispatchHeadNode;
import org.jruby.truffle.nodes.dispatch.DispatchHeadNodeFactory;
import org.jruby.truffle.nodes.methods.UnsupportedOperationBehavior;
@@ -36,8 +38,6 @@
@CoreClass(name = "Fixnum")
public abstract class FixnumNodes {

private static final int BITS = 64;

@CoreMethod(names = "-@")
public abstract static class NegNode extends CoreMethodArrayArgumentsNode {

@@ -827,69 +827,54 @@ public Object bitXOr(VirtualFrame frame, Object a, DynamicObject b) {
@CoreMethod(names = "<<", required = 1, lowerFixnumParameters = 0)
public abstract static class LeftShiftNode extends BignumNodes.BignumCoreMethodNode {

@Child private RightShiftNode rightShiftNode;
@Child private CallDispatchHeadNode fallbackCallNode;

public LeftShiftNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}

protected Object lower(BigInteger value) {
return fixnumOrBignum(value);
}

public abstract Object executeLeftShift(VirtualFrame frame, Object a, Object b);

@Specialization(guards = {"isPositive(b)", "canShiftIntoInt(a, b)"})
@Specialization(guards = { "b >= 0", "canShiftIntoInt(a, b)" })
public int leftShift(int a, int b) {
return a << b;
}

@Specialization(guards = {"isPositive(b)", "canShiftIntoInt(a, b)"})
@Specialization(guards = { "b >= 0", "canShiftIntoInt(a, b)" })
public int leftShift(int a, long b) {
return a << b;
}

@Specialization(guards = "isStrictlyNegative(b)")
public int leftShiftNeg(int a, int b) {
if (-b >= Integer.SIZE) {
return 0;
} else if (b == Integer.MIN_VALUE) {
return 0;
} else {
return a >> -b;
}
}

@Specialization(guards = {"isPositive(b)", "canShiftIntoLong(a, b)"})
@Specialization(guards = { "b >= 0", "canShiftIntoLong(a, b)" })
public long leftShiftToLong(long a, int b) {
return a << b;
}

@Specialization(guards = {"isPositive(b)"})
@Specialization(guards = "b >= 0")
public Object leftShiftWithOverflow(long a, int b) {
if (canShiftIntoLong(a, b)) {
return leftShiftToLong(a, b);
} else {
return lower(BigInteger.valueOf(a).shiftLeft(b));
return fixnumOrBignum(BigInteger.valueOf(a).shiftLeft(b));
}
}

@Specialization(guards = "isStrictlyNegative(b)")
public long leftShiftNeg(long a, int b) {
if (-b >= Integer.SIZE) {
return 0;
} else {
return a >> -b;
@Specialization(guards = "b < 0")
public Object leftShiftNeg(VirtualFrame frame, long a, int b) {
if (rightShiftNode == null) {
CompilerDirectives.transferToInterpreter();
rightShiftNode = insert(RightShiftNodeFactory.create(getContext(), getSourceSection(), new RubyNode[] { null, null }));
}
return rightShiftNode.executeRightShift(frame, a, -b);
}

@Specialization(guards = {"!isInteger(b)", "!isLong(b)"})
@Specialization(guards = { "!isInteger(b)", "!isLong(b)" })
public Object leftShiftFallback(VirtualFrame frame, Object a, Object b) {
if (fallbackCallNode == null) {
CompilerDirectives.transferToInterpreter();
fallbackCallNode = insert(DispatchHeadNodeFactory.createMethodCallOnSelf(getContext()));
}

return fallbackCallNode.call(frame, a, "left_shift_fallback", null, b);
}

@@ -901,26 +886,10 @@ static boolean canShiftIntoInt(int a, long b) {
return Integer.numberOfLeadingZeros(a) - b > 0;
}

static boolean canShiftIntoLong(int a, int b) {
return canShiftIntoLong((long) a, b);
}

static boolean canShiftIntoLong(long a, int b) {
return Long.numberOfLeadingZeros(a) - b > 0;
}

static boolean isPositive(int value) {
return value >= 0;
}

static boolean isPositive(long value) {
return value >= 0;
}

static boolean isStrictlyNegative(int value) {
return value < 0;
}

}

@CoreMethod(names = ">>", required = 1, lowerFixnumParameters = 0)
@@ -933,96 +902,72 @@ public RightShiftNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}

@Specialization
public abstract Object executeRightShift(VirtualFrame frame, Object a, Object b);

@Specialization(guards = "b >= 0")
public Object rightShift(VirtualFrame frame, int a, int b) {
if (b > 0) {
if (b >= BITS - 1) {
if (a < 0) {
return -1;
} else {
return 0;
}
} else if (b == 32) {
return 0;
} else {
return a >> b;
}
if (b >= Integer.SIZE - 1) {
return a < 0 ? -1 : 0;
} else {
if (leftShiftNode == null) {
CompilerDirectives.transferToInterpreter();
leftShiftNode = insert(FixnumNodesFactory.LeftShiftNodeFactory.create(getContext(), getSourceSection(), new RubyNode[]{null, null}));
}

return leftShiftNode.executeLeftShift(frame, a, -b);
return a >> b;
}
}

@Specialization
@Specialization(guards = "b >= 0")
public Object rightShift(VirtualFrame frame, int a, long b) {
if (b > 0) {
if (b >= BITS - 1) {
if (a < 0) {
return -1;
} else {
return 0;
}
} else if (b == 32) {
return 0;
} else {
return a >> b;
}
if (b >= Integer.SIZE - 1) {
return a < 0 ? -1 : 0;
} else {
if (leftShiftNode == null) {
CompilerDirectives.transferToInterpreter();
leftShiftNode = insert(FixnumNodesFactory.LeftShiftNodeFactory.create(getContext(), getSourceSection(), new RubyNode[]{null, null}));
}

return leftShiftNode.executeLeftShift(frame, a, -b);
return a >> b;
}
}

@Specialization
@Specialization(guards = "b >= 0")
public Object rightShift(VirtualFrame frame, long a, int b) {
if (b > 0) {
if (b >= BITS - 1) {
if (a < 0) {
return -1;
} else {
return 0;
}
} else {
return a >> b;
}
if (b >= Long.SIZE - 1) {
return a < 0 ? -1 : 0;
} else {
if (leftShiftNode == null) {
CompilerDirectives.transferToInterpreter();
leftShiftNode = insert(FixnumNodesFactory.LeftShiftNodeFactory.create(getContext(), getSourceSection(), new RubyNode[]{null, null}));
}

return leftShiftNode.executeLeftShift(frame, a, -b);
return a >> b;
}
}

@Specialization(guards = "isRubyBignum(b)")
public int rightShift(int a, DynamicObject b) {
return 0;
@Specialization(guards = "b < 0")
public Object rightShiftNeg(VirtualFrame frame, long a, int b) {
if (leftShiftNode == null) {
CompilerDirectives.transferToInterpreter();
leftShiftNode = insert(FixnumNodesFactory.LeftShiftNodeFactory.create(getContext(), getSourceSection(), new RubyNode[] { null, null }));
}
return leftShiftNode.executeLeftShift(frame, a, -b);
}

@Specialization(guards = "isRubyBignum(b)")
@Specialization(guards = { "isRubyBignum(b)", "isPositive(b)" })
public int rightShift(long a, DynamicObject b) {
return 0;
}

@Specialization(guards = {"!isInteger(b)", "!isLong(b)"})
@Specialization(guards = { "isRubyBignum(b)", "!isPositive(b)" })
public Object rightShiftNeg(VirtualFrame frame, long a, DynamicObject b) {
if (leftShiftNode == null) {
CompilerDirectives.transferToInterpreter();
leftShiftNode = insert(FixnumNodesFactory.LeftShiftNodeFactory.create(getContext(), getSourceSection(), new RubyNode[] { null, null }));
}
CompilerDirectives.transferToInterpreter();
return leftShiftNode.executeLeftShift(frame, a, Layouts.BIGNUM.getValue(b).negate());
}

@Specialization(guards = { "!isInteger(b)", "!isLong(b)" })
public Object rightShiftFallback(VirtualFrame frame, Object a, Object b) {
if (fallbackCallNode == null) {
CompilerDirectives.transferToInterpreter();
fallbackCallNode = insert(DispatchHeadNodeFactory.createMethodCallOnSelf(getContext()));
}

return fallbackCallNode.call(frame, a, "right_shift_fallback", null, b);
}

protected static boolean isPositive(DynamicObject b) {
return Layouts.BIGNUM.getValue(b).signum() >= 0;
}

}

@CoreMethod(names = {"abs", "magnitude"})
Original file line number Diff line number Diff line change
@@ -1663,7 +1663,7 @@ public int doInteger(VirtualFrame frame, DynamicObject module, String name) {
@CoreMethod(names = "sign")
public abstract static class SignNode extends BigDecimalCoreMethodArrayArgumentsNode {

final private ConditionProfile positive = ConditionProfile.createBinaryProfile();
private final ConditionProfile positive = ConditionProfile.createBinaryProfile();
@Child private GetIntegerConstantNode sign;

public SignNode(RubyContext context, SourceSection sourceSection) {
Original file line number Diff line number Diff line change
@@ -9,12 +9,9 @@
*/
package org.jruby.truffle.nodes.rubinius;

import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.api.source.SourceSection;
import jnr.constants.platform.Errno;
import org.jruby.truffle.nodes.RubyGuards;
import org.jruby.truffle.runtime.RubyContext;

/**
@@ -25,122 +22,13 @@ public abstract class ExceptionPrimitiveNodes {
@RubiniusPrimitive(name = "exception_errno_error", needsSelf = false)
public static abstract class ExceptionErrnoErrorPrimitiveNode extends RubiniusPrimitiveNode {

// If you add a constant here, add it below in isExceptionSupported() too.
protected final static int EPERM = Errno.EPERM.intValue();
protected final static int ENOENT = Errno.ENOENT.intValue();
protected final static int EBADF = Errno.EBADF.intValue();
protected final static int EEXIST = Errno.EEXIST.intValue();
protected final static int EACCES = Errno.EACCES.intValue();
protected final static int EFAULT = Errno.EFAULT.intValue();
protected final static int ENOTDIR = Errno.ENOTDIR.intValue();
protected final static int EINVAL = Errno.EINVAL.intValue();
protected final static int EINPROGRESS = Errno.EINPROGRESS.intValue();
protected final static int ENOTCONN = Errno.ENOTCONN.intValue();

public static boolean isExceptionSupported(int errno) {
return errno == EPERM || errno == ENOENT || errno == EBADF || errno == EEXIST || errno == EACCES
|| errno == EFAULT || errno == ENOTDIR || errno == EINVAL || errno == EINPROGRESS
|| errno == ENOTCONN;
}

public ExceptionErrnoErrorPrimitiveNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
}

@Specialization(guards = {"isRubyString(message)", "errno == EPERM"})
public DynamicObject eperm(DynamicObject message, int errno) {
return getContext().getCoreLibrary().operationNotPermittedError(message.toString(), this);
}

@Specialization(guards = {"errno == EPERM", "isNil(message)"})
public DynamicObject eperm(Object message, int errno) {
return getContext().getCoreLibrary().operationNotPermittedError("nil", this);
}

@Specialization(guards = {"isRubyString(message)", "errno == ENOENT"})
public DynamicObject enoent(DynamicObject message, int errno) {
return getContext().getCoreLibrary().fileNotFoundError(message.toString(), this);
}

@Specialization(guards = { "errno == ENOENT", "isNil(message)" })
public DynamicObject enoent(Object message, int errno) {
return getContext().getCoreLibrary().fileNotFoundError("nil", this);
}

@Specialization(guards = { "errno == EBADF", "isNil(message)" })
public DynamicObject ebadf(Object message, int errno) {
return getContext().getCoreLibrary().badFileDescriptor(this);
}

@Specialization(guards = {"isRubyString(message)", "errno == EEXIST"})
public DynamicObject eexist(DynamicObject message, int errno) {
return getContext().getCoreLibrary().fileExistsError(message.toString(), this);
}

@Specialization(guards = { "errno == EEXIST", "isNil(message)" })
public DynamicObject eexist(Object message, int errno) {
return getContext().getCoreLibrary().fileExistsError("nil", this);
}

@Specialization(guards = {"isRubyString(message)", "errno == EACCES"})
public DynamicObject eacces(DynamicObject message, int errno) {
return getContext().getCoreLibrary().permissionDeniedError(message.toString(), this);
}

@Specialization(guards = {"errno == EACCES", "isNil(message)"})
public DynamicObject eacces(Object message, int errno) {
return getContext().getCoreLibrary().permissionDeniedError("nil", this);
}

@Specialization(guards = {"isRubyString(message)", "errno == EFAULT"})
public DynamicObject efault(DynamicObject message, int errno) {
return getContext().getCoreLibrary().badAddressError(this);
}

@Specialization(guards = {"errno == EFAULT", "isNil(message)"})
public DynamicObject efault(Object message, int errno) {
return getContext().getCoreLibrary().badAddressError(this);
}

@Specialization(guards = {"isRubyString(message)", "errno == ENOTDIR"})
public DynamicObject enotdir(DynamicObject message, int errno) {
return getContext().getCoreLibrary().notDirectoryError(message.toString(), this);
}

@Specialization(guards = {"isRubyString(message)", "errno == EINVAL"})
public DynamicObject einval(DynamicObject message, int errno) {
return getContext().getCoreLibrary().errnoError(errno, this);
}

@Specialization(guards = {"isRubyString(message)", "errno == EINPROGRESS"})
public DynamicObject einprogress(DynamicObject message, int errno) {
return getContext().getCoreLibrary().errnoError(errno, this);
}

@Specialization(guards = {"isRubyString(message)", "errno == ENOTCONN"})
public DynamicObject enotconn(DynamicObject message, int errno) {
return getContext().getCoreLibrary().errnoError(errno, this);
}

@TruffleBoundary
@Specialization(guards = "!isExceptionSupported(errno)")
public DynamicObject unsupported(Object message, int errno) {
final Errno errnoObject = Errno.valueOf(errno);

final String messageString;
if (RubyGuards.isRubyString(message)) {
messageString = message.toString();
} else if (message == nil()) {
messageString = "nil";
} else {
messageString = "unsupported message type";
}

if (errnoObject == null) {
throw new UnsupportedOperationException("errno: " + errno + " " + messageString);
} else {
throw new UnsupportedOperationException("errno: " + errnoObject.name());
}
@Specialization
public DynamicObject exceptionErrnoError(DynamicObject message, int errno) {
return getContext().getCoreLibrary().errnoError(errno, message.toString(), this);
}

}
Original file line number Diff line number Diff line change
@@ -557,6 +557,16 @@ public DynamicObject sysread(VirtualFrame frame, DynamicObject file, int length)
getContext().getSafepointManager().poll(this);

final int readIteration = posix().read(fd, buffer, toRead);

if (readIteration == -1) {
CompilerDirectives.transferToInterpreter();
throw new RaiseException(getContext().getCoreLibrary().errnoError(posix().errno(), this));
}

if (readIteration == 0) {
return nil();
}

buffer.position(readIteration);
toRead -= readIteration;
}
Original file line number Diff line number Diff line change
@@ -217,7 +217,7 @@ public static abstract class VMObjectKindOfPrimitiveNode extends RubiniusPrimiti

public VMObjectKindOfPrimitiveNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
isANode = KernelNodesFactory.IsANodeFactory.create(context, sourceSection, new RubyNode[] { null, null });
isANode = KernelNodesFactory.IsANodeFactory.create(context, sourceSection, new RubyNode[]{ null, null });
}

@Specialization(guards = "isRubyModule(rubyClass)")
@@ -251,7 +251,7 @@ public static abstract class VMObjectSingletonClassPrimitiveNode extends Rubiniu

public VMObjectSingletonClassPrimitiveNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
singletonClassNode = KernelNodesFactory.SingletonClassMethodNodeFactory.create(context, sourceSection, new RubyNode[] { null });
singletonClassNode = KernelNodesFactory.SingletonClassMethodNodeFactory.create(context, sourceSection, new RubyNode[]{ null });
}

@Specialization
@@ -345,15 +345,15 @@ public DynamicObject times() {
double utime = 0.0d, stime = 0.0d, cutime = 0.0d, cstime = 0.0d;
if (tms == null) {
ThreadMXBean bean = ManagementFactory.getThreadMXBean();
if(bean.isCurrentThreadCpuTimeSupported()) {
if (bean.isCurrentThreadCpuTimeSupported()) {
cutime = utime = bean.getCurrentThreadUserTime();
cstime = stime = bean.getCurrentThreadCpuTime() - bean.getCurrentThreadUserTime();
}
} else {
utime = (double)tms.utime();
stime = (double)tms.stime();
cutime = (double)tms.cutime();
cstime = (double)tms.cstime();
utime = (double) tms.utime();
stime = (double) tms.stime();
cutime = (double) tms.cutime();
cstime = (double) tms.cstime();
}

long hz = posix().sysconf(Sysconf._SC_CLK_TCK);
@@ -371,13 +371,13 @@ public DynamicObject times() {
final double tstime = 0;

return Layouts.ARRAY.createArray(getContext().getCoreLibrary().getArrayFactory(), new double[]{
utime,
stime,
cutime,
cstime,
tutime,
tstime
}, 6);
utime,
stime,
cutime,
cstime,
tutime,
tstime
}, 6);
}

}
@@ -389,7 +389,7 @@ public VMWatchSignalPrimitiveNode(RubyContext context, SourceSection sourceSecti
super(context, sourceSection);
}

@Specialization(guards = {"isRubyString(signalName)", "isRubyString(action)"})
@Specialization(guards = { "isRubyString(signalName)", "isRubyString(action)" })
public boolean watchSignal(DynamicObject signalName, DynamicObject action) {
if (!action.toString().equals("DEFAULT")) {
throw new UnsupportedOperationException();
@@ -400,14 +400,14 @@ public boolean watchSignal(DynamicObject signalName, DynamicObject action) {
return true;
}

@Specialization(guards = {"isRubyString(signalName)", "isNil(nil)"})
@Specialization(guards = { "isRubyString(signalName)", "isNil(nil)" })
public boolean watchSignal(DynamicObject signalName, Object nil) {
Signal signal = new Signal(signalName.toString());
SignalOperations.watchSignal(signal, SignalOperations.IGNORE_HANDLER);
return true;
}

@Specialization(guards = {"isRubyString(signalName)", "isRubyProc(proc)"})
@Specialization(guards = { "isRubyString(signalName)", "isRubyProc(proc)" })
public boolean watchSignalProc(DynamicObject signalName, DynamicObject proc) {
Signal signal = new Signal(signalName.toString());
SignalOperations.watchSignal(signal, new ProcSignalHandler(getContext(), proc));
@@ -459,7 +459,15 @@ public DynamicObject getSection(DynamicObject section) {
stringValue = value.toString();
}

Object[] objects = new Object[]{Layouts.STRING.createString(getContext().getCoreLibrary().getStringFactory(), RubyString.encodeBytelist(key, UTF8Encoding.INSTANCE), StringSupport.CR_UNKNOWN, null), Layouts.STRING.createString(getContext().getCoreLibrary().getStringFactory(), RubyString.encodeBytelist(stringValue, UTF8Encoding.INSTANCE), StringSupport.CR_UNKNOWN, null)};
Object[] objects = new Object[]{
Layouts.STRING.createString(
getContext().getCoreLibrary().getStringFactory(),
RubyString.encodeBytelist(key, UTF8Encoding.INSTANCE),
StringSupport.CR_UNKNOWN, null),
Layouts.STRING.createString(
getContext().getCoreLibrary().getStringFactory(),
RubyString.encodeBytelist(stringValue, UTF8Encoding.INSTANCE),
StringSupport.CR_UNKNOWN, null) };
sectionKeyValues.add(Layouts.ARRAY.createArray(getContext().getCoreLibrary().getArrayFactory(), objects, objects.length));
}

@@ -482,7 +490,7 @@ public Object waitPID(final int input_pid, boolean no_hang) {
// Transliterated from Rubinius C++ - not tidied up significantly to make merging changes easier

int options = 0;
final int[] statusReference = new int[] { 0 };
final int[] statusReference = new int[]{ 0 };
int pid;

if (no_hang) {
@@ -533,7 +541,7 @@ public Integer block() throws InterruptedException {
stopsig = PosixShim.WAIT_MACROS.WSTOPSIG(status);
}

Object[] objects = new Object[]{output, termsig, stopsig, pid};
Object[] objects = new Object[]{ output, termsig, stopsig, pid };
return Layouts.ARRAY.createArray(getContext().getCoreLibrary().getArrayFactory(), objects, objects.length);
}

Original file line number Diff line number Diff line change
@@ -75,7 +75,7 @@ public final Object execute(VirtualFrame frame) {
if (block != null) {
blockObject = procOrNullNode.executeProcOrNull(block.execute(frame));
} else {
blockObject = null;
blockObject = RubyArguments.getBlock(frame.getArguments());
}

final Object[] argumentsArray;
Original file line number Diff line number Diff line change
@@ -910,6 +910,18 @@ public DynamicObject errnoError(int errno, Node currentNode) {
return ExceptionNodes.createRubyException(getErrnoClass(errnoObj), Layouts.STRING.createString(Layouts.CLASS.getInstanceFactory(stringClass), RubyString.encodeBytelist(errnoObj.description(), UTF8Encoding.INSTANCE), StringSupport.CR_UNKNOWN, null), RubyCallStack.getBacktrace(currentNode));
}

public DynamicObject errnoError(int errno, String message, Node currentNode) {
CompilerAsserts.neverPartOfCompilation();

Errno errnoObj = Errno.valueOf(errno);
if (errnoObj == null) {
return systemCallError(String.format("Unknown Error (%s) - %s", errno, message), currentNode);
}

final DynamicObject errorMessage = Layouts.STRING.createString(Layouts.CLASS.getInstanceFactory(stringClass), RubyString.encodeBytelist(String.format("%s - %s", errnoObj.description(), message), UTF8Encoding.INSTANCE), StringSupport.CR_UNKNOWN, null);
return ExceptionNodes.createRubyException(getErrnoClass(errnoObj), errorMessage, RubyCallStack.getBacktrace(currentNode));
}

public DynamicObject indexError(String message, Node currentNode) {
CompilerAsserts.neverPartOfCompilation();
return ExceptionNodes.createRubyException(indexErrorClass, Layouts.STRING.createString(Layouts.CLASS.getInstanceFactory(stringClass), RubyString.encodeBytelist(message, UTF8Encoding.INSTANCE), StringSupport.CR_UNKNOWN, null), RubyCallStack.getBacktrace(currentNode));
@@ -920,11 +932,6 @@ public DynamicObject indexTooSmallError(String type, int index, int length, Node
return indexError(String.format("index %d too small for %s; minimum: -%d", index, type, length), currentNode);
}

public DynamicObject indexNegativeLength(int length, Node currentNode) {
CompilerAsserts.neverPartOfCompilation();
return indexError(String.format("negative length (%d)", length), currentNode);
}

public DynamicObject localJumpError(String message, Node currentNode) {
CompilerAsserts.neverPartOfCompilation();
return ExceptionNodes.createRubyException(localJumpErrorClass, Layouts.STRING.createString(Layouts.CLASS.getInstanceFactory(stringClass), RubyString.encodeBytelist(message, UTF8Encoding.INSTANCE), StringSupport.CR_UNKNOWN, null), RubyCallStack.getBacktrace(currentNode));
@@ -966,11 +973,6 @@ public DynamicObject typeErrorCantDefineSingleton(Node currentNode) {
return typeError("can't define singleton", currentNode);
}

public DynamicObject typeErrorNoClassToMakeAlias(Node currentNode) {
CompilerAsserts.neverPartOfCompilation();
return typeError("no class to make alias", currentNode);
}

public DynamicObject typeErrorShouldReturn(String object, String method, String expectedType, Node currentNode) {
CompilerAsserts.neverPartOfCompilation();
return typeError(String.format("%s#%s should return %s", object, method, expectedType), currentNode);
@@ -1016,11 +1018,6 @@ public DynamicObject typeErrorBadCoercion(Object from, String to, String coercio
Layouts.MODULE.getFields(getLogicalClass(coercedTo)).getName()), currentNode);
}

public DynamicObject typeErrorCantCoerce(Object from, String to, Node currentNode) {
CompilerAsserts.neverPartOfCompilation();
return typeError(String.format("%s can't be coerced into %s", from, to), currentNode);
}

public DynamicObject typeErrorCantDump(Object object, Node currentNode) {
CompilerAsserts.neverPartOfCompilation();
String logicalClass = Layouts.MODULE.getFields(getLogicalClass(object)).getName();
@@ -1186,57 +1183,11 @@ public DynamicObject mathDomainError(String method, Node currentNode) {
return ExceptionNodes.createRubyException(getErrnoClass(Errno.EDOM), Layouts.STRING.createString(Layouts.CLASS.getInstanceFactory(stringClass), RubyString.encodeBytelist(String.format("Numerical argument is out of domain - \"%s\"", method), UTF8Encoding.INSTANCE), StringSupport.CR_UNKNOWN, null), RubyCallStack.getBacktrace(currentNode));
}

public DynamicObject invalidArgumentError(String value, Node currentNode) {
CompilerAsserts.neverPartOfCompilation();
return ExceptionNodes.createRubyException(getErrnoClass(Errno.EINVAL), Layouts.STRING.createString(Layouts.CLASS.getInstanceFactory(stringClass), RubyString.encodeBytelist(String.format("Invalid argument - %s", value), UTF8Encoding.INSTANCE), StringSupport.CR_UNKNOWN, null), RubyCallStack.getBacktrace(currentNode));
}

public DynamicObject ioError(String fileName, Node currentNode) {
CompilerAsserts.neverPartOfCompilation();
return ExceptionNodes.createRubyException(ioErrorClass, Layouts.STRING.createString(Layouts.CLASS.getInstanceFactory(stringClass), RubyString.encodeBytelist(String.format("Error reading file - %s", fileName), UTF8Encoding.INSTANCE), StringSupport.CR_UNKNOWN, null), RubyCallStack.getBacktrace(currentNode));
}

public DynamicObject badAddressError(Node currentNode) {
CompilerAsserts.neverPartOfCompilation();
return ExceptionNodes.createRubyException(getErrnoClass(Errno.EFAULT), Layouts.STRING.createString(Layouts.CLASS.getInstanceFactory(stringClass), RubyString.encodeBytelist("Bad address", UTF8Encoding.INSTANCE), StringSupport.CR_UNKNOWN, null), RubyCallStack.getBacktrace(currentNode));
}


public DynamicObject badFileDescriptor(Node currentNode) {
CompilerAsserts.neverPartOfCompilation();
return ExceptionNodes.createRubyException(getErrnoClass(Errno.EBADF), Layouts.STRING.createString(Layouts.CLASS.getInstanceFactory(stringClass), RubyString.encodeBytelist("Bad file descriptor", UTF8Encoding.INSTANCE), StringSupport.CR_UNKNOWN, null), RubyCallStack.getBacktrace(currentNode));
}

public DynamicObject fileExistsError(String fileName, Node currentNode) {
CompilerAsserts.neverPartOfCompilation();
return ExceptionNodes.createRubyException(getErrnoClass(Errno.EEXIST), Layouts.STRING.createString(Layouts.CLASS.getInstanceFactory(stringClass), RubyString.encodeBytelist(String.format("File exists - %s", fileName), UTF8Encoding.INSTANCE), StringSupport.CR_UNKNOWN, null), RubyCallStack.getBacktrace(currentNode));
}

public DynamicObject fileNotFoundError(String fileName, Node currentNode) {
CompilerAsserts.neverPartOfCompilation();
return ExceptionNodes.createRubyException(getErrnoClass(Errno.ENOENT), Layouts.STRING.createString(Layouts.CLASS.getInstanceFactory(stringClass), RubyString.encodeBytelist(String.format("No such file or directory - %s", fileName), UTF8Encoding.INSTANCE), StringSupport.CR_UNKNOWN, null), RubyCallStack.getBacktrace(currentNode));
}

public DynamicObject dirNotEmptyError(String path, Node currentNode) {
CompilerAsserts.neverPartOfCompilation();
return ExceptionNodes.createRubyException(getErrnoClass(Errno.ENOTEMPTY), Layouts.STRING.createString(Layouts.CLASS.getInstanceFactory(stringClass), RubyString.encodeBytelist(String.format("Directory not empty - %s", path), UTF8Encoding.INSTANCE), StringSupport.CR_UNKNOWN, null), RubyCallStack.getBacktrace(currentNode));
}

public DynamicObject operationNotPermittedError(String path, Node currentNode) {
CompilerAsserts.neverPartOfCompilation();
return ExceptionNodes.createRubyException(getErrnoClass(Errno.EPERM), Layouts.STRING.createString(Layouts.CLASS.getInstanceFactory(stringClass), RubyString.encodeBytelist(String.format("Operation not permitted - %s", path), UTF8Encoding.INSTANCE), StringSupport.CR_UNKNOWN, null), RubyCallStack.getBacktrace(currentNode));
}

public DynamicObject permissionDeniedError(String path, Node currentNode) {
CompilerAsserts.neverPartOfCompilation();
return ExceptionNodes.createRubyException(getErrnoClass(Errno.EACCES), Layouts.STRING.createString(Layouts.CLASS.getInstanceFactory(stringClass), RubyString.encodeBytelist(String.format("Permission denied - %s", path), UTF8Encoding.INSTANCE), StringSupport.CR_UNKNOWN, null), RubyCallStack.getBacktrace(currentNode));
}

public DynamicObject notDirectoryError(String path, Node currentNode) {
CompilerAsserts.neverPartOfCompilation();
return ExceptionNodes.createRubyException(getErrnoClass(Errno.ENOTDIR), Layouts.STRING.createString(Layouts.CLASS.getInstanceFactory(stringClass), RubyString.encodeBytelist(String.format("Not a directory - %s", path), UTF8Encoding.INSTANCE), StringSupport.CR_UNKNOWN, null), RubyCallStack.getBacktrace(currentNode));
}

public DynamicObject rangeError(int code, DynamicObject encoding, Node currentNode) {
CompilerAsserts.neverPartOfCompilation();
assert RubyGuards.isRubyEncoding(encoding);
Original file line number Diff line number Diff line change
@@ -113,7 +113,7 @@ public class BodyTranslator extends Translator {
debugIgnoredCalls.add("upto");
}

public static final Set<String> THREAD_LOCAL_GLOBAL_VARIABLES = new HashSet<>(Arrays.asList("$~", "$1", "$2", "$3", "$4", "$5", "$6", "$7", "$8", "$9", "$!")); // "$_"
public static final Set<String> THREAD_LOCAL_GLOBAL_VARIABLES = new HashSet<>(Arrays.asList("$~", "$1", "$2", "$3", "$4", "$5", "$6", "$7", "$8", "$9", "$!", "$?")); // "$_"

public BodyTranslator(com.oracle.truffle.api.nodes.Node currentNode, RubyContext context, BodyTranslator parent, TranslatorEnvironment environment, Source source, boolean topLevel) {
super(currentNode, context, source);
@@ -766,7 +766,7 @@ public RubyNode visitCaseNode(org.jruby.ast.CaseNode node) {
final ArrayConcatNode arrayConcatNode = (ArrayConcatNode) rubyExpression;
comparisons.add(new WhenSplatNode(context, sourceSection, NodeUtil.cloneNode(readTemp), arrayConcatNode));
} else {
comparisons.add(new RubyCallNode(context, sourceSection, "===", rubyExpression, null, false, NodeUtil.cloneNode(readTemp)));
comparisons.add(new RubyCallNode(context, sourceSection, "===", rubyExpression, null, false, true, NodeUtil.cloneNode(readTemp)));
}
}

@@ -1442,9 +1442,12 @@ public RubyNode translateGlobalAsgnNode(org.jruby.ast.GlobalAsgnNode node, RubyN
return new UpdateLastBacktraceNode(context, sourceSection, rhs);
}

if (readOnlyGlobalVariables.contains(name)) {
final boolean inCore = rhs.getSourceSection().getSource().getPath().startsWith(context.getCoreLibrary().getCoreLoadPath() + "/core/");
if (!inCore && readOnlyGlobalVariables.contains(name)) {
return new WriteReadOnlyGlobalNode(context, sourceSection, name, rhs);
} else if (THREAD_LOCAL_GLOBAL_VARIABLES.contains(name)) {
}

if (THREAD_LOCAL_GLOBAL_VARIABLES.contains(name)) {
final ThreadLocalObjectNode threadLocalVariablesObjectNode = new ThreadLocalObjectNode(context, sourceSection);
return new WriteInstanceVariableNode(context, sourceSection, name, threadLocalVariablesObjectNode, rhs, true);
} else if (FRAME_LOCAL_GLOBAL_VARIABLES.contains(name)) {
7 changes: 5 additions & 2 deletions truffle/src/main/ruby/core/hash.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# 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
@@ -37,8 +37,11 @@ def initialize(key, value)

end

alias_method :each_original, :each

def each_item
each do |key, value|
# use aliased each to protect against overriding #each
each_original do |key, value|
yield KeyValue.new(key, value)
end
nil
1 change: 0 additions & 1 deletion truffle/src/main/ruby/core/rubinius/common/object_space.rb
Original file line number Diff line number Diff line change
@@ -35,7 +35,6 @@ def self._id2ref(id)
if find_object([:object_id, Integer(id)], ary) > 0
return ary.first
end

return nil
end

15 changes: 15 additions & 0 deletions truffle/src/main/ruby/core/rubinius/primitives.rb
Original file line number Diff line number Diff line change
@@ -33,5 +33,20 @@ def self.module_mirror(obj)

Truffle::Primitive.install_rubinius_primitive method(:module_mirror)

def self.vm_spawn(options, command, arguments)
options ||= {}
env = options[:unsetenv_others] ? {} : ENV.to_hash
env.merge! Hash[options[:env]] if options[:env]

env_array = env.map { |k, v| "#{k}=#{v}" }

if arguments.empty?
command, arguments = 'bash', ['bash', '-c', command]
end

Truffle::Primitive.spawn_process command, arguments, env_array
end

Truffle::Primitive.install_rubinius_primitive method(:vm_spawn)
end
end
19 changes: 16 additions & 3 deletions truffle/src/main/ruby/core/shims.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Copyright (c) 2014, 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
@@ -112,6 +112,15 @@ def [](index)
@array[index]
end
end

class Mirror
module Process
def self.set_status_global(status)
# Rubinius has: `::Thread.current[:$?] = status`
$? = status
end
end
end
end

# We use Rubinius's encoding subsystem for the most part, but we need to keep JRuby's up to date in case we
@@ -197,12 +206,16 @@ def printf(fmt, *args)
end
end

class Exception
class Exception

def locations
# These should be Rubinius::Location
# and use the internal backtrace, never the custom one.
backtrace.each { |s| def s.position; self; end }
backtrace.each do |s|
def s.position
self
end
end
end

def to_s

0 comments on commit 4dcc1ed

Please sign in to comment.