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: 662fe3621f93
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: ca651ec932fd
Choose a head ref
  • 2 commits
  • 9 files changed
  • 1 contributor

Commits on May 11, 2016

  1. Copy the full SHA
    1e171fc View commit details
  2. [Truffle] Replace 6 specializations by one in interop by doing the ca…

    …ching in ToJavaStringNode.
    eregon committed May 11, 2016
    Copy the full SHA
    ca651ec View commit details
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
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,
@@ -9,6 +7,8 @@
* 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.dsl.Cached;
import com.oracle.truffle.api.dsl.NodeChild;
@@ -27,7 +27,7 @@
@NodeChild("receiver"),
@NodeChild("name"),
@NodeChild("stringName"),
@NodeChild("startsAt")
@NodeChild("isIVar")
})
abstract class ForeignReadStringCachedHelperNode extends RubyNode {

@@ -40,62 +40,49 @@ abstract class ForeignReadStringCachedHelperNode extends RubyNode {

protected final static String INDEX_METHOD_NAME = "[]";

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

@Specialization(guards = "startsAt(startsAt)")
@Specialization(guards = "isIVar")
public Object readInstanceVariable(
DynamicObject receiver,
Object name,
String stringName,
boolean startsAt,
boolean isIVar,
@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())"
}
)
@Specialization(guards = {
"!isIVar",
"methodDefined(frame, receiver, stringName, getDefinedNode())"
})
public Object callMethod(
VirtualFrame frame,
DynamicObject receiver,
Object name,
String stringName,
boolean startsAt) {
boolean isIVar) {
return getCallNode().call(frame, receiver, stringName, null);
}

@Specialization(
guards = {
"notStartsAt(startsAt)",
"!methodDefined(frame, receiver, stringName, getDefinedNode())",
"methodDefined(frame, receiver, INDEX_METHOD_NAME, getIndexDefinedNode())"
}
)
@Specialization(guards = {
"!isIVar",
"!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) {
boolean isIVar) {
return getCallNode().call(frame, receiver, "[]", null, name);
}

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

protected DoesRespondDispatchHeadNode getDefinedNode() {
if (definedNode == null) {
CompilerDirectives.transferToInterpreter();
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
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,
@@ -9,6 +7,7 @@
* 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.dsl.Cached;
@@ -20,8 +19,6 @@
import com.oracle.truffle.api.object.DynamicObject;
import org.jruby.truffle.Layouts;
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;

@@ -32,147 +29,52 @@
})
abstract class ForeignReadStringCachingHelperNode extends RubyNode {

@Child IsStringLikeNode isStringLikeNode;

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,
@Specialization(guards = "isStringLike(name)")
public Object cacheStringLikeAndForward(VirtualFrame frame, DynamicObject receiver, Object name,
@Cached("create()") ToJavaStringNode toJavaStringNode,
@Cached("createNextHelper()") ForeignReadStringCachedHelperNode nextHelper) {
return nextHelper.executeStringCachedHelper(frame, receiver, cachedName, cachedString, cachedStartsWithAt);
String nameAsJavaString = toJavaStringNode.executeToJavaString(name);
boolean isIVar = isIVar(nameAsJavaString);
return nextHelper.executeStringCachedHelper(frame, receiver, name, nameAsJavaString, isIVar);
}

@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(guards = { "isRubyString(receiver)", "inRange(receiver, index)" })
public int indexString(DynamicObject receiver, int index) {
return Layouts.STRING.getRope(receiver).get(index);
}

@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));
@Specialization(guards = { "isRubyString(receiver)", "!inRange(receiver, index)" })
public int indexStringOutOfRange(DynamicObject receiver, int index) {
return 0;
}

