Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: jruby/jruby
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: eb55de99de35^
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: a1b3ff3d2dea
Choose a head ref
  • 3 commits
  • 17 files changed
  • 1 contributor

Commits on Apr 28, 2016

  1. Copy the full SHA
    eb55de9 View commit details
  2. Copy the full SHA
    3463e18 View commit details
  3. Copy the full SHA
    a1b3ff3 View commit details
Showing with 910 additions and 988 deletions.
  1. +60 −0 truffle/src/main/java/org/jruby/truffle/interop/ForeignExecuteHelperNode.java
  2. +0 −90 truffle/src/main/java/org/jruby/truffle/interop/ForeignExecuteNode.java
  3. +0 −47 truffle/src/main/java/org/jruby/truffle/interop/ForeignGetSizeNode.java
  4. +0 −27 truffle/src/main/java/org/jruby/truffle/interop/ForeignHasSizeNode.java
  5. +0 −47 truffle/src/main/java/org/jruby/truffle/interop/ForeignInvokeNode.java
  6. +0 −28 truffle/src/main/java/org/jruby/truffle/interop/ForeignIsBoxedNode.java
  7. +0 −27 truffle/src/main/java/org/jruby/truffle/interop/ForeignIsExecutableNode.java
  8. +0 −44 truffle/src/main/java/org/jruby/truffle/interop/ForeignIsNullNode.java
  9. +0 −317 truffle/src/main/java/org/jruby/truffle/interop/ForeignReadNode.java
  10. +131 −0 truffle/src/main/java/org/jruby/truffle/interop/ForeignReadStringCachedHelperNode.java
  11. +177 −0 truffle/src/main/java/org/jruby/truffle/interop/ForeignReadStringCachingHelperNode.java
  12. +0 −52 truffle/src/main/java/org/jruby/truffle/interop/ForeignUnboxNode.java
  13. +0 −308 truffle/src/main/java/org/jruby/truffle/interop/ForeignWriteNode.java
  14. +142 −0 truffle/src/main/java/org/jruby/truffle/interop/ForeignWriteStringCachedHelperNode.java
  15. +158 −0 truffle/src/main/java/org/jruby/truffle/interop/ForeignWriteStringCachingHelperNode.java
  16. +241 −0 truffle/src/main/java/org/jruby/truffle/interop/RubyMessageResolution.java
  17. +1 −1 truffle/src/main/java/org/jruby/truffle/language/RubyObjectType.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* Copyright (c) 2013, 2016 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.interop;

import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.NodeChildren;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.object.DynamicObject;
import org.jruby.truffle.RubyContext;
import org.jruby.truffle.core.Layouts;
import org.jruby.truffle.language.RubyNode;
import org.jruby.truffle.language.methods.CallBoundMethodNode;
import org.jruby.truffle.language.methods.CallBoundMethodNodeGen;
import org.jruby.truffle.language.methods.DeclarationContext;
import org.jruby.truffle.language.yield.CallBlockNode;
import org.jruby.truffle.language.yield.CallBlockNodeGen;

@NodeChildren({
@NodeChild("receiver"),
@NodeChild("arguments")
})
abstract class ForeignExecuteHelperNode extends RubyNode {

public ForeignExecuteHelperNode(RubyContext context) {
super(context, null);
}

public abstract Object executeCall(VirtualFrame frame, Object receiver, Object[] arguments);

@Specialization(guards = "isRubyProc(proc)")
protected Object callProc(VirtualFrame frame, DynamicObject proc, Object[] arguments,
@Cached("createCallBlockNode()") CallBlockNode callBlockNode) {
Object self = Layouts.PROC.getSelf(proc);
return callBlockNode.executeCallBlock(frame, proc, self, null, arguments);
}

protected CallBlockNode createCallBlockNode() {
return CallBlockNodeGen.create(getContext(), getSourceSection(), DeclarationContext.BLOCK, null, null, null, null);
}

@Specialization(guards = "isRubyMethod(method)")
protected Object callMethod(VirtualFrame frame, DynamicObject method, Object[] arguments,
@Cached("createCallBoundMethodNode()") CallBoundMethodNode callBoundMethodNode) {
return callBoundMethodNode.executeCallBoundMethod(frame, method, arguments, null);
}

protected CallBoundMethodNode createCallBoundMethodNode() {
return CallBoundMethodNodeGen.create(getContext(), getSourceSection(), null, null, null);
}

}

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

317 changes: 0 additions & 317 deletions truffle/src/main/java/org/jruby/truffle/interop/ForeignReadNode.java

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
package org.jruby.truffle.interop;

/*
* Copyright (c) 2013, 2016 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
*/
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.NodeChildren;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.object.DynamicObject;
import org.jruby.truffle.language.RubyNode;
import org.jruby.truffle.language.dispatch.CallDispatchHeadNode;
import org.jruby.truffle.language.dispatch.DispatchHeadNodeFactory;
import org.jruby.truffle.language.dispatch.DoesRespondDispatchHeadNode;
import org.jruby.truffle.language.objects.ReadObjectFieldNode;
import org.jruby.truffle.language.objects.ReadObjectFieldNodeGen;

@NodeChildren({
@NodeChild("receiver"),
@NodeChild("name"),
@NodeChild("stringName"),
@NodeChild("startsAt")
})
abstract class ForeignReadStringCachedHelperNode extends RubyNode {

@Child
private DoesRespondDispatchHeadNode definedNode;
@Child
private DoesRespondDispatchHeadNode indexDefinedNode;
@Child
private CallDispatchHeadNode callNode;

protected final static String INDEX_METHOD_NAME = "[]";

public abstract Object executeStringCachedHelper(VirtualFrame frame, DynamicObject receiver, Object name,
String stringName, boolean startsAt);

@Specialization(guards = "startsAt(startsAt)")
public Object readInstanceVariable(
DynamicObject receiver,
Object name,
String stringName,
boolean startsAt,
@Cached("createReadObjectFieldNode(stringName)") ReadObjectFieldNode readObjectFieldNode) {
return readObjectFieldNode.execute(receiver);
}

protected boolean startsAt(boolean startsAt) {
return startsAt;
}

protected ReadObjectFieldNode createReadObjectFieldNode(String name) {
return ReadObjectFieldNodeGen.create(name, nil());
}

@Specialization(
guards = {
"notStartsAt(startsAt)",
"methodDefined(frame, receiver, stringName, getDefinedNode())"
}
)
public Object callMethod(
VirtualFrame frame,
DynamicObject receiver,
Object name,
String stringName,
boolean startsAt) {
return getCallNode().call(frame, receiver, stringName, null);
}

@Specialization(
guards = {
"notStartsAt(startsAt)",
"!methodDefined(frame, receiver, stringName, getDefinedNode())",
"methodDefined(frame, receiver, INDEX_METHOD_NAME, getIndexDefinedNode())"
}
)
public Object index(
VirtualFrame frame,
DynamicObject receiver,
Object name,
String stringName,
boolean startsAt) {
return getCallNode().call(frame, receiver, "[]", null, name);
}

protected boolean notStartsAt(boolean startsAt) {
return !startsAt;
}

protected DoesRespondDispatchHeadNode getDefinedNode() {
if (definedNode == null) {
CompilerDirectives.transferToInterpreter();
definedNode = insert(new DoesRespondDispatchHeadNode(getContext(), true));
}

return definedNode;
}

protected DoesRespondDispatchHeadNode getIndexDefinedNode() {
if (indexDefinedNode == null) {
CompilerDirectives.transferToInterpreter();
indexDefinedNode = insert(new DoesRespondDispatchHeadNode(getContext(), true));
}

return indexDefinedNode;
}

protected boolean methodDefined(VirtualFrame frame, DynamicObject receiver, String stringName,
DoesRespondDispatchHeadNode definedNode) {
return definedNode.doesRespondTo(frame, stringName, receiver);
}

protected CallDispatchHeadNode getCallNode() {
if (callNode == null) {
CompilerDirectives.transferToInterpreter();
callNode = insert(DispatchHeadNodeFactory.createMethodCall(getContext(), true));
}

return callNode;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
package org.jruby.truffle.interop;

/*
* Copyright (c) 2013, 2016 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
*/
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.ImportStatic;
import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.NodeChildren;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.object.DynamicObject;
import org.jruby.truffle.RubyContext;
import org.jruby.truffle.core.Layouts;
import org.jruby.truffle.core.rope.Rope;
import org.jruby.truffle.core.rope.RopeOperations;
import org.jruby.truffle.core.string.StringCachingGuards;
import org.jruby.truffle.language.RubyNode;

@ImportStatic(StringCachingGuards.class)
@NodeChildren({
@NodeChild("receiver"),
@NodeChild("name")
})
abstract class ForeignReadStringCachingHelperNode extends RubyNode {

public ForeignReadStringCachingHelperNode(RubyContext context) {
super(context, null);
}

public abstract Object executeStringCachingHelper(VirtualFrame frame, DynamicObject receiver, Object name);

@Specialization(
guards = {
"isRubyString(name)",
"ropesEqual(name, cachedRope)"
},
limit = "getCacheLimit()"
)
public Object cacheStringAndForward(
VirtualFrame frame,
DynamicObject receiver,
DynamicObject name,
@Cached("privatizeRope(name)") Rope cachedRope,
@Cached("ropeToString(cachedRope)") String cachedString,
@Cached("startsWithAt(cachedString)") boolean cachedStartsWithAt,
@Cached("createNextHelper()") ForeignReadStringCachedHelperNode nextHelper) {
return nextHelper.executeStringCachedHelper(frame, receiver, name, cachedString, cachedStartsWithAt);
}

@Specialization(
guards = "isRubyString(name)",
contains = "cacheStringAndForward"
)
public Object uncachedStringAndForward(
VirtualFrame frame,
DynamicObject receiver,
DynamicObject name,
@Cached("createNextHelper()") ForeignReadStringCachedHelperNode nextHelper) {
final String nameString = objectToString(name);
return nextHelper.executeStringCachedHelper(frame, receiver, name, nameString, startsWithAt(nameString));
}

@Specialization(
guards = {
"isRubySymbol(name)",
"name == cachedName"
},
limit = "getCacheLimit()"
)
public Object cacheSymbolAndForward(
VirtualFrame frame,
DynamicObject receiver,
DynamicObject name,
@Cached("name") DynamicObject cachedName,
@Cached("objectToString(cachedName)") String cachedString,
@Cached("startsWithAt(cachedString)") boolean cachedStartsWithAt,
@Cached("createNextHelper()") ForeignReadStringCachedHelperNode nextHelper) {
return nextHelper.executeStringCachedHelper(frame, receiver, cachedName, cachedString, cachedStartsWithAt);
}

@Specialization(
guards = "isRubySymbol(name)",
contains = "cacheSymbolAndForward"
)
public Object uncachedSymbolAndForward(
VirtualFrame frame,
DynamicObject receiver,
DynamicObject name,
@Cached("createNextHelper()") ForeignReadStringCachedHelperNode nextHelper) {
final String nameString = objectToString(name);
return nextHelper.executeStringCachedHelper(frame, receiver, name, nameString, startsWithAt(nameString));
}

@Specialization(
guards = "name == cachedName",
limit = "getCacheLimit()"
)
public Object cacheJavaStringAndForward(
VirtualFrame frame,
DynamicObject receiver,
String name,
@Cached("name") String cachedName,
@Cached("startsWithAt(cachedName)") boolean cachedStartsWithAt,
@Cached("createNextHelper()") ForeignReadStringCachedHelperNode nextHelper) {
return nextHelper.executeStringCachedHelper(frame, receiver, cachedName, cachedName, cachedStartsWithAt);
}

@Specialization(contains = "cacheJavaStringAndForward")
public Object uncachedJavaStringAndForward(
VirtualFrame frame,
DynamicObject receiver,
String name,
@Cached("createNextHelper()") ForeignReadStringCachedHelperNode nextHelper) {
return nextHelper.executeStringCachedHelper(frame, receiver, name, name, startsWithAt(name));
}

protected ForeignReadStringCachedHelperNode createNextHelper() {
return ForeignReadStringCachedHelperNodeGen.create(null, null, null, null);
}

@CompilerDirectives.TruffleBoundary
protected String objectToString(DynamicObject string) {
return string.toString();
}

protected String ropeToString(Rope rope) {
return RopeOperations.decodeRope(getContext().getJRubyRuntime(), rope);
}

@CompilerDirectives.TruffleBoundary
protected boolean startsWithAt(String name) {
return !name.isEmpty() && name.charAt(0) == '@';
}

@Specialization(guards = {
"isRubyString(receiver)",
"index < 0"
})
public int indexStringNegative(DynamicObject receiver, int index) {
return 0;
}

@Specialization(guards = {
"isRubyString(receiver)",
"index >= 0",
"!inRange(receiver, index)"
})
public int indexStringOutOfRange(DynamicObject receiver, int index) {
return 0;
}

@Specialization(guards = {
"isRubyString(receiver)",
"index >= 0",
"inRange(receiver, index)"
})
public int indexString(DynamicObject receiver, int index) {
return Layouts.STRING.getRope(receiver).get(index);
}

protected boolean inRange(DynamicObject string, int index) {
return index < Layouts.STRING.getRope(string).byteLength();
}

protected int getCacheLimit() {
return getContext().getOptions().INTEROP_READ_CACHE;
}

}

This file was deleted.

308 changes: 0 additions & 308 deletions truffle/src/main/java/org/jruby/truffle/interop/ForeignWriteNode.java

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
package org.jruby.truffle.interop;

/*
* Copyright (c) 2013, 2016 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
*/
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.NodeChildren;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.object.DynamicObject;
import org.jruby.truffle.language.RubyNode;
import org.jruby.truffle.language.dispatch.CallDispatchHeadNode;
import org.jruby.truffle.language.dispatch.DispatchHeadNodeFactory;
import org.jruby.truffle.language.dispatch.DoesRespondDispatchHeadNode;
import org.jruby.truffle.language.objects.WriteObjectFieldNode;
import org.jruby.truffle.language.objects.WriteObjectFieldNodeGen;

@NodeChildren({
@NodeChild("receiver"),
@NodeChild("name"),
@NodeChild("stringName"),
@NodeChild("startsAt"),
@NodeChild("value")
})
abstract class ForeignWriteStringCachedHelperNode extends RubyNode {

@Child
private DoesRespondDispatchHeadNode definedNode;
@Child
private DoesRespondDispatchHeadNode indexDefinedNode;
@Child
private CallDispatchHeadNode callNode;

protected final static String INDEX_METHOD_NAME = "[]=";

public abstract Object executeStringCachedHelper(VirtualFrame frame, DynamicObject receiver, Object name,
String stringName, boolean startsAt, Object value);

@Specialization(guards = "startsAt(startsAt)")
public Object readInstanceVariable(
DynamicObject receiver,
Object name,
String stringName,
boolean startsAt,
Object value,
@Cached("createWriteObjectFieldNode(stringName)") WriteObjectFieldNode writeObjectFieldNode) {
writeObjectFieldNode.execute(receiver, value);
return value;
}

protected boolean startsAt(boolean startsAt) {
return startsAt;
}

protected WriteObjectFieldNode createWriteObjectFieldNode(String name) {
return WriteObjectFieldNodeGen.create(name);
}

@Specialization(
guards = {
"notStartsAt(startsAt)",
"methodDefined(frame, receiver, writeMethodName, getDefinedNode())"
}
)
public Object callMethod(
VirtualFrame frame,
DynamicObject receiver,
Object name,
String stringName,
boolean startsAt,
Object value,
@Cached("createWriteMethodName(stringName)") String writeMethodName) {
return getCallNode().call(frame, receiver, writeMethodName, null, value);
}

protected String createWriteMethodName(String name) {
return name + "=";
}

@Specialization(
guards = {
"notStartsAt(startsAt)",
"!methodDefined(frame, receiver, writeMethodName, getDefinedNode())",
"methodDefined(frame, receiver, INDEX_METHOD_NAME, getIndexDefinedNode())"
}
)
public Object index(
VirtualFrame frame,
DynamicObject receiver,
Object name,
String stringName,
boolean startsAt,
Object value,
@Cached("createWriteMethodName(stringName)") String writeMethodName) {
return getCallNode().call(frame, receiver, "[]", null, name, value);
}

protected boolean notStartsAt(boolean startsAt) {
return !startsAt;
}

protected DoesRespondDispatchHeadNode getDefinedNode() {
if (definedNode == null) {
CompilerDirectives.transferToInterpreter();
definedNode = insert(new DoesRespondDispatchHeadNode(getContext(), true));
}

return definedNode;
}

protected DoesRespondDispatchHeadNode getIndexDefinedNode() {
if (indexDefinedNode == null) {
CompilerDirectives.transferToInterpreter();
indexDefinedNode = insert(new DoesRespondDispatchHeadNode(getContext(), true));
}

return indexDefinedNode;
}

protected boolean methodDefined(VirtualFrame frame, DynamicObject receiver, String stringName,
DoesRespondDispatchHeadNode definedNode) {
return definedNode.doesRespondTo(frame, stringName, receiver);
}

protected CallDispatchHeadNode getCallNode() {
if (callNode == null) {
CompilerDirectives.transferToInterpreter();
callNode = insert(DispatchHeadNodeFactory.createMethodCall(getContext(), true));
}

return callNode;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
package org.jruby.truffle.interop;

/*
* Copyright (c) 2013, 2016 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
*/
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.ImportStatic;
import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.NodeChildren;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.object.DynamicObject;
import org.jruby.truffle.RubyContext;
import org.jruby.truffle.core.rope.Rope;
import org.jruby.truffle.core.rope.RopeOperations;
import org.jruby.truffle.core.string.StringCachingGuards;
import org.jruby.truffle.language.RubyNode;

@ImportStatic(StringCachingGuards.class)
@NodeChildren({
@NodeChild("receiver"),
@NodeChild("name"),
@NodeChild("value")
})
abstract class ForeignWriteStringCachingHelperNode extends RubyNode {

public ForeignWriteStringCachingHelperNode(RubyContext context) {
super(context, null);
}

public abstract Object executeStringCachingHelper(VirtualFrame frame, DynamicObject receiver,
Object name, Object value);

@Specialization(
guards = {
"isRubyString(name)",
"ropesEqual(name, cachedRope)"
},
limit = "getCacheLimit()"
)
public Object cacheStringAndForward(
VirtualFrame frame,
DynamicObject receiver,
DynamicObject name,
Object value,
@Cached("privatizeRope(name)") Rope cachedRope,
@Cached("ropeToString(cachedRope)") String cachedString,
@Cached("startsWithAt(cachedString)") boolean cachedStartsWithAt,
@Cached("createNextHelper()") ForeignWriteStringCachedHelperNode nextHelper) {
return nextHelper.executeStringCachedHelper(frame, receiver, name, cachedString, cachedStartsWithAt, value);
}

@Specialization(
guards = "isRubyString(name)",
contains = "cacheStringAndForward"
)
public Object uncachedStringAndForward(
VirtualFrame frame,
DynamicObject receiver,
DynamicObject name,
Object value,
@Cached("createNextHelper()") ForeignWriteStringCachedHelperNode nextHelper) {
final String nameString = objectToString(name);
return nextHelper.executeStringCachedHelper(frame, receiver, name, nameString,
startsWithAt(nameString), value);
}

@Specialization(
guards = {
"isRubySymbol(name)",
"name == cachedName"
},
limit = "getCacheLimit()"
)
public Object cacheSymbolAndForward(
VirtualFrame frame,
DynamicObject receiver,
DynamicObject name,
Object value,
@Cached("name") DynamicObject cachedName,
@Cached("objectToString(cachedName)") String cachedString,
@Cached("startsWithAt(cachedString)") boolean cachedStartsWithAt,
@Cached("createNextHelper()") ForeignWriteStringCachedHelperNode nextHelper) {
return nextHelper.executeStringCachedHelper(frame, receiver, cachedName, cachedString,
cachedStartsWithAt, value);
}

@Specialization(
guards = "isRubySymbol(name)",
contains = "cacheSymbolAndForward"
)
public Object uncachedSymbolAndForward(
VirtualFrame frame,
DynamicObject receiver,
DynamicObject name,
Object value,
@Cached("createNextHelper()") ForeignWriteStringCachedHelperNode nextHelper) {
final String nameString = objectToString(name);
return nextHelper.executeStringCachedHelper(frame, receiver, name, nameString,
startsWithAt(nameString), value);
}

@Specialization(
guards = "name == cachedName",
limit = "getCacheLimit()"
)
public Object cacheJavaStringAndForward(
VirtualFrame frame,
DynamicObject receiver,
String name,
Object value,
@Cached("name") String cachedName,
@Cached("startsWithAt(cachedName)") boolean cachedStartsWithAt,
@Cached("createNextHelper()") ForeignWriteStringCachedHelperNode nextHelper) {
return nextHelper.executeStringCachedHelper(frame, receiver, cachedName, cachedName,
cachedStartsWithAt, value);
}

@Specialization(contains = "cacheJavaStringAndForward")
public Object uncachedJavaStringAndForward(
VirtualFrame frame,
DynamicObject receiver,
String name,
Object value,
@Cached("createNextHelper()") ForeignWriteStringCachedHelperNode nextHelper) {
return nextHelper.executeStringCachedHelper(frame, receiver, name, name, startsWithAt(name), value);
}

protected ForeignWriteStringCachedHelperNode createNextHelper() {
return ForeignWriteStringCachedHelperNodeGen.create(null, null, null, null, null);
}

@CompilerDirectives.TruffleBoundary
protected String objectToString(DynamicObject string) {
return string.toString();
}

protected String ropeToString(Rope rope) {
return RopeOperations.decodeRope(getContext().getJRubyRuntime(), rope);
}

@CompilerDirectives.TruffleBoundary
protected boolean startsWithAt(String name) {
return !name.isEmpty() && name.charAt(0) == '@';
}

protected int getCacheLimit() {
return getContext().getOptions().INTEROP_WRITE_CACHE;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
/*
* Copyright (c) 2013, 2016 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.interop;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.interop.CanResolve;
import com.oracle.truffle.api.interop.Message;
import com.oracle.truffle.api.interop.MessageResolution;
import com.oracle.truffle.api.interop.Resolve;
import com.oracle.truffle.api.interop.TruffleObject;
import com.oracle.truffle.api.interop.UnsupportedMessageException;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.api.profiles.ConditionProfile;
import org.jruby.truffle.RubyContext;
import org.jruby.truffle.RubyLanguage;
import org.jruby.truffle.core.Layouts;
import org.jruby.truffle.core.rope.Rope;
import org.jruby.truffle.core.string.StringOperations;
import org.jruby.truffle.language.RubyGuards;
import org.jruby.truffle.language.dispatch.DispatchAction;
import org.jruby.truffle.language.dispatch.DispatchHeadNode;
import org.jruby.truffle.language.dispatch.MissingBehavior;

@MessageResolution(
receiverType = DynamicObject.class,
language = RubyLanguage.class
)
public class RubyMessageResolution {

@CanResolve
public abstract static class Check extends Node {

protected static boolean test(DynamicObject receiver) {
return RubyGuards.isRubyBasicObject(receiver);
}

}

@Resolve(message = "EXECUTE")
public static final class ForeignExecuteNode extends Node {

@Child private Node findContextNode;
@Child private ForeignExecuteHelperNode executeMethodNode;

protected Object access(VirtualFrame frame, DynamicObject object, Object[] arguments) {
return getHelperNode().executeCall(frame, object, arguments);
}

private ForeignExecuteHelperNode getHelperNode() {
if (executeMethodNode == null) {
CompilerDirectives.transferToInterpreter();
findContextNode = insert(RubyLanguage.INSTANCE.unprotectedCreateFindContextNode());
final RubyContext context = RubyLanguage.INSTANCE.unprotectedFindContext(findContextNode);
executeMethodNode = insert(ForeignExecuteHelperNodeGen.create(context, null, null));
}

return executeMethodNode;
}

}

@Resolve(message = "GET_SIZE")
public static final class ForeignGetSizeNode extends Node {

@Child private Node findContextNode;
@Child private DispatchHeadNode dispatchNode;

protected Object access(VirtualFrame frame, DynamicObject object) {
return getDispatchNode().dispatch(frame, object, "size", null, new Object[] {});
}

private DispatchHeadNode getDispatchNode() {
if (dispatchNode == null) {
CompilerDirectives.transferToInterpreter();
findContextNode = insert(RubyLanguage.INSTANCE.unprotectedCreateFindContextNode());
final RubyContext context = RubyLanguage.INSTANCE.unprotectedFindContext(findContextNode);
dispatchNode = insert(new DispatchHeadNode(context, true, MissingBehavior.CALL_METHOD_MISSING, DispatchAction.CALL_METHOD));
}

return dispatchNode;
}

}

@Resolve(message = "HAS_SIZE")
public static final class ForeignHasSizeNode extends Node {

public Object access(VirtualFrame frame, DynamicObject object) {
return RubyGuards.isRubyArray(object) || RubyGuards.isRubyHash(object) || RubyGuards.isRubyString(object);
}

}

@Resolve(message = "INVOKE")
public static final class ForeignInvokeNode extends Node {

@Child private Node findContextNode;
@Child private DispatchHeadNode dispatchHeadNode;

protected Object access(VirtualFrame frame, DynamicObject receiver, String name, Object[] arguments) {
return getDispatchHeadNode().dispatch(frame, receiver, name, null, arguments);
}

private DispatchHeadNode getDispatchHeadNode() {
if (dispatchHeadNode == null) {
CompilerDirectives.transferToInterpreter();
findContextNode = insert(RubyLanguage.INSTANCE.unprotectedCreateFindContextNode());
final RubyContext context = RubyLanguage.INSTANCE.unprotectedFindContext(findContextNode);
dispatchHeadNode = insert(new DispatchHeadNode(context, true, MissingBehavior.CALL_METHOD_MISSING, DispatchAction.CALL_METHOD));
}

return dispatchHeadNode;
}

}

@Resolve(message = "IS_BOXED")
public static final class ForeignIsBoxedNode extends Node {

protected Object access(VirtualFrame frame, DynamicObject object) {
return RubyGuards.isRubyString(object) && StringOperations.rope(object).byteLength() == 1;
}

}

@Resolve(message = "IS_EXECUTABLE")
public static final class ForeignIsExecutableNode extends Node {

protected Object access(VirtualFrame frame, DynamicObject object) {
return RubyGuards.isRubyMethod(object) || RubyGuards.isRubyProc(object);
}

}

@Resolve(message = "IS_NULL")
public static final class ForeignIsNullNode extends Node {

@Child private Node findContextNode;
@CompilationFinal RubyContext context;

protected Object access(VirtualFrame frame, DynamicObject object) {
return object == getContext().getCoreLibrary().getNilObject();
}

private RubyContext getContext() {
if (context == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
findContextNode = insert(RubyLanguage.INSTANCE.unprotectedCreateFindContextNode());
context = RubyLanguage.INSTANCE.unprotectedFindContext(findContextNode);
}

return context;
}

}

@Resolve(message = "READ")
public static final class ForeignReadNode extends Node {

@Child private Node findContextNode;
@Child private ForeignReadStringCachingHelperNode helperNode;

protected Object access(VirtualFrame frame, DynamicObject object, Object name) {
return getHelperNode().executeStringCachingHelper(frame, object, name);
}

private ForeignReadStringCachingHelperNode getHelperNode() {
if (helperNode == null) {
CompilerDirectives.transferToInterpreter();
findContextNode = insert(RubyLanguage.INSTANCE.unprotectedCreateFindContextNode());
final RubyContext context = RubyLanguage.INSTANCE.unprotectedFindContext(findContextNode);
helperNode = insert(ForeignReadStringCachingHelperNodeGen.create(context, null, null));
}

return helperNode;
}

}

@Resolve(message = "UNBOX")
public static final class ForeignUnboxNode extends Node {

private final ConditionProfile stringProfile = ConditionProfile.createBinaryProfile();
private final ConditionProfile emptyProfile = ConditionProfile.createBinaryProfile();

protected Object access(VirtualFrame frame, DynamicObject object) {
if (stringProfile.profile(RubyGuards.isRubyString(object))) {
final Rope rope = Layouts.STRING.getRope(object);

if (emptyProfile.profile(rope.byteLength() == 0)) {
unsuported();
throw new IllegalStateException();
} else {
return rope.get(0);
}
} else {
return object;
}
}

@CompilerDirectives.TruffleBoundary
private void unsuported() {
UnsupportedMessageException.raise(Message.UNBOX);
}

}

@Resolve(message = "WRITE")
public static final class ForeignWriteNode extends Node {

@Child private Node findContextNode;
@Child private ForeignWriteStringCachingHelperNode helperNode;

protected Object access(VirtualFrame frame, DynamicObject object, Object name, Object value) {
return getHelperNode().executeStringCachingHelper(frame, object, name, value);
}

private ForeignWriteStringCachingHelperNode getHelperNode() {
if (helperNode == null) {
CompilerDirectives.transferToInterpreter();
findContextNode = insert(RubyLanguage.INSTANCE.unprotectedCreateFindContextNode());
final RubyContext context = RubyLanguage.INSTANCE.unprotectedFindContext(findContextNode);
helperNode = insert(ForeignWriteStringCachingHelperNodeGen.create(context, null, null, null));
}

return helperNode;
}

}

}
Original file line number Diff line number Diff line change
@@ -41,7 +41,7 @@ public String toString(DynamicObject object) {

@Override
public ForeignAccess getForeignAccessFactory(DynamicObject object) {
return RubyObjectTypeForeign.ACCESS;
return null;
}

public static boolean isInstance(TruffleObject object) {