Skip to content

Commit

Permalink
Showing 91 changed files with 1,740 additions and 1,347 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -48,7 +48,7 @@ lib/ruby/stdlib/jar*
lib/ruby/stdlib/jline
lib/ruby/stdlib/jopenssl*
lib/ruby/stdlib/krypt*
lib/ruby/stdlib/openssl
lib/ruby/stdlib/openssl*
lib/ruby/stdlib/org/
lib/ruby/stdlib/readline/*readline*.jar
lib/ruby/stdlib/ripper.jar
10 changes: 5 additions & 5 deletions antlib/extra.xml
Original file line number Diff line number Diff line change
@@ -25,7 +25,7 @@ build jruby-complete.jar
<env key='GEM_PATH' value='lib/ruby/gems/shared'/>
<arg value='-Djruby.home=uri:classloader://META-INF/jruby.home'/>
<arg value='-cp'/>
<arg value='core/target/test-classes:test/target/test-classes:lib/jruby.jar:maven/jruby-stdlib/target/jruby-stdlib-1.7.20-SNAPSHOT.jar'/>
<arg value='core/target/test-classes:test/target/test-classes:lib/jruby.jar:maven/jruby-stdlib/target/jruby-stdlib-1.7.21-SNAPSHOT.jar'/>
<arg value='org.jruby.Main'/>
<arg value='-I.:test/externals/ruby1.9:test/externals/ruby1.9/ruby'/>
<arg value='-r./test/ruby19_env.rb'/>
@@ -136,7 +136,7 @@ build jruby-complete.jar
<env key='GEM_PATH' value='lib/ruby/gems/shared'/>
<arg value='-Djruby.home=uri:classloader://META-INF/jruby.home'/>
<arg value='-cp'/>
<arg value='core/target/test-classes:test/target/test-classes:maven/jruby-complete/target/jruby-complete-1.7.20-SNAPSHOT.jar'/>
<arg value='core/target/test-classes:test/target/test-classes:maven/jruby-complete/target/jruby-complete-1.7.21-SNAPSHOT.jar'/>
<arg value='org.jruby.Main'/>
<arg value='-I.:test/externals/ruby1.9:test/externals/ruby1.9/ruby'/>
<arg value='-r./test/ruby19_env.rb'/>
@@ -152,7 +152,7 @@ build jruby-complete.jar
<env key='GEM_PATH' value='lib/ruby/gems/shared'/>
<arg value='-Djruby.home=uri:classloader://META-INF/jruby.home'/>
<arg value='-cp'/>
<arg value='core/target/test-classes:test/target/test-classes:lib/jruby.jar:maven/jruby-stdlib/target/jruby-stdlib-1.7.20-SNAPSHOT.jar'/>
<arg value='core/target/test-classes:test/target/test-classes:lib/jruby.jar:maven/jruby-stdlib/target/jruby-stdlib-1.7.21-SNAPSHOT.jar'/>
<arg value='org.jruby.Main'/>
<arg value='-I.:test/externals/ruby1.9:test/externals/ruby1.9/ruby'/>
<arg value='-r./test/ruby19_env.rb'/>
@@ -168,7 +168,7 @@ build jruby-complete.jar
<env key='GEM_PATH' value='lib/ruby/gems/shared'/>
<arg value='-Djruby.home=uri:classloader://META-INF/jruby.home'/>
<arg value='-cp'/>
<arg value='core/target/test-classes:test/target/test-classes:lib/jruby.jar:maven/jruby-stdlib/target/jruby-stdlib-1.7.20-SNAPSHOT.jar'/>
<arg value='core/target/test-classes:test/target/test-classes:lib/jruby.jar:maven/jruby-stdlib/target/jruby-stdlib-1.7.21-SNAPSHOT.jar'/>
<arg value='org.jruby.Main'/>
<arg value='-I.:test/externals/ruby1.9:test/externals/ruby1.9/ruby'/>
<arg value='-r./test/ruby19_env.rb'/>
@@ -460,7 +460,7 @@ build jruby-complete.jar
<env key='GEM_PATH' value='lib/ruby/gems/shared'/>
<arg value='-Djruby.home=uri:classloader://META-INF/jruby.home'/>
<arg value='-cp'/>
<arg value='core/target/test-classes:test/target/test-classes:maven/jruby-complete/target/jruby-complete-1.7.20-SNAPSHOT.jar'/>
<arg value='core/target/test-classes:test/target/test-classes:maven/jruby-complete/target/jruby-complete-1.7.21-SNAPSHOT.jar'/>
<arg value='org.jruby.Main'/>
<arg value='-I.:test/externals/ruby1.9:test/externals/ruby1.9/ruby'/>
<arg value='-r./test/ruby19_env.rb'/>
3 changes: 3 additions & 0 deletions core/pom.xml
Original file line number Diff line number Diff line change
@@ -91,6 +91,7 @@
<groupId>com.github.jnr</groupId>
<artifactId>jnr-enxio</artifactId>
<version>0.9</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>com.github.jnr</groupId>
@@ -101,6 +102,7 @@
<groupId>com.github.jnr</groupId>
<artifactId>jnr-unixsocket</artifactId>
<version>0.8</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>com.github.jnr</groupId>
@@ -116,6 +118,7 @@
<groupId>com.github.jnr</groupId>
<artifactId>jnr-ffi</artifactId>
<version>2.0.3</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>com.github.jnr</groupId>
2 changes: 1 addition & 1 deletion core/src/main/java/org/jruby/Ruby.java
Original file line number Diff line number Diff line change
@@ -1288,7 +1288,7 @@ && getInstanceConfig().getCompileMode() != CompileMode.TRUFFLE) {
// attempt to enable unlimited-strength crypto on OpenJDK
try {
Class jceSecurity = Class.forName("javax.crypto.JceSecurity");
Field isRestricted = jceSecurity.getField("isRestricted");
Field isRestricted = jceSecurity.getDeclaredField("isRestricted");
isRestricted.setAccessible(true);
isRestricted.set(null, false);
isRestricted.setAccessible(false);
297 changes: 149 additions & 148 deletions core/src/main/java/org/jruby/RubyArray.java

Large diffs are not rendered by default.

130 changes: 54 additions & 76 deletions core/src/main/java/org/jruby/RubyBasicObject.java
Original file line number Diff line number Diff line change
@@ -29,7 +29,6 @@

import org.jcodings.Encoding;
import org.jruby.ir.interpreter.Interpreter;
import org.jruby.runtime.Arity;
import org.jruby.runtime.ivars.VariableAccessor;
import java.io.IOException;
import java.io.ObjectInputStream;
@@ -59,6 +58,7 @@
import static org.jruby.anno.FrameField.*;
import static org.jruby.runtime.Visibility.*;
import org.jruby.exceptions.RaiseException;
import org.jruby.runtime.Arity;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.runtime.builtin.InstanceVariables;
import org.jruby.runtime.builtin.InternalVariables;
@@ -588,40 +588,43 @@ public RubyClass getType() {
*/
@Override
public final boolean respondsTo(String name) {
Ruby runtime = getRuntime();
final Ruby runtime = getRuntime();

DynamicMethod respondTo = getMetaClass().searchMethod("respond_to?");
DynamicMethod respondToMissing = getMetaClass().searchMethod("respond_to_missing?");
final DynamicMethod respondTo = getMetaClass().searchMethod("respond_to?");
final DynamicMethod respondToMissing = getMetaClass().searchMethod("respond_to_missing?");

if(respondTo.equals(runtime.getRespondToMethod()) && respondToMissing.equals(runtime.getRespondToMissingMethod())) {
if ( respondTo.equals(runtime.getRespondToMethod()) &&
respondToMissing.equals(runtime.getRespondToMissingMethod()) ) {
// fastest path; builtin respond_to? which just does isMethodBound
return getMetaClass().isMethodBound(name, false);
} else if (!(respondTo.isUndefined() && respondToMissing.isUndefined())) {
}

final ThreadContext context = runtime.getCurrentContext();
final RubySymbol mname = runtime.newSymbol(name);
final boolean respondToUndefined = respondTo.isUndefined();
if ( ! ( respondToUndefined && respondToMissing.isUndefined() ) ) {
// medium path, invoke user's respond_to?/respond_to_missing? if defined
DynamicMethod method;
String methodName;
if (respondTo.isUndefined()) {
method = respondToMissing;
methodName = "respond_to_missing?";
final DynamicMethod method; final String respondName;
if ( respondToUndefined ) {
method = respondToMissing; respondName = "respond_to_missing?";
} else {
method = respondTo;
methodName = "respond_to?";
method = respondTo; respondName = "respond_to?";
}

// We have to check and enforce arity
Arity arity = method.getArity();
ThreadContext context = runtime.getCurrentContext();
if (arity.isFixed() && arity.required() == 1) {
return method.call(context, this, metaClass, methodName, runtime.newSymbol(name)).isTrue();
} else if (arity.isFixed() && arity.required() != 2) {
throw runtime.newArgumentError(methodName + " must accept 1 or 2 arguments (requires " + arity.getValue() + ")");
final Arity arity = method.getArity();
if ( arity.isFixed() ) {
if ( arity.required() == 1 ) {
return method.call(context, this, metaClass, respondName, mname).isTrue();
}
if ( arity.required() != 2 ) {
throw runtime.newArgumentError(respondName + " must accept 1 or 2 arguments (requires " + arity.getValue() + ")");
}
}

return method.call(context, this, metaClass, methodName, runtime.newSymbol(name), runtime.newBoolean(true)).isTrue();
} else {
// slowest path, full callMethod to hit method_missing if present, or produce error
return callMethod(runtime.getCurrentContext(), "respond_to?", runtime.newSymbol(name)).isTrue();
return method.call(context, this, metaClass, respondName, mname, runtime.getTrue()).isTrue();
}

// slowest path, full callMethod to hit method_missing if present, or produce error
return callMethod(context, "respond_to?", mname).isTrue();
}

/**
@@ -636,20 +639,14 @@ public final boolean respondsToMissing(String name) {
* Does this object respond to the specified message via "method_missing?"
*/
@Override
public final boolean respondsToMissing(String name, boolean priv) {
public final boolean respondsToMissing(String name, boolean incPrivate) {
DynamicMethod method = getMetaClass().searchMethod("respond_to_missing?");
// perhaps should try a smart version as for respondsTo above?
if(method.isUndefined()) {
return false;
} else {
return method.call(
getRuntime().getCurrentContext(),
this,
metaClass,
"respond_to_missing?",
getRuntime().newSymbol(name),
getRuntime().newBoolean(priv)).isTrue();
}
if ( method.isUndefined() ) return false;
final Ruby runtime = getRuntime();
return method.call(runtime.getCurrentContext(), this, getMetaClass(),
"respond_to_missing?", runtime.newSymbol(name), runtime.newBoolean(incPrivate)
).isTrue();
}

/**
@@ -1553,7 +1550,7 @@ private void raiseFrozenError() throws RaiseException {
public final int getNativeTypeIndex() {
return getNativeClassIndex().ordinal();
}

@Override
public ClassIndex getNativeClassIndex() {
return ClassIndex.BASICOBJECT;
@@ -1877,10 +1874,10 @@ public IRubyObject evalUnder(final ThreadContext context, RubyModule under, Ruby
* operation.
*/
public static class Finalizer implements Finalizable {
private RubyFixnum id;
private final RubyFixnum id;
private final AtomicBoolean finalized;
private IRubyObject firstFinalizer;
private List<IRubyObject> finalizers;
private AtomicBoolean finalized;

public Finalizer(RubyFixnum id) {
this.id = id;
@@ -1995,51 +1992,32 @@ public void checkFrozen() {
* in both the compiler and the interpreter, the performance
* benefit is important for this method.
*/
public RubyBoolean respond_to_p(IRubyObject mname) {
String name = mname.asJavaString();
public final RubyBoolean respond_to_p(IRubyObject mname) {
final String name = mname.asJavaString();
return getRuntime().newBoolean(getMetaClass().isMethodBound(name, true));
}

public IRubyObject respond_to_p19(IRubyObject mname) {
String name = mname.asJavaString();
IRubyObject respond = getRuntime().newBoolean(getMetaClass().isMethodBound(name, true, true));
if (!respond.isTrue()) {
respond = Helpers.invoke(getRuntime().getCurrentContext(), this, "respond_to_missing?", mname, getRuntime().getFalse());
respond = getRuntime().newBoolean(respond.isTrue());
}
return respond;
public final RubyBoolean respond_to_p19(IRubyObject mname) {
return respond_to_p19(mname, false);
}

/** obj_respond_to
*
* respond_to?( aSymbol, includePriv=false ) -> true or false
*
* Returns true if this object responds to the given method. Private
* methods are included in the search only if the optional second
* parameter evaluates to true.
*
* @return true if this responds to the given method
*
* !!! For some reason MRI shows the arity of respond_to? as -1, when it should be -2; that's why this is rest instead of required, optional = 1
*
* Going back to splitting according to method arity. MRI is wrong
* about most of these anyway, and since we have arity splitting
* in both the compiler and the interpreter, the performance
* benefit is important for this method.
*/
public RubyBoolean respond_to_p(IRubyObject mname, IRubyObject includePrivate) {
public final RubyBoolean respond_to_p(IRubyObject mname, IRubyObject includePrivate) {
String name = mname.asJavaString();
return getRuntime().newBoolean(getMetaClass().isMethodBound(name, !includePrivate.isTrue()));
}

public IRubyObject respond_to_p19(IRubyObject mname, IRubyObject includePrivate) {
String name = mname.asJavaString();
IRubyObject respond = getRuntime().newBoolean(getMetaClass().isMethodBound(name, !includePrivate.isTrue()));
if (!respond.isTrue()) {
respond = Helpers.invoke(getRuntime().getCurrentContext(), this, "respond_to_missing?", mname, includePrivate);
respond = getRuntime().newBoolean(respond.isTrue());
}
return respond;
public final RubyBoolean respond_to_p19(IRubyObject mname, IRubyObject includePrivate) {
return respond_to_p19(mname, includePrivate.isTrue());
}

private RubyBoolean respond_to_p19(IRubyObject mname, final boolean includePrivate) {
final Ruby runtime = getRuntime();
final String name = mname.asJavaString();
if ( getMetaClass().isMethodBound(name, !includePrivate, true) ) return runtime.getTrue();
// MRI (1.9) always passes down a symbol when calling respond_to_missing?
if ( ! (mname instanceof RubySymbol) ) mname = runtime.newSymbol(name);
IRubyObject respond = Helpers.invoke(runtime.getCurrentContext(), this, "respond_to_missing?", mname, runtime.newBoolean(includePrivate));
return runtime.newBoolean( respond.isTrue() );
}

/** rb_obj_id
37 changes: 18 additions & 19 deletions core/src/main/java/org/jruby/RubyDir.java
Original file line number Diff line number Diff line change
@@ -151,31 +151,30 @@ public IRubyObject initialize19(ThreadContext context, IRubyObject arg) {

// ----- Ruby Class Methods ----------------------------------------------------

private static List<ByteList> dirGlobs(ThreadContext context, String cwd, IRubyObject[] args, int flags) {
List<ByteList> dirs = new ArrayList<ByteList>();
private static ArrayList<ByteList> dirGlobs(ThreadContext context, String cwd, IRubyObject[] args, int flags) {
ArrayList<ByteList> dirs = new ArrayList<ByteList>();

for (int i = 0; i < args.length; i++) {
for ( int i = 0; i < args.length; i++ ) {
dirs.addAll(Dir.push_glob(context.runtime, cwd, globArgumentAsByteList(context, args[i]), flags));
}

return dirs;
}

private static IRubyObject asRubyStringList(Ruby runtime, List<ByteList> dirs) {
List<RubyString> allFiles = new ArrayList<RubyString>();
private static RubyArray asRubyStringList(Ruby runtime, List<ByteList> dirs) {
final int size = dirs.size();
if ( size == 0 ) return RubyArray.newEmptyArray(runtime);

Encoding enc = runtime.getDefaultExternalEncoding();
if (enc == null) {
enc = UTF8;
}

for (ByteList dir : dirs) {
allFiles.add(RubyString.newString(runtime, dir, enc));
IRubyObject[] dirStrings = new IRubyObject[ size ];
for ( int i = 0; i < size; i++ ) {
dirStrings[i] = RubyString.newString(runtime, dirs.get(i), enc);
}

IRubyObject[] tempFileList = new IRubyObject[allFiles.size()];
allFiles.toArray(tempFileList);

return runtime.newArrayNoCopy(tempFileList);
return RubyArray.newArrayNoCopy(runtime, dirStrings);
}

private static String getCWD(Ruby runtime) {
@@ -459,12 +458,12 @@ public static IRubyObject mkdir19(ThreadContext context, IRubyObject recv, IRuby

private static IRubyObject mkdirCommon(Ruby runtime, String path, IRubyObject[] args) {
File newDir = getDir(runtime, path, false);


String name = path.replace('\\', '/');

boolean startsWithDriveLetterOnWindows = RubyFile.startsWithDriveLetterOnWindows(name);

// don't attempt to create a dir for drive letters
if (startsWithDriveLetterOnWindows) {
// path is just drive letter plus :
@@ -507,7 +506,7 @@ public static IRubyObject open19(ThreadContext context, IRubyObject recv, IRubyO
return block.yield(context, directory);
} finally {
directory.close();
}
}
}

// ----- Ruby Instance Methods -------------------------------------------------
@@ -603,11 +602,11 @@ public IRubyObject set_pos(IRubyObject newPos) {
public IRubyObject path(ThreadContext context) {
return path == null ? context.runtime.getNil() : path.strDup(context.runtime);
}

@JRubyMethod
public IRubyObject to_path(ThreadContext context) {
return path(context);
}
}

/** Returns the next entry from this directory. */
@JRubyMethod(name = "read")
@@ -636,7 +635,7 @@ public static IRubyObject exist(ThreadContext context, IRubyObject recv, IRubyOb
// Capture previous exception if any.
IRubyObject exception = runtime.getGlobalVariables().get("$!");
RubyString path = StringSupport.checkEmbeddedNulls(runtime, RubyFile.get_path(context, arg));

try {
return runtime.newFileStat(path.asJavaString(), false).directory_p();
} catch (Exception e) {
285 changes: 144 additions & 141 deletions core/src/main/java/org/jruby/RubyIO.java

Large diffs are not rendered by default.

20 changes: 11 additions & 9 deletions core/src/main/java/org/jruby/RubyInstanceConfig.java
Original file line number Diff line number Diff line change
@@ -116,7 +116,7 @@ public RubyInstanceConfig() {
try {
environment = System.getenv();
} catch (SecurityException se) {
environment = new HashMap();
environment = new HashMap<String,String>();
}
}

@@ -387,8 +387,8 @@ public InputStream getScriptSource() {
}
return getInput();
} else {
String script = getScriptFileName();
InputStream stream = null;
final String script = getScriptFileName();
final InputStream stream;
if (script.startsWith("file:") && script.indexOf(".jar!/") != -1) {
stream = new URL("jar:" + script).openStream();
} else if (script.startsWith("classpath:")) {
@@ -424,7 +424,7 @@ private InputStream getScriptSourceFromJar(String script) {
}

private static InputStream findScript(File file) throws IOException {
StringBuffer buf = new StringBuffer();
StringBuilder buf = new StringBuilder();
BufferedReader br = new BufferedReader(new FileReader(file));
String currentLine = br.readLine();
while (currentLine != null && !isRubyShebangLine(currentLine)) {
@@ -437,8 +437,8 @@ private static InputStream findScript(File file) throws IOException {
do {
currentLine = br.readLine();
if (currentLine != null) {
buf.append(currentLine);
buf.append("\n");
buf.append(currentLine);
buf.append("\n");
}
} while (!(currentLine == null || currentLine.contains("__END__") || currentLine.contains("\026")));
return new BufferedInputStream(new ByteArrayInputStream(buf.toString().getBytes()), 8192);
@@ -691,8 +691,10 @@ public boolean isSiphashEnabled() {
}

public void setEnvironment(Map newEnvironment) {
if (newEnvironment == null) newEnvironment = new HashMap();
environment = newEnvironment;
if (newEnvironment == null) {
newEnvironment = new HashMap<String, String>();
}
this.environment = newEnvironment;
}

public Map getEnvironment() {
@@ -1448,7 +1450,7 @@ private static ClassLoader setupLoader() {
private String currentDirectory;

/** Environment variables; defaults to System.getenv() in constructor */
private Map environment;
private Map<String, String> environment;
private String[] argv = {};

private final boolean jitLogging;
56 changes: 28 additions & 28 deletions core/src/main/java/org/jruby/RubyInteger.java
Original file line number Diff line number Diff line change
@@ -19,7 +19,7 @@
* Copyright (C) 2002-2004 Thomas E Enebo <enebo@acm.org>
* Copyright (C) 2004 Stefan Matthias Aust <sma@3plus4.de>
* Copyright (C) 2005 Charles O Nutter <headius@headius.com>
*
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
@@ -62,7 +62,7 @@
* @author jpetersen
*/
@JRubyClass(name="Integer", parent="Numeric", include="Precision")
public abstract class RubyInteger extends RubyNumeric {
public abstract class RubyInteger extends RubyNumeric {

public static RubyClass createIntegerClass(Ruby runtime) {
RubyClass integer = runtime.defineClass("Integer", runtime.getNumeric(),
@@ -71,13 +71,13 @@ public static RubyClass createIntegerClass(Ruby runtime) {

integer.setClassIndex(ClassIndex.INTEGER);
integer.setReifiedClass(RubyInteger.class);

integer.kindOf = new RubyModule.JavaClassKindOf(RubyInteger.class);

integer.getSingletonClass().undefineMethod("new");

integer.defineAnnotatedMethods(RubyInteger.class);

return integer;
}

@@ -88,15 +88,15 @@ public RubyInteger(Ruby runtime, RubyClass rubyClass) {
public RubyInteger(RubyClass rubyClass) {
super(rubyClass);
}

public RubyInteger(Ruby runtime, RubyClass rubyClass, boolean useObjectSpace) {
super(runtime, rubyClass, useObjectSpace);
}
}

@Deprecated
public RubyInteger(Ruby runtime, RubyClass rubyClass, boolean useObjectSpace, boolean canBeTainted) {
super(runtime, rubyClass, useObjectSpace, canBeTainted);
}
}

@Override
public RubyInteger convertToInteger() {
@@ -110,11 +110,11 @@ protected RubyFloat toFloat() {

/* ================
* Instance Methods
* ================
* ================
*/

/** int_int_p
*
*
*/
@Override
@JRubyMethod(name = "integer?")
@@ -123,7 +123,7 @@ public IRubyObject integer_p() {
}

/** int_upto
*
*
*/
@JRubyMethod
public IRubyObject upto(ThreadContext context, IRubyObject to, Block block) {
@@ -185,7 +185,7 @@ public IRubyObject size(IRubyObject[] args) {
}

/** int_downto
*
*
*/
// TODO: Make callCoerced work in block context...then fix downto, step, and upto.
@JRubyMethod
@@ -283,7 +283,7 @@ public IRubyObject size(IRubyObject[] args) {
}

/** int_succ
*
*
*/
@JRubyMethod(name = {"succ", "next"})
public IRubyObject succ(ThreadContext context) {
@@ -312,7 +312,7 @@ public IRubyObject succ(ThreadContext context) {
}

/** int_chr
*
*
*/
public RubyString chr(ThreadContext context) {
return chr19(context);
@@ -331,7 +331,7 @@ public RubyString chr19(ThreadContext context) {
throw runtime.newRangeError(this.toString() + " out of char range");
} else {
if (enc == null) enc = USASCIIEncoding.INSTANCE;
return RubyString.newStringNoCopy(runtime, fromEncodedBytes(runtime, enc, (int)value), enc, 0);
return RubyString.newStringNoCopy(runtime, fromEncodedBytes(runtime, enc, value), enc, 0);
}
}
}
@@ -361,7 +361,7 @@ private ByteList fromEncodedBytes(Ruby runtime, Encoding enc, long value) {
}

if (n <= 0) throw runtime.newRangeError(this.toString() + " out of char range");

ByteList bytes = new ByteList(n);

boolean ok = false;
@@ -381,15 +381,15 @@ private ByteList fromEncodedBytes(Ruby runtime, Encoding enc, long value) {
}

/** int_ord
*
*
*/
@JRubyMethod(name = "ord")
public IRubyObject ord(ThreadContext context) {
return this;
}

/** int_to_i
*
*
*/
@JRubyMethod(name = {"to_i", "to_int", "floor", "ceil", "truncate"})
public IRubyObject to_i() {
@@ -412,14 +412,14 @@ public IRubyObject round19(ThreadContext context, IRubyObject arg) {
if (ndigits > 0) return RubyKernel.new_float(this, this);
if (ndigits == 0) return this;
Ruby runtime = context.runtime;

long bytes = (this instanceof RubyFixnum) ? 8 : RubyFixnum.fix2long(callMethod("size"));
/* If 10**N/2 > this, return 0 */
/* We have log_256(10) > 0.415241 and log_256(1/2)=-0.125 */
if (-0.415241 * ndigits - 0.125 > bytes) {
return RubyFixnum.zero(runtime);
}

IRubyObject f = Numeric.int_pow(context, 10, -ndigits);

if (this instanceof RubyFixnum && f instanceof RubyFixnum) {
@@ -443,7 +443,7 @@ public IRubyObject round19(ThreadContext context, IRubyObject arg) {
}

/** integer_to_r
*
*
*/
@JRubyMethod(name = "to_r")
public IRubyObject to_r(ThreadContext context) {
@@ -457,7 +457,7 @@ public IRubyObject to_r(ThreadContext context) {
public IRubyObject rationalize(ThreadContext context, IRubyObject[] args) {
return to_r(context);
}


@JRubyMethod(name = "odd?")
public RubyBoolean odd_p(ThreadContext context) {
@@ -483,25 +483,25 @@ public IRubyObject pred(ThreadContext context) {
}

/** rb_gcd
*
*
*/
@JRubyMethod(name = "gcd")
public IRubyObject gcd(ThreadContext context, IRubyObject other) {
checkInteger(context, other);
return f_gcd(context, this, RubyRational.intValue(context, other));
}
}

/** rb_lcm
*
*
*/
@JRubyMethod(name = "lcm")
public IRubyObject lcm(ThreadContext context, IRubyObject other) {
checkInteger(context, other);
return f_lcm(context, this, RubyRational.intValue(context, other));
}
}

/** rb_gcdlcm
*
*
*/
@JRubyMethod(name = "gcdlcm")
public IRubyObject gcdlcm(ThreadContext context, IRubyObject other) {
@@ -524,11 +524,11 @@ public IRubyObject denominator(ThreadContext context) {

/* ================
* Singleton Methods
* ================
* ================
*/

/** rb_int_induced_from
*
*
*/
@Deprecated
public static IRubyObject induced_from(ThreadContext context, IRubyObject recv, IRubyObject other) {
127 changes: 64 additions & 63 deletions core/src/main/java/org/jruby/RubyKernel.java

Large diffs are not rendered by default.

65 changes: 41 additions & 24 deletions core/src/main/java/org/jruby/RubyModule.java
Original file line number Diff line number Diff line change
@@ -254,11 +254,14 @@ private synchronized Map<String, Autoload> getAutoloadMapForWrite() {
return autoloads == Collections.EMPTY_MAP ? autoloads = new ConcurrentHashMap<String, Autoload>(4, 0.9f, 1) : autoloads;
}

@SuppressWarnings("unchecked")
public void addIncludingHierarchy(IncludedModule hierarchy) {
synchronized (getRuntime().getHierarchyLock()) {
Set<RubyClass> oldIncludingHierarchies = includingHierarchies;
if (oldIncludingHierarchies == Collections.EMPTY_SET) includingHierarchies = oldIncludingHierarchies = new WeakHashSet(4);
oldIncludingHierarchies.add(hierarchy);
Set<RubyClass> including = this.includingHierarchies;
if (including == Collections.EMPTY_SET) {
including = this.includingHierarchies = new WeakHashSet(4);
}
including.add(hierarchy);
}
}

@@ -500,6 +503,9 @@ private String calculateName() {
// First, we count the parents
int parentCount = 0;
for (RubyModule p = getParent() ; p != null && p != objectClass ; p = p.getParent()) {
// Break out of cyclic namespaces like C::A = C2; C2::A = C (jruby/jruby#2314)
if (p == this) break;

parentCount++;
}

@@ -508,6 +514,9 @@ private String calculateName() {
int i = parentCount - 1;
int totalLength = name.length() + parentCount * 2; // name length + enough :: for all parents
for (RubyModule p = getParent() ; p != null && p != objectClass ; p = p.getParent(), i--) {
// Break out of cyclic namespaces like C::A = C2; C2::A = C (jruby/jruby#2314)
if (p == this) break;

String pName = p.getBaseName();

// This is needed when the enclosing class or module is a singleton.
@@ -3736,9 +3745,8 @@ public void setInternalModuleVariable(final String name, final IRubyObject value
protected Map<String, IRubyObject> getClassVariables() {
if (CLASSVARS_UPDATER == null) {
return getClassVariablesForWriteSynchronized();
} else {
return getClassVariablesForWriteAtomic();
}
return getClassVariablesForWriteAtomic();
}

/**
@@ -3748,19 +3756,17 @@ protected Map<String, IRubyObject> getClassVariables() {
* @return the class vars map, ready for assignment
*/
private Map<String,IRubyObject> getClassVariablesForWriteSynchronized() {
Map myClassVars = classVariables;
if (myClassVars == Collections.EMPTY_MAP) {
Map<String, IRubyObject> myClassVars = classVariables;
if ( myClassVars == Collections.EMPTY_MAP ) {
synchronized (this) {
myClassVars = classVariables;

if (myClassVars == Collections.EMPTY_MAP) {
if ( myClassVars == Collections.EMPTY_MAP ) {
return classVariables = new ConcurrentHashMap<String, IRubyObject>(4, 0.75f, 2);
} else {
return myClassVars;
}
return myClassVars;
}
}

return myClassVars;
}

@@ -3773,14 +3779,12 @@ private Map<String,IRubyObject> getClassVariablesForWriteSynchronized() {
*/
private Map<String,IRubyObject> getClassVariablesForWriteAtomic() {
while (true) {
Map myClassVars = classVariables;
Map newClassVars;
Map<String, IRubyObject> myClassVars = classVariables;

if (myClassVars == Collections.EMPTY_MAP) {
newClassVars = new ConcurrentHashMap<String, IRubyObject>(4, 0.75f, 2);
} else {
return myClassVars;
}
if ( myClassVars != Collections.EMPTY_MAP ) return myClassVars;

Map<String, IRubyObject> newClassVars;
newClassVars = new ConcurrentHashMap<String, IRubyObject>(4, 0.75f, 2);

// proceed with atomic update of table, or retry
if (CLASSVARS_UPDATER.compareAndSet(this, myClassVars, newClassVars)) {
@@ -4311,6 +4315,17 @@ public void setCacheProxy(boolean cacheProxy) {
setFlag(USER0_F, cacheProxy);
}

@Override
public Object toJava(Class target) {
if (target == Class.class) { // try java_class for proxy modules
if ( respondsTo("java_class") ) {
return callMethod("java_class").toJava(target);
}
}

return super.toJava(target);
}

public Set<String> discoverInstanceVariables() {
HashSet<String> set = new HashSet();
RubyModule cls = this;
@@ -4409,17 +4424,19 @@ public void setRefinements(Map<RubyClass, RubyModule> refinements) {
/** The moduel where this refinement was defined */
private volatile RubyModule definedAt = null;

private static final AtomicReferenceFieldUpdater CLASSVARS_UPDATER;
private static final AtomicReferenceFieldUpdater<RubyModule, Map> CLASSVARS_UPDATER;

static {
AtomicReferenceFieldUpdater updater = null;
AtomicReferenceFieldUpdater<RubyModule, Map> updater = null;
try {
updater = AtomicReferenceFieldUpdater.newUpdater(RubyModule.class, Map.class, "classVariables");
} catch (RuntimeException re) {
if (re.getCause() instanceof AccessControlException) {
}
catch (final RuntimeException ex) {
if (ex.getCause() instanceof AccessControlException) {
// security prevented creation; fall back on synchronized assignment
} else {
throw re;
}
else {
throw ex;
}
}
CLASSVARS_UPDATER = updater;
7 changes: 3 additions & 4 deletions core/src/main/java/org/jruby/RubyString.java
Original file line number Diff line number Diff line change
@@ -1975,8 +1975,6 @@ public static IRubyObject inspect19(Ruby runtime, ByteList byteList) {
}

while (p < end) {
int cc = 0;

int n = StringSupport.preciseLength(enc, bytes, p, end);
if (!MBCLEN_CHARFOUND_P(n)) {
if (p > prev) result.cat(bytes, prev, p - prev);
@@ -1990,7 +1988,8 @@ public static IRubyObject inspect19(Ruby runtime, ByteList byteList) {
continue;
}
n = MBCLEN_CHARFOUND_LEN(n);
int c = enc.mbcToCode(bytes, p, end);
final int c = enc.mbcToCode(bytes, p, end); int cc = 0;

p += n;
if ((asciiCompat || isUnicode) &&
(c == '"' || c == '\\' ||
@@ -2161,7 +2160,7 @@ private RubyString concatNumeric(Ruby runtime, int c) {
modify19(value.getRealSize() + cl);

if (enc == USASCIIEncoding.INSTANCE) {
if (c > 0xff) runtime.newRangeError(c + " out of char range");
if (c > 0xff) throw runtime.newRangeError(c + " out of char range");
if (c > 0x79) {
value.setEncoding(ASCIIEncoding.INSTANCE);
enc = value.getEncoding();
169 changes: 101 additions & 68 deletions core/src/main/java/org/jruby/RubyTime.java

Large diffs are not rendered by default.

205 changes: 126 additions & 79 deletions core/src/main/java/org/jruby/ext/timeout/Timeout.java
Original file line number Diff line number Diff line change
@@ -10,7 +10,7 @@
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
@@ -35,12 +35,14 @@
import java.util.concurrent.atomic.AtomicBoolean;
import org.jruby.Ruby;
import org.jruby.RubyClass;
import org.jruby.RubyException;
import org.jruby.RubyFixnum;
import org.jruby.RubyKernel;
import org.jruby.RubyModule;
import org.jruby.RubyObject;
import org.jruby.RubyRegexp;
import org.jruby.RubyThread;
import org.jruby.RubyTime;
import org.jruby.anno.JRubyMethod;
import org.jruby.exceptions.RaiseException;
import org.jruby.runtime.Helpers;
@@ -63,8 +65,8 @@ public void load(Ruby runtime, boolean wrap) throws IOException {
// Here we create an "anonymous" exception type used for unrolling the stack.
// MRI creates a new one for *every call* to timeout, which can be costly.
// We opt to use a single exception type for all cases to avoid this overhead.
RubyClass anonEx = runtime.defineClassUnder("AnonymousException", runtime.getException(), runtime.getException().getAllocator(), timeout);
anonEx.setBaseName(null); // clear basename so it's anonymous when raising
RubyClass anonException = runtime.defineClassUnder("AnonymousException", runtime.getException(), runtime.getException().getAllocator(), timeout);
anonException.setBaseName(null); // clear basename so it's anonymous when raising

// These are not really used by timeout, but exposed for compatibility
timeout.defineConstant("THIS_FILE", RubyRegexp.newRegexp(runtime, "timeout\\.rb", new RegexpOptions()));
@@ -83,8 +85,8 @@ public void load(Ruby runtime, boolean wrap) throws IOException {
public static class TimeoutToplevel {
@JRubyMethod(required = 1, optional = 1, visibility = PRIVATE)
public static IRubyObject timeout(ThreadContext context, IRubyObject self, IRubyObject[] args, Block block) {
RubyModule timeout = context.runtime.getModule("Timeout");
final RubyModule timeout = context.runtime.getModule("Timeout");

switch (args.length) {
case 1:
return Timeout.timeout(context, timeout, args[0], block);
@@ -100,7 +102,7 @@ public static IRubyObject timeout(ThreadContext context, IRubyObject self, IRuby
@JRubyMethod(module = true)
public static IRubyObject timeout(final ThreadContext context, IRubyObject timeout, IRubyObject seconds, Block block) {
// No seconds, just yield
if (seconds.isNil() || Helpers.invoke(context, seconds, "zero?").isTrue()) {
if ( nilOrZeroSeconds(context, seconds) ) {
return block.yieldSpecific(context);
}

@@ -115,31 +117,21 @@ public static IRubyObject timeout(final ThreadContext context, IRubyObject timeo
final AtomicBoolean latch = new AtomicBoolean(false);

IRubyObject id = new RubyObject(runtime, runtime.getObject());
Runnable timeoutRunnable = prepareRunnable(currentThread, runtime, latch, id);
Future timeoutFuture = null;
Runnable timeoutRunnable = TimeoutTask.newAnnonymousTask(currentThread, timeout, latch, id);

try {
try {
timeoutFuture = timeoutExecutor.schedule(timeoutRunnable,
(long)(seconds.convertToFloat().getDoubleValue() * 1000000), TimeUnit.MICROSECONDS);

return block.yield(context, seconds);
} finally {
killTimeoutThread(context, timeoutFuture, latch);
}
} catch (RaiseException re) {
if (re.getException().getInternalVariable("__identifier__") == id) {
return raiseTimeoutError(context, re);
} else {
throw re;
}
return yieldWithTimeout(context, seconds, block, timeoutRunnable, latch);
}
catch (RaiseException re) {
raiseTimeoutErrorIfMatches(context, timeout, re, id);
throw re;
}
}

@JRubyMethod(module = true)
public static IRubyObject timeout(final ThreadContext context, IRubyObject timeout, IRubyObject seconds, IRubyObject exceptionType, Block block) {
// No seconds, just yield
if (seconds.isNil() || Helpers.invoke(context, seconds, "zero?").isTrue()) {
if ( nilOrZeroSeconds(context, seconds) ) {
return block.yieldSpecific(context);
}

@@ -154,30 +146,19 @@ public static IRubyObject timeout(final ThreadContext context, IRubyObject timeo
final AtomicBoolean latch = new AtomicBoolean(false);

IRubyObject id = new RubyObject(runtime, runtime.getObject());
RubyClass anonException = (RubyClass)runtime.getClassFromPath("Timeout::AnonymousException");
Runnable timeoutRunnable = exceptionType.isNil() ?
prepareRunnable(currentThread, runtime, latch, id) :
prepareRunnableWithException(currentThread, exceptionType, runtime, latch);
Future timeoutFuture = null;
TimeoutTask.newAnnonymousTask(currentThread, timeout, latch, id) :
TimeoutTask.newTaskWithException(currentThread, timeout, latch, exceptionType);

try {
try {
timeoutFuture = timeoutExecutor.schedule(timeoutRunnable,
(long)(seconds.convertToFloat().getDoubleValue() * 1000000), TimeUnit.MICROSECONDS);

return block.yield(context, seconds);
} finally {
killTimeoutThread(context, timeoutFuture, latch);
}
} catch (RaiseException re) {
return yieldWithTimeout(context, seconds, block, timeoutRunnable, latch);
}
catch (RaiseException re) {
// if it's the exception we're expecting
if (re.getException().getMetaClass() == anonException) {
if (re.getException().getMetaClass() == getAnonymousException(timeout)) {
// and we were not given a specific exception
if (exceptionType.isNil()) {
// and it's the exception intended for us
if (re.getException().getInternalVariable("__identifier__") == id) {
return raiseTimeoutError(context, re);
}
if ( exceptionType.isNil() ) {
raiseTimeoutErrorIfMatches(context, timeout, re, id);
}
}

@@ -186,36 +167,80 @@ public static IRubyObject timeout(final ThreadContext context, IRubyObject timeo
}
}

private static Runnable prepareRunnable(final RubyThread currentThread, final Ruby runtime, final AtomicBoolean latch, final IRubyObject id) {
Runnable timeoutRunnable = new Runnable() {
public void run() {
if (latch.compareAndSet(false, true)) {
if (currentThread.isAlive()) {
RubyClass anonException = (RubyClass)runtime.getClassFromPath("Timeout::AnonymousException");
IRubyObject anonExceptionObj = anonException.newInstance(runtime.getCurrentContext(), runtime.newString("execution expired"), Block.NULL_BLOCK);
anonExceptionObj.getInternalVariables().setInternalVariable("__identifier__", id);
currentThread.raise(anonExceptionObj);
}
}
}
};
return timeoutRunnable;
private static boolean nilOrZeroSeconds(final ThreadContext context, final IRubyObject seconds) {
return seconds.isNil() || Helpers.invoke(context, seconds, "zero?").isTrue();
}

private static Runnable prepareRunnableWithException(final RubyThread currentThread, final IRubyObject exception, final Ruby runtime, final AtomicBoolean latch) {
Runnable timeoutRunnable = new Runnable() {
public void run() {
if (latch.compareAndSet(false, true)) {
if (currentThread.isAlive()) {
currentThread.genericRaise(runtime, new IRubyObject[]{exception, runtime.newString("execution expired")}, null);
}
private static IRubyObject yieldWithTimeout(ThreadContext context,
final IRubyObject seconds, final Block block,
final Runnable runnable, final AtomicBoolean latch) throws RaiseException {

final long micros = (long) ( RubyTime.convertTimeInterval(context, seconds) * 1000000 );
Future timeoutFuture = null;
try {
timeoutFuture = timeoutExecutor.schedule(runnable, micros, TimeUnit.MICROSECONDS);
return block.yield(context, seconds);
}
finally {
if ( timeoutFuture != null ) killTimeoutThread(context, timeoutFuture, latch);
// ... when timeoutFuture == null there's likely an error thrown from schedule
}
}

private static class TimeoutTask implements Runnable {

final RubyThread currentThread;
final AtomicBoolean latch;

final IRubyObject timeout; // Timeout module
final IRubyObject id; // needed for 'anonymous' timeout (no exception passed)
final IRubyObject exception; // if there's exception (type) passed to timeout

private TimeoutTask(final RubyThread currentThread, final IRubyObject timeout,
final AtomicBoolean latch, final IRubyObject id, final IRubyObject exception) {
this.currentThread = currentThread;
this.timeout = timeout;
this.latch = latch;
this.id = id;
this.exception = exception;
}

static TimeoutTask newAnnonymousTask(final RubyThread currentThread, final IRubyObject timeout,
final AtomicBoolean latch, final IRubyObject id) {
return new TimeoutTask(currentThread, timeout, latch, id, null);
}

static TimeoutTask newTaskWithException(final RubyThread currentThread, final IRubyObject timeout,
final AtomicBoolean latch, final IRubyObject exception) {
return new TimeoutTask(currentThread, timeout, latch, null, exception);
}

public void run() {
if ( latch.compareAndSet(false, true) ) {
if ( exception == null ) {
raiseAnnonymous();
}
else {
raiseException();
}
}
};
return timeoutRunnable;
}

private void raiseAnnonymous() {
final Ruby runtime = timeout.getRuntime();
IRubyObject anonException = getAnonymousException(timeout).newInstance(runtime.getCurrentContext(), runtime.newString("execution expired"), Block.NULL_BLOCK);
anonException.getInternalVariables().setInternalVariable("__identifier__", id);
currentThread.internalRaise(new IRubyObject[] { anonException });
}

private void raiseException() {
final Ruby runtime = timeout.getRuntime();
currentThread.internalRaise(new IRubyObject[]{ exception, runtime.newString("execution expired") });
}

}

private static void killTimeoutThread(ThreadContext context, Future timeoutFuture, AtomicBoolean latch) {
private static void killTimeoutThread(ThreadContext context, final Future timeoutFuture, final AtomicBoolean latch) {
if (latch.compareAndSet(false, true) && timeoutFuture.cancel(false)) {
// ok, exception will not fire
if (timeoutExecutor instanceof ScheduledThreadPoolExecutor && timeoutFuture instanceof Runnable) {
@@ -225,10 +250,9 @@ private static void killTimeoutThread(ThreadContext context, Future timeoutFutur
// future is not cancellable, wait for it to run and then poll
try {
timeoutFuture.get();
} catch (ExecutionException ex) {
} catch (InterruptedException ex) {
}

catch (ExecutionException ex) {}
catch (InterruptedException ex) {}
// poll to propagate exception from child thread
context.pollThreadEvents();
}
@@ -237,19 +261,42 @@ private static void killTimeoutThread(ThreadContext context, Future timeoutFutur
private static IRubyObject raiseBecauseCritical(ThreadContext context) {
Ruby runtime = context.runtime;

return RubyKernel.raise(context, runtime.getKernel(), new IRubyObject[]{runtime.getThreadError(), runtime.newString("timeout within critical section")}, Block.NULL_BLOCK);
}

private static IRubyObject raiseTimeoutError(ThreadContext context, RaiseException re) {
Ruby runtime = context.runtime;

return RubyKernel.raise(
context,
runtime.getKernel(),
new IRubyObject[]{
runtime.getClassFromPath("Timeout::Error"),
re.getException().callMethod(context, "message"),
re.getException().callMethod(context, "backtrace")},
new IRubyObject[] {
runtime.getThreadError(),
runtime.newString("timeout within critical section")
},
Block.NULL_BLOCK);
}

private static IRubyObject raiseTimeoutErrorIfMatches(ThreadContext context,
final IRubyObject timeout, final RaiseException ex, final IRubyObject id) {
// check if it's the exception intended for us (@see prepareRunnable):
if ( ex.getException().getInternalVariable("__identifier__") == id ) {
final RubyException rubyException = ex.getException();

return RubyKernel.raise( // throws
context,
context.runtime.getKernel(),
new IRubyObject[] {
getClassFrom(timeout, "Error"), // Timeout::Error
rubyException.callMethod(context, "message"),
rubyException.callMethod(context, "backtrace")
},
Block.NULL_BLOCK);
}
return null;
}

// Timeout::AnonymousException (@see above)
private static RubyClass getAnonymousException(final IRubyObject timeout) {
return getClassFrom(timeout, "AnonymousException");
}

private static RubyClass getClassFrom(final IRubyObject timeout, final String name) {
return ((RubyModule) timeout).getClass(name); // Timeout::[name]
}

}
38 changes: 18 additions & 20 deletions core/src/main/java/org/jruby/java/proxies/JavaProxy.java
Original file line number Diff line number Diff line change
@@ -313,10 +313,7 @@ public IRubyObject java_send(ThreadContext context, IRubyObject rubyName, IRubyO
RubyArray argTypesAry = argTypes.convertToArray();
Ruby runtime = context.runtime;

if (argTypesAry.size() != 0) {
Class[] argTypesClasses = (Class[])argTypesAry.toArray(new Class[argTypesAry.size()]);
throw JavaMethod.newArgSizeMismatchError(runtime, argTypesClasses);
}
checkArgSizeMismatch(runtime, 0, argTypesAry);

JavaMethod method = new JavaMethod(runtime, getMethod(context, name));
return method.invokeDirect(context, getObject());
@@ -328,12 +325,9 @@ public IRubyObject java_send(ThreadContext context, IRubyObject rubyName, IRubyO
RubyArray argTypesAry = argTypes.convertToArray();
Ruby runtime = context.runtime;

if (argTypesAry.size() != 1) {
Class[] argTypesClasses = (Class[])argTypesAry.toArray(new Class[argTypesAry.size()]);
throw JavaMethod.newArgSizeMismatchError(runtime, argTypesClasses);
}
checkArgSizeMismatch(runtime, 1, argTypesAry);

Class argTypeClass = (Class)argTypesAry.eltInternal(0).toJava(Class.class);
Class argTypeClass = (Class) argTypesAry.eltInternal(0).toJava(Class.class);

JavaMethod method = new JavaMethod(runtime, getMethod(context, name, argTypeClass));
return method.invokeDirect(context, getObject(), arg0.toJava(argTypeClass));
@@ -345,22 +339,26 @@ public IRubyObject java_send(ThreadContext context, IRubyObject[] args) {

String name = args[0].asJavaString();
RubyArray argTypesAry = args[1].convertToArray();
int argsLen = args.length - 2;
final int argsLen = args.length - 2;

if (argTypesAry.size() != argsLen) {
Class[] argTypesClasses = (Class[])argTypesAry.toArray(new Class[argTypesAry.size()]);
throw JavaMethod.newArgSizeMismatchError(runtime, argTypesClasses);
}
checkArgSizeMismatch(runtime, argsLen, argTypesAry);

Class[] argTypesClasses = (Class[])argTypesAry.toArray(new Class[argsLen]);
Class[] argTypesClasses = (Class[]) argTypesAry.toArray(new Class[argsLen]);

Object[] argsAry = new Object[argsLen];
for (int i = 0; i < argsLen; i++) {
argsAry[i] = args[i + 2].toJava(argTypesClasses[i]);
Object[] javaArgs = new Object[argsLen];
for ( int i = 0; i < argsLen; i++ ) {
javaArgs[i] = args[i + 2].toJava( argTypesClasses[i] );
}

JavaMethod method = new JavaMethod(runtime, getMethod(context, name, argTypesClasses));
return method.invokeDirect(context, getObject(), argsAry);
return method.invokeDirect(context, getObject(), javaArgs);
}

private static void checkArgSizeMismatch(final Ruby runtime, final int expected, final RubyArray argTypes) {
if ( argTypes.size() != expected ) {
Class[] argTypesClasses = (Class[]) argTypes.toArray(new Class[argTypes.size()]);
throw JavaMethod.newArgSizeMismatchError(runtime, argTypesClasses);
}
}

@JRubyMethod
@@ -374,7 +372,7 @@ public IRubyObject java_method(ThreadContext context, IRubyObject rubyName) {
public IRubyObject java_method(ThreadContext context, IRubyObject rubyName, IRubyObject argTypes) {
String name = rubyName.asJavaString();
RubyArray argTypesAry = argTypes.convertToArray();
Class[] argTypesClasses = (Class[])argTypesAry.toArray(new Class[argTypesAry.size()]);
Class[] argTypesClasses = (Class[]) argTypesAry.toArray(new Class[argTypesAry.size()]);

return getRubyMethod(context, name, argTypesClasses);
}
Original file line number Diff line number Diff line change
@@ -9,7 +9,7 @@ public enum Priority {
RESERVED(0), METHOD(1), FIELD(2), PROTECTED_METHOD(3),
WEAKLY_RESERVED(4), ALIAS(5), PROTECTED_FIELD(6);

private int value;
private final int value;

Priority(int value) {
this.value = value;
378 changes: 185 additions & 193 deletions core/src/main/java/org/jruby/util/Dir.java

Large diffs are not rendered by default.

38 changes: 21 additions & 17 deletions core/src/main/java/org/jruby/util/ShellLauncher.java
Original file line number Diff line number Diff line change
@@ -162,10 +162,10 @@ private Map<String, String> environmentMap(String[] env) {

public void start() throws IOException {
config = new RubyInstanceConfig(parentRuntime.getInstanceConfig());

config.setEnvironment(environmentMap(env));
config.setCurrentDirectory(pwd.toString());

if (pipedStreams) {
config.setInput(new PipedInputStream(processInput));
config.setOutput(new PrintStream(new PipedOutputStream(processOutput)));
@@ -294,7 +294,7 @@ public static String[] getModifiedEnv(Ruby runtime, Collection mergeEnv, boolean
}
}
}

ary = new String[i];
System.arraycopy(ret, 0, ary, 0, i);
return ary;
@@ -479,13 +479,13 @@ public static long runExternal(Ruby runtime, IRubyObject env, IRubyObject prog,
if (env.isNil() || !(env instanceof Map)) {
env = null;
}

IRubyObject[] rawArgs = args.convertToArray().toJavaArray();

OutputStream output = runtime.getOutputStream();
OutputStream error = runtime.getErrorStream();
InputStream input = runtime.getInputStream();

try {
Process aProcess = null;
File pwd = new File(runtime.getCurrentDirectory());
@@ -506,7 +506,7 @@ public static long runExternal(Ruby runtime, IRubyObject env, IRubyObject prog,
} catch (SecurityException se) {
throw runtime.newSecurityError(se.getLocalizedMessage());
}

if (wait) {
handleStreams(runtime, aProcess, input, output, error);
try {
@@ -616,22 +616,22 @@ public static long getPidFromProcess(Process process) {
return reflectPidFromProcess(process);
}
}

private static final Class UNIXProcess;
private static final Field UNIXProcess_pid;
private static final Class ProcessImpl;
private static final Field ProcessImpl_handle;
private interface PidGetter { public long getPid(Process process); }
private static final PidGetter PID_GETTER;

static {
// default PidGetter
PidGetter pg = new PidGetter() {
public long getPid(Process process) {
return process.hashCode();
}
};

Class up = null;
Field pid = null;
try {
@@ -731,7 +731,11 @@ public static POpenProcess popen(Ruby runtime, IRubyObject string, ModeFlags mod
public static POpenProcess popen(Ruby runtime, IRubyObject[] strings, Map env, ModeFlags modes) throws IOException {
return new POpenProcess(popenShared(runtime, strings, env), runtime, modes);
}


public static POpenProcess popen(Ruby runtime, IRubyObject string, Map env, ModeFlags modes) throws IOException {
return new POpenProcess(popenShared(runtime, new IRubyObject[] {string}, env, true), runtime, modes);
}

@Deprecated
public static POpenProcess popen(Ruby runtime, IRubyObject string, IOOptions modes) throws IOException {
return new POpenProcess(popenShared(runtime, new IRubyObject[] {string}, null, true), runtime, modes);
@@ -757,7 +761,7 @@ private static Process popenShared(Ruby runtime, IRubyObject[] strings) throws I
}

private static Process popenShared(Ruby runtime, IRubyObject[] strings, Map env) throws IOException {
return popenShared(runtime, strings, env, true);
return popenShared(runtime, strings, env, false);
}

private static Process popenShared(Ruby runtime, IRubyObject[] strings, Map env, boolean addShell) throws IOException {
@@ -840,7 +844,7 @@ public static OutputStream unwrapBufferedStream(OutputStream filteredStream) {
*/
public static InputStream unwrapBufferedStream(InputStream filteredStream) {
if (RubyInstanceConfig.NO_UNWRAP_PROCESS_STREAMS) return filteredStream;

// Java 7+ uses a stream that drains the child on exit, which when
// unwrapped breaks because the channel gets drained prematurely.
// System.out.println("class is :" + filteredStream.getClass().getName());
@@ -927,7 +931,7 @@ public static class POpenProcess extends Process {
public POpenProcess(Process child, Ruby runtime, IOOptions modes) {
this(child, runtime, modes.getModeFlags());
}

public POpenProcess(Process child, Ruby runtime, ModeFlags modes) {
this.child = child;

@@ -948,7 +952,7 @@ public POpenProcess(Process child, Ruby runtime, ModeFlags modes) {
pumpInput(child, runtime);
}

pumpInerr(child, runtime);
pumpInerr(child, runtime);
}

public POpenProcess(Process child) {
@@ -1434,7 +1438,7 @@ public static Process run(Ruby runtime, IRubyObject[] rawArgs, boolean doExecuta
} catch (SecurityException se) {
throw runtime.newSecurityError(se.getLocalizedMessage());
}

return aProcess;
}

@@ -1682,7 +1686,7 @@ private static String getShell(Ruby runtime) {
return RbConfigLibrary.jrubyShell();
}

private static boolean shouldUseShell(String command) {
public static boolean shouldUseShell(String command) {
boolean useShell = false;
for (char c : command.toCharArray()) {
if (c != ' ' && !Character.isLetter(c) && "*?{}[]<>()~&|\\$;'`\"\n".indexOf(c) != -1) {
7 changes: 6 additions & 1 deletion core/src/main/ruby/jruby/kernel/jruby/process_manager.rb
Original file line number Diff line number Diff line change
@@ -8,6 +8,7 @@ module ProcessManager
java_import org.jruby.util.ShellLauncher
java_import java.lang.ProcessBuilder
java_import org.jruby.runtime.builtin.IRubyObject
java_import org.jruby.platform.Platform

Redirect = ProcessBuilder::Redirect
LaunchConfig = ShellLauncher::LaunchConfig
@@ -18,14 +19,18 @@ def self.`(command)

config = LaunchConfig.new(JRuby.runtime, [command].to_java(IRubyObject), false)

if config.should_run_in_shell?
use_shell = Platform::IS_WINDOWS ? config.should_run_in_shell : false
use_shell |= ShellLauncher.should_use_shell(command)

if use_shell
config.verify_executable_for_shell
else
config.verify_executable_for_direct
end

pb = ProcessBuilder.new(config.exec_args)
pb.redirect_input(Redirect::INHERIT)
pb.redirect_error(Redirect::INHERIT)
pb.environment(ShellLauncher.get_current_env(JRuby.runtime))
pb.directory(JFile.new(JRuby.runtime.current_directory))
process = pb.start
6 changes: 3 additions & 3 deletions lib/ruby/stdlib/date/format.rb
Original file line number Diff line number Diff line change
@@ -939,7 +939,7 @@ def self._iso8601(str) # :nodoc:
h[:sec] = i sec if sec
end

h[:sec_fraction] = sec_fraction if sec_fraction
h[:sec_fraction] = Rational(sec_fraction.to_i, 10**sec_fraction.size) if sec_fraction
set_zone(h, zone)

elsif /\A\s*
@@ -981,7 +981,7 @@ def self._iso8601(str) # :nodoc:
h[:sec] = i sec if sec
end

h[:sec_fraction] = sec_fraction if sec_fraction
h[:sec_fraction] = Rational(sec_fraction.to_i, 10**sec_fraction.size) if sec_fraction
set_zone(h, zone)

elsif /\A\s*
@@ -1004,7 +1004,7 @@ def self._iso8601(str) # :nodoc:
h[:hour] = i hour
h[:min] = i min
h[:sec] = i sec if sec
h[:sec_fraction] = i sec_fraction if sec_fraction
h[:sec_fraction] = Rational(sec_fraction.to_i, 10**sec_fraction.size) if sec_fraction
set_zone(h, zone)
end
h
1 change: 1 addition & 0 deletions lib/ruby/truffle/mri/mutex_m.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
require_relative '../../stdlib/mutex_m'
2 changes: 1 addition & 1 deletion maven/jruby-complete/pom.rb
Original file line number Diff line number Diff line change
@@ -96,7 +96,7 @@

plugin 'org.codehaus.mojo:build-helper-maven-plugin' do
execute_goals( 'attach-artifact',
:id => 'attach javadocs and sources artifacts',
:id => 'attach-artifacts',
'artifacts' => [ { 'file' => '${project.build.directory}/jruby-core-${project.version}-sources.jar',
'classifier' => 'sources' },
{ 'file' => '${project.build.directory}/jruby-core-${project.version}-javadoc.jar',
47 changes: 42 additions & 5 deletions spec/java_integration/methods/java_send_spec.rb
Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@
@list = java.util.ArrayList.new
@integer = java.lang.Integer.new(1)
end

it "works with name only for no-arg methods" do
@list.java_send(:toString).should == "[]"
java.lang.System.java_send(:currentTimeMillis).class.should == Fixnum
@@ -15,19 +15,56 @@
@list.java_send(:toString, []).should == "[]"
java.lang.System.java_send(:currentTimeMillis, []).class.should == Fixnum
end

it "works with a signature" do
@list.java_send :add, [Java::int, java.lang.Object], 0, 'foo'
@list.to_s.should == "[foo]"

java_home = java.lang.System.java_send(:getProperty, [java.lang.String], 'java.home')
java_home.should == java.lang.System.getProperty('java.home')

str = java.lang.StringBuilder.new
str.java_send :append, [Java::long], 1234567890
str.to_s.should == '1234567890'
str.java_send :append, [java.lang.String], " str1"
str.to_s.should == '1234567890 str1'
str.java_send :append, [java.lang.CharSequence], " str2"
str.to_s.should == '1234567890 str1 str2'
str.java_send :append, [Java::char[], Java::int, Java::int], " str3".to_java.to_char_array, 0, 4
str.to_s.should == '1234567890 str1 str2 str'
end

it "works with package-level classes" do
array = Java::int[16].new
array[1] = 10; array[2] = 20

buffer = java.nio.IntBuffer.wrap array # returns a Java::JavaNio::HeapIntBuffer
buffer.java_send(:get, [ Java::int ], 1).should == 10
buffer.java_send(:get).should == 0
buffer.java_send(:get).should == 10
buffer.java_send(:get, []).should == 20
end


it "works with private classes" do
array = Java::int[16].new
array[1] = 10; array[2] = 20

map = java.util.HashMap.new
key_type = java.lang.String.java_class
val_type = java.lang.Number.java_class

map = java.util.Collections.checkedMap(map, key_type, val_type) # returns a private CheckedMap instance
map.java_send(:clear)
map.java_send(:put, [ Java::JavaLang::Object, Java::JavaLang::Object ], '1', 1.to_java)
map.java_send(:get, [ Java::JavaLang::Object ], '').should == nil
map.java_send(:get, [ Java::JavaLang::Object ], '1').should == 1
end

it "raises NameError if the method can't be found" do
lambda do
@list.java_send :foobar
end.should raise_error(NameError)

lambda do
@list.java_send :add, [Java::long, java.lang.Object], 0, 'foo'
end.should raise_error(NameError)
@@ -40,7 +77,7 @@
java.lang.System.java_send :getProperty, [Java::long, Java::long], 0, 0
end.should raise_error(NameError)
end

it "raises ArgumentError if type count doesn't match arg count" do
lambda do
@list.java_send :add, [Java::int, java.lang.Object], 0, 'foo', 'bar'
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
require 'base64'

describe "A badly-encoded UTF-8 String reencoded with replacements as UTF-16 " do
it "completes for all inputs" do
random = Random.new
# We obviously can't test all valid inputs, but we use the script from #2856 to try
10_000.times do
data = random.bytes(1000)
data.force_encoding("UTF-8")
data = data.encode("UTF-16", :undef => :replace, :invalid => :replace, :replace => '')

expect(data).to_not eq(nil)
end
end
end unless RUBY_VERSION.index('1.8') == 0
14 changes: 14 additions & 0 deletions spec/regression/GH-2883_date_sec_fraction_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
require 'rspec'
require 'date'

# https://github.com/jruby/jruby/issues/2883

if RUBY_VERSION > '1.9'
describe 'DateTime.iso8601' do
it 'correctly parses fraction of a second' do
date = DateTime.iso8601('2014-07-08T17:51:36.013Z')
date.sec_fraction.should == Rational(13, 1000)
date.second_fraction.should == Rational(13, 1000)
end
end
end
51 changes: 0 additions & 51 deletions spec/ruby/core/hash/compare_by_identity_spec.rb
Original file line number Diff line number Diff line change
@@ -11,57 +11,6 @@
@h["a".dup].should be_nil
end

ruby_version_is ''...'2.2' do
it "causes future comparisons on the receiver to be made by identity" do
@h["a"] = :a
@h["a"].should == :a
@h.compare_by_identity
@h["a"].should be_nil
end

it "uses the semantics of BasicObject#equal? to determine key identity" do
-0.0.should_not equal(-0.0) # -0.0 is not flonum
@idh[-0.0] = :a
@idh[-0.0] = :b
[1].should_not equal([1])
@idh[[1]] = :c
@idh[[1]] = :d
:bar.should equal(:bar)
@idh[:bar] = :e
@idh[:bar] = :f
"bar".should_not equal('bar')
@idh["bar"] = :g
@idh["bar"] = :h
@idh.values.should == [:a, :b, :c, :d, :f, :g, :h]
end
end

ruby_version_is '2.2' do
it "causes future comparisons on the receiver to be made by identity" do
@h["a"] = :a
@h["a"].should == :a
@h.compare_by_identity
@h["a"].should == :a
end

it "uses the semantics of BasicObject#equal? to determine key identity" do
-0.0.should_not equal(-0.0) # -0.0 is not flonum
@idh[-0.0] = :a
@idh[-0.0] = :b
[1].should_not equal([1])
@idh[[1]] = :c
@idh[[1]] = :d
:bar.should equal(:bar)
@idh[:bar] = :e
@idh[:bar] = :f
# CRuby r44551 [ruby-core:59640] [Bug #9382]
"bar".should_not equal('bar')
@idh["bar"] = :g
@idh["bar"] = :h
@idh.values.should == [:a, :b, :c, :d, :f, :h]
end
end

it "returns self" do
h = new_hash
h[:foo] = :bar
20 changes: 11 additions & 9 deletions spec/ruby/core/hash/reject_spec.rb
Original file line number Diff line number Diff line change
@@ -25,15 +25,17 @@ def h.to_a() end
h.reject { false }.to_a.should == [[1, 2]]
end

ruby_bug "extra states should not be copied", "2.1" do
it "returns Hash instance for subclasses" do
HashSpecs::MyHash[1 => 2, 3 => 4].reject { false }.should be_kind_of(Hash)
HashSpecs::MyHash[1 => 2, 3 => 4].reject { true }.should be_kind_of(Hash)
end

it "taints the resulting hash" do
h = new_hash(:a => 1).taint
h.reject {false}.tainted?.should == false
ruby_version_is "2.2" do
context "with extra state" do
it "returns Hash instance for subclasses" do
HashSpecs::MyHash[1 => 2, 3 => 4].reject { false }.should be_kind_of(Hash)
HashSpecs::MyHash[1 => 2, 3 => 4].reject { true }.should be_kind_of(Hash)
end

it "does not taint the resulting hash" do
h = new_hash(:a => 1).taint
h.reject {false}.tainted?.should == false
end
end
end

2 changes: 0 additions & 2 deletions spec/truffle/tags/core/hash/compare_by_identity_tags.txt

This file was deleted.

1 change: 0 additions & 1 deletion spec/truffle/tags/core/hash/element_reference_tags.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
fails:Hash#[] finds a value via an identical key even when its #eql? isn't reflexive
fails:Hash#[] does not compare keys with different #hash values via #eql?
1 change: 0 additions & 1 deletion spec/truffle/tags/core/hash/eql_tags.txt

This file was deleted.

1 change: 0 additions & 1 deletion spec/truffle/tags/core/hash/equal_value_tags.txt

This file was deleted.

1 change: 0 additions & 1 deletion spec/truffle/tags/core/hash/rehash_tags.txt

This file was deleted.

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

This file was deleted.

46 changes: 25 additions & 21 deletions test/jruby/test_dir.rb
Original file line number Diff line number Diff line change
@@ -24,22 +24,22 @@ def teardown

# JRUBY-2519
def test_dir_instance_should_not_cache_dir_contents

require 'fileutils'
require 'tmpdir'

testdir = File.join(Dir.tmpdir, Process.pid.to_s)
FileUtils.mkdir_p testdir

FileUtils.touch File.join(testdir, 'fileA.txt')
dir = Dir.new(testdir)
FileUtils.touch File.join(testdir, 'fileB.txt')
dir.rewind # does nothing

assert_equal 'fileA.txt', dir.find {|item| item == 'fileA.txt' }
assert_equal 'fileB.txt', dir.find {|item| item == 'fileB.txt' }
end
end

def test_pwd_and_getwd_equivalent
assert_equal(Dir.pwd, Dir.getwd)
end
@@ -72,7 +72,7 @@ def test_dir_entries
end

def test_bogus_glob
# Test unescaped special char that is meant to be used with another
# Test unescaped special char that is meant to be used with another
# (i.e. bogus glob pattern)
assert_equal([], Dir.glob("{"))
end
@@ -83,7 +83,7 @@ def test_glob_empty_string
end

def test_glob_double_star
# Test that glob expansion of ** works ok with non-patterns as path
# Test that glob expansion of ** works ok with non-patterns as path
# elements. This used to throw NPE.
Dir.mkdir("testDir_2")
open("testDir_2/testDir_tmp1", "w").close
@@ -114,6 +114,10 @@ def test_glob_on_shared_string
Dir["blahtest/test_argf.rb"[4..-1]]
end

def test_glob_empty_parens
assert_equal [], Dir['{}'] # #2922 throwing AIOoBE is <= 1.7.20
end

# http://jira.codehaus.org/browse/JRUBY-300
def test_chdir_and_pwd
java_test_classes = File.expand_path(File.dirname(__FILE__) + '/../target/test-classes')
@@ -124,7 +128,7 @@ def test_chdir_and_pwd
pwd.gsub! '\\', '/'
assert_equal("testDir_4", pwd.split("/")[-1].strip)

if (ENV_JAVA['jruby.home'] and not
if (ENV_JAVA['jruby.home'] and not
ENV_JAVA['jruby.home'].match( /!\// ) and not
ENV_JAVA['jruby.home'].match( /:\// ))
pwd = `#{ENV_JAVA['jruby.home']}/bin/jruby -e "puts Dir.pwd"`
@@ -218,7 +222,7 @@ def xxx_test_mktmpdir
# JRUBY-4983
def test_entries_unicode
utf8_dir = "testDir_1/glk\u00a9"

Dir.mkdir("./testDir_1")
Dir.mkdir(utf8_dir)

@@ -270,7 +274,7 @@ def restore_cwd
def test_chdir_exceptions_windows
orig_pwd = Dir.pwd
assert_raise(Errno::EINVAL) {
Dir.chdir('//') # '//' is not a valid thing on Windows
Dir.chdir('//') # '//' is not a valid thing on Windows
}
assert_raise(Errno::ENOENT) {
Dir.chdir('//blah-blah-blah') # doesn't exist
@@ -290,7 +294,7 @@ def test_chdir_exceptions_windows
ensure
Dir.chdir(orig_pwd)
end

def test_new_windows
slashes = ['/', '\\']
slashes.each { |slash|
@@ -307,7 +311,7 @@ def test_new_windows
assert_equal(drive_root_entries, slash_entries, "slash - #{slash}")
}
end

def test_new_with_drive_letter
current_drive_letter = Dir.pwd[0..2]

@@ -319,7 +323,7 @@ def test_new_with_drive_letter
Dir.new(current_drive_letter + "\\").entries,
Dir.new(current_drive_letter).entries)
end

def test_entries_with_drive_letter
current_drive_letter = Dir.pwd[0..2]

@@ -331,7 +335,7 @@ def test_entries_with_drive_letter
Dir.entries(current_drive_letter + "\\"),
Dir.entries(current_drive_letter))
end

def test_open_windows
slashes = ['/', '\\']
slashes.each { |slash|
@@ -348,13 +352,13 @@ def test_open_windows
assert_equal(drive_root_entries, slash_entries, "slash - #{slash}")
}
end

def test_dir_new_exceptions_windows
assert_raise(Errno::ENOENT) {
Dir.new('')
}
assert_raise(Errno::EINVAL) {
Dir.new('//') # '//' is not a valid thing on Windows
Dir.new('//') # '//' is not a valid thing on Windows
}
assert_raise(Errno::ENOENT) {
Dir.new('//blah-blah-blah') # doesn't exist
@@ -372,7 +376,7 @@ def test_dir_new_exceptions_windows
Dir.new('\\\\\\') # doesn't exist
}
end

def test_entries_windows
slashes = ['/', '\\']
slashes.each { |slash|
@@ -388,7 +392,7 @@ def test_entries_exceptions_windows
Dir.entries('')
}
assert_raise(Errno::EINVAL) {
Dir.entries('//') # '//' is not a valid thing on Windows
Dir.entries('//') # '//' is not a valid thing on Windows
}
assert_raise(Errno::ENOENT) {
Dir.entries('//blah-blah-blah') # doesn't exist
@@ -447,11 +451,11 @@ def test_pathname_realpath_works_with_drive_letters
require 'pathname'
win_dir = nil
if FileTest.exist?('C:/windows')
win_dir = "windows"
win_dir = "windows"
elsif FileTest.exist?('C:/winnt')
win_dir = "winnt"
win_dir = "winnt"
end

if (win_dir != nil)
Pathname.new("C:\\#{win_dir}").realpath.to_s
Pathname.new("C:\\#{win_dir}\\..\\#{win_dir}").realpath.to_s
123 changes: 89 additions & 34 deletions test/jruby/test_kernel.rb
Original file line number Diff line number Diff line change
@@ -57,7 +57,7 @@ class ToAryReturnsAnInteger; def to_ary() 2 end; end

def test_Array_should_use_to_ary_and_make_sure_that_it_returns_either_array_or_nil
assert_equal [1], Kernel.Array(ToAryReturnsAnArray.new)
assert_raises(TypeError) { Kernel.Array(ToAryReturnsAnInteger.new) }
assert_raises(TypeError) { Kernel.Array(ToAryReturnsAnInteger.new) }
end

class BothToAryAndToADefined; def to_ary() [3] end; def to_a() raise "to_a() should not be called" end; end
@@ -71,7 +71,7 @@ class NeitherToANorToAryDefined; end
def test_Array_should_return_array_containing_argument_if_the_argument_has_neither_to_ary_nor_to_a
assert_equal [1], Kernel.Array(1)
assert_equal [:foo], Kernel.Array(:foo)
obj = NeitherToANorToAryDefined.new
obj = NeitherToANorToAryDefined.new
assert_equal [obj], Kernel.Array(obj)
end

@@ -142,7 +142,7 @@ def test_throw_should_bubble_up_to_the_right_catch
end
been_at_fred2 = true
end
assert been_at_fred2
assert been_at_fred2
end

def test_invalid_throw_after_inner_catch_should_unwind_the_stack_all_the_way_to_the_top
@@ -200,8 +200,8 @@ def test_eval_should_not_bring_local_variables_defined_in_its_input_to_parent_sc

Kernel.eval("new_variable = another_variable = existing_variable")

assert_equal 0, existing_variable
assert_equal 0, another_variable
assert_equal 0, existing_variable
assert_equal 0, another_variable
assert !(defined? new_variable)
end

@@ -214,7 +214,7 @@ def test_eval_should_not_bring_local_variables_defined_in_its_input_to_parent_sc
# fork

def test_format
assert_equal "Hello, world", Kernel.format("Hello, %s", "world")
assert_equal "Hello, world", Kernel.format("Hello, %s", "world")
assert_raises(TypeError) { Kernel.format("%01.3f", nil) }
end

@@ -360,8 +360,8 @@ def test_raise_in_debug_mode

def test_sleep
assert_raises(ArgumentError) { sleep(-10) }
# FIXME: below is true for MRI, but not for JRuby
# assert_raises(TypeError) { sleep "foo" }
assert_raises(TypeError) { sleep "foo" }

assert_equal 0, sleep(0)
t1 = Time.now
sleep(0.1)
@@ -370,6 +370,63 @@ def test_sleep
assert t2 >= t1 + 0.08
end

class SecDuration

def initialize(value); @value = value end

def respond_to?(name)
@value.respond_to?(name)
end

private

def method_missing(method, *args, &block)
@value.send(method, *args, &block)
end

end

def test_sleep_arg
sleep SecDuration.new(0.01)

begin
sleep SecDuration.new(-0.01)
rescue ArgumentError => e
assert_equal "time interval must be positive", e.message
else
fail 'argument error expected'
end

begin
sleep []
rescue TypeError => e
assert_equal "can't convert Array into time interval", e.message
else
fail 'type error expected'
end
end

class SecDuration19

def initialize(value); @value = value end

def respond_to_missing?(name, include_private = false)
@value.respond_to?(name)
end

private

def method_missing(method, *args, &block)
@value.send(method, *args, &block)
end

end

def test_sleep_arg19
sleep(SecDuration19.new(0.10))
assert_raises(ArgumentError) { sleep(SecDuration19.new(-0.01)) }
end

def test_sprintf
assert_equal 'Hello', Kernel.sprintf('Hello')
end
@@ -497,20 +554,19 @@ def test_exec_non_existing_with_args
end

# JRUBY-4834
# GH #2047 backquote fails with null byte ArgumentError
# def test_backquote_with_changed_path
# orig_env = ENV['PATH']
def test_backquote_with_changed_path
orig_env = ENV['PATH']

# # Append a directory where testapp resides to the PATH
# paths = (ENV["PATH"] || "").split(File::PATH_SEPARATOR)
# paths.unshift TESTAPP_DIR
# ENV["PATH"] = paths.uniq.join(File::PATH_SEPARATOR)
# Append a directory where testapp resides to the PATH
paths = (ENV["PATH"] || "").split(File::PATH_SEPARATOR)
paths.unshift TESTAPP_DIR
ENV["PATH"] = paths.uniq.join(File::PATH_SEPARATOR)

# res = `testapp`.chomp
# assert_equal("NO_ARGS", res)
# ensure
# ENV['PATH'] = orig_env
# end
res = `testapp`.chomp
assert_equal("NO_ARGS", res)
ensure
ENV['PATH'] = orig_env
end

# JRUBY-4127
def test_backquote_with_quotes
@@ -545,20 +601,19 @@ def test_backquote1_1
assert_equal(expected, result)
end

# GH #2047 backquote fails with null byte ArgumentError
# def test_backquote2
# TESTAPPS.each { |app|
# if (app =~ /\/.*\.bat/ && Pathname.new(app).relative?)
# # MRI can't launch relative BAT files with / in their paths
# log "-- skipping #{app}"
# next
# end
# log "testing #{app}"
def test_backquote2
TESTAPPS.each { |app|
if (app =~ /\/.*\.bat/ && Pathname.new(app).relative?)
# MRI can't launch relative BAT files with / in their paths
log "-- skipping #{app}"
next
end
log "testing #{app}"

# result = `#{app}`.strip
# assert_equal('NO_ARGS', result, "Can't properly launch '#{app}'")
# }
# end
result = `#{app}`.strip
assert_equal('NO_ARGS', result, "Can't properly launch '#{app}'")
}
end

def test_backquote2_1
TESTAPPS.each { |app|
@@ -683,7 +738,7 @@ def test_backquote4_1
next
end
log "testing #{app}"

if (TESTAPP_DIR =~ /\s/) # spaces in paths, quote!
app = '"' + app + '"'
end
42 changes: 40 additions & 2 deletions test/jruby/test_respond_to.rb
Original file line number Diff line number Diff line change
@@ -23,7 +23,7 @@ module ClassMethods
attr_reader :hashable_methods
def method_added(method_name)
@hashable_methods ||= []
@hashable_methods << {:method_name => method_name, :key => method_name.to_s }
@hashable_methods << {:method_name => method_name, :key => method_name.to_s }
end
end
end
@@ -85,7 +85,7 @@ class ABasicObject #:nodoc:
instance_methods.each do |m|
undef_method(m) if m.to_s !~ /(?:^__|^nil\?$|^send$|^object_id$)/
end

attr_accessor :respond_to_called

def method_missing(name, *args)
@@ -127,4 +127,42 @@ def test_respond_to_doesnt_fastpath_if_respond_to_missing_exists
obj = Duration.new
assert(10 * obj == 100)
end
end

class TestRespondToMissing < Test::Unit::TestCase

class Foo
def respond_to_missing?(method, private = false)
return true if method.to_s == 'foo'
super(method, private)
end

def method_missing(method, *args)
return method if method.to_s == 'foo'
super
end
end

def test_respond_to_missing
obj = Foo.new
assert obj.respond_to?(:to_s)
assert ! obj.respond_to?(:fo)
assert obj.respond_to?(:foo)

assert_equal :foo, obj.foo
end

class Bar
def respond_to_missing?(method, private = false)
method.eql? :bar
end
end

def test_respond_to_missing_gets_a_symbol_name
obj = Bar.new
assert obj.respond_to?(:bar)
assert ! obj.respond_to?(:ba)
assert obj.respond_to?('bar')
end

end
43 changes: 41 additions & 2 deletions test/jruby/test_timeout.rb
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@
require 'net/http'

class TestTimeout < Test::Unit::TestCase

def test_timeout_for_loop
n = 10000000
assert_raises(Timeout::Error) do
@@ -13,7 +14,7 @@ def test_timeout_for_loop

def do_timeout(time, count, pass_expected, timeout_expected = 0, &block)
pass = timeout = error = 0
count.times do |i|
count.times do
begin
Timeout::timeout(time, &block)
pass += 1
@@ -70,7 +71,7 @@ def test_net_http_timeout
assert_raises Net::OpenTimeout do
http = Net::HTTP.new('8.8.8.8')
http.open_timeout = 0.001
response = http.start do |h|
http.start do |h|
h.request_get '/index.html'
# ensure we timeout even if we're fast
sleep(0.01)
@@ -145,4 +146,42 @@ def test_nested_timeout

assert_equal expected, result
end

class Seconds

attr_reader :value

def initialize(value); @value = value end

def self.===(other); other.is_a?(Seconds) end

def ==(other)
if Seconds === other
other.value == value
else
other == value
end
end

def eql?(other); other.is_a?(Seconds) && self == other end

def divmod(divisor)
value.divmod(divisor)
end

private

def method_missing(method, *args, &block)
value.send(method, *args, &block)
end

end

def test_timeout_interval_argument
assert_equal 42, Timeout::timeout(Seconds.new(2)) { 42 }
assert_raises(Timeout::Error) do
Timeout::timeout(Seconds.new(0.3)) { sleep(0.5) }
end
end

end
1 change: 1 addition & 0 deletions test/mri/excludes_truffle/TestDigest/TestBase.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
exclude :"test_base", "needs investigation"
12 changes: 12 additions & 0 deletions test/mri/excludes_truffle/TestDigest/TestMD5.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
exclude :"test_bubblebabble_class", "needs investigation"
exclude :"test_bubblebabble_instance", "needs investigation"
exclude :"test_eq", "needs investigation"
exclude :"test_s_base64digest", "needs investigation"
exclude :"test_s_file", "needs investigation"
exclude :"test_s_hexdigest", "needs investigation"
exclude :"test_bubblebabble_class", "needs investigation"
exclude :"test_bubblebabble_instance", "needs investigation"
exclude :"test_eq", "needs investigation"
exclude :"test_s_base64digest", "needs investigation"
exclude :"test_s_file", "needs investigation"
exclude :"test_s_hexdigest", "needs investigation"
12 changes: 12 additions & 0 deletions test/mri/excludes_truffle/TestDigest/TestSHA1.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
exclude :"test_bubblebabble_class", "needs investigation"
exclude :"test_bubblebabble_instance", "needs investigation"
exclude :"test_eq", "needs investigation"
exclude :"test_s_base64digest", "needs investigation"
exclude :"test_s_file", "needs investigation"
exclude :"test_s_hexdigest", "needs investigation"
exclude :"test_bubblebabble_class", "needs investigation"
exclude :"test_bubblebabble_instance", "needs investigation"
exclude :"test_eq", "needs investigation"
exclude :"test_s_base64digest", "needs investigation"
exclude :"test_s_file", "needs investigation"
exclude :"test_s_hexdigest", "needs investigation"
12 changes: 12 additions & 0 deletions test/mri/excludes_truffle/TestDigest/TestSHA256.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
exclude :"test_bubblebabble_class", "needs investigation"
exclude :"test_bubblebabble_instance", "needs investigation"
exclude :"test_eq", "needs investigation"
exclude :"test_s_base64digest", "needs investigation"
exclude :"test_s_file", "needs investigation"
exclude :"test_s_hexdigest", "needs investigation"
exclude :"test_bubblebabble_class", "needs investigation"
exclude :"test_bubblebabble_instance", "needs investigation"
exclude :"test_eq", "needs investigation"
exclude :"test_s_base64digest", "needs investigation"
exclude :"test_s_file", "needs investigation"
exclude :"test_s_hexdigest", "needs investigation"
12 changes: 12 additions & 0 deletions test/mri/excludes_truffle/TestDigest/TestSHA384.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
exclude :"test_bubblebabble_class", "needs investigation"
exclude :"test_bubblebabble_instance", "needs investigation"
exclude :"test_eq", "needs investigation"
exclude :"test_s_base64digest", "needs investigation"
exclude :"test_s_file", "needs investigation"
exclude :"test_s_hexdigest", "needs investigation"
exclude :"test_bubblebabble_class", "needs investigation"
exclude :"test_bubblebabble_instance", "needs investigation"
exclude :"test_eq", "needs investigation"
exclude :"test_s_base64digest", "needs investigation"
exclude :"test_s_file", "needs investigation"
exclude :"test_s_hexdigest", "needs investigation"
12 changes: 12 additions & 0 deletions test/mri/excludes_truffle/TestDigest/TestSHA512.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
exclude :"test_bubblebabble_class", "needs investigation"
exclude :"test_bubblebabble_instance", "needs investigation"
exclude :"test_eq", "needs investigation"
exclude :"test_s_base64digest", "needs investigation"
exclude :"test_s_file", "needs investigation"
exclude :"test_s_hexdigest", "needs investigation"
exclude :"test_bubblebabble_class", "needs investigation"
exclude :"test_bubblebabble_instance", "needs investigation"
exclude :"test_eq", "needs investigation"
exclude :"test_s_base64digest", "needs investigation"
exclude :"test_s_file", "needs investigation"
exclude :"test_s_hexdigest", "needs investigation"
3 changes: 3 additions & 0 deletions test/mri/excludes_truffle/TestSecureRandom.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
exclude :test_hex_encoding, "needs investigation"
exclude :test_s_random_number_float, "right shift bignum long"
exclude :test_s_random_number_float_by_zero, "right shift bignum long"
10 changes: 5 additions & 5 deletions test/mri_truffle.index
Original file line number Diff line number Diff line change
@@ -111,14 +111,14 @@ test_ipaddr.rb

test_mathn.rb

# test_mutex_m.rb # cannot load such file -- mutex_m
test_mutex_m.rb
test_open3.rb
test_pp.rb
test_prettyprint.rb
test_prime.rb
# test_pstore.rb
test_pstore.rb
# test_pty.rb
# test_securerandom.rb
test_securerandom.rb
test_set.rb
test_shellwords.rb
test_singleton.rb
@@ -173,8 +173,8 @@ date/test_date_strptime.rb
date/test_date_parse.rb
date/test_switch_hitter.rb

# digest/test_digest.rb
# digest/test_digest_extend.rb
digest/test_digest.rb
# digest/test_digest_extend.rb # all failed

erb/test_erb.rb
erb/test_erb_m17n.rb
21 changes: 21 additions & 0 deletions test/truffle/coverage/subject.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
a = 1
b = 2

10.times do
c = 3
end

d = 4

if d == 4
e = 5
end

def foo
f = 6
end

foo
foo

g = 7
12 changes: 12 additions & 0 deletions test/truffle/coverage/test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
require 'coverage'

Coverage.start

require_relative 'subject.rb'

data = Coverage.result.values.first
expected = [1, 1, nil, 1, 10, nil, nil, 1, nil, 1, 1, nil, nil, 1, 2, nil, nil, 1, 1, nil, 1]

p data

raise 'failed' unless data == expected
43 changes: 43 additions & 0 deletions test/truffle/run.sh
Original file line number Diff line number Diff line change
@@ -1,7 +1,50 @@
#!/bin/bash

PORT=8080

function wait_until_port_open {
until lsof -i :$PORT
do
sleep 1
done
sleep 1
}

echo "Building..."
mvn install -Pbootstrap

echo "Array#pack with real usage..."
bin/jruby -X+T test/truffle/pack-real-usage.rb

echo "Simple web server..."
bin/jruby -X+T test/truffle/simple-server.rb &
wait_until_port_open
curl http://localhost:$PORT/
kill -9 $!
wait

echo "Simple Webrick web server..."
bin/jruby -X+T test/truffle/simple-webrick-server.rb &
wait_until_port_open
curl http://localhost:$PORT/
kill -9 $!
wait

echo "Simple Rack web server..."
bin/jruby bin/gem install rack
bin/jruby -X+T -Ilib/ruby/gems/shared/gems/rack-1.6.1/lib test/truffle/simple-webrick-server.rb &
wait_until_port_open
curl http://localhost:$PORT/
kill -9 $!
wait

echo "Simple Sinatra web server..."
bin/jruby bin/gem install sinatra
bin/jruby -X+T -Ilib/ruby/gems/shared/gems/rack-1.6.1/lib -Ilib/ruby/gems/shared/gems/tilt-2.0.1/lib -Ilib/ruby/gems/shared/gems/rack-protection-1.5.3/lib -Ilib/ruby/gems/shared/gems/sinatra-1.4.6/lib test/truffle/simple-sinatra-server.rb &
wait_until_port_open
curl http://localhost:$PORT/
kill -9 $!
wait

echo "Coverage..."
bin/jruby -X+T -Xtruffle.coverage=true test/truffle/coverage/test.rb
10 changes: 10 additions & 0 deletions test/truffle/simple-sinatra-server.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
require 'sinatra'

# bin/jruby bin/gem install sinatra
# jt run -Ilib/ruby/gems/shared/gems/rack-1.6.1/lib -Ilib/ruby/gems/shared/gems/tilt-2.0.1/lib -Ilib/ruby/gems/shared/gems/rack-protection-1.5.3/lib -Ilib/ruby/gems/shared/gems/sinatra-1.4.6/lib test/truffle/simple-sinatra-server.rb

set :port, 8080

get '/' do
"Hello Sinatra!"
end
27 changes: 17 additions & 10 deletions tool/jt.rb
Original file line number Diff line number Diff line change
@@ -16,6 +16,9 @@

JRUBY_DIR = File.expand_path('../..', __FILE__)

JDEBUG_PORT = 51819
JDEBUG = "-J-agentlib:jdwp=transport=dt_socket,server=y,address=#{JDEBUG_PORT},suspend=y"

# wait for sub-processes to handle the interrupt
trap(:INT) {}

@@ -226,7 +229,7 @@ def run(*args)
end

if args.delete('--jdebug')
jruby_args += %w[-J-agentlib:jdwp=transport=dt_socket,server=y,address=51819,suspend=y]
jruby_args << JDEBUG
end

if args.delete('--server')
@@ -268,15 +271,16 @@ def test_mri(*args)
private :test_mri

def test(*args)
return test_pe(*args.drop(1)) if args.first == 'pe'
return test_mri(*args.drop(1)) if args.first == 'mri'
return test_specs(*args.drop(1)) if args.first == 'specs'
path, *rest = args

if args.empty?
test_specs(*args)
test_mri(*args)
case path
when nil
test_specs
test_mri
when 'pe' then test_pe(*rest)
when 'specs' then test_specs(*rest)
when 'mri' then test_mri(*rest)
else
path = args.first
if File.expand_path(path).start_with?("#{JRUBY_DIR}/test")
test_mri(*args)
else
@@ -300,12 +304,15 @@ def test_specs(*args)
options += %w[--excl-tag slow]
end

if args.first == '--graal'
args.shift
if args.delete('--graal')
env_vars["JAVACMD"] = Utilities.find_graal
options << '-T-J-server'
end

if args.delete('--jdebug')
options << "-T#{JDEBUG}"
end

mspec_env env_vars, 'run', *options, *args
end
private :test_specs
Original file line number Diff line number Diff line change
@@ -10,6 +10,8 @@
package org.jruby.truffle.nodes;

import com.oracle.truffle.api.interop.TruffleObject;
import org.jruby.truffle.nodes.core.BigDecimalNodes;
import org.jruby.truffle.runtime.ThreadLocalObject;
import org.jruby.truffle.runtime.UndefinedPlaceholder;
import org.jruby.truffle.runtime.core.*;

@@ -96,7 +98,7 @@ public static boolean isRubyBasicObject(Object value) {
}

public static boolean isThreadLocal(Object value) {
return value instanceof ThreadLocal;
return value instanceof ThreadLocalObject;
}

public static boolean isForeignObject(Object object) {
@@ -115,4 +117,8 @@ public static boolean isInfinity(double value) {
return Double.isInfinite(value);
}

public static boolean isRubyBigDecimal(RubyBasicObject value) {
return value.getDynamicObject().getShape().getObjectType() == BigDecimalNodes.BIG_DECIMAL_TYPE;
}

}
Original file line number Diff line number Diff line change
@@ -29,7 +29,7 @@ public WhenSplatNode(RubyContext context, SourceSection sourceSection, RubyNode
super(context, sourceSection);
this.readCaseExpression = readCaseExpression;
this.splat = splat;
dispatchCaseEqual = DispatchHeadNodeFactory.createMethodCall(context, false, false, null);
dispatchCaseEqual = DispatchHeadNodeFactory.createMethodCall(context);
}

@Override
Original file line number Diff line number Diff line change
@@ -16,6 +16,7 @@
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.NodeUtil;
import com.oracle.truffle.api.source.SourceSection;

import org.jruby.runtime.Visibility;
import org.jruby.truffle.nodes.RubyNode;
import org.jruby.truffle.nodes.cast.BooleanCastNodeGen;
@@ -98,7 +99,7 @@ public abstract static class NotEqualNode extends CoreMethodArrayArgumentsNode {

public NotEqualNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
equalNode = DispatchHeadNodeFactory.createMethodCall(context, false, false, null);
equalNode = DispatchHeadNodeFactory.createMethodCall(context);
}

@Specialization
@@ -270,7 +271,9 @@ public abstract static class SendNode extends CoreMethodArrayArgumentsNode {
public SendNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);

dispatchNode = DispatchHeadNodeFactory.createMethodCall(context, true, DispatchNode.DISPATCH_METAPROGRAMMING_ALWAYS_INDIRECT, MissingBehavior.CALL_METHOD_MISSING);
dispatchNode = new CallDispatchHeadNode(context, true,
DispatchNode.DISPATCH_METAPROGRAMMING_ALWAYS_INDIRECT,
MissingBehavior.CALL_METHOD_MISSING);

if (DispatchNode.DISPATCH_METAPROGRAMMING_ALWAYS_UNCACHED) {
dispatchNode.forceUncached();
Original file line number Diff line number Diff line change
@@ -19,6 +19,7 @@
import org.jruby.truffle.nodes.objects.Allocator;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.core.*;
import org.jruby.truffle.runtime.object.BasicObjectType;

import java.math.BigDecimal;
import java.math.MathContext;
@@ -27,7 +28,14 @@
@CoreClass(name = "Truffle::BigDecimal")
public abstract class BigDecimalNodes {

public static class BigDecimalType extends BasicObjectType {

}

public static final BigDecimalType BIG_DECIMAL_TYPE = new BigDecimalType();

private static final HiddenKey VALUE_IDENTIFIER = new HiddenKey("value");
public static final Shape BIG_DECIMAL_SHAPE;
private static final DynamicObjectFactory BIG_DECIMAL_FACTORY;
public static final Property VALUE_PROPERTY;

@@ -37,15 +45,8 @@ public abstract class BigDecimalNodes {
VALUE_IDENTIFIER,
allocator.locationForType(BigDecimal.class, EnumSet.of(LocationModifier.NonNull)),
0);
final Shape shape = RubyBasicObject.EMPTY_SHAPE.addProperty(VALUE_PROPERTY);
BIG_DECIMAL_FACTORY = shape.createFactory();
}

// TODO (pitr 15-May-2015) figure out where to put RubyBigDecimal, or remove completely
public static class RubyBigDecimal extends RubyBasicObject {
public RubyBigDecimal(RubyClass rubyClass, DynamicObject dynamicObject) {
super(rubyClass, dynamicObject);
}
BIG_DECIMAL_SHAPE = RubyBasicObject.LAYOUT.createShape(BIG_DECIMAL_TYPE).addProperty(VALUE_PROPERTY);
BIG_DECIMAL_FACTORY = BIG_DECIMAL_SHAPE.createFactory();
}

public static class RubyBigDecimalAllocator implements Allocator {
@@ -84,19 +85,15 @@ public static void setBigDecimalValue(RubyBasicObject bignum, BigDecimal value)
VALUE_PROPERTY.setSafe(bignum.getDynamicObject(), value, null);
}

public static RubyBigDecimal createRubyBigDecimal(RubyClass rubyClass, BigDecimal value) {
return new RubyBigDecimal(rubyClass, BIG_DECIMAL_FACTORY.newInstance(value));
public static RubyBasicObject createRubyBigDecimal(RubyClass rubyClass, BigDecimal value) {
return new RubyBasicObject(rubyClass, BIG_DECIMAL_FACTORY.newInstance(value));
}

public abstract static class BigDecimalCoreMethodNode extends CoreMethodArrayArgumentsNode {

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

public static boolean isRubyBigDecimal(Object value) {
return value instanceof RubyBigDecimal;
}
}

@CoreMethod(names = "initialize", required = 1)
@@ -107,12 +104,12 @@ public InitializeNode(RubyContext context, SourceSection sourceSection) {
}

@Specialization(guards = "isRubyBigDecimal(v)")
public RubyBasicObject initialize(RubyBigDecimal self, RubyBasicObject v) {
public RubyBasicObject initialize(RubyBasicObject self, RubyBasicObject v) {
return v;
}

@Specialization(guards = "isRubyString(v)")
public RubyBasicObject initializeFromString(RubyBigDecimal self, RubyBasicObject v) {
public RubyBasicObject initializeFromString(RubyBasicObject self, RubyBasicObject v) {
// TODO (pitr 20-May-2015): add NaN, Infinity handling
switch (v.toString()) {
case "NaN":
Original file line number Diff line number Diff line change
@@ -17,14 +17,13 @@
import com.oracle.truffle.api.frame.FrameSlot;
import com.oracle.truffle.api.frame.MaterializedFrame;
import com.oracle.truffle.api.source.SourceSection;
import org.jruby.truffle.nodes.globals.GetFromThreadLocalNode;
import org.jruby.truffle.nodes.globals.WrapInThreadLocalNode;
import org.jruby.truffle.nodes.locals.ReadFrameSlotNode;
import org.jruby.truffle.nodes.locals.ReadFrameSlotNodeGen;
import org.jruby.truffle.nodes.locals.WriteFrameSlotNode;
import org.jruby.truffle.nodes.locals.WriteFrameSlotNodeGen;
import org.jruby.truffle.runtime.RubyArguments;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.ThreadLocalObject;
import org.jruby.truffle.runtime.control.RaiseException;
import org.jruby.truffle.runtime.core.RubyArray;
import org.jruby.truffle.runtime.core.RubyBinding;
@@ -117,7 +116,11 @@ public Object localVariableGetLastLine(RubyBinding binding, RubySymbol symbol) {
}

final Object value = frame.getValue(frameSlot);
return GetFromThreadLocalNode.get(getContext(), value);
if (value instanceof ThreadLocalObject) {
return ((ThreadLocalObject) value).get();
} else {
return value;
}
}

protected FrameDescriptor getFrameDescriptor(RubyBinding binding) {
@@ -192,7 +195,7 @@ public Object localVariableSetUncached(RubyBinding binding, RubySymbol symbol, O
public Object localVariableSetLastLine(RubyBinding binding, RubySymbol symbol, Object value) {
final MaterializedFrame frame = binding.getFrame();
final FrameSlot frameSlot = frame.getFrameDescriptor().findFrameSlot(symbol.toString());
frame.setObject(frameSlot, WrapInThreadLocalNode.wrap(getContext(), value));
frame.setObject(frameSlot, ThreadLocalObject.wrap(getContext(), value));
return value;
}

Original file line number Diff line number Diff line change
@@ -40,6 +40,14 @@ public RubyBasicObject initialize(RubyException exception, UndefinedPlaceholder
return nil();
}

@Specialization(guards = {"isNil(object)"})
public RubyBasicObject initialize(RubyException exception, RubyBasicObject object) {
CompilerDirectives.transferToInterpreter();

exception.initialize(getContext().makeString(""));
return nil();
}

@Specialization
public RubyBasicObject initialize(RubyException exception, RubyString message) {
CompilerDirectives.transferToInterpreter();
Original file line number Diff line number Diff line change
@@ -224,7 +224,7 @@ public double div(double a, RubyBasicObject b) {
public Object div(VirtualFrame frame, double a, Object b) {
if (redoCoercedNode == null) {
CompilerDirectives.transferToInterpreter();
redoCoercedNode = insert(DispatchHeadNodeFactory.createMethodCall(getContext(), true));
redoCoercedNode = insert(DispatchHeadNodeFactory.createMethodCallOnSelf(getContext()));
}

return redoCoercedNode.call(frame, a, "redo_coerced", null, getContext().getSymbolTable().getSymbol("/"), b);
@@ -407,7 +407,7 @@ public boolean equal(double a, RubyBasicObject b) {
public Object equal(VirtualFrame frame, double a, RubyBasicObject b) {
if (fallbackCallNode == null) {
CompilerDirectives.transferToInterpreter();
fallbackCallNode = insert(DispatchHeadNodeFactory.createMethodCall(getContext(), true));
fallbackCallNode = insert(DispatchHeadNodeFactory.createMethodCallOnSelf(getContext()));
}

return fallbackCallNode.call(frame, a, "equal_fallback", null, b);
Original file line number Diff line number Diff line change
@@ -36,7 +36,6 @@
import org.jruby.truffle.nodes.core.KernelNodesFactory.SameOrEqualNodeFactory;
import org.jruby.truffle.nodes.core.KernelNodesFactory.SingletonMethodsNodeFactory;
import org.jruby.truffle.nodes.dispatch.*;
import org.jruby.truffle.nodes.globals.WrapInThreadLocalNode;
import org.jruby.truffle.nodes.objects.*;
import org.jruby.truffle.nodes.objectstorage.WriteHeadObjectFieldNode;
import org.jruby.truffle.nodes.rubinius.ObjectPrimitiveNodes;
@@ -163,7 +162,7 @@ private boolean areSame(VirtualFrame frame, Object left, Object right) {
private boolean areEqual(VirtualFrame frame, Object left, Object right) {
if (equalNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
equalNode = insert(DispatchHeadNodeFactory.createMethodCall(getContext(), false, false, null));
equalNode = insert(DispatchHeadNodeFactory.createMethodCall(getContext()));
}

return equalNode.callBoolean(frame, left, "==", null, right);
@@ -192,7 +191,7 @@ public abstract static class NotMatchNode extends CoreMethodArrayArgumentsNode {

public NotMatchNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
matchNode = DispatchHeadNodeFactory.createMethodCall(context, false, false, null);
matchNode = DispatchHeadNodeFactory.createMethodCall(context);
}

@Specialization
@@ -365,7 +364,7 @@ public RubyBasicObject copy(VirtualFrame frame, RubyBasicObject self) {

final RubyBasicObject newObject = self.getLogicalClass().allocate(this);

newObject.getObjectType().setInstanceVariables(newObject, self.getObjectType().getInstanceVariables(self));
RubyBasicObject.setInstanceVariables(newObject, RubyBasicObject.getInstanceVariables(self));

return newObject;
}
@@ -387,7 +386,7 @@ public CloneNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
copyNode = CopyNodeFactory.create(context, sourceSection, null);
// Calls private initialize_clone on the new copy.
initializeCloneNode = DispatchHeadNodeFactory.createMethodCall(context, true, MissingBehavior.CALL_METHOD_MISSING);
initializeCloneNode = DispatchHeadNodeFactory.createMethodCallOnSelf(context);
isFrozenNode = IsFrozenNodeGen.create(context, sourceSection, null);
freezeNode = FreezeNodeGen.create(context, sourceSection, null);
singletonClassNode = SingletonClassNodeGen.create(context, sourceSection, null);
@@ -425,7 +424,7 @@ public DupNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
copyNode = CopyNodeFactory.create(context, sourceSection, null);
// Calls private initialize_dup on the new copy.
initializeDupNode = DispatchHeadNodeFactory.createMethodCall(context, true, MissingBehavior.CALL_METHOD_MISSING);
initializeDupNode = DispatchHeadNodeFactory.createMethodCallOnSelf(context);
}

@Specialization
@@ -708,7 +707,7 @@ public String block() throws InterruptedException {
final FrameSlot slot = caller.getFrameDescriptor().findFrameSlot("$_");

if (slot != null) {
caller.setObject(slot, WrapInThreadLocalNode.wrap(getContext(), rubyLine));
caller.setObject(slot, ThreadLocalObject.wrap(getContext(), rubyLine));
}

return rubyLine;
@@ -879,14 +878,14 @@ public InstanceVariableSetNode(RubyContext context, SourceSection sourceSection)
@TruffleBoundary
@Specialization
public Object instanceVariableSet(RubyBasicObject object, RubyString name, Object value) {
object.getObjectType().setInstanceVariable(object, RubyContext.checkInstanceVariableName(getContext(), name.toString(), this), value);
RubyBasicObject.setInstanceVariable(object, RubyContext.checkInstanceVariableName(getContext(), name.toString(), this), value);
return value;
}

@TruffleBoundary
@Specialization
public Object instanceVariableSet(RubyBasicObject object, RubySymbol name, Object value) {
object.getObjectType().setInstanceVariable(object, RubyContext.checkInstanceVariableName(getContext(), name.toString(), this), value);
RubyBasicObject.setInstanceVariable(object, RubyContext.checkInstanceVariableName(getContext(), name.toString(), this), value);
return value;
}

@@ -903,7 +902,7 @@ public InstanceVariablesNode(RubyContext context, SourceSection sourceSection) {
public RubyArray instanceVariables(RubyBasicObject self) {
CompilerDirectives.transferToInterpreter();

final Object[] instanceVariableNames = self.getObjectType().getFieldNames(self);
final Object[] instanceVariableNames = RubyBasicObject.getFieldNames(self);

Arrays.sort(instanceVariableNames);

Loading

0 comments on commit bffa746

Please sign in to comment.