protected ForeignReadStringCachedHelperNode createNextHelper() {
return ForeignReadStringCachedHelperNodeGen.create(null, null, null, null);
protected boolean inRange(DynamicObject string, int index) {
return index >= 0 && index < Layouts.STRING.getRope(string).byteLength();
}

@CompilerDirectives.TruffleBoundary
protected String objectToString(DynamicObject string) {
return string.toString();
}
protected boolean isStringLike(Object value) {
if (isStringLikeNode == null) {
CompilerDirectives.transferToInterpreter();
isStringLikeNode = insert(IsStringLikeNode.create());
}

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

@CompilerDirectives.TruffleBoundary
protected boolean startsWithAt(String name) {
protected boolean isIVar(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;
protected ForeignReadStringCachedHelperNode createNextHelper() {
return ForeignReadStringCachedHelperNodeGen.create(null, null, null, null);
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
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,
@@ -9,6 +7,8 @@
* 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.dsl.Cached;
import com.oracle.truffle.api.dsl.NodeChild;
@@ -27,86 +27,73 @@
@NodeChild("receiver"),
@NodeChild("name"),
@NodeChild("stringName"),
@NodeChild("startsAt"),
@NodeChild("isIVar"),
@NodeChild("value")
})
abstract class ForeignWriteStringCachedHelperNode extends RubyNode {

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

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

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

@Specialization(guards = "startsAt(startsAt)")
@Specialization(guards = "isIVar")
public Object readInstanceVariable(
DynamicObject receiver,
Object name,
String stringName,
boolean startsAt,
boolean isIVar,
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())"
}
)
@Specialization(guards = { "not(isIVar)", "methodDefined(frame, receiver, writeMethodName, getDefinedNode())" })
public Object callMethod(
VirtualFrame frame,
DynamicObject receiver,
Object name,
String stringName,
boolean startsAt,
boolean isIVar,
Object value,
@Cached("createWriteMethodName(stringName)") String writeMethodName) {
return getCallNode().call(frame, receiver, writeMethodName, null, value);
}

// Workaround for DSL bug
protected boolean not(boolean value) {
return !value;
}

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

@Specialization(
guards = {
"notStartsAt(startsAt)",
"!methodDefined(frame, receiver, writeMethodName, getDefinedNode())",
"methodDefined(frame, receiver, INDEX_METHOD_NAME, getIndexDefinedNode())"
}
)
@Specialization(guards = {
"!isIVar",
"!methodDefined(frame, receiver, writeMethodName, getDefinedNode())",
"methodDefined(frame, receiver, INDEX_SET_METHOD_NAME, getIndexDefinedNode())"
})
public Object index(
VirtualFrame frame,
DynamicObject receiver,
Object name,
String stringName,
boolean startsAt,
boolean isIVar,
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();
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
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,
@@ -9,6 +7,8 @@
* 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.dsl.Cached;
import com.oracle.truffle.api.dsl.ImportStatic;
@@ -18,8 +18,6 @@
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;

@@ -31,128 +29,38 @@
})
abstract class ForeignWriteStringCachingHelperNode extends RubyNode {

@Child IsStringLikeNode isStringLikeNode;

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

public abstract Object executeStringCachingHelper(VirtualFrame frame, DynamicObject receiver,
Object name, Object value);
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,
@Specialization(guards = "isStringLike(name)")
public Object cacheStringLikeAndForward(VirtualFrame frame, DynamicObject receiver, Object name, Object value,
@Cached("create()") ToJavaStringNode toJavaStringNode,
@Cached("createNextHelper()") ForeignWriteStringCachedHelperNode nextHelper) {
return nextHelper.executeStringCachedHelper(frame, receiver, name, cachedString, cachedStartsWithAt, value);
String nameAsJavaString = toJavaStringNode.executeToJavaString(name);
boolean isIVar = isIVar(nameAsJavaString);
return nextHelper.executeStringCachedHelper(frame, receiver, name, nameAsJavaString, isIVar, 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);
}
protected boolean isStringLike(Object value) {
if (isStringLikeNode == null) {
CompilerDirectives.transferToInterpreter();
isStringLikeNode = insert(IsStringLikeNode.create());
}

@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);
return isStringLikeNode.executeIsStringLike(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 boolean isIVar(String name) {
return !name.isEmpty() && name.charAt(0) == '@';
}

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
@@ -126,14 +126,14 @@ public Object invokeCached(
Object[] args,
@Cached("args.length") int cachedArgsLength,
@Cached("createInvokeNode(cachedArgsLength)") Node invokeNode,
@Cached("createToJavaStringNode()") ToJavaStringNode toJavaStringNode,
@Cached("create()") ToJavaStringNode toJavaStringNode,
@Cached("create()") BranchProfile exceptionProfile) {
try {
return ForeignAccess.sendInvoke(
invokeNode,
frame,
receiver,
toJavaStringNode.executeToJavaString(frame, identifier),
toJavaStringNode.executeToJavaString(identifier),
args);
} catch (UnsupportedTypeException
| ArityException
@@ -144,10 +144,6 @@ public Object invokeCached(
}
}

protected ToJavaStringNode createToJavaStringNode() {
return ToJavaStringNodeGen.create(getContext(), null, null);
}

@Specialization(
guards = "isRubyString(identifier) || isRubySymbol(identifier)",
contains = "invokeCached"
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright (c) 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.NodeChild;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.object.DynamicObject;
import org.jruby.truffle.language.RubyNode;

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

public static IsStringLikeNode create() {
return IsStringLikeNodeGen.create(null);
}

public abstract boolean executeIsStringLike(Object value);

@Specialization(guards = "isRubyString(value)")
boolean isRubyString(DynamicObject value) {
return true;
}

@Specialization(guards = "isRubySymbol(value)")
public boolean isRubySymbol(DynamicObject value) {
return true;
}

@Specialization
public boolean isJavaString(String value) {
return true;
}

@Specialization(guards = { "!isRubyString(value)", "!isRubySymbol(value)", "!isString(value)" })
public boolean notStringLike(Object value) {
return false;
}

}
Original file line number Diff line number Diff line change
@@ -34,12 +34,8 @@ public RubyToForeignNode(RubyContext context, SourceSection sourceSection) {
public String convert(
VirtualFrame frame,
DynamicObject value,
@Cached("createToJavaStringNode()") ToJavaStringNode toJavaStringNode) {
return toJavaStringNode.executeToJavaString(frame, value);
}

protected ToJavaStringNode createToJavaStringNode() {
return ToJavaStringNodeGen.create(getContext(), null, null);
@Cached("create()") ToJavaStringNode toJavaStringNode) {
return toJavaStringNode.executeToJavaString(value);
}

@Specialization(guards = {"!isRubyString(value)", "!isRubySymbol(value)"})
Original file line number Diff line number Diff line change
@@ -14,11 +14,8 @@
import com.oracle.truffle.api.dsl.ImportStatic;
import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.api.source.SourceSection;
import org.jruby.truffle.Layouts;
import org.jruby.truffle.RubyContext;
import org.jruby.truffle.core.rope.Rope;
import org.jruby.truffle.core.string.StringCachingGuards;
import org.jruby.truffle.language.RubyNode;
@@ -27,45 +24,52 @@
@NodeChild(value = "value", type = RubyNode.class)
public abstract class ToJavaStringNode extends RubyNode {

public ToJavaStringNode(RubyContext context, SourceSection sourceSection) {
super(context, sourceSection);
public static ToJavaStringNode create() {
return ToJavaStringNodeGen.create(null);
}

public abstract String executeToJavaString(VirtualFrame frame, Object value);
public abstract String executeToJavaString(Object value);

@Specialization(
guards = {
"isRubyString(value)",
"ropesEqual(value, cachedRope)"
},
limit = "getLimit()")
public String stringUncached(
DynamicObject value,
@Specialization(guards = { "isRubyString(value)", "ropesEqual(value, cachedRope)" }, limit = "getLimit()")
String stringCached(DynamicObject value,
@Cached("privatizeRope(value)") Rope cachedRope,
@Cached("value.toString()") String convertedString) {
return convertedString;
}

protected String objectToString(DynamicObject object) {
return object.toString();
}

@TruffleBoundary
@Specialization(guards = "isRubyString(value)", contains = "stringUncached")
public String stringCached(DynamicObject value) {
@Specialization(guards = "isRubyString(value)", contains = "stringCached")
public String stringUncached(DynamicObject value) {
return value.toString();
}

@Specialization(guards = "isRubySymbol(value)")
public String symbol(DynamicObject value) {
return Layouts.SYMBOL.getString(value);
@Specialization(guards = { "isRubySymbol(symbol)", "symbol == cachedSymbol" }, limit = "getLimit()")
public String symbolCached(DynamicObject symbol,
@Cached("symbol") DynamicObject cachedSymbol,
@Cached("symbolToString(symbol)") String convertedString) {
return convertedString;
}

@Specialization(guards = "isRubySymbol(symbol)", contains = "symbolCached")
public String symbolUncached(DynamicObject symbol) {
return symbolToString(symbol);
}

@Specialization
public String javaString(String value) {
@Specialization(guards = "string == cachedString", limit = "getLimit()")
public String javaStringCached(String string,
@Cached("string") String cachedString) {
return cachedString;
}

@Specialization(contains = "javaStringCached")
public String javaStringUncached(String value) {
return value;
}

protected String symbolToString(DynamicObject symbol) {
return Layouts.SYMBOL.getString(symbol);
}

protected int getLimit() {
return getContext().getOptions().INTEROP_CONVERT_CACHE;
}
Original file line number Diff line number Diff line change
@@ -47,7 +47,7 @@ public UncachedDispatchNode(RubyContext context, boolean ignoreVisibility, Dispa
this.missingBehavior = missingBehavior;
this.indirectCallNode = Truffle.getRuntime().createIndirectCallNode();
this.toSymbolNode = ToSymbolNodeGen.create(context, null, null);
this.toJavaStringNode = ToJavaStringNodeGen.create(context, null, null);
this.toJavaStringNode = ToJavaStringNodeGen.create();
this.metaClassNode = MetaClassNodeGen.create(context, null, null);
}

@@ -67,7 +67,7 @@ public Object executeDispatch(

final DynamicObject callerClass = ignoreVisibility ? null : metaClassNode.executeMetaClass(RubyArguments.getSelf(frame));

final InternalMethod method = lookup(callerClass, receiverObject, toJavaStringNode.executeToJavaString(frame, name), ignoreVisibility);
final InternalMethod method = lookup(callerClass, receiverObject, toJavaStringNode.executeToJavaString(name), ignoreVisibility);

if (method != null) {
if (dispatchAction == DispatchAction.CALL_METHOD) {