From 495ba3d286665b02221b3ae15dc1340e3f3136b1 Mon Sep 17 00:00:00 2001
From: Tomas Stupka <tomas.stupka@oracle.com>
Date: Tue, 23 Jan 2018 20:11:28 +0100
Subject: [PATCH] handle guest language exceptions in interop calls

---
 .../r/nodes/builtin/base/BasePackage.java     | 15 ++-
 .../r/nodes/builtin/fastr/FastRInterop.java   | 97 +++++++++++++++++--
 .../{FastRTry.java => FastRTestsTry.java}     | 18 ++--
 .../function/FunctionDefinitionNode.java      |  6 +-
 .../truffle/r/nodes/function/RCallNode.java   |  5 +-
 .../com/oracle/truffle/r/runtime/RError.java  | 24 +++++
 .../truffle/r/runtime/context/RContext.java   |  5 +-
 .../interop/FastRInteropTryException.java     | 39 ++++++++
 .../interop/FastrInteropTryContextState.java  | 57 +++++++++++
 9 files changed, 245 insertions(+), 21 deletions(-)
 rename com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/{FastRTry.java => FastRTestsTry.java} (86%)
 create mode 100644 com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/interop/FastRInteropTryException.java
 create mode 100644 com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/interop/FastrInteropTryContextState.java

diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java
index e08c08bf7d..0a3cf0343b 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java
@@ -108,7 +108,13 @@ import com.oracle.truffle.r.nodes.builtin.fastr.FastRIdentityNodeGen;
 import com.oracle.truffle.r.nodes.builtin.fastr.FastRInspect;
 import com.oracle.truffle.r.nodes.builtin.fastr.FastRInspectNodeGen;
 import com.oracle.truffle.r.nodes.builtin.fastr.FastRInterop;
+import com.oracle.truffle.r.nodes.builtin.fastr.FastRInterop.FastRInteropClearException;
+import com.oracle.truffle.r.nodes.builtin.fastr.FastRInterop.FastRInteropGetException;
+import com.oracle.truffle.r.nodes.builtin.fastr.FastRInterop.FastRInteropTry;
 import com.oracle.truffle.r.nodes.builtin.fastr.FastRInteropFactory;
+import com.oracle.truffle.r.nodes.builtin.fastr.FastRInteropFactory.FastRInteropClearExceptionNodeGen;
+import com.oracle.truffle.r.nodes.builtin.fastr.FastRInteropFactory.FastRInteropGetExceptionNodeGen;
+import com.oracle.truffle.r.nodes.builtin.fastr.FastRInteropFactory.FastRInteropTryNodeGen;
 import com.oracle.truffle.r.nodes.builtin.fastr.FastRLibPaths;
 import com.oracle.truffle.r.nodes.builtin.fastr.FastRLibPathsNodeGen;
 import com.oracle.truffle.r.nodes.builtin.fastr.FastROptionBuiltin;
@@ -132,8 +138,8 @@ import com.oracle.truffle.r.nodes.builtin.fastr.FastRTree;
 import com.oracle.truffle.r.nodes.builtin.fastr.FastRTreeNodeGen;
 import com.oracle.truffle.r.nodes.builtin.fastr.FastRTreeStats;
 import com.oracle.truffle.r.nodes.builtin.fastr.FastRTreeStatsNodeGen;
-import com.oracle.truffle.r.nodes.builtin.fastr.FastRTry;
-import com.oracle.truffle.r.nodes.builtin.fastr.FastRTryNodeGen;
+import com.oracle.truffle.r.nodes.builtin.fastr.FastRTestsTry;
+import com.oracle.truffle.r.nodes.builtin.fastr.FastRTestsTryNodeGen;
 import com.oracle.truffle.r.nodes.builtin.fastr.FastrDqrls;
 import com.oracle.truffle.r.nodes.builtin.fastr.FastrDqrlsNodeGen;
 import com.oracle.truffle.r.nodes.builtin.fastr.memprof.FastRprofmem;
