Skip to content

Commit

Permalink
Showing 23 changed files with 412 additions and 224 deletions.
9 changes: 7 additions & 2 deletions bench/java/bench_java_overloaded_invocation.rb
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@

import java.lang.Math

Benchmark.bm(40) {|bm|
Benchmark.bm(45) {|bm|
# Math.abs computation cost is negligible for these tests
(ARGV[0] || 5).to_i.times {
bm.report("Control") {
@@ -41,5 +41,10 @@
value = 2.0 ** 128
10_000_000.times { m.abs(value) }
}
bm.report("Contended Math.abs with double-ranged Float") {
m = Math
value = 2.0 ** 128
10.times.map {|i| Thread.new { 1_000_000.times { m.abs(value) } } }.each(&:join)
}
}
}
}
2 changes: 1 addition & 1 deletion core/pom.rb
Original file line number Diff line number Diff line change
@@ -45,7 +45,7 @@
jar 'com.github.jnr:jnr-enxio:0.9', :exclusions => ['com.github.jnr:jnr-ffi']
jar 'com.github.jnr:jnr-x86asm:1.0.2', :exclusions => ['com.github.jnr:jnr-ffi']
jar 'com.github.jnr:jnr-unixsocket:0.8', :exclusions => ['com.github.jnr:jnr-ffi']
jar 'com.github.jnr:jnr-posix:3.0.18-SNAPSHOT', :exclusions => ['com.github.jnr:jnr-ffi']
jar 'com.github.jnr:jnr-posix:3.0.18', :exclusions => ['com.github.jnr:jnr-ffi']
jar 'com.github.jnr:jnr-constants:0.9.0', :exclusions => ['com.github.jnr:jnr-ffi']
jar 'com.github.jnr:jnr-ffi:2.0.4'
jar 'com.github.jnr:jffi:${jffi.version}'
2 changes: 1 addition & 1 deletion core/pom.xml
Original file line number Diff line number Diff line change
@@ -136,7 +136,7 @@ DO NOT MODIFIY - GENERATED CODE
<dependency>
<groupId>com.github.jnr</groupId>
<artifactId>jnr-posix</artifactId>
<version>3.0.18-SNAPSHOT</version>
<version>3.0.18</version>
<exclusions>
<exclusion>
<artifactId>jnr-ffi</artifactId>
10 changes: 5 additions & 5 deletions core/src/main/java/org/jruby/RubyHash.java
Original file line number Diff line number Diff line change
@@ -1317,7 +1317,7 @@ public RubyHash eachCommon(final ThreadContext context, final Block block) {
iteratorVisitAll(new Visitor() {
@Override
public void visit(IRubyObject key, IRubyObject value) {
block.yieldSpecific(context, key, value);
block.yieldArray(context, context.runtime.newArray(key, value), null);
}
});
} else {
@@ -1326,7 +1326,7 @@ public void visit(IRubyObject key, IRubyObject value) {
iteratorVisitAll(new Visitor() {
@Override
public void visit(IRubyObject key, IRubyObject value) {
block.yield(context, RubyArray.newArray(runtime, key, value));
block.yieldArray(context, RubyArray.newArray(runtime, key, value), null);
}
});
}
@@ -1425,7 +1425,7 @@ public boolean keep_ifCommon(final ThreadContext context, final Block block) {
iteratorVisitAll(new Visitor() {
@Override
public void visit(IRubyObject key, IRubyObject value) {
if (!block.yieldSpecific(context, key, value).isTrue()) {
if (!block.yieldArray(context, context.runtime.newArray(key, value), null).isTrue()) {
modified[0] = true;
remove(key);
}
@@ -1878,7 +1878,7 @@ private IRubyObject any_p_i(ThreadContext context, Block block) {
try {
for (RubyHashEntry entry = head.nextAdded; entry != head; entry = entry.nextAdded) {
IRubyObject newAssoc = RubyArray.newArray(context.runtime, entry.key, entry.value);
if (block.yield(context, newAssoc).isTrue())
if (block.yieldArray(context, newAssoc, null).isTrue())
return context.getRuntime().getTrue();
}
return context.getRuntime().getFalse();
@@ -1891,7 +1891,7 @@ private IRubyObject any_p_i_fast(ThreadContext context, Block block) {
iteratorEntry();
try {
for (RubyHashEntry entry = head.nextAdded; entry != head; entry = entry.nextAdded) {
if (block.yieldSpecific(context, entry.key, entry.value).isTrue())
if (block.yieldArray(context, context.runtime.newArray(entry.key, entry.value), null).isTrue())
return context.getRuntime().getTrue();
}
return context.getRuntime().getFalse();
164 changes: 91 additions & 73 deletions core/src/main/java/org/jruby/java/dispatch/CallableSelector.java
Original file line number Diff line number Diff line change
@@ -16,10 +16,13 @@
import org.jruby.RubyInteger;
import org.jruby.RubyProc;
import org.jruby.RubyString;
import org.jruby.java.invokers.RubyToJavaInvoker;
import org.jruby.javasupport.Java;
import org.jruby.javasupport.JavaCallable;
import org.jruby.javasupport.JavaClass;
import org.jruby.javasupport.JavaUtil;
import org.jruby.javasupport.ParameterTypes;
import org.jruby.javasupport.proxy.JavaProxyConstructor;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.util.collections.IntHashMap;
import static org.jruby.util.CodegenUtils.getBoxType;
@@ -35,117 +38,62 @@ private CallableSelector() { /* no-instances */ }

//private static final boolean DEBUG = true;

@SuppressWarnings("unchecked")
public static ParameterTypes matchingCallableArityN(Ruby runtime, Map cache, ParameterTypes[] methods, IRubyObject[] args) {
final int signatureCode = argsHashCode(args);
ParameterTypes method = (ParameterTypes) cache.get(signatureCode);
if (method == null) {
method = findMatchingCallableForArgs(runtime, methods, args);
if (method != null) cache.put(signatureCode, method);
}
return method;
}

// NOTE: The five match methods are arity-split to avoid the cost of boxing arguments
// when there's already a cached match. Do not condense them into a single
// method.
@SuppressWarnings("unchecked")
public static JavaCallable matchingCallableArityN(Ruby runtime, Map cache, JavaCallable[] methods, IRubyObject[] args) {
public static JavaProxyConstructor matchingCallableArityN(Ruby runtime, Java.JCreateMethod invoker, JavaProxyConstructor[] methods, IRubyObject[] args) {
final int signatureCode = argsHashCode(args);
JavaCallable method = (JavaCallable) cache.get(signatureCode);
org.jruby.javasupport.proxy.JavaProxyConstructor method = invoker.getSignature(signatureCode);
if (method == null) {
method = findMatchingCallableForArgs(runtime, methods, args);
if (method != null) cache.put(signatureCode, method);
}
return method;
}

public static JavaCallable matchingCallableArityOne(Ruby runtime, Map cache, JavaCallable[] methods, IRubyObject arg0) {
final int signatureCode = argsHashCode(arg0);
JavaCallable method = (JavaCallable) cache.get(signatureCode);
if (method == null) {
method = findMatchingCallableForArgs(runtime, methods, arg0);
if (method != null) cache.put(signatureCode, method);
}
return method;
}

public static JavaCallable matchingCallableArityTwo(Ruby runtime, Map cache, JavaCallable[] methods, IRubyObject arg0, IRubyObject arg1) {
final int signatureCode = argsHashCode(arg0, arg1);
JavaCallable method = (JavaCallable) cache.get(signatureCode);
if (method == null) {
method = findMatchingCallableForArgs(runtime, methods, arg0, arg1);
if (method != null) cache.put(signatureCode, method);
}
return method;
}

public static JavaCallable matchingCallableArityThree(Ruby runtime, Map cache, JavaCallable[] methods, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2) {
final int signatureCode = argsHashCode(arg0, arg1, arg2);
JavaCallable method = (JavaCallable) cache.get(signatureCode);
if (method == null) {
method = findMatchingCallableForArgs(runtime, methods, arg0, arg1, arg2);
if (method != null) cache.put(signatureCode, method);
if (method != null) invoker.putSignature(signatureCode, method);
}
return method;
}

public static JavaCallable matchingCallableArityFour(Ruby runtime, Map cache, JavaCallable[] methods, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, IRubyObject arg3) {
final int signatureCode = argsHashCode(arg0, arg1, arg2, arg3);
JavaCallable method = (JavaCallable) cache.get(signatureCode);
if (method == null) {
method = findMatchingCallableForArgs(runtime, methods, arg0, arg1, arg2, arg3);
if (method != null) cache.put(signatureCode, method);
}
return method;
}

public static <T extends ParameterTypes> T matchingCallableArityN(Ruby runtime, IntHashMap<T> cache, T[] methods, IRubyObject[] args) {
public static <T extends JavaCallable> T matchingCallableArityN(Ruby runtime, RubyToJavaInvoker<T> invoker, T[] methods, IRubyObject[] args) {
final int signatureCode = argsHashCode(args);
T method = cache.get(signatureCode);
T method = invoker.getSignature(signatureCode);
if (method == null) {
method = findMatchingCallableForArgs(runtime, methods, args);
if (method != null) cache.put(signatureCode, method);
if (method != null) invoker.putSignature(signatureCode, method);
}
return method;
}

public static <T extends ParameterTypes> T matchingCallableArityOne(Ruby runtime, IntHashMap<T> cache, T[] methods, IRubyObject arg0) {
public static <T extends JavaCallable> T matchingCallableArityOne(Ruby runtime, RubyToJavaInvoker<T> invoker, T[] methods, IRubyObject arg0) {
final int signatureCode = argsHashCode(arg0);
T method = cache.get(signatureCode);
T method = invoker.getSignature(signatureCode);
if (method == null) {
method = findMatchingCallableForArgs(runtime, methods, arg0);
if (method != null) cache.put(signatureCode, method);
if (method != null) invoker.putSignature(signatureCode, method);
}
return method;
}

public static <T extends ParameterTypes> T matchingCallableArityTwo(Ruby runtime, IntHashMap<T> cache, T[] methods, IRubyObject arg0, IRubyObject arg1) {
public static <T extends JavaCallable> T matchingCallableArityTwo(Ruby runtime, RubyToJavaInvoker<T> invoker, T[] methods, IRubyObject arg0, IRubyObject arg1) {
final int signatureCode = argsHashCode(arg0, arg1);
T method = cache.get(signatureCode);
T method = invoker.getSignature(signatureCode);
if (method == null) {
method = findMatchingCallableForArgs(runtime, methods, arg0, arg1);
if (method != null) cache.put(signatureCode, method);
if (method != null) invoker.putSignature(signatureCode, method);
}
return method;
}

public static <T extends ParameterTypes> T matchingCallableArityThree(Ruby runtime, IntHashMap<T> cache, T[] methods, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2) {
public static <T extends JavaCallable> T matchingCallableArityThree(Ruby runtime, RubyToJavaInvoker<T> invoker, T[] methods, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2) {
final int signatureCode = argsHashCode(arg0, arg1, arg2);
T method = cache.get(signatureCode);
T method = invoker.getSignature(signatureCode);
if (method == null) {
method = findMatchingCallableForArgs(runtime, methods, arg0, arg1, arg2);
if (method != null) cache.put(signatureCode, method);
if (method != null) invoker.putSignature(signatureCode, method);
}
return method;
}

public static <T extends ParameterTypes> T matchingCallableArityFour(Ruby runtime, IntHashMap<T> cache, T[] methods, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, IRubyObject arg3) {
public static <T extends JavaCallable> T matchingCallableArityFour(Ruby runtime, RubyToJavaInvoker<T> invoker, T[] methods, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, IRubyObject arg3) {
final int signatureCode = argsHashCode(arg0, arg1, arg2, arg3);
T method = cache.get(signatureCode);
T method = invoker.getSignature(signatureCode);
if (method == null) {
method = findMatchingCallableForArgs(runtime, methods, arg0, arg1, arg2, arg3);
if (method != null) cache.put(signatureCode, method);
if (method != null) invoker.putSignature(signatureCode, method);
}
return method;
}
@@ -762,4 +710,74 @@ public static <T extends ParameterTypes> IntHashMap<T> newCallableCache() {
return new IntHashMap<T>(8);
}

@SuppressWarnings("unchecked")
@Deprecated
public static ParameterTypes matchingCallableArityN(Ruby runtime, Map cache, ParameterTypes[] methods, IRubyObject[] args) {
final int signatureCode = argsHashCode(args);
ParameterTypes method = (ParameterTypes) cache.get(signatureCode);
if (method == null) {
method = findMatchingCallableForArgs(runtime, methods, args);
if (method != null) cache.put(signatureCode, method);
}
return method;
}

// NOTE: The five match methods are arity-split to avoid the cost of boxing arguments
// when there's already a cached match. Do not condense them into a single
// method.
@SuppressWarnings("unchecked")
@Deprecated
public static JavaCallable matchingCallableArityN(Ruby runtime, Map cache, JavaCallable[] methods, IRubyObject[] args) {
final int signatureCode = argsHashCode(args);
JavaCallable method = (JavaCallable) cache.get(signatureCode);
if (method == null) {
method = findMatchingCallableForArgs(runtime, methods, args);
if (method != null) cache.put(signatureCode, method);
}
return method;
}

@Deprecated
public static JavaCallable matchingCallableArityOne(Ruby runtime, Map cache, JavaCallable[] methods, IRubyObject arg0) {
final int signatureCode = argsHashCode(arg0);
JavaCallable method = (JavaCallable) cache.get(signatureCode);
if (method == null) {
method = findMatchingCallableForArgs(runtime, methods, arg0);
if (method != null) cache.put(signatureCode, method);
}
return method;
}

@Deprecated
public static JavaCallable matchingCallableArityTwo(Ruby runtime, Map cache, JavaCallable[] methods, IRubyObject arg0, IRubyObject arg1) {
final int signatureCode = argsHashCode(arg0, arg1);
JavaCallable method = (JavaCallable) cache.get(signatureCode);
if (method == null) {
method = findMatchingCallableForArgs(runtime, methods, arg0, arg1);
if (method != null) cache.put(signatureCode, method);
}
return method;
}

@Deprecated
public static JavaCallable matchingCallableArityThree(Ruby runtime, Map cache, JavaCallable[] methods, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2) {
final int signatureCode = argsHashCode(arg0, arg1, arg2);
JavaCallable method = (JavaCallable) cache.get(signatureCode);
if (method == null) {
method = findMatchingCallableForArgs(runtime, methods, arg0, arg1, arg2);
if (method != null) cache.put(signatureCode, method);
}
return method;
}

@Deprecated
public static JavaCallable matchingCallableArityFour(Ruby runtime, Map cache, JavaCallable[] methods, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, IRubyObject arg3) {
final int signatureCode = argsHashCode(arg0, arg1, arg2, arg3);
JavaCallable method = (JavaCallable) cache.get(signatureCode);
if (method == null) {
method = findMatchingCallableForArgs(runtime, methods, arg0, arg1, arg2, arg3);
if (method != null) cache.put(signatureCode, method);
}
return method;
}
}
135 changes: 75 additions & 60 deletions core/src/main/java/org/jruby/java/invokers/RubyToJavaInvoker.java

Large diffs are not rendered by default.

70 changes: 45 additions & 25 deletions core/src/main/java/org/jruby/javasupport/Java.java
Original file line number Diff line number Diff line change
@@ -605,41 +605,61 @@ public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule claz
}
});

subclass.addMethod("__jcreate!", new JavaMethodN(subclassSingleton, PUBLIC) {
subclass.addMethod("__jcreate!", new JCreateMethod(subclassSingleton));
}

private final IntHashMap<JavaProxyConstructor> cache = newCallableCache();
public static class JCreateMethod extends JavaMethodN {
private final IntHashMap<JavaProxyConstructor> writeCache = newCallableCache();
private volatile IntHashMap<JavaProxyConstructor> readCache = (IntHashMap)writeCache.clone();

@Override
public IRubyObject call(final ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject[] args) {
IRubyObject proxyClass = self.getMetaClass().getInstanceVariables().getInstanceVariable("@java_proxy_class");
if (proxyClass == null || proxyClass.isNil()) {
proxyClass = JavaProxyClass.get_with_class(self, self.getMetaClass());
self.getMetaClass().getInstanceVariables().setInstanceVariable("@java_proxy_class", proxyClass);
}
JCreateMethod(RubyModule cls) {
super(cls, PUBLIC);
}

public JavaProxyConstructor getSignature(int signatureCode) {
return readCache.get(signatureCode);
}

final int argsLength = args.length;
final JavaProxyConstructor[] constructors = ((JavaProxyClass) proxyClass).getConstructors();
ArrayList<JavaProxyConstructor> forArity = findCallablesForArity(argsLength, constructors);
public void putSignature(int signatureCode, JavaProxyConstructor callable) {
synchronized (writeCache) {
writeCache.put(signatureCode, callable);
readCache = (IntHashMap<JavaProxyConstructor>)writeCache.clone();
}
}

if ( forArity.size() == 0 ) {
throw context.runtime.newArgumentError("wrong number of arguments for constructor");
}
@Override
public IRubyObject call(final ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject[] args) {
IRubyObject proxyClass = self.getMetaClass().getInstanceVariables().getInstanceVariable("@java_proxy_class");
if (proxyClass == null || proxyClass.isNil()) {
proxyClass = JavaProxyClass.get_with_class(self, self.getMetaClass());
self.getMetaClass().getInstanceVariables().setInstanceVariable("@java_proxy_class", proxyClass);
}

final int argsLength = args.length;
final JavaProxyConstructor[] constructors = ((JavaProxyClass) proxyClass).getConstructors();
ArrayList<JavaProxyConstructor> forArity = findCallablesForArity(argsLength, constructors);

final JavaProxyConstructor matching = CallableSelector.matchingCallableArityN(
context.runtime, cache,
if ( forArity.size() == 0 ) {
throw context.runtime.newArgumentError("wrong number of arguments for constructor");
}

final JavaProxyConstructor matching;
synchronized (writeCache) {
matching = CallableSelector.matchingCallableArityN(
context.runtime, this,
forArity.toArray(new JavaProxyConstructor[forArity.size()]), args
);
}

if ( matching == null ) {
throw context.runtime.newArgumentError("wrong number of arguments for constructor");
}
if ( matching == null ) {
throw context.runtime.newArgumentError("wrong number of arguments for constructor");
}

final Object[] javaArgs = convertArguments(matching, args);
JavaObject newObject = matching.newInstance(self, javaArgs);
final Object[] javaArgs = convertArguments(matching, args);
JavaObject newObject = matching.newInstance(self, javaArgs);

return JavaUtilities.set_java_object(self, self, newObject);
}
});
return JavaUtilities.set_java_object(self, self, newObject);
}
}

// NOTE: move to RubyToJavaInvoker for re-use ?!
13 changes: 13 additions & 0 deletions core/src/main/java/org/jruby/util/collections/IntHashMap.java
Original file line number Diff line number Diff line change
@@ -397,6 +397,19 @@ public String toString() {
}
}

@Override
public Object clone() {
IntHashMap<V> newMap = new IntHashMap<>(table.length, loadFactor);
for (int i = 0; i < table.length; i++) {
Entry<V> entry = table[i];
while (entry != null) {
newMap.put(entry.getKey(), entry.getValue());
entry = entry.next;
}
}
return newMap;
}

@SuppressWarnings("unchecked")
public static <U> IntHashMap<U> nullMap() { return NullMap.INSTANCE; }

30 changes: 30 additions & 0 deletions spec/regression/GH-3137_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
require 'rspec'

describe "A bunch of hash methods" do
let(:hash) { {a: :b} }

it "can handle standard arities for any?" do
hash.any? { |k| expect(k).to eq(:a) }
hash.any? { |k,v| expect(k).to eq(:a); expect(v).to eq(:b) }
end

it "can handle standard arities for delete_if" do
hash.delete_if { |k| expect(k).to eq(:a) }
hash.delete_if { |k,v| expect(k).to eq(:a); expect(v).to eq(:b) }
end

it "can handle standard arities for each" do
hash.each { |k| expect(k).to eq(:a) }
hash.each { |k,v| expect(k).to eq(:a); expect(v).to eq(:b) }
end

it "can handle standard arities for select" do
hash.select { |k| expect(k).to eq(:a) }
hash.select { |k,v| expect(k).to eq(:a); expect(v).to eq(:b) }
end

it "can handle standard arities for select!" do
hash.select! { |k| expect(k).to eq(:a) }
hash.select! { |k,v| expect(k).to eq(:a); expect(v).to eq(:b) }
end
end
2 changes: 2 additions & 0 deletions spec/ruby/core/module/alias_method_spec.rb
Original file line number Diff line number Diff line change
@@ -39,6 +39,8 @@

it "fails if origin method not found" do
lambda { @class.make_alias :ni, :san }.should raise_error(NameError)
# a NameError and not a NoMethodError
lambda { @class.make_alias :ni, :san }.should_not raise_error(NoMethodError)
end

it "raises RuntimeError if frozen" do
2 changes: 2 additions & 0 deletions spec/ruby/core/module/undef_method_spec.rb
Original file line number Diff line number Diff line change
@@ -50,6 +50,8 @@ class Descendant < Ancestor

it "raises a NameError when passed a missing name" do
lambda { @module.send :undef_method, :not_exist }.should raise_error(NameError)
# a NameError and not a NoMethodError
lambda { @module.send :undef_method, :not_exist }.should_not raise_error(NoMethodError)
end

describe "on frozen instance" do
6 changes: 6 additions & 0 deletions spec/ruby/language/alias_spec.rb
Original file line number Diff line number Diff line change
@@ -162,4 +162,10 @@ def test_with_check(*args)
# because it defines on the default definee / current module
ruby_exe("def foo; end; alias bla foo; print method(:bla).owner", escape: true).should == "Object"
end

it "raises a NameError when passed a missing name" do
lambda { @meta.class_eval { alias undef_method not_exist } }.should raise_error(NameError)
# a NameError and not a NoMethodError
lambda { @meta.class_eval { alias undef_method not_exist } }.should_not raise_error(NoMethodError)
end
end
6 changes: 6 additions & 0 deletions spec/ruby/language/undef_spec.rb
Original file line number Diff line number Diff line change
@@ -13,4 +13,10 @@ class ::UndefSpecClass
end
lambda { obj.meth 5 }.should raise_error(NoMethodError)
end

it "raises a NameError when passed a missing name" do
lambda { class ::UndefSpecClass; undef not_exist; end }.should raise_error(NameError)
# a NameError and not a NoMethodError
lambda { class ::UndefSpecClass; undef not_exist; end }.should_not raise_error(NoMethodError)
end
end
5 changes: 1 addition & 4 deletions test/truffle/simple-webrick-server.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
require 'webrick'

root = File.expand_path '~/public_html'
server = WEBrick::HTTPServer.new :Port => 8080, :DocumentRoot => root

trap 'INT' do server.shutdown end
server = WEBrick::HTTPServer.new(:Port => 8080)

server.mount_proc '/' do |req, res|
res.body = "Hello, world!\n"
Original file line number Diff line number Diff line change
@@ -1881,15 +1881,8 @@ public DynamicObject undefMethods(VirtualFrame frame, DynamicObject module, Obje
private void undefMethod(VirtualFrame frame, DynamicObject module, String name) {
raiseIfFrozenNode.execute(frame);

final InternalMethod method = ModuleOperations.lookupMethod(module, name);

if (method != null) {
Layouts.MODULE.getFields(module).undefMethod(getContext(), this, method);
methodUndefinedNode.call(frame, module, "method_undefined", null, getSymbol(name));
} else {
CompilerDirectives.transferToInterpreter();
throw new RaiseException(getContext().getCoreLibrary().noMethodErrorOnModule(name, module, this));
}
Layouts.MODULE.getFields(module).undefMethod(getContext(), this, name);
methodUndefinedNode.call(frame, module, "method_undefined", null, getSymbol(name));
}

}
Original file line number Diff line number Diff line change
@@ -33,6 +33,7 @@ public TopLevelRaiseHandler(RubyContext context, SourceSection sourceSection, Ru
this.body = body;
}

@SuppressWarnings("finally")
@Override
public Object execute(VirtualFrame frame) {
DynamicObject lastException = null;
Original file line number Diff line number Diff line change
@@ -45,7 +45,9 @@
import com.oracle.truffle.api.source.SourceSection;
import jnr.constants.platform.Errno;
import jnr.constants.platform.Fcntl;
import jnr.ffi.Pointer;
import jnr.ffi.*;
import jnr.posix.DefaultNativeTimeval;
import jnr.posix.Timeval;
import org.jruby.truffle.nodes.RubyGuards;
import org.jruby.truffle.nodes.dispatch.CallDispatchHeadNode;
import org.jruby.truffle.nodes.dispatch.DispatchHeadNodeFactory;
@@ -100,7 +102,7 @@ public IOConnectPipeNode(RubyContext context, SourceSection sourceSection) {
}

@Specialization
public boolean connectPipe(VirtualFrame frame, DynamicObject lhs, DynamicObject rhs) {
public boolean connectPipe(DynamicObject lhs, DynamicObject rhs) {
final int[] fds = new int[2];

if (posix().pipe(fds) == -1) {
@@ -269,13 +271,11 @@ public Object readIfAvailable(VirtualFrame frame, DynamicObject file, int number
final FDSet fdSet = fdSetFactory.create();
fdSet.set(fd);

// TODO CS 2-Sep-15 why are longs 8 bytes? Is that always the case?
final Pointer timeout = getContext().getMemoryManager().allocateDirect(8 * 2); // Needs to be two longs.
timeout.putLong(0, 0);
timeout.putLong(8, 0);
final Timeval timeoutObject = new DefaultNativeTimeval(jnr.ffi.Runtime.getSystemRuntime());
timeoutObject.setTime(new long[]{0, 0});

final int res = nativeSockets().select(fd + 1, fdSet.getPointer(),
PointerNodes.NULL_POINTER, PointerNodes.NULL_POINTER, timeout);
PointerNodes.NULL_POINTER, PointerNodes.NULL_POINTER, timeoutObject);

if (res == 0) {
CompilerDirectives.transferToInterpreter();
@@ -594,19 +594,31 @@ public Object select(DynamicObject readables, DynamicObject writables, DynamicOb
readableSet.set(fd);
}

final int result = getContext().getThreadManager().runUntilResult(this, new ThreadManager.BlockingAction<Integer>() {
final ThreadManager.ResultOrTimeout<Integer> result = getContext().getThreadManager().runUntilTimeout(this, timeout, new ThreadManager.BlockingTimeoutAction<Integer>() {
@Override
public Integer block() throws InterruptedException {
return nativeSockets().select(
public Integer block(Timeval timeoutToUse) throws InterruptedException {
final int result = nativeSockets().select(
max(readableFds) + 1,
readableSet.getPointer(),
PointerNodes.NULL_POINTER,
PointerNodes.NULL_POINTER,
PointerNodes.NULL_POINTER);
timeoutToUse);

if (result == 0) {
return null;
}

return result;
}
});

if (result == -1) {
if (result instanceof ThreadManager.TimedOut) {
return nil();
}

final int resultCode = ((ThreadManager.ResultWithinTime<Integer>) result).getValue();

if (resultCode == -1 || resultCode == 0) {
return nil();
}

@@ -637,7 +649,7 @@ public Integer block() throws InterruptedException {
PointerNodes.NULL_POINTER,
writableSet.getPointer(),
PointerNodes.NULL_POINTER,
PointerNodes.NULL_POINTER);
null);
}
});

Original file line number Diff line number Diff line change
@@ -389,9 +389,9 @@ public static abstract class StringChrAtPrimitiveNode extends RubiniusPrimitiveN

public StringChrAtPrimitiveNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
stringByteSubstringNode = StringPrimitiveNodesFactory.StringByteSubstringPrimitiveNodeFactory.create(getContext(), getSourceSection(), new RubyNode[] {});
}

@TruffleBoundary
@Specialization
public Object stringChrAt(VirtualFrame frame, DynamicObject string, int byteIndex) {
// Taken from Rubinius's Character::create_from.
@@ -404,7 +404,7 @@ public Object stringChrAt(VirtualFrame frame, DynamicObject string, int byteInde

final int p = bytes.getBegin() + byteIndex;
final int end = bytes.getBegin() + bytes.getRealSize();
final int c = StringSupport.preciseLength(bytes.getEncoding(), bytes.getUnsafeBytes(), p, end);
final int c = preciseLength(bytes, p, end);

if (! StringSupport.MBCLEN_CHARFOUND_P(c)) {
return nil();
@@ -415,20 +415,14 @@ public Object stringChrAt(VirtualFrame frame, DynamicObject string, int byteInde
return nil();
}

if (stringByteSubstringNode == null) {
CompilerDirectives.transferToInterpreter();

stringByteSubstringNode = insert(
StringPrimitiveNodesFactory.StringByteSubstringPrimitiveNodeFactory.create(
getContext(),
getSourceSection(),
new RubyNode[]{})
);
}

return stringByteSubstringNode.stringByteSubstring(frame, string, byteIndex, n);
}

@TruffleBoundary
private int preciseLength(final ByteList bytes, final int p, final int end) {
return StringSupport.preciseLength(bytes.getEncoding(), bytes.getUnsafeBytes(), p, end);
}

}

@RubiniusPrimitive(name = "string_compare_substring")
Original file line number Diff line number Diff line change
@@ -1148,12 +1148,6 @@ public DynamicObject noSuperMethodError(Node currentNode) {
return noMethodError;
}

public DynamicObject noMethodErrorOnModule(String name, DynamicObject module, Node currentNode) {
CompilerAsserts.neverPartOfCompilation();
assert RubyGuards.isRubyModule(module);
return noMethodError(String.format("undefined method `%s' for %s", name, Layouts.MODULE.getFields(module).getName()), name, currentNode);
}

public DynamicObject noMethodErrorOnReceiver(String name, Object receiver, Node currentNode) {
CompilerAsserts.neverPartOfCompilation();
DynamicObject logicalClass = getLogicalClass(receiver);
Original file line number Diff line number Diff line change
@@ -355,15 +355,10 @@ public void undefMethod(RubyContext context, Node currentNode, String methodName
if (method == null) {
throw new RaiseException(context.getCoreLibrary().nameErrorUndefinedMethod(methodName, rubyModuleObject, currentNode));
} else {
undefMethod(context, currentNode, method);
addMethod(context, currentNode, method.undefined());
}
}

@CompilerDirectives.TruffleBoundary
public void undefMethod(RubyContext context, Node currentNode, InternalMethod method) {
addMethod(context, currentNode, method.undefined());
}

/**
* Also searches on Object for modules.
* Used for alias_method, visibility changes, etc.
@@ -393,7 +388,7 @@ public void alias(RubyContext context, Node currentNode, String newName, String

if (method == null) {
CompilerDirectives.transferToInterpreter();
throw new RaiseException(context.getCoreLibrary().noMethodErrorOnModule(oldName, rubyModuleObject, currentNode));
throw new RaiseException(context.getCoreLibrary().nameErrorUndefinedMethod(oldName, rubyModuleObject, currentNode));
}

InternalMethod aliasMethod = method.withName(newName);
Original file line number Diff line number Diff line change
@@ -10,6 +10,7 @@
package org.jruby.truffle.runtime.sockets;

import jnr.ffi.Pointer;
import jnr.posix.Timeval;

public interface NativeSockets {

@@ -86,7 +87,7 @@ public interface NativeSockets {
* fd_set *restrict errorfds, struct timeval *restrict timeout);
*/

int select(int nfds, Pointer readfds, Pointer writefds, Pointer errorfds, Pointer timeout);
int select(int nfds, Pointer readfds, Pointer writefds, Pointer errorfds, Timeval timeout);

/*
* int
Original file line number Diff line number Diff line change
@@ -12,6 +12,8 @@
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.object.DynamicObject;
import jnr.posix.DefaultNativeTimeval;
import jnr.posix.Timeval;
import org.jruby.RubyThread.Status;
import org.jruby.truffle.nodes.RubyGuards;
import org.jruby.truffle.nodes.core.FiberNodes;
@@ -52,13 +54,16 @@ public DynamicObject getRootThread() {
return rootThread;
}


public static interface BlockingAction<T> {
public static boolean SUCCESS = true;
public interface BlockingAction<T> {
boolean SUCCESS = true;

T block() throws InterruptedException;
}

public interface BlockingTimeoutAction<T> {
T block(Timeval timeoutToUse) throws InterruptedException;
}

/**
* Runs {@code action} until it returns a non-null value.
* The action might be {@link Thread#interrupted()}, for instance by
@@ -90,6 +95,85 @@ public <T> T runUntilResult(Node currentNode, BlockingAction<T> action) {
return result;
}

public interface ResultOrTimeout<T> {
}

public static class ResultWithinTime<T> implements ResultOrTimeout<T> {

private final T value;

public ResultWithinTime(T value) {
this.value = value;
}

public T getValue() {
return value;
}

}

public static class TimedOut<T> implements ResultOrTimeout<T> {
}

public <T> ResultOrTimeout<T> runUntilTimeout(Node currentNode, int timeout, final BlockingTimeoutAction<T> action) {
final Timeval timeoutToUse = new DefaultNativeTimeval(jnr.ffi.Runtime.getSystemRuntime());

if (timeout == 0) {
timeoutToUse.setTime(new long[]{0, 0});

return new ResultWithinTime<>(runUntilResult(currentNode, new BlockingAction<T>() {

@Override
public T block() throws InterruptedException {
return action.block(timeoutToUse);
}

}));
} else {
final int pollTime = 500_000_000;
final long requestedTimeoutAt = System.nanoTime() + timeout * 1_000;

return runUntilResult(currentNode, new BlockingAction<ResultOrTimeout<T>>() {

@Override
public ResultOrTimeout<T> block() throws InterruptedException {
final long timeUntilRequestedTimeout = requestedTimeoutAt - System.nanoTime();

if (timeUntilRequestedTimeout <= 0) {
return new TimedOut<>();
}

final boolean timeoutForPoll;
final long effectiveTimeout;

if (timeUntilRequestedTimeout < pollTime) {
timeoutForPoll = false;
effectiveTimeout = timeUntilRequestedTimeout;
} else {
timeoutForPoll = true;
effectiveTimeout = pollTime;
}

final long effectiveTimeoutMicros = effectiveTimeout / 1_000;
timeoutToUse.setTime(new long[]{effectiveTimeoutMicros / 1_000_000, effectiveTimeoutMicros % 1_000_000});

final T result = action.block(timeoutToUse);

if (result == null && timeoutForPoll && requestedTimeoutAt - System.nanoTime() > 0) {
throw new InterruptedException();
}

if (result == null) {
return new TimedOut<>();
}

return new ResultWithinTime<>(result);
}

});
}
}

public void initializeCurrentThread(DynamicObject thread) {
assert RubyGuards.isRubyThread(thread);
currentThread.set(thread);
Original file line number Diff line number Diff line change
@@ -81,7 +81,7 @@ public RubyRootNode parse(RubyContext context, Source source, Encoding defaultEn
final DynamicScope dynamicScope = new ManyVarsDynamicScope(staticScope);

boolean isInlineSource = parserContext == ParserContext.SHELL;
boolean isEvalParse = parserContext == ParserContext.EVAL || parserContext == ParserContext.MODULE;
boolean isEvalParse = parserContext == ParserContext.EVAL || parserContext == ParserContext.INLINE || parserContext == ParserContext.MODULE;
final org.jruby.parser.ParserConfiguration parserConfiguration = new org.jruby.parser.ParserConfiguration(context.getRuntime(), 0, isInlineSource, !isEvalParse, true);
parserConfiguration.setDefaultEncoding(defaultEncoding);

0 comments on commit f678b4f

Please sign in to comment.