From 65eeba37c659ce8d74d15a639b60e9782dc8087d Mon Sep 17 00:00:00 2001
From: Florian Angerer <florian.angerer@oracle.com>
Date: Wed, 15 Nov 2017 13:55:53 +0100
Subject: [PATCH] Using operator '@' to send INVOKE to foreign objects.

---
 .../truffle/r/nodes/builtin/base/Slot.java    | 31 +++++++++++++++++--
 .../truffle/r/nodes/builtin/CastBuilder.java  |  5 +++
 .../truffle/r/nodes/builtin/RBuiltinNode.java |  4 +++
 .../truffle/r/nodes/function/RCallNode.java   | 14 ++++++++-
 4 files changed, 51 insertions(+), 3 deletions(-)

diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Slot.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Slot.java
index b1ee00c719..2ef8f6aa55 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Slot.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/Slot.java
@@ -13,18 +13,23 @@
 
 package com.oracle.truffle.r.nodes.builtin.base;
 
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.foreign;
 import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
 import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
 import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Specialization;
+import com.oracle.truffle.api.interop.TruffleObject;
 import com.oracle.truffle.api.profiles.ValueProfile;
 import com.oracle.truffle.r.nodes.access.AccessSlotNode;
 import com.oracle.truffle.r.nodes.access.AccessSlotNodeGen;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
+import com.oracle.truffle.r.nodes.function.RCallNode;
 import com.oracle.truffle.r.nodes.function.opt.UpdateShareableChildValueNode;
 import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.Utils;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.data.Closure;
@@ -33,12 +38,13 @@ import com.oracle.truffle.r.runtime.data.RPromise;
 @RBuiltin(name = "@", kind = PRIMITIVE, parameterNames = {"", ""}, nonEvalArgs = 1, behavior = COMPLEX)
 public abstract class Slot extends RBuiltinNode.Arg2 {
 
+    @CompilationFinal private boolean isLhsOfCall;
     @Child private UpdateShareableChildValueNode sharedAttrUpdate = UpdateShareableChildValueNode.create();
     @Child private AccessSlotNode accessSlotNode = AccessSlotNodeGen.create(true);
 
     static {
         Casts casts = new Casts(Slot.class);
-        casts.arg(0).asAttributable(true, true, true);
+        casts.arg(0).returnIf(foreign()).asAttributable(true, true, true);
     }
 
     private String getName(Object nameObj) {
@@ -55,7 +61,22 @@ public abstract class Slot extends RBuiltinNode.Arg2 {
         throw error(RError.Message.GENERIC, "invalid type or length for slot name");
     }
 
-    @Specialization
+    protected boolean isLhsOfForeignCall(Object o) {
+        return isLhsOfCall && RRuntime.isForeignObject(o);
+    }
+
+    @Specialization(guards = {"isLhsOfForeignCall(object)"})
+    protected Object getSlot(TruffleObject object, Object nameObj,
+                    @Cached("createClassProfile()") ValueProfile nameObjProfile) {
+
+        String name = getName(nameObjProfile.profile(nameObj));
+        assert Utils.isInterned(name);
+
+        // just return evaluated receiver object and name
+        return RCallNode.createDeferredMemberAccess(object, name);
+    }
+
+    @Specialization(guards = "!isLhsOfForeignCall(object)")
     protected Object getSlot(Object object, Object nameObj,
                     @Cached("createClassProfile()") ValueProfile nameObjProfile) {
         String name = getName(nameObjProfile.profile(nameObj));
@@ -66,4 +87,10 @@ public abstract class Slot extends RBuiltinNode.Arg2 {
         sharedAttrUpdate.execute(object, result);
         return result;
     }
+
+    @Override
+    public void setLhsOfCall(boolean value) {
+        CompilerDirectives.transferToInterpreterAndInvalidate();
+        this.isLhsOfCall = value;
+    }
 }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/CastBuilder.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/CastBuilder.java
index adef023a62..fd5623610a 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/CastBuilder.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/CastBuilder.java
@@ -26,6 +26,7 @@ import java.util.Arrays;
 import java.util.function.Function;
 
 import com.oracle.truffle.api.CompilerDirectives;
+import com.oracle.truffle.api.interop.TruffleObject;
 import com.oracle.truffle.r.nodes.builtin.casts.Filter;
 import com.oracle.truffle.r.nodes.builtin.casts.Filter.AndFilter;
 import com.oracle.truffle.r.nodes.builtin.casts.Filter.CompareFilter;
@@ -662,6 +663,10 @@ public final class CastBuilder {
             return new TypeFilter<>(RFunction.class, x -> x.isBuiltin());
         }
 
+        public static TypeFilter<Object, TruffleObject> foreign() {
+            return new TypeFilter<>(TruffleObject.class, x -> RRuntime.isForeignObject(x));
+        }
+
         public static <R extends RAbstractIntVector> Filter<Object, R> integerValue() {
             return new RTypeFilter<>(RType.Integer);
         }
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RBuiltinNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RBuiltinNode.java
index 11315ae997..26cb852738 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RBuiltinNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RBuiltinNode.java
@@ -148,6 +148,10 @@ public abstract class RBuiltinNode extends RBuiltinBaseNode implements NodeWithA
         }
     }
 
+    public void setLhsOfCall(@SuppressWarnings("unused") boolean value) {
+        // default: do nothing
+    }
+
     public abstract static class Arg0 extends RBuiltinNode {
 
         public abstract Object execute(VirtualFrame frame);
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallNode.java
index 98e02a92ca..72ad55339c 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallNode.java
@@ -692,7 +692,12 @@ public abstract class RCallNode extends RCallBaseNode implements RSyntaxNode, RS
      * {@code src == RSyntaxNode.EAGER_DEPARSE} we force a deparse.
      */
     public static RCallNode createCall(SourceSection src, RNode function, ArgumentsSignature signature, RSyntaxNode... arguments) {
-        return RCallNodeGen.create(src, arguments, signature, function);
+        return RCallNodeGen.create(src, arguments, signature, tagFunctionNode(function));
+    }
+
+    private static RNode tagFunctionNode(RNode function) {
+        // TODO Auto-generated method stub
+        return function;
     }
 
     /**
@@ -895,6 +900,9 @@ public abstract class RCallNode extends RCallBaseNode implements RSyntaxNode, RS
             varArgSeen = new boolean[formals.getLength()];
             nonWrapSeen = new boolean[formals.getLength()];
             wrapSeen = new boolean[formals.getLength()];
+
+            // Tell this builtin that it is LHS of a call which might imply different behavior.
+            builtin.setLhsOfCall(true);
         }
 
         @Override
@@ -1189,4 +1197,8 @@ public abstract class RCallNode extends RCallBaseNode implements RSyntaxNode, RS
         }
 
     }
+
+    public static Object createDeferredMemberAccess(TruffleObject object, String name) {
+        return new DeferredFunctionValue(object, name);
+    }
 }
-- 
GitLab