@@ -437,7 +443,10 @@ public class BasePackage extends RBuiltinPackage {
         add(FastRHelpRd.class, FastRHelpRdNodeGen::create);
         add(FastRIdentity.class, FastRIdentityNodeGen::create);
         add(FastROptionBuiltin.class, FastROptionBuiltin::create);
-        add(FastRTry.class, FastRTryNodeGen::create);
+        add(FastRTestsTry.class, FastRTestsTryNodeGen::create);
+        add(FastRInteropTry.class, FastRInteropTryNodeGen::create);
+        add(FastRInteropGetException.class, FastRInteropGetExceptionNodeGen::create);
+        add(FastRInteropClearException.class, FastRInteropClearExceptionNodeGen::create);
         add(FastRInspect.class, FastRInspectNodeGen::create);
         add(FastRInterop.Eval.class, FastRInteropFactory.EvalNodeGen::create);
         add(FastRInterop.Export.class, FastRInteropFactory.ExportNodeGen::create);
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRInterop.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRInterop.java
index 8148f31c79..300ec8e7de 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRInterop.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRInterop.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -24,18 +24,14 @@ package com.oracle.truffle.r.nodes.builtin.fastr;
 
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.doubleValue;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.integerValue;
-import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.logicalValue;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.notLogicalNA;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.numericValue;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.rawValue;
-import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.singleElement;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.stringValue;
 import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.typeName;
 import static com.oracle.truffle.r.runtime.RVisibility.CUSTOM;
 import static com.oracle.truffle.r.runtime.RVisibility.OFF;
 import static com.oracle.truffle.r.runtime.RVisibility.ON;
-import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
-import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 
 import java.io.File;
 import java.io.IOException;
@@ -47,6 +43,7 @@ import com.oracle.truffle.api.CompilerAsserts;
 import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.Truffle;
+import com.oracle.truffle.api.TruffleException;
 import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Fallback;
 import com.oracle.truffle.api.dsl.ImportStatic;
@@ -66,16 +63,25 @@ import com.oracle.truffle.api.profiles.ConditionProfile;
 import com.oracle.truffle.api.source.Source;
 import com.oracle.truffle.api.source.Source.Builder;
 import com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.instanceOf;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.logicalValue;
+import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.singleElement;
+import com.oracle.truffle.r.nodes.builtin.NodeWithArgumentCasts;
 import com.oracle.truffle.r.nodes.builtin.NodeWithArgumentCasts.Casts;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
+import com.oracle.truffle.r.nodes.function.call.RExplicitCallNode;
 import com.oracle.truffle.r.nodes.function.visibility.SetVisibilityNode;
 import com.oracle.truffle.r.runtime.RError;
+import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RRuntime;
 import com.oracle.truffle.r.runtime.RSource;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.data.RDataFactory;
+import com.oracle.truffle.r.runtime.data.RFunction;
 import com.oracle.truffle.r.runtime.data.RInteropScalar;
 import com.oracle.truffle.r.runtime.data.RInteropScalar.RInteropByte;
 import com.oracle.truffle.r.runtime.data.RInteropScalar.RInteropChar;
@@ -94,6 +100,8 @@ import com.oracle.truffle.r.runtime.data.model.RAbstractRawVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector;
 import com.oracle.truffle.r.runtime.data.model.RAbstractVector;
 import com.oracle.truffle.r.runtime.data.nodes.GetReadonlyData;
+import com.oracle.truffle.r.runtime.interop.FastRInteropTryException;
+import com.oracle.truffle.r.runtime.interop.FastrInteropTryContextState;
 import com.oracle.truffle.r.runtime.interop.Foreign2R;
 import com.oracle.truffle.r.runtime.interop.ForeignArray2R;
 import com.oracle.truffle.r.runtime.interop.R2Foreign;
@@ -610,7 +618,7 @@ public class FastRInterop {
 
         static {
             Casts casts = new Casts(ToJavaArray.class);
-            casts.arg("x").mustNotBeMissing();
+            casts.arg("x").castForeignObjects(false).mustNotBeMissing();
             casts.arg("className").allowMissing().mustBe(stringValue()).asStringVector().mustBe(Predef.singleElement()).findFirst();
             casts.arg("flat").mapMissing(Predef.constant(RRuntime.LOGICAL_TRUE)).mustBe(logicalValue().or(Predef.nullValue())).asLogicalVector().mustBe(singleElement()).findFirst().mustBe(
                             notLogicalNA()).map(Predef.toBoolean());
@@ -846,6 +854,8 @@ public class FastRInterop {
             } catch (IllegalStateException | SecurityException | IllegalArgumentException | UnsupportedTypeException | ArityException | UnsupportedMessageException e) {
                 String msg = isTesting ? "error during Java object instantiation" : "error during Java object instantiation: " + e.getMessage();
                 throw error(RError.Message.GENERIC, msg);
+            } catch (RuntimeException e) {
+                throw RError.handleInteropException(this, e, clazz);
             }
         }
 
@@ -948,4 +958,79 @@ public class FastRInterop {
         }
     }
 
+    @RBuiltin(name = ".fastr.interop.try", kind = PRIMITIVE, parameterNames = {"function", "check"}, behavior = COMPLEX)
+    public abstract static class FastRInteropTry extends RBuiltinNode.Arg2 {
+        @Node.Child private RExplicitCallNode call = RExplicitCallNode.create();
+
+        static {
+            Casts casts = new Casts(FastRInteropTry.class);
+            casts.arg("function").mustBe(instanceOf(RFunction.class));
+            casts.arg("check").mustBe(logicalValue()).asLogicalVector().mustBe(singleElement()).findFirst();
+        }
+
+        @Specialization
+        public Object tryFunc(VirtualFrame frame, RFunction function, byte check) {
+            boolean isCheck = RRuntime.fromLogical(check);
+            getInteropTryState().stepIn();
+            try {
+                return call.execute(frame, function, RArgsValuesAndNames.EMPTY);
+            } catch (FastRInteropTryException e) {
+                Throwable cause = e.getCause();
+                CompilerDirectives.transferToInterpreter();
+                if (cause instanceof TruffleException) {
+                    cause = cause.getCause();
+                    if (isCheck) {
+                        String causeName = cause.getClass().getName();
+                        String msg = cause.getMessage();
+                        msg = msg != null ? String.format("%s: %s", causeName, msg) : causeName;
+                        throw RError.error(RError.SHOW_CALLER, RError.Message.GENERIC, msg);
+                    } else {
+                        getInteropTryState().lastException = cause;
+                    }
+                } else {
+                    RInternalError.reportError(e);
+                }
+            } finally {
+                getInteropTryState().stepOut();
+            }
+            return RNull.instance;
+        }
+
+    }
+
+    @RBuiltin(name = ".fastr.interop.getTryException", kind = PRIMITIVE, parameterNames = {"clear"}, behavior = COMPLEX)
+    public abstract static class FastRInteropGetException extends RBuiltinNode.Arg1 {
+        static {
+            Casts casts = new Casts(FastRInteropGetException.class);
+            casts.arg("clear").mustBe(logicalValue()).asLogicalVector().mustBe(singleElement()).findFirst();
+        }
+
+        @Specialization
+        public Object getException(byte clear) {
+            Throwable ret = getInteropTryState().lastException;
+            if (RRuntime.fromLogical(clear)) {
+                getInteropTryState().lastException = null;
+            }
+            return ret != null ? JavaInterop.asTruffleObject(ret) : RNull.instance;
+        }
+    }
+
+    @RBuiltin(name = ".fastr.interop.clearTryException", kind = PRIMITIVE, parameterNames = {}, behavior = COMPLEX)
+    public abstract static class FastRInteropClearException extends RBuiltinNode.Arg0 {
+
+        static {
+            NodeWithArgumentCasts.Casts.noCasts(FastRInteropClearException.class);
+        }
+
+        @Specialization
+        public Object clearException() {
+            getInteropTryState().lastException = null;
+            return RNull.instance;
+        }
+    }
+
+    private static FastrInteropTryContextState getInteropTryState() {
+        return RContext.getInstance().stateInteropTry;
+    }
+
 }
diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRTry.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRTestsTry.java
similarity index 86%
rename from com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRTry.java
rename to com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRTestsTry.java
index 1fb8fe48ae..7b7a63ecee 100644
--- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRTry.java
+++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRTestsTry.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -22,18 +22,16 @@
  */
 package com.oracle.truffle.r.nodes.builtin.fastr;
 
-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.TruffleBoundary;
 import com.oracle.truffle.api.dsl.Specialization;
 import com.oracle.truffle.api.frame.VirtualFrame;
 import com.oracle.truffle.r.nodes.builtin.RBuiltinNode;
 import com.oracle.truffle.r.nodes.function.call.RExplicitCallNode;
 import com.oracle.truffle.r.runtime.RErrorHandling;
-import com.oracle.truffle.r.runtime.RInternalError;
 import com.oracle.truffle.r.runtime.RRuntime;
+import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
+import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE;
 import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames;
 import com.oracle.truffle.r.runtime.data.RFunction;
 
@@ -41,13 +39,17 @@ import com.oracle.truffle.r.runtime.data.RFunction;
  * Allows to be 100% robust even in the case of FastR errors like runtime exceptions. The argument
  * must be a single parameter-less function. The return value is true on success, otherwise error
  * message.
+ * <p>
+ * <b>WARNING: For use in tests only! </b><br>
+ * There is no guarantee that after an error the internal FastR state will stay consistent.
+ * </p>
  */
