Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge JRuby 9.1.7.0 into truffle-head
Browse files Browse the repository at this point in the history
chrisseaton committed Jan 11, 2017
2 parents e22ed14 + 68056ae commit 87f774a
Showing 39 changed files with 738 additions and 484 deletions.
2 changes: 1 addition & 1 deletion COPYING
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
JRuby is Copyright (c) 2007-2016 The JRuby project, and is released
JRuby is Copyright (c) 2007-2017 The JRuby project, and is released
under a tri EPL/GPL/LGPL license. You can use it, redistribute it
and/or modify it under the terms of the:

2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
9.1.7.0-SNAPSHOT
9.1.7.0
2 changes: 1 addition & 1 deletion core/pom.rb
Original file line number Diff line number Diff line change
@@ -59,7 +59,7 @@

jar 'com.headius:invokebinder:1.7'
jar 'com.headius:options:1.4'
jar 'com.headius:unsafe-fences:1.0-SNAPSHOT'
jar 'com.headius:unsafe-fences:1.0'

jar 'bsf:bsf:2.4.0', :scope => 'provided'
jar 'com.jcraft:jzlib:1.1.3'
4 changes: 2 additions & 2 deletions core/pom.xml
Original file line number Diff line number Diff line change
@@ -12,7 +12,7 @@ DO NOT MODIFIY - GENERATED CODE
<parent>
<groupId>org.jruby</groupId>
<artifactId>jruby-parent</artifactId>
<version>9.1.7.0-SNAPSHOT</version>
<version>9.1.7.0</version>
</parent>
<artifactId>jruby-core</artifactId>
<name>JRuby Core</name>
@@ -203,7 +203,7 @@ DO NOT MODIFIY - GENERATED CODE
<dependency>
<groupId>com.headius</groupId>
<artifactId>unsafe-fences</artifactId>
<version>1.0-SNAPSHOT</version>
<version>1.0</version>
</dependency>
<dependency>
<groupId>bsf</groupId>
44 changes: 25 additions & 19 deletions core/src/main/java/org/jruby/Ruby.java
Original file line number Diff line number Diff line change
@@ -2862,16 +2862,11 @@ public void loadFile(String scriptName, InputStream in, boolean wrap) {
ThreadContext.pushBacktrace(context, "(root)", file, 0);
context.preNodeEval(self);
ParseResult parseResult = parseFile(scriptName, in, null);
RootNode root = (RootNode) parseResult;

if (wrap) {
// toss an anonymous module into the search path
RubyModule wrapper = RubyModule.newModule(this);
((RubyBasicObject)self).extend(new IRubyObject[] {wrapper});
RootNode root = (RootNode) parseResult;
StaticScope top = root.getStaticScope();
StaticScope newTop = staticScopeFactory.newLocalScope(null);
top.setPreviousCRefScope(newTop);
top.setModule(wrapper);
wrapRootForLoad((RubyBasicObject) self, root);
}

runInterpreter(context, parseResult, self);
@@ -2903,24 +2898,39 @@ public void loadScope(IRScope scope, boolean wrap) {
}

public void compileAndLoadFile(String filename, InputStream in, boolean wrap) {
InputStream readStream = in;

// script was not found in cache above, so proceed to compile
RootNode scriptNode = (RootNode) parseFile(readStream, filename, null);

IRubyObject self = wrap ? getTopSelf().rbClone() : getTopSelf();
ThreadContext context = getCurrentContext();
InputStream readStream = in;

String oldFile = context.getFile();
int oldLine = context.getLine();
try {
context.setFileAndLine(scriptNode.getFile(), scriptNode.getLine());
context.preNodeEval(self);
ParseResult parseResult = parseFile(filename, in, null);
RootNode root = (RootNode) parseResult;

runNormally(scriptNode, wrap);
if (wrap) {
wrapRootForLoad((RubyBasicObject) self, root);
} else {
root.getStaticScope().setModule(getObject());
}

runNormally(root, wrap);
} finally {
context.setFileAndLine(oldFile, oldLine);
context.postNodeEval();
}
}

private void wrapRootForLoad(RubyBasicObject self, RootNode root) {
// toss an anonymous module into the search path
RubyModule wrapper = RubyModule.newModule(this);
self.extend(new IRubyObject[] {wrapper});
StaticScope top = root.getStaticScope();
StaticScope newTop = staticScopeFactory.newLocalScope(null);
top.setPreviousCRefScope(newTop);
top.setModule(wrapper);
}

public void loadScript(Script script) {
loadScript(script, false);
}
@@ -3167,10 +3177,6 @@ public void tearDown(boolean systemExit) {

final ThreadContext context = getCurrentContext();

// Disable event hooks during runtime teardown
// This avoids deadlocks if some other thread is holding a debugger lock while we're trying to exit
context.setEventHooksEnabled(false);

// FIXME: 73df3d230b9d92c7237d581c6366df1b92ad9b2b exposed no toplevel scope existing anymore (I think the
// bogus scope I removed was playing surrogate toplevel scope and wallpapering this bug). For now, add a
// bogus scope back for at_exit block run. This is buggy if at_exit is capturing vars.
45 changes: 23 additions & 22 deletions core/src/main/java/org/jruby/RubyArray.java
Original file line number Diff line number Diff line change
@@ -71,6 +71,7 @@

import java.io.IOException;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
@@ -3021,46 +3022,46 @@ protected boolean flatten(ThreadContext context, final int level, final RubyArra
// TODO: (CON) We can flatten packed versions efficiently if length does not change (e.g. [[1,2],[]])
unpack();
final Ruby runtime = context.runtime;
RubyArray stack = newArrayLight(runtime, ARRAY_DEFAULT_SIZE);
IdentityHashMap<Object, Object> memo = new IdentityHashMap<Object, Object>();
RubyArray ary = this;
memo.put(ary, NEVER);
boolean modified = false;

ArrayList<Object> stack = null;
IdentityHashMap<RubyArray, IRubyObject> memo = null; // used as an IdentityHashSet

RubyArray ary = this;
int i = 0;

try {
while (true) {
IRubyObject tmp;
while (i < ary.realLength) {
IRubyObject elt = ary.eltOk(i++);
if (level >= 0 && stack.size() / 2 >= level) {
if (level >= 0 && (stack == null ? 0 : stack.size()) / 2 >= level) {
result.append(elt);
continue;
}
tmp = TypeConverter.checkArrayType(elt);
if (tmp.isNil()) {
result.append(elt);
} else {
modified = true;
IRubyObject tmp = TypeConverter.checkArrayType(elt);
if (tmp.isNil()) result.append(elt);
else { // nested array element
if (memo == null) {
memo = new IdentityHashMap<>(4 + 1);
memo.put(this, NEVER);
}
if (memo.get(tmp) != null) throw runtime.newArgumentError("tried to flatten recursive array");
memo.put(tmp, NEVER);
stack.append(ary);
stack.append(RubyFixnum.newFixnum(runtime, i));
ary = (RubyArray)tmp;
if (stack == null) stack = new ArrayList<>(8); // fine hold 4-level deep nesting
stack.add(ary); stack.add(i); // add (ary, i) pair
ary = (RubyArray) tmp;
memo.put(ary, NEVER);
i = 0;
}
}
if (stack.realLength == 0) break;
memo.remove(ary);
tmp = stack.pop(context);
i = (int) ((RubyFixnum) tmp).getLongValue();
ary = (RubyArray) stack.pop(context);
if (stack == null || stack.size() == 0) break;
memo.remove(ary); // memo != null since stack != null
final int s = stack.size(); // pop (ary, i)
i = (Integer) stack.remove(s - 1);
ary = (RubyArray) stack.remove(s - 2);
}
} catch (ArrayIndexOutOfBoundsException ex) {
throw concurrentModification(context.runtime, ex);
}
return modified;
return stack != null;
}

@JRubyMethod(name = "flatten!")
46 changes: 27 additions & 19 deletions core/src/main/java/org/jruby/compiler/impl/SkinnyMethodAdapter.java
Original file line number Diff line number Diff line change
@@ -563,35 +563,43 @@ public void start() {
getMethodVisitor().visitCode();
getMethodVisitor().visitLabel(start);
}


static final Runnable NO_LOCALS = new Runnable() { public void run() { /* no-op */ } };

public void end() {
end(new Runnable() {
public void run() {
}
});
end(NO_LOCALS);
}

public void end(Runnable locals) {
if (DEBUG) {
PrintWriter pw = new PrintWriter(System.out);
String className = "(unknown class)";
if (cv instanceof ClassWriter) {
className = new ClassReader(((ClassWriter)cv).toByteArray()).getClassName();
}
if (name != null) {
pw.write("*** Dumping " + className + "." + name + " ***\n");
} else {
pw.write("*** Dumping ***\n");
}
printer.print(pw);
pw.flush();
}
if (DEBUG) printByteCode(getClassName());
getMethodVisitor().visitLabel(end);
locals.run();
getMethodVisitor().visitMaxs(1, 1);
getMethodVisitor().visitEnd();
}

private String getClassName() {
if (cv instanceof ClassWriter) {
return new ClassReader(((ClassWriter) cv).toByteArray()).getClassName();
}
return "(unknown class)";
}

private PrintWriter outDebugWriter() {
return new PrintWriter(System.out);
}

private void printByteCode(final String className) {
PrintWriter pw = outDebugWriter();
if (name != null) {
pw.write("*** Dumping " + className + '.' + name + " ***\n");
} else {
pw.write("*** Dumping ***\n");
}
if (printer != null) printer.print(pw);
pw.flush();
}

public void local(int index, String name, Class type) {
getMethodVisitor().visitLocalVariable(name, ci(type), null, start, end, index);
}
Original file line number Diff line number Diff line change
@@ -212,7 +212,7 @@ public static byte[] createHandleBytes(Method method, String name) {
private static String createHandleName(Method method) {
Class returnType = method.getReturnType();
Class[] paramTypes = method.getParameterTypes();
return method.getDeclaringClass().getCanonicalName().replaceAll("\\.", "__") + "#" + method.getName() + "#" + JITCompiler.getHashForString(pretty(returnType, paramTypes));
return method.getDeclaringClass().getCanonicalName().replaceAll("\\.", "__") + '#' + method.getName() + '#' + JITCompiler.getHashForString(pretty(returnType, paramTypes));
}

public static void loadUnboxedArgument(SkinnyMethodAdapter m, int index, Class type) {
9 changes: 7 additions & 2 deletions core/src/main/java/org/jruby/ext/JRubyPOSIXHandler.java
Original file line number Diff line number Diff line change
@@ -72,12 +72,17 @@ public File getCurrentWorkingDirectory() {
@SuppressWarnings("unchecked")
public String[] getEnv() {
RubyHash hash = (RubyHash) runtime.getObject().getConstant("ENV");
int i=0;

String[] env = new String[hash.size()];
if (env.length == 0) return env;

StringBuilder str = new StringBuilder(); int i=0;

for (Iterator<Entry<Object, Object>> iter = hash.directEntrySet().iterator(); iter.hasNext(); i++) {
Map.Entry<Object, Object> entry = iter.next();
env[i] = entry.getKey().toString() + "=" + entry.getValue().toString();
str.setLength(0);
str.append(entry.getKey()).append('=').append(entry.getValue());
env[i] = str.toString();
}

return env;
10 changes: 9 additions & 1 deletion core/src/main/java/org/jruby/ext/bigdecimal/RubyBigDecimal.java
Original file line number Diff line number Diff line change
@@ -442,8 +442,16 @@ public static RubyBigDecimal getVpRubyObjectWithPrec19Inner(ThreadContext contex
private static RubyBigDecimal getVpValueWithPrec19(ThreadContext context, IRubyObject value, long precision, boolean must) {
if (value instanceof RubyFloat) {
if (precision > Long.MAX_VALUE) return cannotBeCoerced(context, value, must);
double doubleValue = ((RubyFloat) value).getDoubleValue();

return new RubyBigDecimal(context.runtime, BigDecimal.valueOf(((RubyFloat) value).getDoubleValue()));
if (Double.isInfinite(doubleValue)) {
throw context.runtime.newFloatDomainError(doubleValue < 0 ? "-Infinity" : "Infinity");
}
if (Double.isNaN(doubleValue)) {
throw context.runtime.newFloatDomainError("NaN");
}

return new RubyBigDecimal(context.runtime, BigDecimal.valueOf(doubleValue));
}
else if (value instanceof RubyRational) {
if (precision < 0) {
611 changes: 343 additions & 268 deletions core/src/main/java/org/jruby/ext/stringio/StringIO.java

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions core/src/main/java/org/jruby/ext/tempfile/Tempfile.java
Original file line number Diff line number Diff line change
@@ -110,9 +110,9 @@ private IRubyObject initializeCommon(ThreadContext context, IRubyObject[] args)
BlockCallback body = new TempfileCallback();

// #create and #make_tmpname come from Dir::Tmpname, included into
// tempfile in lib/ruby/shared/tempfile.rb. We use create here to
// tempfile in lib/ruby/stdlib/tempfile.rb. We use create here to
// match filename algorithm and allow them to be overridden.
callMethod(context, "create", args, CallBlock19.newCallClosure(this, this.getMetaClass(), Signature.OPTIONAL, body, context));
callMethod(context, "create", args, CallBlock19.newCallClosure(this, getMetaClass(), Signature.OPTIONAL, body, context));

// GH#1905: don't use JDK's deleteOnExit because it grows a set without bounds
context.runtime.addInternalFinalizer(Tempfile.this);
@@ -150,7 +150,7 @@ public IRubyObject call(ThreadContext context, IRubyObject[] args, Block block)
runtime.getPosix().chmod(tmp.getAbsolutePath(), 0600);
tmpFile = tmp;
} else {
throw context.runtime.newErrnoEEXISTError(openFile.getPath());
throw context.runtime.newErrnoEEXISTError(getPath());
}
} catch (IOException e) {
throw context.runtime.newIOErrorFromException(e);
13 changes: 9 additions & 4 deletions core/src/main/java/org/jruby/ir/instructions/BSwitchInstr.java
Original file line number Diff line number Diff line change
@@ -127,9 +127,14 @@ public Label[] getJumpTargets() {
return jumpTargets;
}

private static boolean jumpsAreSorted(int[] jumps) {
int[] jumps2 = jumps.clone();
Arrays.sort(jumps2);
return Arrays.equals(jumps, jumps2);
private static boolean jumpsAreSorted(final int[] jumps) {
if ( jumps.length == 0 ) return true;
int prev = jumps[0];
for ( int i = 1; i < jumps.length; i++ ) {
int curr = jumps[i];
if ( prev > curr ) return false; // not sorted
prev = curr;
}
return true;
}
}
4 changes: 2 additions & 2 deletions core/src/main/java/org/jruby/ir/operands/Label.java
Original file line number Diff line number Diff line change
@@ -38,7 +38,7 @@ public OperandType getOperandType() {

@Override
public String toString() {
return prefix + "_" + id + ":" + targetPC;
return prefix + '_' + id + ':' + targetPC;
}

@Override
@@ -101,7 +101,7 @@ public static Label decode(IRReaderDecoder d) {
// Check if this label was already created
// Important! Program would not be interpreted correctly
// if new name will be created every time
String fullLabel = prefix + "_" + id;
String fullLabel = prefix + '_' + id;
if (d.getVars().containsKey(fullLabel)) {
return (Label) d.getVars().get(fullLabel);
}
46 changes: 29 additions & 17 deletions core/src/main/java/org/jruby/ir/passes/LocalOptimizationPass.java
Original file line number Diff line number Diff line change
@@ -8,8 +8,6 @@
import org.jruby.ir.operands.Operand;
import org.jruby.ir.operands.Variable;
import org.jruby.ir.representations.BasicBlock;
import org.jruby.ir.instructions.ClosureAcceptingInstr;
import org.jruby.ir.operands.WrappedIRClosure;

import java.util.*;

@@ -107,16 +105,6 @@ public static Instr optInstr(IRScope s, Instr instr, Map<Operand,Operand> valueM
return newInstr;
}

private static boolean isDataflowBarrier(Instr i, IRScope s) {
boolean reset = false;
if (!i.isDead() && i instanceof ClosureAcceptingInstr) {
Operand o = ((ClosureAcceptingInstr)i).getClosureArg();
reset = s.bindingHasEscaped() || (o != null && o instanceof WrappedIRClosure);
}

return reset;
}

public static void runLocalOptsOnInstrArray(IRScope s, Instr[] instrs) {
// Reset value map if this instruction is the start/end of a basic block
Map<Operand,Operand> valueMap = new HashMap<>();
@@ -128,10 +116,21 @@ public static void runLocalOptsOnInstrArray(IRScope s, Instr[] instrs) {
instrs[i] = newInstr;
}

// Reset simplification info if this starts/ends a basic block
// or if the instr is a dataflow barrier.
// If the call has been optimized away in the previous step, it is no longer a hard boundary for opts!
//
// Right now, calls are considered hard boundaries for optimization and
// information cannot be propagated across them!
//
// SSS FIXME: Rather than treat all calls with a broad brush, what we need
// is to capture different attributes about a call :
// - uses closures
// - known call target
// - can modify scope,
// - etc.
//
// This information is present in instruction flags on CallBase. Use it!
Operation iop = instr.getOperation();
if (iop.startsBasicBlock() || iop.endsBasicBlock() || isDataflowBarrier(instr, s)) {
if (iop.startsBasicBlock() || iop.endsBasicBlock() || (iop.isCall() && !instr.isDead())) {
valueMap = new HashMap<>();
simplificationMap = new HashMap<>();
}
@@ -152,8 +151,21 @@ public static void runLocalOptsOnBasicBlock(IRScope s, BasicBlock b) {
instrs.set(newInstr);
}

// Reset simplification info if this is a dataflow barrier.
if (isDataflowBarrier(instr, s)) {
// If the call has been optimized away in the previous step, it is no longer a hard boundary for opts!
//
// Right now, calls are considered hard boundaries for optimization and
// information cannot be propagated across them!
//
// SSS FIXME: Rather than treat all calls with a broad brush, what we need
// is to capture different attributes about a call :
// - uses closures
// - known call target
// - can modify scope,
// - etc.
//
// This information is present in instruction flags on CallBase. Use it!
Operation iop = instr.getOperation();
if (iop.isCall() && !instr.isDead()) {
valueMap = new HashMap<>();
simplificationMap = new HashMap<>();
}
11 changes: 6 additions & 5 deletions core/src/main/java/org/jruby/ir/targets/JVMVisitor.java
Original file line number Diff line number Diff line change
@@ -862,11 +862,12 @@ public void BSwitchInstr(BSwitchInstr bswitchinstr) {
for (int i = 0; i < targets.length; i++) jvmTargets[i] = getJVMLabel(targets[i]);

// if jump table is all contiguous values, use a tableswitch
int[] jumps = bswitchinstr.getJumps();
int low = jumps[0];
int high = jumps[jumps.length - 1];
int span = high - low;
if (span == jumps.length) {
int[] jumps = bswitchinstr.getJumps(); // always ordered e.g. [2, 3, 4]

int low = jumps[0]; // 2
int high = jumps[jumps.length - 1]; // 4
int span = high - low + 1;
if (span == jumps.length) { // perfectly compact - no "holes"
jvmAdapter().tableswitch(low, high, getJVMLabel(bswitchinstr.getElseTarget()), jvmTargets);
} else {
jvmAdapter().lookupswitch(getJVMLabel(bswitchinstr.getElseTarget()), bswitchinstr.getJumps(), jvmTargets);
Original file line number Diff line number Diff line change
@@ -189,7 +189,7 @@ private static <T extends ParameterTypes> T findMatchingCallableForArgs(final Ru

final boolean lastArgProc = procArity != Integer.MIN_VALUE;
final Boolean moreSpecific = moreSpecificTypes(msTypes, cTypes, lastArgProc);
if ( (Object) moreSpecific == Boolean.TRUE ) {
if ( moreSpecific == Boolean.TRUE ) {
mostSpecific = candidate; msTypes = cTypes;
ambiguous = false; continue /* OUTER */;
}
10 changes: 3 additions & 7 deletions core/src/main/java/org/jruby/javasupport/JavaSupportImpl.java
Original file line number Diff line number Diff line change
@@ -129,19 +129,15 @@ public synchronized RubyModule computeValue(Class<?> klass) {

this.staticAssignedNames = ClassValue.newInstance(new ClassValueCalculator<Map<String, AssignedName>>() {
@Override
public Map<String, AssignedName> computeValue(Class<?> cls) {
return new HashMap<String, AssignedName>();
}
public Map<String, AssignedName> computeValue(Class<?> cls) { return new HashMap<>(); }
});
this.instanceAssignedNames = ClassValue.newInstance(new ClassValueCalculator<Map<String, AssignedName>>() {
@Override
public Map<String, AssignedName> computeValue(Class<?> cls) {
return new HashMap<String, AssignedName>();
}
public Map<String, AssignedName> computeValue(Class<?> cls) { return new HashMap<>(); }
});

// Proxy creation is synchronized (see above) so a HashMap is fine for recursion detection.
this.unfinishedProxies = new ConcurrentHashMap<Class, UnfinishedProxy>(8, 0.75f, 1);
this.unfinishedProxies = new ConcurrentHashMap<>(8, 0.75f, 1);
}

public Class loadJavaClass(String className) throws ClassNotFoundException {
Original file line number Diff line number Diff line change
@@ -3,13 +3,17 @@
/**
* Created by headius on 2/26/15.
*/
public class AssignedName {
String name;
Priority type;
public final class AssignedName {
final String name;
final Priority type;

public AssignedName() {}
public AssignedName(String name, Priority type) {
AssignedName(String name, Priority type) {
this.name = name;
this.type = type;
}

@Override
public String toString() {
return getClass().getName() + '@' + Integer.toHexString(hashCode()) + '(' + name + ' ' + type + ')';
}
}
Original file line number Diff line number Diff line change
@@ -115,7 +115,8 @@ private void setupClassMethods(Class<?> javaClass, State state) {
// TODO: protected methods. this is going to require a rework of some of the mechanism.
final Map<String, List<Method>> nameMethods = getMethods(javaClass);

for (List<Method> methods : nameMethods.values()) {
for (Map.Entry<String, List<Method>> entry : nameMethods.entrySet()) {
final List<Method> methods = entry.getValue();
for (int i = methods.size(); --i >= 0; ) {
// we need to collect all methods, though we'll only
// install the ones that are named in this class
@@ -192,7 +193,7 @@ private static void assignInstanceAliases(State state) {
MethodInstaller methodInstaller = (MethodInstaller)entry.getValue();

// no aliases for __method methods
if (entry.getKey().endsWith("__method")) continue;
if (entry.getKey().endsWith(METHOD_MANGLE)) continue;

if (methodInstaller.hasLocalMethod()) {
assignAliases(methodInstaller, state.instanceNames);
54 changes: 28 additions & 26 deletions core/src/main/java/org/jruby/javasupport/binding/Initializer.java
Original file line number Diff line number Diff line change
@@ -37,15 +37,16 @@
* Created by headius on 2/26/15.
*/
public abstract class Initializer {

static final Logger LOG = LoggerFactory.getLogger(Initializer.class);

public static final boolean DEBUG_SCALA = false;

protected final Ruby runtime;
protected final JavaSupport javaSupport;
protected final Class javaClass;

private static final Logger LOG = LoggerFactory.getLogger(Initializer.class);

private static final int ACC_BRIDGE = 0x00000040;

public static final boolean DEBUG_SCALA = false;
private static final int ACC_BRIDGE = 0x00000040;

public static final String METHOD_MANGLE = "__method";

@@ -176,7 +177,7 @@ private static void setupStaticMethods(Map<String, NamedInstaller> methodCallbac
protected static void assignStaticAliases(final State state) {
for (Map.Entry<String, NamedInstaller> entry : state.staticInstallers.entrySet()) {
// no aliases for __method methods
if (entry.getKey().endsWith("__method")) continue;
if (entry.getKey().endsWith(METHOD_MANGLE)) continue;

if (entry.getValue().type == NamedInstaller.STATIC_METHOD && entry.getValue().hasLocalMethod()) {
assignAliases((MethodInstaller) entry.getValue(), state.staticNames);
@@ -253,7 +254,8 @@ private static void addUnassignedAlias(String name, Map<String, AssignedName> as
if (Priority.ALIAS.moreImportantThan(assignedName)) {
installer.addAlias(name);
assignedNames.put(name, new AssignedName(name, Priority.ALIAS));
} else if (Priority.ALIAS.asImportantAs(assignedName)) {
}
else if (Priority.ALIAS.asImportantAs(assignedName)) {
installer.addAlias(name);
}
}
@@ -267,7 +269,7 @@ protected static String fixScalaNames(final String name) {
}

static {
HashMap<String, String> scalaOperators = new HashMap<String, String>();
HashMap<String, String> scalaOperators = new HashMap<>(24, 1);
scalaOperators.put("\\$plus", "+");
scalaOperators.put("\\$minus", "-");
scalaOperators.put("\\$colon", ":");
@@ -296,20 +298,23 @@ protected static void handleScalaSingletons(final Class<?> javaClass, final Stat
if ( loader == null ) return; //this is a core class, bail

// scan annotations for "scala" packages; if none present, it's not scala
Annotation[] annotations = javaClass.getAnnotations();
boolean foundScala = false;
for (int i = 0; i < annotations.length; i++) {
if (annotations[i].annotationType().getPackage().getName().startsWith("scala.")) foundScala = true;
boolean scalaAnno = false;
for ( Annotation anno : javaClass.getAnnotations() ) {
Package pkg = anno.annotationType().getPackage();
if ( pkg != null && pkg.getName() != null && pkg.getName().startsWith("scala.") ) {
scalaAnno = true; break;
}
}
if (!foundScala) return;
if ( ! scalaAnno ) return;

Class<?> companionClass = loader.loadClass(javaClass.getName() + '$');
final Field field = companionClass.getField("MODULE$");
final Object singleton = field.get(null);
if ( singleton == null ) return;

final Map<String, List<Method>> scalaMethods = getMethods(companionClass);
for (List<Method> methods : scalaMethods.values()) {
for (Map.Entry<String, List<Method>> entry : scalaMethods.entrySet()) {
final List<Method> methods = entry.getValue();
for (int j = 0; j < methods.size(); j++) {
final Method method = methods.get(j);
String name = method.getName();
@@ -426,19 +431,19 @@ public static class State {

final Map<String, AssignedName> staticNames;
final Map<String, AssignedName> instanceNames;
final Map<String, NamedInstaller> staticInstallers = new HashMap<String, NamedInstaller>();
final Map<String, NamedInstaller> instanceInstallers = new HashMap<String, NamedInstaller>();
final List<ConstantField> constantFields = new ArrayList<ConstantField>();
final Map<String, NamedInstaller> staticInstallers = new HashMap<>();
final Map<String, NamedInstaller> instanceInstallers = new HashMap<>();
final List<ConstantField> constantFields = new ArrayList<>();

ConstructorInvokerInstaller constructorInstaller;

State(final Ruby runtime, final Class superClass) {
if (superClass == null) {
staticNames = new HashMap<String, AssignedName>();
instanceNames = new HashMap<String, AssignedName>();
staticNames = new HashMap<>(8);
instanceNames = new HashMap<>(26);
} else {
staticNames = new HashMap<String, AssignedName>(runtime.getJavaSupport().getStaticAssignedNames().get(superClass));
instanceNames = new HashMap<String, AssignedName>(runtime.getJavaSupport().getInstanceAssignedNames().get(superClass));
staticNames = new HashMap<>(runtime.getJavaSupport().getStaticAssignedNames().get(superClass));
instanceNames = new HashMap<>(runtime.getJavaSupport().getInstanceAssignedNames().get(superClass));
}
staticNames.putAll(STATIC_RESERVED_NAMES);
instanceNames.putAll(INSTANCE_RESERVED_NAMES);
@@ -470,9 +475,6 @@ public Class<?>[] computeValue(Class cls) {
static Map<String, List<Method>> getMethods(final Class<?> javaClass) {
HashMap<String, List<Method>> nameMethods = new HashMap<>(32);

// to better size the final ArrayList below
int totalMethods = 0;

// we scan all superclasses, but avoid adding superclass methods with
// same name+signature as subclass methods (see JRUBY-3130)
for ( Class<?> klass = javaClass; klass != null; klass = klass.getSuperclass() ) {
@@ -483,7 +485,7 @@ static Map<String, List<Method>> getMethods(final Class<?> javaClass) {
try {
// add methods, including static if this is the actual class,
// and replacing child methods with equivalent parent methods
totalMethods += addNewMethods(nameMethods, DECLARED_METHODS.get(klass), klass == javaClass, true);
addNewMethods(nameMethods, DECLARED_METHODS.get(klass), klass == javaClass, true);
}
catch (SecurityException e) { /* ignored */ }
}
@@ -494,7 +496,7 @@ static Map<String, List<Method>> getMethods(final Class<?> javaClass) {
// add methods, not including static (should be none on
// interfaces anyway) and not replacing child methods with
// parent methods
totalMethods += addNewMethods(nameMethods, METHODS.get(iface), false, false);
addNewMethods(nameMethods, METHODS.get(iface), false, false);
}
catch (SecurityException e) { /* ignored */ }
}
Original file line number Diff line number Diff line change
@@ -12,7 +12,7 @@ public class InstanceMethodInvokerInstaller extends MethodInstaller {

@Override void install(final RubyModule proxy) {
if ( hasLocalMethod() ) {
defineMethods(proxy, new InstanceMethodInvoker(proxy, methods), true);
defineMethods(proxy, new InstanceMethodInvoker(proxy, methods));
}
}
}
Original file line number Diff line number Diff line change
@@ -13,30 +13,34 @@
*/
public abstract class MethodInstaller extends NamedInstaller {

protected final List<Method> methods = new ArrayList<Method>(4);
protected final List<Method> methods = new ArrayList<>(4);
protected List<String> aliases;
private boolean localMethod;

public MethodInstaller(String name, int type) { super(name, type); }

// called only by initializing thread; no synchronization required
void addMethod(final Method method, final Class<?> clazz) {
final void addMethod(final Method method, final Class<?> clazz) {
this.methods.add(method);
localMethod |=
clazz == method.getDeclaringClass() ||
method.getDeclaringClass().isInterface();
}

// called only by initializing thread; no synchronization required
void addAlias(final String alias) {
final void addAlias(final String alias) {
List<String> aliases = this.aliases;
if (aliases == null) {
aliases = this.aliases = new ArrayList<String>(4);
aliases = this.aliases = new ArrayList<>(4);
}
if ( ! aliases.contains(alias) ) aliases.add(alias);
}

protected void defineMethods(RubyModule target, DynamicMethod invoker, boolean checkDups) {
final void defineMethods(RubyModule target, DynamicMethod invoker) {
defineMethods(target, invoker, true);
}

protected final void defineMethods(RubyModule target, DynamicMethod invoker, boolean checkDups) {
String oldName = this.name;
target.addMethod(oldName, invoker);

5 changes: 3 additions & 2 deletions core/src/main/java/org/jruby/runtime/Helpers.java
Original file line number Diff line number Diff line change
@@ -990,10 +990,11 @@ public static void checkSuperDisabledOrOutOfMethod(ThreadContext context, RubyMo
if (klass == null) {
if (name != null) {
throw context.runtime.newNameError("superclass method '" + name + "' disabled", name);
} else {
throw context.runtime.newNoMethodError("super called outside of method", null, context.nil);
}
}
if (name == null) {
throw context.runtime.newNoMethodError("super called outside of method", null, context.nil);
}
}

public static Block ensureSuperBlock(Block given, Block parent) {
16 changes: 15 additions & 1 deletion core/src/main/java/org/jruby/runtime/Signature.java
Original file line number Diff line number Diff line change
@@ -256,7 +256,7 @@ public static Signature decode(long l) {
(int)(l >>> ENCODE_KWARGS_SHIFT) & MAX_ENCODED_ARGS_MASK,
(int)(l >>> ENCODE_REQKWARGS_SHIFT) & MAX_ENCODED_ARGS_MASK,
Rest.fromOrdinal((int)((l >>> ENCODE_REST_SHIFT) & MAX_ENCODED_ARGS_MASK)),
(byte)(l >>> ENCODE_RESTKWARGS_SHIFT) & MAX_ENCODED_ARGS_MASK
(byte)((l >>> ENCODE_RESTKWARGS_SHIFT) & MAX_ENCODED_ARGS_MASK)

);
}
@@ -283,4 +283,18 @@ public void checkArity(Ruby runtime, IRubyObject[] args) {
}
}
}

public boolean equals(Object other) {
if (!(other instanceof Signature)) return false;

Signature otherSig = (Signature) other;

return pre == otherSig.pre &&
opt == otherSig.opt &&
post == otherSig.post &&
rest == otherSig.rest &&
kwargs == otherSig.kwargs &&
requiredKwargs == otherSig.requiredKwargs &&
keyRest == otherSig.keyRest;
}
}
2 changes: 1 addition & 1 deletion core/src/main/java/org/jruby/util/JRubyFile.java
Original file line number Diff line number Diff line change
@@ -163,7 +163,7 @@ public String getAbsolutePath() {
final String path = super.getPath();
if (path.startsWith("uri:")) {
// TODO better do not collapse // to / for uri: files
return path.replaceFirst(":/([^/])", "://$1" );
return normalizeSeps(path.replaceFirst(":/([^/])", "://$1" ));
}
return normalizeSeps(new File(path).getAbsolutePath());
}
35 changes: 16 additions & 19 deletions core/src/main/java/org/jruby/util/RecursiveComparator.java
Original file line number Diff line number Diff line change
@@ -31,7 +31,9 @@ public static <T> IRubyObject compare(ThreadContext context, T invokable, IRubyO
RecursiveComparator.Pair pair = new RecursiveComparator.Pair(a, b);

if ((seen = context.getRecursiveSet()) == null) {
context.setRecursiveSet(seen = new HashSet<Pair>());
// 95+% of time set stays low - holding 1 object
// NOTE: maybe its worth starting with a singletonSet?
context.setRecursiveSet(seen = new HashSet<Pair>(4));
clear = true;
}
else if (seen.contains(pair)) { // are we recursing?
@@ -44,21 +46,22 @@ else if (seen.contains(pair)) { // are we recursing?
if (a instanceof RubyHash) {
RubyHash hash = (RubyHash) a;
return hash.compare(context, (RubyHash.VisitorWithState<RubyHash>) invokable, b);
} else if (a instanceof RubyArray) {
}
if (a instanceof RubyArray) {
RubyArray array = (RubyArray) a;
return array.compare(context, (CallSite) invokable, b);
} else {
return ((CallSite) invokable).call(context, a, a, b);
}
} finally {
return ((CallSite) invokable).call(context, a, a, b);
}
finally {
if (clear) context.setRecursiveSet(null);
}
}

public static class Pair
{
private int a;
private int b;
final int a;
final int b;

public Pair(IRubyObject a, IRubyObject b) {
this.a = System.identityHashCode(a);
@@ -67,23 +70,17 @@ public Pair(IRubyObject a, IRubyObject b) {

@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (other == null || !(other instanceof Pair)) {
return false;
if (this == other) return true;
if (other instanceof Pair) {
Pair pair = (Pair) other;
return a == pair.a && b == pair.b;
}

Pair pair = (Pair) other;

return a == pair.a && b == pair.b;
return false;
}

@Override
public int hashCode() {
int result = a;
result = 31 * result + b;
return result;
return 31 * a + b;
}
}

40 changes: 13 additions & 27 deletions core/src/main/ruby/jruby/kernel/enumerable.rb
Original file line number Diff line number Diff line change
@@ -67,50 +67,35 @@ def slice_when(&block)
raise ArgumentError.new("missing block") unless block

Enumerator.new do |enum|
ary = nil
last_after = nil
if size == 1
each {|x| enum.yield [x]}
else
each_cons(2) do |before, after|
last_after = after
match = block.call before, after

ary ||= []
if match
ary << before
enum.yield ary
ary = []
else
ary << before
end
end

unless ary.nil?
ary << last_after
enum.yield ary
end
end
__slicey_chunky(false, enum, block)
end
end

def chunk_while(&block)
raise ArgumentError.new("missing block") unless block

Enumerator.new do |enum|
__slicey_chunky(true, enum, block)
end
end

def __slicey_chunky(invert, enum, block)
if respond_to?(:size) && size == 1
each {|x| enum.yield [x]}
else
ary = nil
last_after = nil
each_cons(2) do |before, after|
last_after = after
match = block.call before, after

ary ||= []
if match
ary << before
else
if invert ? !match : match
ary << before
enum.yield ary
ary = []
else
ary << before
end
end

@@ -120,6 +105,7 @@ def chunk_while(&block)
end
end
end
private :__slicey_chunky

def lazy
klass = Enumerator::Lazy::LAZY_WITH_NO_BLOCK # Note: class_variable_get is private in 1.8
1 change: 1 addition & 0 deletions core/src/test/java/org/jruby/test/MainTestSuite.java
Original file line number Diff line number Diff line change
@@ -93,6 +93,7 @@ public static Test suite() throws Throwable {
suite.addTestSuite(TestRubyRational.class);
suite.addTestSuite(TestRecursiveCheck.class);
suite.addTestSuite(TestEncodingAPI.class);
suite.addTestSuite(TestSignature.class);
// Disabled test due to difficulty of making WeakRef logic deterministic
// suite.addTestSuite(TestRegexpCache.class);
return suite;
39 changes: 39 additions & 0 deletions core/src/test/java/org/jruby/test/TestRubyBigDecimal.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
package org.jruby.test;

import junit.framework.TestCase;
import org.jruby.Ruby;
import org.jruby.RubyException;
import org.jruby.RubyFloat;
import org.jruby.exceptions.RaiseException;
import org.jruby.ext.bigdecimal.RubyBigDecimal;
import org.jruby.runtime.ThreadContext;

import java.math.BigDecimal;

public class TestRubyBigDecimal extends TestCase {
public void testFormatWithLeadingPlus() {
@@ -16,6 +23,38 @@ public void testFormatWithLeadingSpace() {
assertFalse(RubyBigDecimal.formatHasLeadingSpace("1"));
}

public void testGetVpValueWithPrec19ToRaiseFloatDomainErrorExceptionForFloatNAN() {
Ruby runtime = Ruby.newInstance();
ThreadContext currentContext = runtime.getCurrentContext();

RubyBigDecimal decimalValue = new RubyBigDecimal(runtime, new BigDecimal("1"));

RubyFloat NAN = RubyFloat.newFloat(runtime, RubyFloat.NAN);

try {
decimalValue.op_quo20(currentContext, NAN);
} catch (RaiseException re) {
RubyException rubyException = re.getException();
assertEquals(runtime.getFloatDomainError(), rubyException.getMetaClass());
}
}

public void testGetVpValueWithPrec19ToRaiseFloatDomainErrorExceptionForFloatINFINITY() {
Ruby runtime = Ruby.newInstance();
ThreadContext currentContext = runtime.getCurrentContext();

RubyBigDecimal decimalValue = new RubyBigDecimal(runtime, new BigDecimal("1"));

RubyFloat INFINITY = RubyFloat.newFloat(runtime, RubyFloat.INFINITY);

try {
decimalValue.op_quo20(currentContext, INFINITY);
} catch (RaiseException re) {
RubyException rubyException = re.getException();
assertEquals(runtime.getFloatDomainError(), rubyException.getMetaClass());
}
}

public void testFormatWithFloatingPointNotation() {
assertFalse(RubyBigDecimal.formatHasFloatingPointNotation("5E"));
assertTrue(RubyBigDecimal.formatHasFloatingPointNotation("5F"));
16 changes: 16 additions & 0 deletions core/src/test/java/org/jruby/test/TestSignature.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.jruby.test;

import junit.framework.TestCase;
import org.jruby.runtime.Signature;

public class TestSignature extends TestCase {
public void testEncodeDecode() {
Signature sig = Signature.from(1, 1, 1, 1, 1, Signature.Rest.NORM, 1);

assertEquals(sig, Signature.decode(sig.encode()));

sig = Signature.from(1, 1, 1, 1, 1, Signature.Rest.ANON, -11);

assertEquals(sig, Signature.decode(sig.encode()));
}
}
4 changes: 2 additions & 2 deletions lib/pom.xml
Original file line number Diff line number Diff line change
@@ -12,7 +12,7 @@ DO NOT MODIFIY - GENERATED CODE
<parent>
<groupId>org.jruby</groupId>
<artifactId>jruby-parent</artifactId>
<version>9.1.7.0-SNAPSHOT</version>
<version>9.1.7.0</version>
</parent>
<artifactId>jruby-stdlib</artifactId>
<name>JRuby Lib Setup</name>
@@ -28,7 +28,7 @@ DO NOT MODIFIY - GENERATED CODE
<dependency>
<groupId>org.jruby</groupId>
<artifactId>jruby-core</artifactId>
<version>9.1.7.0-SNAPSHOT</version>
<version>9.1.7.0</version>
<scope>test</scope>
</dependency>
<dependency>
15 changes: 4 additions & 11 deletions lib/ruby/stdlib/tempfile.rb
Original file line number Diff line number Diff line change
@@ -32,18 +32,11 @@ class Tempfile
# ... do something with f ...
# end
#
def Tempfile.create(basename, *rest)
def Tempfile.create(basename, tmpdir=nil, mode: 0, **options)
tmpfile = nil
Dir::Tmpname.create(basename, *rest) do |tmpname, n, opts|
mode = File::RDWR|File::CREAT|File::EXCL
perm = 0600
if opts
mode |= opts.delete(:mode) || 0
opts[:perm] = perm
perm = nil
else
opts = perm
end
Dir::Tmpname.create(basename, tmpdir, options) do |tmpname, n, opts|
mode |= File::RDWR|File::CREAT|File::EXCL
opts[:perm] = 0600
tmpfile = File.open(tmpname, mode, opts)
end
if block_given?
2 changes: 1 addition & 1 deletion pom.rb
Original file line number Diff line number Diff line change
@@ -80,7 +80,7 @@
# used in ./lib/pom.rb and ./maven/jruby-stdlib/pom.rb
'rdoc.version' => '4.2.0',
'rake.version' => '10.4.2',
'jar-dependencies.version' => '0.3.5',
'jar-dependencies.version' => '0.3.9',

'jruby-launcher.version' => '1.1.1',
'ant.version' => '1.9.2',
7 changes: 5 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
@@ -16,7 +16,7 @@ DO NOT MODIFIY - GENERATED CODE
</parent>
<groupId>org.jruby</groupId>
<artifactId>jruby-parent</artifactId>
<version>9.1.7.0-SNAPSHOT</version>
<version>9.1.7.0</version>
<packaging>pom</packaging>
<name>JRuby</name>
<description>JRuby is the effort to recreate the Ruby (http://www.ruby-lang.org) interpreter in Java.</description>
@@ -128,7 +128,7 @@ DO NOT MODIFIY - GENERATED CODE
<jruby.plugins.version>1.0.10</jruby.plugins.version>
<invoker.skip>true</invoker.skip>
<json.version>1.8.3</json.version>
<jar-dependencies.version>0.3.5</jar-dependencies.version>
<jar-dependencies.version>0.3.9</jar-dependencies.version>
<power_assert.version>0.2.3</power_assert.version>
<version.jruby>${project.version}</version.jruby>
<bouncy-castle.version>1.47</bouncy-castle.version>
@@ -273,6 +273,9 @@ DO NOT MODIFIY - GENERATED CODE
<requireMavenVersion>
<version>[3.3.0,)</version>
</requireMavenVersion>
<requireReleaseDeps>
<message>No Snapshots Allowed!</message>
</requireReleaseDeps>
</rules>
</configuration>
</execution>
17 changes: 16 additions & 1 deletion spec/java_integration/paths/uri_classloader_spec.rb
Original file line number Diff line number Diff line change
@@ -5,8 +5,23 @@
let(:unc_like_path) { "uri:classloader://foo/b.gemspec" }
let(:normal_path) { "uri:classloader:/foo/b.gemspec" }

it "sent to expand_path will normalize slashes" do
let(:sub_path) { "../../../vendor/jface/*.jar" }
let(:base_path) { "uri:classloader:/gems/swt-4.6.1/lib/swt/full.rb" }
let(:resolved_path) { "uri:classloader://gems/swt-4.6.1/vendor/jface/*.jar" }
let(:resolved_path_win) { "uri:classloader:/gems/swt-4.6.1/vendor/jface/*.jar" }
let(:windows) { RbConfig::CONFIG['host_os'] =~ /Windows|mswin/ }

it "sent to expand_patyh will normalize slashes" do
expect(File.expand_path(unc_like_path)).to eq(normal_path)

# FIXME: It is unclear why 1 arg expand path is '/' and not '//' so perhaps
# we need to resolve this inconsistency.
if windows
expect(File.expand_path(sub_path, base_path)).to eq(resolved_path_win)
else
expect(File.expand_path(sub_path, base_path)).to eq(resolved_path)
end

# On windows this converts to many backslashes on front and backslashes
# Everywhere. JRuby #3771 is tracking this. We should add a check
# to not only make sure :/ vs :\\\\ but also that not \ is present at
6 changes: 6 additions & 0 deletions spec/ruby/core/enumerable/chunk_while_spec.rb
Original file line number Diff line number Diff line change
@@ -34,5 +34,11 @@
lambda { @enum.chunk_while }.should raise_error(ArgumentError)
end
end

context "on a single-element array" do
it "ignores the block and returns an enumerator that yields [element]" do
[1].chunk_while {|x| x.even?}.to_a.should == [[1]]
end
end
end
end
57 changes: 56 additions & 1 deletion test/jruby/test_case.rb
Original file line number Diff line number Diff line change
@@ -22,7 +22,6 @@ def test_case_with_no_expression
end

def test_case_with_ranges
x = nil
case 10
when 1..3
x = 'a'
@@ -49,6 +48,62 @@ def test_case_with_else
assert_equal('c', x)
end

def test_case_consecutive
params = [1, 2, 3, 5, 10, 11, 14, 15, 16, 17, 8, 9]
expect = [10, 20, 30, 50, 0, 10, 40, 0, 10, 20, 8, 9]
assert_equal expect, params.map { |p| case_12345(p) }
end

def case_12345(p)
p = p % 5 if p >= 10
case p
when 1
10
when 2
20
when 3
30
when 4
40
when 5
50
else return p
end
end
private :case_12345

def test_case_with_holes
params = [1, 2, 3, 5, 10, 11, 15, 16, 18, 8]
expect = [10, nil, 30, 50, nil, 10, nil, 10, 30, nil]
assert_equal expect, params.map { |p| case_135(p) }
end

def case_135(p)
p = p % 5 if p >= 10
case p
when 1 then 10
when 3 then 30
when 5 then 50
end
end

def test_case_24 # GH-4429
args_21 = [1, 2]; args_22 = [2, 3]; args_23 = ['3', '4']
args_41 = [1, 2, nil, 3]; args_42 = ['2', '3', '4', '5']
expect = [2, 3, '4', 3, '5']
assert_equal expect, [args_21, args_22, args_23, args_41, args_42].map { |a| case_24(*a) }
assert_raise(ArgumentError) { case_24 }
assert_raise(ArgumentError) { case_24(1) }
end

def case_24(*args)
case s = args.size
when 2 then args[1]
when 4 then args[3]
else raise ArgumentError.new("size: #{s} " + args.inspect)
end
end

def test_case_no_match_returns_nil
x = case nil
when String then "HEH1"
2 changes: 1 addition & 1 deletion truffle/pom.xml
Original file line number Diff line number Diff line change
@@ -12,7 +12,7 @@ DO NOT MODIFIY - GENERATED CODE
<parent>
<groupId>org.jruby</groupId>
<artifactId>jruby-parent</artifactId>
<version>9.1.7.0-SNAPSHOT</version>
<version>9.1.7.0</version>
</parent>
<artifactId>jruby-truffle</artifactId>
<name>JRuby Truffle</name>

0 comments on commit 87f774a

Please sign in to comment.