Skip to content

Commit

Permalink
Merge branch 'ruby-2.3' into ruby-2.3+socket
Browse files Browse the repository at this point in the history
headius committed Feb 7, 2016
2 parents 5f0985f + 68071f8 commit 2029231
Showing 816 changed files with 7,414 additions and 5,994 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -40,7 +40,7 @@ For [`rbenv`](https://github.com/sstephenson/rbenv) you will need the
package manager can provide these. Then you can run:

```
$ rbenv install jruby-9.0.0.0-dev
$ rbenv install jruby-9.0.5.0
```

For [`rvm`](https://rvm.io) you can simply do:
5 changes: 1 addition & 4 deletions bin/jruby+truffle
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
#!/usr/bin/env bash
"exec" "`dirname $BASH_SOURCE[0]`/jruby" "$0" "$@"

require File.join(JRuby.runtime.instance_config.jruby_home, 'lib/ruby/truffle/jruby+truffle/runner')

JRubyTruffleRunner.new
exec `dirname $BASH_SOURCE[0]`/jruby `dirname $BASH_SOURCE[0]`/../lib/ruby/truffle/jruby+truffle/bin/jruby+truffle "$@"
23 changes: 23 additions & 0 deletions core/src/main/java/org/jruby/Ruby.java
Original file line number Diff line number Diff line change
@@ -4860,6 +4860,24 @@ public Object constant() {
return constant;
}

/**
* Set the base Class#new method.
*
* @param baseNewMethod
*/
public void setBaseNewMethod(DynamicMethod baseNewMethod) {
this.baseNewMethod = baseNewMethod;
}

/**
* Get the base Class#new method.
*
* @return the base Class#new method
*/
public DynamicMethod getBaseNewMethod() {
return baseNewMethod;
}

@Deprecated
public int getSafeLevel() {
return 0;
@@ -5213,4 +5231,9 @@ public void addToObjectSpace(boolean useObjectSpace, IRubyObject object) {
* accesses.
*/
private final Object constant;

/**
* The built-in Class#new method, so we can bind more directly to allocate and initialize.
*/
private DynamicMethod baseNewMethod;
}
11 changes: 10 additions & 1 deletion core/src/main/java/org/jruby/RubyBasicObject.java
Original file line number Diff line number Diff line change
@@ -233,10 +233,19 @@ static void recacheBuiltinMethods(Ruby runtime) {
}

@JRubyMethod(name = "initialize", visibility = PRIVATE)
public IRubyObject initialize19(ThreadContext context) {
public IRubyObject initialize(ThreadContext context) {
return context.nil;
}

@Deprecated
public IRubyObject initialize19(ThreadContext context) {
return initialize(context);
}

//public IRubyObject initialize(ThreadContext context, IRubyObject[] args) {
// return initialize(context);
//}

/**
* Standard path for object creation. Objects are entered into ObjectSpace
* only if ObjectSpace is enabled.
2 changes: 2 additions & 0 deletions core/src/main/java/org/jruby/RubyClass.java
Original file line number Diff line number Diff line change
@@ -113,6 +113,8 @@ public static void createClassClass(Ruby runtime, RubyClass classClass) {
classClass.undefineMethod("extend_object");

classClass.defineAnnotatedMethods(RubyClass.class);

runtime.setBaseNewMethod(classClass.searchMethod("new"));
}

public static final ObjectAllocator CLASS_ALLOCATOR = new ObjectAllocator() {
10 changes: 0 additions & 10 deletions core/src/main/java/org/jruby/RubyObject.java
Original file line number Diff line number Diff line change
@@ -279,16 +279,6 @@ public IRubyObject allocate(Ruby runtime, RubyClass klass) {
}
};

@Deprecated
@Override
public IRubyObject initialize() {
return getRuntime().getNil();
}

public IRubyObject initialize(ThreadContext context) {
return initialize19(context);
}

/**
* Will make sure that this object is added to the current object
* space.
34 changes: 7 additions & 27 deletions core/src/main/java/org/jruby/RubyString.java
Original file line number Diff line number Diff line change
@@ -1497,40 +1497,20 @@ public static RubyString newInstance(IRubyObject recv, IRubyObject[] args, Block
}

@Override
@JRubyMethod(name = "initialize", visibility = PRIVATE)
public IRubyObject initialize(ThreadContext context) {
return initialize19(context);
}

public IRubyObject initialize(ThreadContext context, IRubyObject arg0) {
return initialize19(context, new IRubyObject[] { arg0 });
return this;
}

@JRubyMethod(name = "initialize", visibility = PRIVATE)
@Override
public IRubyObject initialize19(ThreadContext context) {
public IRubyObject initialize(ThreadContext context, IRubyObject arg0) {
replace19(arg0);
return this;
}

@JRubyMethod(name = "initialize", visibility = PRIVATE, optional = 2)
public IRubyObject initialize19(ThreadContext context, IRubyObject[] args) {
if ( args.length == 0 ) return this;

IRubyObject arg = args[ args.length - 1 ];

final IRubyObject string; final IRubyObject encoding;
if ( arg instanceof RubyHash ) { // new encoding: ...
final RubySymbol enc = context.runtime.newSymbol("encoding");
encoding = ( (RubyHash) arg ).fastARef(enc);
string = args.length > 1 ? args[0] : null;
}
else {
string = arg; encoding = null;
}

if ( string != null ) replace19(string);
if ( encoding != null ) force_encoding(context, encoding);

return this;
@Deprecated
public IRubyObject initialize19(ThreadContext context, IRubyObject arg0) {
return initialize(context, arg0);
}

public IRubyObject casecmp(ThreadContext context, IRubyObject other) {
40 changes: 21 additions & 19 deletions core/src/main/java/org/jruby/compiler/JITCompiler.java
Original file line number Diff line number Diff line change
@@ -49,10 +49,12 @@
import org.jruby.util.JavaNameMangler;
import org.jruby.util.OneShotClassLoader;
import org.jruby.util.cli.Options;
import org.jruby.util.collections.IntHashMap;
import org.jruby.util.log.Logger;
import org.jruby.util.log.LoggerFactory;
import org.objectweb.asm.Opcodes;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.security.MessageDigest;
@@ -185,9 +187,9 @@ public void buildThresholdReached(ThreadContext context, final Compilable method
private static final MethodHandles.Lookup PUBLIC_LOOKUP = MethodHandles.publicLookup().in(Ruby.class);

private class FullBuildTask implements Runnable {
private final Compilable method;
private final Compilable<InterpreterContext> method;

public FullBuildTask(Compilable method) {
FullBuildTask(Compilable<InterpreterContext> method) {
this.method = method;
}

@@ -278,41 +280,41 @@ public void run() {
}

if (config.isJitLogging()) {
log(method.getImplementationClass(), method.getFile(), method.getLine(), className + "." + methodName, "done jitting");
log(method.getImplementationClass(), method.getFile(), method.getLine(), className + '.' + methodName, "done jitting");
}

Map<Integer, MethodType> signatures = context.getNativeSignatures();
String jittedName = context.getJittedName();
if (signatures.size() == 1) {
final String jittedName = context.getJittedName();
MethodHandle variable = PUBLIC_LOOKUP.findStatic(sourceClass, jittedName, context.getNativeSignature(-1));
IntHashMap<MethodType> signatures = context.getNativeSignaturesExceptVariable();

if (signatures.size() == 0) {
// only variable-arity
method.completeBuild(
new CompiledIRMethod(
PUBLIC_LOOKUP.findStatic(sourceClass, jittedName, signatures.get(-1)),
variable,
method.getIRScope(),
method.getVisibility(),
method.getImplementationClass(),
method.getIRScope().receivesKeywordArgs()));

} else {
// also specific-arity
for (Map.Entry<Integer, MethodType> entry : signatures.entrySet()) {
if (entry.getKey() == -1) continue; // variable arity handle pushed above

for (IntHashMap.Entry<MethodType> entry : signatures.entrySet()) {
method.completeBuild(
new CompiledIRMethod(
PUBLIC_LOOKUP.findStatic(sourceClass, jittedName, signatures.get(-1)),
variable,
PUBLIC_LOOKUP.findStatic(sourceClass, jittedName, entry.getValue()),
entry.getKey(),
method.getIRScope(),
method.getVisibility(),
method.getImplementationClass(),
method.getIRScope().receivesKeywordArgs()));
break;
break; // FIXME: only supports one arity
}
}
} catch (Throwable t) {
if (config.isJitLogging()) {
log(method.getImplementationClass(), method.getFile(), method.getLine(), className + "." + methodName, "Could not compile; passes run: " + method.getIRScope().getExecutedPasses(), t.getMessage());
log(method.getImplementationClass(), method.getFile(), method.getLine(), className + '.' + methodName, "Could not compile; passes run: " + method.getIRScope().getExecutedPasses(), t.getMessage());
if (config.isJitLoggingVerbose()) {
t.printStackTrace();
}
@@ -475,7 +477,7 @@ public String name() {

@Override
public String toString() {
return methodName + "() at " + method.getFile() + ":" + method.getLine();
return methodName + "() at " + method.getFile() + ':' + method.getLine();
}

private final String packageName;
@@ -487,7 +489,7 @@ public String toString() {

private byte[] bytecode;
private long compileTime;
private String name;
private final String name;
}

public static class BlockJITClassGenerator {
@@ -505,8 +507,8 @@ public BlockJITClassGenerator(String className, String methodName, String key, R
} else {
digestString = key;
}
this.className = packageName + "/" + className.replace('.', '/') + CLASS_METHOD_DELIMITER + JavaNameMangler.mangleMethodName(methodName) + "_" + digestString;
this.name = this.className.replaceAll("/", ".");
this.className = packageName + '/' + className.replace('.', '/') + CLASS_METHOD_DELIMITER + JavaNameMangler.mangleMethodName(methodName) + '_' + digestString;
this.name = this.className.replace('/', '.');
this.methodName = methodName;
this.body = body;
this.visitor = visitor;
@@ -562,7 +564,7 @@ public String name() {

@Override
public String toString() {
return "{} at " + body.getFile() + ":" + body.getLine();
return "{} at " + body.getFile() + ':' + body.getLine();
}

private final String packageName;
@@ -574,7 +576,7 @@ public String toString() {

private byte[] bytecode;
private long compileTime;
private String name;
private final String name;
}

static void log(RubyModule implementationClass, String file, int line, String name, String message, String... reason) {
3 changes: 1 addition & 2 deletions core/src/main/java/org/jruby/ext/ffi/Struct.java
Original file line number Diff line number Diff line change
@@ -123,11 +123,10 @@ static final StructLayout getStructLayout(Ruby runtime, IRubyObject structClass)
}
}

@Override
@JRubyMethod(name = "initialize", visibility = PRIVATE)
public IRubyObject initialize(ThreadContext context) {

memory = MemoryPointer.allocate(context.runtime, layout.getSize(), 1, true);

return this;
}

1 change: 1 addition & 0 deletions core/src/main/java/org/jruby/ext/socket/RubyUDPSocket.java
Original file line number Diff line number Diff line change
@@ -95,6 +95,7 @@ public RubyUDPSocket(Ruby runtime, RubyClass type) {
super(runtime, type);
}

@Override
@JRubyMethod(visibility = Visibility.PRIVATE)
public IRubyObject initialize(ThreadContext context) {
return initialize(context, StandardProtocolFamily.INET);
Original file line number Diff line number Diff line change
@@ -11,18 +11,15 @@
import org.jruby.runtime.ArgumentDescriptor;
import org.jruby.runtime.Arity;
import org.jruby.runtime.Block;
import org.jruby.runtime.Helpers;
import org.jruby.runtime.PositionAware;
import org.jruby.runtime.Signature;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.Visibility;
import org.jruby.runtime.builtin.IRubyObject;

import java.io.ByteArrayOutputStream;
import java.lang.invoke.MethodHandle;

import org.jruby.runtime.Helpers;
import org.jruby.util.cli.Options;

public class CompiledIRMethod extends AbstractIRMethod {
protected final MethodHandle variable;

@@ -88,147 +85,112 @@ protected void pre(ThreadContext context, StaticScope staticScope, RubyModule im

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject[] args, Block block) {
try {
if (!hasExplicitCallProtocol) return callNoProtocol(context, self, name, args, block);
if (!hasExplicitCallProtocol) return callNoProtocol(context, self, name, args, block);

if (hasKwargs) IRRuntimeHelpers.frobnicateKwargsArgument(context, getSignature().required(), args);
if (hasKwargs) IRRuntimeHelpers.frobnicateKwargsArgument(context, getSignature().required(), args);

return (IRubyObject)this.variable.invokeExact(context, staticScope, self, args, block, implementationClass, name);
} catch (Throwable t) {
Helpers.throwException(t);
// not reached
return null;
}
return invokeExact(this.variable, context, staticScope, self, args, block, implementationClass, name);
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, Block block) {
try {
if (specificArity != 0) return call(context, self, clazz, name, IRubyObject.NULL_ARRAY, block);
if (specificArity != 0) return call(context, self, clazz, name, IRubyObject.NULL_ARRAY, block);

if (!hasExplicitCallProtocol) return callNoProtocol(context, self, clazz, name, block);
if (!hasExplicitCallProtocol) return callNoProtocol(context, self, clazz, name, block);

return (IRubyObject)this.specific.invokeExact(context, staticScope, self, block, implementationClass, name);
} catch (Throwable t) {
Helpers.throwException(t);
// not reached
return null;
}
return invokeExact(this.specific, context, staticScope, self, block, implementationClass, name);
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, Block block) {
try {
if (!hasExplicitCallProtocol) return callNoProtocol(context, self, clazz, name, arg0, block);
if (!hasExplicitCallProtocol) return callNoProtocol(context, self, clazz, name, arg0, block);

if (specificArity != 1) return call(context, self, clazz, name, new IRubyObject[]{arg0}, block);
if (specificArity != 1) return call(context, self, clazz, name, new IRubyObject[]{arg0}, block);

return (IRubyObject)this.specific.invokeExact(context, staticScope, self, arg0, block, implementationClass, name);
} catch (Throwable t) {
Helpers.throwException(t);
// not reached
return null;
}
return invokeExact(this.specific, context, staticScope, self, arg0, block, implementationClass, name);
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, IRubyObject arg1, Block block) {
try {
if (!hasExplicitCallProtocol) return callNoProtocol(context, self, clazz, name, arg0, arg1, block);
if (!hasExplicitCallProtocol) return callNoProtocol(context, self, clazz, name, arg0, arg1, block);

if (specificArity != 2) return call(context, self, clazz, name, new IRubyObject[] {arg0, arg1}, block);
if (specificArity != 2) return call(context, self, clazz, name, new IRubyObject[] {arg0, arg1}, block);

return (IRubyObject)this.specific.invokeExact(context, staticScope, self, arg0, arg1, block, implementationClass, name);
} catch (Throwable t) {
Helpers.throwException(t);
// not reached
return null;
}
return invokeExact(this.specific, context, staticScope, self, arg0, arg1, block, implementationClass, name);
}

@Override
public IRubyObject call(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, Block block) {
try {
if (!hasExplicitCallProtocol) return callNoProtocol(context, self, clazz, name, arg0, arg1, arg2, block);
if (!hasExplicitCallProtocol) return callNoProtocol(context, self, clazz, name, arg0, arg1, arg2, block);

if (specificArity != 3) return call(context, self, clazz, name, new IRubyObject[] {arg0, arg1, arg2 }, block);
if (specificArity != 3) return call(context, self, clazz, name, new IRubyObject[] {arg0, arg1, arg2 }, block);

return (IRubyObject)this.specific.invokeExact(context, staticScope, self, arg0, arg1, arg2, block, implementationClass, name);
} catch (Throwable t) {
Helpers.throwException(t);
// not reached
return null;
}
return invokeExact(this.specific, context, staticScope, self, arg0, arg1, arg2, block, implementationClass, name);
}

private IRubyObject callNoProtocol(ThreadContext context, IRubyObject self, String name, IRubyObject[] args, Block block) throws Throwable {
private IRubyObject callNoProtocol(ThreadContext context, IRubyObject self, String name, IRubyObject[] args, Block block) {
StaticScope staticScope = this.staticScope;
RubyModule implementationClass = this.implementationClass;
pre(context, staticScope, implementationClass, self, name, block);

if (hasKwargs) IRRuntimeHelpers.frobnicateKwargsArgument(context, getSignature().required(), args);

try {
return (IRubyObject)this.variable.invokeExact(context, staticScope, self, args, block, implementationClass, name);
} finally {
post(context);
return invokeExact(this.variable, context, staticScope, self, args, block, implementationClass, name);
}
finally { post(context); }
}

public IRubyObject callNoProtocol(ThreadContext context, IRubyObject self, RubyModule clazz, String name, Block block) throws Throwable {
public final IRubyObject callNoProtocol(ThreadContext context, IRubyObject self, RubyModule clazz, String name, Block block) {
if (specificArity != 0) return call(context, self, clazz, name, IRubyObject.NULL_ARRAY, block);

StaticScope staticScope = this.staticScope;
RubyModule implementationClass = this.implementationClass;
pre(context, staticScope, implementationClass, self, name, block);

try {
return (IRubyObject)this.specific.invokeExact(context, staticScope, self, block, implementationClass, name);
} finally {
post(context);
return invokeExact(this.specific, context, staticScope, self, block, implementationClass, name);
}
finally { post(context); }
}

public IRubyObject callNoProtocol(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, Block block) throws Throwable {
public final IRubyObject callNoProtocol(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, Block block) {
if (specificArity != 1) return call(context, self, clazz, name, Helpers.arrayOf(arg0), block);

StaticScope staticScope = this.staticScope;
RubyModule implementationClass = this.implementationClass;
pre(context, staticScope, implementationClass, self, name, block);

try {
return (IRubyObject)this.specific.invokeExact(context, staticScope, self, arg0, block, implementationClass, name);
} finally {
post(context);
return invokeExact(this.specific, context, staticScope, self, arg0, block, implementationClass, name);
}
finally { post(context); }
}

public IRubyObject callNoProtocol(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, IRubyObject arg1, Block block) throws Throwable {
public final IRubyObject callNoProtocol(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, IRubyObject arg1, Block block) {
if (specificArity != 2) return call(context, self, clazz, name, Helpers.arrayOf(arg0, arg1), block);

StaticScope staticScope = this.staticScope;
RubyModule implementationClass = this.implementationClass;
pre(context, staticScope, implementationClass, self, name, block);

try {
return (IRubyObject)this.specific.invokeExact(context, staticScope, self, arg0, arg1, block, implementationClass, name);
} finally {
post(context);
return invokeExact(this.specific, context, staticScope, self, arg0, arg1, block, implementationClass, name);
}
finally { post(context); }
}

public IRubyObject callNoProtocol(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, Block block) throws Throwable {
public final IRubyObject callNoProtocol(ThreadContext context, IRubyObject self, RubyModule clazz, String name, IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, Block block) {
if (specificArity != 3) return call(context, self, clazz, name, Helpers.arrayOf(arg0, arg1, arg2), block);

StaticScope staticScope = this.staticScope;
RubyModule implementationClass = this.implementationClass;
pre(context, staticScope, implementationClass, self, name, block);

try {
return (IRubyObject)this.specific.invokeExact(context, staticScope, self, arg0, arg1, arg2, block, implementationClass, name);
} finally {
post(context);
return invokeExact(this.specific, context, staticScope, self, arg0, arg1, arg2, block, implementationClass, name);
}
finally { post(context); }
}

@Override
@@ -243,4 +205,70 @@ public String getFile() {
public int getLine() {
return method.getLineNumber();
}

private static IRubyObject invokeExact(MethodHandle method,
ThreadContext context, StaticScope staticScope, IRubyObject self,
IRubyObject[] args, Block block,
RubyModule implementationClass, String name) {
try {
return (IRubyObject) method.invokeExact(context, staticScope, self, args, block, implementationClass, name);
}
catch (Throwable t) {
Helpers.throwException(t);
return null; // not reached
}
}

private static IRubyObject invokeExact(MethodHandle method,
ThreadContext context, StaticScope staticScope, IRubyObject self,
Block block,
RubyModule implementationClass, String name) {
try {
return (IRubyObject) method.invokeExact(context, staticScope, self, block, implementationClass, name);
}
catch (Throwable t) {
Helpers.throwException(t);
return null; // not reached
}
}

private static IRubyObject invokeExact(MethodHandle method,
ThreadContext context, StaticScope staticScope, IRubyObject self,
IRubyObject arg0, Block block,
RubyModule implementationClass, String name) {
try {
return (IRubyObject) method.invokeExact(context, staticScope, self, arg0, block, implementationClass, name);
}
catch (Throwable t) {
Helpers.throwException(t);
return null; // not reached
}
}

private static IRubyObject invokeExact(MethodHandle method,
ThreadContext context, StaticScope staticScope, IRubyObject self,
IRubyObject arg0, IRubyObject arg1, Block block,
RubyModule implementationClass, String name) {
try {
return (IRubyObject) method.invokeExact(context, staticScope, self, arg0, arg1, block, implementationClass, name);
}
catch (Throwable t) {
Helpers.throwException(t);
return null; // not reached
}
}

private static IRubyObject invokeExact(MethodHandle method,
ThreadContext context, StaticScope staticScope, IRubyObject self,
IRubyObject arg0, IRubyObject arg1, IRubyObject arg2, Block block,
RubyModule implementationClass, String name) {
try {
return (IRubyObject) method.invokeExact(context, staticScope, self, arg0, arg1, arg2, block, implementationClass, name);
}
catch (Throwable t) {
Helpers.throwException(t);
return null; // not reached
}
}

}
13 changes: 9 additions & 4 deletions core/src/main/java/org/jruby/ir/IRBuilder.java
Original file line number Diff line number Diff line change
@@ -379,7 +379,7 @@ private Operand buildOperand(Node node) throws NotCompilableException {
case ANDNODE: return buildAnd((AndNode) node);
case ARGSCATNODE: return buildArgsCat((ArgsCatNode) node);
case ARGSPUSHNODE: return buildArgsPush((ArgsPushNode) node);
case ARRAYNODE: return buildArray((ArrayNode) node);
case ARRAYNODE: return buildArray((ArrayNode) node, false);
case ATTRASSIGNNODE: return buildAttrAssign((AttrAssignNode) node);
case BACKREFNODE: return buildBackref((BackRefNode) node);
case BEGINNODE: return buildBegin((BeginNode) node);
@@ -815,15 +815,16 @@ public Operand buildAnd(final AndNode andNode) {
}
}

public Operand buildArray(ArrayNode node) {
public Operand buildArray(ArrayNode node, boolean operandOnly) {
Node[] nodes = node.children();
Operand[] elts = new Operand[nodes.length];
boolean containsAssignments = node.containsVariableAssignment();
for (int i = 0; i < nodes.length; i++) {
elts[i] = buildWithOrder(nodes[i], containsAssignments);
}

return copyAndReturnValue(new Array(elts));
Operand array = new Array(elts);
return operandOnly ? array : copyAndReturnValue(array);
}

public Operand buildArgsCat(final ArgsCatNode argsCatNode) {
@@ -3602,7 +3603,11 @@ public Operand buildYield(YieldNode node) {
}

Variable ret = createTemporaryVariable();
addInstr(new YieldInstr(ret, scope.getYieldClosureVariable(), build(argNode), unwrap));
if (argNode instanceof ArrayNode && unwrap) {
addInstr(new YieldInstr(ret, scope.getYieldClosureVariable(), buildArray((ArrayNode)argNode, true), unwrap));
} else {
addInstr(new YieldInstr(ret, scope.getYieldClosureVariable(), build(argNode), unwrap));
}
return ret;
}

2 changes: 0 additions & 2 deletions core/src/main/java/org/jruby/ir/IRClassBody.java
Original file line number Diff line number Diff line change
@@ -2,8 +2,6 @@

import org.jruby.parser.StaticScope;

/**
*/
public class IRClassBody extends IRModuleBody {
public IRClassBody(IRManager manager, IRScope lexicalParent, String name, int lineNumber, StaticScope scope) {
super(manager, lexicalParent, name, lineNumber, scope);
4 changes: 0 additions & 4 deletions core/src/main/java/org/jruby/ir/IRMethod.java
Original file line number Diff line number Diff line change
@@ -3,13 +3,9 @@
import org.jruby.ast.DefNode;
import org.jruby.ir.interpreter.InterpreterContext;
import org.jruby.ir.operands.LocalVariable;
import org.jruby.ir.persistence.IRDumper;
import org.jruby.ir.representations.BasicBlock;
import org.jruby.parser.StaticScope;
import org.jruby.runtime.ArgumentDescriptor;
import org.jruby.util.cli.Options;

import java.io.ByteArrayOutputStream;

public class IRMethod extends IRScope {
public final boolean isInstanceMethod;
5 changes: 0 additions & 5 deletions core/src/main/java/org/jruby/ir/IRScope.java
Original file line number Diff line number Diff line change
@@ -11,21 +11,16 @@
import org.jruby.ir.interpreter.InterpreterContext;
import org.jruby.ir.operands.*;
import org.jruby.ir.operands.Float;
import org.jruby.ir.operands.Boolean;
import org.jruby.ir.passes.*;
import org.jruby.ir.persistence.IRDumper;
import org.jruby.ir.representations.BasicBlock;
import org.jruby.ir.representations.CFG;
import org.jruby.ir.transformations.inlining.CFGInliner;
import org.jruby.ir.transformations.inlining.SimpleCloneInfo;
import org.jruby.parser.StaticScope;

import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;

import org.jruby.util.cli.Options;
import org.jruby.util.log.Logger;
import org.jruby.util.log.LoggerFactory;

12 changes: 10 additions & 2 deletions core/src/main/java/org/jruby/ir/instructions/YieldInstr.java
Original file line number Diff line number Diff line change
@@ -3,6 +3,7 @@
import org.jruby.ir.IRVisitor;
import org.jruby.ir.Interp;
import org.jruby.ir.Operation;
import org.jruby.ir.operands.Array;
import org.jruby.ir.operands.Operand;
import org.jruby.ir.operands.UndefinedValue;
import org.jruby.ir.operands.Variable;
@@ -72,8 +73,15 @@ public Object interpret(ThreadContext context, StaticScope currScope, DynamicSco
if (getYieldArg() == UndefinedValue.UNDEFINED) {
return IRRuntimeHelpers.yieldSpecific(context, blk);
} else {
IRubyObject yieldVal = (IRubyObject) getYieldArg().retrieve(context, self, currScope, currDynScope, temp);
return IRRuntimeHelpers.yield(context, blk, yieldVal, unwrapArray);
Operand yieldOp = getYieldArg();
if (unwrapArray && yieldOp instanceof Array && ((Array)yieldOp).size() > 1) {
// Special case this path!
// Don't build a RubyArray.
return blk.yieldValues(context, ((Array)yieldOp).retrieveArrayElts(context, self, currScope, currDynScope, temp));
} else {
IRubyObject yieldVal = (IRubyObject) yieldOp.retrieve(context, self, currScope, currDynScope, temp);
return IRRuntimeHelpers.yield(context, blk, yieldVal, unwrapArray);
}
}
}

18 changes: 14 additions & 4 deletions core/src/main/java/org/jruby/ir/operands/Array.java
Original file line number Diff line number Diff line change
@@ -9,6 +9,8 @@
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;

import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

@@ -17,7 +19,7 @@
// NOTE: This operand is only used in the initial stages of optimization.
// Further down the line, this array operand could get converted to calls
// that actually build a Ruby object
public class Array extends Operand {
public class Array extends Operand implements Iterable<Operand> {
private final Operand[] elts;

// SSS FIXME: Do we create a special-case for zero-length arrays?
@@ -107,15 +109,18 @@ public static Array decode(IRReaderDecoder d) {
return new Array(d.decodeOperandArray());
}

@Override
public Object retrieve(ThreadContext context, IRubyObject self, StaticScope currScope, DynamicScope currDynScope, Object[] temp) {
public IRubyObject[] retrieveArrayElts(ThreadContext context, IRubyObject self, StaticScope currScope, DynamicScope currDynScope, Object[] temp) {
IRubyObject[] elements = new IRubyObject[elts.length];

for (int i = 0; i < elements.length; i++) {
elements[i] = (IRubyObject) elts[i].retrieve(context, self, currScope, currDynScope, temp);
}
return elements;
}

return context.runtime.newArray(elements);
@Override
public Object retrieve(ThreadContext context, IRubyObject self, StaticScope currScope, DynamicScope currDynScope, Object[] temp) {
return context.runtime.newArray(retrieveArrayElts(context, self, currScope, currDynScope, temp));
}

@Override
@@ -126,4 +131,9 @@ public void visit(IRVisitor visitor) {
public Operand[] getElts() {
return elts;
}

@Override
public Iterator<Operand> iterator() {
return Arrays.asList(elts).iterator();
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package org.jruby.ir.passes;

import org.jruby.ir.*;
import org.jruby.ir.dataflow.analyses.StoreLocalVarPlacementProblem;
import org.jruby.ir.instructions.*;
import org.jruby.runtime.Signature;
import org.jruby.ir.operands.ImmutableLiteral;
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package org.jruby.ir.passes;

import org.jruby.ir.IRClosure;
import org.jruby.ir.IRFlags;
import org.jruby.ir.IRScope;
import org.jruby.ir.dataflow.analyses.LoadLocalVarPlacementProblem;
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package org.jruby.ir.passes;

import org.jruby.ir.IRClosure;
import org.jruby.ir.IRScope;
import org.jruby.ir.dataflow.analyses.LiveVariablesProblem;

Original file line number Diff line number Diff line change
@@ -82,7 +82,6 @@ public static Instr optInstr(IRScope s, Instr instr, Map<Operand,Operand> valueM
if (!instr.hasSideEffects()) {
if (instr instanceof CopyInstr) {
if (res.equals(val) && instr.canBeDeletedFromScope(s)) {
System.out.println("DEAD: marking instr dead!!");
instr.markDead();
}
} else {
Original file line number Diff line number Diff line change
@@ -8,12 +8,10 @@
import java.util.Map;
import org.jruby.ir.IRScope;
import org.jruby.ir.instructions.*;
import org.jruby.ir.operands.ImmutableLiteral;
import org.jruby.ir.operands.Operand;
import org.jruby.ir.operands.TemporaryVariable;
import org.jruby.ir.operands.Variable;


/**
* Takes multiple single def-use temporary variables and reduces them to share the same temp variable.
* This ends up reducing the amount of allocation and most likely helps hotspot warm up in some way quicker.
43 changes: 31 additions & 12 deletions core/src/main/java/org/jruby/ir/runtime/IRRuntimeHelpers.java
Original file line number Diff line number Diff line change
@@ -14,8 +14,6 @@
import org.jruby.internal.runtime.methods.InterpretedIRMethod;
import org.jruby.internal.runtime.methods.MixedModeIRMethod;
import org.jruby.internal.runtime.methods.UndefinedMethod;
import org.jruby.ir.IRFlags;
import org.jruby.ir.IRManager;
import org.jruby.ir.IRMetaClassBody;
import org.jruby.ir.IRScope;
import org.jruby.ir.IRScopeType;
@@ -833,27 +831,46 @@ public static RubyModule findInstanceMethodContainer(ThreadContext context, Dyna
}

public static RubyBoolean isBlockGiven(ThreadContext context, Object blk) {
if (blk instanceof RubyProc) blk = ((RubyProc)blk).getBlock();
if (blk instanceof RubyProc) blk = ((RubyProc) blk).getBlock();
if (blk instanceof RubyNil) blk = Block.NULL_BLOCK;
Block b = (Block)blk;
return context.runtime.newBoolean(b.isGiven());
return context.runtime.newBoolean( ((Block) blk).isGiven() );
}

public static IRubyObject receiveRestArg(ThreadContext context, Object[] args, int required, int argIndex, boolean acceptsKeywordArguments) {
RubyHash keywordArguments = extractKwargsHash(args, required, acceptsKeywordArguments);
return constructRestArg(context, args, keywordArguments, required, argIndex);
}

public static IRubyObject receiveRestArg(ThreadContext context, IRubyObject[] args, int required, int argIndex, boolean acceptsKeywordArguments) {
RubyHash keywordArguments = extractKwargsHash(args, required, acceptsKeywordArguments);
return constructRestArg(context, args, keywordArguments, required, argIndex);
}

public static IRubyObject constructRestArg(ThreadContext context, Object[] args, RubyHash keywordArguments, int required, int argIndex) {
int argsLength = keywordArguments != null ? args.length - 1 : args.length;
int remainingArguments = argsLength - required;

if (remainingArguments <= 0) return context.runtime.newArray(IRubyObject.NULL_ARRAY);
if (remainingArguments <= 0) return context.runtime.newEmptyArray();

IRubyObject[] restArgs = new IRubyObject[remainingArguments];
System.arraycopy(args, argIndex, restArgs, 0, remainingArguments);

return RubyArray.newArrayNoCopy(context.runtime, restArgs);
}

private static IRubyObject constructRestArg(ThreadContext context, IRubyObject[] args, RubyHash keywordArguments, int required, int argIndex) {
int argsLength = keywordArguments != null ? args.length - 1 : args.length;
if ( required == 0 && argsLength == args.length ) {
return RubyArray.newArray(context.runtime, args);
}
int remainingArguments = argsLength - required;

if (remainingArguments <= 0) return context.runtime.newEmptyArray();

IRubyObject[] restArgs = new IRubyObject[remainingArguments];
System.arraycopy(args, argIndex, restArgs, 0, remainingArguments);

return context.runtime.newArray(restArgs);
return RubyArray.newArrayNoCopy(context.runtime, restArgs);
}

@JIT
@@ -924,7 +941,8 @@ public static IRubyObject instanceSuperSplatArgs(ThreadContext context, IRubyObj
public static IRubyObject instanceSuper(ThreadContext context, IRubyObject self, String methodName, RubyModule definingModule, IRubyObject[] args, Block block) {
RubyClass superClass = definingModule.getMethodLocation().getSuperClass();
DynamicMethod method = superClass != null ? superClass.searchMethod(methodName) : UndefinedMethod.INSTANCE;
IRubyObject rVal = method.isUndefined() ? Helpers.callMethodMissing(context, self, method.getVisibility(), methodName, CallType.SUPER, args, block)
IRubyObject rVal = method.isUndefined() ?
Helpers.callMethodMissing(context, self, method.getVisibility(), methodName, CallType.SUPER, args, block)
: method.call(context, self, superClass, methodName, args, block);
return rVal;
}
@@ -938,7 +956,8 @@ public static IRubyObject classSuperSplatArgs(ThreadContext context, IRubyObject
public static IRubyObject classSuper(ThreadContext context, IRubyObject self, String methodName, RubyModule definingModule, IRubyObject[] args, Block block) {
RubyClass superClass = definingModule.getMetaClass().getMethodLocation().getSuperClass();
DynamicMethod method = superClass != null ? superClass.searchMethod(methodName) : UndefinedMethod.INSTANCE;
IRubyObject rVal = method.isUndefined() ? Helpers.callMethodMissing(context, self, method.getVisibility(), methodName, CallType.SUPER, args, block)
IRubyObject rVal = method.isUndefined() ?
Helpers.callMethodMissing(context, self, method.getVisibility(), methodName, CallType.SUPER, args, block)
: method.call(context, self, superClass, methodName, args, block);
return rVal;
}
@@ -958,7 +977,7 @@ public static IRubyObject unresolvedSuper(ThreadContext context, IRubyObject sel
RubyClass superClass = implMod.getSuperClass();
DynamicMethod method = superClass != null ? superClass.searchMethod(methodName) : UndefinedMethod.INSTANCE;

IRubyObject rVal = null;
IRubyObject rVal;
if (method.isUndefined()) {
rVal = Helpers.callMethodMissing(context, self, method.getVisibility(), methodName, CallType.SUPER, args, block);
} else {
@@ -1421,7 +1440,7 @@ public static RaiseException newRequiredKeywordArgumentError(ThreadContext conte
public static void pushExitBlock(ThreadContext context, Block blk) {
context.runtime.pushExitBlock(context.runtime.newProc(Block.Type.LAMBDA, blk));
}

@JIT
public static FunctionalCachingCallSite newFunctionalCachingCallSite(String name) {
return new FunctionalCachingCallSite(name);
@@ -1624,8 +1643,8 @@ public static IRubyObject[] prepareFixedBlockArgs(ThreadContext context, Block b
}

boolean isProcCall = context.getCurrentBlockType() == Block.Type.PROC;
org.jruby.runtime.Signature sig = block.getBody().getSignature();
if (block.type == Block.Type.LAMBDA) {
org.jruby.runtime.Signature sig = block.getBody().getSignature();
// We don't need to check for the 1 required arg case here
// since that goes down the prepareSingleBlockArgs route
if (!isProcCall && sig.arityValue() != 1) {
240 changes: 116 additions & 124 deletions core/src/main/java/org/jruby/ir/targets/Bootstrap.java
Original file line number Diff line number Diff line change
@@ -12,7 +12,6 @@
import org.jruby.internal.runtime.methods.*;
import org.jruby.ir.IRScope;
import org.jruby.ir.JIT;
import org.jruby.ir.operands.UndefinedValue;
import org.jruby.ir.runtime.IRRuntimeHelpers;
import org.jruby.parser.StaticScope;
import org.jruby.runtime.Binding;
@@ -137,22 +136,6 @@ public static CallSite ivar(Lookup lookup, String name, MethodType type) throws
return site;
}

public static CallSite searchConst(Lookup lookup, String name, MethodType type, int noPrivateConsts) {
MutableCallSite site = new MutableCallSite(type);
String[] bits = name.split(":");
String constName = bits[1];

MethodHandle handle = Binder
.from(lookup, type)
.append(site, constName.intern())
.append(noPrivateConsts == 0 ? false : true)
.invokeStaticQuiet(LOOKUP, Bootstrap.class, bits[0]);

site.setTarget(handle);

return site;
}

public static Handle string() {
return new Handle(Opcodes.H_INVOKESTATIC, p(Bootstrap.class), "string", sig(CallSite.class, Lookup.class, String.class, MethodType.class, String.class, String.class, int.class));
}
@@ -181,10 +164,6 @@ public static Handle ivar() {
return new Handle(Opcodes.H_INVOKESTATIC, p(Bootstrap.class), "ivar", sig(CallSite.class, Lookup.class, String.class, MethodType.class));
}

public static Handle searchConst() {
return new Handle(Opcodes.H_INVOKESTATIC, p(Bootstrap.class), "searchConst", sig(CallSite.class, Lookup.class, String.class, MethodType.class, int.class));
}

public static Handle global() {
return new Handle(Opcodes.H_INVOKESTATIC, p(Bootstrap.class), "globalBootstrap", sig(CallSite.class, Lookup.class, String.class, MethodType.class));
}
@@ -373,6 +352,112 @@ static MethodHandle buildGenericHandle(InvokeSite site, DynamicMethod method, Ru
return binder.invokeVirtualQuiet(LOOKUP, "call").handle();
}

static MethodHandle buildAttrHandle(InvokeSite site, DynamicMethod method, IRubyObject self, RubyClass dispatchClass) {
if (method instanceof AttrReaderMethod) {
AttrReaderMethod attrReader = (AttrReaderMethod) method;
String varName = attrReader.getVariableName();

// we getVariableAccessorForWrite here so it is eagerly created and we don't cache the DUMMY
VariableAccessor accessor = dispatchClass.getRealClass().getVariableAccessorForWrite(varName);

// Ruby to attr reader
if (Options.INVOKEDYNAMIC_LOG_BINDING.load()) {
// if (accessor instanceof FieldVariableAccessor) {
// LOG.info(site.name() + "\tbound as field attr reader " + logMethod(method) + ":" + ((AttrReaderMethod)method).getVariableName());
// } else {
// LOG.info(site.name() + "\tbound as attr reader " + logMethod(method) + ":" + ((AttrReaderMethod)method).getVariableName());
// }
}

return createAttrReaderHandle(site, self, dispatchClass.getRealClass(), accessor);
} else if (method instanceof AttrWriterMethod) {
AttrWriterMethod attrReader = (AttrWriterMethod)method;
String varName = attrReader.getVariableName();

// we getVariableAccessorForWrite here so it is eagerly created and we don't cache the DUMMY
VariableAccessor accessor = dispatchClass.getRealClass().getVariableAccessorForWrite(varName);

// Ruby to attr reader
// if (Options.INVOKEDYNAMIC_LOG_BINDING.load()) {
// if (accessor instanceof FieldVariableAccessor) {
// LOG.info(site.name() + "\tbound as field attr writer " + logMethod(method) + ":" + ((AttrWriterMethod) method).getVariableName());
// } else {
// LOG.info(site.name() + "\tbound as attr writer " + logMethod(method) + ":" + ((AttrWriterMethod) method).getVariableName());
// }
// }

return createAttrWriterHandle(site, self, dispatchClass.getRealClass(), accessor);
}

return null;
}

private static MethodHandle createAttrReaderHandle(InvokeSite site, IRubyObject self, RubyClass cls, VariableAccessor accessor) {
MethodHandle nativeTarget;

MethodHandle filter = Binder
.from(IRubyObject.class, IRubyObject.class)
.insert(1, cls.getRuntime().getNil())
.cast(IRubyObject.class, IRubyObject.class, IRubyObject.class)
.invokeStaticQuiet(lookup(), Bootstrap.class, "valueOrNil");

MethodHandle getValue;

// FIXME: Duplicated from ivar get
if (accessor instanceof FieldVariableAccessor) {
int offset = ((FieldVariableAccessor)accessor).getOffset();
getValue = Binder.from(site.type())
.drop(0, 2)
.filterReturn(filter)
.cast(methodType(Object.class, self.getClass()))
.getFieldQuiet(LOOKUP, "var" + offset);
} else {
getValue = Binder.from(site.type())
.drop(0, 2)
.filterReturn(filter)
.cast(methodType(Object.class, RubyBasicObject.class))
.append(accessor.getIndex())
.invokeVirtualQuiet(LOOKUP, "getVariable");
}

// NOTE: Must not cache the fully-bound handle in the method, since it's specific to this class

return getValue;
}

public static IRubyObject valueOrNil(IRubyObject value, IRubyObject nil) {
return value == null ? nil : value;
}

private static MethodHandle createAttrWriterHandle(InvokeSite site, IRubyObject self, RubyClass cls, VariableAccessor accessor) {
MethodHandle nativeTarget;

MethodHandle filter = Binder
.from(IRubyObject.class, Object.class)
.drop(0)
.constant(cls.getRuntime().getNil());

MethodHandle setValue;

if (accessor instanceof FieldVariableAccessor) {
int offset = ((FieldVariableAccessor)accessor).getOffset();
setValue = Binder.from(site.type())
.drop(0, 2)
.filterReturn(filter)
.cast(methodType(void.class, self.getClass(), Object.class))
.invokeVirtualQuiet(LOOKUP, "setVariable" + offset);
} else {
setValue = Binder.from(site.type())
.drop(0, 2)
.filterReturn(filter)
.cast(methodType(void.class, RubyBasicObject.class, Object.class))
.insert(1, accessor.getIndex())
.invokeVirtualQuiet(LOOKUP, "setVariable");
}

return setValue;
}

static MethodHandle buildJittedHandle(InvokeSite site, DynamicMethod method, boolean blockGiven) {
MethodHandle mh = null;
SmartBinder binder;
@@ -708,113 +793,21 @@ public static boolean testType(RubyClass original, IRubyObject self) {
///////////////////////////////////////////////////////////////////////////
// constant lookup

public static IRubyObject searchConst(ThreadContext context, StaticScope staticScope, MutableCallSite site, String constName, boolean noPrivateConsts) throws Throwable {

// Lexical lookup
Ruby runtime = context.getRuntime();
RubyModule object = runtime.getObject();
IRubyObject constant = (staticScope == null) ? object.getConstant(constName) : staticScope.getConstantInner(constName);

// Inheritance lookup
RubyModule module = null;
if (constant == null) {
// SSS FIXME: Is this null check case correct?
module = staticScope == null ? object : staticScope.getModule();
constant = noPrivateConsts ? module.getConstantFromNoConstMissing(constName, false) : module.getConstantNoConstMissing(constName);
}

// Call const_missing or cache
if (constant == null) {
return module.callMethod(context, "const_missing", context.runtime.fastNewSymbol(constName));
}

SwitchPoint switchPoint = (SwitchPoint)runtime.getConstantInvalidator(constName).getData();

// bind constant until invalidated
MethodHandle target = Binder.from(site.type())
.drop(0, 2)
.constant(constant);
MethodHandle fallback = Binder.from(site.type())
.append(site, constName)
.append(noPrivateConsts)
.invokeStatic(LOOKUP, Bootstrap.class, "searchConst");

site.setTarget(switchPoint.guardWithTest(target, fallback));

return constant;
}

public static IRubyObject inheritanceSearchConst(ThreadContext context, IRubyObject cmVal, MutableCallSite site, String constName, boolean noPrivateConsts) throws Throwable {
Ruby runtime = context.runtime;
RubyModule module;

if (cmVal instanceof RubyModule) {
module = (RubyModule) cmVal;
} else {
throw runtime.newTypeError(cmVal + " is not a type/class");
}

IRubyObject constant = noPrivateConsts ? module.getConstantFromNoConstMissing(constName, false) : module.getConstantNoConstMissing(constName);

if (constant == null) {
constant = UndefinedValue.UNDEFINED;
}

// This caching does not take into consideration sites that have many different module targets.
//
if (true) {
return constant;
}

SwitchPoint switchPoint = (SwitchPoint)runtime.getConstantInvalidator(constName).getData();

// bind constant until invalidated
MethodHandle target = Binder.from(site.type())
.drop(0, 2)
.constant(constant);
MethodHandle fallback = Binder.from(site.type())
.append(site, constName)
.append(noPrivateConsts)
.invokeStatic(LOOKUP, Bootstrap.class, "inheritanceSearchConst");

// test that module is same as before
MethodHandle test = Binder.from(site.type().changeReturnType(boolean.class))
.drop(0, 1)
.insert(1, module.id)
.invokeStaticQuiet(LOOKUP, Bootstrap.class, "testArg0ModuleMatch");
target = guardWithTest(test, target, fallback);
site.setTarget(switchPoint.guardWithTest(target, fallback));

if (Options.INVOKEDYNAMIC_LOG_CONSTANTS.load()) {
LOG.info(constName + "\tretrieved and cached from type " + cmVal.getMetaClass());// + " added to PIC" + extractSourceInfo(site));
}

return constant;
public static Handle searchConst() {
return new Handle(Opcodes.H_INVOKESTATIC, p(Bootstrap.class), "searchConst", sig(CallSite.class, Lookup.class, String.class, MethodType.class, String.class, int.class));
}

public static IRubyObject lexicalSearchConst(ThreadContext context, StaticScope scope, MutableCallSite site, String constName, boolean noPrivateConsts) throws Throwable {
Ruby runtime = context.runtime;

IRubyObject constant = scope.getConstantInner(constName);

if (constant == null) {
constant = UndefinedValue.UNDEFINED;
}

SwitchPoint switchPoint = (SwitchPoint)runtime.getConstantInvalidator(constName).getData();
public static CallSite searchConst(Lookup lookup, String searchType, MethodType type, String constName, int publicOnly) {
ConstantLookupSite site = new ConstantLookupSite(type, constName, publicOnly == 0 ? false : true);

// bind constant until invalidated
MethodHandle target = Binder.from(site.type())
.drop(0, 2)
.constant(constant);
MethodHandle fallback = Binder.from(site.type())
.append(site, constName)
.append(noPrivateConsts)
.invokeStatic(LOOKUP, Bootstrap.class, "lexicalSearchConst");
MethodHandle handle = Binder
.from(lookup, type)
.insert(0, site)
.invokeVirtualQuiet(LOOKUP, searchType);

site.setTarget(switchPoint.guardWithTest(target, fallback));
site.setTarget(handle);

return constant;
return site;
}

///////////////////////////////////////////////////////////////////////////
@@ -832,7 +825,6 @@ public static IRubyObject instVarNullToNil(IRubyObject value, IRubyObject nil, S
}

public static boolean testArg0ModuleMatch(IRubyObject arg0, int id) {
System.out.println("testing " + arg0 + " for match (id = " + ((RubyModule)arg0).id + "): " + (arg0 instanceof RubyModule && ((RubyModule)arg0).id == id));
return arg0 instanceof RubyModule && ((RubyModule)arg0).id == id;
}

155 changes: 155 additions & 0 deletions core/src/main/java/org/jruby/ir/targets/ConstantLookupSite.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
package org.jruby.ir.targets;

import com.headius.invokebinder.Binder;
import org.jruby.Ruby;
import org.jruby.RubyModule;
import org.jruby.ir.operands.UndefinedValue;
import org.jruby.parser.StaticScope;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.util.cli.Options;
import org.jruby.util.log.Logger;
import org.jruby.util.log.LoggerFactory;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;
import java.lang.invoke.MutableCallSite;
import java.lang.invoke.SwitchPoint;

import static java.lang.invoke.MethodHandles.guardWithTest;

/**
* Created by headius on 1/31/16.
*/
public class ConstantLookupSite extends MutableCallSite {
private static final Logger LOG = LoggerFactory.getLogger("ConstantLookupSite");
private final String name;
private final boolean publicOnly;

public ConstantLookupSite(MethodType type, String name, boolean publicOnly) {
super(type);

this.name = name;
this.publicOnly = publicOnly;
}

public IRubyObject searchConst(ThreadContext context, StaticScope staticScope) {
// Lexical lookup
Ruby runtime = context.getRuntime();
RubyModule object = runtime.getObject();
IRubyObject constant = (staticScope == null) ? object.getConstant(name) : staticScope.getConstantInner(name);

// Inheritance lookup
RubyModule module = null;
if (constant == null) {
// SSS FIXME: Is this null check case correct?
module = staticScope == null ? object : staticScope.getModule();
constant = publicOnly ? module.getConstantFromNoConstMissing(name, false) : module.getConstantNoConstMissing(name);
}

// Call const_missing or cache
if (constant == null) {
return module.callMethod(context, "const_missing", context.runtime.fastNewSymbol(name));
}

SwitchPoint switchPoint = (SwitchPoint) runtime.getConstantInvalidator(name).getData();

// bind constant until invalidated
MethodHandle target = Binder.from(type())
.drop(0, 2)
.constant(constant);
MethodHandle fallback = getTarget();
if (fallback == null) {
fallback = Binder.from(type())
.insert(0, this)
.invokeVirtualQuiet(Bootstrap.LOOKUP, "searchConst");
}

setTarget(switchPoint.guardWithTest(target, fallback));

if (Options.INVOKEDYNAMIC_LOG_CONSTANTS.load()) {
LOG.info(name + "\tretrieved and cached from scope " + staticScope.getIRScope());// + " added to PIC" + extractSourceInfo(site));
}

return constant;
}

public IRubyObject inheritanceSearchConst(ThreadContext context, IRubyObject cmVal) {
Ruby runtime = context.runtime;
RubyModule module;

if (cmVal instanceof RubyModule) {
module = (RubyModule) cmVal;
} else {
throw runtime.newTypeError(cmVal + " is not a type/class");
}

IRubyObject constant = publicOnly ? module.getConstantFromNoConstMissing(name, false) : module.getConstantNoConstMissing(name);

if (constant == null) {
constant = UndefinedValue.UNDEFINED;
}

SwitchPoint switchPoint = (SwitchPoint) runtime.getConstantInvalidator(name).getData();

// bind constant until invalidated
MethodHandle target = Binder.from(type())
.drop(0, 2)
.constant(constant);

MethodHandle fallback = getTarget();
if (fallback == null) {
fallback = Binder.from(type())
.insert(0, this)
.invokeVirtualQuiet(Bootstrap.LOOKUP, "inheritanceSearchConst");
}

// test that module is same as before
MethodHandle test = Binder.from(type().changeReturnType(boolean.class))
.drop(0, 1)
.insert(1, module.id)
.invokeStaticQuiet(Bootstrap.LOOKUP, Bootstrap.class, "testArg0ModuleMatch");

target = guardWithTest(test, target, fallback);

setTarget(switchPoint.guardWithTest(target, fallback));

if (Options.INVOKEDYNAMIC_LOG_CONSTANTS.load()) {
LOG.info(name + "\tretrieved and cached from type " + cmVal.getMetaClass());// + " added to PIC" + extractSourceInfo(site));
}

return constant;
}

public IRubyObject lexicalSearchConst(ThreadContext context, StaticScope scope) {
Ruby runtime = context.runtime;

IRubyObject constant = scope.getConstantInner(name);

if (constant == null) {
constant = UndefinedValue.UNDEFINED;
}

SwitchPoint switchPoint = (SwitchPoint) runtime.getConstantInvalidator(name).getData();

// bind constant until invalidated
MethodHandle target = Binder.from(type())
.drop(0, 2)
.constant(constant);

MethodHandle fallback = getTarget();
if (fallback == null) {
fallback = Binder.from(type())
.insert(0, this)
.invokeVirtualQuiet(Bootstrap.LOOKUP, "lexicalSearchConst");
}

setTarget(switchPoint.guardWithTest(target, fallback));

if (Options.INVOKEDYNAMIC_LOG_CONSTANTS.load()) {
LOG.info(name + "\tretrieved and cached from scope " + scope.getIRScope());// + " added to PIC" + extractSourceInfo(site));
}

return constant;
}
}
Original file line number Diff line number Diff line change
@@ -583,6 +583,13 @@ public org.objectweb.asm.Label newLabel() {
*/
public abstract void yieldSpecific();

/**
* Yield a number of flat arguments to a block.
*
* Stack required: context, block
*/
public abstract void yieldValues(int arity);

/**
* Prepare a block for a subsequent call.
*
10 changes: 7 additions & 3 deletions core/src/main/java/org/jruby/ir/targets/IRBytecodeAdapter6.java
Original file line number Diff line number Diff line change
@@ -818,13 +818,17 @@ public void setGlobalVariable(String name) {

@Override
public void yield(boolean unwrap) {
adapter.ldc(unwrap);
invokeIRHelper("yield", sig(IRubyObject.class, ThreadContext.class, Block.class, IRubyObject.class, boolean.class));
adapter.invokedynamic("yield", sig(JVM.OBJECT, params(ThreadContext.class, Block.class, JVM.OBJECT)), YieldSite.BOOTSTRAP, unwrap ? 1 : 0);
}

@Override
public void yieldSpecific() {
invokeIRHelper("yieldSpecific", sig(IRubyObject.class, ThreadContext.class, Block.class));
adapter.invokedynamic("yieldSpecific", sig(JVM.OBJECT, params(ThreadContext.class, Block.class)), YieldSite.BOOTSTRAP, 0);
}

@Override
public void yieldValues(int arity) {
adapter.invokedynamic("yieldValues", sig(JVM.OBJECT, params(ThreadContext.class, Block.class, JVM.OBJECT, arity)), YieldSite.BOOTSTRAP, 0);
}

@Override
16 changes: 3 additions & 13 deletions core/src/main/java/org/jruby/ir/targets/IRBytecodeAdapter7.java
Original file line number Diff line number Diff line change
@@ -240,15 +240,15 @@ public void invokeZSuper(String name, int arity, boolean hasClosure, boolean[] s
}

public void searchConst(String name, boolean noPrivateConsts) {
adapter.invokedynamic("searchConst:" + name, sig(JVM.OBJECT, params(ThreadContext.class, StaticScope.class)), Bootstrap.searchConst(), noPrivateConsts?1:0);
adapter.invokedynamic("searchConst", sig(JVM.OBJECT, params(ThreadContext.class, StaticScope.class)), Bootstrap.searchConst(), name, noPrivateConsts?1:0);
}

public void inheritanceSearchConst(String name, boolean noPrivateConsts) {
adapter.invokedynamic("inheritanceSearchConst:" + name, sig(JVM.OBJECT, params(ThreadContext.class, IRubyObject.class)), Bootstrap.searchConst(), noPrivateConsts?1:0);
adapter.invokedynamic("inheritanceSearchConst", sig(JVM.OBJECT, params(ThreadContext.class, IRubyObject.class)), Bootstrap.searchConst(), name, noPrivateConsts?1:0);
}

public void lexicalSearchConst(String name) {
adapter.invokedynamic("lexicalSearchConst:" + name, sig(JVM.OBJECT, params(ThreadContext.class, StaticScope.class)), Bootstrap.searchConst(), 0);
adapter.invokedynamic("lexicalSearchConst", sig(JVM.OBJECT, params(ThreadContext.class, StaticScope.class)), Bootstrap.searchConst(), name, 0);
}

public void pushNil() {
@@ -303,16 +303,6 @@ public void checkpoint() {
Bootstrap.checkpointHandle());
}

@Override
public void yield(boolean unwrap) {
adapter.invokedynamic("yield", sig(JVM.OBJECT, params(ThreadContext.class, Block.class, JVM.OBJECT)), YieldSite.BOOTSTRAP, unwrap ? 1 : 0);
}

@Override
public void yieldSpecific() {
adapter.invokedynamic("yieldSpecific", sig(JVM.OBJECT, params(ThreadContext.class, Block.class)), YieldSite.BOOTSTRAP, 0);
}

@Override
public void prepareBlock(Handle handle, org.jruby.runtime.Signature signature, String className) {
Handle scopeHandle = new Handle(Opcodes.H_GETSTATIC, getClassData().clsName, handle.getName() + "_IRScope", ci(IRScope.class));
42 changes: 36 additions & 6 deletions core/src/main/java/org/jruby/ir/targets/InvokeSite.java
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@
import com.headius.invokebinder.Signature;
import com.headius.invokebinder.SmartBinder;
import com.headius.invokebinder.SmartHandle;
import org.jruby.Ruby;
import org.jruby.RubyBasicObject;
import org.jruby.RubyBoolean;
import org.jruby.RubyClass;
@@ -17,6 +18,7 @@
import org.jruby.runtime.Block;
import org.jruby.runtime.CallType;
import org.jruby.runtime.Helpers;
import org.jruby.runtime.ObjectAllocator;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
import org.jruby.runtime.callsite.CacheEntry;
@@ -128,7 +130,7 @@ public IRubyObject invoke(ThreadContext context, IRubyObject caller, IRubyObject
return callMethodMissing(entry, callType, context, self, methodName, args, block);
}

MethodHandle mh = getHandle(selfClass, this, method);
MethodHandle mh = getHandle(self, selfClass, method);

updateInvocationTarget(mh, self, selfClass, entry, switchPoint);

@@ -182,19 +184,47 @@ public Binder prepareBinder() {
return binder.binder();
}

MethodHandle getHandle(RubyClass dispatchClass, InvokeSite site, DynamicMethod method) throws Throwable {
MethodHandle getHandle(IRubyObject self, RubyClass dispatchClass, DynamicMethod method) throws Throwable {
boolean blockGiven = signature.lastArgType() == Block.class;

MethodHandle mh = Bootstrap.buildNativeHandle(site, method, blockGiven);
if (mh == null) mh = Bootstrap.buildIndyHandle(site, method, method.getImplementationClass());
if (mh == null) mh = Bootstrap.buildJittedHandle(site, method, blockGiven);
if (mh == null) mh = Bootstrap.buildGenericHandle(site, method, dispatchClass);
MethodHandle mh = buildNewInstanceHandle(method, self, blockGiven);
if (mh == null) mh = Bootstrap.buildNativeHandle(this, method, blockGiven);
if (mh == null) mh = Bootstrap.buildIndyHandle(this, method, method.getImplementationClass());
if (mh == null) mh = Bootstrap.buildJittedHandle(this, method, blockGiven);
if (mh == null) mh = Bootstrap.buildAttrHandle(this, method, self, dispatchClass);
if (mh == null) mh = Bootstrap.buildGenericHandle(this, method, dispatchClass);

assert mh != null : "we should have a method handle of some sort by now";

return mh;
}

MethodHandle buildNewInstanceHandle(DynamicMethod method, IRubyObject self, boolean blockGiven) {
MethodHandle mh = null;

if (method == self.getRuntime().getBaseNewMethod()) {
RubyClass recvClass = (RubyClass) self;

// Bind a second site as a dynamic invoker to guard against changes in new object's type
CallSite initSite = SelfInvokeSite.bootstrap(lookup(), "callFunctional:initialize", type());
MethodHandle initHandle = initSite.dynamicInvoker();

MethodHandle allocFilter = Binder.from(IRubyObject.class, IRubyObject.class)
.cast(IRubyObject.class, RubyClass.class)
.insert(0, new Class[] {ObjectAllocator.class, Ruby.class}, recvClass.getAllocator(), self.getRuntime())
.invokeVirtualQuiet(lookup(), "allocate");

mh = SmartBinder.from(lookup(), signature)
.filter("self", allocFilter)
.fold("dummy", initHandle)
.permute("self")
.identity()
.handle();
}

return mh;
}

/**
* Update the given call site using the new target, wrapping with appropriate
* guard and argument-juggling logic. Return a handle suitable for invoking
48 changes: 28 additions & 20 deletions core/src/main/java/org/jruby/ir/targets/JVMVisitor.java
Original file line number Diff line number Diff line change
@@ -36,6 +36,7 @@
import org.jruby.util.RegexpOptions;
import org.jruby.util.StringSupport;
import org.jruby.util.cli.Options;
import org.jruby.util.collections.IntHashMap;
import org.jruby.util.log.Logger;
import org.jruby.util.log.LoggerFactory;
import org.objectweb.asm.Handle;
@@ -225,9 +226,10 @@ public static final Signature signatureFor(IRScope method, boolean aritySplit) {
!argScope.getSignature().hasKwargs()) {
// we have only required arguments...emit a signature appropriate to that arity
String[] args = new String[argScope.getSignature().required()];
Class[] types = Helpers.arrayOf(Class.class, args.length, IRubyObject.class);
Class[] types = new Class[args.length]; // Class...
for (int i = 0; i < args.length; i++) {
args[i] = "arg" + i;
types[i] = IRubyObject.class;
}
return METHOD_SIGNATURE_BASE.insertArgs(3, args, types);
}
@@ -252,14 +254,14 @@ public void emitScriptBody(IRScriptBody script) {
}

public void emitMethod(IRMethod method, JVMVisitorMethodContext context) {
String name = JavaNameMangler.encodeScopeForBacktrace(method) + "$" + methodIndex++;
String name = JavaNameMangler.encodeScopeForBacktrace(method) + '$' + methodIndex++;

emitWithSignatures(method, context, name);
}

public void emitMethodJIT(IRMethod method, JVMVisitorMethodContext context) {
String clsName = jvm.scriptToClass(method.getFileName());
String name = JavaNameMangler.encodeScopeForBacktrace(method) + "$" + methodIndex++;
String name = JavaNameMangler.encodeScopeForBacktrace(method) + '$' + methodIndex++;
jvm.pushscript(clsName, method.getFileName());

emitWithSignatures(method, context, name);
@@ -270,7 +272,7 @@ public void emitMethodJIT(IRMethod method, JVMVisitorMethodContext context) {

public void emitBlockJIT(IRClosure closure, JVMVisitorMethodContext context) {
String clsName = jvm.scriptToClass(closure.getFileName());
String name = JavaNameMangler.encodeScopeForBacktrace(closure) + "$" + methodIndex++;
String name = JavaNameMangler.encodeScopeForBacktrace(closure) + '$' + methodIndex++;
jvm.pushscript(clsName, closure.getFileName());

emitScope(closure, name, CLOSURE_SIGNATURE, false, true);
@@ -1009,14 +1011,13 @@ public void DefineClassMethodInstr(DefineClassMethodInstr defineclassmethodinstr
JVMVisitorMethodContext context = new JVMVisitorMethodContext();
emitMethod(method, context);

Map<Integer, MethodType> signatures = context.getNativeSignatures();

MethodType signature = signatures.get(-1);
MethodType variable = context.getNativeSignature(-1); // always a variable arity handle
assert(variable != null);

String defSignature = pushHandlesForDef(
context.getJittedName(),
signatures,
signature,
context.getNativeSignaturesExceptVariable(),
variable,
sig(void.class, ThreadContext.class, java.lang.invoke.MethodHandle.class, IRScope.class, IRubyObject.class),
sig(void.class, ThreadContext.class, java.lang.invoke.MethodHandle.class, java.lang.invoke.MethodHandle.class, int.class, IRScope.class, IRubyObject.class));

@@ -1039,13 +1040,13 @@ public void DefineInstanceMethodInstr(DefineInstanceMethodInstr defineinstanceme
m.loadContext();

emitMethod(method, context);
Map<Integer, MethodType> signatures = context.getNativeSignatures();

MethodType variable = signatures.get(-1); // always a variable arity handle
MethodType variable = context.getNativeSignature(-1); // always a variable arity handle
assert(variable != null);

String defSignature = pushHandlesForDef(
context.getJittedName(),
signatures,
context.getNativeSignaturesExceptVariable(),
variable,
sig(void.class, ThreadContext.class, java.lang.invoke.MethodHandle.class, IRScope.class, DynamicScope.class, IRubyObject.class),
sig(void.class, ThreadContext.class, java.lang.invoke.MethodHandle.class, java.lang.invoke.MethodHandle.class, int.class, IRScope.class, DynamicScope.class, IRubyObject.class));
@@ -1058,22 +1059,20 @@ public void DefineInstanceMethodInstr(DefineInstanceMethodInstr defineinstanceme
a.invokestatic(p(IRRuntimeHelpers.class), "defCompiledInstanceMethod", defSignature);
}

public String pushHandlesForDef(String name, Map<Integer, MethodType> signatures, MethodType variable, String variableOnly, String variableAndSpecific) {
private String pushHandlesForDef(String name, IntHashMap<MethodType> signaturesExceptVariable, MethodType variable, String variableOnly, String variableAndSpecific) {
String defSignature;

jvmMethod().pushHandle(new Handle(Opcodes.H_INVOKESTATIC, jvm.clsData().clsName, name, sig(variable.returnType(), variable.parameterArray())));

if (signatures.size() == 1) {
if (signaturesExceptVariable.size() == 0) {
defSignature = variableOnly;
} else {
defSignature = variableAndSpecific;

// FIXME: only supports one arity
for (Map.Entry<Integer, MethodType> entry : signatures.entrySet()) {
if (entry.getKey() == -1) continue; // variable arity signature pushed above
for (IntHashMap.Entry<MethodType> entry : signaturesExceptVariable.entrySet()) {
jvmMethod().pushHandle(new Handle(Opcodes.H_INVOKESTATIC, jvm.clsData().clsName, name, sig(entry.getValue().returnType(), entry.getValue().parameterArray())));
jvmAdapter().pushInt(entry.getKey());
break;
break; // FIXME: only supports one arity
}
}
return defSignature;
@@ -2018,8 +2017,17 @@ public void YieldInstr(YieldInstr yieldinstr) {
if (yieldinstr.getYieldArg() == UndefinedValue.UNDEFINED) {
jvmMethod().yieldSpecific();
} else {
visit(yieldinstr.getYieldArg());
jvmMethod().yield(yieldinstr.isUnwrapArray());
Operand yieldOp = yieldinstr.getYieldArg();
if (yieldinstr.isUnwrapArray() && yieldOp instanceof Array && ((Array) yieldOp).size() > 1) {
Array yieldValues = (Array) yieldOp;
for (Operand yieldValue : yieldValues) {
visit(yieldValue);
}
jvmMethod().yieldValues(yieldValues.size());
} else {
visit(yieldinstr.getYieldArg());
jvmMethod().yield(yieldinstr.isUnwrapArray());
}
}

jvmStoreLocal(yieldinstr.getResult());
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
package org.jruby.ir.targets;

import java.lang.invoke.MethodType;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import org.jruby.util.collections.IntHashMap;

/**
* Context for JITing methods. Temporary data.
@@ -13,7 +12,8 @@ public class JVMVisitorMethodContext {
private String jittedName;

// Signatures to the jitted versions of this method
private Map<Integer, MethodType> signatures;
private IntHashMap<MethodType> signatures;
private MethodType varSignature; // for arity == -1

public void setJittedName(String jittedName) {
this.jittedName = jittedName;
@@ -23,13 +23,27 @@ public String getJittedName() {
return jittedName;
}


public void addNativeSignature(int arity, MethodType signature) {
if (signatures == null) signatures = new HashMap<>(1);
signatures.put(arity, signature);
if ( arity == -1 ) varSignature = signature;
else {
if ( signatures == null ) signatures = new IntHashMap<>(2);
signatures.put(arity, signature);
}
}

public MethodType getNativeSignature(int arity) {
if ( arity == -1 ) return varSignature;
return signatures == null ? null : signatures.get(arity);
}

public Map<Integer, MethodType> getNativeSignatures() {
return Collections.unmodifiableMap(signatures);
public int getNativeSignaturesCount() {
int count = varSignature == null ? 0 : 1;
if ( signatures != null ) count += signatures.size();
return count;
}

public IntHashMap<MethodType> getNativeSignaturesExceptVariable() {
return signatures == null ? IntHashMap.<MethodType>nullMap() : signatures;
}

}
104 changes: 69 additions & 35 deletions core/src/main/java/org/jruby/ir/targets/YieldSite.java
Original file line number Diff line number Diff line change
@@ -3,7 +3,9 @@
import com.headius.invokebinder.Binder;
import org.jruby.ir.runtime.IRRuntimeHelpers;
import org.jruby.runtime.Block;
import org.jruby.runtime.BlockBody;
import org.jruby.runtime.CompiledIRBlockBody;
import org.jruby.runtime.Helpers;
import org.jruby.runtime.ThreadContext;
import org.jruby.runtime.builtin.IRubyObject;
import org.objectweb.asm.Handle;
@@ -35,52 +37,84 @@ public YieldSite(MethodType type, boolean unwrap) {
public static CallSite bootstrap(MethodHandles.Lookup lookup, String name, MethodType type, int unwrap) throws Throwable {
YieldSite site = new YieldSite(type, unwrap == 1 ? true : false);

MethodHandle handle = Binder.from(type)
.prepend(YieldSite.class, site)
.invokeVirtual(lookup, name);
MethodHandle handle;
switch (name) {
case "yield":
case "yieldSpecific":
handle = Binder.from(type)
.prepend(YieldSite.class, site)
.invokeVirtual(lookup, name);
break;
case "yieldValues":
handle = Binder.from(type)
.collect(2, IRubyObject[].class)
.prepend(YieldSite.class, site)
.invokeVirtual(lookup, name);
break;
default:
throw new RuntimeException("invalid yield type: " + name);
}

site.setTarget(handle);

return site;
}

public IRubyObject yield(ThreadContext context, Block block, IRubyObject arg) throws Throwable {
if (block.getBody() instanceof CompiledIRBlockBody) {
CompiledIRBlockBody compiledBody = (CompiledIRBlockBody) block.getBody();

MethodHandle target = unwrap ? compiledBody.getNormalYieldUnwrapHandle() : compiledBody.getNormalYieldHandle();
MethodHandle fallback = getTarget();
MethodHandle test = compiledBody.getTestBlockBody();

MethodHandle guard = MethodHandles.guardWithTest(test, target, fallback);

setTarget(guard);

return (IRubyObject)target.invokeExact(context, block, arg);
}

context.setCurrentBlockType(Block.Type.NORMAL);

// BlockBody body = block.getBody();
// MethodHandle target;
//
// if (block.getBody() instanceof CompiledIRBlockBody) {
// CompiledIRBlockBody compiledBody = (CompiledIRBlockBody) block.getBody();
//
// target = unwrap ? compiledBody.getNormalYieldUnwrapHandle() : compiledBody.getNormalYieldHandle();
// } else {
// target = Binder.from(type())
// .append(unwrap)
// .invokeStaticQuiet(MethodHandles.lookup(), IRRuntimeHelpers.class, "yield");
// }
//
// MethodHandle fallback = getTarget();
// MethodHandle test = body.getTestBlockBody();
//
// MethodHandle guard = MethodHandles.guardWithTest(test, target, fallback);
//
// setTarget(guard);
//
// return (IRubyObject)target.invokeExact(context, block, arg);

// Fully MH-based dispatch for these still seems slower than megamorphic path
return IRRuntimeHelpers.yield(context, block, arg, unwrap);
}

public IRubyObject yieldSpecific(ThreadContext context, Block block) throws Throwable {
if (block.getBody() instanceof CompiledIRBlockBody) {
CompiledIRBlockBody compiledBody = (CompiledIRBlockBody) block.getBody();

MethodHandle target = compiledBody.getNormalYieldSpecificHandle();
MethodHandle fallback = getTarget();
MethodHandle test = compiledBody.getTestBlockBody();

MethodHandle guard = MethodHandles.guardWithTest(test, target, fallback);

setTarget(guard);

return (IRubyObject)target.invokeExact(context, block);
}

context.setCurrentBlockType(Block.Type.NORMAL);

// BlockBody body = block.getBody();
// MethodHandle target;
//
// if (block.getBody() instanceof CompiledIRBlockBody) {
// CompiledIRBlockBody compiledBody = (CompiledIRBlockBody) block.getBody();
//
// target = compiledBody.getNormalYieldSpecificHandle();
// } else {
// target = Binder.from(type())
// .permute(0, 1)
// .invokeVirtualQuiet(MethodHandles.lookup(), "yieldSpecific");
// }
//
// MethodHandle fallback = getTarget();
// MethodHandle test = body.getTestBlockBody();
//
// MethodHandle guard = MethodHandles.guardWithTest(test, target, fallback);
//
// setTarget(guard);
//
// return (IRubyObject)target.invokeExact(context, block);

// Fully MH-based dispatch for these still seems slower than megamorphic path
return IRRuntimeHelpers.yieldSpecific(context, block);
}

public IRubyObject yieldValues(ThreadContext context, Block block, IRubyObject[] args) {
return block.yieldValues(context, args);
}
}
7 changes: 5 additions & 2 deletions core/src/main/java/org/jruby/runtime/Block.java
Original file line number Diff line number Diff line change
@@ -175,11 +175,14 @@ public IRubyObject yieldArray(ThreadContext context, IRubyObject value, IRubyObj
// SSS FIXME: Later on, we can move this code into IR insructions or
// introduce a specialized entry-point when we know that this block has
// explicit call protocol IR instructions.
IRubyObject[] args;
args = IRRuntimeHelpers.singleBlockArgToArray(value);
IRubyObject[] args = IRRuntimeHelpers.singleBlockArgToArray(value);
return body.yield(context, this, args, self);
}

public IRubyObject yieldValues(ThreadContext context, IRubyObject[] args) {
return body.yield(context, this, args, null);
}

public Block cloneBlock() {
Block newBlock = new Block(body, binding);

17 changes: 17 additions & 0 deletions core/src/main/java/org/jruby/runtime/BlockBody.java
Original file line number Diff line number Diff line change
@@ -33,13 +33,17 @@

package org.jruby.runtime;

import com.headius.invokebinder.Binder;
import org.jruby.EvalType;
import org.jruby.RubyArray;
import org.jruby.RubyProc;
import org.jruby.ir.runtime.IRRuntimeHelpers;
import org.jruby.parser.StaticScope;
import org.jruby.runtime.builtin.IRubyObject;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;

/**
* The executable body portion of a closure.
*/
@@ -48,6 +52,7 @@ public abstract class BlockBody {
public static final String[] EMPTY_PARAMETER_LIST = org.jruby.util.StringSupport.EMPTY_STRING_ARRAY;

protected final Signature signature;
protected volatile MethodHandle testBlockBody;

public BlockBody(Signature signature) {
this.signature = signature;
@@ -64,6 +69,18 @@ public boolean canCallDirect() {
return false;
}

public MethodHandle getTestBlockBody() {
if (testBlockBody != null) return testBlockBody;

return testBlockBody = Binder.from(boolean.class, ThreadContext.class, Block.class).drop(0).append(this).invoke(TEST_BLOCK_BODY);
}

private static final MethodHandle TEST_BLOCK_BODY = Binder.from(boolean.class, Block.class, BlockBody.class).invokeStaticQuiet(MethodHandles.lookup(), BlockBody.class, "testBlockBody");

public static boolean testBlockBody(Block block, BlockBody body) {
return block.getBody() == body;
}

protected IRubyObject callDirect(ThreadContext context, Block block, IRubyObject[] args, Block blockArg) {
throw new RuntimeException("callDirect not implemented in base class. We should never get here.");
}
52 changes: 34 additions & 18 deletions core/src/main/java/org/jruby/runtime/CompiledIRBlockBody.java
Original file line number Diff line number Diff line change
@@ -3,6 +3,7 @@
import com.headius.invokebinder.Binder;
import org.jruby.ir.IRScope;
import org.jruby.ir.runtime.IRRuntimeHelpers;
import org.jruby.parser.StaticScope;
import org.jruby.runtime.builtin.IRubyObject;

import java.lang.invoke.MethodHandle;
@@ -13,7 +14,8 @@ public class CompiledIRBlockBody extends IRBlockBody {
protected volatile MethodHandle normalYieldSpecificHandle;
protected volatile MethodHandle normalYieldHandle;
protected volatile MethodHandle normalYieldUnwrapHandle;
protected volatile MethodHandle testBlockBody;
protected volatile MethodHandle yieldTwoValuesHandle;
protected volatile MethodHandle yieldThreeValuesHandle;

public CompiledIRBlockBody(MethodHandle handle, IRScope closure, long encodedSignature) {
super(closure, Signature.decode(encodedSignature));
@@ -23,12 +25,6 @@ public CompiledIRBlockBody(MethodHandle handle, IRScope closure, long encodedSig
closure.getStaticScope().determineModule();
}

private static final MethodHandle TEST_BLOCK_BODY = Binder.from(boolean.class, Block.class, IRBlockBody.class).invokeStaticQuiet(MethodHandles.lookup(), CompiledIRBlockBody.class, "testBlockBody");

public static boolean testBlockBody(Block block, IRBlockBody body) {
return block.getBody() == body;
}

private static final MethodHandle FOLD_METHOD1 = Binder.from(String.class, ThreadContext.class, Block.class).invokeStaticQuiet(MethodHandles.lookup(), CompiledIRBlockBody.class, "foldMethod");
private static String foldMethod(ThreadContext context, Block block) {
return block.getBinding().getMethod();
@@ -78,10 +74,8 @@ public MethodHandle getNormalYieldSpecificHandle() {
.foldVoid(SET_NORMAL)
.fold(FOLD_METHOD1)
.fold(FOLD_TYPE1)
.append(getStaticScope())
.append(IRubyObject.class, null)
.append(IRubyObject[].class, null)
.append(Block.class, Block.NULL_BLOCK)
.append(new Class[] {StaticScope.class, IRubyObject.class, IRubyObject[].class, Block.class},
getStaticScope(), null, null, Block.NULL_BLOCK)
.permute(2, 3, 4, 5, 6, 7, 1, 0)
.invoke(handle);
}
@@ -94,8 +88,7 @@ public MethodHandle getNormalYieldHandle() {
.fold(FOLD_METHOD2)
.fold(FOLD_TYPE2)
.filter(4, WRAP_VALUE)
.insert(4, getStaticScope())
.insert(5, IRubyObject.class, null)
.insert(4, new Class[]{StaticScope.class, IRubyObject.class}, getStaticScope(), null)
.append(Block.class, Block.NULL_BLOCK)
.permute(2, 3, 4, 5, 6, 7, 1, 0)
.invoke(handle);
@@ -109,17 +102,40 @@ public MethodHandle getNormalYieldUnwrapHandle() {
.fold(FOLD_METHOD2)
.fold(FOLD_TYPE2)
.filter(4, VALUE_TO_ARRAY)
.insert(4, getStaticScope())
.insert(5, IRubyObject.class, null)
.insert(4, new Class[] {StaticScope.class, IRubyObject.class}, getStaticScope(), null)
.append(Block.class, Block.NULL_BLOCK)
.permute(2, 3, 4, 5, 6, 7, 1, 0)
.invoke(handle);
}

public MethodHandle getTestBlockBody() {
if (testBlockBody != null) return testBlockBody;
public MethodHandle getYieldTwoValuesHandle() {
if (yieldTwoValuesHandle != null) return yieldTwoValuesHandle;

return testBlockBody = Binder.from(boolean.class, ThreadContext.class, Block.class).drop(0).append(this).invoke(TEST_BLOCK_BODY);
return yieldTwoValuesHandle = Binder.from(IRubyObject.class, ThreadContext.class, Block.class, IRubyObject.class, IRubyObject.class)
.foldVoid(SET_NORMAL)
.fold(FOLD_METHOD1)
.fold(FOLD_TYPE1)
.collect(5, IRubyObject[].class)
.insert(5, new Class[] {StaticScope.class, IRubyObject.class},
getStaticScope(), null)
.append(new Class[] {Block.class}, Block.NULL_BLOCK)
.permute(2, 3, 4, 5, 6, 7, 1, 0)
.invoke(handle);
}

public MethodHandle getYieldThreeValuesHandle() {
if (yieldThreeValuesHandle != null) return yieldThreeValuesHandle;

return yieldThreeValuesHandle = Binder.from(IRubyObject.class, ThreadContext.class, Block.class, IRubyObject.class, IRubyObject.class, IRubyObject.class)
.foldVoid(SET_NORMAL)
.fold(FOLD_METHOD1)
.fold(FOLD_TYPE1)
.collect(5, IRubyObject[].class)
.insert(5, new Class[] {StaticScope.class, IRubyObject.class},
getStaticScope(), null)
.append(new Class[] {Block.class}, Block.NULL_BLOCK)
.permute(2, 3, 4, 5, 6, 7, 1, 0)
.invoke(handle);
}

@Override
8 changes: 5 additions & 3 deletions core/src/main/java/org/jruby/util/cli/Options.java
Original file line number Diff line number Diff line change
@@ -160,7 +160,7 @@ public class Options {
public static final Option<Boolean> NATIVE_EXEC = bool(MISCELLANEOUS, "native.exec", true, "Do a true process-obliterating native exec for Kernel#exec.");
public static final Option<Boolean> ENUMERATOR_LIGHTWEIGHT = bool(MISCELLANEOUS, "enumerator.lightweight", true, "Use lightweight Enumerator#next logic when possible.");
public static final Option<Boolean> CONSISTENT_HASHING = bool(MISCELLANEOUS, "consistent.hashing", false, "Generate consistent object hashes across JVMs");
public static final Option<Boolean> REIFY_VARIABLES = bool(MISCELLANEOUS, "reify.variables", false, "Attempt to expand instance vars into Java fields");
public static final Option<Boolean> REIFY_VARIABLES = bool(MISCELLANEOUS, "reify.variables", true, "Attempt to expand instance vars into Java fields");
public static final Option<Boolean> PREFER_IPV4 = bool(MISCELLANEOUS, "net.preferIPv4", true, "Prefer IPv4 network stack");
public static final Option<Boolean> FCNTL_LOCKING = bool(MISCELLANEOUS, "file.flock.fcntl", true, "Use fcntl rather than flock for File#flock");
public static final Option<Boolean> VOLATILE_VARIABLES = bool(MISCELLANEOUS, "volatile.variables", true, "Always ensure volatile semantics for instance variables.");
@@ -239,8 +239,8 @@ public class Options {
public static final Option<Integer> TRUFFLE_METHOD_TO_PROC_CACHE = integer(TRUFFLE, "truffle.to_proc.cache", TRUFFLE_DEFAULT_CACHE.load(), "Method#to_proc cache size");
public static final Option<Integer> TRUFFLE_IS_A_CACHE = integer(TRUFFLE, "truffle.is_a.cache", TRUFFLE_DEFAULT_CACHE.load(), "Kernel#is_a? and #kind_of? cache size");
public static final Option<Integer> TRUFFLE_BIND_CACHE = integer(TRUFFLE, "truffle.bind.cache", TRUFFLE_DEFAULT_CACHE.load(), "Test for being able to bind a method to a module cache size");
public static final Option<Integer> TRUFFLE_CONSTANT_LOOKUP_CACHE = integer(TRUFFLE, "truffle.constant_lookup.cache", TRUFFLE_DEFAULT_CACHE.load(), "Constant lookup cache size");
public static final Option<Integer> TRUFFLE_FIELD_LOOKUP_CACHE = integer(TRUFFLE, "truffle.field_lookup.cache", TRUFFLE_DEFAULT_CACHE.load(), "Object field lookup cache size");
public static final Option<Integer> TRUFFLE_CONSTANT_CACHE = integer(TRUFFLE, "truffle.constant.cache", TRUFFLE_DEFAULT_CACHE.load(), "Constant cache size");
public static final Option<Integer> TRUFFLE_INSTANCE_VARIABLE_CACHE = integer(TRUFFLE, "truffle.instance_variable.cache", TRUFFLE_DEFAULT_CACHE.load(), "Instance variable cache size");
public static final Option<Integer> TRUFFLE_BINDING_LOCAL_VARIABLE_CACHE = integer(TRUFFLE, "truffle.binding_local_variable.cache", TRUFFLE_DEFAULT_CACHE.load(), "Binding#local_variable_get/set cache size");
public static final Option<Integer> TRUFFLE_SYMBOL_TO_PROC_CACHE = integer(TRUFFLE, "truffle.symbol_to_proc.cache", TRUFFLE_DEFAULT_CACHE.load(), "Symbol#to_proc cache size");
public static final Option<Integer> TRUFFLE_ALLOCATE_CLASS_CACHE = integer(TRUFFLE, "truffle.allocate_class.cache", TRUFFLE_DEFAULT_CACHE.load(), "Allocation size class cache size");
@@ -265,6 +265,8 @@ public class Options {
public static final Option<Boolean> TRUFFLE_EXCEPTIONS_PRINT_JAVA = bool(TRUFFLE, "truffle.exceptions.print_java", false, "Print Java exceptions at the point of translating them to Ruby exceptions.");
public static final Option<Boolean> TRUFFLE_EXCEPTIONS_PRINT_UNCAUGHT_JAVA = bool(TRUFFLE, "truffle.exceptions.print_uncaught_java", false, "Print uncaught Java exceptions at the point of translating them to Ruby exceptions.");
public static final Option<Boolean> TRUFFLE_BACKTRACES_HIDE_CORE_FILES = bool(TRUFFLE, "truffle.backtraces.hide_core_files", true, "Hide core source files in backtraces, like MRI does.");
public static final Option<Integer> TRUFFLE_BACKTRACES_LIMIT = integer(TRUFFLE, "truffle.backtraces.limit", 9999, "Limit the size of Ruby backtraces.");
public static final Option<Boolean> TRUFFLE_BACKTRACES_OMIT_UNUSED = bool(TRUFFLE, "truffle.backtraces.omit_unused", true, "Omit backtraces that should be unused as they have pure rescue expressions.");
public static final Option<Boolean> TRUFFLE_INCLUDE_CORE_FILE_CALLERS_IN_SET_TRACE_FUNC = bool(TRUFFLE, "truffle.set_trace_func.include_core_file_callers", false, "Include internal core library calls in set_trace_func output.");

public static final Option<Boolean> TRUFFLE_METRICS_TIME = bool(TRUFFLE, "truffle.metrics.time", false, "Print the time at various stages of VM operation.");
5 changes: 5 additions & 0 deletions lib/ruby/truffle/jruby+truffle/bin/jruby+truffle
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#!/usr/bin/env ruby

require_relative '../lib/runner.rb'

JRubyTruffleRunner.new
File renamed without changes.
4 changes: 1 addition & 3 deletions lib/ruby/truffle/truffle/truffle/execjs.rb
Original file line number Diff line number Diff line change
@@ -65,9 +65,7 @@ def name
end

def available?
defined?(Truffle::Interop) && Truffle::Interop.eval(JS_MIME_TYPE, 'true')
rescue RubyTruffleError
false
Truffle::Interop.mime_type_supported?('application/javascript')
end

end
1 change: 1 addition & 0 deletions spec/mspec/.travis.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
sudo: false
language: ruby
script:
- bundle exec rspec
21 changes: 15 additions & 6 deletions spec/mspec/lib/mspec/matchers/raise_error.rb
Original file line number Diff line number Diff line change
@@ -9,17 +9,26 @@ def matches?(proc)
@result = proc.call
return false
rescue Exception => @actual
return false unless @exception === @actual
if matching_exception?(@actual)
return true
else
raise @actual
end
end

def matching_exception?(exc)
return false unless @exception === exc
if @message then
case @message
when String then
return false if @message != @actual.message
when Regexp then
return false if @message !~ @actual.message
when String
return false if @message != exc.message
when Regexp
return false if @message !~ exc.message
end
end

@block[@actual] if @block
# The block has its own expectations and will throw an exception if it fails
@block[exc] if @block

return true
end
8 changes: 4 additions & 4 deletions spec/mspec/lib/mspec/runner/evaluate.rb
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@ def self.desc
end

def initialize(ruby, desc)
@ruby = ruby
@ruby = ruby.rstrip
@desc = desc || self.class.desc
end

@@ -18,8 +18,8 @@ def initialize(ruby, desc)
# the source does contain newline characters, sets the indent level to four
# characters.
def format(ruby, newline=true)
if /\n/ =~ ruby
lines = ruby.rstrip.each_line.to_a
if ruby.include?("\n")
lines = ruby.each_line.to_a
if /( *)/ =~ lines.first
if $1.size > 4
dedent = $1.size - 4
@@ -31,7 +31,7 @@ def format(ruby, newline=true)
end
"\n#{ruby}"
else
"'#{ruby}'"
"'#{ruby.lstrip}'"
end
end

2 changes: 1 addition & 1 deletion spec/mspec/lib/mspec/utils/name_map.rb
Original file line number Diff line number Diff line change
@@ -110,7 +110,7 @@ def map(hash, constants, mod=nil)
ms.map! { |x| x.to_s }
hash["#{name}#"] = ms.sort unless ms.empty?

map hash, m.constants, name
map hash, m.constants(false), name
end

hash
2 changes: 1 addition & 1 deletion spec/mspec/lib/mspec/utils/options.rb
Original file line number Diff line number Diff line change
@@ -150,7 +150,7 @@ def parse(argv=ARGV)
opt, arg, rest = split rest, 1
opt = "-" + opt
option = process argv, opt, opt, arg
break if option.arg?
break if !option or option.arg?
end
end

41 changes: 32 additions & 9 deletions spec/mspec/spec/matchers/raise_error_spec.rb
Original file line number Diff line number Diff line change
@@ -8,7 +8,8 @@ class UnexpectedException < Exception; end
describe RaiseErrorMatcher do
it "matches when the proc raises the expected exception" do
proc = Proc.new { raise ExpectedException }
RaiseErrorMatcher.new(ExpectedException, nil).matches?(proc).should == true
matcher = RaiseErrorMatcher.new(ExpectedException, nil)
matcher.matches?(proc).should == true
end

it "executes it's optional block if matched" do
@@ -25,28 +26,50 @@ class UnexpectedException < Exception; end

it "matches when the proc raises the expected exception with the expected message" do
proc = Proc.new { raise ExpectedException, "message" }
RaiseErrorMatcher.new(ExpectedException, "message").matches?(proc).should == true
matcher = RaiseErrorMatcher.new(ExpectedException, "message")
matcher.matches?(proc).should == true
end

it "matches when the proc raises the expected exception with a matching message" do
proc = Proc.new { raise ExpectedException, "some message" }
matcher = RaiseErrorMatcher.new(ExpectedException, /some/)
matcher.matches?(proc).should == true
end

it "does not match when the proc does not raise the expected exception" do
proc = Proc.new { raise UnexpectedException }
RaiseErrorMatcher.new(ExpectedException, nil).matches?(proc).should == false
exc = UnexpectedException.new
matcher = RaiseErrorMatcher.new(ExpectedException, nil)

matcher.matching_exception?(exc).should == false
lambda {
matcher.matches?(Proc.new { raise exc })
}.should raise_error(UnexpectedException)
end

it "does not match when the proc raises the expected exception with an unexpected message" do
proc = Proc.new { raise ExpectedException, "unexpected" }
RaiseErrorMatcher.new(ExpectedException, "expected").matches?(proc).should == false
exc = ExpectedException.new("unexpected")
matcher = RaiseErrorMatcher.new(ExpectedException, "expected")

matcher.matching_exception?(exc).should == false
lambda {
matcher.matches?(Proc.new { raise exc })
}.should raise_error(ExpectedException)
end

it "does not match when the proc does not raise an exception" do
proc = Proc.new {}
RaiseErrorMatcher.new(ExpectedException, "expected").matches?(proc).should == false
matcher = RaiseErrorMatcher.new(ExpectedException, "expected")
matcher.matches?(proc).should == false
end

it "provides a useful failure message" do
proc = Proc.new { raise UnexpectedException, "unexpected" }
exc = UnexpectedException.new("unexpected")
matcher = RaiseErrorMatcher.new(ExpectedException, "expected")
matcher.matches?(proc)

matcher.matching_exception?(exc).should == false
lambda {
matcher.matches?(Proc.new { raise exc })
}.should raise_error(UnexpectedException)
matcher.failure_message.should ==
["Expected ExpectedException (expected)", "but got UnexpectedException (unexpected)"]
end
74 changes: 2 additions & 72 deletions spec/mspec/spec/matchers/raise_exception_spec.rb
Original file line number Diff line number Diff line change
@@ -6,77 +6,7 @@ class ExpectedException < Exception; end
class UnexpectedException < Exception; end

describe RaiseExceptionMatcher do
it "matches when the proc raises the expected exception" do
proc = Proc.new { raise ExpectedException }
RaiseErrorMatcher.new(ExpectedException, nil).matches?(proc).should == true
end

it "executes it's optional block if matched" do
run = false
proc = Proc.new { raise ExpectedException }
matcher = RaiseErrorMatcher.new(ExpectedException, nil) { |error|
run = true
error.class.should == ExpectedException
}

matcher.matches?(proc).should == true
run.should == true
end

it "matches when the proc raises the expected exception with the expected message" do
proc = Proc.new { raise ExpectedException, "message" }
RaiseErrorMatcher.new(ExpectedException, "message").matches?(proc).should == true
end

it "does not match when the proc does not raise the expected exception" do
proc = Proc.new { raise UnexpectedException }
RaiseErrorMatcher.new(ExpectedException, nil).matches?(proc).should == false
end

it "does not match when the proc raises the expected exception with an unexpected message" do
proc = Proc.new { raise ExpectedException, "unexpected" }
RaiseErrorMatcher.new(ExpectedException, "expected").matches?(proc).should == false
end

it "does not match when the proc does not raise an exception" do
proc = Proc.new {}
RaiseErrorMatcher.new(ExpectedException, "expected").matches?(proc).should == false
end

it "does not match when the raised exception is not an instance of the expected exception" do
proc = Proc.new { raise Exception }
RaiseErrorMatcher.new(ExpectedException, nil).matches?(proc).should == false
end

it "provides a useful failure message" do
proc = Proc.new { raise UnexpectedException, "unexpected" }
matcher = RaiseErrorMatcher.new(ExpectedException, "expected")
matcher.matches?(proc)
matcher.failure_message.should ==
["Expected ExpectedException (expected)", "but got UnexpectedException (unexpected)"]
end

it "provides a useful failure message when no exception is raised" do
proc = Proc.new { 120 }
matcher = RaiseErrorMatcher.new(ExpectedException, "expected")
matcher.matches?(proc)
matcher.failure_message.should ==
["Expected ExpectedException (expected)", "but no exception was raised (120 was returned)"]
end

it "provides a useful negative failure message" do
proc = Proc.new { raise ExpectedException, "expected" }
matcher = RaiseErrorMatcher.new(ExpectedException, "expected")
matcher.matches?(proc)
matcher.negative_failure_message.should ==
["Expected to not get ExpectedException (expected)", ""]
end

it "provides a useful negative failure message for strict subclasses of the matched exception class" do
proc = Proc.new { raise UnexpectedException, "unexpected" }
matcher = RaiseErrorMatcher.new(Exception, nil)
matcher.matches?(proc)
matcher.negative_failure_message.should ==
["Expected to not get Exception", "but got UnexpectedException (unexpected)"]
it "is a legac alias of RaiseErrorMatcher" do
RaiseExceptionMatcher.should equal(RaiseErrorMatcher)
end
end
6 changes: 3 additions & 3 deletions spec/ruby/CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -37,7 +37,7 @@ Specify `core` or `library` as the `base`.

#### Finding unspecified core methods

This is very easy, just run the command below in your `rubyspec` directory.
This is very easy, just run the command below in your `spec` directory.
`ruby` must be a recent version of MRI.

$ ruby --disable-gem ../mspec/bin/mkspec
@@ -56,8 +56,8 @@ In general, the usage of guards should be minimized as possible.
The following guards are deprecated and should not be used in new code:
* `not_compliant_on`: Simply tag the spec as failing instead.
If it makes sense to test part of the example, split it (an example should have only one or a few `should`).
* `compliant_on` / `deviates_on`: RubySpec defines common behavior and not implementation details.
Use the implementation suite of tests/specs for these.
* `compliant_on` / `deviates_on`: The Ruby Spec Suite defines common behavior and not implementation details.
Use the implementation test suite for these.

### Style

41 changes: 30 additions & 11 deletions spec/ruby/README.md
Original file line number Diff line number Diff line change
@@ -1,33 +1,44 @@
# RubySpec
# The Ruby Spec Suite

[![Build Status](https://travis-ci.org/ruby/rubyspec.svg)](https://travis-ci.org/ruby/rubyspec)
[![Build Status](https://travis-ci.org/ruby/spec.svg)](https://travis-ci.org/ruby/spec)

RubySpec is an executable specification for the Ruby programming language.
The specs describe the Ruby language syntax as well as the core and standard library classes.
The Ruby Spec Suite is a test suite for the behavior of the Ruby programming language.

The RubySpec files are written using a RSpec-like syntax.
MSpec is the purpose-built framework for running RubySpec.
It is not a standardized specification like the ISO one, and does not aim to become one.
Instead, it is a practical tool to describe and test the behavior of Ruby with code.

Every example code has a textual description, which presents several advantages:

* It is easier to understand the intent of the author
* It documents how recent versions of Ruby should behave
* It helps Ruby implementations to agree on a common behavior

The specs are written with syntax similar to RSpec 2.
They are run with MSpec, the purpose-built framework for running the Ruby Spec Suite.
For more information, see the [MSpec](http://github.com/ruby/mspec) project.

The specs describe the [language syntax](language/), the [core library](core/) and the [standard library](library/).
The language specs are grouped by keyword while the core and standard library specs are grouped by class and method.

### Running the specs

First, clone this repository:

$ git clone https://github.com/ruby/rubyspec.git
$ git clone https://github.com/ruby/spec.git

Then move to it:

$ cd rubyspec
$ cd spec

Clone [MSpec](http://github.com/ruby/mspec):

$ git clone https://github.com/ruby/mspec.git ../mspec

And run the RubySpec suite:
And run the spec suite:

$ ../mspec/bin/mspec

This will execute all the RubySpec specs using the executable named `ruby` on your current PATH.
This will execute all the specs using the executable named `ruby` on your current PATH.

### Running Specs with a Specific Ruby Implementation

@@ -59,4 +70,12 @@ In similar fashion, the following commands run the respective specs:

### Contributing

See [CONTRIBUTING.md](https://github.com/ruby/rubyspec/blob/master/CONTRIBUTING.md).
See [CONTRIBUTING.md](https://github.com/ruby/spec/blob/master/CONTRIBUTING.md).

### History and RubySpec

This project was originally born from [Rubinius](https://github.com/rubinius/rubinius) tests being converted to the spec style.
These specs were later extracted to their own project, RubySpec, with a specific vision and principles.
At the end of 2014, Brian Shirai, the creator of RubySpec, decided to [end RubySpec](http://rubinius.com/2014/12/31/matz-s-ruby-developers-don-t-use-rubyspec/).
A couple months later, the different repositories were merged and [the project was revived](http://eregon.github.io/rubyspec/2015/07/29/rubyspec-is-reborn.html).
On 12 January 2016, the name was changed to "The Ruby Spec Suite" for clarity and to let the RubySpec ideology rest in peace.
65 changes: 11 additions & 54 deletions spec/ruby/core/bignum/size_spec.rb
Original file line number Diff line number Diff line change
@@ -2,60 +2,17 @@

describe "Bignum#size" do
ruby_version_is "2.1" do
it "returns the number of bytes whose number of bytes is larger than the size of allocated binum data" do
(256**7).size.should >= 8
(256**8).size.should >= 9
(256**9).size.should >= 10
(256**10).size.should >= 10
(256**10-1).size.should >= 9
(256**11).size.should >= 12
(256**12).size.should >= 13
(256**20-1).size.should >= 20
(256**40-1).size.should >= 40
end
end

ruby_version_is ""..."2.1" do
compliant_on :ironruby do
it "returns the number of bytes in the machine representation in multiples of sizeof(BDIGIT) which is 4 where long long is 64 bit" do
(256**7).size.should == 8
(256**8).size.should == 12
(256**9).size.should == 12
(256**10).size.should == 12
(256**10-1).size.should == 12
(256**11).size.should == 12
(256**12).size.should == 16
(256**20-1).size.should == 20
(256**40-1).size.should == 40
end
end

deviates_on :rubinius, :jruby do
it "returns the number of bytes in the machine representation" do
(256**7).size .should == 8
(256**8).size .should == 9
(256**9).size .should == 10
(256**10).size .should == 11
(256**10-1).size.should == 10
(256**11).size .should == 12
(256**12).size .should == 13
(256**20-1).size .should == 20
(256**40-1).size .should == 40
end
end

deviates_on :maglev do
it "returns the number of bytes in the machine representation in multiples of four" do
(256**7).size .should == 8
(256**8).size .should == 16
(256**9).size .should == 16
(256**10).size .should == 16
(256**10-1).size.should == 16
(256**11).size .should == 16
(256**12).size .should == 20
(256**20-1).size.should == 24
(256**40-1).size.should == 44
end
it "returns the number of bytes required to hold the unsigned bignum data" do
# that is, n such that 256 * n <= val.abs < 256 * (n+1)
(256**7).size.should == 8
(256**8).size.should == 9
(256**9).size.should == 10
(256**10).size.should == 11
(256**10-1).size.should == 10
(256**11).size.should == 12
(256**12).size.should == 13
(256**20-1).size.should == 20
(256**40-1).size.should == 40
end
end
end
3 changes: 1 addition & 2 deletions spec/ruby/core/encoding/converter/primitive_convert_spec.rb
Original file line number Diff line number Diff line change
@@ -86,8 +86,7 @@
end

it "accepts an options hash" do
@ec.primitive_convert("","",nil,nil, {after_output: true})\
.should_not raise_error(ArgumentError)
@ec.primitive_convert("","",nil,nil, {after_output: true}).should == :finished
end

it "sets the destination buffer's encoding to the destination encoding if the conversion suceeded" do
25 changes: 10 additions & 15 deletions spec/ruby/core/float/constants_spec.rb
Original file line number Diff line number Diff line change
@@ -5,8 +5,9 @@
Float::DIG.should == 15
end

it "EPSILON is" do
Float::EPSILON.should == eval("0.0000000000000002220446049250313080847263336181640625")
it "EPSILON is 2.220446049250313e-16" do
Float::EPSILON.should == 2.0 ** -52
Float::EPSILON.should == 2.220446049250313e-16
end

it "MANT_DIG is 53" do
@@ -29,21 +30,15 @@
Float::MIN_EXP.should == -1021
end

it "MAX is 1.79769313486232e+308" do
Float::MAX.should == eval("179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.0")
it "MAX is 1.7976931348623157e+308" do
# See https://en.wikipedia.org/wiki/Double-precision_floating-point_format#Double-precision_examples
Float::MAX.should == (1 + (1 - (2 ** -52))) * (2.0 ** 1023)
Float::MAX.should == 1.7976931348623157e+308
end

not_compliant_on :jruby do
it "MIN is 2.2250738585072e-308" do
Float::MIN.should == eval("2.225073858507201383090232717332404064219215980462331830553327416887204434813918195854283159012511020564067339731035811005152434161553460108856012385377718821130777993532002330479610147442583636071921565046942503734208375250806650616658158948720491179968591639648500635908770118304874799780887753749949451580451605050915399856582470818645113537935804992115981085766051992433352114352390148795699609591288891602992641511063466313393663477586513029371762047325631781485664350872122828637642044846811407613911477062801689853244110024161447421618567166150540154285084716752901903161322778896729707373123334086988983175067838846926092773977972858659654941091369095406136467568702398678315290680984617210924625396728515625e-308")
end
end

# TODO: Does this actually constitute noncompliance?
deviates_on :jruby do
it "MIN is 4.9e-324" do
Float::MIN.should == 4.9e-324
end
it "MIN is 2.2250738585072014e-308" do
Float::MIN.should == (2.0 ** -1022)
Float::MIN.should == 2.2250738585072014e-308
end

it "RADIX is 2" do
4 changes: 2 additions & 2 deletions spec/ruby/core/hash/allocate_spec.rb
Original file line number Diff line number Diff line change
@@ -2,12 +2,12 @@

describe "Hash.allocate" do
it "returns an instance of Hash" do
hsh = hash_class.allocate
hsh = Hash.allocate
hsh.should be_an_instance_of(Hash)
end

it "returns a fully-formed instance of Hash" do
hsh = hash_class.allocate
hsh = Hash.allocate
hsh.size.should == 0
hsh[:a] = 1
hsh.should == { a: 1 }
10 changes: 5 additions & 5 deletions spec/ruby/core/hash/any_spec.rb
Original file line number Diff line number Diff line change
@@ -3,27 +3,27 @@
describe "Hash#any?" do
describe 'with no block given' do
it "checks if there are any members of a Hash" do
empty_hash = new_hash
empty_hash = {}
empty_hash.any?.should == false

hash_with_members = new_hash('key' => 'value')
hash_with_members = { 'key' => 'value' }
hash_with_members.any?.should == true
end
end

describe 'with a block given' do
it 'is false if the hash is empty' do
empty_hash = new_hash
empty_hash = {}
empty_hash.any? {|k,v| 1 == 1 }.should == false
end

it 'is true if the block returns true for any member of the hash' do
hash_with_members = new_hash('a' => false, 'b' => false, 'c' => true, 'd' => false)
hash_with_members = { 'a' => false, 'b' => false, 'c' => true, 'd' => false }
hash_with_members.any? {|k,v| v == true}.should == true
end

it 'is false if the block returns false for all members of the hash' do
hash_with_members = new_hash('a' => false, 'b' => false, 'c' => true, 'd' => false)
hash_with_members = { 'a' => false, 'b' => false, 'c' => true, 'd' => false }
hash_with_members.any? {|k,v| v == 42}.should == false
end
end
10 changes: 5 additions & 5 deletions spec/ruby/core/hash/clear_spec.rb
Original file line number Diff line number Diff line change
@@ -3,24 +3,24 @@

describe "Hash#clear" do
it "removes all key, value pairs" do
h = new_hash(1 => 2, 3 => 4)
h = { 1 => 2, 3 => 4 }
h.clear.should equal(h)
h.should == new_hash
h.should == {}
end

it "does not remove default values" do
h = new_hash 5
h = Hash.new(5)
h.clear
h.default.should == 5

h = new_hash("a" => 100, "b" => 200)
h = { "a" => 100, "b" => 200 }
h.default = "Go fish"
h.clear
h["z"].should == "Go fish"
end

it "does not remove default procs" do
h = new_hash { 5 }
h = Hash.new { 5 }
h.clear
h.default_proc.should_not == nil
end
2 changes: 1 addition & 1 deletion spec/ruby/core/hash/clone_spec.rb
Original file line number Diff line number Diff line change
@@ -2,7 +2,7 @@

describe "Hash#clone" do
it "copies instance variable but not the objects they refer to" do
hash = new_hash('key' => 'value')
hash = { 'key' => 'value' }

clone = hash.clone

Loading

0 comments on commit 2029231

Please sign in to comment.