Skip to content

Commit

Permalink
Showing 138 changed files with 2,117 additions and 1,150 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -86,5 +86,5 @@ notifications:
urls:
- "https://rubies.travis-ci.org/rebuild/jruby-head"
# we are on a branch
#on_success: always
on_success: always
on_failure: never
2 changes: 1 addition & 1 deletion bin/jirb
Original file line number Diff line number Diff line change
@@ -9,7 +9,7 @@

require "irb"

if __FILE__.sub(/file:/, '') == $0.sub(/file:/, '')
if __FILE__.sub(/file:/, '').gsub(/ /,'%20') == $0.gsub(/file:/, '').gsub(/ /,'%20')
IRB.start(__FILE__)
else
# check -e option
2 changes: 1 addition & 1 deletion core/pom.rb
Original file line number Diff line number Diff line change
@@ -37,7 +37,7 @@
jar 'com.github.jnr:jnr-unixsocket:0.3'
jar 'com.github.jnr:jnr-posix:3.0.7'
jar 'com.github.jnr:jnr-constants:0.8.6-SNAPSHOT'
jar 'com.github.jnr:jnr-ffi:2.0.0-SNAPSHOT'
jar 'com.github.jnr:jnr-ffi:2.0.0'
jar 'com.github.jnr:jffi:${jffi.version}'
jar 'com.github.jnr:jffi:${jffi.version}:native'

