Skip to content

Commit

Permalink
Merge branch 'jruby-9.1'
Browse files Browse the repository at this point in the history
  • Loading branch information
headius committed Apr 12, 2018
2 parents e8b8f24 + 79e9b26 commit 333a45e
Show file tree
Hide file tree
Showing 19 changed files with 1,210 additions and 303 deletions.
9 changes: 6 additions & 3 deletions bin/jruby.bash
Expand Up @@ -239,11 +239,12 @@ do
if [ "${val:0:3}" = "-ea" ]; then
VERIFY_JRUBY="yes"
elif [ "${val:0:16}" = "-Dfile.encoding=" ]; then
JAVA_ENCODING=$val
JAVA_ENCODING=$val${val:16}
elif [ "${val:0:20}" = "-Djava.security.egd=" ]; then
JAVA_SECURITY_EGD=$val
JAVA_SECURITY_EGD=${val:20}
else
java_args=("${java_args[@]}" "${1:2}")
fi
java_args=("${java_args[@]}" "${1:2}")
fi
;;
# Pass -X... and -X? search options through
Expand Down Expand Up @@ -321,6 +322,8 @@ done
# Force file.encoding to UTF-8 when on Mac, since Apple JDK defaults to MacRoman (JRUBY-3576)
if [[ $darwin && -z "$JAVA_ENCODING" ]]; then
java_args=("${java_args[@]}" "-Dfile.encoding=UTF-8")
elif [[ -n "$JAVA_ENCODING" ]]; then
java_args=("${java_args[@]}" "-Dfile.encoding=$JAVA_ENCODING")
fi

# Force OpenJDK-based JVMs to use /dev/urandom for random number generation
Expand Down
2 changes: 1 addition & 1 deletion core/pom.xml
Expand Up @@ -28,7 +28,7 @@ DO NOT MODIFIY - GENERATED CODE
<prawn.git.repo>git://github.com/sandal/prawn.git</prawn.git.repo>
<version.ruby.minor>0</version.ruby.minor>
<tzdata.version>2013d</tzdata.version>
<install4j.executable>/Applications/install4j 4/bin/install4jc</install4j.executable>
<install4j.executable>/Applications/install4j7/bin/install4jc</install4j.executable>
<jay.bin>jay</jay.bin>
<polyglot.dump.readonly>true</polyglot.dump.readonly>
<dest.lib.dir>${lib.dir}</dest.lib.dir>
Expand Down
12 changes: 12 additions & 0 deletions core/src/main/java/org/jruby/Ruby.java
Expand Up @@ -2918,6 +2918,18 @@ public void printError(RubyException excp) {
}
}

public void printError(Throwable t) {
if (t instanceof RaiseException) {
printError(((RaiseException) t).getException());
}
PrintStream errorStream = getErrorStream();
try {
t.printStackTrace(errorStream);
} catch (Exception e) {
t.printStackTrace(System.err);
}
}

static final String ROOT_FRAME_NAME = "(root)";