-@RBuiltin(name = ".fastr.try", kind = PRIMITIVE, parameterNames = {""}, behavior = COMPLEX)
-public abstract class FastRTry extends RBuiltinNode.Arg1 {
+@RBuiltin(name = ".fastr.tests.try", kind = PRIMITIVE, parameterNames = {""}, behavior = COMPLEX)
+public abstract class FastRTestsTry extends RBuiltinNode.Arg1 {
     @Child private RExplicitCallNode call = RExplicitCallNode.create();
 
     static {
-        Casts.noCasts(FastRTry.class);
+        Casts.noCasts(FastRTestsTry.class);
     }
 
     @Specialization
diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionDefinitionNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionDefinitionNode.java
index 39fa33fc40..8001fc0100 100644
--- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionDefinitionNode.java
+++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/FunctionDefinitionNode.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -76,6 +76,7 @@ import com.oracle.truffle.r.runtime.data.RNull;
 import com.oracle.truffle.r.runtime.data.RPairList;
 import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor;
 import com.oracle.truffle.r.runtime.env.frame.RFrameSlot;
+import com.oracle.truffle.r.runtime.interop.FastRInteropTryException;
 import com.oracle.truffle.r.runtime.nodes.RCodeBuilder;
 import com.oracle.truffle.r.runtime.nodes.RNode;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxCall;
@@ -319,6 +320,9 @@ public final class FunctionDefinitionNode extends RRootNode implements RSyntaxNo
             CompilerDirectives.transferToInterpreter();
             runOnExitHandlers = false;
             throw e;
+        } catch (FastRInteropTryException e) {
+            CompilerDirectives.transferToInterpreter();
+            throw e;
         } catch (Throwable e) {
             CompilerDirectives.transferToInterpreter();
             runOnExitHandlers = false;
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 523058ffd6..8fd61c7ef5 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
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -31,7 +31,6 @@ import com.oracle.truffle.api.CompilerDirectives;
 import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
 import com.oracle.truffle.api.RootCallTarget;
-import com.oracle.truffle.api.Truffle;
 import com.oracle.truffle.api.dsl.Cached;
 import com.oracle.truffle.api.dsl.Fallback;
 import com.oracle.truffle.api.dsl.NodeChild;
@@ -641,6 +640,8 @@ public abstract class RCallNode extends RCallBaseNode implements RSyntaxNode, RS
                 CompilerDirectives.transferToInterpreter();
                 RInternalError.reportError(e);
                 throw RError.interopError(RError.findParentRBase(this), e, function);
+            } catch (RuntimeException e) {
+                throw RError.handleInteropException(this, e, function);
             }
         }
 
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java
index 3e62bfa253..26cb1c5f0f 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java
@@ -23,7 +23,9 @@ import com.oracle.truffle.api.nodes.Node;
 import com.oracle.truffle.api.source.SourceSection;
 import com.oracle.truffle.r.runtime.builtins.RBuiltin;
 import com.oracle.truffle.r.runtime.builtins.RBuiltinKind;
+import com.oracle.truffle.r.runtime.context.RContext;
 import com.oracle.truffle.r.runtime.env.REnvironment.PutException;
+import com.oracle.truffle.r.runtime.interop.FastRInteropTryException;
 import com.oracle.truffle.r.runtime.nodes.RBaseNode;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
 
@@ -189,6 +191,28 @@ public final class RError extends RuntimeException implements TruffleException {
         throw error0(node, RError.Message.GENERIC, "Foreign function failed: " + (e.getMessage() != null ? e.getMessage() : e.toString()) + " on object " + o);
     }
 