2 changes: 1 addition & 1 deletion core/pom.xml
Original file line number Diff line number Diff line change
@@ -81,7 +81,7 @@
<dependency>
<groupId>com.github.jnr</groupId>
<artifactId>jnr-ffi</artifactId>
<version>2.0.0-SNAPSHOT</version>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>com.github.jnr</groupId>
8 changes: 8 additions & 0 deletions core/src/main/java/org/jruby/RubyBasicObject.java
Original file line number Diff line number Diff line change
@@ -1690,6 +1690,8 @@ public IRubyObject instance_exec19(ThreadContext context, IRubyObject[] args, Bl
protected IRubyObject yieldUnder(final ThreadContext context, RubyModule under, IRubyObject[] args, Block block, EvalType evalType) {
context.preExecuteUnder(under, block);

IRubyObject savedBindingSelf = block.getBinding().getSelf();
IRubyObject savedFrameSelf = block.getBinding().getFrame().getSelf();
Visibility savedVisibility = block.getBinding().getVisibility();
block.getBinding().setVisibility(PUBLIC);

@@ -1706,6 +1708,8 @@ protected IRubyObject yieldUnder(final ThreadContext context, RubyModule under,
return (IRubyObject) bj.getValue();
} finally {
block.getBinding().setVisibility(savedVisibility);
block.getBinding().setSelf(savedBindingSelf);
block.getBinding().getFrame().setSelf(savedFrameSelf);

context.postExecuteUnder();
}
@@ -1728,6 +1732,8 @@ private Block setupBlock(Block block, EvalType evalType) {
protected IRubyObject yieldUnder(final ThreadContext context, RubyModule under, Block block, EvalType evalType) {
context.preExecuteUnder(under, block);

IRubyObject savedBindingSelf = block.getBinding().getSelf();
IRubyObject savedFrameSelf = block.getBinding().getFrame().getSelf();
Visibility savedVisibility = block.getBinding().getVisibility();
block.getBinding().setVisibility(PUBLIC);

@@ -1738,6 +1744,8 @@ protected IRubyObject yieldUnder(final ThreadContext context, RubyModule under,
return (IRubyObject) bj.getValue();
} finally {
block.getBinding().setVisibility(savedVisibility);
block.getBinding().setSelf(savedBindingSelf);
block.getBinding().getFrame().setSelf(savedFrameSelf);

context.postExecuteUnder();
}
9 changes: 7 additions & 2 deletions core/src/main/java/org/jruby/RubyClassPathVariable.java
Original file line number Diff line number Diff line change
@@ -66,10 +66,15 @@ public IRubyObject append(ThreadContext context, IRubyObject obj) {
paths = context.runtime.newArray(obj).toJavaArray();
}

boolean is1_8 = context.getRuntime().is1_8();
for (IRubyObject path: paths) {
String ss = path.convertToString().toString();
try {
URL url = getURL(ss);
URL url = getURL(path.convertToString().toString());
if (url.getProtocol().equals("file")) {
path = is1_8 ? RubyFile.expand_path(context, null, new IRubyObject[]{ path })
: RubyFile.expand_path19(context, null, new IRubyObject[]{ path });
url = getURL(path.convertToString().toString());
}
getRuntime().getJRubyClassLoader().addURL(url);
} catch (MalformedURLException mue) {
throw getRuntime().newArgumentError(mue.getLocalizedMessage());
5 changes: 4 additions & 1 deletion core/src/main/java/org/jruby/RubyFile.java
Original file line number Diff line number Diff line change
@@ -934,7 +934,10 @@ public static IRubyObject rename(ThreadContext context, IRubyObject recv, IRubyO
JRubyFile oldFile = JRubyFile.create(runtime.getCurrentDirectory(), oldNameJavaString);
JRubyFile newFile = JRubyFile.create(runtime.getCurrentDirectory(), newNameJavaString);

if (!oldFile.exists() || !newFile.getParentFile().exists()) {
boolean isOldSymlink = RubyFileTest.symlink_p(recv, oldNameString).isTrue();
// Broken symlinks considered by exists() as non-existing,
// so we need to check for symlinks explicitly.
if (!(oldFile.exists() || isOldSymlink) || !newFile.getParentFile().exists()) {
throw runtime.newErrnoENOENTError(oldNameJavaString + " or " + newNameJavaString);
}

3 changes: 1 addition & 2 deletions core/src/main/java/org/jruby/RubyFileTest.java
Original file line number Diff line number Diff line change
@@ -197,8 +197,7 @@ public static IRubyObject readable_p(ThreadContext context, IRubyObject recv, IR
filename = get_path(context, filename);
}

FileStat stat = fileResource(filename).stat();
return runtime.newBoolean(stat != null && stat.isReadable());
return runtime.newBoolean(fileResource(filename).canRead());
}

// Not exposed by filetest, but so similiar in nature that it is stored here
6 changes: 3 additions & 3 deletions core/src/main/java/org/jruby/RubyThread.java
Original file line number Diff line number Diff line change
@@ -1259,10 +1259,10 @@ public <Data, Return> Return executeTask(ThreadContext context, Data data, Task<
this.unblockFunc = task;
this.unblockArg = data;

enterSleep();

// check for interrupt before going into blocking call
context.pollThreadEvents();
pollThreadEvents(context);

enterSleep();

return task.run(context, data);
} finally {
5 changes: 5 additions & 0 deletions core/src/main/java/org/jruby/ast/DXStrNode.java
Original file line number Diff line number Diff line change
@@ -31,6 +31,7 @@
***** END LICENSE BLOCK *****/
package org.jruby.ast;

import org.jcodings.Encoding;
import org.jruby.ast.types.ILiteralNode;
import org.jruby.ast.visitor.NodeVisitor;
import org.jruby.lexer.yacc.ISourcePosition;
@@ -44,6 +45,10 @@ public DXStrNode(ISourcePosition position, DStrNode node) {
super(position);
addAll(node);
}

public DXStrNode(ISourcePosition position, Encoding encoding) {
super(position, encoding);
}

public DXStrNode(ISourcePosition position) {
super(position);
56 changes: 41 additions & 15 deletions core/src/main/java/org/jruby/ext/ffi/Enum.java
Original file line number Diff line number Diff line change
@@ -30,7 +30,7 @@

import java.util.IdentityHashMap;
import java.util.Map;
import org.jcodings.util.IntHash;
import java.util.concurrent.ConcurrentHashMap;
import org.jruby.anno.JRubyClass;
import org.jruby.anno.JRubyMethod;
import org.jruby.Ruby;
@@ -52,12 +52,12 @@
*/
@JRubyClass(name="FFI::Enum", parent="Object")
public final class Enum extends RubyObject {
private final IRubyObject nativeType;
private IRubyObject nativeType;
private final RubyHash kv_map;
private volatile IRubyObject tag;

private volatile Map<RubySymbol, RubyInteger> symbolToValue = new IdentityHashMap<RubySymbol, RubyInteger>();
private volatile IntHash<RubySymbol> valueToSymbol = new IntHash<RubySymbol>();
private volatile ConcurrentHashMap<Long, RubySymbol> valueToSymbol = new ConcurrentHashMap<Long, RubySymbol>();

public static RubyClass createEnumClass(Ruby runtime, RubyModule ffiModule) {
RubyClass enumClass = ffiModule.defineClassUnder("Enum", runtime.getObject(),
@@ -79,27 +79,53 @@ public final IRubyObject allocate(Ruby runtime, RubyClass klass) {

private Enum(Ruby runtime, RubyClass klass) {
super(runtime, klass);
nativeType = runtime.getModule("FFI").getClass("Type").getConstant("INT");
kv_map = RubyHash.newHash(runtime);
tag = runtime.getNil();
}

@JRubyMethod(name = "initialize", visibility = Visibility.PRIVATE)
public final IRubyObject initialize(ThreadContext context, IRubyObject values, IRubyObject tag) {
this.tag = tag;
return initialize(context, values);
public final IRubyObject initialize(ThreadContext context, IRubyObject arg) {
return initialize(context, null, null, arg);
}

@JRubyMethod(name = "initialize", visibility = Visibility.PRIVATE)
public final IRubyObject initialize(ThreadContext context, IRubyObject values) {
public final IRubyObject initialize(ThreadContext context, IRubyObject arg0, IRubyObject arg1) {
if (arg0 instanceof org.jruby.ext.ffi.Type)
return initialize(context, arg0, arg1, null);

if (arg1.isNil())
return initialize(context, null, arg0, null);

// Handles bad args and tag, values case.
return initialize(context, null, arg0, arg1);
}

@JRubyMethod(name = "initialize", visibility = Visibility.PRIVATE)
public final IRubyObject initialize(ThreadContext context, IRubyObject type, IRubyObject values, IRubyObject tag) {
int offset = 0;
if (type instanceof org.jruby.ext.ffi.Type) {
nativeType = type;
} else {
if (!(type == null || type.isNil()))
throw context.runtime.newTypeError(type, context.runtime.getModule("FFI").getClass("Type"));

nativeType = context.runtime.getModule("FFI").getClass("Type").getConstant("INT");
}

if (!(tag == null || tag.isNil() || tag instanceof RubySymbol))
throw context.runtime.newTypeError(tag, context.runtime.getSymbol());

this.tag = tag;

if (!(values instanceof RubyArray)) {
throw context.runtime.newTypeError(values, context.runtime.getArray());
}

RubyArray ary = (RubyArray) values;

Map<RubySymbol, RubyInteger> s2v = new IdentityHashMap<RubySymbol, RubyInteger>();
IRubyObject prevConstant = null;
int nextValue = 0;
long nextValue = 0;

for (int i = 0; i < ary.size(); i++) {
IRubyObject v = ary.entry(i);
@@ -109,24 +135,24 @@ public final IRubyObject initialize(ThreadContext context, IRubyObject values) {
prevConstant = v;
nextValue++;

} else if (v instanceof RubyFixnum) {
} else if (v instanceof RubyInteger) {
if (prevConstant == null) {
throw context.runtime.newArgumentError("invalid enum sequence - no symbol for value "
+ v);
}
s2v.put((RubySymbol) prevConstant, (RubyFixnum) v);
nextValue = (int) ((RubyInteger) v).getLongValue() + 1;
nextValue = ((RubyInteger) v).getLongValue() + 1;

} else {
throw context.runtime.newTypeError(v, context.runtime.getSymbol());
}
}

symbolToValue = new IdentityHashMap<RubySymbol, RubyInteger>(s2v);
valueToSymbol = new IntHash<RubySymbol>(symbolToValue.size());
valueToSymbol = new ConcurrentHashMap<Long, RubySymbol>(symbolToValue.size());
for (Map.Entry<RubySymbol, RubyInteger> e : symbolToValue.entrySet()) {
kv_map.fastASet(e.getKey(), e.getValue());
valueToSymbol.put((int) e.getValue().getLongValue(), e.getKey());
valueToSymbol.put(e.getValue().getLongValue(), e.getKey());
}

return this;
@@ -139,7 +165,7 @@ public final IRubyObject find(ThreadContext context, IRubyObject query) {
return value != null ? value : context.runtime.getNil();

} else if (query instanceof RubyInteger) {
RubySymbol symbol = valueToSymbol.get((int) ((RubyInteger) query).getLongValue());
RubySymbol symbol = valueToSymbol.get((Long)((RubyInteger) query).getLongValue());
return symbol != null ? symbol : context.runtime.getNil();

} else {
@@ -194,7 +220,7 @@ public final IRubyObject from_native(ThreadContext context, IRubyObject value, I

RubySymbol sym;

if (value instanceof RubyInteger && (sym = valueToSymbol.get((int) ((RubyInteger) value).getLongValue())) != null) {
if (value instanceof RubyInteger && (sym = valueToSymbol.get((Long)((RubyInteger) value).getLongValue())) != null) {
return sym;
}

4 changes: 3 additions & 1 deletion core/src/main/java/org/jruby/ext/ffi/Enums.java
Original file line number Diff line number Diff line change
@@ -93,7 +93,9 @@ public IRubyObject append(final ThreadContext context, IRubyObject item){
}
allEnums.append(item);
if (!(item == null || item == context.nil)){
taggedEnums.fastASet(((Enum)item).tag(context), item);
IRubyObject tag = ((Enum)item).tag(context);
if (tag != null && !tag.isNil())
taggedEnums.fastASet(tag, item);
}
symbolMap.merge_bang(context, ((Enum)item).symbol_map(context), Block.NULL_BLOCK);
return item;
Empty file.
2 changes: 1 addition & 1 deletion core/src/main/java/org/jruby/runtime/Frame.java
Original file line number Diff line number Diff line change
@@ -253,7 +253,7 @@ public String getName() {
*
* @return The self for the frame
*/
IRubyObject getSelf() {
public IRubyObject getSelf() {
return self;
}

22 changes: 22 additions & 0 deletions core/src/main/java/org/jruby/runtime/callsite/CachingCallSite.java
Original file line number Diff line number Diff line change
@@ -65,6 +65,8 @@ public IRubyObject call(ThreadContext context, IRubyObject caller, IRubyObject s

public IRubyObject call(ThreadContext context, IRubyObject caller, IRubyObject self, IRubyObject... args) {
RubyClass selfType = getClass(self);
// This must be retrieved *once* to avoid racing with other threads.
CacheEntry cache = this.cache;
if (CacheEntry.typeOk(cache, selfType)) {
return cache.method.call(context, self, selfType, methodName, args);
}
@@ -73,6 +75,8 @@ public IRubyObject call(ThreadContext context, IRubyObject caller, IRubyObject s

private IRubyObject callBlock(ThreadContext context, IRubyObject caller, IRubyObject self, IRubyObject[] args, Block block) {
RubyClass selfType = getClass(self);
// This must be retrieved *once* to avoid racing with other threads.
CacheEntry cache = this.cache;
if (CacheEntry.typeOk(cache, selfType)) {
return cache.method.call(context, self, selfType, methodName, args, block);
}
@@ -123,6 +127,8 @@ public IRubyObject callVarargsIter(ThreadContext context, IRubyObject caller, IR

public IRubyObject call(ThreadContext context, IRubyObject caller, IRubyObject self) {
RubyClass selfType = getClass(self);
// This must be retrieved *once* to avoid racing with other threads.
CacheEntry cache = this.cache;
if (CacheEntry.typeOk(cache, selfType)) {
return cache.method.call(context, self, selfType, methodName);
}
@@ -131,6 +137,8 @@ public IRubyObject call(ThreadContext context, IRubyObject caller, IRubyObject s

private IRubyObject callBlock(ThreadContext context, IRubyObject caller, IRubyObject self, Block block) {
RubyClass selfType = getClass(self);
// This must be retrieved *once* to avoid racing with other threads.
CacheEntry cache = this.cache;
if (CacheEntry.typeOk(cache, selfType)) {
return cache.method.call(context, self, selfType, methodName, block);
}
@@ -151,6 +159,8 @@ public IRubyObject callIter(ThreadContext context, IRubyObject caller, IRubyObje

public IRubyObject call(ThreadContext context, IRubyObject caller, IRubyObject self, IRubyObject arg1) {
RubyClass selfType = getClass(self);
// This must be retrieved *once* to avoid racing with other threads.
CacheEntry cache = this.cache;
if (CacheEntry.typeOk(cache, selfType)) {
return cache.method.call(context, self, selfType, methodName, arg1);
}
@@ -159,6 +169,8 @@ public IRubyObject call(ThreadContext context, IRubyObject caller, IRubyObject s

private IRubyObject callBlock(ThreadContext context, IRubyObject caller, IRubyObject self, IRubyObject arg1, Block block) {
RubyClass selfType = getClass(self);
// This must be retrieved *once* to avoid racing with other threads.
CacheEntry cache = this.cache;
if (CacheEntry.typeOk(cache, selfType)) {
return cache.method.call(context, self, selfType, methodName, arg1, block);
}
@@ -179,6 +191,8 @@ public IRubyObject callIter(ThreadContext context, IRubyObject caller, IRubyObje

public IRubyObject call(ThreadContext context, IRubyObject caller, IRubyObject self, IRubyObject arg1, IRubyObject arg2) {
RubyClass selfType = getClass(self);
// This must be retrieved *once* to avoid racing with other threads.
CacheEntry cache = this.cache;
if (CacheEntry.typeOk(cache, selfType)) {
return cache.method.call(context, self, selfType, methodName, arg1, arg2);
}
@@ -187,6 +201,8 @@ public IRubyObject call(ThreadContext context, IRubyObject caller, IRubyObject s

private IRubyObject callBlock(ThreadContext context, IRubyObject caller, IRubyObject self, IRubyObject arg1, IRubyObject arg2, Block block) {
RubyClass selfType = getClass(self);
// This must be retrieved *once* to avoid racing with other threads.
CacheEntry cache = this.cache;
if (CacheEntry.typeOk(cache, selfType)) {
return cache.method.call(context, self, selfType, methodName, arg1, arg2, block);
}
@@ -207,6 +223,8 @@ public IRubyObject callIter(ThreadContext context, IRubyObject caller, IRubyObje

public IRubyObject call(ThreadContext context, IRubyObject caller, IRubyObject self, IRubyObject arg1, IRubyObject arg2, IRubyObject arg3) {
RubyClass selfType = getClass(self);
// This must be retrieved *once* to avoid racing with other threads.
CacheEntry cache = this.cache;
if (CacheEntry.typeOk(cache, selfType)) {
return cache.method.call(context, self, selfType, methodName, arg1, arg2, arg3);
}
@@ -215,6 +233,8 @@ public IRubyObject call(ThreadContext context, IRubyObject caller, IRubyObject s

private IRubyObject callBlock(ThreadContext context, IRubyObject caller, IRubyObject self, IRubyObject arg1, IRubyObject arg2, IRubyObject arg3, Block block) {
RubyClass selfType = getClass(self);
// This must be retrieved *once* to avoid racing with other threads.
CacheEntry cache = this.cache;
if (CacheEntry.typeOk(cache, selfType)) {
return cache.method.call(context, self, selfType, methodName, arg1, arg2, arg3, block);
}
@@ -234,6 +254,8 @@ public IRubyObject callIter(ThreadContext context, IRubyObject caller, IRubyObje
}

public CacheEntry retrieveCache(RubyClass selfType, String methodName) {
// This must be retrieved *once* to avoid racing with other threads.
CacheEntry cache = this.cache;
if (CacheEntry.typeOk(cache, selfType)) {
return cache;
}
2 changes: 2 additions & 0 deletions core/src/main/java/org/jruby/truffle/TruffleBridgeImpl.java
Original file line number Diff line number Diff line change
@@ -153,6 +153,8 @@ public Object get() {
final RubyRootNode parsedRootNode = truffleContext.getTranslator().parse(truffleContext, source, parserContext, parentFrame, null);
final CallTarget callTarget = Truffle.getRuntime().createCallTarget(parsedRootNode);

// TODO(CS): we really need a method here - it's causing problems elsewhere

return callTarget.call(RubyArguments.pack(null, parentFrame, self, null, new Object[]{}));
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright (c) 2014 Oracle and/or its affiliates. All rights reserved. This
* code is released under a tri EPL/GPL/LGPL license. You can use it,
* redistribute it and/or modify it under the terms of the:
*
* Eclipse Public License version 1.0
* GNU General Public License version 2
* GNU Lesser General Public License version 2.1
*/
package org.jruby.truffle.nodes;

import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.source.SourceSection;
import org.jruby.truffle.runtime.RubyContext;

public class AssignmentWrapperNode extends RubyNode {

@Child protected RubyNode child;

public AssignmentWrapperNode(RubyContext context, SourceSection sourceSection, RubyNode child) {
super(context, sourceSection);
this.child = child;
}

@Override
public Object execute(VirtualFrame frame) {
return child.execute(frame);
}

@Override
public Object isDefined(VirtualFrame frame) {
return getContext().makeString("assignment");
}

}
Original file line number Diff line number Diff line change
@@ -39,7 +39,7 @@ public Object execute(VirtualFrame frame) {

return new RubyProc(getContext().getCoreLibrary().getProcClass(), RubyProc.Type.LAMBDA,
method.getSharedMethodInfo(), method.getCallTarget(), method.getCallTarget(),
method.getDeclarationFrame(), RubyArguments.getSelf(frame.getArguments()), null);
method.getDeclarationFrame(), method.getDeclaringModule(), method, RubyArguments.getSelf(frame.getArguments()), null);
}

@Override
19 changes: 12 additions & 7 deletions core/src/main/java/org/jruby/truffle/nodes/cast/SplatCastNode.java
Original file line number Diff line number Diff line change
@@ -19,6 +19,7 @@
import org.jruby.truffle.runtime.*;
import org.jruby.truffle.runtime.core.RubyArray;
import org.jruby.truffle.runtime.core.RubyNilClass;
import org.jruby.truffle.runtime.core.RubyString;

/**
* Splat as used to cast a value to an array if it isn't already, as in {@code *value}.
@@ -33,18 +34,24 @@ public static enum NilBehavior {

private final NilBehavior nilBehavior;

@Child protected DispatchHeadNode respondToToA;
@Child protected BooleanCastNode respondToCast;
@Child protected DispatchHeadNode toA;

public SplatCastNode(RubyContext context, SourceSection sourceSection, NilBehavior nilBehavior) {
super(context, sourceSection);
this.nilBehavior = nilBehavior;
// Calling private #to_a is allowed for the *splat operator.
respondToToA = new DispatchHeadNode(context, true, Dispatch.MissingBehavior.RETURN_MISSING);
respondToCast = BooleanCastNodeFactory.create(context, sourceSection, null);
toA = new DispatchHeadNode(context, true, Dispatch.MissingBehavior.RETURN_MISSING);
}

public SplatCastNode(SplatCastNode prev) {
super(prev);
nilBehavior = prev.nilBehavior;
respondToToA = prev.respondToToA;
respondToCast = prev.respondToCast;
toA = prev.toA;
}

@@ -75,19 +82,17 @@ public RubyArray splat(RubyArray array) {
public RubyArray splat(VirtualFrame frame, Object object) {
notDesignedForCompilation();

if (toA.doesRespondTo(frame, "to_a", object)) {
RubyString toAString = getContext().makeString("to_a"); // TODO

if (respondToCast.executeBoolean(frame, respondToToA.call(frame, object, "respond_to?", null, toAString, true))) {
final Object array = toA.call(frame, object, "to_a", null);

if (array instanceof RubyArray) {
return (RubyArray) array;
}

// TODO(CS): surely this is an error? to_a returned something that was not an array

return RubyArray.fromObject(getContext().getCoreLibrary().getArrayClass(), object);
} else {
return RubyArray.fromObject(getContext().getCoreLibrary().getArrayClass(), object);
}

return RubyArray.fromObject(getContext().getCoreLibrary().getArrayClass(), object);
}

}
Original file line number Diff line number Diff line change
@@ -13,21 +13,21 @@
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.UnexpectedResultException;
import org.jruby.truffle.nodes.RubyNode;
import org.jruby.truffle.nodes.dispatch.DispatchHeadNode;
import org.jruby.truffle.nodes.dispatch.PredicateDispatchHeadNode;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.core.RubyArray;

public class WhenSplatNode extends RubyNode {

@Child protected RubyNode readCaseExpression;
@Child protected RubyNode splat;
@Child protected DispatchHeadNode dispatchCaseEqual;
@Child protected PredicateDispatchHeadNode dispatchCaseEqual;

public WhenSplatNode(RubyContext context, SourceSection sourceSection, RubyNode readCaseExpression, RubyNode splat) {
super(context, sourceSection);
this.readCaseExpression = readCaseExpression;
this.splat = splat;
dispatchCaseEqual = new DispatchHeadNode(context);
dispatchCaseEqual = new PredicateDispatchHeadNode(context);
}

@Override
@@ -45,7 +45,7 @@ public boolean executeBoolean(VirtualFrame frame) {
}

for (Object value : array.slowToArray()) {
if (dispatchCaseEqual.callIsTruthy(frame, caseExpression, "===", null, value)) {
if (dispatchCaseEqual.call(frame, caseExpression, "===", null, value)) {
return true;
}
}
23 changes: 18 additions & 5 deletions core/src/main/java/org/jruby/truffle/nodes/core/ArrayNodes.java
Original file line number Diff line number Diff line change
@@ -27,6 +27,7 @@
import org.jruby.truffle.nodes.RubyRootNode;
import org.jruby.truffle.nodes.dispatch.Dispatch;
import org.jruby.truffle.nodes.dispatch.DispatchHeadNode;
import org.jruby.truffle.nodes.dispatch.PredicateDispatchHeadNode;
import org.jruby.truffle.nodes.methods.arguments.MissingArgumentBehaviour;
import org.jruby.truffle.nodes.methods.arguments.ReadPreArgumentNode;
import org.jruby.truffle.nodes.methods.locals.ReadLevelVariableNodeFactory;
@@ -430,11 +431,11 @@ public RubyArray orObject(RubyArray a, RubyArray b) {
@CoreMethod(names = {"==", "eql?"}, required = 1)
public abstract static class EqualNode extends ArrayCoreMethodNode {

@Child protected DispatchHeadNode equals;
@Child protected PredicateDispatchHeadNode equals;

public EqualNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
equals = new DispatchHeadNode(context);
equals = new PredicateDispatchHeadNode(context);
}

public EqualNode(EqualNode prev) {
@@ -503,7 +504,7 @@ public boolean equal(VirtualFrame frame, RubyArray a, RubyArray b) {
final Object[] bs = b.slowToArray();

for (int n = 0; n < a.getSize(); n++) {
if (!equals.callIsTruthy(frame, as[n], "==", null, bs[n])) {
if (!equals.call(frame, as[n], "==", null, bs[n])) {
return false;
}
}
@@ -2365,7 +2366,7 @@ public Object max(VirtualFrame frame, RubyArray array) {

final RubyProc block = new RubyProc(getContext().getCoreLibrary().getProcClass(), RubyProc.Type.PROC,
maxBlock.getSharedMethodInfo(), maxBlock.getCallTarget(), maxBlock.getCallTarget(),
maximumClosureFrame.materialize(), array, null);
maximumClosureFrame.materialize(), null, null, array, null);

eachNode.call(frame, array, "each", block);

@@ -2440,10 +2441,16 @@ public FrameSlot getFrameSlot() {
return frameSlot;
}

@Override
public SharedMethodInfo getSharedMethodInfo() {
return sharedMethodInfo;
}

@Override
public RubyModule getDeclaringModule() {
throw new UnsupportedOperationException();
}

public CallTarget getCallTarget() {
return callTarget;
}
@@ -2478,7 +2485,7 @@ public Object min(VirtualFrame frame, RubyArray array) {

final RubyProc block = new RubyProc(getContext().getCoreLibrary().getProcClass(), RubyProc.Type.PROC,
minBlock.getSharedMethodInfo(), minBlock.getCallTarget(), minBlock.getCallTarget(),
minimumClosureFrame.materialize(), array, null);
minimumClosureFrame.materialize(), null, null, array, null);

eachNode.call(frame, array, "each", block);

@@ -2553,10 +2560,16 @@ public FrameSlot getFrameSlot() {
return frameSlot;
}

@Override
public SharedMethodInfo getSharedMethodInfo() {
return sharedMethodInfo;
}

@Override
public RubyModule getDeclaringModule() {
throw new UnsupportedOperationException();
}

public CallTarget getCallTarget() {
return callTarget;
}
Original file line number Diff line number Diff line change
@@ -11,8 +11,6 @@

import java.util.*;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.ExactMath;
import com.oracle.truffle.api.source.*;
import com.oracle.truffle.api.dsl.*;
import com.oracle.truffle.api.frame.VirtualFrame;
@@ -21,6 +19,7 @@
import org.jruby.truffle.nodes.cast.BooleanCastNodeFactory;
import org.jruby.truffle.nodes.dispatch.Dispatch;
import org.jruby.truffle.nodes.dispatch.DispatchHeadNode;
import org.jruby.truffle.nodes.dispatch.PredicateDispatchHeadNode;
import org.jruby.truffle.nodes.yield.YieldDispatchHeadNode;
import org.jruby.truffle.runtime.*;
import org.jruby.truffle.runtime.control.RaiseException;
@@ -31,7 +30,7 @@
public abstract class BasicObjectNodes {

@CoreMethod(names = "!")
public abstract static class NotNode extends CoreMethodNode {
public abstract static class NotNode extends UnaryCoreMethodNode {

public NotNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
@@ -41,10 +40,8 @@ public NotNode(NotNode prev) {
super(prev);
}

@CreateCast("arguments") public RubyNode[] createCast(RubyNode[] arguments) {
return new RubyNode[] {
BooleanCastNodeFactory.create(getContext(), getSourceSection(), arguments[0])
};
@CreateCast("operand") public RubyNode createCast(RubyNode operand) {
return BooleanCastNodeFactory.create(getContext(), getSourceSection(), operand);
}

@Specialization
@@ -57,11 +54,11 @@ public boolean not(boolean value) {
@CoreMethod(names = "!=", required = 1)
public abstract static class NotEqualNode extends CoreMethodNode {

@Child protected DispatchHeadNode equalNode;
@Child protected PredicateDispatchHeadNode equalNode;

public NotEqualNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
equalNode = new DispatchHeadNode(context);
equalNode = new PredicateDispatchHeadNode(context);
}

public NotEqualNode(NotEqualNode prev) {
@@ -71,7 +68,7 @@ public NotEqualNode(NotEqualNode prev) {

@Specialization
public boolean equal(VirtualFrame frame, Object a, Object b) {
return !equalNode.callIsTruthy(frame, a, "==", null, b);
return !equalNode.call(frame, a, "==", null, b);
}

}
@@ -149,7 +146,7 @@ protected boolean isSmallFixnum(long fixnum) {
}

@CoreMethod(names = {"equal?", "=="}, required = 1)
public abstract static class ReferenceEqualNode extends CoreMethodNode {
public abstract static class ReferenceEqualNode extends BinaryCoreMethodNode {

public ReferenceEqualNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
@@ -170,17 +167,17 @@ public ReferenceEqualNode(ReferenceEqualNode prev) {
return a == b;
}

@Specialization(guards = {"isNotRubyBasicObject(arguments[0])", "isNotRubyBasicObject(arguments[1])", "notSameClass"})
@Specialization(guards = {"isNotRubyBasicObject(left)", "isNotRubyBasicObject(right)", "notSameClass"})
public boolean equal(Object a, Object b) {
return false;
}

@Specialization(guards = "isNotRubyBasicObject(arguments[0])")
@Specialization(guards = "isNotRubyBasicObject(left)")
public boolean equal(Object a, RubyBasicObject b) {
return false;
}

@Specialization(guards = "isNotRubyBasicObject(arguments[1])")
@Specialization(guards = "isNotRubyBasicObject(right)")
public boolean equal(RubyBasicObject a, Object b) {
return false;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright (c) 2013, 2014 Oracle and/or its affiliates. All rights reserved. This
* code is released under a tri EPL/GPL/LGPL license. You can use it,
* redistribute it and/or modify it under the terms of the:
*
* Eclipse Public License version 1.0
* GNU General Public License version 2
* GNU Lesser General Public License version 2.1
*/
package org.jruby.truffle.nodes.core;

import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.NodeChildren;
import com.oracle.truffle.api.source.SourceSection;
import org.jruby.truffle.nodes.RubyNode;
import org.jruby.truffle.runtime.RubyContext;

@NodeChildren({
@NodeChild(value = "left", type = RubyNode.class),
@NodeChild(value = "right", type = RubyNode.class)})
public abstract class BinaryCoreMethodNode extends RubyNode {

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

public BinaryCoreMethodNode(BinaryCoreMethodNode prev) {
super(prev);
}

public abstract RubyNode getLeft();

public abstract RubyNode getRight();

}
Original file line number Diff line number Diff line change
@@ -37,8 +37,8 @@

public abstract class CoreMethodNodeManager {

public static void addCoreMethodNodes(RubyClass rubyObjectClass, List<? extends NodeFactory<? extends CoreMethodNode>> nodeFactories) {
for (NodeFactory<? extends CoreMethodNode> nodeFactory : nodeFactories) {
public static void addCoreMethodNodes(RubyClass rubyObjectClass, List<? extends NodeFactory<? extends RubyNode>> nodeFactories) {
for (NodeFactory<? extends RubyNode> nodeFactory : nodeFactories) {
final GeneratedBy generatedBy = nodeFactory.getClass().getAnnotation(GeneratedBy.class);
final Class<?> nodeClass = generatedBy.value();
final CoreClass classAnnotation = nodeClass.getEnclosingClass().getAnnotation(CoreClass.class);
@@ -164,7 +164,18 @@ private static RubyRootNode makeGenericMethod(RubyContext context, MethodDetails
argumentsNodes.add(new ReadBlockNode(context, sourceSection, UndefinedPlaceholder.INSTANCE));
}

final RubyNode methodNode = methodDetails.getNodeFactory().createNode(context, sourceSection, argumentsNodes.toArray(new RubyNode[argumentsNodes.size()]));
final RubyNode methodNode;
List<List<Class<?>>> signatures = methodDetails.getNodeFactory().getNodeSignatures();
if (signatures.size() < 1 || signatures.get(0).get(2) == RubyNode[].class) {
methodNode = methodDetails.getNodeFactory().createNode(context, sourceSection, argumentsNodes.toArray(new RubyNode[argumentsNodes.size()]));
} else {
Object[] args = new Object[2 + argumentsNodes.size()];
args[0] = context;
args[1] = sourceSection;
System.arraycopy(argumentsNodes.toArray(new RubyNode[argumentsNodes.size()]), 0, args, 2, argumentsNodes.size());
methodNode = methodDetails.getNodeFactory().createNode(args);
}

final CheckArityNode checkArity = new CheckArityNode(context, sourceSection, arity);
final RubyNode block = SequenceNode.sequence(context, sourceSection, checkArity, methodNode);
final ExceptionTranslatingNode exceptionTranslatingNode = new ExceptionTranslatingNode(context, sourceSection, block);
32 changes: 16 additions & 16 deletions core/src/main/java/org/jruby/truffle/nodes/core/HashNodes.java
Original file line number Diff line number Diff line change
@@ -18,9 +18,9 @@
import com.oracle.truffle.api.frame.*;
import com.oracle.truffle.api.utilities.BranchProfile;
import org.jruby.runtime.Visibility;
import org.jruby.truffle.nodes.RubyNode;
import org.jruby.truffle.nodes.RubyRootNode;
import org.jruby.truffle.nodes.dispatch.DispatchHeadNode;
import org.jruby.truffle.nodes.dispatch.PredicateDispatchHeadNode;
import org.jruby.truffle.nodes.yield.YieldDispatchHeadNode;
import org.jruby.truffle.runtime.*;
import org.jruby.truffle.runtime.core.*;
@@ -33,11 +33,11 @@ public abstract class HashNodes {
@CoreMethod(names = "==", required = 1)
public abstract static class EqualNode extends HashCoreMethodNode {

@Child protected DispatchHeadNode equalNode;
@Child protected PredicateDispatchHeadNode equalNode;

public EqualNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
equalNode = new DispatchHeadNode(context);
equalNode = new PredicateDispatchHeadNode(context);
}

public EqualNode(EqualNode prev) {
@@ -69,7 +69,7 @@ public boolean equalObjectArray(VirtualFrame frame, RubyHash a, RubyHash b) {
}

for (int n = 0; n < aSize * 2; n++) {
if (!equalNode.callIsTruthy(frame, aStore[n], "==", null, bStore[n])) {
if (!equalNode.call(frame, aStore[n], "==", null, bStore[n])) {
return false;
}
}
@@ -199,15 +199,15 @@ public RubyHash constructObjectLinkedMapMap(Object[] args) {
@CoreMethod(names = "[]", required = 1)
public abstract static class GetIndexNode extends HashCoreMethodNode {

@Child protected DispatchHeadNode eqlNode;
@Child protected PredicateDispatchHeadNode eqlNode;
@Child protected YieldDispatchHeadNode yield;

private final BranchProfile notInHashProfile = BranchProfile.create();
private final BranchProfile useDefaultProfile = BranchProfile.create();

public GetIndexNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
eqlNode = new DispatchHeadNode(context);
eqlNode = new PredicateDispatchHeadNode(context);
yield = new YieldDispatchHeadNode(context);
}

@@ -237,7 +237,7 @@ public Object getObjectArray(VirtualFrame frame, RubyHash hash, Object key) {
final int size = hash.getStoreSize();

for (int n = 0; n < RubyHash.HASHES_SMALL; n++) {
if (n < size && eqlNode.callIsTruthy(frame, store[n * 2], "eql?", null, key)) {
if (n < size && eqlNode.call(frame, store[n * 2], "eql?", null, key)) {
return store[n * 2 + 1];
}
}
@@ -285,15 +285,15 @@ public Object getObjectLinkedHashMap(VirtualFrame frame, RubyHash hash, Object k
@CoreMethod(names = "[]=", required = 2)
public abstract static class SetIndexNode extends HashCoreMethodNode {

@Child protected DispatchHeadNode eqlNode;
@Child protected PredicateDispatchHeadNode eqlNode;

private final BranchProfile considerExtendProfile = BranchProfile.create();
private final BranchProfile extendProfile = BranchProfile.create();
private final BranchProfile transitionToLinkedHashMapProfile = BranchProfile.create();

public SetIndexNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
eqlNode = new DispatchHeadNode(context);
eqlNode = new PredicateDispatchHeadNode(context);
}

public SetIndexNode(SetIndexNode prev) {
@@ -320,7 +320,7 @@ public Object setObjectArray(VirtualFrame frame, RubyHash hash, Object key, Obje
final int size = hash.getStoreSize();

for (int n = 0; n < RubyHash.HASHES_SMALL; n++) {
if (n < size && eqlNode.callIsTruthy(frame, store[n * 2], "eql?", null, key)) {
if (n < size && eqlNode.call(frame, store[n * 2], "eql?", null, key)) {
store[n * 2 + 1] = value;
return value;
}
@@ -723,11 +723,11 @@ public RubyString inspectObjectLinkedHashMap(VirtualFrame frame, RubyHash hash)
@CoreMethod(names = "key?", required = 1)
public abstract static class KeyNode extends HashCoreMethodNode {

@Child protected DispatchHeadNode eqlNode;
@Child protected PredicateDispatchHeadNode eqlNode;

public KeyNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
eqlNode = new DispatchHeadNode(context);
eqlNode = new PredicateDispatchHeadNode(context);
}

public KeyNode(KeyNode prev) {
@@ -748,7 +748,7 @@ public boolean keyObjectArray(VirtualFrame frame, RubyHash hash, Object key) {
final Object[] store = (Object[]) hash.getStore();

for (int n = 0; n < store.length; n += 2) {
if (n < size && eqlNode.callIsTruthy(frame, store[n], "eql?", null, key)) {
if (n < size && eqlNode.call(frame, store[n], "eql?", null, key)) {
return true;
}
}
@@ -896,7 +896,7 @@ public RubyArray mapObjectLinkedHashMap(VirtualFrame frame, RubyHash hash, RubyP
@CoreMethod(names = "merge", required = 1)
public abstract static class MergeNode extends HashCoreMethodNode {

@Child protected DispatchHeadNode eqlNode;
@Child protected PredicateDispatchHeadNode eqlNode;

private final BranchProfile nothingFromFirstProfile = BranchProfile.create();
private final BranchProfile considerNothingFromSecondProfile = BranchProfile.create();
@@ -908,7 +908,7 @@ public abstract static class MergeNode extends HashCoreMethodNode {

public MergeNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
eqlNode = new DispatchHeadNode(context);
eqlNode = new PredicateDispatchHeadNode(context);
}

public MergeNode(MergeNode prev) {
@@ -944,7 +944,7 @@ public RubyHash mergeObjectArrayObjectArray(VirtualFrame frame, RubyHash hash, R

for (int b = 0; b < RubyHash.HASHES_SMALL; b++) {
if (b < storeBSize) {
if (eqlNode.callIsTruthy(frame, storeA[a * 2], "eql?", null, storeB[b * 2])) {
if (eqlNode.call(frame, storeA[a * 2], "eql?", null, storeB[b * 2])) {
merge = false;
break;
}
23 changes: 12 additions & 11 deletions core/src/main/java/org/jruby/truffle/nodes/core/KernelNodes.java
Original file line number Diff line number Diff line change
@@ -26,6 +26,7 @@
import org.jruby.truffle.nodes.control.*;
import org.jruby.truffle.nodes.dispatch.Dispatch;
import org.jruby.truffle.nodes.dispatch.DispatchHeadNode;
import org.jruby.truffle.nodes.dispatch.PredicateDispatchHeadNode;
import org.jruby.truffle.nodes.literal.*;
import org.jruby.truffle.nodes.yield.*;
import org.jruby.truffle.runtime.*;
@@ -50,7 +51,7 @@ public abstract class KernelNodes {
public abstract static class SameOrEqualNode extends CoreMethodNode {

@Child protected BasicObjectNodes.ReferenceEqualNode referenceEqualNode;
@Child protected DispatchHeadNode equalNode;
@Child protected PredicateDispatchHeadNode equalNode;

public SameOrEqualNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
@@ -63,17 +64,17 @@ public SameOrEqualNode(SameOrEqualNode prev) {
protected boolean areSame(VirtualFrame frame, Object left, Object right) {
if (referenceEqualNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
referenceEqualNode = insert(BasicObjectNodesFactory.ReferenceEqualNodeFactory.create(getContext(), getSourceSection(), new RubyNode[]{null, null}));
referenceEqualNode = insert(BasicObjectNodesFactory.ReferenceEqualNodeFactory.create(getContext(), getSourceSection(), null, null));
}
return referenceEqualNode.executeReferenceEqual(frame, left, right);
}

protected boolean areEqual(VirtualFrame frame, Object left, Object right) {
if (equalNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
equalNode = insert(new DispatchHeadNode(getContext()));
equalNode = insert(new PredicateDispatchHeadNode(getContext()));
}
return equalNode.callIsTruthy(frame, left, "==", null, right);
return equalNode.call(frame, left, "==", null, right);
}

public abstract boolean executeSameOrEqual(VirtualFrame frame, Object a, Object b);
@@ -108,11 +109,11 @@ public RubyNilClass equal(Object other) {
@CoreMethod(names = "!~", required = 1)
public abstract static class NotMatchNode extends CoreMethodNode {

@Child protected DispatchHeadNode matchNode;
@Child protected PredicateDispatchHeadNode matchNode;

public NotMatchNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
matchNode = new DispatchHeadNode(context);
matchNode = new PredicateDispatchHeadNode(context);
}

public NotMatchNode(NotMatchNode prev) {
@@ -122,7 +123,7 @@ public NotMatchNode(NotMatchNode prev) {

@Specialization
public boolean notMatch(VirtualFrame frame, Object self, Object other) {
return !matchNode.callIsTruthy(frame, self, "=~", null, other);
return !matchNode.call(frame, self, "=~", null, other);
}

}
@@ -1082,7 +1083,7 @@ public RubyProc proc(RubyProc block) {

return new RubyProc(getContext().getCoreLibrary().getProcClass(), RubyProc.Type.LAMBDA,
block.getSharedMethodInfo(), block.getCallTargetForMethods(), block.getCallTargetForMethods(),
block.getDeclarationFrame(), block.getSelfCapturedInScope(), block.getBlockCapturedInScope());
block.getDeclarationFrame(), block.getDeclaringModule(), block.getMethod(), block.getSelfCapturedInScope(), block.getBlockCapturedInScope());
}
}

@@ -1318,7 +1319,7 @@ public RubyProc proc(RubyProc block) {

return new RubyProc(getContext().getCoreLibrary().getProcClass(), RubyProc.Type.PROC,
block.getSharedMethodInfo(), block.getCallTarget(), block.getCallTargetForMethods(), block.getDeclarationFrame(),
block.getSelfCapturedInScope(), block.getBlockCapturedInScope());
block.getDeclaringModule(), block.getMethod(), block.getSelfCapturedInScope(), block.getBlockCapturedInScope());
}
}

@@ -1480,8 +1481,8 @@ public abstract static class RespondToNode extends CoreMethodNode {
public RespondToNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);

dispatch = new DispatchHeadNode(context, false, Dispatch.MissingBehavior.CALL_METHOD_MISSING);
dispatchIgnoreVisibility = new DispatchHeadNode(context, true, Dispatch.MissingBehavior.CALL_METHOD_MISSING);
dispatch = new DispatchHeadNode(context, false, Dispatch.MissingBehavior.RETURN_MISSING);
dispatchIgnoreVisibility = new DispatchHeadNode(context, true, Dispatch.MissingBehavior.RETURN_MISSING);

if (Options.TRUFFLE_DISPATCH_METAPROGRAMMING_ALWAYS_UNCACHED.load()) {
dispatch.forceUncached();
Original file line number Diff line number Diff line change
@@ -40,7 +40,7 @@ public InitializeNode(InitializeNode prev) {
@Specialization
public RubyNilClass initialize(RubyProc proc, RubyProc block) {
proc.initialize(block.getSharedMethodInfo(), block.getCallTargetForMethods(), block.getCallTargetForMethods(),
block.getDeclarationFrame(), block.getSelfCapturedInScope(), block.getBlockCapturedInScope());
block.getDeclarationFrame(), block.getDeclaringModule(), block.getMethod(), block.getSelfCapturedInScope(), block.getBlockCapturedInScope());

return getContext().getCoreLibrary().getNilObject();
}
19 changes: 10 additions & 9 deletions core/src/main/java/org/jruby/truffle/nodes/core/RangeNodes.java
Original file line number Diff line number Diff line change
@@ -16,6 +16,7 @@
import com.oracle.truffle.api.utilities.*;
import org.jruby.truffle.nodes.RubyRootNode;
import org.jruby.truffle.nodes.dispatch.DispatchHeadNode;
import org.jruby.truffle.nodes.dispatch.PredicateDispatchHeadNode;
import org.jruby.truffle.runtime.*;
import org.jruby.truffle.runtime.core.*;
import org.jruby.truffle.runtime.core.RubyArray;
@@ -207,15 +208,15 @@ public Object each(RubyRange.ObjectRange range) {
@CoreMethod(names = {"include?", "==="}, optional = 1, lowerFixnumSelf = true, lowerFixnumParameters = 0)
public abstract static class IncludeNode extends CoreMethodNode {

@Child protected DispatchHeadNode callLess;
@Child protected DispatchHeadNode callGreater;
@Child protected DispatchHeadNode callGreaterEqual;
@Child protected PredicateDispatchHeadNode callLess;
@Child protected PredicateDispatchHeadNode callGreater;
@Child protected PredicateDispatchHeadNode callGreaterEqual;

public IncludeNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
callLess = new DispatchHeadNode(context);
callGreater = new DispatchHeadNode(context);
callGreaterEqual = new DispatchHeadNode(context);
callLess = new PredicateDispatchHeadNode(context);
callGreater = new PredicateDispatchHeadNode(context);
callGreaterEqual = new PredicateDispatchHeadNode(context);
}

public IncludeNode(IncludeNode prev) {
@@ -234,16 +235,16 @@ public boolean include(RubyRange.IntegerFixnumRange range, int value) {
public boolean include(VirtualFrame frame, RubyRange.ObjectRange range, Object value) {
notDesignedForCompilation();

if (callLess.callIsTruthy(frame, value, "<", null, range.getBegin())) {
if (callLess.call(frame, value, "<", null, range.getBegin())) {
return false;
}

if (range.doesExcludeEnd()) {
if (callGreaterEqual.callIsTruthy(frame, value, ">=", null, range.getEnd())) {
if (callGreaterEqual.call(frame, value, ">=", null, range.getEnd())) {
return false;
}
} else {
if (callGreater.callIsTruthy(frame, value, ">", null, range.getEnd())) {
if (callGreater.call(frame, value, ">", null, range.getEnd())) {
return false;
}
}
11 changes: 3 additions & 8 deletions core/src/main/java/org/jruby/truffle/nodes/core/RegexpNodes.java
Original file line number Diff line number Diff line change
@@ -15,7 +15,6 @@
import com.oracle.truffle.api.frame.VirtualFrame;
import org.jruby.runtime.Visibility;
import org.jruby.truffle.nodes.RubyNode;
import org.jruby.truffle.nodes.dispatch.DispatchHeadNode;
import org.jruby.truffle.runtime.*;
import org.jruby.truffle.runtime.core.*;
import org.jruby.util.ByteList;
@@ -79,7 +78,7 @@ public CaseEqualNode(CaseEqualNode prev) {
public Object match(RubyRegexp regexp, RubyString string) {
notDesignedForCompilation();

return regexp.matchOperator(string.toString()) != getContext().getCoreLibrary().getNilObject();
return regexp.matchCommon(string.getBytes(), true) != getContext().getCoreLibrary().getNilObject();
}

}
@@ -97,9 +96,7 @@ public MatchOperatorNode(MatchOperatorNode prev) {

@Specialization
public Object match(RubyRegexp regexp, RubyString string) {
notDesignedForCompilation();

return regexp.matchOperator(string.toString());
return regexp.matchCommon(string.getBytes(), true);
}

@Specialization
@@ -205,9 +202,7 @@ public MatchNode(MatchNode prev) {

@Specialization
public Object match(RubyRegexp regexp, RubyString string) {
notDesignedForCompilation();

return regexp.match(string);
return regexp.matchCommon(string.getBytes(), false);
}

}
10 changes: 3 additions & 7 deletions core/src/main/java/org/jruby/truffle/nodes/core/StringNodes.java
Original file line number Diff line number Diff line change
@@ -257,9 +257,7 @@ public MatchOperatorNode(MatchOperatorNode prev) {

@Specialization
public Object match(RubyString string, RubyRegexp regexp) {
notDesignedForCompilation();

return regexp.matchOperator(string.toString());
return regexp.matchCommon(string.getBytes(), true);
}
}

@@ -647,14 +645,12 @@ public Object match(RubyString string, RubyString regexpString) {
notDesignedForCompilation();

final RubyRegexp regexp = new RubyRegexp(this, getContext().getCoreLibrary().getRegexpClass(), regexpString.toString(), Option.DEFAULT);
return regexp.match(string);
return regexp.matchCommon(string.getBytes(), false);
}

@Specialization
public Object match(RubyString string, RubyRegexp regexp) {
notDesignedForCompilation();

return regexp.match(string);
return regexp.matchCommon(string.getBytes(), false);
}
}

Original file line number Diff line number Diff line change
@@ -137,7 +137,7 @@ public ParseTreeNode(ParseTreeNode prev) {
public Object parseTree() {
notDesignedForCompilation();

final org.jruby.ast.Node parseTree = RubyCallStack.getCurrentMethod().getSharedMethodInfo().getParseTree();
final org.jruby.ast.Node parseTree = RubyCallStack.getCallingMethod().getSharedMethodInfo().getParseTree();

if (parseTree == null) {
return getContext().getCoreLibrary().getNilObject();
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright (c) 2013, 2014 Oracle and/or its affiliates. All rights reserved. This
* code is released under a tri EPL/GPL/LGPL license. You can use it,
* redistribute it and/or modify it under the terms of the:
*
* Eclipse Public License version 1.0
* GNU General Public License version 2
* GNU Lesser General Public License version 2.1
*/
package org.jruby.truffle.nodes.core;

import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.NodeChildren;
import com.oracle.truffle.api.source.SourceSection;
import org.jruby.truffle.nodes.RubyNode;
import org.jruby.truffle.runtime.RubyContext;

@NodeChild(value = "operand", type = RubyNode.class)
public abstract class UnaryCoreMethodNode extends RubyNode {

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

public UnaryCoreMethodNode(UnaryCoreMethodNode prev) {
super(prev);
}

public abstract RubyNode getOperand();

}
Original file line number Diff line number Diff line change
@@ -68,15 +68,6 @@ public Object call(
Dispatch.DispatchAction.CALL_METHOD);
}

public boolean callIsTruthy(
VirtualFrame frame,
Object receiverObject,
Object methodName,
RubyProc blockObject,
Object... argumentsObjects) {
return context.getCoreLibrary().isTruthy(call(frame, receiverObject, methodName, blockObject, argumentsObjects));
}

public double callFloat(
VirtualFrame frame,
Object receiverObject,
@@ -145,15 +136,15 @@ public boolean doesRespondTo(
VirtualFrame frame,
Object methodName,
Object receiverObject) {
return context.getCoreLibrary().isTruthy(dispatch(
return (boolean) dispatch(
frame,
context.getCoreLibrary().getNilObject(),
null, // TODO(eregon): was RubyArguments.getSelf(frame.getArguments()),
receiverObject,
methodName,
null,
null,
Dispatch.DispatchAction.RESPOND_TO_METHOD));
Dispatch.DispatchAction.RESPOND_TO_METHOD);
}

public Object dispatch(
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright (c) 2014 Oracle and/or its affiliates. All rights reserved. This
* code is released under a tri EPL/GPL/LGPL license. You can use it,
* redistribute it and/or modify it under the terms of the:
*
* Eclipse Public License version 1.0
* GNU General Public License version 2
* GNU Lesser General Public License version 2.1
*/
package org.jruby.truffle.nodes.dispatch;

import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.Node;
import org.jruby.truffle.nodes.cast.BooleanCastNode;
import org.jruby.truffle.nodes.cast.BooleanCastNodeFactory;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.truffle.runtime.core.RubyProc;

public class PredicateDispatchHeadNode extends Node {

@Child protected DispatchHeadNode dispatchNode;
@Child protected BooleanCastNode booleanCastNode;

public PredicateDispatchHeadNode(RubyContext context) {
dispatchNode = new DispatchHeadNode(context);
booleanCastNode = BooleanCastNodeFactory.create(context, getSourceSection(), null);
}

public boolean call(
VirtualFrame frame,
Object receiverObject,
Object methodName,
RubyProc blockObject,
Object... argumentsObjects) {
return booleanCastNode.executeBoolean(frame,
dispatchNode.call(frame, receiverObject, methodName, blockObject, argumentsObjects));
}
}
Original file line number Diff line number Diff line change
@@ -36,4 +36,9 @@ public boolean executeBoolean(VirtualFrame frame) {
return value;
}

@Override
public Object isDefined(VirtualFrame frame) {
return getContext().makeString(value ? "true" : "false");
}

}
Original file line number Diff line number Diff line change
@@ -14,6 +14,7 @@
import com.oracle.truffle.api.nodes.*;
import org.jruby.truffle.nodes.*;
import org.jruby.truffle.nodes.dispatch.DispatchHeadNode;
import org.jruby.truffle.nodes.dispatch.PredicateDispatchHeadNode;
import org.jruby.truffle.runtime.*;
import org.jruby.truffle.runtime.core.RubyHash;
import org.jruby.truffle.runtime.core.RubyString;
@@ -75,11 +76,11 @@ public RubyHash executeRubyHash(VirtualFrame frame) {

public static class SmallHashLiteralNode extends HashLiteralNode {

@Child protected DispatchHeadNode equalNode;
@Child protected PredicateDispatchHeadNode equalNode;

public SmallHashLiteralNode(RubyContext context, SourceSection sourceSection, RubyNode[] keyValues) {
super(context, sourceSection, keyValues);
equalNode = new DispatchHeadNode(context);
equalNode = new PredicateDispatchHeadNode(context);
}

@ExplodeLoop
@@ -99,7 +100,7 @@ public RubyHash executeRubyHash(VirtualFrame frame) {
final Object value = keyValues[n + 1].execute(frame);

for (int i = 0; i < n; i += 2) {
if (equalNode.callIsTruthy(frame, key, "eql?", null, storage[i])) {
if (equalNode.call(frame, key, "eql?", null, storage[i])) {
storage[i + 1] = value;
continue initializers;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright (c) 2013, 2014 Oracle and/or its affiliates. All rights reserved. This
* code is released under a tri EPL/GPL/LGPL license. You can use it,
* redistribute it and/or modify it under the terms of the:
*
* Eclipse Public License version 1.0
* GNU General Public License version 2
* GNU Lesser General Public License version 2.1
*/
package org.jruby.truffle.nodes.literal;

import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.NodeCost;
import com.oracle.truffle.api.nodes.NodeInfo;
import com.oracle.truffle.api.source.SourceSection;
import org.jruby.truffle.nodes.RubyNode;
import org.jruby.truffle.runtime.RubyContext;

@NodeInfo(cost = NodeCost.NONE)
public class NilLiteralNode extends RubyNode {

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

@Override
public Object execute(VirtualFrame frame) {
return getContext().getCoreLibrary().getNilObject();
}

@Override
public Object isDefined(VirtualFrame frame) {
return getContext().makeString("nil");
}

}
Original file line number Diff line number Diff line change
@@ -46,8 +46,18 @@ public Object execute(VirtualFrame frame) {
declarationFrame = null;
}

final MethodLike methodLike = RubyArguments.getMethod(frame.getArguments());

final RubyModule declaringModule;

if (methodLike == null) {
declaringModule = null;
} else {
declaringModule = methodLike.getDeclaringModule();
}

return new RubyProc(getContext().getCoreLibrary().getProcClass(), RubyProc.Type.PROC, sharedMethodInfo,
callTarget, callTargetForMethods, declarationFrame, RubyArguments.getSelf(frame.getArguments()),
callTarget, callTargetForMethods, declarationFrame, declaringModule, RubyArguments.getMethod(frame.getArguments()), RubyArguments.getSelf(frame.getArguments()),
RubyArguments.getBlock(frame.getArguments()));
}

Original file line number Diff line number Diff line change
@@ -15,6 +15,7 @@
import com.oracle.truffle.api.frame.*;
import org.jruby.truffle.nodes.*;
import org.jruby.truffle.runtime.*;
import org.jruby.truffle.translator.BodyTranslator;

public abstract class ReadLevelVariableNode extends FrameSlotNode implements ReadNode {

@@ -64,4 +65,18 @@ public RubyNode makeWriteNode(RubyNode rhs) {
return WriteLevelVariableNodeFactory.create(getContext(), getSourceSection(), frameSlot, varLevel, rhs);
}

@Override
public Object isDefined(VirtualFrame frame) {
// TODO(CS): copy and paste of ReadLocalVariableNode
if (BodyTranslator.FRAME_LOCAL_GLOBAL_VARIABLES.contains(frameSlot.getIdentifier())) {
if (ReadLocalVariableNode.ALWAYS_DEFINED_GLOBALS.contains(frameSlot.getIdentifier()) || doObject(frame) != getContext().getCoreLibrary().getNilObject()) {
return getContext().makeString("global-variable");
} else {
return getContext().getCoreLibrary().getNilObject();
}
} else {
return getContext().makeString("local-variable");
}
}

}
Original file line number Diff line number Diff line change
@@ -17,6 +17,10 @@
import org.jruby.truffle.runtime.*;
import org.jruby.truffle.translator.BodyTranslator;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

public abstract class ReadLocalVariableNode extends FrameSlotNode implements ReadNode {

public ReadLocalVariableNode(RubyContext context, SourceSection sourceSection, FrameSlot slot) {
@@ -57,13 +61,16 @@ public RubyNode makeWriteNode(RubyNode rhs) {
return WriteLocalVariableNodeFactory.create(getContext(), getSourceSection(), frameSlot, rhs);
}

public static final Set<String> ALWAYS_DEFINED_GLOBALS = new HashSet<>(Arrays.asList("$~"));

@Override
public Object isDefined(VirtualFrame frame) {
// TODO(CS): copy and paste of ReadLevelVariableNode
if (BodyTranslator.FRAME_LOCAL_GLOBAL_VARIABLES.contains(frameSlot.getIdentifier())) {
if (frameSlot.getIdentifier().equals("$+") && getObject(frame) == getContext().getCoreLibrary().getNilObject()) {
return getContext().getCoreLibrary().getNilObject();
} else {
if (ALWAYS_DEFINED_GLOBALS.contains(frameSlot.getIdentifier()) || doObject(frame) != getContext().getCoreLibrary().getNilObject()) {
return getContext().makeString("global-variable");
} else {
return getContext().getCoreLibrary().getNilObject();
}
} else {
return getContext().makeString("local-variable");
Original file line number Diff line number Diff line change
@@ -69,4 +69,10 @@ public Object doObject(VirtualFrame frame, Object value) {
public RubyNode makeReadNode() {
return ReadLevelVariableNodeFactory.create(getContext(), getSourceSection(), frameSlot, varLevel);
}

@Override
public Object isDefined(VirtualFrame frame) {
return getContext().makeString("assignment");
}

}
Original file line number Diff line number Diff line change
@@ -62,4 +62,9 @@ public RubyNode makeReadNode() {
return ReadLocalVariableNodeFactory.create(getContext(), getSourceSection(), frameSlot);
}

@Override
public Object isDefined(VirtualFrame frame) {
return getContext().makeString("assignment");
}

}
Original file line number Diff line number Diff line change
@@ -32,8 +32,6 @@ public Object execute(VirtualFrame frame) {

@Override
public Object isDefined(VirtualFrame frame) {
notDesignedForCompilation();

return getContext().makeString("self");
}

Original file line number Diff line number Diff line change
@@ -46,4 +46,9 @@ public Object execute(VirtualFrame frame) {
return rhsValue;
}

@Override
public Object isDefined(VirtualFrame frame) {
return getContext().makeString("assignment");
}

}
Original file line number Diff line number Diff line change
@@ -113,4 +113,10 @@ public Object execute(VirtualFrame frame) {
public RubyNode makeReadNode() {
return new ReadInstanceVariableNode(getContext(), getSourceSection(), writeNode.getName(), receiver, isGlobal);
}

@Override
public Object isDefined(VirtualFrame frame) {
return getContext().makeString("assignment");
}

}
Original file line number Diff line number Diff line change
@@ -31,6 +31,7 @@ public RespondToNode(RubyContext context, SourceSection sourceSection, RubyNode
}

public boolean executeBoolean(VirtualFrame frame) {
// TODO(cseaton): check this is actually a static "find if there is such method" and not a dynamic call to respond_to?
return dispatch.doesRespondTo(frame, methodName, child.execute(frame));
}

Original file line number Diff line number Diff line change
@@ -10,6 +10,7 @@
package org.jruby.truffle.nodes.supercall;

import com.oracle.truffle.api.*;
import com.oracle.truffle.api.frame.FrameInstance;
import com.oracle.truffle.api.source.*;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.DirectCallNode;
@@ -19,21 +20,20 @@
import org.jruby.truffle.runtime.core.RubyBasicObject;
import org.jruby.truffle.runtime.core.RubyClass;
import org.jruby.truffle.runtime.core.RubyModule;
import org.jruby.truffle.runtime.core.RubyProc;
import org.jruby.truffle.runtime.methods.MethodLike;
import org.jruby.truffle.runtime.methods.RubyMethod;
import org.jruby.truffle.runtime.LexicalScope;

public abstract class AbstractGeneralSuperCallNode extends RubyNode {

private final String name;

@Child protected DirectCallNode callNode;

@CompilerDirectives.CompilationFinal protected Assumption unmodifiedAssumption;
@CompilerDirectives.CompilationFinal protected RubyMethod method;

public AbstractGeneralSuperCallNode(RubyContext context, SourceSection sourceSection, String name) {
public AbstractGeneralSuperCallNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
this.name = name;
}

protected boolean guard() {
@@ -44,8 +44,17 @@ protected boolean guard() {
protected void lookup(VirtualFrame frame) {
CompilerAsserts.neverPartOfCompilation();

final FrameInstance currentFrame = Truffle.getRuntime().getCurrentFrame();
MethodLike methodLike = RubyCallStack.getMethod(currentFrame);

while (!(methodLike instanceof RubyMethod)) {
methodLike = ((RubyProc) methodLike).getMethod();
}

final String name = ((RubyMethod) methodLike).getName();

// TODO: this is wrong, we need the lexically enclosing method (or define_method)'s module
final RubyModule declaringModule = RubyCallStack.getCurrentMethod().getDeclaringModule();
final RubyModule declaringModule = RubyCallStack.getCurrentDeclaringModule();
final RubyClass selfMetaClass = getContext().getCoreLibrary().getMetaClass(RubyArguments.getSelf(frame.getArguments()));

method = ModuleOperations.lookupSuperMethod(declaringModule, name, selfMetaClass);
Original file line number Diff line number Diff line change
@@ -29,8 +29,8 @@ public class GeneralSuperCallNode extends AbstractGeneralSuperCallNode {
@Child protected RubyNode block;
@Children protected final RubyNode[] arguments;

public GeneralSuperCallNode(RubyContext context, SourceSection sourceSection, String name, RubyNode block, RubyNode[] arguments, boolean isSplatted) {
super(context, sourceSection, name);
public GeneralSuperCallNode(RubyContext context, SourceSection sourceSection, RubyNode block, RubyNode[] arguments, boolean isSplatted) {
super(context, sourceSection);
assert arguments != null;
assert !isSplatted || arguments.length == 1;
this.block = block;
Original file line number Diff line number Diff line change
@@ -19,8 +19,8 @@ public class GeneralSuperReCallNode extends AbstractGeneralSuperCallNode {

private final boolean inBlock;

public GeneralSuperReCallNode(RubyContext context, SourceSection sourceSection, String name, boolean inBlock) {
super(context, sourceSection, name);
public GeneralSuperReCallNode(RubyContext context, SourceSection sourceSection, boolean inBlock) {
super(context, sourceSection);
this.inBlock = inBlock;
}

13 changes: 8 additions & 5 deletions core/src/main/java/org/jruby/truffle/runtime/RubyCallStack.java
Original file line number Diff line number Diff line change
@@ -27,14 +27,17 @@

public abstract class RubyCallStack {

public static RubyModule getCurrentDeclaringModule() {
final FrameInstance currentFrame = Truffle.getRuntime().getCurrentFrame();
final MethodLike method = getMethod(currentFrame);
return method.getDeclaringModule();
}

public static RubyMethod getCurrentMethod() {
CompilerAsserts.neverPartOfCompilation();

MethodLike method;

final FrameInstance currentFrame = Truffle.getRuntime().getCurrentFrame();

method = getMethod(currentFrame);
final MethodLike method = getMethod(currentFrame);

if (method instanceof RubyMethod) {
return (RubyMethod) method;
@@ -92,7 +95,7 @@ public RubyMethod visitFrame(FrameInstance frameInstance) {
});
}

private static MethodLike getMethod(FrameInstance frame) {
public static MethodLike getMethod(FrameInstance frame) {
CompilerAsserts.neverPartOfCompilation();

if (frame == null) {
12 changes: 0 additions & 12 deletions core/src/main/java/org/jruby/truffle/runtime/core/CoreLibrary.java
Original file line number Diff line number Diff line change
@@ -405,18 +405,6 @@ public RubyClass getLogicalClass(Object object) {
}
}

/**
* Convert a value to a boolean according to Ruby rules. Never fails.
*/
public boolean isTruthy(Object value) {
// TODO(CS): mark are neverPartOfCompilation
if (value instanceof Boolean) {
return (boolean) value;
} else {
return value != nilObject;
}
}

public RubyException runtimeError(String message, Node currentNode) {
CompilerAsserts.neverPartOfCompilation();
return new RubyException(runtimeErrorClass, context.makeString(String.format("%s", message)), RubyCallStack.getBacktrace(currentNode));
19 changes: 16 additions & 3 deletions core/src/main/java/org/jruby/truffle/runtime/core/RubyProc.java
Original file line number Diff line number Diff line change
@@ -56,6 +56,8 @@ public static enum Type {
/** Call target for lambdas and methods, which have strict arguments destructuring */
@CompilationFinal private CallTarget callTargetForMethods;
@CompilationFinal private MaterializedFrame declarationFrame;
@CompilationFinal private RubyModule declaringModule;
@CompilationFinal private MethodLike method;
@CompilationFinal private Object self;
@CompilationFinal private RubyProc block;

@@ -65,17 +67,19 @@ public RubyProc(RubyClass procClass, Type type) {
}

public RubyProc(RubyClass procClass, Type type, SharedMethodInfo sharedMethodInfo, CallTarget callTarget,
CallTarget callTargetForMethods, MaterializedFrame declarationFrame, Object self, RubyProc block) {
CallTarget callTargetForMethods, MaterializedFrame declarationFrame, RubyModule declaringModule, MethodLike method, Object self, RubyProc block) {
this(procClass, type);
initialize(sharedMethodInfo, callTarget, callTargetForMethods, declarationFrame, self, block);
initialize(sharedMethodInfo, callTarget, callTargetForMethods, declarationFrame, declaringModule, method, self, block);
}

public void initialize(SharedMethodInfo sharedMethodInfo, CallTarget callTarget, CallTarget callTargetForMethods,
MaterializedFrame declarationFrame, Object self, RubyProc block) {
MaterializedFrame declarationFrame, RubyModule declaringModule, MethodLike method, Object self, RubyProc block) {
this.sharedMethodInfo = sharedMethodInfo;
this.callTarget = callTarget;
this.callTargetForMethods = callTargetForMethods;
this.declarationFrame = declarationFrame;
this.declaringModule = declaringModule;
this.method = method;
this.self = self;
this.block = block;
}
@@ -119,6 +123,15 @@ public MaterializedFrame getDeclarationFrame() {
return declarationFrame;
}

@Override
public RubyModule getDeclaringModule() {
return declaringModule;
}

public MethodLike getMethod() {
return method;
}

public Object getSelfCapturedInScope() {
return self;
}
135 changes: 59 additions & 76 deletions core/src/main/java/org/jruby/truffle/runtime/core/RubyRegexp.java
Original file line number Diff line number Diff line change
@@ -20,7 +20,9 @@
import org.joni.*;
import org.joni.exception.ValueException;
import org.jruby.truffle.nodes.RubyNode;
import org.jruby.truffle.runtime.RubyArguments;
import org.jruby.truffle.runtime.RubyContext;
import org.jruby.util.ByteList;

import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
@@ -80,127 +82,108 @@ public Regex getRegex() {
return regex;
}

@CompilerDirectives.TruffleBoundary
public String getSource() {
return source;
}

@CompilerDirectives.TruffleBoundary
public Object matchOperator(String string) {
// TODO(CS) merge with match
public Object matchCommon(ByteList bytes, boolean operator) {
final RubyContext context = getContext();

final Frame frame = Truffle.getRuntime().getCallerFrame().getFrame(FrameInstance.FrameAccess.READ_WRITE, false);

final RubyContext context = getContext();

final byte[] stringBytes = string.getBytes(StandardCharsets.UTF_8);
final byte[] stringBytes = bytes.bytes();
final Matcher matcher = regex.matcher(stringBytes);
final int match = matcher.search(0, stringBytes.length, Option.DEFAULT);

if (match != -1) {
final Region region = matcher.getEagerRegion();
final RubyNilClass nil = getContext().getCoreLibrary().getNilObject();

final Object[] values = new Object[region.numRegs];
if (operator) {
for (int n = 0; n < 10; n++) {
set(frame, "$" + n, nil);
}
}

for (int n = 0; n < region.numRegs; n++) {
final int start = region.beg[n];
final int end = region.end[n];
if (match == -1) {
set(frame, "$&", nil);
set(frame, "$~", nil);
set(frame, "$`", nil);
set(frame, "$'", nil);

if (operator) {
set(frame, "$+", nil);
}

return getContext().getCoreLibrary().getNilObject();
}

final Region region = matcher.getEagerRegion();
final Object[] values = new Object[region.numRegs];

for (int n = 0; n < region.numRegs; n++) {
final int start = region.beg[n];
final int end = region.end[n];

if (operator) {
final Object groupString;

if (start > -1 && end > -1) {
groupString = context.makeString(string.substring(start, end));
groupString = new RubyString(context.getCoreLibrary().getStringClass(), bytes.makeShared(start, end - start).dup());
} else {
groupString = getContext().getCoreLibrary().getNilObject();
}

values[n] = groupString;

final FrameSlot slot = frame.getFrameDescriptor().findFrameSlot("$" + n);

if (slot != null) {
frame.setObject(slot, groupString);
set(frame, "$" + n, groupString);
} else {
if (start == -1 || end == -1) {
values[n] = getContext().getCoreLibrary().getNilObject();
} else {
final RubyString groupString = new RubyString(context.getCoreLibrary().getStringClass(), bytes.makeShared(start, end - start).dup());
values[n] = groupString;
}
}
}

if (values.length > 0) {
final FrameSlot slot = frame.getFrameDescriptor().findFrameSlot("$+");
final RubyMatchData matchObject = new RubyMatchData(context.getCoreLibrary().getMatchDataClass(), values);

if (operator) {
if (values.length > 0) {
int nonNil = values.length - 1;

while (values[nonNil] == getContext().getCoreLibrary().getNilObject()) {
nonNil--;
}

if (slot != null) {
frame.setObject(slot, values[nonNil]);
}
set(frame, "$+", values[nonNil]);
} else {
set(frame, "$+", getContext().getCoreLibrary().getNilObject());
}
}

final RubyMatchData matchObject = new RubyMatchData(context.getCoreLibrary().getMatchDataClass(), values);

final FrameSlot slot = frame.getFrameDescriptor().findFrameSlot("$~");
set(frame, "$`", new RubyString(context.getCoreLibrary().getStringClass(), bytes.makeShared(0, region.beg[0]).dup()));
set(frame, "$'", new RubyString(context.getCoreLibrary().getStringClass(), bytes.makeShared(region.end[0], bytes.length() - region.end[0]).dup()));
set(frame, "$&", new RubyString(context.getCoreLibrary().getStringClass(), bytes.makeShared(region.beg[0], region.end[0] - region.beg[0]).dup()));

if (slot != null) {
frame.setObject(slot, matchObject);
}
set(frame, "$~", matchObject);

if (operator) {
return matcher.getBegin();
} else {
final FrameSlot slot = frame.getFrameDescriptor().findFrameSlot("$~");

if (slot != null) {
frame.setObject(slot, getContext().getCoreLibrary().getNilObject());
}

return getContext().getCoreLibrary().getNilObject();
return matchObject;
}
}

@CompilerDirectives.TruffleBoundary
public Object match(RubyString string) {
final RubyContext context = getContext();

final Frame frame = Truffle.getRuntime().getCallerFrame().getFrame(FrameInstance.FrameAccess.READ_WRITE, false);

final byte[] stringBytes = string.getBytes().bytes();
final Matcher matcher = regex.matcher(stringBytes);
final int match = matcher.search(0, stringBytes.length, Option.DEFAULT);

if (match != -1) {
final Region region = matcher.getEagerRegion();

final Object[] values = new Object[region.numRegs];

for (int n = 0; n < region.numRegs; n++) {
final int start = region.beg[n];
final int end = region.end[n];

if (start == -1 || end == -1) {
values[n] = getContext().getCoreLibrary().getNilObject();
} else {
final RubyString groupString = new RubyString(context.getCoreLibrary().getStringClass(), string.getBytes().makeShared(start, end - start).dup());
values[n] = groupString;
}
}

final RubyMatchData matchObject = new RubyMatchData(context.getCoreLibrary().getMatchDataClass(), values);

final FrameSlot slot = frame.getFrameDescriptor().findFrameSlot("$~");
private void set(Frame frame, String name, Object value) {
while (frame != null) {
final FrameSlot slot = frame.getFrameDescriptor().findFrameSlot(name);

if (slot != null) {
frame.setObject(slot, matchObject);
}

return matchObject;
} else {
final FrameSlot slot = frame.getFrameDescriptor().findFrameSlot("$~");

if (slot != null) {
frame.setObject(slot, getContext().getCoreLibrary().getNilObject());
frame.setObject(slot, value);
break;
}

return getContext().getCoreLibrary().getNilObject();
frame = RubyArguments.getDeclarationFrame(frame.getArguments());
}
}

Original file line number Diff line number Diff line change
@@ -53,7 +53,7 @@ public RubyProc toProc(SourceSection sourceSection, final RubyNode currentNode)
final CallTarget callTarget = Truffle.getRuntime().createCallTarget(rootNode);

return new RubyProc(context.getCoreLibrary().getProcClass(), RubyProc.Type.PROC, sharedMethodInfo, callTarget,
callTarget, null, getContext().getCoreLibrary().getNilObject(), null);
callTarget, null, null, null, getContext().getCoreLibrary().getNilObject(), null);
}

public org.jruby.RubySymbol getJRubySymbol() {
Original file line number Diff line number Diff line change
@@ -9,8 +9,11 @@
*/
package org.jruby.truffle.runtime.methods;

import org.jruby.truffle.runtime.core.RubyModule;

public interface MethodLike {

SharedMethodInfo getSharedMethodInfo();
RubyModule getDeclaringModule();

}
Original file line number Diff line number Diff line change
@@ -48,6 +48,7 @@ public SharedMethodInfo getSharedMethodInfo() {
return sharedMethodInfo;
}

@Override
public RubyModule getDeclaringModule() { return declaringModule; }

public String getName() {
123 changes: 67 additions & 56 deletions core/src/main/java/org/jruby/truffle/translator/BodyTranslator.java
Original file line number Diff line number Diff line change
@@ -40,18 +40,15 @@
import org.jruby.truffle.nodes.literal.*;
import org.jruby.truffle.nodes.literal.ArrayLiteralNode;
import org.jruby.truffle.nodes.methods.*;
import org.jruby.truffle.nodes.methods.AliasNode;
import org.jruby.truffle.nodes.methods.UndefNode;
import org.jruby.truffle.nodes.methods.locals.*;
import org.jruby.truffle.nodes.objects.*;
import org.jruby.truffle.nodes.objects.ClassNode;
import org.jruby.truffle.nodes.objects.SelfNode;
import org.jruby.truffle.nodes.yield.YieldNode;
import org.jruby.truffle.runtime.*;
import org.jruby.truffle.runtime.LexicalScope;
import org.jruby.truffle.runtime.core.*;
import org.jruby.truffle.runtime.methods.*;
import org.jruby.util.ByteList;
import org.jruby.util.KeyValuePair;
import org.jruby.util.cli.Options;

@@ -72,28 +69,6 @@ public class BodyTranslator extends Translator {
private boolean translatingNextExpression = false;
private String currentCallMethodName = null;

private static final Map<Class, String> nodeDefinedNames = new HashMap<>();

static {
// TODO(CS): this was a temporary hack and needs to be removed
nodeDefinedNames.put(org.jruby.ast.SelfNode.class, "self");
nodeDefinedNames.put(org.jruby.ast.NilNode.class, "nil");
nodeDefinedNames.put(org.jruby.ast.TrueNode.class, "true");
nodeDefinedNames.put(org.jruby.ast.FalseNode.class, "false");
nodeDefinedNames.put(org.jruby.ast.LocalAsgnNode.class, "assignment");
nodeDefinedNames.put(org.jruby.ast.DAsgnNode.class, "assignment");
nodeDefinedNames.put(org.jruby.ast.GlobalAsgnNode.class, "assignment");
nodeDefinedNames.put(org.jruby.ast.InstAsgnNode.class, "assignment");
nodeDefinedNames.put(org.jruby.ast.ClassVarAsgnNode.class, "assignment");
nodeDefinedNames.put(org.jruby.ast.OpAsgnAndNode.class, "assignment");
nodeDefinedNames.put(org.jruby.ast.OpAsgnOrNode.class, "assignment");
nodeDefinedNames.put(org.jruby.ast.OpAsgnNode.class, "assignment");
nodeDefinedNames.put(org.jruby.ast.OpElementAsgnNode.class, "assignment");
nodeDefinedNames.put(org.jruby.ast.MultipleAsgn19Node.class, "assignment");
nodeDefinedNames.put(org.jruby.ast.LocalVarNode.class, "local-variable");
nodeDefinedNames.put(org.jruby.ast.DVarNode.class, "local-variable");
}

private static final Set<String> debugIgnoredCalls = new HashSet<>();

static {
@@ -106,7 +81,7 @@ public class BodyTranslator extends Translator {
/**
* Global variables which in common usage have frame local semantics.
*/
public static final Set<String> FRAME_LOCAL_GLOBAL_VARIABLES = new HashSet<>(Arrays.asList("$_", "$~", "$+"));
public static final Set<String> FRAME_LOCAL_GLOBAL_VARIABLES = new HashSet<>(Arrays.asList("$_", "$~", "$+", "$&", "$`", "$'", "$1", "$2", "$3", "$4", "$5", "$6", "$7", "$8", "$9"));

public BodyTranslator(RubyNode currentNode, RubyContext context, BodyTranslator parent, TranslatorEnvironment environment, Source source) {
super(currentNode, context, source);
@@ -658,6 +633,14 @@ private RubyNode translateCPath(SourceSection sourceSection, org.jruby.ast.Colon
}
}

@Override
public RubyNode visitComplexNode(ComplexNode node) {
final SourceSection sourceSection = translate(node.getPosition());

// TODO: implement Complex
return node.getNumber().accept(this);
}

@Override
public RubyNode visitConstDeclNode(org.jruby.ast.ConstDeclNode node) {
final SourceSection sourceSection = translate(node.getPosition());
@@ -768,13 +751,6 @@ public RubyNode visitDefinedNode(org.jruby.ast.DefinedNode node) {
expressionNode = ((org.jruby.ast.NewlineNode) expressionNode).getNextNode();
}

final String name = nodeDefinedNames.get(expressionNode.getClass());

if (name != null) {
final StringLiteralNode literal = new StringLiteralNode(context, sourceSection, ByteList.create(name));
return literal;
}

return new DefinedNode(context, sourceSection, node.getExpressionNode().accept(this));
}

@@ -1073,7 +1049,7 @@ public RubyNode visitGlobalVarNode(org.jruby.ast.GlobalVarNode node) {
if (FRAME_LOCAL_GLOBAL_VARIABLES.contains(name)) {
// Assignment is implicit for many of these, so we need to declare when we use

environment.declareVar(name);
environment.declareVarWhereAllowed(name);

final RubyNode readNode = environment.findLocalVarNode(name, sourceSection);

@@ -1348,6 +1324,8 @@ public RubyNode visitMultipleAsgnNode(org.jruby.ast.MultipleAsgn19Node node) {
rhsTranslated = rhs.accept(this);
}

final RubyNode result;

if (preArray != null
&& node.getPost() == null
&& node.getRest() == null
@@ -1393,7 +1371,7 @@ public RubyNode visitMultipleAsgnNode(org.jruby.ast.MultipleAsgn19Node node) {

final ElidableResultNode elidableResult = new ElidableResultNode(context, sourceSection, blockNode, arrayNode);

return elidableResult;
result = elidableResult;
} else if (preArray != null) {
/*
* The other simple case is
@@ -1450,11 +1428,11 @@ public RubyNode visitMultipleAsgnNode(org.jruby.ast.MultipleAsgn19Node node) {
sequence.add(translateDummyAssignment(node.getRest(), assignedValue));
}

return SequenceNode.sequence(context, sourceSection, sequence);
result = SequenceNode.sequence(context, sourceSection, sequence);
} else if (node.getPre() == null
&& node.getPost() == null
&& node.getRest() instanceof org.jruby.ast.StarNode) {
return rhsTranslated;
result = rhsTranslated;
} else if (node.getPre() == null
&& node.getPost() == null
&& node.getRest() != null
@@ -1487,7 +1465,7 @@ public RubyNode visitMultipleAsgnNode(org.jruby.ast.MultipleAsgn19Node node) {

final SplatCastNode rhsSplatCast = SplatCastNodeFactory.create(context, sourceSection, translatingNextExpression ? SplatCastNode.NilBehavior.EMPTY_ARRAY : SplatCastNode.NilBehavior.ARRAY_WITH_NIL, rhsTranslated);

return restRead.makeWriteNode(rhsSplatCast);
result = restRead.makeWriteNode(rhsSplatCast);
} else if (node.getPre() == null
&& node.getPost() == null
&& node.getRest() != null
@@ -1518,7 +1496,7 @@ public RubyNode visitMultipleAsgnNode(org.jruby.ast.MultipleAsgn19Node node) {
throw new RuntimeException("Unknown form of multiple assignment " + node + " at " + node.getPosition());
}

return restRead.makeWriteNode(rhsTranslated);
result = restRead.makeWriteNode(rhsTranslated);
} else if (node.getPre() == null && node.getRest() != null && node.getPost() != null) {
/*
* Something like
@@ -1563,11 +1541,13 @@ public RubyNode visitMultipleAsgnNode(org.jruby.ast.MultipleAsgn19Node node) {
sequence.add(translateDummyAssignment(postArray.get(n), assignedValue));
}

return SequenceNode.sequence(context, sourceSection, sequence);
result = SequenceNode.sequence(context, sourceSection, sequence);
} else {
context.getRuntime().getWarnings().warn(IRubyWarnings.ID.TRUFFLE, node.getPosition().getFile(), node.getPosition().getLine(), node + " unknown form of multiple assignment");
return new ObjectLiteralNode(context, sourceSection, context.getCoreLibrary().getNilObject());
result = new ObjectLiteralNode(context, sourceSection, context.getCoreLibrary().getNilObject());
}

return new AssignmentWrapperNode(context, sourceSection, result);
}

private RubyNode translateDummyAssignment(org.jruby.ast.Node dummyAssignment, RubyNode rhs) {
@@ -1672,35 +1652,57 @@ public RubyNode visitNilNode(org.jruby.ast.NilNode node) {
return new DeadNode(context, null);
}

return new ObjectLiteralNode(context, translate(node.getPosition()), context.getCoreLibrary().getNilObject());
return new NilLiteralNode(context, translate(node.getPosition()));
}

@Override
public RubyNode visitNthRefNode(org.jruby.ast.NthRefNode node) {
final SourceSection sourceSection = translate(node.getPosition());

final String name = "$" + node.getMatchNumber();

RubyNode readLocal = environment.findLocalVarNode(name, sourceSection);
// This is wrong I think - should reference one of the existing global variables or something like that

if (readLocal == null) {
environment.declareVar(name);
readLocal = environment.findLocalVarNode(name, sourceSection);
}
final String name = "$" + node.getMatchNumber();

return readLocal;
return new GlobalVarNode(node.getPosition(), name).accept(this);
}

@Override
public RubyNode visitOpAsgnAndNode(org.jruby.ast.OpAsgnAndNode node) {
/*
* This doesn't translate as you might expect!
*
* http://www.rubyinside.com/what-rubys-double-pipe-or-equals-really-does-5488.html
*/

final SourceSection sourceSection = translate(node.getPosition());

final org.jruby.ast.Node lhs = node.getFirstNode();
final org.jruby.ast.Node rhs = node.getSecondNode();

return new AndNode(context, translate(node.getPosition()), lhs.accept(this), rhs.accept(this));
return new AssignmentWrapperNode(context, sourceSection, new AndNode(context, sourceSection, lhs.accept(this), rhs.accept(this)));
}

@Override
public RubyNode visitOpAsgnNode(org.jruby.ast.OpAsgnNode node) {
if (node.getOperatorName() == "||") {
// Why does this ||= come through as a visitOpAsgnNode and not a visitOpAsgnOrNode?

final String temp = environment.allocateLocalTemp("opassign");
final org.jruby.ast.Node writeReceiverToTemp = new org.jruby.ast.LocalAsgnNode(node.getPosition(), temp, 0, node.getReceiverNode());
final org.jruby.ast.Node readReceiverFromTemp = new org.jruby.ast.LocalVarNode(node.getPosition(), 0, temp);

final org.jruby.ast.Node readMethod = new CallNode(node.getPosition(), readReceiverFromTemp, node.getVariableName(), null, null);
final org.jruby.ast.Node writeMethod = new CallNode(node.getPosition(), readReceiverFromTemp, node.getVariableName() + "=", buildArrayNode(node.getPosition(),
node.getValueNode()), null);

final SourceSection sourceSection = translate(node.getPosition());

RubyNode lhs = readMethod.accept(this);
RubyNode rhs = writeMethod.accept(this);

return new AssignmentWrapperNode(context, sourceSection, SequenceNode.sequence(context, sourceSection, writeReceiverToTemp.accept(this), new OrNode(context, sourceSection, lhs, rhs)));
}

/*
* We're going to de-sugar a.foo += c into a.foo = a.foo + c. Note that we can't evaluate a
* more than once, so we put it into a temporary, and we're doing something more like:
@@ -1727,12 +1729,13 @@ public RubyNode visitOpAsgnNode(org.jruby.ast.OpAsgnNode node) {
@Override
public RubyNode visitOpAsgnOrNode(org.jruby.ast.OpAsgnOrNode node) {
/*
* De-sugar x ||= y into ([defined?(x) &&] x) || x = y.
* The defined? check is only needed for some expressions.
* It's also basically how jruby-parser represents it already.
* We'll do it directly, rather than via another JRuby AST node.
* This doesn't translate as you might expect!
*
* http://www.rubyinside.com/what-rubys-double-pipe-or-equals-really-does-5488.html
*/

final SourceSection sourceSection = translate(node.getPosition());

RubyNode lhs = node.getFirstNode().accept(this);
RubyNode rhs = node.getSecondNode().accept(this);

@@ -1741,7 +1744,7 @@ public RubyNode visitOpAsgnOrNode(org.jruby.ast.OpAsgnOrNode node) {
lhs = new AndNode(context, lhs.getSourceSection(), defined, lhs);
}

return new OrNode(context, translate(node.getPosition()), lhs, rhs);
return new AssignmentWrapperNode(context, sourceSection, new OrNode(context, sourceSection, lhs, rhs));
}

@Override
@@ -1835,6 +1838,14 @@ public RubyNode visitPostExeNode(PostExeNode node) {
return node.getBodyNode().accept(this);
}

@Override
public RubyNode visitRationalNode(RationalNode node) {
final SourceSection sourceSection = translate(node.getPosition());

// TODO: implement Rational
return new FixnumLiteralNode.LongFixnumLiteralNode(context, sourceSection, node.getNumerator());
}

@Override
public RubyNode visitRedoNode(org.jruby.ast.RedoNode node) {
return new RedoNode(context, translate(node.getPosition()));
Original file line number Diff line number Diff line change
@@ -208,7 +208,7 @@ public RubyNode visitSuperNode(org.jruby.ast.SuperNode node) {

final ArgumentsAndBlockTranslation argumentsAndBlock = translateArgumentsAndBlock(sourceSection, node.getIterNode(), node.getArgsNode(), null, environment.getNamedMethodName());

return new GeneralSuperCallNode(context, sourceSection, environment.getNamedMethodName(), argumentsAndBlock.getBlock(), argumentsAndBlock.getArguments(), argumentsAndBlock.isSplatted());
return new GeneralSuperCallNode(context, sourceSection, argumentsAndBlock.getBlock(), argumentsAndBlock.getArguments(), argumentsAndBlock.isSplatted());
}

@Override
@@ -220,7 +220,7 @@ public RubyNode visitZSuperNode(org.jruby.ast.ZSuperNode node) {
environment.setNeedsDeclarationFrame();
}

return new GeneralSuperReCallNode(context, sourceSection, environment.getNamedMethodName(), environment.isBlock());
return new GeneralSuperReCallNode(context, sourceSection, environment.isBlock());
}

@Override
Original file line number Diff line number Diff line change
@@ -177,7 +177,7 @@ public RubyRootNode parse(RubyNode currentNode, RubyContext context, Source sour

// Shell result

return new RubyRootNode(context, truffleNode.getSourceSection(), environment.getFrameDescriptor(), environment.getSharedMethodInfo(), truffleNode);
return new RubyRootNode(context, truffleNode.getSourceSection(), environment.getFrameDescriptor(), sharedMethodInfo, truffleNode);
}

private Object getData(RubyContext context) {
Original file line number Diff line number Diff line change
@@ -97,6 +97,14 @@ public FrameSlot declareVar(String name) {
return getFrameDescriptor().findOrAddFrameSlot(name);
}

public FrameSlot declareVarWhereAllowed(String name) {
if (isBlock) {
return parent.declareVarWhereAllowed(name);
} else {
return declareVar(name);
}
}

public SharedMethodInfo findMethodForLocalVar(String name) {
TranslatorEnvironment current = this;
do {
4 changes: 4 additions & 0 deletions core/src/main/java/org/jruby/util/JarResource.java
Original file line number Diff line number Diff line change
@@ -24,6 +24,10 @@ public static JarResource create(String pathname) {
// since pathname is actually an uri we need to decode any url decoded characters like %20
// which happens when directory names contain spaces
sanitized = URLDecoder.decode(sanitized, "UTF-8");
} catch (IllegalArgumentException iae) {
// something in the path did not decode, so it's probably not a URI
// See jruby/jruby#2264.
return null;
} catch (UnsupportedEncodingException e) {
throw new RuntimeException( "hmm - system does not know UTF-8 string encoding :(" );
}
7 changes: 7 additions & 0 deletions core/src/main/java/org/jruby/util/ShellLauncher.java
Original file line number Diff line number Diff line change
@@ -766,6 +766,13 @@ private static Process popenShared(Ruby runtime, IRubyObject[] strings, Map env,
File pwd = new File(runtime.getCurrentDirectory());

try {
// Peel off env hash, if given
IRubyObject envHash = null;
if (env == null && strings.length > 0 && !(envHash = TypeConverter.checkHashType(runtime, strings[0])).isNil()) {
strings = Arrays.copyOfRange(strings, 1, strings.length);
env = (Map)envHash;
}

String[] args = parseCommandLine(runtime.getCurrentContext(), runtime, strings);
boolean useShell = false;
if (addShell) for (String arg : args) useShell |= shouldUseShell(arg);
9 changes: 9 additions & 0 deletions core/src/main/ruby/jruby/truffle/core/kernel.rb
Original file line number Diff line number Diff line change
@@ -33,6 +33,15 @@ def puts(*args)
end
end

# Minimal support for language specs
def Rational(numerator, denominator)
numerator
end

def Complex(real, imaginary)
imaginary
end

end

def STDOUT.internal_encoding
24 changes: 22 additions & 2 deletions lib/ruby/stdlib/ffi/library.rb
Original file line number Diff line number Diff line change
@@ -424,18 +424,38 @@ def typedef(old, add, info=nil)
# @example
# enum [:zero, :one, :two] # unnamed enum, equivalent to above example
# @param [Array] values values for enum
# @overload enum(native_type, name, values)
# Create a named enum and specify the native type.
# @example
# enum FFI::Type::UINT64, :foo, [:zero, :one, :two] # named enum
# @param [FFI::Type] native_type native type for new enum
# @param [Symbol] name name for new enum
# @param [Array] values values for enum
# @overload enum(native_type, *args)
# Create an unnamed enum and specify the native type.
# @example
# enum FFI::Type::UINT64, :zero, :one, :two # unnamed enum
# @param [FFI::Type] native_type native type for new enum
# @param args values for enum
# @overload enum(native_type, values)
# Create an unnamed enum and specify the native type.
# @example
# enum Type::UINT64, [:zero, :one, :two] # unnamed enum, equivalent to above example
# @param [FFI::Type] native_type native type for new enum
# @param [Array] values values for enum
# @return [FFI::Enum]
# Create a new {FFI::Enum}.
def enum(*args)
native_type = args.first.kind_of?(FFI::Type) ? args.shift : nil
name, values = if args[0].kind_of?(Symbol) && args[1].kind_of?(Array)
[ args[0], args[1] ]
elsif args[0].kind_of?(Array)
[ nil, args[0] ]
else
[ nil, args ]
end
@ffi_enums ||= FFI::Enums.new
@ffi_enums << (e = FFI::Enum.new(values, name))
@ffi_enums = FFI::Enums.new unless defined?(@ffi_enums)
@ffi_enums << (e = native_type ? FFI::Enum.new(native_type, values, name) : FFI::Enum.new(values, name))

# If called as enum :foo, [ :zero, :one, :two ], add a typedef alias
typedef(e, name) if name
2 changes: 1 addition & 1 deletion lib/ruby/stdlib/rubygems.rb
Original file line number Diff line number Diff line change
@@ -9,7 +9,7 @@
require 'thread'

module Gem
VERSION = '2.4.3'
VERSION = '2.4.4'
end

# Must be first since it unloads the prelude from 1.9.2
2 changes: 1 addition & 1 deletion lib/ruby/stdlib/rubygems/commands/contents_command.rb
Original file line number Diff line number Diff line change
@@ -102,7 +102,7 @@ def files_in_gem spec
end

def files_in_default_gem spec
spec.files.sort.map do |file|
spec.files.map do |file|
case file
when /\A#{spec.bindir}\//
[RbConfig::CONFIG['bindir'], $POSTMATCH]
2 changes: 1 addition & 1 deletion lib/ruby/stdlib/rubygems/resolver/api_specification.rb
Original file line number Diff line number Diff line change
@@ -19,7 +19,7 @@ def initialize(set, api_data)
@set = set
@name = api_data[:name]
@version = Gem::Version.new api_data[:number]
@platform = api_data[:platform]
@platform = Gem::Platform.new api_data[:platform]
@dependencies = api_data[:dependencies].map do |name, ver|
Gem::Dependency.new name, ver.split(/\s*,\s*/)
end
25 changes: 25 additions & 0 deletions lib/ruby/stdlib/rubygems/ssl_certs/AddTrustExternalCARoot-2048.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
-----BEGIN CERTIFICATE-----
MIIENjCCAx6gAwIBAgIBATANBgkqhkiG9w0BAQUFADBvMQswCQYDVQQGEwJTRTEU
MBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFs
IFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBFeHRlcm5hbCBDQSBSb290
MB4XDTAwMDUzMDEwNDgzOFoXDTIwMDUzMDEwNDgzOFowbzELMAkGA1UEBhMCU0Ux
FDASBgNVBAoTC0FkZFRydXN0IEFCMSYwJAYDVQQLEx1BZGRUcnVzdCBFeHRlcm5h
bCBUVFAgTmV0d29yazEiMCAGA1UEAxMZQWRkVHJ1c3QgRXh0ZXJuYWwgQ0EgUm9v
dDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALf3GjPm8gAELTngTlvt
H7xsD821+iO2zt6bETOXpClMfZOfvUq8k+0DGuOPz+VtUFrWlymUWoCwSXrbLpX9
uMq/NzgtHj6RQa1wVsfwTz/oMp50ysiQVOnGXw94nZpAPA6sYapeFI+eh6FqUNzX
mk6vBbOmcZSccbNQYArHE504B4YCqOmoaSYYkKtMsE8jqzpPhNjfzp/haW+710LX
a0Tkx63ubUFfclpxCDezeWWkWaCUN/cALw3CknLa0Dhy2xSoRcRdKn23tNbE7qzN
E0S3ySvdQwAl+mG5aWpYIxG3pzOPVnVZ9c0p10a3CitlttNCbxWyuHv77+ldU9U0
WicCAwEAAaOB3DCB2TAdBgNVHQ4EFgQUrb2YejS0Jvf6xCZU7wO94CTLVBowCwYD
VR0PBAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wgZkGA1UdIwSBkTCBjoAUrb2YejS0
Jvf6xCZU7wO94CTLVBqhc6RxMG8xCzAJBgNVBAYTAlNFMRQwEgYDVQQKEwtBZGRU
cnVzdCBBQjEmMCQGA1UECxMdQWRkVHJ1c3QgRXh0ZXJuYWwgVFRQIE5ldHdvcmsx
IjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENBIFJvb3SCAQEwDQYJKoZIhvcN
AQEFBQADggEBALCb4IUlwtYj4g+WBpKdQZic2YR5gdkeWxQHIzZlj7DYd7usQWxH
YINRsPkyPef89iYTx4AWpb9a/IfPeHmJIZriTAcKhjW88t5RxNKWt9x+Tu5w/Rw5
6wwCURQtjr0W4MHfRnXnJK3s9EK0hZNwEGe6nQY1ShjTK3rMUUKhemPR5ruhxSvC
Nr4TDea9Y355e6cJDUCrat2PisP29owaQgVR1EX1n6diIWgVIEM8med8vSTYqZEX
c4g/VhsxOBi0cQ+azcgOno4uG+GMmIPLHzHxREzGBHNJdmAPx/i9F4BrLunMTA5a
mnkPIAou1Z5jJh5VkpTYghdae9C8x49OhgQ=
-----END CERTIFICATE-----
11 changes: 5 additions & 6 deletions spec/ffi/async_callback_spec.rb
Original file line number Diff line number Diff line change
@@ -3,8 +3,7 @@
# For licensing, see LICENSE.SPECS
#

require 'ffi'
require_relative 'spec_helper'
require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper"))

describe "async callback" do
module LibTest
@@ -21,16 +20,16 @@ module LibTest
called = false
cb = Proc.new {|i| v = i; called = true }
LibTest.testAsyncCallback(cb, 0x7fffffff)
called.should be_true
v.should == 0x7fffffff
expect(called).to be true
expect(v).to eq(0x7fffffff)
end

it "called a second time" do
v = 0xdeadbeef
called = false
cb = Proc.new {|i| v = i; called = true }
LibTest.testAsyncCallback(cb, 0x7fffffff)
called.should be_true
v.should == 0x7fffffff
expect(called).to be true
expect(v).to eq(0x7fffffff)
end
end
19 changes: 10 additions & 9 deletions spec/ffi/bool_spec.rb
Original file line number Diff line number Diff line change
@@ -3,8 +3,7 @@
# For licensing, see LICENSE.SPECS
#

require 'ffi'
require_relative 'spec_helper'
require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper"))

describe "Function with primitive boolean arguments and return values" do
module LibTest
@@ -15,17 +14,19 @@ module LibTest
attach_function :bool_return_val, [ :bool ], :bool
attach_function :bool_reverse_val, [ :bool ], :bool
end

it "bools" do
LibTest.bool_return_true.should == true
LibTest.bool_return_false.should == false
expect(LibTest.bool_return_true).to be true
expect(LibTest.bool_return_false).to be false

LibTest.bool_return_val(true).should == true
LibTest.bool_return_val(false).should == false
expect(LibTest.bool_return_val(true)).to be true
expect(LibTest.bool_return_val(false)).to be false

LibTest.bool_reverse_val(true).should == false
LibTest.bool_reverse_val(false).should == true
expect(LibTest.bool_reverse_val(true)).to be false
expect(LibTest.bool_reverse_val(false)).to be true
end

it "raise error on invalid types" do
lambda { LibTest.bool_return_val(nil) }.should raise_error(::TypeError)
expect { LibTest.bool_return_val(nil) }.to raise_error(::TypeError)
end
end
103 changes: 65 additions & 38 deletions spec/ffi/buffer_spec.rb
Original file line number Diff line number Diff line change
@@ -3,23 +3,25 @@
# For licensing, see LICENSE.SPECS
#

require 'ffi'
require_relative 'spec_helper'
require File.expand_path(File.join(File.dirname(__FILE__), "spec_helper"))

describe "Buffer#total" do
[1,2,3].each do |i|
{ :char => 1, :uchar => 1, :short => 2, :ushort => 2, :int => 4,
:uint => 4, :long => FFI::Type::LONG.size, :ulong => FFI::Type::ULONG.size,
:long_long => 8, :ulong_long => 8, :float => 4, :double => 8
}.each_pair do |t, s|

it "Buffer.alloc_in(#{t}, #{i}).total == #{i * s}" do
FFI::Buffer.alloc_in(t, i).total.should == i * s
expect(FFI::Buffer.alloc_in(t, i).total).to eq(i * s)
end

it "Buffer.alloc_out(#{t}, #{i}).total == #{i * s}" do
FFI::Buffer.alloc_out(t, i).total.should == i * s
expect(FFI::Buffer.alloc_out(t, i).total).to eq(i * s)
end

it "Buffer.alloc_inout(#{t}, #{i}).total == #{i * s}" do
FFI::Buffer.alloc_inout(t, i).total.should == i * s
expect(FFI::Buffer.alloc_inout(t, i).total).to eq(i * s)
end
end
end
@@ -30,214 +32,239 @@
(0..127).each do |i|
(0..bufsize-1).each do |offset|
it "put_char(#{offset}, #{i}).get_char(#{offset}) == #{i}" do
FFI::Buffer.alloc_in(bufsize).put_char(offset, i).get_char(offset).should == i
expect(FFI::Buffer.alloc_in(bufsize).put_char(offset, i).get_char(offset)).to eq(i)
end
end
end
end

describe "Buffer#put_uchar" do
bufsize = 4
(0..255).each do |i|
(0..bufsize-1).each do |offset|
it "Buffer.put_uchar(#{offset}, #{i}).get_uchar(#{offset}) == #{i}" do
FFI::Buffer.alloc_in(bufsize).put_uchar(offset, i).get_uchar(offset).should == i
expect(FFI::Buffer.alloc_in(bufsize).put_uchar(offset, i).get_uchar(offset)).to eq(i)
end
end
end
end

describe "Buffer#put_short" do
bufsize = 4
[0, 1, 128, 32767].each do |i|
(0..bufsize-2).each do |offset|
it "put_short(#{offset}, #{i}).get_short(#{offset}) == #{i}" do
FFI::Buffer.alloc_in(bufsize).put_short(offset, i).get_short(offset).should == i
expect(FFI::Buffer.alloc_in(bufsize).put_short(offset, i).get_short(offset)).to eq(i)
end
end
end
end

describe "Buffer#put_ushort" do
bufsize = 4
[ 0, 1, 128, 32767, 65535, 0xfee1, 0xdead, 0xbeef, 0xcafe ].each do |i|
(0..bufsize-2).each do |offset|
it "put_ushort(#{offset}, #{i}).get_ushort(#{offset}) == #{i}" do
FFI::Buffer.alloc_in(bufsize).put_ushort(offset, i).get_ushort(offset).should == i
expect(FFI::Buffer.alloc_in(bufsize).put_ushort(offset, i).get_ushort(offset)).to eq(i)
end
end
end
end

describe "Buffer#put_int" do
bufsize = 8
[0, 1, 128, 32767, 0x7ffffff ].each do |i|
(0..bufsize-4).each do |offset|
it "put_int(#{offset}, #{i}).get_int(#{offset}) == #{i}" do
FFI::Buffer.alloc_in(bufsize).put_int(offset, i).get_int(offset).should == i
expect(FFI::Buffer.alloc_in(bufsize).put_int(offset, i).get_int(offset)).to eq(i)
end
end
end
end

describe "Buffer#put_uint" do
bufsize = 8
[ 0, 1, 128, 32767, 65535, 0xfee1dead, 0xcafebabe, 0xffffffff ].each do |i|
(0..bufsize-4).each do |offset|
it "put_uint(#{offset}, #{i}).get_uint(#{offset}) == #{i}" do
FFI::Buffer.alloc_in(bufsize).put_uint(offset, i).get_uint(offset).should == i
expect(FFI::Buffer.alloc_in(bufsize).put_uint(offset, i).get_uint(offset)).to eq(i)
end
end
end
end

describe "Buffer#put_long" do
bufsize = 16
[0, 1, 128, 32767, 0x7ffffff ].each do |i|
(0..bufsize-FFI::Type::LONG.size).each do |offset|
it "put_long(#{offset}, #{i}).get_long(#{offset}) == #{i}" do
FFI::Buffer.alloc_in(bufsize).put_long(offset, i).get_long(offset).should == i
expect(FFI::Buffer.alloc_in(bufsize).put_long(offset, i).get_long(offset)).to eq(i)
end
end
end
end

describe "Buffer#put_ulong" do
bufsize = 16
[ 0, 1, 128, 32767, 65535, 0xfee1dead, 0xcafebabe, 0xffffffff ].each do |i|
(0..bufsize-FFI::Type::LONG.size).each do |offset|
it "put_ulong(#{offset}, #{i}).get_ulong(#{offset}) == #{i}" do
FFI::Buffer.alloc_in(bufsize).put_ulong(offset, i).get_ulong(offset).should == i
expect(FFI::Buffer.alloc_in(bufsize).put_ulong(offset, i).get_ulong(offset)).to eq(i)
end
end
end
end

describe "Buffer#put_long_long" do
bufsize = 16
[0, 1, 128, 32767, 0x7ffffffffffffff ].each do |i|
(0..bufsize-8).each do |offset|
it "put_long_long(#{offset}, #{i}).get_long_long(#{offset}) == #{i}" do
FFI::Buffer.alloc_in(bufsize).put_long_long(offset, i).get_long_long(offset).should == i
expect(FFI::Buffer.alloc_in(bufsize).put_long_long(offset, i).get_long_long(offset)).to eq(i)
end
end
end
end

describe "Buffer#put_ulong_long" do
bufsize = 16
[ 0, 1, 128, 32767, 65535, 0xdeadcafebabe, 0x7fffffffffffffff ].each do |i|
(0..bufsize-8).each do |offset|
it "put_ulong_long(#{offset}, #{i}).get_ulong_long(#{offset}) == #{i}" do
FFI::Buffer.alloc_in(bufsize).put_ulong_long(offset, i).get_ulong_long(offset).should == i
expect(FFI::Buffer.alloc_in(bufsize).put_ulong_long(offset, i).get_ulong_long(offset)).to eq(i)
end
end
end
end

describe "Reading/Writing binary strings" do
it "Buffer#put_bytes" do
str = "hello\0world"
buf = FFI::Buffer.new 1024
buf.put_bytes(0, str);
s2 = buf.get_bytes(0, 11);
s2.should == str
expect(s2).to eq(str)
end

it "Buffer#put_bytes with index and length" do
str = "hello\0world"
buf = FFI::Buffer.new 1024
buf.put_bytes(0, str, 5, 6);
s2 = buf.get_bytes(0, 6);
s2.should == str[5..-1]
expect(s2).to eq(str[5..-1])
end

it "Buffer#put_bytes with only index" do
str = "hello\0world"
buf = FFI::Buffer.new 1024
buf.put_bytes(0, str, 5);
s2 = buf.get_bytes(0, 6);
s2.should == str[5..-1]
expect(s2).to eq(str[5..-1])
end

it "Buffer#put_bytes with index > str.length" do
str = "hello\0world"
buf = FFI::Buffer.new 1024
lambda { buf.put_bytes(0, str, 12); }.should raise_error
expect { buf.put_bytes(0, str, 12); }.to raise_error
end

it "Buffer#put_bytes with length > str.length" do
str = "hello\0world"
buf = FFI::Buffer.new 1024
lambda { buf.put_bytes(0, str, 0, 12); }.should raise_error
expect { buf.put_bytes(0, str, 0, 12); }.to raise_error
end
it "Buffer#put_bytes with negative index" do

it "Buffer#put_bytes with negative index" do
str = "hello\0world"
buf = FFI::Buffer.new 1024
lambda { buf.put_bytes(0, str, -1, 12); }.should raise_error
end
expect { buf.put_bytes(0, str, -1, 12); }.to raise_error
end

it "Buffer#write_bytes" do
str = "hello\0world"
buf = FFI::Buffer.new 1024
buf.write_bytes(str)
s2 = buf.get_bytes(0, 11)
s2.should == str
expect(s2).to eq(str)
end

it "Buffer#write_bytes with index and length" do
str = "hello\0world"
buf = FFI::Buffer.new 1024
buf.write_bytes(str, 5, 6)
s2 = buf.get_bytes(0, 6)
s2.should == str[5..-1]
expect(s2).to eq(str[5..-1])
end

it "Buffer#write_bytes with only index" do
str = "hello\0world"
buf = FFI::Buffer.new 1024
buf.write_bytes(str, 5)
s2 = buf.get_bytes(0, 6)
s2.should == str[5..-1]
expect(s2).to eq(str[5..-1])
end

it "Buffer#write_bytes with index > str.length" do
str = "hello\0world"
buf = FFI::Buffer.new 1024
lambda { buf.write_bytes(str, 12) }.should raise_error
expect { buf.write_bytes(str, 12) }.to raise_error
end

it "Buffer#put_bytes with length > str.length" do
str = "hello\0world"
buf = FFI::Buffer.new 1024
lambda { buf.put_bytes(0, str, 0, 12) }.should raise_error
expect { buf.put_bytes(0, str, 0, 12) }.to raise_error
end
it "Buffer#write_bytes with negative index" do

it "Buffer#write_bytes with negative index" do
str = "hello\0world"
buf = FFI::Buffer.new 1024
lambda { buf.write_bytes(str, -1, 12) }.should raise_error
expect { buf.write_bytes(str, -1, 12) }.to raise_error
end
end

describe "Reading/Writing ascii strings" do
it "Buffer#put_string with string containing zero byte" do
str = "hello\0world"
buf = FFI::Buffer.new 1024
buf.put_string(0, str);
s2 = buf.get_bytes(0, 11);
s2.should == str
expect(s2).to eq(str)
end

it "Buffer#get_string with string containing zero byte" do
str = "hello\0world"
buf = FFI::Buffer.new 1024
buf.put_bytes(0, str);
s2 = buf.get_string(0, 11);
s2.should == "hello"
expect(s2).to eq("hello")
end

it "Buffer#put_string without length should NUL terminate" do
str = "hello"
buf = FFI::Buffer.new 1024
buf.put_string(0, str);
s2 = buf.get_bytes(0, 6);
s2.should == "hello\0"
expect(s2).to eq("hello\0")
end
end

describe "Buffer#put_pointer" do
it "put_pointer(0, p).get_pointer(0) == p" do
p = FFI::MemoryPointer.new :ulong_long
p.put_uint(0, 0xdeadbeef)
buf = FFI::Buffer.alloc_inout 8
p2 = buf.put_pointer(0, p).get_pointer(0)
p2.should_not be_nil
p2.should == p
p2.get_uint(0).should == 0xdeadbeef
expect(p2).not_to be_nil
expect(p2).to eq(p)
expect(p2.get_uint(0)).to eq(0xdeadbeef)
end
end

describe "Buffer#size" do
it "should return size" do
buf = FFI::Buffer.new 14
buf.size.should == 14
expect(buf.size).to eq(14)
end
end

@@ -247,6 +274,6 @@
FFI::Buffer.new(:pointer) do |ptr|
block_executed = true
end
block_executed.should be_true
expect(block_executed).to be true
end
end
Loading

0 comments on commit e2a3b8c

Please sign in to comment.