public void loadFile(String scriptName, InputStream in, boolean wrap) {
Expand Down
109 changes: 67 additions & 42 deletions core/src/main/java/org/jruby/RubyIO.java
Expand Up @@ -4206,7 +4206,8 @@ public static IRubyObject copy_stream(ThreadContext context, IRubyObject recv, I
RubyIO io1 = null;
RubyIO io2 = null;

RubyString read = null;
Channel channel1 = null;
Channel channel2 = null;

if (args.length >= 3) {
length = args[2].convertToInteger();
Expand All @@ -4217,106 +4218,130 @@ public static IRubyObject copy_stream(ThreadContext context, IRubyObject recv, I

IOSites sites = sites(context);

boolean close1 = false;
boolean close2 = false;
// whether we were given an IO or had to produce a channel locally in some other way
boolean userProvidedReadIO = false;

// whether we constructed, and should close, the indicated IO
boolean local1 = false;
boolean local2 = false;

try {
if (arg1 instanceof RubyString) {
io1 = (RubyIO) RubyFile.open(context, runtime.getFile(), new IRubyObject[]{arg1}, Block.NULL_BLOCK);
close1 = true;
local1 = true;
} else if (arg1 instanceof RubyIO) {
io1 = (RubyIO) arg1;
userProvidedReadIO = true;
} else if (sites.to_path_checked1.respond_to_X.respondsTo(context, arg1, arg1)) {
RubyString path = (RubyString) TypeConverter.convertToType(context, arg1, runtime.getString(), sites.to_path_checked1);
io1 = (RubyIO) RubyFile.open(context, runtime.getFile(), new IRubyObject[]{path}, Block.NULL_BLOCK);
close1 = true;
local1 = true;
} else if (sites.respond_to_read.respondsTo(context, arg1, arg1, true)) {
io1 = newIO(runtime, new IOChannel.IOReadableByteChannel(arg1));
io1.setAutoclose(false);
channel1 = new IOChannel.IOReadableByteChannel(arg1);
} else {
throw runtime.newArgumentError("Should be String or IO");
}

// for instance IO, just use its channel
if (io1 instanceof RubyIO) {
io1.openFile.checkReadable(context);
channel1 = io1.getChannel();
}

if (arg2 instanceof RubyString) {
io2 = (RubyIO) RubyFile.open(context, runtime.getFile(), new IRubyObject[]{arg2, runtime.newString("w")}, Block.NULL_BLOCK);
close2 = true;
local2 = true;
} else if (arg2 instanceof RubyIO) {
io2 = (RubyIO) arg2;
} else if (sites.to_path_checked2.respond_to_X.respondsTo(context, arg2, arg2)) {
RubyString path = (RubyString) TypeConverter.convertToType(context, arg2, runtime.getString(), sites.to_path_checked2);
io2 = (RubyIO) RubyFile.open(context, runtime.getFile(), new IRubyObject[]{path, runtime.newString("w")}, Block.NULL_BLOCK);
close2 = true;
local2 = true;
} else if (sites.respond_to_write.respondsTo(context, arg2, arg2, true)) {
io2 = newIO(runtime, new IOChannel.IOWritableByteChannel(arg2));
io2.setAutoclose(false);
channel2 = new IOChannel.IOWritableByteChannel(arg2);
} else {
throw runtime.newArgumentError("Should be String or IO");
}

if (io1 == null) {
IRubyObject size = io2.write(context, read);
// for instanceof IO, just use its write channel
if (io2 instanceof RubyIO) {
io2 = io2.GetWriteIO();
io2.openFile.checkWritable(context);
io2.flush(context);
return size;
channel2 = io2.getChannel();
}

io2 = io2.GetWriteIO();
if (!(channel1 instanceof ReadableByteChannel)) throw runtime.newIOError("from IO is not readable");
if (!(channel2 instanceof WritableByteChannel)) throw runtime.newIOError("to IO is not writable");

if (!io1.openFile.isReadable()) throw runtime.newIOError("from IO is not readable");
if (!io2.openFile.isWritable()) throw runtime.newIOError("to IO is not writable");
boolean locked = false;
OpenFile fptr1 = null;

io2.flush(context);
// attempt to preserve position of original and lock user IO for duration of copy
if (userProvidedReadIO) {
fptr1 = io1.getOpenFileChecked();

// attempt to preserve position of original
OpenFile fptr = io1.getOpenFileChecked();
locked = fptr1.lock();
}

boolean locked = fptr.lock();
try {
long pos = fptr.tell(context);
long pos = 0;
long size = 0;

if (userProvidedReadIO) {
pos = fptr1.tell(context);
}

try {
if (io1.openFile.fileChannel() == null) {
if ((channel1 instanceof FileChannel)) {
FileChannel from = (FileChannel) channel1;
WritableByteChannel to = (WritableByteChannel) channel2;
long remaining = length == null ? from.size() : length.getLongValue();
long position = offset == null ? from.position() : offset.getLongValue();

size = transfer(from, to, remaining, position);
} else {
long remaining = length == null ? -1 : length.getLongValue();
long position = offset == null ? -1 : offset.getLongValue();
if (io2.openFile.fileChannel() == null) {
ReadableByteChannel from = io1.openFile.readChannel();
WritableByteChannel to = io2.openFile.writeChannel();
if ((channel2 instanceof FileChannel)) {
ReadableByteChannel from = (ReadableByteChannel) channel1;
FileChannel to = (FileChannel) channel2;

size = transfer(context, from, to, remaining, position);
} else {
ReadableByteChannel from = io1.openFile.readChannel();
FileChannel to = io2.openFile.fileChannel();
ReadableByteChannel from = (ReadableByteChannel) channel1;
WritableByteChannel to = (WritableByteChannel) channel2;

size = transfer(context, from, to, remaining, position);
}
} else {
FileChannel from = io1.openFile.fileChannel();
WritableByteChannel to = io2.openFile.writeChannel();
long remaining = length == null ? from.size() : length.getLongValue();
long position = offset == null ? from.position() : offset.getLongValue();

size = transfer(from, to, remaining, position);
}

return context.runtime.newFixnum(size);
} catch (IOException ioe) {
ioe.printStackTrace();
throw runtime.newIOErrorFromException(ioe);
} finally {
if (offset != null) {
fptr.seek(context, pos, PosixShim.SEEK_SET);
} else {
fptr.seek(context, pos + size, PosixShim.SEEK_SET);
if (userProvidedReadIO) {
if (offset != null) {
fptr1.seek(context, pos, PosixShim.SEEK_SET);
} else {
fptr1.seek(context, pos + size, PosixShim.SEEK_SET);
}
}
}
} finally {
if (locked) fptr.unlock();
if (userProvidedReadIO && locked) fptr1.unlock();
}
} finally {
if (close1 && io1 != null) {

// Clean up locally-created IO objects
if (local1) {
try {io1.close();} catch (Exception e) {}
}
if (close2 && io2 != null) {
if (local2) {
try {io2.close();} catch (Exception e) {}
}

}
}

Expand Down
7 changes: 3 additions & 4 deletions core/src/main/java/org/jruby/RubyModule.java
Expand Up @@ -167,9 +167,8 @@ public static RubyClass createModuleClass(Ruby runtime, RubyClass moduleClass) {

public void checkValidBindTargetFrom(ThreadContext context, RubyModule originModule) throws RaiseException {
// Module methods can always be transplanted
if (originModule.isModule()) return;

if (!this.hasModuleInHierarchy(originModule)) {
if (!originModule.isModule() &&
!this.hasModuleInHierarchy(originModule)) {
if (originModule instanceof MetaClass) {
throw context.runtime.newTypeError("can't bind singleton method to a different class");
} else {
Expand Down Expand Up @@ -2038,7 +2037,7 @@ public IRubyObject defineMethodFromCallable(ThreadContext context, IRubyObject a
} else if (arg1 instanceof AbstractRubyMethod) {
AbstractRubyMethod method = (AbstractRubyMethod)arg1;

checkValidBindTargetFrom(context, (RubyModule)method.owner(context));
checkValidBindTargetFrom(context, (RubyModule) method.owner(context));

newMethod = method.getMethod().dup();
newMethod.setImplementationClass(this);
Expand Down
68 changes: 33 additions & 35 deletions core/src/main/java/org/jruby/RubyThread.java
Expand Up @@ -141,7 +141,7 @@ public class RubyThread extends RubyObject implements ExecutionContext {
* it here to continue propagating it while handling thread shutdown
* logic and abort_on_exception.
*/
private volatile RaiseException exitingException;
private volatile Throwable exitingException;

/** The ThreadGroup to which this thread belongs */
private volatile RubyThreadGroup threadGroup;
Expand Down Expand Up @@ -1106,11 +1106,14 @@ public IRubyObject join(ThreadContext context, IRubyObject[] args) {
}

if (exitingException != null) {
// Set $! in the current thread before exiting
runtime.getGlobalVariables().set("$!", (IRubyObject)exitingException.getException());
exceptionCaptured = true;
throw exitingException;

if (exitingException instanceof RaiseException) {
RaiseException raiseException = (RaiseException) exitingException;
// Set $! in the current thread before exiting
runtime.getGlobalVariables().set("$!", (IRubyObject) raiseException.getException());
} else {
runtime.getGlobalVariables().set("$!", JavaUtil.convertJavaToUsableRubyObject(runtime, exitingException));
}
Helpers.throwException(exitingException);
}

// check events before leaving
Expand Down Expand Up @@ -1712,20 +1715,7 @@ private boolean isCurrent() {
}

public void exceptionRaised(RaiseException exception) {
assert isCurrent();

RubyException rubyException = exception.getException();
Ruby runtime = rubyException.getRuntime();
if (runtime.getSystemExit().isInstance(rubyException)) {
runtime.getThreadService().getMainThread().raise(rubyException);
} else if (abortOnException(runtime)) {
runtime.getThreadService().getMainThread().raise(rubyException);
return;
} else if (reportOnException.isTrue()) {
printReportExceptionWarning();
runtime.printError(exception.getException());
}
exitingException = exception;
exceptionRaised((Throwable) exception);
}

protected void printReportExceptionWarning() {
Expand All @@ -1735,27 +1725,35 @@ protected void printReportExceptionWarning() {
}

/**
* For handling all non-Ruby exceptions bubbling out of threads
* @param exception
* For handling all exceptions bubbling out of threads
* @param throwable
*/
@SuppressWarnings("deprecation")
public void exceptionRaised(Throwable exception) {
if (exception instanceof RaiseException) {
exceptionRaised((RaiseException)exception);
return;
}
public void exceptionRaised(Throwable throwable) {
Ruby runtime = getRuntime();

assert isCurrent();

Ruby runtime = getRuntime();
if (abortOnException(runtime) && exception instanceof Error) {
// re-propagate on main thread
exceptionCaptured = true;
runtime.getThreadService().getMainThread().raise(JavaUtil.convertJavaToUsableRubyObject(runtime, exception));
IRubyObject rubyException;

if (throwable instanceof RaiseException) {
RaiseException exception = (RaiseException) throwable;
rubyException = exception.getException();
} else {
// just rethrow on this thread, let system handlers report it
Helpers.throwException(exception);
rubyException = JavaUtil.convertJavaToUsableRubyObject(runtime, throwable);
}

if (runtime.getSystemExit().isInstance(rubyException)) {
runtime.getThreadService().getMainThread().raise(rubyException);
} else if (abortOnException(runtime)) {
runtime.getThreadService().getMainThread().raise(rubyException);
} else if (reportOnException.isTrue()) {
printReportExceptionWarning();
runtime.printError(throwable);
} else if (runtime.isDebug()) {
runtime.printError(throwable);
}

exitingException = throwable;
}

private boolean abortOnException(Ruby runtime) {
Expand Down

0 comments on commit 333a45e

Please sign in to comment.