+    @TruffleBoundary
+    public static RError handleInteropException(Node node, RuntimeException e, TruffleObject o) {
+        if (e instanceof TruffleException) {
+            if (RContext.getInstance().stateInteropTry.isInTry()) {
+                // will be catched and handled in .fastr.interop.try builtin
+                throw new FastRInteropTryException(e);
+            } else {
+                Throwable cause = e.getCause();
+                // TODO
+                // - e.getCause() seems to work for java, but how to inspect guest lang exceptions
+                // - stacktrace in log or console?
+                // Object eo = ((TruffleException) e).getExceptionObject() ?
+
+                String clsName = cause.getClass().getName();
+                String msg = cause.getMessage();
+                msg = msg != null ? String.format("Foreign function failed: %s: %s", clsName, msg) : String.format("Foreign function failed: %s", clsName);
+                throw RError.error(findParentRBase(node), RError.Message.GENERIC, msg);
+            }
+        }
+        throw e;
+    }
+
     @TruffleBoundary
     public static RError ioError(RBaseNode node, IOException ex) {
         throw error0(node, Message.GENERIC, ex.getMessage());
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/RContext.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/RContext.java
index 28c057b810..add7d0383c 100644
--- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/RContext.java
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/context/RContext.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -102,6 +102,7 @@ import com.oracle.truffle.r.runtime.ffi.DLL;
 import com.oracle.truffle.r.runtime.ffi.RFFIContext;
 import com.oracle.truffle.r.runtime.ffi.RFFIFactory;
 import com.oracle.truffle.r.runtime.instrument.InstrumentationState;
+import com.oracle.truffle.r.runtime.interop.FastrInteropTryContextState;
 import com.oracle.truffle.r.runtime.nodes.RCodeBuilder;
 import com.oracle.truffle.r.runtime.nodes.RSyntaxNode;
 import com.oracle.truffle.r.runtime.rng.RRNG;
@@ -341,6 +342,7 @@ public final class RContext {
     public final ROptions.ContextStateImpl stateROptions;
     public final REnvironment.ContextStateImpl stateREnvironment;
     public final RErrorHandling.ContextStateImpl stateRErrorHandling;
+    public final FastrInteropTryContextState stateInteropTry;
     public final ConnectionSupport.ContextStateImpl stateRConnection;
     public final RRNG.ContextStateImpl stateRNG;
     public final RSerialize.ContextStateImpl stateRSerialize;
@@ -444,6 +446,7 @@ public final class RContext {
         this.stateStdConnections = StdConnections.ContextStateImpl.newContextState();
         this.stateREnvironment = REnvironment.ContextStateImpl.newContextState(this);
         this.stateRErrorHandling = RErrorHandling.ContextStateImpl.newContextState();
+        this.stateInteropTry = FastrInteropTryContextState.newContextState();
         this.stateRConnection = ConnectionSupport.ContextStateImpl.newContextState();
         this.stateRNG = RRNG.ContextStateImpl.newContextState();
         this.stateRSerialize = RSerialize.ContextStateImpl.newContextState();
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/interop/FastRInteropTryException.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/interop/FastRInteropTryException.java
new file mode 100644
index 0000000000..ac3429cb56
--- /dev/null
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/interop/FastRInteropTryException.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.r.runtime.interop;
+
+/**
+ * Denotes a TruffleException coming from a foreign call. Supposed to be handled in the
+ * .fastr.interop.try builtin-s.
+ */
+@SuppressWarnings("serial")
+public class FastRInteropTryException extends RuntimeException {
+    public FastRInteropTryException(RuntimeException cause) {
+        super(cause);
+    }
+
+    @Override
+    public synchronized Throwable fillInStackTrace() {
+        return null;
+    }
+}
diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/interop/FastrInteropTryContextState.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/interop/FastrInteropTryContextState.java
new file mode 100644
index 0000000000..4505048c89
--- /dev/null
+++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/interop/FastrInteropTryContextState.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.truffle.r.runtime.interop;
+
+import com.oracle.truffle.r.runtime.context.RContext;
+
+/**
+ * Context-specific state relevant to the .fastr.interop.try builtins.
+ */
+@SuppressWarnings("serial")
+public class FastrInteropTryContextState implements RContext.ContextState {
+    /**
+     * Values is either NULL or an RPairList, for {@code restarts}.
+     */
+    public Throwable lastException = null;
+    /**
+     * Determines if in scope of a .fastr.interop.try builtin call.
+     */
+    private int tryCounter = 0;
+
+    public static FastrInteropTryContextState newContextState() {
+        return new FastrInteropTryContextState();
+    }
+
+    public void stepIn() {
+        tryCounter++;
+    }
+
+    public void stepOut() {
+        tryCounter--;
+        assert tryCounter >= 0;
+    }
+
+    public boolean isInTry() {
+        return tryCounter > 0;
+    }
+}
-- 
GitLab