diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/JavaUpCallsRFFIImpl.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/JavaUpCallsRFFIImpl.java index afe1fbe86e60e3a01bf8ecf1449578dbb6bce7e1..f7852a696915fb0e128fb6a90969735fe7a12ddc 100644 --- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/JavaUpCallsRFFIImpl.java +++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/common/JavaUpCallsRFFIImpl.java @@ -32,9 +32,10 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; -import java.util.HashSet; +import java.util.IdentityHashMap; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Function; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; @@ -309,7 +310,7 @@ public abstract class JavaUpCallsRFFIImpl implements UpCallsRFFI { @Override @TruffleBoundary - public int Rf_setAttrib(Object obj, Object name, Object val) { + public Object Rf_setAttrib(Object obj, Object name, Object val) { if (obj instanceof RAttributable) { RAttributable attrObj = (RAttributable) obj; String nameAsString; @@ -334,7 +335,7 @@ public abstract class JavaUpCallsRFFIImpl implements UpCallsRFFI { } else { throw RInternalError.shouldNotReachHere(); } - return 0; + return val; } @TruffleBoundary @@ -1598,8 +1599,11 @@ public abstract class JavaUpCallsRFFIImpl implements UpCallsRFFI { @TruffleBoundary public void R_PreserveObject(Object obj) { guaranteeInstanceOf(obj, RObject.class); - HashSet<RObject> list = getContext().preserveList; - list.add((RObject) obj); + IdentityHashMap<RObject, AtomicInteger> preserveList = getContext().preserveList; + AtomicInteger prevCnt = preserveList.putIfAbsent((RObject) obj, new AtomicInteger(1)); + if (prevCnt != null) { + prevCnt.incrementAndGet(); + } } @Override @@ -1607,9 +1611,17 @@ public abstract class JavaUpCallsRFFIImpl implements UpCallsRFFI { public void R_ReleaseObject(Object obj) { guaranteeInstanceOf(obj, RObject.class); RFFIContext context = getContext(); - HashSet<RObject> list = context.preserveList; - if (list.remove(obj)) { - context.registerReferenceUsedInNative(obj); + IdentityHashMap<RObject, AtomicInteger> preserveList = context.preserveList; + AtomicInteger atomicInteger = preserveList.get(obj); + if (atomicInteger != null) { + int decrementAndGet = atomicInteger.decrementAndGet(); + if (decrementAndGet == 0) { + // remove from "list" + preserveList.remove(obj); + context.registerReferenceUsedInNative(obj); + } + } else { + // TODO report ? } } diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Context.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Context.java index 333ad6593297054a1d7dba4703ad877df342c309..e5bb3244f4c3098f3746147634d6c4c26528fea1 100644 --- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Context.java +++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_Context.java @@ -31,6 +31,8 @@ import java.util.ArrayList; import java.util.EnumMap; import com.oracle.truffle.api.CompilerAsserts; +import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.interop.ForeignAccess; import com.oracle.truffle.api.interop.InteropException; import com.oracle.truffle.api.interop.Message; @@ -169,7 +171,7 @@ final class TruffleNFI_Context extends RFFIContext { } } - private void initCallbacks(RContext context) { + private void initCallbacksAddress() { // get the address of the native thread local try { Node bind = Message.createInvoke(1).createNode(); @@ -179,7 +181,9 @@ final class TruffleNFI_Context extends RFFIContext { } catch (InteropException ex) { throw RInternalError.shouldNotReachHere(ex); } + } + private void initCallbacks(RContext context) { if (context.getKind() == ContextKind.SHARE_NOTHING) { // create and fill a new callbacks table callbacks = UnsafeAdapter.UNSAFE.allocateMemory(Callbacks.values().length * Unsafe.ARRAY_LONG_INDEX_SCALE); @@ -203,10 +207,16 @@ final class TruffleNFI_Context extends RFFIContext { } private long callbacks; - private long callbacksAddress; + @CompilationFinal private long callbacksAddress; private long pushCallbacks() { + if (callbacksAddress == 0) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + initCallbacksAddress(); + } long oldCallbacks = UnsafeAdapter.UNSAFE.getLong(callbacksAddress); + assert callbacks != 0L; + assert callbacksAddress != 0L; UnsafeAdapter.UNSAFE.putLong(callbacksAddress, callbacks); return oldCallbacks; } diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_DLL.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_DLL.java index 3279573549545bf005d13825528ed7558f12d6f2..1a9fd3ffe3b1c9c561aa4801a36a18799c32e9cd 100644 --- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_DLL.java +++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/nfi/TruffleNFI_DLL.java @@ -22,8 +22,10 @@ */ package com.oracle.truffle.r.ffi.impl.nfi; +import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.TruffleLanguage.Env; +import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.interop.ForeignAccess; import com.oracle.truffle.api.interop.InteropException; import com.oracle.truffle.api.interop.Message; @@ -76,12 +78,17 @@ public class TruffleNFI_DLL implements DLLRFFI { private static class TruffleNFI_DLSymNode extends Node implements DLLRFFI.DLSymNode { + @Child private Node lookupSymbol; + @Override @TruffleBoundary public SymbolHandle execute(Object handle, String symbol) { assert handle instanceof NFIHandle; NFIHandle nfiHandle = (NFIHandle) handle; - Node lookupSymbol = Message.READ.createNode(); + if (lookupSymbol == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + lookupSymbol = insert(Message.READ.createNode()); + } try { TruffleObject result = (TruffleObject) ForeignAccess.sendRead(lookupSymbol, nfiHandle.libHandle, symbol); return new SymbolHandle(result); diff --git a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/upcalls/StdUpCallsRFFI.java b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/upcalls/StdUpCallsRFFI.java index 9138e92393ae206aee397b439c9c9932c1b45a01..50237121f198930541ce26863c986c1336c2ec47 100644 --- a/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/upcalls/StdUpCallsRFFI.java +++ b/com.oracle.truffle.r.ffi.impl/src/com/oracle/truffle/r/ffi/impl/upcalls/StdUpCallsRFFI.java @@ -127,7 +127,7 @@ public interface StdUpCallsRFFI { Object Rf_getAttrib(Object obj, Object name); - int /* void */ Rf_setAttrib(Object obj, Object name, Object val); + Object Rf_setAttrib(Object obj, Object name, Object val); int Rf_inherits(Object x, @RFFICstring String clazz); diff --git a/com.oracle.truffle.r.ffi.processor/src/com/oracle/truffle/r/ffi/processor/FFIProcessor.java b/com.oracle.truffle.r.ffi.processor/src/com/oracle/truffle/r/ffi/processor/FFIProcessor.java index e2086b05f7e568503714014e2aa1fb3637c249fc..19e6f1d16a4c89b89f4fd647d639159d309f5a7d 100644 --- a/com.oracle.truffle.r.ffi.processor/src/com/oracle/truffle/r/ffi/processor/FFIProcessor.java +++ b/com.oracle.truffle.r.ffi.processor/src/com/oracle/truffle/r/ffi/processor/FFIProcessor.java @@ -237,7 +237,7 @@ public final class FFIProcessor extends AbstractProcessor { w.append("import com.oracle.truffle.api.interop.TruffleObject;\n"); w.append("import com.oracle.truffle.api.nodes.RootNode;\n"); w.append("import com.oracle.truffle.r.runtime.context.RContext;\n"); - if (returnKind != TypeKind.VOID) { + if (!returnKind.isPrimitive() && returnKind != TypeKind.VOID) { w.append("import com.oracle.truffle.r.runtime.data.RDataFactory;\n"); } w.append("import com.oracle.truffle.r.runtime.ffi.CallRFFI.HandleUpCallExceptionNode;\n"); @@ -330,7 +330,9 @@ public final class FFIProcessor extends AbstractProcessor { w.append(" } catch (Exception ex) {\n"); w.append(" CompilerDirectives.transferToInterpreter();\n"); w.append(" handleExceptionNode.execute(ex);\n"); - if (returnKind != TypeKind.VOID) { + if (returnKind.isPrimitive()) { + w.append(" resultRObj = Integer.valueOf(-1);\n"); + } else if (returnKind != TypeKind.VOID) { w.append(" resultRObj = RDataFactory.createIntVectorFromScalar(-1);\n"); } w.append(" }\n"); diff --git a/com.oracle.truffle.r.native/fficall/src/truffle_common/Rinternals_truffle_common.h b/com.oracle.truffle.r.native/fficall/src/truffle_common/Rinternals_truffle_common.h index cc43c6ff9c3a6be99e9ca259d73771dfd4ee38fc..9d45df5e3de0f6ef336daf3ac068a5b5c8e2ec1a 100644 --- a/com.oracle.truffle.r.native/fficall/src/truffle_common/Rinternals_truffle_common.h +++ b/com.oracle.truffle.r.native/fficall/src/truffle_common/Rinternals_truffle_common.h @@ -158,7 +158,7 @@ void Rf_gsetVar(SEXP symbol, SEXP value, SEXP rho) { } SEXP Rf_coerceVector(SEXP x, SEXPTYPE mode) { - TRACE0(); + TRACE(TARGpp, x, mode); SEXP result = ((call_Rf_coerceVector) callbacks[Rf_coerceVector_x])(x, mode); checkExitCall(); return result; diff --git a/com.oracle.truffle.r.native/version.source b/com.oracle.truffle.r.native/version.source index 87523dd7a0632907d61799465827c3f08825fa47..d81cc0710eb6cf9efd5b920a8453e1e07157b6cd 100644 --- a/com.oracle.truffle.r.native/version.source +++ b/com.oracle.truffle.r.native/version.source @@ -1 +1 @@ -41 +42 diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BrowserFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BrowserFunctions.java index 2b58ec4749cd083200e13849baf26ea8b2a1bb9b..e7ed553b70f60e6c3e5230daf1fe48174e6d61b6 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BrowserFunctions.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BrowserFunctions.java @@ -48,6 +48,7 @@ import com.oracle.truffle.r.runtime.RRuntime; import com.oracle.truffle.r.runtime.SubstituteVirtualFrame; import com.oracle.truffle.r.runtime.builtins.RBuiltin; import com.oracle.truffle.r.runtime.context.RContext; +import com.oracle.truffle.r.runtime.context.RContext.ContextKind; import com.oracle.truffle.r.runtime.data.RFunction; import com.oracle.truffle.r.runtime.data.RNull; import com.oracle.truffle.r.runtime.instrument.InstrumentationState.BrowserState; @@ -78,7 +79,12 @@ public class BrowserFunctions { @Specialization protected RNull browser(VirtualFrame frame, Object text, RNull condition, boolean expr, int skipCalls) { if (expr) { - BrowserState browserState = RContext.getInstance().stateInstrumentation.getBrowserState(); + RContext instance = RContext.getInstance(); + if (instance.getKind() == ContextKind.SHARE_NOTHING) { + return RNull.instance; + } + + BrowserState browserState = instance.stateInstrumentation.getBrowserState(); try { browserState.push(new HelperState(text, condition)); MaterializedFrame mFrame = frame.materialize(); diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IsTypeFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IsTypeFunctions.java index ee53ad4ccb06eae03887b1328a78b5b9dcc0102b..1c8e9a467c3778ce3db3195961695c17e40ca341 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IsTypeFunctions.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/IsTypeFunctions.java @@ -181,7 +181,7 @@ public class IsTypeFunctions { public abstract static class IsCall extends MissingAdapter { static { - createCasts(IsCall.class); + Casts.noCasts(IsCall.class); } @Specialization @@ -189,7 +189,7 @@ public class IsTypeFunctions { return RRuntime.LOGICAL_TRUE; } - @Specialization(guards = {"!isRMissing(value)", "!isRLanguage(value)"}) + @Specialization(guards = {"!isRLanguage(value)"}) protected byte isType(@SuppressWarnings("unused") Object value) { return RRuntime.LOGICAL_FALSE; } diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/CallAndExternalFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/CallAndExternalFunctions.java index d1d2b58ef79d253bb9b8b16e53db020ce812e26a..64b4b0f6136b8399ccf3ef6f7075b5f849e41e77 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/CallAndExternalFunctions.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/CallAndExternalFunctions.java @@ -236,10 +236,10 @@ public class CallAndExternalFunctions { return new Object[]{RMissing.instance, RArgsValuesAndNames.EMPTY, RMissing.instance}; } - private Object[] materializeArgs(VirtualFrame frame, Object[] args) { + private Object[] materializeArgs(Object[] args) { Object[] materializedArgs = new Object[args.length]; for (int i = 0; i < args.length; i++) { - materializedArgs[i] = materializeNode.execute(frame, args[i]); + materializedArgs[i] = materializeNode.execute(args[i]); } return materializedArgs; } @@ -666,38 +666,37 @@ public class CallAndExternalFunctions { */ @SuppressWarnings("unused") @Specialization(limit = "2", guards = {"cached == symbol", "builtin == null"}) - protected Object callNamedFunction(VirtualFrame frame, RList symbol, RArgsValuesAndNames args, Object packageName, + protected Object callNamedFunction(RList symbol, RArgsValuesAndNames args, Object packageName, @Cached("symbol") RList cached, @Cached("lookupBuiltin(symbol)") RExternalBuiltinNode builtin, @Cached("new()") ExtractNativeCallInfoNode extractSymbolInfo, @Cached("extractSymbolInfo.execute(symbol)") NativeCallInfo nativeCallInfo) { - return callRFFINode.dispatch(nativeCallInfo, materializeArgs(frame, args.getArguments())); + return callRFFINode.dispatch(nativeCallInfo, materializeArgs(args.getArguments())); } /** * For some reason, the list instance may change, although it carries the same info. For * such cases there is this generic version. */ - @SuppressWarnings("unused") @Specialization(replaces = {"callNamedFunction", "doExternal"}) - protected Object callNamedFunctionGeneric(VirtualFrame frame, RList symbol, RArgsValuesAndNames args, Object packageName, + protected Object callNamedFunctionGeneric(RList symbol, RArgsValuesAndNames args, @SuppressWarnings("unused") Object packageName, @Cached("new()") ExtractNativeCallInfoNode extractSymbolInfo) { RExternalBuiltinNode builtin = lookupBuiltin(symbol); if (builtin != null) { throw RInternalError.shouldNotReachHere("Cache for .Calls with FastR reimplementation (lookupBuiltin(...) != null) exceeded the limit"); } NativeCallInfo nativeCallInfo = extractSymbolInfo.execute(symbol); - return callRFFINode.dispatch(nativeCallInfo, materializeArgs(frame, args.getArguments())); + return callRFFINode.dispatch(nativeCallInfo, materializeArgs(args.getArguments())); } /** * {@code .NAME = string}, no package specified. */ @Specialization - protected Object callNamedFunction(VirtualFrame frame, String symbol, RArgsValuesAndNames args, @SuppressWarnings("unused") RMissing packageName, + protected Object callNamedFunction(String symbol, RArgsValuesAndNames args, @SuppressWarnings("unused") RMissing packageName, @Cached("createRegisteredNativeSymbol(CallNST)") DLL.RegisteredNativeSymbol rns, @Cached("create()") DLL.RFindSymbolNode findSymbolNode) { - return callNamedFunctionWithPackage(frame, symbol, args, null, rns, findSymbolNode); + return callNamedFunctionWithPackage(symbol, args, null, rns, findSymbolNode); } /** @@ -705,19 +704,19 @@ public class CallAndExternalFunctions { * define that symbol. */ @Specialization - protected Object callNamedFunctionWithPackage(VirtualFrame frame, String symbol, RArgsValuesAndNames args, String packageName, + protected Object callNamedFunctionWithPackage(String symbol, RArgsValuesAndNames args, String packageName, @Cached("createRegisteredNativeSymbol(CallNST)") DLL.RegisteredNativeSymbol rns, @Cached("create()") DLL.RFindSymbolNode findSymbolNode) { DLL.SymbolHandle func = findSymbolNode.execute(symbol, packageName, rns); if (func == DLL.SYMBOL_NOT_FOUND) { throw error(RError.Message.SYMBOL_NOT_IN_TABLE, symbol, "Call", packageName); } - return callRFFINode.dispatch(new NativeCallInfo(symbol, func, rns.getDllInfo()), materializeArgs(frame, args.getArguments())); + return callRFFINode.dispatch(new NativeCallInfo(symbol, func, rns.getDllInfo()), materializeArgs(args.getArguments())); } @Specialization - protected Object callNamedFunctionWithPackage(VirtualFrame frame, RExternalPtr symbol, RArgsValuesAndNames args, @SuppressWarnings("unused") RMissing packageName) { - return callRFFINode.dispatch(new NativeCallInfo("", symbol.getAddr(), null), materializeArgs(frame, args.getArguments())); + protected Object callNamedFunctionWithPackage(RExternalPtr symbol, RArgsValuesAndNames args, @SuppressWarnings("unused") RMissing packageName) { + return callRFFINode.dispatch(new NativeCallInfo("", symbol.getAddr(), null), materializeArgs(args.getArguments())); } @SuppressWarnings("unused") diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RBuiltinRootNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RBuiltinRootNode.java index 59f8e137407371489ff75d290648b83bf1eeb627..379d3db97ade1ad8effe85a433bdbd2a45d7e2bd 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RBuiltinRootNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RBuiltinRootNode.java @@ -95,6 +95,11 @@ public final class RBuiltinRootNode extends RRootNode { initialize(); Object[] arguments = new Object[args.length]; for (int i = 0; i < args.length; i++) { + if (args[i] == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + args[i] = insert(AccessArgumentNode.create(i)); + args[i].setFormals(call.getFormals()); + } arguments[i] = args[i].execute(frame); } return call.execute(frame, null, new RArgsValuesAndNames(arguments, factory.getSignature()), null); @@ -117,10 +122,6 @@ public final class RBuiltinRootNode extends RRootNode { assert factory.getSignature().getVarArgCount() == 0 || factory.getSignature().getVarArgIndex() == factory.getSignature().getLength() - 1 : "only last argument can be vararg"; } call = insert(new BuiltinCallNode(builtin, factory, formalArguments, null, true)); - for (int i = 0; i < args.length; i++) { - args[i] = insert(AccessArgumentNode.create(i)); - args[i].setFormals(formalArguments); - } } } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/helpers/MaterializeNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/helpers/MaterializeNode.java index 26f99e343d3a5f7f48e3445aa881b2fcd10b772d..c762b1d26b3acd70fb21ccfb88f908a327c34ebb 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/helpers/MaterializeNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/helpers/MaterializeNode.java @@ -24,10 +24,10 @@ package com.oracle.truffle.r.nodes.helpers; import com.oracle.truffle.api.CompilerAsserts; import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Fallback; 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.r.nodes.attributes.HasAttributesNode; import com.oracle.truffle.r.nodes.attributes.IterableAttributeNode; @@ -45,9 +45,6 @@ public abstract class MaterializeNode extends Node { @Child private IterableAttributeNode attributesIt; @Child private SetAttributeNode setAttributeNode; - @Child private MaterializeNode recursive; - @Child private MaterializeNode recursiveAttr; - private final boolean deep; protected MaterializeNode(boolean deep) { @@ -58,63 +55,39 @@ public abstract class MaterializeNode extends Node { } } - public abstract Object execute(VirtualFrame frame, Object arg); + public abstract Object execute(Object arg); @Specialization - protected RList doList(VirtualFrame frame, RList vec) { - RList materialized = materializeContents(frame, vec); - materializeAttributes(frame, materialized); + protected RList doList(RList vec) { + RList materialized = materializeContents(vec); + materializeAttributes(materialized); return materialized; } - private RList materializeContents(VirtualFrame frame, RList list) { - boolean changed = false; - RList materializedContents = null; - for (int i = 0; i < list.getLength(); i++) { - if (recursive == null) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - recursive = insert(MaterializeNode.create(deep)); - } - Object element = list.getDataAt(i); - Object materializedElem = recursive.execute(frame, element); - if (materializedElem != element) { - materializedContents = (RList) list.copy(); - changed = true; - } - if (changed && materializedElem != element) { - materializedContents.setDataAt(i, materializedElem); - } - } - if (changed) { - return materializedContents; - } - return list; - } - @Specialization(limit = "LIMIT", guards = {"vec.getClass() == cachedClass"}) - protected RAttributable doAbstractContainerCached(VirtualFrame frame, RAttributable vec, + protected RAttributable doAbstractContainerCached(RAttributable vec, @SuppressWarnings("unused") @Cached("vec.getClass()") Class<?> cachedClass) { if (vec instanceof RList) { - return doList(frame, (RList) vec); + return doList((RList) vec); } else if (vec instanceof RAbstractContainer) { RAbstractContainer materialized = ((RAbstractContainer) vec).materialize(); - materializeAttributes(frame, materialized); + materializeAttributes(materialized); return materialized; } - materializeAttributes(frame, vec); + materializeAttributes(vec); return vec; } @Specialization(replaces = "doAbstractContainerCached") - protected RAttributable doAbstractContainer(VirtualFrame frame, RAttributable vec) { + protected RAttributable doAbstractContainer(RAttributable vec) { if (vec instanceof RList) { - return doList(frame, (RList) vec); + return doList((RList) vec); } else if (vec instanceof RAbstractContainer) { RAbstractContainer materialized = ((RAbstractContainer) vec).materialize(); - materializeAttributes(frame, materialized); + materializeAttributes(materialized); return materialized; } - materializeAttributes(frame, vec); + materializeAttributes(vec); return vec; } @@ -123,17 +96,35 @@ public abstract class MaterializeNode extends Node { return o; } - private void materializeAttributes(VirtualFrame frame, RAttributable materialized) { + private RList materializeContents(RList list) { + boolean changed = false; + RList materializedContents = null; + for (int i = 0; i < list.getLength(); i++) { + Object element = list.getDataAt(i); + Object materializedElem = doGenericSlowPath(element); + if (materializedElem != element) { + materializedContents = (RList) list.copy(); + changed = true; + } + if (changed && materializedElem != element) { + materializedContents.setDataAt(i, materializedElem); + } + } + if (changed) { + return materializedContents; + } + return list; + } + + private void materializeAttributes(RAttributable materialized) { // TODO we could further optimize by first checking for fixed/special attributes if (deep && hasAttributes.execute(materialized)) { if (attributesIt == null) { - assert recursiveAttr == null; CompilerDirectives.transferToInterpreterAndInvalidate(); attributesIt = insert(IterableAttributeNode.create()); - recursiveAttr = insert(MaterializeNode.create(deep)); } for (RAttribute attr : attributesIt.execute(materialized)) { - Object materializedAttr = recursiveAttr.execute(frame, attr.getValue()); + Object materializedAttr = doGenericSlowPath(attr.getValue()); if (materializedAttr != attr.getValue()) { if (setAttributeNode == null) { CompilerDirectives.transferToInterpreterAndInvalidate(); @@ -145,6 +136,14 @@ public abstract class MaterializeNode extends Node { } } + @TruffleBoundary + private Object doGenericSlowPath(Object element) { + if (element instanceof RAttributable) { + return doAbstractContainer((RAttributable) element); + } + return element; + } + public static MaterializeNode create(boolean deep) { return MaterializeNodeGen.create(deep); } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RDeparse.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RDeparse.java index c718f5a9f116393422cc23251773e2daff5d8996..56e8a2ff6f40e00eaca833f92d2b4385919d4dbb 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RDeparse.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RDeparse.java @@ -349,11 +349,9 @@ public class RDeparse { private static MessageDigest digest = null; - private Path emitToFile(String qualifiedFunctionName) throws IOException, NoSuchAlgorithmException { - Path tmpDir = Paths.get(Utils.getUserTempDir()).resolve("deparse"); - if (!Files.exists(tmpDir)) { - Files.createDirectory(tmpDir); - } + private Path emitToFile(String qualifiedFunctionName, String deparsePath) throws IOException, NoSuchAlgorithmException { + Path tmpDir = Paths.get(deparsePath); + assert Files.exists(tmpDir); Path path; if (FastROptions.EmitTmpHashed.getBooleanValue()) { @@ -382,8 +380,9 @@ public class RDeparse { } public void fixupSources() { - if (FastROptions.EmitTmpSource.getBooleanValue()) { - fixupSourcesTempFile(); + String deparsePath = TempPathName.deparsePath(); + if (FastROptions.EmitTmpSource.getBooleanValue() && deparsePath != null) { + fixupSourcesTempFile(deparsePath); } else { fixupSourcesTextInternal(); } @@ -396,11 +395,11 @@ public class RDeparse { } } - private void fixupSourcesTempFile() { + private void fixupSourcesTempFile(String deparsePath) { try { RootNode rootNode = getRootNode(); String name = rootNode != null ? rootNode.getName() : null; - Path path = emitToFile(name); + Path path = emitToFile(name, deparsePath); Source source = RSource.fromFile(path.toFile()); for (SourceSectionElement s : sources) { if (s.element.getLazySourceSection() == null || s.element.getLazySourceSection() == RSyntaxNode.LAZY_DEPARSE) { diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RRuntime.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RRuntime.java index 7951ef74bb05ff6fc2da8b3c2b418d24b0c4b8fa..063888c989a5f6bcc42eacc59695e0c10abf5895 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RRuntime.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RRuntime.java @@ -206,6 +206,12 @@ public class RRuntime { return frame; } + public static FrameDescriptor createFrameDescriptorWithMetaData(String name) { + FrameDescriptor fd = new FrameDescriptor(); + FrameSlotChangeMonitor.initializeFunctionFrameDescriptor(name, fd); + return fd; + } + /** * Since a distinguished NaN value is used for NA, checking for {@code isNaN} suffices. */ diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/TempPathName.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/TempPathName.java index c48a542d24f1f597e2360c95b18e62567c71d04c..fc28f3a8ebb2e13d7eb2e507d4b8902d3f1ccf97 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/TempPathName.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/TempPathName.java @@ -33,6 +33,7 @@ import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; import java.util.Random; +import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.r.runtime.context.RContext; import com.oracle.truffle.r.runtime.ffi.BaseRFFI; @@ -48,6 +49,9 @@ public class TempPathName implements RContext.ContextState { private static final int RANDOM_CHARACTERS_LENGTH = RANDOM_CHARACTERS.length(); private static final int RANDOM_LENGTH = 12; // as per GnuR private static final Random rand = new Random(); + private static final String DEPARSE_DIR_NAME = "deparse"; + + @CompilationFinal private static String deparseDir = null; private String tempDirPath; @@ -70,9 +74,54 @@ public class TempPathName implements RContext.ContextState { } else { Utils.rSuicide("cannot create 'R_TempDir'"); } + + // initialize deparse directory + if (deparseDir == null && FastROptions.EmitTmpSource.getBooleanValue()) { + deparseDir = determineDeparseDir(); + } + return this; } + @TruffleBoundary + private String determineDeparseDir() { + try { + // primary location: $R_HOME/deparse + String rHomeParent = ensureAccessibleDeparseDir(REnvVars.rHome()); + if (rHomeParent != null) { + return rHomeParent; + } + + // secondary location: /tmp/deparse + String tmpDir = ensureAccessibleDeparseDir(Utils.getUserTempDir()); + if (tmpDir != null) { + return tmpDir; + } + + // tertiary location: RtmpXXXXXX/deparse + String rtmpDir = ensureAccessibleDeparseDir(tempDirPath); + if (rtmpDir != null) { + return rtmpDir; + } + } catch (IOException e) { + // Be very defensive and do not even report this exception. + } + + // Cannot find writable location + return null; + } + + private static String ensureAccessibleDeparseDir(String parentDeparseDir) throws IOException { + Path rHomePath = Paths.get(parentDeparseDir); + if (Files.isDirectory(rHomePath) && Files.isWritable(rHomePath)) { + Path resolvedDeparseDir = Files.createDirectories(rHomePath.resolve(DEPARSE_DIR_NAME)); + if (Files.isDirectory(resolvedDeparseDir) && Files.isWritable(resolvedDeparseDir)) { + return resolvedDeparseDir.toAbsolutePath().toString(); + } + } + return null; + } + @Override @TruffleBoundary public void beforeDispose(RContext context) { @@ -94,6 +143,10 @@ public class TempPathName implements RContext.ContextState { return new TempPathName(); } + public static String deparsePath() { + return deparseDir; + } + @TruffleBoundary public static String createNonExistingFilePath(String pattern, String tempDir, String fileExt) { while (true) { diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/Utils.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/Utils.java index 7f887376309f6383d770b73bcbbbeab0277b5438..49576678aa8db83694bf7a05fd90db527222b682 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/Utils.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/Utils.java @@ -283,8 +283,14 @@ public final class Utils { */ public static Path getLogPath(String fileName) { String root = RContext.isEmbedded() ? "/tmp" : REnvVars.rHome(); - int pid = (int) BaseRFFI.GetpidRootNode.create().getCallTarget().call(); - String baseName = RContext.isEmbedded() ? fileName + "-" + Integer.toString(pid) : fileName; + String baseName; + if (RContext.isEmbedded()) { + int pid = RContext.getInitialPid(); + String threadName = Thread.currentThread().getName(); + baseName = fileName + "-" + Integer.toString(pid) + "-" + threadName; + } else { + baseName = fileName; + } return FileSystems.getDefault().getPath(root, baseName); } @@ -735,4 +741,11 @@ public final class Utils { return (int) (fileTime.toMillis() / 1000); } } + + /** + * Determines the PID using a system call. + */ + public static int getPid() { + return (int) BaseRFFI.GetpidRootNode.create().getCallTarget().call(); + } } 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 c20435b25add40eb7965dd44a5b16fb917ddfdfe..6de77baa7e43dbc7c86d6b2b19189aae6815a519 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 @@ -87,6 +87,7 @@ import com.oracle.truffle.r.runtime.RRuntime; import com.oracle.truffle.r.runtime.RRuntimeASTAccess; import com.oracle.truffle.r.runtime.RSerialize; import com.oracle.truffle.r.runtime.TempPathName; +import com.oracle.truffle.r.runtime.Utils; import com.oracle.truffle.r.runtime.builtins.RBuiltinDescriptor; import com.oracle.truffle.r.runtime.builtins.RBuiltinKind; import com.oracle.truffle.r.runtime.builtins.RBuiltinLookup; @@ -299,6 +300,7 @@ public final class RContext implements RTruffleObject { @CompilationFinal private static RBuiltinLookup builtinLookup; @CompilationFinal private static RForeignAccessFactory foreignAccessFactory; @CompilationFinal private static boolean initialContextInitialized; + @CompilationFinal private static int initialPid; public static boolean isInitialContextInitialized() { return initialContextInitialized; @@ -529,6 +531,11 @@ public final class RContext implements RTruffleObject { // that methods package is loaded this.methodTableDispatchOn = parentContext.methodTableDispatchOn; } + + if (initial) { + RContext.initialPid = Utils.getPid(); + } + if (initial && !embedded) { initialContextInitialized = true; } @@ -1012,4 +1019,11 @@ public final class RContext implements RTruffleObject { public RFFIContext getStateRFFI() { return stateRFFI; } + + /** + * Returns the PID as retrieved by the initial context. + */ + public static int getInitialPid() { + return initialPid; + } } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/env/REnvironment.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/env/REnvironment.java index c5cb2fe28d7ed2164b885310c67de92827874ca7..aea39489db98957faa8048ecfe9e8687fddfd185 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/env/REnvironment.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/env/REnvironment.java @@ -22,14 +22,20 @@ */ package com.oracle.truffle.r.runtime.env; +import java.lang.ref.WeakReference; import java.util.ArrayList; +import java.util.Collections; +import java.util.Map; +import java.util.WeakHashMap; import java.util.regex.Pattern; import com.oracle.truffle.api.CompilerAsserts; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.frame.Frame; +import com.oracle.truffle.api.frame.FrameDescriptor; import com.oracle.truffle.api.frame.FrameSlot; +import com.oracle.truffle.api.frame.FrameSlotKind; import com.oracle.truffle.api.frame.MaterializedFrame; import com.oracle.truffle.api.profiles.ValueProfile; import com.oracle.truffle.r.runtime.AnonymousFrameVariable; @@ -110,6 +116,8 @@ import com.oracle.truffle.r.runtime.env.frame.REnvTruffleFrameAccess; public abstract class REnvironment extends RAttributeStorage { public static final class ContextStateImpl implements RContext.ContextState { + private static Map<RStringVector, WeakReference<FrameDescriptor>> frameDescriptorCache = Collections.synchronizedMap(new WeakHashMap<>(0)); + private final MaterializedFrame globalFrame; @CompilationFinal private Base baseEnv; @CompilationFinal private REnvironment namespaceRegistry; @@ -175,6 +183,27 @@ public abstract class REnvironment extends RAttributeStorage { this.namespaceRegistry = newNamespaceRegistry; this.searchPath = newSearchPath; } + + public static FrameDescriptor getFrameDescriptorFromList(RList list) { + CompilerAsserts.neverPartOfCompilation(); + + RStringVector names = list.getNames(); + WeakReference<FrameDescriptor> weakReference = frameDescriptorCache.get(names); + FrameDescriptor fd = weakReference != null ? weakReference.get() : null; + + if (fd == null) { + // ensure that string vector is not modified anymore + names.makeSharedPermanent(); + + fd = RRuntime.createFrameDescriptorWithMetaData("<new-cachedfd-env>"); + for (int i = 0; i < list.getLength(); i++) { + FrameSlotKind valueSlotKind = RRuntime.getSlotKind(list.getDataAt(i)); + FrameSlotChangeMonitor.findOrAddFrameSlot(fd, names.getDataAt(i), valueSlotKind); + } + frameDescriptorCache.put(names, new WeakReference<>(fd)); + } + return fd; + } } public static class PutException extends RErrorException { @@ -217,6 +246,7 @@ public abstract class REnvironment extends RAttributeStorage { public static final String UNNAMED = new String(""); private static final String NAME_ATTR_KEY = "name"; private static final Empty emptyEnv = new Empty(); + private static final int LARGE_LIST_THRESHOLD = 100; private final String name; private final REnvFrameAccess frameAccess; @@ -662,9 +692,15 @@ public abstract class REnvironment extends RAttributeStorage { */ @TruffleBoundary public static REnvironment createFromList(RList list, REnvironment parent) { - REnvironment result = RDataFactory.createNewEnv(null); - RArguments.initializeEnclosingFrame(result.getFrame(), parent.getFrame()); + REnvironment result; RStringVector names = list.getNames(); + if (list.getLength() >= LARGE_LIST_THRESHOLD) { + FrameDescriptor cachedFd = ContextStateImpl.getFrameDescriptorFromList(list); + result = RDataFactory.createNewEnv(cachedFd, null); + } else { + result = RDataFactory.createNewEnv(null); + } + RArguments.initializeEnclosingFrame(result.getFrame(), parent.getFrame()); for (int i = 0; i < list.getLength(); i++) { try { result.put(names.getDataAt(i), UpdateShareableChildValue.update(list, list.getDataAt(i))); diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/DLL.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/DLL.java index 3c87ec5882ed1be0c79e2863e150b374ebbe4f85..94db655cbf26e41ce2bd67c199bd8cf7e0bec360 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/DLL.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/DLL.java @@ -503,6 +503,8 @@ public class DLL { * that errors loading (user) packages added to R_DEFAULT_PACKAGES do throw RErrors. */ private synchronized DLLInfo doLoad(String absPath, boolean local, boolean now, boolean addToList) throws DLLException { + RFFIContext stateRFFI = RContext.getInstance().getStateRFFI(); + long before = stateRFFI.beforeDowncall(); try { Object handle = dlOpenNode.execute(absPath, local, now); DLLInfo dllInfo = DLLInfo.create(libName(absPath), absPath, true, handle, addToList); @@ -514,6 +516,8 @@ public class DLL { } else { throw Utils.rSuicide(ex, "error loading default package: " + absPath + "\n" + dlError); } + } finally { + stateRFFI.afterDowncall(before); } } } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIContext.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIContext.java index d7c4b530deb07b92c8c632c3cfb218ff5de1603a..869932d2811be3a17f640145d5fc7d969d037e60 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIContext.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ffi/RFFIContext.java @@ -23,7 +23,8 @@ package com.oracle.truffle.r.runtime.ffi; import java.util.ArrayList; -import java.util.HashSet; +import java.util.IdentityHashMap; +import java.util.concurrent.atomic.AtomicInteger; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.interop.TruffleObject; @@ -74,7 +75,7 @@ public abstract class RFFIContext extends RFFI { * FastR equivalent of GNUR's special dedicated global list that is GC root and so any vectors * added to it will be guaranteed to be preserved. */ - public final HashSet<RObject> preserveList = new HashSet<>(); + public final IdentityHashMap<RObject, AtomicInteger> preserveList = new IdentityHashMap<>(); public abstract TruffleObject lookupNativeFunction(NativeFunction function); diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test index 06d54805ff27dfbfbc043ccfef202f211626b0d3..2a5c05f9d5f5e7d081d06d50d2491a96819121dc 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/ExpectedTestOutput.test @@ -29806,6 +29806,21 @@ character(0) #argv <- list(structure(c(TRUE, TRUE, FALSE, FALSE, TRUE, FALSE, FALSE, TRUE), .Names = c('1', '2', '3', '4', '5', '6', '7', '8')));is.atomic(argv[[1]]); [1] TRUE +##com.oracle.truffle.r.test.builtins.TestBuiltin_iscall.testIsCall# +#{ callExpr <- quote(asd[,1]); lapply(callExpr, function(x) is.call(x)) } +[[1]] +[1] FALSE + +[[2]] +[1] FALSE + +[[3]] +[1] FALSE + +[[4]] +[1] FALSE + + ##com.oracle.truffle.r.test.builtins.TestBuiltin_iscall.testIsCall# #{ cl <- call("f") ; is.call(cl) } [1] TRUE @@ -47870,6 +47885,10 @@ Error in quote() : 0 arguments passed to 'quote' which requires 1 #quote(expr=) +##com.oracle.truffle.r.test.builtins.TestBuiltin_quote.testQuote#Ignored.ImplementationError# +#typeof(quote(a[,2])[[3]]) +[1] "symbol" + ##com.oracle.truffle.r.test.builtins.TestBuiltin_quote.testQuote# #{ class(quote(x + y)) } [1] "call" @@ -47916,6 +47935,10 @@ list(1, 2) #{ quote(x <- x + 1) } x <- x + 1 +##com.oracle.truffle.r.test.builtins.TestBuiltin_quote.testQuote#Ignored.ImplementationError# +#{ res <- quote(a[,2])[[3]]; typeof(res) } +Error in typeof(res) : argument "res" is missing, with no default + ##com.oracle.truffle.r.test.builtins.TestBuiltin_quote.testQuote# #{ typeof(quote(1)) } [1] "double" diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_iscall.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_iscall.java index 3284783f60a7c80fdf8cbd61c6fae9af3e5ee2f1..08aa002465099c7aee8d8e131b1b65d513851389 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_iscall.java +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_iscall.java @@ -4,7 +4,7 @@ * http://www.gnu.org/licenses/gpl-2.0.html * * Copyright (c) 2012-2014, Purdue University - * Copyright (c) 2013, 2016, Oracle and/or its affiliates + * Copyright (c) 2013, 2017, Oracle and/or its affiliates * * All rights reserved. */ @@ -148,5 +148,6 @@ public class TestBuiltin_iscall extends TestBase { assertEval("{ cl <- call(\"f\", 2, 3) ; is.call(cl) }"); assertEval("{ cl <- list(f, 2, 3) ; is.call(cl) }"); assertEval("{ is.call(call) }"); + assertEval("{ callExpr <- quote(asd[,1]); lapply(callExpr, function(x) is.call(x)) }"); } } diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_quote.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_quote.java index 2eeee8079985c3634978268e7e77c3010d67cfdf..014088dfd5a3f4d81b6a4a3abb9cd6b3ffb733dc 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_quote.java +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/builtins/TestBuiltin_quote.java @@ -45,5 +45,8 @@ public class TestBuiltin_quote extends TestBase { // in GNUR, these behave inconsistently: assertEval(Ignored.ImplementationError, "quote()"); assertEval("quote(expr=)"); + + assertEval(Ignored.ImplementationError, "typeof(quote(a[,2])[[3]])"); + assertEval(Ignored.ImplementationError, "{ res <- quote(a[,2])[[3]]; typeof(res) }"); } }