Skip to content
Snippets Groups Projects
Commit 0f060934 authored by stepan's avatar stepan
Browse files

Implement INTERNAL_GENERIC dispatch for INTERNAL builtins

parent da84091c
No related branches found
No related tags found
No related merge requests found
......@@ -34,21 +34,13 @@ import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Fallback;
import com.oracle.truffle.api.dsl.ImportStatic;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.object.DynamicObject;
import com.oracle.truffle.api.profiles.ConditionProfile;
import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.RemoveNamesAttributeNode;
import com.oracle.truffle.r.nodes.attributes.UnaryCopyAttributesNode;
import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
import com.oracle.truffle.r.nodes.builtin.base.AsVectorNodeGen.AsVectorInternalNodeGen;
import com.oracle.truffle.r.nodes.builtin.base.AsVectorNodeGen.AsVectorInternalNodeGen.CastPairListNodeGen;
import com.oracle.truffle.r.nodes.builtin.base.AsVectorNodeGen.AsVectorInternalNodeGen.DropAttributesNodeGen;
import com.oracle.truffle.r.nodes.function.CallMatcherNode;
import com.oracle.truffle.r.nodes.function.ClassHierarchyNode;
import com.oracle.truffle.r.nodes.function.ClassHierarchyNodeGen;
import com.oracle.truffle.r.nodes.function.S3FunctionLookupNode;
import com.oracle.truffle.r.nodes.function.S3FunctionLookupNode.Result;
import com.oracle.truffle.r.nodes.builtin.base.AsVectorNodeGen.CastPairListNodeGen;
import com.oracle.truffle.r.nodes.builtin.base.AsVectorNodeGen.DropAttributesNodeGen;
import com.oracle.truffle.r.nodes.objects.GetS4DataSlot;
import com.oracle.truffle.r.nodes.unary.CastComplexNode;
import com.oracle.truffle.r.nodes.unary.CastDoubleNode;
......@@ -60,7 +52,6 @@ import com.oracle.truffle.r.nodes.unary.CastNode;
import com.oracle.truffle.r.nodes.unary.CastRawNode;
import com.oracle.truffle.r.nodes.unary.CastStringNode;
import com.oracle.truffle.r.nodes.unary.CastSymbolNode;
import com.oracle.truffle.r.runtime.ArgumentsSignature;
import com.oracle.truffle.r.runtime.RError;
import com.oracle.truffle.r.runtime.RError.Message;
import com.oracle.truffle.r.runtime.RInternalError;
......@@ -85,301 +76,258 @@ import com.oracle.truffle.r.runtime.env.REnvironment;
import com.oracle.truffle.r.runtime.interop.ForeignArray2R;
import com.oracle.truffle.r.runtime.nodes.RBaseNode;
@ImportStatic(RRuntime.class)
@RBuiltin(name = "as.vector", kind = INTERNAL, parameterNames = {"x", "mode"}, dispatch = INTERNAL_GENERIC, behavior = COMPLEX)
public abstract class AsVector extends RBuiltinNode.Arg2 {
@Child private AsVectorInternal internal = AsVectorInternalNodeGen.create();
@Child private ClassHierarchyNode classHierarchy = ClassHierarchyNodeGen.create(false, false);
@Child private S3FunctionLookupNode lookup;
@Child private CallMatcherNode callMatcher;
private final ConditionProfile hasClassProfile = ConditionProfile.createBinaryProfile();
static {
Casts casts = new Casts(AsVector.class);
casts.arg("mode").mustBe(stringValue()).asStringVector().mustBe(singleElement()).findFirst();
}
protected static AsVectorInternal createInternal() {
return AsVectorInternalNodeGen.create();
@Child private GetS4DataSlot getS4DataSlotNode;
private final ConditionProfile indirectMatchProfile = ConditionProfile.createBinaryProfile();
protected static CastNode createCast(RType type) {
if (type != null) {
switch (type) {
case Any:
return null;
case Character:
return CastStringNode.createNonPreserving();
case Complex:
return CastComplexNode.createNonPreserving();
case Double:
return CastDoubleNode.createNonPreserving();
case Expression:
return CastExpressionNode.createNonPreserving();
case Function:
case Closure:
throw RInternalError.unimplemented("as.vector cast to 'function'");
case Integer:
return CastIntegerNode.createNonPreserving();
case List:
return CastListNodeGen.create(true, false, false);
case Logical:
return CastLogicalNode.createNonPreserving();
case PairList:
return CastPairListNodeGen.create();
case Raw:
return CastRawNode.createNonPreserving();
case Symbol:
return CastSymbolNode.createNonPreserving();
}
}
throw RError.error(RError.SHOW_CALLER, Message.INVALID_ARGUMENT, "mode");
}
private static final ArgumentsSignature SIGNATURE = ArgumentsSignature.get("x", "mode");
protected boolean matchesMode(String mode, String cachedMode) {
return mode == cachedMode || indirectMatchProfile.profile(cachedMode.equals(mode));
}
@TruffleBoundary
@Specialization
protected Object asVector(VirtualFrame frame, Object x, String mode) {
// TODO given dispatch = INTERNAL_GENERIC, this should not be necessary
// However, INTERNAL_GENERIC is not handled for INTERNAL builtins
RStringVector clazz = classHierarchy.execute(x);
if (hasClassProfile.profile(clazz != null)) {
// Note: this dispatch takes care of factor, because there is as.vector.factor
// specialization in R
if (lookup == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
lookup = insert(S3FunctionLookupNode.create(false, false));
}
Result lookupResult = lookup.execute(frame, "as.vector", clazz, null, frame.materialize(), null);
if (lookupResult != null) {
if (callMatcher == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
callMatcher = insert(CallMatcherNode.create(false));
}
return callMatcher.execute(frame, SIGNATURE, new Object[]{x, mode}, lookupResult.function, lookupResult.targetFunctionName, lookupResult.createS3Args(frame));
}
}
return internal.execute(x, mode);
protected Object asVector(@SuppressWarnings("unused") REnvironment x, String mode) {
RType type = RType.fromMode(mode);
throw RError.error(RError.SHOW_CALLER, Message.CANNOT_COERCE, RType.Environment.getName(), type != null ? type.getName() : mode);
}
@ImportStatic(RRuntime.class)
public abstract static class AsVectorInternal extends Node {
public abstract Object execute(Object x, String mode);
@Child private GetS4DataSlot getS4DataSlotNode;
private final ConditionProfile indirectMatchProfile = ConditionProfile.createBinaryProfile();
protected static CastNode createCast(RType type) {
if (type != null) {
switch (type) {
case Any:
return null;
case Character:
return CastStringNode.createNonPreserving();
case Complex:
return CastComplexNode.createNonPreserving();
case Double:
return CastDoubleNode.createNonPreserving();
case Expression:
return CastExpressionNode.createNonPreserving();
case Function:
case Closure:
throw RInternalError.unimplemented("as.vector cast to 'function'");
case Integer:
return CastIntegerNode.createNonPreserving();
case List:
return CastListNodeGen.create(true, false, false);
case Logical:
return CastLogicalNode.createNonPreserving();
case PairList:
return CastPairListNodeGen.create();
case Raw:
return CastRawNode.createNonPreserving();
case Symbol:
return CastSymbolNode.createNonPreserving();
}
protected static boolean isREnvironment(Object value) {
return value instanceof REnvironment;
}
// there should never be more than ~12 specializations
@SuppressWarnings("unused")
@Specialization(limit = "99", guards = {"!isREnvironment(x)", "matchesMode(mode, cachedMode)"})
protected Object asVector(Object x, String mode,
@Cached("mode") String cachedMode,
@Cached("fromMode(cachedMode)") RType type,
@Cached("createCast(type)") CastNode cast,
@Cached("create(type)") DropAttributesNode drop,
@Cached("create()") ForeignArray2R foreignArray2R) {
if (RRuntime.isForeignObject(x)) {
Object o = foreignArray2R.convert(x);
if (!RRuntime.isForeignObject(o)) {
return cast == null ? o : cast.doCast(o);
}
throw RError.error(RError.SHOW_CALLER, Message.INVALID_ARGUMENT, "mode");
throw RError.error(RError.SHOW_CALLER, RError.Message.CANNOT_COERCE_EXTERNAL_OBJECT_TO_VECTOR, type == RType.List ? "list" : "vector");
}
protected boolean matchesMode(String mode, String cachedMode) {
return mode == cachedMode || indirectMatchProfile.profile(cachedMode.equals(mode));
Object result = x;
if (x instanceof RS4Object) {
result = getS4DataSlot((RS4Object) x);
}
return drop.execute(result, cast == null ? x : cast.doCast(result));
}
@TruffleBoundary
@Specialization
protected Object asVector(@SuppressWarnings("unused") REnvironment x, String mode) {
RType type = RType.fromMode(mode);
throw RError.error(RError.SHOW_CALLER, Message.CANNOT_COERCE, RType.Environment.getName(), type != null ? type.getName() : mode);
private Object getS4DataSlot(RS4Object o) {
if (getS4DataSlotNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
getS4DataSlotNode = insert(GetS4DataSlot.create(RType.Any));
}
return getS4DataSlotNode.executeObject(o);
}
protected static boolean isREnvironment(Object value) {
return value instanceof REnvironment;
}
public abstract static class DropAttributesNode extends RBaseNode {
// there should never be more than ~12 specializations
@SuppressWarnings("unused")
@Specialization(limit = "99", guards = {"!isREnvironment(x)", "matchesMode(mode, cachedMode)"})
protected Object asVector(Object x, String mode,
@Cached("mode") String cachedMode,
@Cached("fromMode(cachedMode)") RType type,
@Cached("createCast(type)") CastNode cast,
@Cached("create(type)") DropAttributesNode drop,
@Cached("create()") ForeignArray2R foreignArray2R) {
if (RRuntime.isForeignObject(x)) {
Object o = foreignArray2R.convert(x);
if (!RRuntime.isForeignObject(o)) {
return cast == null ? o : cast.doCast(o);
}
throw RError.error(RError.SHOW_CALLER, RError.Message.CANNOT_COERCE_EXTERNAL_OBJECT_TO_VECTOR, type == RType.List ? "list" : "vector");
}
Object result = x;
if (x instanceof RS4Object) {
result = getS4DataSlot((RS4Object) x);
}
return drop.execute(result, cast == null ? x : cast.doCast(result));
}
private final RType targetType;
private Object getS4DataSlot(RS4Object o) {
if (getS4DataSlotNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
getS4DataSlotNode = insert(GetS4DataSlot.create(RType.Any));
}
return getS4DataSlotNode.executeObject(o);
protected DropAttributesNode(RType targetType) {
this.targetType = targetType;
}
public abstract static class DropAttributesNode extends RBaseNode {
private final RType targetType;
public abstract Object execute(Object original, Object o);
protected DropAttributesNode(RType targetType) {
this.targetType = targetType;
}
public abstract Object execute(Object original, Object o);
public static DropAttributesNode create(RType targetType) {
return DropAttributesNodeGen.create(targetType);
}
public static DropAttributesNode create(RType targetType) {
return DropAttributesNodeGen.create(targetType);
}
protected static boolean hasAttributes(Class<? extends RAbstractAtomicVector> clazz, RAbstractAtomicVector o) {
return clazz.cast(o).getAttributes() != null;
}
protected static boolean hasAttributes(Class<? extends RAbstractAtomicVector> clazz, RAbstractAtomicVector o) {
return clazz.cast(o).getAttributes() != null;
}
@Specialization(guards = "o.getAttributes() == null")
protected static RSharingAttributeStorage drop(@SuppressWarnings("unused") Object original, RSharingAttributeStorage o) {
// quickly reject any RSharingAttributeStorage without attributes
return o;
}
@Specialization(guards = "o.getAttributes() == null")
protected static RSharingAttributeStorage drop(@SuppressWarnings("unused") Object original, RSharingAttributeStorage o) {
// quickly reject any RSharingAttributeStorage without attributes
return o;
}
@Specialization(guards = {"o.getClass() == oClass", "o.getAttributes() != null"})
protected RAbstractVector dropCached(@SuppressWarnings("unused") Object original, RAbstractAtomicVector o,
@Cached("o.getClass()") Class<? extends RAbstractAtomicVector> oClass,
@Cached("createBinaryProfile()") ConditionProfile profile) {
return profile.profile(hasAttributes(oClass, o)) ? oClass.cast(o).copyDropAttributes() : o;
}
@Specialization(guards = {"o.getClass() == oClass", "o.getAttributes() != null"})
protected RAbstractVector dropCached(@SuppressWarnings("unused") Object original, RAbstractAtomicVector o,
@Cached("o.getClass()") Class<? extends RAbstractAtomicVector> oClass,
@Cached("createBinaryProfile()") ConditionProfile profile) {
return profile.profile(hasAttributes(oClass, o)) ? oClass.cast(o).copyDropAttributes() : o;
}
@Specialization(replaces = "dropCached", guards = "o.getAttributes() != null")
protected RAbstractVector drop(@SuppressWarnings("unused") Object original, RAbstractAtomicVector o,
@Cached("createBinaryProfile()") ConditionProfile profile) {
return profile.profile(o.getAttributes() != null) ? o.copyDropAttributes() : o;
}
@Specialization(replaces = "dropCached", guards = "o.getAttributes() != null")
protected RAbstractVector drop(@SuppressWarnings("unused") Object original, RAbstractAtomicVector o,
@Cached("createBinaryProfile()") ConditionProfile profile) {
return profile.profile(o.getAttributes() != null) ? o.copyDropAttributes() : o;
}
@Specialization(guards = {"o.isLanguage()", "o.getAttributes() != null"})
protected RPairList drop(@SuppressWarnings("unused") Object original, RPairList o) {
switch (targetType) {
case Any:
case PairList:
case List:
return o;
}
return RDataFactory.createLanguage(o.getClosure());
@Specialization(guards = {"o.isLanguage()", "o.getAttributes() != null"})
protected RPairList drop(@SuppressWarnings("unused") Object original, RPairList o) {
switch (targetType) {
case Any:
case PairList:
case List:
return o;
}
return RDataFactory.createLanguage(o.getClosure());
}
@Specialization(guards = "o.getAttributes() != null")
protected static RSymbol drop(Object original, RSymbol o) {
return original == o ? o : RDataFactory.createSymbol(o.getName());
}
@Specialization(guards = "o.getAttributes() != null")
protected static RSymbol drop(Object original, RSymbol o) {
return original == o ? o : RDataFactory.createSymbol(o.getName());
}
@Specialization(guards = "pairList.getAttributes() != null")
protected Object dropPairList(@SuppressWarnings("unused") Object original, RPairList pairList) {
// dropping already done in the cast node CastPairListNode below
return pairList;
}
@Specialization(guards = "pairList.getAttributes() != null")
protected Object dropPairList(@SuppressWarnings("unused") Object original, RPairList pairList) {
// dropping already done in the cast node CastPairListNode below
return pairList;
}
@Specialization(guards = "list.getAttributes() != null")
protected Object drop(Object original, RAbstractListVector list,
@Cached("create()") UnaryCopyAttributesNode copyAttributesNode,
@Cached("createBinaryProfile()") ConditionProfile originalIsAtomic) {
if (originalIsAtomic.profile(getRType(original).isAtomic())) {
return list;
}
if (original instanceof RAbstractVector) {
copyAttributesNode.execute(list, (RAbstractVector) original);
}
@Specialization(guards = "list.getAttributes() != null")
protected Object drop(Object original, RAbstractListVector list,
@Cached("create()") UnaryCopyAttributesNode copyAttributesNode,
@Cached("createBinaryProfile()") ConditionProfile originalIsAtomic) {
if (originalIsAtomic.profile(getRType(original).isAtomic())) {
return list;
}
@Fallback
protected Object drop(@SuppressWarnings("unused") Object original, Object o) {
// includes RExpression, RSymbol
return o;
if (original instanceof RAbstractVector) {
copyAttributesNode.execute(list, (RAbstractVector) original);
}
return list;
}
private static RType getRType(Object original) {
return original instanceof RTypedValue ? ((RTypedValue) original).getRType() : RType.Any;
}
@Fallback
protected Object drop(@SuppressWarnings("unused") Object original, Object o) {
// includes RExpression, RSymbol
return o;
}
// NOTE: this cast takes care of attributes dropping too. Names are never dropped, and other
// attrs copied only in the case of list, pairlist and expressions (all of them are
// RAbstractListVector).
protected abstract static class CastPairListNode extends CastNode {
private static RType getRType(Object original) {
return original instanceof RTypedValue ? ((RTypedValue) original).getRType() : RType.Any;
}
}
@Specialization
protected Object castPairlist(RExpression x,
@Cached("create()") RemoveNamesAttributeNode removeNamesAttributeNode) {
return fromVectorWithAttributes(x, removeNamesAttributeNode);
}
// NOTE: this cast takes care of attributes dropping too. Names are never dropped, and other
// attrs copied only in the case of list, pairlist and expressions (all of them are
// RAbstractListVector).
protected abstract static class CastPairListNode extends CastNode {
@Specialization
protected Object castPairlist(RAbstractListVector x,
@Cached("create()") RemoveNamesAttributeNode removeNamesAttributeNode) {
return fromVectorWithAttributes(x, removeNamesAttributeNode);
}
@Specialization
protected Object castPairlist(RExpression x,
@Cached("create()") RemoveNamesAttributeNode removeNamesAttributeNode) {
return fromVectorWithAttributes(x, removeNamesAttributeNode);
}
@Specialization
protected Object castPairlist(RAbstractAtomicVector x) {
return x.getLength() == 0 ? RNull.instance : fromVector(x);
}
@Specialization
protected Object castPairlist(RAbstractListVector x,
@Cached("create()") RemoveNamesAttributeNode removeNamesAttributeNode) {
return fromVectorWithAttributes(x, removeNamesAttributeNode);
}
@Specialization
protected Object doRNull(@SuppressWarnings("unused") RNull value) {
return RNull.instance;
}
@Specialization
protected Object castPairlist(RAbstractAtomicVector x) {
return x.getLength() == 0 ? RNull.instance : fromVector(x);
}
@Specialization
protected Object doRLanguage(RPairList pairlist) {
return pairlist;
}
@Specialization
protected Object doRNull(@SuppressWarnings("unused") RNull value) {
return RNull.instance;
}
@Fallback
protected Object castPairlist(Object x) {
String name = x instanceof RTypedValue ? ((RTypedValue) x).getRType().getName() : x.getClass().getSimpleName();
throw error(Message.CANNOT_COERCE, name, RType.PairList.getName());
}
@Specialization
protected Object doRLanguage(RPairList pairlist) {
return pairlist;
}
@TruffleBoundary
private static Object fromVectorWithAttributes(RAbstractContainer x, RemoveNamesAttributeNode removeNamesAttributeNode) {
if (x.getLength() == 0) {
return RNull.instance;
} else {
Object list = fromVector(x);
DynamicObject attributes = x.getAttributes();
if (attributes != null) {
((RPairList) list).initAttributes(RAttributesLayout.copy(attributes));
// names are part of the list already
removeNamesAttributeNode.execute(list);
}
return list;
@Fallback
protected Object castPairlist(Object x) {
String name = x instanceof RTypedValue ? ((RTypedValue) x).getRType().getName() : x.getClass().getSimpleName();
throw error(Message.CANNOT_COERCE, name, RType.PairList.getName());
}
@TruffleBoundary
private static Object fromVectorWithAttributes(RAbstractContainer x, RemoveNamesAttributeNode removeNamesAttributeNode) {
if (x.getLength() == 0) {
return RNull.instance;
} else {
Object list = fromVector(x);
DynamicObject attributes = x.getAttributes();
if (attributes != null) {
((RPairList) list).initAttributes(RAttributesLayout.copy(attributes));
// names are part of the list already
removeNamesAttributeNode.execute(list);
}
return list;
}
}
@TruffleBoundary
private static RPairList fromVector(RAbstractContainer x) {
Object list = RNull.instance;
RStringVector names = x.getNames();
// "" name turns into NULL, but only if there are only "" names, otherwise "" stays
// see the tests for examples
if (names != null) {
boolean allEmpty = true;
for (int i = 0; i < names.getLength(); i++) {
if (!names.getDataAt(i).isEmpty()) {
allEmpty = false;
break;
}
}
if (allEmpty) {
names = null;
@TruffleBoundary
private static RPairList fromVector(RAbstractContainer x) {
Object list = RNull.instance;
RStringVector names = x.getNames();
// "" name turns into NULL, but only if there are only "" names, otherwise "" stays
// see the tests for examples
if (names != null) {
boolean allEmpty = true;
for (int i = 0; i < names.getLength(); i++) {
if (!names.getDataAt(i).isEmpty()) {
allEmpty = false;
break;
}
}
for (int i = x.getLength() - 1; i >= 0; i--) {
Object name = names == null ? RNull.instance : RDataFactory.createSymbolInterned(names.getDataAt(i));
Object data = x.getDataAtAsObject(i);
list = RDataFactory.createPairList(data, list, name);
if (allEmpty) {
names = null;
}
return (RPairList) list;
}
for (int i = x.getLength() - 1; i >= 0; i--) {
Object name = names == null ? RNull.instance : RDataFactory.createSymbolInterned(names.getDataAt(i));
Object data = x.getDataAtAsObject(i);
list = RDataFactory.createPairList(data, list, name);
}
return (RPairList) list;
}
}
}
......@@ -29,11 +29,18 @@ import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.ExplodeLoop;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.profiles.ConditionProfile;
import com.oracle.truffle.api.source.SourceSection;
import com.oracle.truffle.r.nodes.control.OperatorNode;
import com.oracle.truffle.r.nodes.function.CallMatcherNode;
import com.oracle.truffle.r.nodes.function.ClassHierarchyNode;
import com.oracle.truffle.r.nodes.function.ClassHierarchyNodeGen;
import com.oracle.truffle.r.nodes.function.PromiseHelperNode.PromiseCheckHelperNode;
import com.oracle.truffle.r.nodes.function.S3FunctionLookupNode;
import com.oracle.truffle.r.nodes.function.S3FunctionLookupNode.Result;
import com.oracle.truffle.r.nodes.function.visibility.SetVisibilityNode;
import com.oracle.truffle.r.runtime.ArgumentsSignature;
import com.oracle.truffle.r.runtime.RDispatch;
import com.oracle.truffle.r.runtime.RError;
import com.oracle.truffle.r.runtime.RError.Message;
import com.oracle.truffle.r.runtime.RInternalError;
......@@ -42,6 +49,7 @@ import com.oracle.truffle.r.runtime.conn.RConnection;
import com.oracle.truffle.r.runtime.context.RContext;
import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
import com.oracle.truffle.r.runtime.data.REmpty;
import com.oracle.truffle.r.runtime.data.RStringVector;
import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
import com.oracle.truffle.r.runtime.nodes.RBaseNode;
import com.oracle.truffle.r.runtime.nodes.RNode;
......@@ -193,6 +201,7 @@ public abstract class InternalNode extends OperatorNode {
@Children protected final RNode[] arguments;
@Child private RBuiltinNode builtin;
@Child private SetVisibilityNode visibility = SetVisibilityNode.create();
@Child private InternalGenericDispatchNode internalGenericDispatchNode;
InternalCallNode(SourceSection src, RSyntaxLookup operator, ArgumentsSignature outerSignature, RSyntaxNode[] outerArgs, RBuiltinFactory factory, RSyntaxElement[] args) {
super(src, operator, outerSignature, outerArgs);
......@@ -214,13 +223,28 @@ public abstract class InternalNode extends OperatorNode {
@Override
public Object execute(VirtualFrame frame) {
Object result = builtin.call(frame, prepareArgs(frame));
assert result != null : "builtins cannot return 'null': " + factory.getName();
assert !(result instanceof RConnection) : "builtins cannot return connection': " + factory.getName();
visibility.execute(frame, factory.getVisibility());
Object[] args = prepareArgs(frame);
Object result = doInternalDispatch(frame, factory, args);
if (result == null) {
result = builtin.call(frame, prepareArgs(frame));
assert result != null : "builtins cannot return 'null': " + factory.getName();
assert !(result instanceof RConnection) : "builtins cannot return connection': " + factory.getName();
visibility.execute(frame, factory.getVisibility());
}
return result;
}
private Object doInternalDispatch(VirtualFrame frame, RBuiltinFactory factory, Object[] args) {
if (factory.getDispatch() != RDispatch.INTERNAL_GENERIC) {
return null;
}
if (internalGenericDispatchNode == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
internalGenericDispatchNode = insert(new InternalGenericDispatchNode());
}
return internalGenericDispatchNode.doInternalDispatch(frame, factory, args);
}
@Override
public void voidExecute(VirtualFrame frame) {
builtin.call(frame, prepareArgs(frame));
......@@ -319,6 +343,41 @@ public abstract class InternalNode extends OperatorNode {
}
}
private static final class InternalGenericDispatchNode extends Node {
private final ConditionProfile hasClassProfile;
@Child private ClassHierarchyNode classHierarchy;
@Child private S3FunctionLookupNode lookup;
@Child private CallMatcherNode callMatcher;
InternalGenericDispatchNode() {
classHierarchy = ClassHierarchyNodeGen.create(false, false);
hasClassProfile = ConditionProfile.createBinaryProfile();
}
protected Object doInternalDispatch(VirtualFrame frame, RBuiltinFactory factory, Object[] args) {
if (args.length == 0) {
return null;
}
Object x = args[0];
RStringVector clazz = classHierarchy.execute(x);
if (hasClassProfile.profile(clazz != null)) {
if (lookup == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
lookup = insert(S3FunctionLookupNode.create(false, false, false));
}
Result lookupResult = lookup.execute(frame, factory.getGenericName(), clazz, null, frame.materialize(), null);
if (lookupResult != null) {
if (callMatcher == null) {
CompilerDirectives.transferToInterpreterAndInvalidate();
callMatcher = insert(CallMatcherNode.create(false));
}
return callMatcher.execute(frame, factory.getSignature(), args, lookupResult.function, lookupResult.targetFunctionName, lookupResult.createS3Args(frame));
}
}
return null;
}
}
@Override
public RSyntaxNode[] getSyntaxArguments() {
return outerArgs;
......
......@@ -63,10 +63,12 @@ public abstract class S3FunctionLookupNode extends RBaseNode {
protected final boolean throwsError;
protected final boolean nextMethod;
protected final boolean defaultMethod;
private S3FunctionLookupNode(boolean throwsError, boolean nextMethod) {
private S3FunctionLookupNode(boolean throwsError, boolean nextMethod, boolean defaultMethod) {
this.throwsError = throwsError;
this.nextMethod = nextMethod;
this.defaultMethod = defaultMethod;
}
@ValueType
......@@ -93,15 +95,19 @@ public abstract class S3FunctionLookupNode extends RBaseNode {
}
public static S3FunctionLookupNode create(boolean throwsError, boolean nextMethod) {
return new UseMethodFunctionLookupUninitializedNode(throwsError, nextMethod, 0);
return create(throwsError, nextMethod, true);
}
public static S3FunctionLookupNode create(boolean throwsError, boolean nextMethod, boolean defaultMethod) {
return new UseMethodFunctionLookupUninitializedNode(throwsError, nextMethod, defaultMethod, 0);
}
public static S3FunctionLookupNode createWithError() {
return new UseMethodFunctionLookupUninitializedNode(true, false, 0);
return new UseMethodFunctionLookupUninitializedNode(true, false, true, 0);
}
public static S3FunctionLookupNode createWithException() {
return new UseMethodFunctionLookupUninitializedNode(false, false, 0);
return new UseMethodFunctionLookupUninitializedNode(false, false, true, 0);
}
@FunctionalInterface
......@@ -115,7 +121,8 @@ public abstract class S3FunctionLookupNode extends RBaseNode {
}
@TruffleBoundary
private static Result performLookup(MaterializedFrame callerFrame, String genericName, String groupName, RStringVector type, boolean nextMethod, LookupOperation op, GetMethodsTable getTable) {
private static Result performLookup(MaterializedFrame callerFrame, String genericName, String groupName, RStringVector type, boolean nextMethod, boolean defaultMethod, LookupOperation op,
GetMethodsTable getTable) {
Result result;
Object methodsTable = getTable.get();
if (methodsTable instanceof RPromise) {
......@@ -129,13 +136,16 @@ public abstract class S3FunctionLookupNode extends RBaseNode {
}
// look for the default method
String functionName = genericName + RRuntime.RDOT + RRuntime.DEFAULT;
RFunction function = checkPromise(op.read(callerFrame, functionName, false));
if (function == null && methodsTableFrame != null) {
function = checkPromise(op.read(methodsTableFrame, functionName, true));
}
if (function != null) {
return new Result(genericName, function, RNull.instance, functionName, false);
if (defaultMethod) {
String functionName = genericName + RRuntime.RDOT + RRuntime.DEFAULT;
RFunction function = checkPromise(op.read(callerFrame, functionName, false));
if (function == null && methodsTableFrame != null) {
function = checkPromise(op.read(methodsTableFrame, functionName, true));
}
if (function != null) {
return new Result(genericName, function, RNull.instance, functionName, false);
}
}
return null;
}
......@@ -253,16 +263,17 @@ public abstract class S3FunctionLookupNode extends RBaseNode {
}
};
Result result = performLookup(callerFrame, genericName, group, type, next.nextMethod, op, getTable);
Result result = performLookup(callerFrame, genericName, group, type, next.nextMethod, next.defaultMethod, op, getTable);
UseMethodFunctionLookupCachedNode cachedNode;
if (result != null) {
cachedNode = new UseMethodFunctionLookupCachedNode(next.throwsError, next.nextMethod, genericName, type, group, null, unsuccessfulReadsCaller, unsuccessfulReadsTable,
cachedNode = new UseMethodFunctionLookupCachedNode(next.throwsError, next.nextMethod, next.defaultMethod, genericName, type, group, null, unsuccessfulReadsCaller, unsuccessfulReadsTable,
reads.methodsTableRead, reads.successfulRead, reads.successfulReadTable, result.function, result.clazz, result.targetFunctionName, result.groupMatch, next);
} else {
RFunction builtin = next.throwsError ? RContext.getInstance().lookupBuiltin(genericName) : null;
cachedNode = new UseMethodFunctionLookupCachedNode(next.throwsError, next.nextMethod, genericName, type, group, builtin, unsuccessfulReadsCaller, unsuccessfulReadsTable,
cachedNode = new UseMethodFunctionLookupCachedNode(next.throwsError, next.nextMethod, next.defaultMethod, genericName, type, group, builtin, unsuccessfulReadsCaller,
unsuccessfulReadsTable,
reads.methodsTableRead, null, null, null, null, null, false, next);
}
return cachedNode;
......@@ -272,8 +283,8 @@ public abstract class S3FunctionLookupNode extends RBaseNode {
private static final class UseMethodFunctionLookupUninitializedNode extends S3FunctionLookupNode {
private final int depth;
UseMethodFunctionLookupUninitializedNode(boolean throwsError, boolean nextMethod, int depth) {
super(throwsError, nextMethod);
UseMethodFunctionLookupUninitializedNode(boolean throwsError, boolean nextMethod, boolean defaultMethod, int depth) {
super(throwsError, nextMethod, defaultMethod);
this.depth = depth;
}
......@@ -281,10 +292,11 @@ public abstract class S3FunctionLookupNode extends RBaseNode {
public Result execute(VirtualFrame frame, String genericName, RStringVector type, String group, MaterializedFrame callerFrame, MaterializedFrame genericDefFrame) {
CompilerDirectives.transferToInterpreterAndInvalidate();
if (depth > MAX_CACHE_DEPTH) {
return replace(new UseMethodFunctionLookupGenericNode(throwsError, nextMethod)).execute(frame, genericName, type, group, callerFrame, genericDefFrame);
return replace(new UseMethodFunctionLookupGenericNode(throwsError, nextMethod, defaultMethod)).execute(frame, genericName, type, group, callerFrame, genericDefFrame);
} else {
UseMethodFunctionLookupCachedNode cachedNode = replace(
specialize(frame, genericName, type, group, callerFrame, genericDefFrame, new UseMethodFunctionLookupUninitializedNode(throwsError, nextMethod, depth + 1)));
specialize(frame, genericName, type, group, callerFrame, genericDefFrame,
new UseMethodFunctionLookupUninitializedNode(throwsError, nextMethod, defaultMethod, depth + 1)));
return cachedNode.execute(frame, genericName, type, group, callerFrame, genericDefFrame);
}
}
......@@ -316,10 +328,10 @@ public abstract class S3FunctionLookupNode extends RBaseNode {
private final String cachedGroup;
private final RFunction builtin;
UseMethodFunctionLookupCachedNode(boolean throwsError, boolean nextMethod, String genericName, RStringVector type, String group, RFunction builtin,
UseMethodFunctionLookupCachedNode(boolean throwsError, boolean nextMethod, boolean defaultMethod, String genericName, RStringVector type, String group, RFunction builtin,
List<ReadVariableNode> unsuccessfulReadsCaller, List<LocalReadVariableNode> unsuccessfulReadsTable, LocalReadVariableNode readS3MethodsTable, ReadVariableNode successfulRead,
LocalReadVariableNode successfulReadTable, RFunction function, Object clazz, String targetFunctionName, boolean groupMatch, S3FunctionLookupNode next) {
super(throwsError, nextMethod);
super(throwsError, nextMethod, defaultMethod);
this.cachedGenericName = genericName;
this.cachedGroup = group;
this.builtin = builtin;
......@@ -431,8 +443,8 @@ public abstract class S3FunctionLookupNode extends RBaseNode {
private static final class UseMethodFunctionLookupGenericNode extends S3FunctionLookupNode {
protected UseMethodFunctionLookupGenericNode(boolean throwsError, boolean nextMethod) {
super(throwsError, nextMethod);
protected UseMethodFunctionLookupGenericNode(boolean throwsError, boolean nextMethod, boolean defaultMethod) {
super(throwsError, nextMethod, defaultMethod);
}
@Override
......@@ -458,7 +470,7 @@ public abstract class S3FunctionLookupNode extends RBaseNode {
}
};
Result result = performLookup(callerFrame, genericName, group, type, nextMethod, op, getTable);
Result result = performLookup(callerFrame, genericName, group, type, nextMethod, defaultMethod, op, getTable);
if (result == null) {
if (throwsError) {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment