diff --git a/.gitignore b/.gitignore index 5d9988fc555a0ffe21e17fc12ad5b733bf6df86d..3eed6ca4779f8143dad9407775f4be957a28d9d6 100644 --- a/.gitignore +++ b/.gitignore @@ -28,7 +28,7 @@ /com.oracle.truffle.r.native/include/linked /com.oracle.truffle.r.native/fficall/jni.done /com.oracle.truffle.r.native/fficall/jniboot.done - +/com.oracle.truffle.r.native.recommended/install.recommended /com.oracle.truffle.r.test.native/packages/copy_recommended /com.oracle.truffle.r.test.native/packages/recommended /com.oracle.truffle.r.test.native/packages/*/lib/* diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/REngine.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/REngine.java index 6e568a59be33fe0180fbcdf9d9144a7b885e4ddc..7589fcf8a876b1e29552a48df8e27e5fb4ddd724 100644 --- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/REngine.java +++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/REngine.java @@ -429,8 +429,7 @@ final class REngine implements Engine, Engine.Timings { } } RArgsValuesAndNames reorderedArgs = CallMatcherGenericNode.reorderArguments(args, func, - names == null ? ArgumentsSignature.empty(args.length) : ArgumentsSignature.get(names.getDataWithoutCopying()), false, - RError.NO_CALLER); + names == null ? ArgumentsSignature.empty(args.length) : ArgumentsSignature.get(names.getDataWithoutCopying()), RError.NO_CALLER); Object[] newArgs = reorderedArgs.getArguments(); for (int i = 0; i < newArgs.length; i++) { Object arg = newArgs[i]; diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RRuntimeASTAccessImpl.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RRuntimeASTAccessImpl.java index 5267b65c3b23ac7727aa16c60915587ba23389b8..385068b962e9778e2a57a8c6c5b3b901ec02782b 100644 --- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RRuntimeASTAccessImpl.java +++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/RRuntimeASTAccessImpl.java @@ -43,7 +43,6 @@ import com.oracle.truffle.r.nodes.RASTBuilder; import com.oracle.truffle.r.nodes.RASTUtils; import com.oracle.truffle.r.nodes.RRootNode; import com.oracle.truffle.r.nodes.access.ConstantNode; -import com.oracle.truffle.r.nodes.access.WriteVariableNode; import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode; import com.oracle.truffle.r.nodes.builtin.RBuiltinNode; import com.oracle.truffle.r.nodes.builtin.RBuiltinRootNode; @@ -54,7 +53,7 @@ import com.oracle.truffle.r.nodes.builtin.helpers.TraceHandling; import com.oracle.truffle.r.nodes.control.AbstractLoopNode; import com.oracle.truffle.r.nodes.control.BlockNode; import com.oracle.truffle.r.nodes.control.IfNode; -import com.oracle.truffle.r.nodes.control.ReplacementNode; +import com.oracle.truffle.r.nodes.control.ReplacementDispatchNode; import com.oracle.truffle.r.nodes.function.FunctionDefinitionNode; import com.oracle.truffle.r.nodes.function.FunctionExpressionNode; import com.oracle.truffle.r.nodes.function.RCallNode; @@ -442,22 +441,9 @@ class RRuntimeASTAccessImpl implements RRuntimeASTAccess { @Override public String getCallerSource(RLanguage rl) { - RLanguage elem = rl; - - /* - * This checks for the specific structure of replacements, to display the replacement - * instead of the "internal" form (with *tmp*, etc.) of the update call. - */ - - RSyntaxNode sn = (RSyntaxNode) rl.getRep(); - Node parent = RASTUtils.unwrapParent(sn.asNode()); - if (parent instanceof WriteVariableNode) { - WriteVariableNode wvn = (WriteVariableNode) parent; - if (wvn.getParent() instanceof ReplacementNode) { - elem = RDataFactory.createLanguage((RNode) wvn.getParent()); - } - } - + // This checks for the specific structure of replacements + RLanguage replacement = ReplacementDispatchNode.getRLanguage(rl); + RLanguage elem = replacement == null ? rl : replacement; String string = RDeparse.deparse(elem, RDeparse.DEFAULT_Cutoff, true, 0, -1); return string.split("\n")[0]; } @@ -517,8 +503,8 @@ class RRuntimeASTAccessImpl implements RRuntimeASTAccess { @Override public RSyntaxNode[] isReplacementNode(Node node) { - if (node instanceof ReplacementNode) { - ReplacementNode rn = (ReplacementNode) node; + if (node instanceof ReplacementDispatchNode) { + ReplacementDispatchNode rn = (ReplacementDispatchNode) node; return new RSyntaxNode[]{rn.getLhs(), rn.getRhs()}; } else { return null; diff --git a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/Covcor.java b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/Covcor.java index d8e7acbe2183ac5c099d815e9a5709584b925488..1aff0091ef52ff496d09f462d7c2a1fd8db2e4dd 100644 --- a/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/Covcor.java +++ b/com.oracle.truffle.r.library/src/com/oracle/truffle/r/library/stats/Covcor.java @@ -11,15 +11,22 @@ */ package com.oracle.truffle.r.library.stats; +import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.eq; +import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.toBoolean; +import static com.oracle.truffle.r.runtime.RError.NO_CALLER; +import static com.oracle.truffle.r.runtime.RError.SHOW_CALLER; + import java.util.Arrays; +import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.profiles.BranchProfile; import com.oracle.truffle.api.profiles.ConditionProfile; import com.oracle.truffle.api.profiles.LoopConditionProfile; +import com.oracle.truffle.r.nodes.builtin.CastBuilder; import com.oracle.truffle.r.nodes.builtin.RExternalBuiltinNode; import com.oracle.truffle.r.runtime.RError; +import com.oracle.truffle.r.runtime.RError.Message; import com.oracle.truffle.r.runtime.RRuntime; -import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames; import com.oracle.truffle.r.runtime.data.RDataFactory; import com.oracle.truffle.r.runtime.data.RDoubleVector; import com.oracle.truffle.r.runtime.data.RIntVector; @@ -31,7 +38,7 @@ import com.oracle.truffle.r.runtime.ops.na.NACheck; /* * Logic derived from GNU-R, library/stats/src/cov.c */ -public final class Covcor extends RExternalBuiltinNode { +public abstract class Covcor extends RExternalBuiltinNode.Arg4 { private final boolean isCor; @@ -40,20 +47,21 @@ public final class Covcor extends RExternalBuiltinNode { } @Override - public Object call(RArgsValuesAndNames args) { - Object[] argValues = args.getArguments(); - if (argValues[0] == RNull.instance) { - throw RError.error(this, RError.Message.IS_NULL, "x"); - } + protected void createCasts(CastBuilder casts) { + casts.arg(0).mustNotBeNull(SHOW_CALLER, Message.IS_NULL, "x").asDoubleVector(); + casts.arg(1).allowNull().asDoubleVector(); + casts.arg(2).asIntegerVector().findFirst().mustBe(eq(4), this, Message.NYI, "covcor: other method than 4 not implemented."); + casts.arg(3).asLogicalVector().findFirst().map(toBoolean()); + } - RAbstractDoubleVector x = castDouble(castVector(argValues[0])); - RAbstractDoubleVector y = argValues[1] == RNull.instance ? null : castDouble(castVector(argValues[1])); - int method = castInt(castVector(argValues[2])); - if (method != 4) { - throw RError.nyi(this, "method"); - } - boolean iskendall = RRuntime.fromLogical(castLogical(castVector(argValues[3]))); - return corcov(x.materialize(), y != null ? y.materialize() : null, method, iskendall, this); + @Specialization + public Object call(RAbstractDoubleVector x, @SuppressWarnings("unused") RNull y, int method, boolean iskendall) { + return corcov(x.materialize(), null, method, iskendall, this); + } + + @Specialization + public Object call(RAbstractDoubleVector x, RAbstractDoubleVector y, int method, boolean iskendall) { + return corcov(x.materialize(), y.materialize(), method, iskendall, this); } private final NACheck check = NACheck.create(); @@ -730,9 +738,8 @@ public final class Covcor extends RExternalBuiltinNode { } } - private static void error(String string) { - // TODO should be an R error - throw new UnsupportedOperationException("error: " + string); + private static void error(String message) { + RError.error(NO_CALLER, Message.GENERIC, message); } private boolean checkNAs(double... xs) { diff --git a/com.oracle.truffle.r.native.recommended/Makefile b/com.oracle.truffle.r.native.recommended/Makefile index 0b26a47e96d57e4be0a80cc85d7b15b16ecc8efd..3d63321e40ed093c6ad02e5474a62e6c68106e3e 100644 --- a/com.oracle.truffle.r.native.recommended/Makefile +++ b/com.oracle.truffle.r.native.recommended/Makefile @@ -33,10 +33,10 @@ FASTR_R_HOME := $(abspath $(CURDIR)/..) NATIVE_PROJECT := $(subst native.recommended,native,$(CURDIR)) R_VERSION := $(notdir $(wildcard $(NATIVE_PROJECT)/gnur/R-*)) GNUR_HOME := $(NATIVE_PROJECT)/gnur/$(R_VERSION) -GNUR_RECOMMENDED_TARS := $(wildcard $(GNUR_HOME)/src/library/Recommended/*.tgz) +# order matters due to inter-package dependencies +GNUR_RECOMMENDED_PKGNAMES := MASS boot class cluster codetools lattice nnet spatial Matrix survival KernSmooth foreign nlme rpart +GNUR_RECOMMENDED_TARS := $(foreach pkg, $(GNUR_RECOMMENDED_PKGNAMES),$(GNUR_HOME)/src/library/Recommended/$(pkg).tgz) #$(info GNUR_RECOMMENDED_TARS=$(GNUR_RECOMMENDED_TARS)) -GNUR_RECOMMENDED_PKGNAMES := $(foreach tar,$(GNUR_RECOMMENDED_TARS),$(notdir $(basename $(tar)))) -#$(info GNUR_RECOMMENDED_PKGNAMES=$(GNUR_RECOMMENDED_PKGNAMES)) all: install.recommended diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/RBuiltinPackage.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/RBuiltinPackage.java index 06b2e17a7b9cd91938240e5a16400b54c68ef8d5..bc9a8097e0afd1b4b9d4f1119f307885cf0144fa 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/RBuiltinPackage.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/RBuiltinPackage.java @@ -22,10 +22,6 @@ */ package com.oracle.truffle.r.nodes.builtin; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -36,8 +32,8 @@ import com.oracle.truffle.api.frame.MaterializedFrame; import com.oracle.truffle.api.source.Source; import com.oracle.truffle.r.runtime.ArgumentsSignature; import com.oracle.truffle.r.runtime.RInternalError; +import com.oracle.truffle.r.runtime.RSource; import com.oracle.truffle.r.runtime.ResourceHandlerFactory; -import com.oracle.truffle.r.runtime.Utils; import com.oracle.truffle.r.runtime.builtins.RBuiltin; import com.oracle.truffle.r.runtime.builtins.RSpecialFactory; import com.oracle.truffle.r.runtime.context.Engine.ParseException; @@ -93,7 +89,6 @@ public abstract class RBuiltinPackage { protected RBuiltinPackage(String name) { this.name = name; - // Check for overriding R code ArrayList<Source> componentList = getRFiles(getName()); if (componentList.size() > 0) { @@ -111,22 +106,10 @@ public abstract class RBuiltinPackage { */ public static ArrayList<Source> getRFiles(String pkgName) { ArrayList<Source> componentList = new ArrayList<>(); - try { - InputStream is = ResourceHandlerFactory.getHandler().getResourceAsStream(RBuiltinPackage.class, pkgName + "/R"); - if (is != null) { - try (BufferedReader r = new BufferedReader(new InputStreamReader(is))) { - String line; - while ((line = r.readLine()) != null) { - if (line.endsWith(".r") || line.endsWith(".R")) { - final String rResource = pkgName + "/R/" + line.trim(); - Source content = Utils.getResourceAsSource(RBuiltinPackage.class, rResource); - componentList.add(content); - } - } - } - } - } catch (IOException ex) { - Utils.rSuicide("error loading R code from " + pkgName + " : " + ex); + String[] rFileContents = ResourceHandlerFactory.getHandler().getRFiles(RBuiltinPackage.class, pkgName); + for (String rFileContent : rFileContents) { + Source content = RSource.fromTextInternal(rFileContent, RSource.Internal.R_IMPL); + componentList.add(content); } return componentList; } diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/S3DispatchFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/S3DispatchFunctions.java index 94669aeca126c7bc2bdba0e2c953aa716111eee4..91aa642cb77db7cde6343807f75c40d3cd2de3af 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/S3DispatchFunctions.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/S3DispatchFunctions.java @@ -53,6 +53,7 @@ import com.oracle.truffle.r.runtime.data.RNull; import com.oracle.truffle.r.runtime.data.RPromise; import com.oracle.truffle.r.runtime.data.RStringVector; import com.oracle.truffle.r.runtime.data.model.RAbstractContainer; +import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector; public abstract class S3DispatchFunctions extends RBuiltinNode { @@ -64,7 +65,7 @@ public abstract class S3DispatchFunctions extends RBuiltinNode { protected S3DispatchFunctions(boolean nextMethod) { methodLookup = S3FunctionLookupNode.create(true, nextMethod); - callMatcher = CallMatcherNode.create(nextMethod, false); + callMatcher = CallMatcherNode.create(false); } protected MaterializedFrame getCallerFrame(VirtualFrame frame) { @@ -219,6 +220,9 @@ public abstract class S3DispatchFunctions extends RBuiltinNode { } protected static boolean isNotString(Object obj) { + // Note: if RAbstractStringVector becomes expected, then it must have length == 1, GnuR + // ignores character vectors longer than 1 as the "generic" argument of NextMethod + assert !(obj instanceof RAbstractStringVector) || ((RAbstractStringVector) obj).getLength() != 1 : "unexpected RAbstractStringVector with length != 1"; return !(obj instanceof String); } diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/ForeignFunctions.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/ForeignFunctions.java index 76f8082bdc4eb0bc4ab028f23c6fe318ac9a0d42..2821716614aecf0854f0b63cd0fee013341867f0 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/ForeignFunctions.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/foreign/ForeignFunctions.java @@ -42,7 +42,7 @@ import com.oracle.truffle.r.library.methods.SlotFactory.R_setSlotNodeGen; import com.oracle.truffle.r.library.methods.SubstituteDirectNodeGen; import com.oracle.truffle.r.library.parallel.ParallelFunctionsFactory.MCIsChildNodeGen; import com.oracle.truffle.r.library.stats.CompleteCases; -import com.oracle.truffle.r.library.stats.Covcor; +import com.oracle.truffle.r.library.stats.CovcorNodeGen; import com.oracle.truffle.r.library.stats.Dbinom; import com.oracle.truffle.r.library.stats.GammaFunctionsFactory.QgammaNodeGen; import com.oracle.truffle.r.library.stats.Pbinom; @@ -345,9 +345,9 @@ public class ForeignFunctions { case "fft": return FftNodeGen.create(); case "cov": - return new Covcor(false); + return CovcorNodeGen.create(false); case "cor": - return new Covcor(true); + return CovcorNodeGen.create(true); case "SplineCoef": return SplineCoefNodeGen.create(); case "SplineEval": diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/AccessField.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/AccessField.java index b7042dba728d69019cdbbdddff0ed9051dd10677..0aafef63839fe6e7fb309d335155efc1b8a8c9b5 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/AccessField.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/AccessField.java @@ -87,7 +87,7 @@ public abstract class AccessField extends RBuiltinNode { casts.arg(1).defaultError(Message.INVALID_SUBSCRIPT_TYPE, RType.Language.getName()).mustBe(stringValue()).asStringVector().findFirst(); } - public static RNode createSpecial(ArgumentsSignature signature, RNode[] arguments) { + public static RNode createSpecial(ArgumentsSignature signature, RNode[] arguments, @SuppressWarnings("unused") boolean inReplacement) { return signature.getNonNullCount() == 0 ? AccessFieldSpecialNodeGen.create(arguments) : null; } diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/Subscript.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/Subscript.java index 0151d99d910b40d7313c630e7684cc1c3a429a20..3a88c5ec748882bb8d2b9c2b75a58972e53c037f 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/Subscript.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/Subscript.java @@ -123,7 +123,7 @@ abstract class SubscriptSpecial extends SubscriptSpecialBase { } } -@RBuiltin(name = "[[", kind = PRIMITIVE, parameterNames = {"", "...", "exact", "drop"}, dispatch = INTERNAL_GENERIC, behavior = PURE) +@RBuiltin(name = "[[", kind = PRIMITIVE, parameterNames = {"x", "...", "exact", "drop"}, dispatch = INTERNAL_GENERIC, behavior = PURE) public abstract class Subscript extends RBuiltinNode { @RBuiltin(name = ".subset2", kind = PRIMITIVE, parameterNames = {"x", "...", "exact", "drop"}, behavior = PURE) @@ -131,7 +131,7 @@ public abstract class Subscript extends RBuiltinNode { // same implementation as "[[", with different dispatch } - public static RNode special(ArgumentsSignature signature, RNode[] arguments) { + public static RNode special(ArgumentsSignature signature, RNode[] arguments, @SuppressWarnings("unused") boolean inReplacement) { return signature.getNonNullCount() == 0 && arguments.length == 2 ? SubscriptSpecialNodeGen.create(arguments) : null; } diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/Subset.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/Subset.java index 1a9f76aff93e2c48a5fc300d204adf92b837fea9..09f0694fc7d050d6ce1b44fc01a32a4482b6754b 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/Subset.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/Subset.java @@ -88,8 +88,14 @@ public abstract class Subset extends RBuiltinNode { // same implementation as "[", with different dispatch } - public static RNode special(ArgumentsSignature signature, RNode[] arguments) { - return signature.getNonNullCount() == 0 && arguments.length == 2 ? SubsetSpecialNodeGen.create(arguments) : null; + public static RNode special(ArgumentsSignature signature, RNode[] arguments, boolean inReplacement) { + boolean correctSignature = signature.getNonNullCount() == 0 && arguments.length == 2; + if (!correctSignature) { + return null; + } + // Subset adds support for lists returning newly created list, which cannot work when used + // in replacement, because we need the reference to the existing (materialized) list element + return inReplacement ? SubscriptSpecialBaseNodeGen.create(arguments) : SubsetSpecialNodeGen.create(arguments); } @Child private ExtractVectorNode extractNode = ExtractVectorNode.create(ElementAccessMode.SUBSET, false); diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/UpdateField.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/UpdateField.java index 0d8cb22f53ff6da3aed269da0a809f3b061d5823..7a9928cbd661f3ee540bbac308e5f8190d38d11e 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/UpdateField.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/UpdateField.java @@ -82,7 +82,6 @@ abstract class UpdateFieldSpecial extends SpecialsUtils.ListFieldSpecialBase { } @Fallback - @SuppressWarnings("unused") public void doFallback(Object container, Object field, Object value) { throw RSpecialFactory.throwFullCallNeeded(); } @@ -109,7 +108,7 @@ public abstract class UpdateField extends RBuiltinNode { casts.arg(1).defaultError(Message.INVALID_SUBSCRIPT).mustBe(stringValue()).asStringVector().findFirst(); } - public static RNode createSpecial(ArgumentsSignature signature, RNode[] arguments) { + public static RNode createSpecial(ArgumentsSignature signature, RNode[] arguments, @SuppressWarnings("unused") boolean inReplacement) { return SpecialsUtils.isCorrectUpdateSignature(signature) && arguments.length == 3 ? UpdateFieldSpecialNodeGen.create(arguments) : null; } diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/UpdateSubscript.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/UpdateSubscript.java index 9172b162b1c920dd68846c03d6566791804f71f7..4d17163f6d024c20abb2f8c7cba29e93e613a98f 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/UpdateSubscript.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/UpdateSubscript.java @@ -74,50 +74,60 @@ abstract class UpdateSubscriptSpecial extends SubscriptSpecialCommon { } @Specialization(guards = {"simple(vector)", "!vector.isShared()", "isValidIndex(vector, index)"}) - protected RIntVector access(RIntVector vector, int index, int value) { + protected RIntVector set(RIntVector vector, int index, int value) { return vector.updateDataAt(index - 1, value, naCheck); } @Specialization(guards = {"simple(vector)", "!vector.isShared()", "isValidIndex(vector, index)"}) - protected RDoubleVector access(RDoubleVector vector, int index, double value) { + protected RDoubleVector set(RDoubleVector vector, int index, double value) { return vector.updateDataAt(index - 1, value, naCheck); } @Specialization(guards = {"simple(vector)", "!vector.isShared()", "isValidIndex(vector, index)"}) - protected RStringVector access(RStringVector vector, int index, String value) { + protected RStringVector set(RStringVector vector, int index, String value) { return vector.updateDataAt(index - 1, value, naCheck); } @Specialization(guards = {"simple(list)", "!list.isShared()", "isValidIndex(list, index)", "isSingleElement(value)"}) - protected static Object access(RList list, int index, Object value) { + protected static Object set(RList list, int index, Object value) { list.setDataAt(list.getInternalStore(), index - 1, value); return list; } @Specialization(guards = {"simple(vector)", "!vector.isShared()", "isValidDoubleIndex(vector, index)"}) - protected RIntVector accessDoubleIndex(RIntVector vector, double index, int value) { + protected RIntVector setDoubleIndex(RIntVector vector, double index, int value) { + return vector.updateDataAt(toIndex(index) - 1, value, naCheck); + } + + @Specialization(guards = {"simple(vector)", "!vector.isShared()", "isValidIndex(vector, index)"}) + protected RDoubleVector setDoubleIntIndexIntValue(RDoubleVector vector, int index, int value) { + return vector.updateDataAt(toIndex(index) - 1, value, naCheck); + } + + @Specialization(guards = {"simple(vector)", "!vector.isShared()", "isValidDoubleIndex(vector, index)"}) + protected RDoubleVector setDoubleIndexIntValue(RDoubleVector vector, double index, int value) { return vector.updateDataAt(toIndex(index) - 1, value, naCheck); } @Specialization(guards = {"simple(vector)", "!vector.isShared()", "isValidDoubleIndex(vector, index)"}) - protected RDoubleVector accessDoubleIndex(RDoubleVector vector, double index, double value) { + protected RDoubleVector setDoubleIndex(RDoubleVector vector, double index, double value) { return vector.updateDataAt(toIndex(index) - 1, value, naCheck); } @Specialization(guards = {"simple(vector)", "!vector.isShared()", "isValidDoubleIndex(vector, index)"}) - protected RStringVector accessDoubleIndex(RStringVector vector, double index, String value) { + protected RStringVector setDoubleIndex(RStringVector vector, double index, String value) { return vector.updateDataAt(toIndex(index) - 1, value, naCheck); } @Specialization(guards = {"simple(list)", "!list.isShared()", "isValidDoubleIndex(list, index)", "isSingleElement(value)"}) - protected Object accessDoubleIndex(RList list, double index, Object value) { + protected Object setDoubleIndex(RList list, double index, Object value) { list.setDataAt(list.getInternalStore(), toIndex(index) - 1, value); return list; } @SuppressWarnings("unused") @Fallback - protected static Object access(Object vector, Object index, Object value) { + protected static Object setFallback(Object vector, Object index, Object value) { throw RSpecialFactory.throwFullCallNeeded(); } } @@ -129,7 +139,7 @@ public abstract class UpdateSubscript extends RBuiltinNode { private final ConditionProfile argsLengthLargerThanOneProfile = ConditionProfile.createBinaryProfile(); - public static RNode special(ArgumentsSignature signature, RNode[] arguments) { + public static RNode special(ArgumentsSignature signature, RNode[] arguments, @SuppressWarnings("unused") boolean inReplacement) { return SpecialsUtils.isCorrectUpdateSignature(signature) && arguments.length == 3 ? UpdateSubscriptSpecialNodeGen.create(arguments) : null; } diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/UpdateSubset.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/UpdateSubset.java index a876a55c0ed452d0fd1fb0e9ad4475e51b382bd9..5c92d85bf3ca0df6b123e20f9cec2b15573b631a 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/UpdateSubset.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/infix/UpdateSubset.java @@ -60,7 +60,7 @@ public abstract class UpdateSubset extends RBuiltinNode { @Child private ReplaceVectorNode replaceNode = ReplaceVectorNode.create(ElementAccessMode.SUBSET, false); private final ConditionProfile argsLengthLargerThanOneProfile = ConditionProfile.createBinaryProfile(); - public static RNode special(ArgumentsSignature signature, RNode[] arguments) { + public static RNode special(ArgumentsSignature signature, RNode[] arguments, @SuppressWarnings("unused") boolean inReplacement) { return SpecialsUtils.isCorrectUpdateSignature(signature) && arguments.length == 3 ? UpdateSubsetSpecialNodeGen.create(arguments) : null; } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RASTBuilder.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RASTBuilder.java index 6bf78693713bc14e41e9f5d82229a6155e261e4e..630d3da88adc1d0578fdf2807ca06fcc3ac7a772 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RASTBuilder.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/RASTBuilder.java @@ -22,7 +22,6 @@ */ package com.oracle.truffle.r.nodes; -import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -41,24 +40,24 @@ import com.oracle.truffle.r.nodes.control.ForNode; import com.oracle.truffle.r.nodes.control.IfNode; import com.oracle.truffle.r.nodes.control.NextNode; import com.oracle.truffle.r.nodes.control.RepeatNode; -import com.oracle.truffle.r.nodes.control.ReplacementNode; +import com.oracle.truffle.r.nodes.control.ReplacementDispatchNode; +import com.oracle.truffle.r.nodes.control.ReplacementDispatchNode.LHSError; import com.oracle.truffle.r.nodes.control.WhileNode; import com.oracle.truffle.r.nodes.function.FormalArguments; import com.oracle.truffle.r.nodes.function.FunctionDefinitionNode; import com.oracle.truffle.r.nodes.function.FunctionExpressionNode; import com.oracle.truffle.r.nodes.function.PostProcessArgumentsNode; +import com.oracle.truffle.r.nodes.function.RCallNode; import com.oracle.truffle.r.nodes.function.RCallSpecialNode; import com.oracle.truffle.r.nodes.function.SaveArgumentsNode; import com.oracle.truffle.r.nodes.function.WrapDefaultArgumentNode; import com.oracle.truffle.r.nodes.function.signature.MissingNode; -import com.oracle.truffle.r.nodes.unary.GetNonSharedNodeGen; import com.oracle.truffle.r.runtime.ArgumentsSignature; import com.oracle.truffle.r.runtime.FastROptions; import com.oracle.truffle.r.runtime.RInternalError; import com.oracle.truffle.r.runtime.RRuntime; import com.oracle.truffle.r.runtime.builtins.FastPathFactory; import com.oracle.truffle.r.runtime.data.REmpty; -import com.oracle.truffle.r.runtime.data.RNull; import com.oracle.truffle.r.runtime.env.frame.FrameSlotChangeMonitor; import com.oracle.truffle.r.runtime.nodes.EvaluatedArgumentsVisitor; import com.oracle.truffle.r.runtime.nodes.RCodeBuilder; @@ -79,6 +78,7 @@ import com.oracle.truffle.r.runtime.nodes.RSyntaxNode; public final class RASTBuilder implements RCodeBuilder<RSyntaxNode> { private final Map<String, Object> constants; + private CodeBuilderContext context = CodeBuilderContext.DEFAULT; public RASTBuilder() { this.constants = null; @@ -88,6 +88,10 @@ public final class RASTBuilder implements RCodeBuilder<RSyntaxNode> { this.constants = constants; } + private RCallNode unused() { + return null; // we need reference to RCallNode, otherwise it won't compile, compilation bug? + } + @Override public RSyntaxNode call(SourceSection source, RSyntaxNode lhs, List<Argument<RSyntaxNode>> args) { if (lhs instanceof RSyntaxLookup) { @@ -164,7 +168,7 @@ public final class RASTBuilder implements RCodeBuilder<RSyntaxNode> { if (c.getValue() instanceof String) { name = (String) c.getValue(); } else { - return new ReplacementNode.LHSError(source, operator, replacementLhs, replacementRhs, false); + return new LHSError(source, operator, replacementLhs, replacementRhs); } } else { throw RInternalError.unimplemented("unexpected lhs type: " + replacementLhs.getClass()); @@ -190,162 +194,8 @@ public final class RASTBuilder implements RCodeBuilder<RSyntaxNode> { } } - /** - * Creates a call that looks like {@code fun} but has the first argument replaced with - * {@code newLhs}. - */ - private RNode createSpecialFunctionQuery(RSyntaxNode newLhs, RSyntaxCall fun) { - RSyntaxElement[] arguments = fun.getSyntaxArguments(); - - RSyntaxNode[] argNodes = new RSyntaxNode[arguments.length]; - for (int i = 0; i < arguments.length; i++) { - argNodes[i] = i == 0 ? newLhs : process(arguments[i]); - } - - return RCallSpecialNode.createCall(fun.getSourceSection(), process(fun.getSyntaxLHS()).asRNode(), fun.getSyntaxSignature(), argNodes).asRNode(); - } - - /** - * Creates a call that looks like {@code fun}, but has its first argument replaced with - * {@code newLhs}, its target turned into an update function ("foo<-"), with the given value - * added to the arguments list. - */ - private RNode createFunctionUpdate(SourceSection source, RSyntaxNode newLhs, RSyntaxNode rhs, RSyntaxCall fun) { - RSyntaxElement[] arguments = fun.getSyntaxArguments(); - - ArgumentsSignature signature = fun.getSyntaxSignature(); - RSyntaxNode[] argNodes = new RSyntaxNode[arguments.length + 1]; - String[] names = new String[argNodes.length]; - for (int i = 0; i < arguments.length; i++) { - names[i] = signature.getName(i); - argNodes[i] = i == 0 ? newLhs : process(arguments[i]); - } - argNodes[argNodes.length - 1] = rhs; - names[argNodes.length - 1] = "value"; - - RSyntaxElement syntaxLHS = fun.getSyntaxLHS(); - RSyntaxNode newSyntaxLHS; - if (syntaxLHS instanceof RSyntaxLookup) { - RSyntaxLookup lookupLHS = (RSyntaxLookup) syntaxLHS; - String symbol = lookupLHS.getIdentifier(); - if ("slot".equals(symbol) || "@".equals(symbol)) { - // this is pretty gross, but at this point seems like the only way to get setClass - // to work properly - argNodes[0] = GetNonSharedNodeGen.create(argNodes[0].asRNode()); - } - newSyntaxLHS = lookup(lookupLHS.getSourceSection(), symbol + "<-", true); - } else { - // data types (and lengths) are verified in isNamespaceLookupCall - RSyntaxCall callLHS = (RSyntaxCall) syntaxLHS; - RSyntaxElement[] oldArgs = callLHS.getSyntaxArguments(); - RSyntaxNode[] newArgs = new RSyntaxNode[2]; - newArgs[0] = (RSyntaxNode) oldArgs[0]; - newArgs[1] = lookup(oldArgs[1].getSourceSection(), ((RSyntaxLookup) oldArgs[1]).getIdentifier() + "<-", true); - newSyntaxLHS = RCallSpecialNode.createCall(callLHS.getSourceSection(), ((RSyntaxNode) callLHS.getSyntaxLHS()).asRNode(), callLHS.getSyntaxSignature(), newArgs); - } - return RCallSpecialNode.createCall(source, newSyntaxLHS.asRNode(), ArgumentsSignature.get(names), argNodes).asRNode(); - } - - /* - * Determines if syntax call is of the form foo::bar - */ - private static boolean isNamespaceLookupCall(RSyntaxElement e) { - if (e instanceof RSyntaxCall) { - RSyntaxCall call = (RSyntaxCall) e; - // check for syntax nodes as this will be required to recreate a call during - // replacement form construction in createFunctionUpdate - if (call.getSyntaxLHS() instanceof RSyntaxLookup && call.getSyntaxLHS() instanceof RSyntaxNode) { - if (((RSyntaxLookup) call.getSyntaxLHS()).getIdentifier().equals("::")) { - RSyntaxElement[] args = call.getSyntaxArguments(); - if (args.length == 2 && args[0] instanceof RSyntaxLookup && args[0] instanceof RSyntaxNode && args[1] instanceof RSyntaxLookup && args[1] instanceof RSyntaxNode) { - return true; - } - } - } - } - return false; - } - - // used to create unique temp names for nested replacements - private int tempNamesCount; - - /** - * This method builds the sequence of calls needed to represent a replacement.<br/> - * For example, the replacement {@code a(b(c(x),i),j) <- z} should be decomposed into the - * following statements: - * - * <pre> - * t3 <- x - * t2 <- c(t3) - * t1 <- b(t2,i) - * tt0 <- z - * tt1 <- `a<-`(t1, j, tt0) // b(...) with a replaced - * tt2 <- `b<-`(t2, i, tt1) // a(...) with b replaced - * x <- `c<-`(t3, tt2) // x with c replaced - * </pre> - */ private RSyntaxNode createReplacement(SourceSection source, RSyntaxNode lhs, RSyntaxNode rhs, String operator, boolean isSuper) { - /* - * Collect all the function calls in this replacement. For "a(b(x)) <- z", this would be - * "a(...)" and "b(...)". - */ - List<RSyntaxCall> calls = new ArrayList<>(); - RSyntaxElement current = lhs; - while (!(current instanceof RSyntaxLookup)) { - if (!(current instanceof RSyntaxCall)) { - return new ReplacementNode.LHSError(source, operator, lhs, rhs, current instanceof RSyntaxConstant && ((RSyntaxConstant) current).getValue() == RNull.instance); - } - RSyntaxCall call = (RSyntaxCall) current; - calls.add(call); - - RSyntaxElement syntaxLHS = call.getSyntaxLHS(); - if (call.getSyntaxArguments().length == 0 || !(syntaxLHS instanceof RSyntaxLookup || isNamespaceLookupCall(syntaxLHS))) { - return new ReplacementNode.LHSError(source, operator, lhs, rhs, true); - } - current = call.getSyntaxArguments()[0]; - } - RSyntaxLookup variable = (RSyntaxLookup) current; - - List<RNode> instructions = new ArrayList<>(); - int tempNamesIndex = tempNamesCount; - tempNamesCount += calls.size() + 1; - - /* - * Create the calls that extract inner components - only needed for complex replacements - * like "a(b(x)) <- z" (where we would extract "b(x)"). - */ - for (int i = calls.size() - 1; i >= 1; i--) { - ReadVariableNode newLhs = ReadVariableNode.create("*tmp*" + (tempNamesIndex + i + 1)); - RNode update = createSpecialFunctionQuery(newLhs, calls.get(i)); - instructions.add(WriteVariableNode.createAnonymous("*tmp*" + (tempNamesIndex + i), update, WriteVariableNode.Mode.INVISIBLE)); - } - /* - * Create the update calls, for "a(b(x)) <- z", this would be `a<-` and `b<-`. - */ - for (int i = 0; i < calls.size(); i++) { - RNode update = createFunctionUpdate(source, ReadVariableNode.create("*tmp*" + (tempNamesIndex + i + 1)), ReadVariableNode.create("*tmpr*" + (tempNamesIndex + i - 1)), - calls.get(i)); - if (i < calls.size() - 1) { - instructions.add(WriteVariableNode.createAnonymous("*tmpr*" + (tempNamesIndex + i), update, WriteVariableNode.Mode.INVISIBLE)); - } else { - instructions.add(WriteVariableNode.createAnonymous(variable.getIdentifier(), update, WriteVariableNode.Mode.REGULAR, isSuper)); - } - } - - ReadVariableNode variableValue = createReplacementForVariableUsing(variable, isSuper); - ReplacementNode newReplacementNode = new ReplacementNode(source, operator, lhs, rhs, "*tmpr*" + (tempNamesIndex - 1), - variableValue, "*tmp*" + (tempNamesIndex + calls.size()), instructions); - - tempNamesCount -= calls.size() + 1; - return newReplacementNode; - } - - private static ReadVariableNode createReplacementForVariableUsing(RSyntaxLookup var, boolean isSuper) { - if (isSuper) { - return ReadVariableNode.createSuperLookup(var.getSourceSection(), var.getIdentifier()); - } else { - return ReadVariableNode.create(var.getSourceSection(), var.getIdentifier(), true); - } + return new ReplacementDispatchNode(source, operator, lhs, rhs, isSuper, this.context.getReplacementVarsStartIndex()); } public static FastPathFactory createFunctionFastPath(RSyntaxElement body, ArgumentsSignature signature) { @@ -411,6 +261,16 @@ public final class RASTBuilder implements RCodeBuilder<RSyntaxNode> { return Truffle.getRuntime().createCallTarget(rootNode); } + @Override + public void setContext(CodeBuilderContext context) { + this.context = context; + } + + @Override + public CodeBuilderContext getContext() { + return context; + } + @Override public RSyntaxNode constant(SourceSection source, Object value) { if (value instanceof String && !RRuntime.isNA((String) value)) { diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/binary/BinaryArithmeticSpecial.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/binary/BinaryArithmeticSpecial.java index 5324ee862791188fe3812ab83b1b57ff2ff1ec9e..d65a911c9c9c4623a8c69d4098f968626840fc02 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/binary/BinaryArithmeticSpecial.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/binary/BinaryArithmeticSpecial.java @@ -58,10 +58,11 @@ public abstract class BinaryArithmeticSpecial extends RNode { final boolean handleNA = !(opFactory == BinaryArithmetic.POW || opFactory == BinaryArithmetic.MOD); boolean handleIntegers = !(opFactory == BinaryArithmetic.POW || opFactory == BinaryArithmetic.DIV); if (handleIntegers) { - return (signature, arguments) -> signature.getNonNullCount() == 0 && arguments.length == 2 + return (signature, arguments, inReplacement) -> signature.getNonNullCount() == 0 && arguments.length == 2 ? IntegerBinaryArithmeticSpecialNodeGen.create(opFactory.createOperation(), handleNA, arguments) : null; } else { - return (signature, arguments) -> signature.getNonNullCount() == 0 && arguments.length == 2 ? BinaryArithmeticSpecialNodeGen.create(opFactory.createOperation(), handleNA, arguments) + return (signature, arguments, inReplacement) -> signature.getNonNullCount() == 0 && arguments.length == 2 + ? BinaryArithmeticSpecialNodeGen.create(opFactory.createOperation(), handleNA, arguments) : null; } } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/binary/BinaryBooleanSpecial.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/binary/BinaryBooleanSpecial.java index 5aeba584433b2a661fa11aadff1a7725351359c8..1a93af53656d1dc6c0d57ab6e5dd3892d7492a18 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/binary/BinaryBooleanSpecial.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/binary/BinaryBooleanSpecial.java @@ -48,7 +48,7 @@ public abstract class BinaryBooleanSpecial extends RNode { } public static RSpecialFactory createSpecialFactory(final BooleanOperationFactory opFactory) { - return (signature, arguments) -> signature.getNonNullCount() == 0 && arguments.length == 2 ? BinaryBooleanSpecialNodeGen.create(opFactory.createOperation(), arguments) : null; + return (signature, arguments, inReplacement) -> signature.getNonNullCount() == 0 && arguments.length == 2 ? BinaryBooleanSpecialNodeGen.create(opFactory.createOperation(), arguments) : null; } @Specialization diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RInternalCodeBuiltinNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RInternalCodeBuiltinNode.java index 6099c04dfecf655d59ff6d3a77b9e66864e4ba08..78914ee9313f30da5b6375abe5aac790555c4695 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RInternalCodeBuiltinNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/builtin/RInternalCodeBuiltinNode.java @@ -45,7 +45,7 @@ public final class RInternalCodeBuiltinNode extends RExternalBuiltinNode { private final Source code; private final String functionName; - @Child private CallMatcherNode call = CallMatcherNode.create(false, true); + @Child private CallMatcherNode call = CallMatcherNode.create(true); @CompilationFinal private RFunction function; public RInternalCodeBuiltinNode(RContext context, String basePackage, Source code, String functionName) { diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ReplacementDispatchNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ReplacementDispatchNode.java new file mode 100644 index 0000000000000000000000000000000000000000..24a8116b92f459a8bbdb39b0b06d458b28795f4e --- /dev/null +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ReplacementDispatchNode.java @@ -0,0 +1,240 @@ +/* + * Copyright (c) 2016, 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.nodes.control; + +import java.util.ArrayList; +import java.util.List; + +import com.oracle.truffle.api.CompilerAsserts; +import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.source.SourceSection; +import com.oracle.truffle.r.nodes.RASTUtils; +import com.oracle.truffle.r.nodes.access.RemoveAndAnswerNode; +import com.oracle.truffle.r.nodes.access.WriteVariableNode; +import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode; +import com.oracle.truffle.r.nodes.function.visibility.SetVisibilityNode; +import com.oracle.truffle.r.runtime.ArgumentsSignature; +import com.oracle.truffle.r.runtime.RError; +import com.oracle.truffle.r.runtime.data.RLanguage; +import com.oracle.truffle.r.runtime.data.RNull; +import com.oracle.truffle.r.runtime.nodes.RSourceSectionNode; +import com.oracle.truffle.r.runtime.nodes.RSyntaxCall; +import com.oracle.truffle.r.runtime.nodes.RSyntaxConstant; +import com.oracle.truffle.r.runtime.nodes.RSyntaxElement; +import com.oracle.truffle.r.runtime.nodes.RSyntaxLookup; +import com.oracle.truffle.r.runtime.nodes.RSyntaxNode; + +/** + * Represents a replacement consisting of execution of the RHS, call to the actual replacement + * sequence and removal of RHS returning the RHS value to the caller. The actual replacement is + * created lazily. Moreover, we use 'special' fast-path version of replacement where possible with + * fallback to generic implementation. + */ +public final class ReplacementDispatchNode extends RSourceSectionNode implements RSyntaxNode, RSyntaxCall { + + @Child private ReplacementNode replacementNode; + + @Child private WriteVariableNode storeRhs; + @Child private RemoveAndAnswerNode removeRhs; + @Child private SetVisibilityNode visibility = SetVisibilityNode.create(); + + private final String operator; + private final RSyntaxNode lhs; + private final boolean isSuper; + private final String rhsName; + private final int tempNamesStartIndex; + + public ReplacementDispatchNode(SourceSection src, String operator, RSyntaxNode lhs, RSyntaxNode rhs, boolean isSuper, int tempNamesStartIndex) { + super(src); + assert "<-".equals(operator) || "<<-".equals(operator) || "=".equals(operator); + assert lhs != null && rhs != null; + rhsName = "*rhs*" + tempNamesStartIndex; + storeRhs = WriteVariableNode.createAnonymous(rhsName, rhs.asRNode(), WriteVariableNode.Mode.INVISIBLE); + removeRhs = RemoveAndAnswerNode.create(rhsName); + this.operator = operator; + this.lhs = lhs; + this.isSuper = isSuper; + this.tempNamesStartIndex = tempNamesStartIndex; + } + + @Override + public Object execute(VirtualFrame frame) { + storeRhs.execute(frame); + getReplacementNode().execute(frame); + Object result = removeRhs.execute(frame); + visibility.execute(frame, false); + return result; + } + + /** + * Support for syntax tree visitor. + */ + public RSyntaxNode getLhs() { + return lhs; + } + + /** + * Support for syntax tree visitor. + */ + public RSyntaxNode getRhs() { + return storeRhs.getRhs().asRSyntaxNode(); + } + + @Override + public RSyntaxElement getSyntaxLHS() { + return RSyntaxLookup.createDummyLookup(null, operator, true); + } + + @Override + public ArgumentsSignature getSyntaxSignature() { + return ArgumentsSignature.empty(2); + } + + @Override + public RSyntaxElement[] getSyntaxArguments() { + return new RSyntaxElement[]{lhs, storeRhs.getRhs().asRSyntaxNode()}; + } + + private ReplacementNode getReplacementNode() { + if (replacementNode == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + replacementNode = insert(createReplacementNode()); + } + return replacementNode; + } + + private ReplacementNode createReplacementNode() { + CompilerAsserts.neverPartOfCompilation(); + + /* + * Collect all the function calls in this replacement. For "a(b(x)) <- z", this would be + * "a(...)" and "b(...)". + */ + List<RSyntaxCall> calls = new ArrayList<>(); + RSyntaxElement current = lhs; + while (!(current instanceof RSyntaxLookup)) { + if (!(current instanceof RSyntaxCall)) { + if (current instanceof RSyntaxConstant && ((RSyntaxConstant) current).getValue() == RNull.instance) { + throw RError.error(this, RError.Message.INVALID_NULL_LHS); + } else { + throw RError.error(this, RError.Message.NON_LANG_ASSIGNMENT_TARGET); + } + } + RSyntaxCall call = (RSyntaxCall) current; + calls.add(call); + + RSyntaxElement syntaxLHS = call.getSyntaxLHS(); + if (call.getSyntaxArguments().length == 0 || !(syntaxLHS instanceof RSyntaxLookup || isNamespaceLookupCall(syntaxLHS))) { + throw RError.error(this, RError.Message.INVALID_NULL_LHS); + } + current = call.getSyntaxArguments()[0]; + } + RSyntaxLookup variable = (RSyntaxLookup) current; + ReadVariableNode varRead = createReplacementForVariableUsing(variable, isSuper); + return ReplacementNodeGen.create(getSourceSection(), calls, rhsName, variable.getIdentifier(), isSuper, tempNamesStartIndex, varRead); + } + + private static ReadVariableNode createReplacementForVariableUsing(RSyntaxLookup var, boolean isSuper) { + if (isSuper) { + return ReadVariableNode.createSuperLookup(var.getSourceSection(), var.getIdentifier()); + } else { + return ReadVariableNode.create(var.getSourceSection(), var.getIdentifier(), true); + } + } + + /* + * Determines if syntax call is of the form foo::bar + */ + private static boolean isNamespaceLookupCall(RSyntaxElement e) { + if (e instanceof RSyntaxCall) { + RSyntaxCall call = (RSyntaxCall) e; + // check for syntax nodes as this will be required to recreate a call during + // replacement form construction in createFunctionUpdate + if (call.getSyntaxLHS() instanceof RSyntaxLookup && call.getSyntaxLHS() instanceof RSyntaxNode) { + if (((RSyntaxLookup) call.getSyntaxLHS()).getIdentifier().equals("::")) { + RSyntaxElement[] args = call.getSyntaxArguments(); + if (args.length == 2 && args[0] instanceof RSyntaxLookup && args[0] instanceof RSyntaxNode && args[1] instanceof RSyntaxLookup && args[1] instanceof RSyntaxNode) { + return true; + } + } + } + } + return false; + } + + /* + * Encapsulates check for the specific structure of replacements, to display the replacement + * instead of the "internal" form (with *tmp*, etc.) of the update call. + */ + public static RLanguage getRLanguage(RLanguage language) { + RSyntaxNode sn = (RSyntaxNode) language.getRep(); + Node parent = RASTUtils.unwrapParent(sn.asNode()); + if (parent instanceof WriteVariableNode) { + WriteVariableNode wvn = (WriteVariableNode) parent; + return ReplacementNode.getLanguage(wvn); + } + return null; + } + + /** + * Used by the parser for assignments that miss a left hand side. This node will raise an error + * once executed. + */ + public static final class LHSError extends RSourceSectionNode implements RSyntaxNode, RSyntaxCall { + + private final String operator; + private final RSyntaxElement lhs; + private final RSyntaxElement rhs; + + public LHSError(SourceSection sourceSection, String operator, RSyntaxElement lhs, RSyntaxElement rhs) { + super(sourceSection); + this.operator = operator; + this.lhs = lhs; + this.rhs = rhs; + } + + @Override + public Object execute(VirtualFrame frame) { + CompilerDirectives.transferToInterpreter(); + throw RError.error(this, RError.Message.INVALID_LHS, "do_set"); + } + + @Override + public RSyntaxElement getSyntaxLHS() { + return RSyntaxLookup.createDummyLookup(null, operator, true); + } + + @Override + public RSyntaxElement[] getSyntaxArguments() { + return new RSyntaxElement[]{lhs, rhs}; + } + + @Override + public ArgumentsSignature getSyntaxSignature() { + return ArgumentsSignature.empty(2); + } + } +} diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ReplacementNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ReplacementNode.java index 9c83ce14cdf3f5fdcd8cccf933df560c8b30490c..d4c2e4381e467e8aa18fb638aa443c1a178f957d 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ReplacementNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/control/ReplacementNode.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 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 @@ -20,153 +20,300 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + package com.oracle.truffle.r.nodes.control; +import java.util.ArrayList; import java.util.List; -import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.CompilerAsserts; +import com.oracle.truffle.api.dsl.Cached; +import com.oracle.truffle.api.dsl.NodeChild; +import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.dsl.TypeSystemReference; 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.source.SourceSection; +import com.oracle.truffle.r.nodes.EmptyTypeSystemFlatLayout; import com.oracle.truffle.r.nodes.access.RemoveAndAnswerNode; import com.oracle.truffle.r.nodes.access.WriteVariableNode; -import com.oracle.truffle.r.nodes.function.visibility.SetVisibilityNode; +import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode; +import com.oracle.truffle.r.nodes.function.RCallSpecialNode; +import com.oracle.truffle.r.nodes.function.RCallSpecialNode.RecursiveSpecialBailout; +import com.oracle.truffle.r.nodes.unary.GetNonSharedNodeGen; import com.oracle.truffle.r.runtime.ArgumentsSignature; -import com.oracle.truffle.r.runtime.RError; +import com.oracle.truffle.r.runtime.builtins.RSpecialFactory.FullCallNeededException; +import com.oracle.truffle.r.runtime.context.RContext; +import com.oracle.truffle.r.runtime.data.RDataFactory; +import com.oracle.truffle.r.runtime.data.RLanguage; +import com.oracle.truffle.r.runtime.nodes.RCodeBuilder.CodeBuilderContext; import com.oracle.truffle.r.runtime.nodes.RNode; -import com.oracle.truffle.r.runtime.nodes.RSourceSectionNode; import com.oracle.truffle.r.runtime.nodes.RSyntaxCall; -import com.oracle.truffle.r.runtime.nodes.RSyntaxConstant; import com.oracle.truffle.r.runtime.nodes.RSyntaxElement; import com.oracle.truffle.r.runtime.nodes.RSyntaxLookup; import com.oracle.truffle.r.runtime.nodes.RSyntaxNode; -/** - * Holds the sequence of nodes created for R's replacement assignment. Allows custom deparse and - * debug handling. - * - */ -public final class ReplacementNode extends RSourceSectionNode implements RSyntaxNode, RSyntaxCall { +@NodeChild(value = "target") +@TypeSystemReference(EmptyTypeSystemFlatLayout.class) +abstract class ReplacementNode extends RNode { + + private final int tempNamesStartIndex; + private final SourceSection source; + private final List<RSyntaxCall> calls; + private final String targetVarName; + private final String rhsVarName; + private final boolean isSuper; + + ReplacementNode(SourceSection source, List<RSyntaxCall> calls, String rhsVarName, String targetVarName, boolean isSuper, int tempNamesStartIndex) { + this.source = source; + this.calls = calls; + this.rhsVarName = rhsVarName; + this.targetVarName = targetVarName; + this.isSuper = isSuper; + this.tempNamesStartIndex = tempNamesStartIndex; + } + + @Specialization + protected Object doRObject(VirtualFrame frame, Object target, + @Cached("createTargetTmpWrite()") WriteVariableNode targetTmpWrite, + @Cached("createTargetTmpRemove()") RemoveAndAnswerNode targetTmpRemove, + @Cached("createTargetWrite()") WriteVariableNode targetWrite, + @Cached("createReplacementNode()") ReplacementBase replacement) { + targetTmpWrite.execute(frame, target); + replacement.execute(frame); + targetWrite.execute(frame, targetTmpRemove.execute(frame)); + return null; + } + + protected final WriteVariableNode createTargetTmpWrite() { + return WriteVariableNode.createAnonymous(getTargetTmpName(), null, WriteVariableNode.Mode.INVISIBLE); + } + + protected final WriteVariableNode createTargetWrite() { + return WriteVariableNode.createAnonymous(targetVarName, null, WriteVariableNode.Mode.INVISIBLE, isSuper); + } + + protected final RemoveAndAnswerNode createTargetTmpRemove() { + return RemoveAndAnswerNode.create(getTargetTmpName()); + } + + protected final ReplacementBase createReplacementNode() { + return createReplacementNode(true); + } + + private RNode createReplacementNodeWithoutSpecials() { + return createReplacementNode(false); + } + + private ReplacementBase createReplacementNode(boolean useSpecials) { + CompilerAsserts.neverPartOfCompilation(); + // Note: if specials are turned off in FastR, onlySpecials will never be true + boolean createSpecial = hasOnlySpecialCalls() && useSpecials; + return createSpecial ? createSpecialReplacement(source, calls) : createGenericReplacement(source, calls); + } + + private String getTargetTmpName() { + return "*tmp*" + tempNamesStartIndex; + } + + private boolean hasOnlySpecialCalls() { + for (int i = 0; i < calls.size(); i++) { + if (!(calls.get(i) instanceof RCallSpecialNode)) { + return false; + } + } + return true; + } /** - * This is just the left hand side of the assignment and only used when looking at the original - * structure of this replacement. + * Creates a replacement that consists only of {@link RCallSpecialNode} calls. */ - private final RSyntaxNode syntaxLhs; - private final String operator; + private SpecialReplacementNode createSpecialReplacement(SourceSection source, List<RSyntaxCall> calls) { + CodeBuilderContext codeBuilderContext = new CodeBuilderContext(tempNamesStartIndex + 2); + RNode extractFunc = ReadVariableNode.create(getTargetTmpName()); + for (int i = calls.size() - 1; i >= 1; i--) { + extractFunc = createSpecialFunctionQuery(extractFunc.asRSyntaxNode(), calls.get(i), codeBuilderContext); + } + RNode updateFunc = createFunctionUpdate(source, extractFunc.asRSyntaxNode(), ReadVariableNode.create(rhsVarName), calls.get(0), codeBuilderContext); + assert updateFunc instanceof RCallSpecialNode : "should be only specials"; + return new SpecialReplacementNode((RCallSpecialNode) updateFunc); + } /** - * The original right hand side in the source can be found by {@code storeRhs.getRhs()}. + * When there are more than two function calls in LHS, then we save some function calls by + * saving the intermediate results into temporary variables and reusing them. */ - @Child private WriteVariableNode storeRhs; - @Child private WriteVariableNode storeValue; - @Children private final RNode[] updates; - @Child private RemoveAndAnswerNode removeTemp; - @Child private RemoveAndAnswerNode removeRhs; - @Child private SetVisibilityNode visibility = SetVisibilityNode.create(); - - public ReplacementNode(SourceSection src, String operator, RSyntaxNode syntaxLhs, RSyntaxNode rhs, String rhsSymbol, RNode v, String tmpSymbol, List<RNode> updates) { - super(src); - assert "<-".equals(operator) || "<<-".equals(operator) || "=".equals(operator); - this.operator = operator; - this.syntaxLhs = syntaxLhs; - this.storeRhs = WriteVariableNode.createAnonymous(rhsSymbol, rhs.asRNode(), WriteVariableNode.Mode.INVISIBLE); - this.storeValue = WriteVariableNode.createAnonymous(tmpSymbol, v, WriteVariableNode.Mode.INVISIBLE); - this.updates = updates.toArray(new RNode[updates.size()]); - // remove var and rhs, returning rhs' value - this.removeTemp = RemoveAndAnswerNode.create(tmpSymbol); - this.removeRhs = RemoveAndAnswerNode.create(rhsSymbol); + private GenericReplacementNode createGenericReplacement(SourceSection source, List<RSyntaxCall> calls) { + List<RNode> instructions = new ArrayList<>(); + CodeBuilderContext codeBuilderContext = new CodeBuilderContext(tempNamesStartIndex + calls.size() + 1); + + /* + * Create the calls that extract inner components - only needed for complex replacements + * like "a(b(x)) <- z" (where we would extract "b(x)"). The extracted values are saved into + * temporary variables *tmp*{index} indexed from tempNamesIndex to (tempNamesIndex + + * calls.size()-1), the first such temporary variable holds the "target" of the replacement, + * 'x' in our example (the assignment from 'x' is not done in this loop). + */ + for (int i = calls.size() - 1, tmpIndex = 0; i >= 1; i--, tmpIndex++) { + ReadVariableNode newLhs = ReadVariableNode.create("*tmp*" + (tempNamesStartIndex + tmpIndex)); + RNode update = createSpecialFunctionQuery(newLhs, calls.get(i), codeBuilderContext); + instructions.add(WriteVariableNode.createAnonymous("*tmp*" + (tempNamesStartIndex + tmpIndex + 1), update, WriteVariableNode.Mode.INVISIBLE)); + } + /* + * Create the update calls, for "a(b(x)) <- z", this would be `a<-` and `b<-`, the + * intermediate results are stored to temporary variables *tmpr*{index}. + */ + for (int i = 0; i < calls.size(); i++) { + int tmpIndex = tempNamesStartIndex + calls.size() - i - 1; + String tmprName = i == 0 ? rhsVarName : "*tmpr*" + (tempNamesStartIndex + i - 1); + RNode update = createFunctionUpdate(source, ReadVariableNode.create("*tmp*" + tmpIndex), ReadVariableNode.create(tmprName), calls.get(i), codeBuilderContext); + if (i < calls.size() - 1) { + instructions.add(WriteVariableNode.createAnonymous("*tmpr*" + (tempNamesStartIndex + i), update, WriteVariableNode.Mode.INVISIBLE)); + } else { + instructions.add(WriteVariableNode.createAnonymous(getTargetTmpName(), update, WriteVariableNode.Mode.REGULAR)); + } + } + + GenericReplacementNode newReplacementNode = new GenericReplacementNode(instructions); + return newReplacementNode; } /** - * Support for syntax tree visitor. + * Creates a call that looks like {@code fun} but has the first argument replaced with + * {@code newLhs}. */ - public RSyntaxNode getLhs() { - return syntaxLhs; + private static RNode createSpecialFunctionQuery(RSyntaxNode newLhs, RSyntaxCall fun, CodeBuilderContext codeBuilderContext) { + RSyntaxElement[] arguments = fun.getSyntaxArguments(); + + RSyntaxNode[] argNodes = new RSyntaxNode[arguments.length]; + for (int i = 0; i < arguments.length; i++) { + argNodes[i] = i == 0 ? newLhs : process(arguments[i], codeBuilderContext); + } + + return RCallSpecialNode.createCallInReplace(fun.getSourceSection(), process(fun.getSyntaxLHS(), codeBuilderContext).asRNode(), fun.getSyntaxSignature(), argNodes).asRNode(); } /** - * Support for syntax tree visitor. + * Creates a call that looks like {@code fun}, but has its first argument replaced with + * {@code newLhs}, its target turned into an update function ("foo<-"), with the given value + * added to the arguments list. */ - public RSyntaxNode getRhs() { - return storeRhs.getRhs().asRSyntaxNode(); - } + private static RNode createFunctionUpdate(SourceSection source, RSyntaxNode newLhs, RSyntaxNode rhs, RSyntaxCall fun, CodeBuilderContext codeBuilderContext) { + RSyntaxElement[] arguments = fun.getSyntaxArguments(); - @Override - @ExplodeLoop - public Object execute(VirtualFrame frame) { - storeRhs.execute(frame); - storeValue.execute(frame); - for (RNode update : updates) { - update.execute(frame); + ArgumentsSignature signature = fun.getSyntaxSignature(); + RSyntaxNode[] argNodes = new RSyntaxNode[arguments.length + 1]; + String[] names = new String[argNodes.length]; + for (int i = 0; i < arguments.length; i++) { + names[i] = signature.getName(i); + argNodes[i] = i == 0 ? newLhs : process(arguments[i], codeBuilderContext); } - removeTemp.execute(frame); - try { - return removeRhs.execute(frame); - } finally { - visibility.execute(frame, false); + argNodes[argNodes.length - 1] = rhs; + names[argNodes.length - 1] = "value"; + + RSyntaxElement syntaxLHS = fun.getSyntaxLHS(); + RSyntaxNode newSyntaxLHS; + if (syntaxLHS instanceof RSyntaxLookup) { + RSyntaxLookup lookupLHS = (RSyntaxLookup) syntaxLHS; + String symbol = lookupLHS.getIdentifier(); + if ("slot".equals(symbol) || "@".equals(symbol)) { + // this is pretty gross, but at this point seems like the only way to get setClass + // to work properly + argNodes[0] = GetNonSharedNodeGen.create(argNodes[0].asRNode()); + } + newSyntaxLHS = lookup(lookupLHS.getSourceSection(), symbol + "<-", true); + } else { + // data types (and lengths) are verified in isNamespaceLookupCall + RSyntaxCall callLHS = (RSyntaxCall) syntaxLHS; + RSyntaxElement[] oldArgs = callLHS.getSyntaxArguments(); + RSyntaxNode[] newArgs = new RSyntaxNode[2]; + newArgs[0] = (RSyntaxNode) oldArgs[0]; + newArgs[1] = lookup(oldArgs[1].getSourceSection(), ((RSyntaxLookup) oldArgs[1]).getIdentifier() + "<-", true); + newSyntaxLHS = RCallSpecialNode.createCall(callLHS.getSourceSection(), ((RSyntaxNode) callLHS.getSyntaxLHS()).asRNode(), callLHS.getSyntaxSignature(), newArgs); } + return RCallSpecialNode.createCall(source, newSyntaxLHS.asRNode(), ArgumentsSignature.get(names), argNodes).asRNode(); } - @Override - public RSyntaxElement getSyntaxLHS() { - return RSyntaxLookup.createDummyLookup(null, operator, true); + private static RSyntaxNode process(RSyntaxElement original, CodeBuilderContext codeBuilderContext) { + return RContext.getASTBuilder().process(original, codeBuilderContext); } - @Override - public RSyntaxElement[] getSyntaxArguments() { - return new RSyntaxElement[]{syntaxLhs, storeRhs.getRhs().asRSyntaxNode()}; + private static RSyntaxNode lookup(SourceSection source, String symbol, boolean functionLookup) { + return RContext.getASTBuilder().lookup(source, symbol, functionLookup); + } + + static RLanguage getLanguage(WriteVariableNode wvn) { + Node parent = wvn.getParent(); + if (parent instanceof ReplacementBase) { + Node replacementBlock = ((ReplacementBase) parent).getReplacementNodeParent().getParent(); + assert replacementBlock instanceof ReplacementDispatchNode; + return RDataFactory.createLanguage((RNode) replacementBlock); + } + return null; } - @Override - public ArgumentsSignature getSyntaxSignature() { - return ArgumentsSignature.empty(2); + /** + * Base class for nodes implementing the actual replacement. + */ + protected abstract static class ReplacementBase extends RNode { + protected final ReplacementNode getReplacementNodeParent() { + // Note: new DSL puts another node in between ReplacementBase instance and + // ReplacementNode, to be flexible we traverse the parents until we reach it + Node current = this; + do { + current = current.getParent(); + } while (!(current instanceof ReplacementNode)); + return (ReplacementNode) current; + } } /** - * Used by the parser for assignments that miss a left hand side. This node will raise an error - * once executed. + * Replacement that is made of only special calls, if one of the special calls falls back to + * full version, the replacement also falls back to {@link ReplacementNode}. */ - public static final class LHSError extends RSourceSectionNode implements RSyntaxNode, RSyntaxCall { - - private final String operator; - private final RSyntaxElement lhs; - private final RSyntaxElement rhs; - private final boolean nullError; - - public LHSError(SourceSection sourceSection, String operator, RSyntaxElement lhs, RSyntaxElement rhs, boolean nullError) { - super(sourceSection); - this.operator = operator; - this.lhs = lhs; - this.rhs = rhs; - this.nullError = nullError; + private static final class SpecialReplacementNode extends ReplacementBase { + + @Child private RCallSpecialNode replaceCall; + + SpecialReplacementNode(RCallSpecialNode replaceCall) { + this.replaceCall = replaceCall; + this.replaceCall.setPropagateFullCallNeededException(true); } @Override public Object execute(VirtualFrame frame) { - CompilerDirectives.transferToInterpreter(); - if (nullError) { - throw RError.error(this, RError.Message.INVALID_NULL_LHS); - } else if (lhs instanceof RSyntaxConstant) { - throw RError.error(this, RError.Message.INVALID_LHS, "do_set"); - } else { - throw RError.error(this, RError.Message.NON_LANG_ASSIGNMENT_TARGET); + try { + // Note: the very last call is the actual assignment, e.g. [[<-, if this call's + // argument is shared, it bails out. Moreover, if that call's argument is not + // shared, it could not be extracted from a shared container (list), so we should be + // OK with not calling any other update function and just update the value directly. + replaceCall.execute(frame); + } catch (FullCallNeededException | RecursiveSpecialBailout e) { + RNode newReplacement = getReplacementNodeParent().createReplacementNodeWithoutSpecials(); + return replace(newReplacement).execute(frame); } + return null; } + } - @Override - public RSyntaxElement getSyntaxLHS() { - return RSyntaxLookup.createDummyLookup(null, operator, true); - } + /** + * Holds the sequence of nodes created for R's replacement assignment. + */ + private static final class GenericReplacementNode extends ReplacementBase { + @Children private final RNode[] updates; - @Override - public RSyntaxElement[] getSyntaxArguments() { - return new RSyntaxElement[]{lhs, rhs}; + GenericReplacementNode(List<RNode> updates) { + this.updates = updates.toArray(new RNode[updates.size()]); } @Override - public ArgumentsSignature getSyntaxSignature() { - return ArgumentsSignature.empty(2); + @ExplodeLoop + public Object execute(VirtualFrame frame) { + for (RNode update : updates) { + update.execute(frame); + } + return null; } } } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ArgumentMatcher.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ArgumentMatcher.java index a56caa191cadd1e6ac3cde0064ab64bd3d226668..177b16a70499c1366fcb1fef5c3640acbb72a669 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ArgumentMatcher.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/ArgumentMatcher.java @@ -42,13 +42,13 @@ import com.oracle.truffle.r.runtime.Arguments; import com.oracle.truffle.r.runtime.ArgumentsSignature; import com.oracle.truffle.r.runtime.RArguments; import com.oracle.truffle.r.runtime.RArguments.S3DefaultArguments; -import com.oracle.truffle.r.runtime.builtins.FastPathFactory; -import com.oracle.truffle.r.runtime.builtins.RBuiltinDescriptor; -import com.oracle.truffle.r.runtime.builtins.RBuiltinKind; 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.Utils; +import com.oracle.truffle.r.runtime.builtins.FastPathFactory; +import com.oracle.truffle.r.runtime.builtins.RBuiltinDescriptor; +import com.oracle.truffle.r.runtime.builtins.RBuiltinKind; import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames; import com.oracle.truffle.r.runtime.data.REmpty; import com.oracle.truffle.r.runtime.data.RFunction; @@ -67,7 +67,7 @@ import com.oracle.truffle.r.runtime.nodes.RNode; * {@link #matchArguments(RRootNode, CallArgumentsNode, ArgumentsSignature, S3DefaultArguments, RBaseNode, boolean)} * . The other match functions are used for special cases, where builtins make it necessary to * re-match parameters, e.g.: - * {@link #matchArgumentsEvaluated(RRootNode, RArgsValuesAndNames, S3DefaultArguments, boolean, RBaseNode)} + * {@link #matchArgumentsEvaluated(RRootNode, RArgsValuesAndNames, S3DefaultArguments, RBaseNode)} * for 'UseMethod'. * </p> * @@ -157,9 +157,9 @@ public class ArgumentMatcher { return ArgumentMatcher.matchNodes(target, argNodes, signature, s3DefaultArguments, callingNode, arguments, noOpt); } - public static MatchPermutation matchArguments(ArgumentsSignature supplied, ArgumentsSignature formal, RBaseNode callingNode, boolean forNextMethod, RBuiltinDescriptor builtin) { + public static MatchPermutation matchArguments(ArgumentsSignature supplied, ArgumentsSignature formal, RBaseNode callingNode, RBuiltinDescriptor builtin) { CompilerAsserts.neverPartOfCompilation(); - return permuteArguments(supplied, formal, callingNode, forNextMethod, index -> false, index -> supplied.getName(index) == null ? "" : supplied.getName(index), builtin); + return permuteArguments(supplied, formal, callingNode, index -> false, index -> supplied.getName(index) == null ? "" : supplied.getName(index), builtin); } public static ArgumentsSignature getFunctionSignature(RFunction function) { @@ -219,15 +219,13 @@ public class ArgumentMatcher { * taken from the stack) * @param s3DefaultArguments default values carried over from S3 group dispatch method (e.g. * from max to Summary.factor). {@code null} if there are no such arguments. - * @param forNextMethod matching when evaluating NextMethod * @param callingNode The {@link Node} invoking the match * @return A Fresh {@link RArgsValuesAndNames} containing the arguments rearranged and stuffed * with default values (in the form of {@link RPromise}s where needed) */ - public static RArgsValuesAndNames matchArgumentsEvaluated(RRootNode target, RArgsValuesAndNames evaluatedArgs, S3DefaultArguments s3DefaultArguments, boolean forNextMethod, - RBaseNode callingNode) { + public static RArgsValuesAndNames matchArgumentsEvaluated(RRootNode target, RArgsValuesAndNames evaluatedArgs, S3DefaultArguments s3DefaultArguments, RBaseNode callingNode) { FormalArguments formals = target.getFormalArguments(); - MatchPermutation match = permuteArguments(evaluatedArgs.getSignature(), formals.getSignature(), callingNode, forNextMethod, index -> { + MatchPermutation match = permuteArguments(evaluatedArgs.getSignature(), formals.getSignature(), callingNode, index -> { throw RInternalError.unimplemented("S3Dispatch should not have arg length mismatch"); }, index -> evaluatedArgs.getSignature().getName(index), null); @@ -310,7 +308,7 @@ public class ArgumentMatcher { FormalArguments formals = target.getFormalArguments(); // Rearrange arguments - MatchPermutation match = permuteArguments(suppliedSignature, formals.getSignature(), callingNode, false, + MatchPermutation match = permuteArguments(suppliedSignature, formals.getSignature(), callingNode, index -> RASTUtils.isLookup(suppliedArgs[index], ArgumentsSignature.VARARG_NAME), index -> getErrorForArgument(suppliedArgs, suppliedSignature, index), target.getBuiltin()); @@ -491,12 +489,11 @@ public class ArgumentMatcher { * @param signature The signature (==names) of the supplied arguments * @param formalSignature The signature (==names) of the formal arguments * @param callingNode The {@link Node} invoking the match - * @param forNextMethod matching when evaluating NextMethod * @param builtin builtin function descriptor (or null if not a builtin) * @return An array of type <T> with the supplied arguments in the correct order */ @TruffleBoundary - private static MatchPermutation permuteArguments(ArgumentsSignature signature, ArgumentsSignature formalSignature, RBaseNode callingNode, boolean forNextMethod, IntPredicate isVarSuppliedVarargs, + private static MatchPermutation permuteArguments(ArgumentsSignature signature, ArgumentsSignature formalSignature, RBaseNode callingNode, IntPredicate isVarSuppliedVarargs, IntFunction<String> errorString, RBuiltinDescriptor builtin) { // assert Arrays.stream(suppliedNames).allMatch(name -> name == null || !name.isEmpty()); @@ -517,8 +514,7 @@ public class ArgumentMatcher { } // Search for argument name inside formal arguments - int formalIndex = findParameterPosition(formalSignature, signature.getName(suppliedIndex), resultPermutation, suppliedIndex, hasVarArgs, callingNode, varArgIndex, forNextMethod, - errorString, builtin); + int formalIndex = findParameterPosition(formalSignature, signature.getName(suppliedIndex), resultPermutation, suppliedIndex, hasVarArgs, callingNode, varArgIndex, errorString, builtin); if (formalIndex != MatchPermutation.UNMATCHED) { resultPermutation[formalIndex] = suppliedIndex; resultSignature[formalIndex] = signature.getName(suppliedIndex); @@ -541,10 +537,6 @@ public class ArgumentMatcher { break outer; } if (!matchedSuppliedArgs[suppliedIndex]) { - if (forNextMethod) { - // for NextMethod, unused parameters are matched even when named - break; - } if (signature.getName(suppliedIndex) == null || signature.getName(suppliedIndex).isEmpty()) { // unnamed parameter, match by position break; @@ -638,7 +630,7 @@ public class ArgumentMatcher { * argument has been matched before */ private static <T> int findParameterPosition(ArgumentsSignature formalsSignature, String suppliedName, int[] resultPermutation, int suppliedIndex, boolean hasVarArgs, RBaseNode callingNode, - int varArgIndex, boolean forNextMethod, IntFunction<String> errorString, RBuiltinDescriptor builtin) { + int varArgIndex, IntFunction<String> errorString, RBuiltinDescriptor builtin) { int found = MatchPermutation.UNMATCHED; for (int i = 0; i < formalsSignature.getLength(); i++) { String formalName = formalsSignature.getName(i); @@ -672,7 +664,7 @@ public class ArgumentMatcher { } } } - if (found >= 0 || hasVarArgs || forNextMethod) { + if (found >= 0 || hasVarArgs) { return found; } throw RError.error(callingNode, RError.Message.UNUSED_ARGUMENT, errorString.apply(suppliedIndex)); diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/CallMatcherNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/CallMatcherNode.java index 6e4b72a729c466500034a71502520b95483f5c58..9454d1b46b985707c8c836d1c714a9b1b86936e3 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/CallMatcherNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/CallMatcherNode.java @@ -27,10 +27,10 @@ import com.oracle.truffle.r.nodes.function.ArgumentMatcher.MatchPermutation; import com.oracle.truffle.r.nodes.function.call.CallRFunctionCachedNode; import com.oracle.truffle.r.nodes.function.call.CallRFunctionCachedNodeGen; import com.oracle.truffle.r.nodes.function.call.CallRFunctionNode; +import com.oracle.truffle.r.nodes.function.signature.VarArgsHelper; import com.oracle.truffle.r.nodes.function.visibility.SetVisibilityNode; import com.oracle.truffle.r.nodes.unary.CastNode; import com.oracle.truffle.r.runtime.ArgumentsSignature; -import com.oracle.truffle.r.runtime.ArgumentsSignature.VarArgsInfo; import com.oracle.truffle.r.runtime.RArguments; import com.oracle.truffle.r.runtime.RArguments.DispatchArgs; import com.oracle.truffle.r.runtime.RCaller; @@ -46,7 +46,6 @@ import com.oracle.truffle.r.runtime.nodes.RSyntaxNode; public abstract class CallMatcherNode extends RBaseNode { - protected final boolean forNextMethod; protected final boolean argsAreEvaluated; @Child private PromiseHelperNode promiseHelper; @@ -54,15 +53,14 @@ public abstract class CallMatcherNode extends RBaseNode { protected final ConditionProfile missingArgProfile = ConditionProfile.createBinaryProfile(); protected final ConditionProfile emptyArgProfile = ConditionProfile.createBinaryProfile(); - private CallMatcherNode(boolean forNextMethod, boolean argsAreEvaluated) { - this.forNextMethod = forNextMethod; + private CallMatcherNode(boolean argsAreEvaluated) { this.argsAreEvaluated = argsAreEvaluated; } private static final int MAX_CACHE_DEPTH = 3; - public static CallMatcherNode create(boolean forNextMethod, boolean argsAreEvaluated) { - return new CallMatcherUninitializedNode(forNextMethod, argsAreEvaluated); + public static CallMatcherNode create(boolean argsAreEvaluated) { + return new CallMatcherUninitializedNode(argsAreEvaluated); } public abstract Object execute(VirtualFrame frame, ArgumentsSignature suppliedSignature, Object[] suppliedArguments, RFunction function, String functionName, DispatchArgs dispatchArgs); @@ -85,15 +83,15 @@ public abstract class CallMatcherNode extends RBaseNode { int argCount = suppliedArguments.length; // extract vararg signatures from the arguments - VarArgsInfo varArgsInfo = suppliedSignature.getVarArgsInfo(suppliedArguments); + VarArgsHelper varArgsInfo = VarArgsHelper.create(suppliedSignature, suppliedArguments); int argListSize = varArgsInfo.getArgListSize(); // see flattenIndexes for the interpretation of the values long[] preparePermutation; ArgumentsSignature resultSignature; if (varArgsInfo.hasVarArgs()) { - resultSignature = suppliedSignature.flattenNames(varArgsInfo); - preparePermutation = suppliedSignature.flattenIndexes(varArgsInfo); + resultSignature = varArgsInfo.flattenNames(suppliedSignature); + preparePermutation = varArgsInfo.flattenIndexes(suppliedSignature); } else { preparePermutation = new long[argListSize]; String[] newSuppliedSignature = new String[argListSize]; @@ -110,9 +108,9 @@ public abstract class CallMatcherNode extends RBaseNode { assert resultSignature != null; ArgumentsSignature formalSignature = ArgumentMatcher.getFunctionSignature(function); - MatchPermutation permutation = ArgumentMatcher.matchArguments(resultSignature, formalSignature, this, forNextMethod, function.getRBuiltin()); + MatchPermutation permutation = ArgumentMatcher.matchArguments(resultSignature, formalSignature, this, function.getRBuiltin()); - return new CallMatcherCachedNode(suppliedSignature, varArgsInfo.getVarArgsSignatures(), function, preparePermutation, permutation, forNextMethod, argsAreEvaluated, next); + return new CallMatcherCachedNode(suppliedSignature, varArgsInfo.getVarArgsSignatures(), function, preparePermutation, permutation, argsAreEvaluated, next); } protected final void evaluatePromises(VirtualFrame frame, RFunction function, Object[] args, int varArgIndex) { @@ -153,8 +151,8 @@ public abstract class CallMatcherNode extends RBaseNode { @NodeInfo(cost = NodeCost.UNINITIALIZED) private static final class CallMatcherUninitializedNode extends CallMatcherNode { - CallMatcherUninitializedNode(boolean forNextMethod, boolean argsAreEvaluated) { - super(forNextMethod, argsAreEvaluated); + CallMatcherUninitializedNode(boolean argsAreEvaluated) { + super(argsAreEvaluated); } private int depth; @@ -163,7 +161,7 @@ public abstract class CallMatcherNode extends RBaseNode { public Object execute(VirtualFrame frame, ArgumentsSignature suppliedSignature, Object[] suppliedArguments, RFunction function, String functionName, DispatchArgs dispatchArgs) { CompilerDirectives.transferToInterpreterAndInvalidate(); if (++depth > MAX_CACHE_DEPTH) { - return replace(new CallMatcherGenericNode(forNextMethod, argsAreEvaluated)).execute(frame, suppliedSignature, suppliedArguments, function, functionName, dispatchArgs); + return replace(new CallMatcherGenericNode(argsAreEvaluated)).execute(frame, suppliedSignature, suppliedArguments, function, functionName, dispatchArgs); } else { CallMatcherCachedNode cachedNode = replace(specialize(suppliedSignature, suppliedArguments, function, this)); // for splitting if necessary @@ -193,7 +191,7 @@ public abstract class CallMatcherNode extends RBaseNode { private final ArgumentsSignature[] cachedVarArgSignatures; private final RFunction cachedFunction; /** - * {@link ArgumentsSignature#flattenNames(VarArgsInfo)} for the interpretation of the + * {@link VarArgsHelper#flattenNames(ArgumentsSignature)} for the interpretation of the * values. */ @CompilationFinal private final long[] preparePermutation; @@ -201,8 +199,8 @@ public abstract class CallMatcherNode extends RBaseNode { private final FormalArguments formals; CallMatcherCachedNode(ArgumentsSignature suppliedSignature, ArgumentsSignature[] varArgSignatures, RFunction function, long[] preparePermutation, MatchPermutation permutation, - boolean forNextMethod, boolean argsAreEvaluated, CallMatcherNode next) { - super(forNextMethod, argsAreEvaluated); + boolean argsAreEvaluated, CallMatcherNode next) { + super(argsAreEvaluated); this.cachedSuppliedSignature = suppliedSignature; this.cachedVarArgSignatures = varArgSignatures; this.cachedFunction = function; @@ -300,9 +298,9 @@ public abstract class CallMatcherNode extends RBaseNode { Object[] values = new Object[preparePermutation.length]; for (int i = 0; i < values.length; i++) { long source = preparePermutation[i]; - if (ArgumentsSignature.isVarArgsIndex(source)) { - int varArgsIdx = ArgumentsSignature.extractVarArgsIndex(source); - int argsIdx = ArgumentsSignature.extractVarArgsArgumentIndex(source); + if (VarArgsHelper.isVarArgsIndex(source)) { + int varArgsIdx = VarArgsHelper.extractVarArgsIndex(source); + int argsIdx = VarArgsHelper.extractVarArgsArgumentIndex(source); RArgsValuesAndNames varargs = (RArgsValuesAndNames) arguments[varArgsIdx]; values[i] = varargs.getArguments()[argsIdx]; } else { @@ -315,8 +313,8 @@ public abstract class CallMatcherNode extends RBaseNode { public static final class CallMatcherGenericNode extends CallMatcherNode { - CallMatcherGenericNode(boolean forNextMethod, boolean argsAreEvaluated) { - super(forNextMethod, argsAreEvaluated); + CallMatcherGenericNode(boolean argsAreEvaluated) { + super(argsAreEvaluated); } @Child private CallRFunctionCachedNode call = CallRFunctionCachedNodeGen.create(0); @@ -324,7 +322,7 @@ public abstract class CallMatcherNode extends RBaseNode { @Override public Object execute(VirtualFrame frame, ArgumentsSignature suppliedSignature, Object[] suppliedArguments, RFunction function, String functionName, DispatchArgs dispatchArgs) { - RArgsValuesAndNames reorderedArgs = reorderArguments(suppliedArguments, function, suppliedSignature, forNextMethod, this); + RArgsValuesAndNames reorderedArgs = reorderArguments(suppliedArguments, function, suppliedSignature, this); evaluatePromises(frame, function, reorderedArgs.getArguments(), reorderedArgs.getSignature().getVarArgIndex()); RCaller parent = RArguments.getCall(frame).getParent(); @@ -351,7 +349,7 @@ public abstract class CallMatcherNode extends RBaseNode { } @TruffleBoundary - public static RArgsValuesAndNames reorderArguments(Object[] args, RFunction function, ArgumentsSignature paramSignature, boolean forNextMethod, RBaseNode callingNode) { + public static RArgsValuesAndNames reorderArguments(Object[] args, RFunction function, ArgumentsSignature paramSignature, RBaseNode callingNode) { assert paramSignature.getLength() == args.length; int argCount = args.length; @@ -412,7 +410,7 @@ public abstract class CallMatcherNode extends RBaseNode { RArgsValuesAndNames evaledArgs = new RArgsValuesAndNames(argValues, signature); // ...to match them against the chosen function's formal arguments - RArgsValuesAndNames evaluated = ArgumentMatcher.matchArgumentsEvaluated((RRootNode) function.getRootNode(), evaledArgs, null, forNextMethod, callingNode); + RArgsValuesAndNames evaluated = ArgumentMatcher.matchArgumentsEvaluated((RRootNode) function.getRootNode(), evaledArgs, null, callingNode); return evaluated; } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallSpecialNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallSpecialNode.java index 521cc42bc3a68ad0891f1b68c74e0dfc8f8f2a5e..ebdf2442aca958996f449690d8aefef563ca74f2 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallSpecialNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallSpecialNode.java @@ -37,6 +37,7 @@ import com.oracle.truffle.r.runtime.RDeparse; import com.oracle.truffle.r.runtime.RInternalError; import com.oracle.truffle.r.runtime.builtins.RBuiltinDescriptor; import com.oracle.truffle.r.runtime.builtins.RSpecialFactory; +import com.oracle.truffle.r.runtime.builtins.RSpecialFactory.FullCallNeededException; import com.oracle.truffle.r.runtime.context.RContext; import com.oracle.truffle.r.runtime.data.RFunction; import com.oracle.truffle.r.runtime.data.RPromise; @@ -121,6 +122,7 @@ public final class RCallSpecialNode extends RCallBaseNode implements RSyntaxNode * index into the parent arguments array, otherwise {@link #NO_RECURSIVE_ARGUMENT_INDEX}. */ private int argumentIndex = NO_RECURSIVE_ARGUMENT_INDEX; + private boolean propagateFullCallNeededException; private RCallSpecialNode(SourceSection sourceSection, RNode functionNode, RFunction expectedFunction, RSyntaxNode[] arguments, ArgumentsSignature signature, RNode special) { this.sourceSectionR = sourceSection; @@ -131,10 +133,18 @@ public final class RCallSpecialNode extends RCallBaseNode implements RSyntaxNode this.signature = signature; } + public static RSyntaxNode createCallInReplace(SourceSection sourceSection, RNode functionNode, ArgumentsSignature signature, RSyntaxNode[] arguments) { + return createCall(sourceSection, functionNode, signature, arguments, true); + } + public static RSyntaxNode createCall(SourceSection sourceSection, RNode functionNode, ArgumentsSignature signature, RSyntaxNode[] arguments) { + return createCall(sourceSection, functionNode, signature, arguments, false); + } + + private static RSyntaxNode createCall(SourceSection sourceSection, RNode functionNode, ArgumentsSignature signature, RSyntaxNode[] arguments, boolean inReplace) { RCallSpecialNode special = null; if (useSpecials) { - special = tryCreate(sourceSection, functionNode, signature, arguments); + special = tryCreate(sourceSection, functionNode, signature, arguments, inReplace); } if (special != null) { if (sourceSection == RSyntaxNode.EAGER_DEPARSE) { @@ -146,7 +156,7 @@ public final class RCallSpecialNode extends RCallBaseNode implements RSyntaxNode } } - private static RCallSpecialNode tryCreate(SourceSection sourceSection, RNode functionNode, ArgumentsSignature signature, RSyntaxNode[] arguments) { + private static RCallSpecialNode tryCreate(SourceSection sourceSection, RNode functionNode, ArgumentsSignature signature, RSyntaxNode[] arguments, boolean inReplace) { RSyntaxNode syntaxFunction = functionNode.asRSyntaxNode(); if (!(syntaxFunction instanceof RSyntaxLookup)) { // LHS is not a simple lookup -> bail out @@ -181,7 +191,7 @@ public final class RCallSpecialNode extends RCallBaseNode implements RSyntaxNode localArguments[i] = arguments[i].asRNode(); } } - RNode special = specialCall.create(signature, localArguments); + RNode special = specialCall.create(signature, localArguments, inReplace); if (special == null) { // the factory refused to create a special call -> bail out return null; @@ -236,7 +246,7 @@ public final class RCallSpecialNode extends RCallBaseNode implements RSyntaxNode // Note: we need to check the parent's parent, because it might have been rewritten by // bailout of some of its other arguments. If parent is special node, then its parent must // be RCallSpecialNode - return argumentIndex != NO_RECURSIVE_ARGUMENT_INDEX && getParent() != null && getParent().getParent() instanceof RCallSpecialNode; + return propagateFullCallNeededException || (argumentIndex != NO_RECURSIVE_ARGUMENT_INDEX && getParent() != null && getParent().getParent() instanceof RCallSpecialNode); } private RCallNode getRCallNode(RSyntaxNode[] newArguments) { @@ -251,6 +261,14 @@ public final class RCallSpecialNode extends RCallBaseNode implements RSyntaxNode argumentIndex = index; } + /** + * If set to {@code true} the special call will raise {@link FullCallNeededException} even when + * the parent is a special call. + */ + public void setPropagateFullCallNeededException(boolean flag) { + propagateFullCallNeededException = flag; + } + @Override public Object execute(VirtualFrame frame) { return execute(frame, functionNode.execute(frame)); @@ -273,7 +291,7 @@ public final class RCallSpecialNode extends RCallBaseNode implements RSyntaxNode } @SuppressWarnings("serial") - private static final class RecursiveSpecialBailout extends RuntimeException { + public static final class RecursiveSpecialBailout extends RuntimeException { public final int argumentIndex; RecursiveSpecialBailout(int argumentIndex) { diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallerHelper.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallerHelper.java index af7f0639df7fd2f1dc27484dbcd0017a644bce79..d16b817a367f6b68725a7aefcb16ba29ef59aa49 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallerHelper.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/RCallerHelper.java @@ -27,6 +27,7 @@ import java.util.function.Supplier; import com.oracle.truffle.r.nodes.RASTUtils; import com.oracle.truffle.r.nodes.access.ConstantNode; import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode; +import com.oracle.truffle.r.nodes.function.signature.VarArgsHelper; import com.oracle.truffle.r.runtime.ArgumentsSignature; import com.oracle.truffle.r.runtime.RCaller; import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames; @@ -133,12 +134,12 @@ public final class RCallerHelper { String[] names = new String[preparePermutation.length]; for (int i = 0; i < values.length; i++) { long source = preparePermutation[i]; - if (!ArgumentsSignature.isVarArgsIndex(source)) { + if (!VarArgsHelper.isVarArgsIndex(source)) { values[i] = suppliedArguments[(int) source]; names[i] = suppliedSignature.getName((int) source); } else { - int varArgsIdx = ArgumentsSignature.extractVarArgsIndex(source); - int argsIdx = ArgumentsSignature.extractVarArgsArgumentIndex(source); + int varArgsIdx = VarArgsHelper.extractVarArgsIndex(source); + int argsIdx = VarArgsHelper.extractVarArgsArgumentIndex(source); RArgsValuesAndNames varargs = (RArgsValuesAndNames) suppliedArguments[varArgsIdx]; values[i] = varargs.getArguments()[argsIdx]; names[i] = varargs.getSignature().getName(argsIdx); diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/UseMethodInternalNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/UseMethodInternalNode.java index 284b6b0c0825045733416846ed73b1ab1641f761..739931976e6def1f5933a27c1c92806339e413c4 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/UseMethodInternalNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/UseMethodInternalNode.java @@ -24,7 +24,7 @@ public final class UseMethodInternalNode extends RNode { @Child private ClassHierarchyNode classHierarchyNode = ClassHierarchyNodeGen.create(true, true); @Child private S3FunctionLookupNode lookup = S3FunctionLookupNode.create(false, false); - @Child private CallMatcherNode callMatcher = CallMatcherNode.create(false, false); + @Child private CallMatcherNode callMatcher = CallMatcherNode.create(false); @Child private PreProcessArgumentsNode argPreProcess; private final String generic; diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/PrepareArguments.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/PrepareArguments.java index ee7c56dace3c2fb6fc13e9ff516535ec2344587d..60218f9968d1610f1a04a6f303e88eb16bbd1e53 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/PrepareArguments.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/call/PrepareArguments.java @@ -183,7 +183,7 @@ public abstract class PrepareArguments extends Node { CachedExplicitPrepareArguments(PrepareArguments next, RRootNode target, RCallNode call, ArgumentsSignature explicitArgSignature) { this.next = next; formals = target.getFormalArguments(); - permutation = ArgumentMatcher.matchArguments(explicitArgSignature, formals.getSignature(), call, false, target.getBuiltin()); + permutation = ArgumentMatcher.matchArguments(explicitArgSignature, formals.getSignature(), call, target.getBuiltin()); cachedExplicitArgSignature = explicitArgSignature; } @@ -208,7 +208,7 @@ public abstract class PrepareArguments extends Node { public RArgsValuesAndNames execute(VirtualFrame frame, RArgsValuesAndNames explicitArgs, S3DefaultArguments s3DefaultArguments, RCallNode call) { CompilerDirectives.transferToInterpreter(); // Function and arguments may change every call: Flatt'n'Match on SlowPath! :-/ - return ArgumentMatcher.matchArgumentsEvaluated(target, explicitArgs, s3DefaultArguments, false, RError.ROOTNODE); + return ArgumentMatcher.matchArgumentsEvaluated(target, explicitArgs, s3DefaultArguments, RError.ROOTNODE); } } } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/signature/CombineSignaturesNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/signature/CombineSignaturesNode.java index 2c604132b4fb7bd50d6fc309aec5410761cd6a58..b360a6e5aa9cee0c359b52901ed7dae3ab61e7c9 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/signature/CombineSignaturesNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/signature/CombineSignaturesNode.java @@ -31,7 +31,6 @@ import com.oracle.truffle.api.dsl.TypeSystemReference; import com.oracle.truffle.api.profiles.ConditionProfile; import com.oracle.truffle.r.nodes.EmptyTypeSystemFlatLayout; import com.oracle.truffle.r.runtime.ArgumentsSignature; -import com.oracle.truffle.r.runtime.ArgumentsSignature.VarArgsInfo; import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames; import com.oracle.truffle.r.runtime.nodes.RBaseNode; @@ -76,7 +75,7 @@ public abstract class CombineSignaturesNode extends RBaseNode { @Cached("createBinaryProfile()") ConditionProfile noVarArgsProfile) { Object[] flatLeftValues = leftValues; if (noVarArgsProfile.profile(resultCached.varArgsInfo.hasVarArgs())) { - flatLeftValues = left.flattenValues(leftValues, resultCached.varArgsInfo); + flatLeftValues = resultCached.varArgsInfo.flattenValues(left, leftValues); } return new RArgsValuesAndNames(resultCached.getValues(flatLeftValues, rightValues, shufflingProfile), resultCached.signature); } @@ -84,10 +83,10 @@ public abstract class CombineSignaturesNode extends RBaseNode { @TruffleBoundary protected CombineResult combine(ArgumentsSignature leftOriginal, Object[] leftValues, ArgumentsSignature right) { // flatten any varargs, note there should not be any in right - VarArgsInfo varArgsInfo = leftOriginal.getVarArgsInfo(leftValues); + VarArgsHelper varArgsInfo = VarArgsHelper.create(leftOriginal, leftValues); ArgumentsSignature left = leftOriginal; if (varArgsInfo.hasVarArgs()) { - left = leftOriginal.flattenNames(varArgsInfo); + left = varArgsInfo.flattenNames(leftOriginal); } // calculate the size of the resulting signature - some values in left are overridden by the @@ -157,9 +156,9 @@ public abstract class CombineSignaturesNode extends RBaseNode { */ private final int[] valuesIndexes; - private final VarArgsInfo varArgsInfo; + private final VarArgsHelper varArgsInfo; - CombineResult(ArgumentsSignature signature, int[] valuesIndexes, VarArgsInfo varArgsInfo) { + CombineResult(ArgumentsSignature signature, int[] valuesIndexes, VarArgsHelper varArgsInfo) { this.varArgsInfo = varArgsInfo; assert valuesIndexes == null || signature.getLength() == valuesIndexes.length; this.signature = signature; diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/signature/VarArgsHelper.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/signature/VarArgsHelper.java new file mode 100644 index 0000000000000000000000000000000000000000..b0b110efecb3470bd4b48d404f831aa15b605cfd --- /dev/null +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/function/signature/VarArgsHelper.java @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2016, 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.nodes.function.signature; + +import com.oracle.truffle.r.runtime.ArgumentsSignature; +import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames; + +/** + * Represents the information about varargs contained in {@link ArgumentsSignature} and its argument + * values and provides helper functions related to flattening the varargs. + */ +public final class VarArgsHelper { + + /** + * Array of the same size as the original signature with {@code null} in places where there are + * regular arguments and with {@link ArgumentsSignature} instances under the same indexes of + * their corresponding {@link RArgsValuesAndNames}. + */ + private final ArgumentsSignature[] varArgs; + + /** + * The total number of arguments including those in varargs, and excluding unmatched ones. + */ + private final int argListSize; + + private VarArgsHelper(ArgumentsSignature[] varArgs, int argListSize) { + this.varArgs = varArgs; + this.argListSize = argListSize; + } + + public static VarArgsHelper create(ArgumentsSignature signature, Object[] suppliedArguments) { + assert signature.getLength() == suppliedArguments.length; + ArgumentsSignature[] varArgs = null; + int argListSize = signature.getLength(); + for (int i = 0; i < suppliedArguments.length; i++) { + Object arg = suppliedArguments[i]; + if (arg instanceof RArgsValuesAndNames) { + if (varArgs == null) { + varArgs = new ArgumentsSignature[suppliedArguments.length]; + } + varArgs[i] = ((RArgsValuesAndNames) arg).getSignature(); + argListSize += ((RArgsValuesAndNames) arg).getLength() - 1; + } else if (signature.isUnmatched(i)) { + argListSize--; + } + } + return new VarArgsHelper(varArgs, argListSize); + } + + public boolean hasVarArgs() { + return varArgs != null; + } + + public ArgumentsSignature[] getVarArgsSignatures() { + return varArgs; + } + + public int getArgListSize() { + return argListSize; + } + + /** + * Returns an array where each index is either index into the variables array (positive number) + * or it is a packed representation of two indices: one into the variables array pointing to + * varargs instance and the other is index into this varargs' arguments array. Use static + * methods {@link #isVarArgsIndex(long)}, {@link #extractVarArgsArgumentIndex(long)} and + * {@link #extractVarArgsArgumentIndex(long)} to access the data packed in the {@code long} + * value. This method also removes arguments that are marked as 'unmatched' in the signature. + * + * Note: where {@link #hasVarArgs()} returns {@code false}, then the flattening may not be + * necessary. This optimization is left to the caller. + */ + public long[] flattenIndexes(ArgumentsSignature signature) { + long[] preparePermutation = new long[argListSize]; + int index = 0; + for (int i = 0; i < varArgs.length; i++) { + ArgumentsSignature varArgSignature = varArgs[i]; + if (varArgSignature != null) { + for (int j = 0; j < varArgSignature.getLength(); j++) { + preparePermutation[index++] = -((((long) i) << 32) + j) - 1; + } + } else if (!signature.isUnmatched(i)) { + preparePermutation[index++] = i; + } + } + return preparePermutation; + } + + /** @see #flattenIndexes(ArgumentsSignature) */ + public Object[] flattenValues(ArgumentsSignature signature, Object[] values) { + Object[] result = new Object[argListSize]; + int resultIdx = 0; + for (int valuesIdx = 0; valuesIdx < values.length; valuesIdx++) { + if (varArgs[valuesIdx] != null) { + assert values[valuesIdx] instanceof RArgsValuesAndNames; + assert ((RArgsValuesAndNames) values[valuesIdx]).getSignature() == varArgs[valuesIdx]; + RArgsValuesAndNames varArgs = (RArgsValuesAndNames) values[valuesIdx]; + for (int i = 0; i < varArgs.getLength(); i++) { + result[resultIdx++] = varArgs.getArgument(i); + } + } else if (!signature.isUnmatched(valuesIdx)) { + result[resultIdx++] = values[valuesIdx]; + } + } + return result; + } + + /** @see #flattenIndexes(ArgumentsSignature) */ + public ArgumentsSignature flattenNames(ArgumentsSignature signature) { + String[] argNames = new String[argListSize]; + int index = 0; + for (int i = 0; i < varArgs.length; i++) { + ArgumentsSignature varArgSignature = varArgs[i]; + if (varArgSignature != null) { + for (int j = 0; j < varArgSignature.getLength(); j++) { + argNames[index++] = varArgSignature.getName(j); + } + } else if (!signature.isUnmatched(i)) { + argNames[index++] = signature.getName(i); + } + } + return ArgumentsSignature.get(argNames); + } + + /* + * Utility functions for interpreting the result of flattenIndexes. + */ + + public static boolean isVarArgsIndex(long idx) { + return idx < 0; + } + + public static int extractVarArgsIndex(long idx) { + return (int) ((-idx - 1) >> 32); + } + + public static int extractVarArgsArgumentIndex(long idx) { + return (int) (-idx - 1); + } +} diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/ExecuteMethod.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/ExecuteMethod.java index a486be7101ff3eb27da17ed9a59ad0f3c320ef5c..4619df7873767cf57eb2f9b2b0a24c3512e4f14e 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/ExecuteMethod.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/objects/ExecuteMethod.java @@ -42,7 +42,7 @@ final class ExecuteMethod extends RBaseNode { if (collectArgs == null) { CompilerDirectives.transferToInterpreterAndInvalidate(); collectArgs = insert(CollectArgumentsNodeGen.create()); - callMatcher = insert(CallMatcherNode.create(false, false)); + callMatcher = insert(CallMatcherNode.create(false)); } FormalArguments formals = ((RRootNode) fdef.getRootNode()).getFormalArguments(); diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ArgumentsSignature.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ArgumentsSignature.java index 1edbe70707699fc90a97203aaa9cdf50f27b17d7..20acad69f07ff206c3be83053d1d928b41138016 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ArgumentsSignature.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ArgumentsSignature.java @@ -30,7 +30,6 @@ import com.oracle.truffle.api.CompilerAsserts; import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.CompilationFinal; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; -import com.oracle.truffle.r.runtime.data.RArgsValuesAndNames; public final class ArgumentsSignature implements Iterable<String> { @@ -193,136 +192,4 @@ public final class ArgumentsSignature implements Iterable<String> { public String toString() { return "Signature " + Arrays.toString(names); } - - /* - * Utility functions - */ - - public static boolean isVarArgsIndex(long idx) { - return idx < 0; - } - - public static int extractVarArgsIndex(long idx) { - return (int) ((-idx - 1) >> 32); - } - - public static int extractVarArgsArgumentIndex(long idx) { - return (int) (-idx - 1); - } - - /** - * Creates instance of {@link VarArgsInfo} for this signature and supplied arguments values. - * This object is required by other utility methods handling varargs. - */ - public VarArgsInfo getVarArgsInfo(Object[] suppliedArguments) { - ArgumentsSignature[] varArgs = null; - int argListSize = getLength(); - for (int i = 0; i < suppliedArguments.length; i++) { - Object arg = suppliedArguments[i]; - if (arg instanceof RArgsValuesAndNames) { - if (varArgs == null) { - varArgs = new ArgumentsSignature[suppliedArguments.length]; - } - varArgs[i] = ((RArgsValuesAndNames) arg).getSignature(); - argListSize += ((RArgsValuesAndNames) arg).getLength() - 1; - } else if (isUnmatched(i)) { - argListSize--; - } - } - return new VarArgsInfo(varArgs, argListSize); - } - - /** - * Returns an array where each index is either index into the variables array (positive number) - * or it is a packed representation of two indices: one into the variables array pointing to - * varargs instance and the other is index into this varargs' arguments array. Use static - * methods {@link #isVarArgsIndex(long)}, {@link #extractVarArgsArgumentIndex(long)} and - * {@link #extractVarArgsArgumentIndex(long)} to access the data packed in the {@code long} - * value. This method also removes arguments that are marked as 'unmatched' in the signature. - * - * Note: where {@link VarArgsInfo#hasVarArgs()} returns {@code false}, then the flattening may - * not be necessary. This optimization is left to the caller. - */ - public long[] flattenIndexes(VarArgsInfo varArgsInfo) { - long[] preparePermutation = new long[varArgsInfo.argListSize]; - int index = 0; - for (int i = 0; i < varArgsInfo.varArgs.length; i++) { - ArgumentsSignature varArgSignature = varArgsInfo.varArgs[i]; - if (varArgSignature != null) { - for (int j = 0; j < varArgSignature.getLength(); j++) { - preparePermutation[index++] = -((((long) i) << 32) + j) - 1; - } - } else if (!isUnmatched(i)) { - preparePermutation[index++] = i; - } - } - return preparePermutation; - } - - /** @see #flattenIndexes(VarArgsInfo varArgsInfo) */ - public Object[] flattenValues(Object[] values, VarArgsInfo varArgsInfo) { - Object[] result = new Object[varArgsInfo.argListSize]; - int resultIdx = 0; - for (int valuesIdx = 0; valuesIdx < values.length; valuesIdx++) { - if (varArgsInfo.varArgs[valuesIdx] != null) { - assert values[valuesIdx] instanceof RArgsValuesAndNames; - assert ((RArgsValuesAndNames) values[valuesIdx]).getSignature() == varArgsInfo.varArgs[valuesIdx]; - RArgsValuesAndNames varArgs = (RArgsValuesAndNames) values[valuesIdx]; - for (int i = 0; i < varArgs.getLength(); i++) { - result[resultIdx++] = varArgs.getArgument(i); - } - } else if (!isUnmatched(valuesIdx)) { - result[resultIdx++] = values[valuesIdx]; - } - } - return result; - } - - /** @see #flattenIndexes(VarArgsInfo varArgsInfo) */ - public ArgumentsSignature flattenNames(VarArgsInfo varArgsInfo) { - String[] argNames = new String[varArgsInfo.argListSize]; - int index = 0; - for (int i = 0; i < varArgsInfo.varArgs.length; i++) { - ArgumentsSignature varArgSignature = varArgsInfo.varArgs[i]; - if (varArgSignature != null) { - for (int j = 0; j < varArgSignature.getLength(); j++) { - argNames[index++] = varArgSignature.getName(j); - } - } else if (!isUnmatched(i)) { - argNames[index++] = getName(i); - } - } - return ArgumentsSignature.get(argNames); - } - - public static final class VarArgsInfo { - /** - * Array of the same size as the original signature with {@code null} in places where there - * are regular arguments and with {@link ArgumentsSignature} instances under the same - * indexes of their corresponding {@link RArgsValuesAndNames}. - */ - private final ArgumentsSignature[] varArgs; - - /** - * The total number of arguments including those in varargs, and excluding unmatched ones. - */ - private final int argListSize; - - private VarArgsInfo(ArgumentsSignature[] varArgs, int argListSize) { - this.varArgs = varArgs; - this.argListSize = argListSize; - } - - public boolean hasVarArgs() { - return varArgs != null; - } - - public ArgumentsSignature[] getVarArgsSignatures() { - return varArgs; - } - - public int getArgListSize() { - return argListSize; - } - } } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/DefaultResourceHandlerFactory.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/DefaultResourceHandlerFactory.java index 98185edaafddc48b1194ac3b0b60e38cbe8eb7ea..0f90524e98b782cbc2fed6760582fa4c96e4a0e7 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/DefaultResourceHandlerFactory.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/DefaultResourceHandlerFactory.java @@ -22,8 +22,19 @@ */ package com.oracle.truffle.r.runtime; +import java.io.BufferedReader; +import java.io.File; import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URI; import java.net.URL; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.CodeSource; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; import com.oracle.truffle.r.runtime.ResourceHandlerFactory.Handler; @@ -46,4 +57,59 @@ class DefaultResourceHandlerFactory extends ResourceHandlerFactory implements Ha protected Handler newHandler() { return this; } + + @Override + public String[] getRFiles(Class<?> accessor, String pkgName) { + CodeSource source = accessor.getProtectionDomain().getCodeSource(); + ArrayList<String> list = new ArrayList<>(); + try { + URL url = source.getLocation(); + Path sourcePath = Paths.get(url.toURI().getPath()); + File sourceFile = sourcePath.toFile(); + if (sourceFile.isDirectory()) { + InputStream is = accessor.getResourceAsStream(pkgName + "/R"); + if (is != null) { + try (BufferedReader r = new BufferedReader(new InputStreamReader(is))) { + String line; + while ((line = r.readLine()) != null) { + if (line.endsWith(".r") || line.endsWith(".R")) { + final String rResource = pkgName + "/R/" + line.trim(); + list.add(Utils.getResourceAsString(accessor, rResource, true)); + } + } + } + } + } else { + JarFile fastrJar = new JarFile(sourceFile); + Enumeration<JarEntry> iter = fastrJar.entries(); + while (iter.hasMoreElements()) { + JarEntry entry = iter.nextElement(); + String name = entry.getName(); + if (name.endsWith(".R") || name.endsWith(".r")) { + Path p = Paths.get(name); + String entryPkg = p.getName(p.getNameCount() - 3).getFileName().toString(); + String entryParent = p.getName(p.getNameCount() - 2).getFileName().toString(); + if (entryParent.equals("R") && entryPkg.equals(pkgName)) { + int size = (int) entry.getSize(); + byte[] buf = new byte[size]; + InputStream is = fastrJar.getInputStream(entry); + int totalRead = 0; + int n; + while ((n = is.read(buf, totalRead, buf.length - totalRead)) > 0) { + totalRead += n; + } + list.add(new String(buf)); + } + } + } + } + String[] result = new String[list.size()]; + list.toArray(result); + return result; + } catch (Exception ex) { + Utils.rSuicide(ex.getMessage()); + return null; + } + } + } diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ResourceHandlerFactory.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ResourceHandlerFactory.java index 0fddff7ddb5511d204ab70cce66397c540d59db1..b20589fd8459804332cbe644af1209bdca750a7c 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ResourceHandlerFactory.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/ResourceHandlerFactory.java @@ -44,6 +44,12 @@ public abstract class ResourceHandlerFactory { * See {@link java.lang.Class#getResourceAsStream(String)}. */ InputStream getResourceAsStream(Class<?> accessor, String name); + + /** + * Return the contents of all "R" files (ending with ".r" or ".R") relative to + * {@code accessor} and {@code pkgname/R}. I.e. essentially a directory search. + */ + String[] getRFiles(Class<?> accessor, String pkgName); } static { diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/builtins/RSpecialFactory.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/builtins/RSpecialFactory.java index da9ce251356d3592525d6c44565c51bdc0f1b859..d078aab5d1c6f57154a261b42cea87132f4bfbc9 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/builtins/RSpecialFactory.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/builtins/RSpecialFactory.java @@ -41,8 +41,13 @@ public interface RSpecialFactory { /** * Returns a 'special node' if the given arguments with their signature can be handled by it. If * if returns {@code null}, the full blown built-in node will be created. + * + * @param inReplacement whether a non-replacement call is part of replacement sequence, but not + * whether the call itself is a replacement (i.e. with arrow '<-'). Example: f(g(x)) + * <- val, the call to g(x) will be constructed with {@code inReplacement == true}, + * but the call to `g<-(...)` won't. */ - RNode create(ArgumentsSignature argumentsSignature, RNode[] arguments); + RNode create(ArgumentsSignature argumentsSignature, RNode[] arguments, boolean inReplacement); @SuppressWarnings("serial") final class FullCallNeededException extends RuntimeException { diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RCodeBuilder.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RCodeBuilder.java index c04bf7676a094d05b55ce87eca8d32f5e11e6ee8..68a6831e22872a16a63fddd7ee51a3f1b8df4d63 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RCodeBuilder.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/nodes/RCodeBuilder.java @@ -48,6 +48,26 @@ public interface RCodeBuilder<T> { } } + final class CodeBuilderContext { + public static CodeBuilderContext DEFAULT = new CodeBuilderContext(0); + + private final int replacementVarsStartIndex; + + public CodeBuilderContext(int replacementVarsStartIndex) { + this.replacementVarsStartIndex = replacementVarsStartIndex; + } + + /** + * Used to initialize {@code ReplacementBlockNode}. When we are processing a replacement AST + * that is within another replacement, example {@code x[x[1]<-2]<-3}, we set this value so + * that newly created replacements within the original replacement have different temporary + * variable names. + */ + public int getReplacementVarsStartIndex() { + return replacementVarsStartIndex; + } + } + /** * Creates a function call argument. */ @@ -98,6 +118,10 @@ public interface RCodeBuilder<T> { */ RootCallTarget rootFunction(SourceSection source, List<Argument<T>> arguments, T body, String name); + void setContext(CodeBuilderContext context); + + CodeBuilderContext getContext(); + /** * This method returns a newly created AST fragment for the given original element. This * functionality can be used to quickly create new AST snippets for existing code. @@ -137,6 +161,15 @@ public interface RCodeBuilder<T> { }.accept(original); } + /** @see #process(RSyntaxElement) */ + default T process(RSyntaxElement original, CodeBuilderContext context) { + CodeBuilderContext saved = getContext(); + setContext(context); + T result = process(original); + setContext(saved); + return result; + } + /** * Helper function: create a call with no arguments. */ 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 92c540ced4e357138a097bae61e42c3771bf6656..872652954e2863f1c016e2ffffbaf8ebebd4cfa8 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 @@ -57620,6 +57620,335 @@ g.default args: NULL +##com.oracle.truffle.r.test.functions.TestS3Dispatch.runRSourceTests# +#{ source("mxbuild/com.oracle.truffle.r.test/bin/com/oracle/truffle/r/test/functions/S3/R/nextMethod.R") } +called foo.default with 42 +with '' as class +called foo.default with 42 +with classes baz and bar: +called foo.baz with 42 +called foo.bar with 42 + +##com.oracle.truffle.r.test.functions.TestS3Dispatch.runRSourceTests# +#{ source("mxbuild/com.oracle.truffle.r.test/bin/com/oracle/truffle/r/test/functions/S3/R/nextMethodAdditionalArgs1.R") } +called foo.baz with 42 +list() +called foo.bar with 42 +[[1]] +[1] 3 + + +##com.oracle.truffle.r.test.functions.TestS3Dispatch.runRSourceTests# +#{ source("mxbuild/com.oracle.truffle.r.test/bin/com/oracle/truffle/r/test/functions/S3/R/nextMethodAdditionalArgs2.R") } +foo.bar with: +$x +[1] 42 +attr(,"class") +[1] "baz" "bar" + +$a +[1] "caller-a" + +$b +[1] "caller-b" + +$c +[1] "explicit-from-baz" + +$cc +[1] "caller-d" + +$named +$named$e +[1] "caller-e" + +$named$f +[1] "named-from-baz" + + +foo.bar with: +$x +[1] 42 +attr(,"class") +[1] "bazz" "bar" + +$a +[1] "caller-a" + +$b +[1] "caller-b" + +$c +[1] "explicit-from-baz" + +$cc +[1] "caller-d" + +$named +$named$e +[1] "caller-e" + +$named$f +[1] "named-from-baz" + + +foo.bar with: +$x +[1] 42 +attr(,"class") +[1] "bazzz" "bar" + +$a +[1] "caller-a" + +$b +[1] "caller-b" + +$c +[1] "explicit-from-baz" + +$cc +[1] "caller-d" + +$named +$named$e +[1] "caller-e" + +$named[[2]] +[1] "matched-positionally?" + +$named$f +[1] "named-from-baz" + + + +##com.oracle.truffle.r.test.functions.TestS3Dispatch.runRSourceTests# +#{ source("mxbuild/com.oracle.truffle.r.test/bin/com/oracle/truffle/r/test/functions/S3/R/nextMethodAdditionalArgs3.R") } +foo.baz +foo.bar with: +evaluated b-from-caller +$x +[1] 42 +attr(,"class") +[1] "baz" "bar" + +$a +[1] "a-from-baz" + +$b +[1] "b-from-caller" + +$named +list() + +foo.bar with: +evaluated b-from-caller +$x +[1] 42 +attr(,"class") +[1] "bazz" "bar" + +$a +[1] "a-from-bazz" + +$b +[1] "b-from-caller" + +$named +list() + +foo.bar with: +$x +[1] 42 +attr(,"class") +[1] "bazzz" "bar" + +$a +[1] "a-from-bazz" + +$b +[1] "b-from-caller" + +$named +list() + + +##com.oracle.truffle.r.test.functions.TestS3Dispatch.runRSourceTests# +#{ source("mxbuild/com.oracle.truffle.r.test/bin/com/oracle/truffle/r/test/functions/S3/R/nextMethodAdditionalArgs4.R") } +foo.baz +foo.bar with: +$x +[1] 42 +attr(,"class") +[1] "baz" "bar" + +$a +[1] "positional-from-caller" + +$b +[1] "positional-explicit" + +$mynamed +[1] "named-explicit" + + +##com.oracle.truffle.r.test.functions.TestS3Dispatch.runRSourceTests#Ignored.Unknown# +#{ source("mxbuild/com.oracle.truffle.r.test/bin/com/oracle/truffle/r/test/functions/S3/R/nextMethodAdditionalArgsPromises1.R") } +foo.bar with: +$x +[1] 42 +attr(,"class") +[1] "baz" "bar" + +$a +[1] "caller-a" + +$b +[1] "caller-b" + +$c +[1] "explicit-from-baz" + +$cc +[1] "caller-d" + +$named +$named$e +[1] "caller-e" + +$named$f +[1] "named-from-baz" + + +foo.bar with: +$x +[1] 42 +attr(,"class") +[1] "bazz" "bar" + +$a +[1] "caller-a" + +$b +[1] "caller-b" + +$c +[1] "explicit-from-baz" + +$cc +[1] "caller-d" + +$named +$named$e +[1] "caller-e" + +$named$f +[1] "named-from-baz" + + +foo.bar with: +evaluated matched-positionally? +$x +[1] 42 +attr(,"class") +[1] "bazzz" "bar" + +$a +[1] "caller-a" + +$b +[1] "caller-b" + +$c +[1] "explicit-from-baz" + +$cc +[1] "caller-d" + +$named +$named$e +[1] "caller-e" + +$named[[2]] +[1] "matched-positionally?" + +$named$f +[1] "named-from-baz" + + + +##com.oracle.truffle.r.test.functions.TestS3Dispatch.runRSourceTests# +#{ source("mxbuild/com.oracle.truffle.r.test/bin/com/oracle/truffle/r/test/functions/S3/R/nextMethodArgsMatching.R") } +foo.bar with: +$x +[1] 42 +attr(,"class") +[1] "baz" "bar" + +$a +[1] "caller-a" + +$b +[1] "caller-b" + +$c +[1] "def-bar" + +$cc +[1] "caller-d" + +$named +$named$e +[1] "caller-e" + + +foo.bar with: +$x +[1] 42 +attr(,"class") +[1] "bazz" "bar" + +$a +[1] "caller-a" + +$b +[1] "caller-b" + +$c +[1] "def-bar" + +$cc +[1] "caller-d" + +$named +$named$e +[1] "caller-e" + + +foo.bar with: +$x +[1] 42 +attr(,"class") +[1] "bazzz" "bar" + +$a +[1] "caller-a" + +$b +[1] "caller-b" + +$c +[1] "def-bar" + +$cc +[1] "caller-d" + +$named +$named$e +[1] "caller-e" + + + ##com.oracle.truffle.r.test.functions.TestS3Dispatch.testComplexGroupDispatch# #{x<--7+2i;class(x)<-"foo";Complex.foo<-function(z){1;};Im(x);} [1] 1 @@ -65132,10 +65461,34 @@ $z [1] 1 4 8 +##com.oracle.truffle.r.test.library.base.TestSimpleLists.testListUpdate# +#{ l <- list(42); l[1][1] <- 7; l } +[[1]] +[1] 7 + + ##com.oracle.truffle.r.test.library.base.TestSimpleLists.testListUpdate# #{ l <- list(c(1,2,3),c(4,5,6)) ; l[[1]] <- c(7,8,9) ; l[[1]] } [1] 7 8 9 +##com.oracle.truffle.r.test.library.base.TestSimpleLists.testListUpdate# +#{ l <- list(c(42)); idx <- TRUE; l[idx] <- list(c(1,2,3)); l } +[[1]] +[1] 1 2 3 + + +##com.oracle.truffle.r.test.library.base.TestSimpleLists.testListUpdate# +#{ l <- list(c(42)); l[1][1] <- 7; l } +[[1]] +[1] 7 + + +##com.oracle.truffle.r.test.library.base.TestSimpleLists.testListUpdate# +#{ l <- list(c(42, 43)); l[[1]][1] <- 7; l } +[[1]] +[1] 7 43 + + ##com.oracle.truffle.r.test.library.base.TestSimpleLoop.testDynamic# #{ l <- quote({x <- 0 ; for(i in 1:10) { x <- x + i } ; x}) ; f <- function() { eval(l) } ; x <<- 10 ; f() } [1] 55 @@ -109924,6 +110277,26 @@ attr(,"is.truffle.object") #if (length(grep("FastR", R.Version()$version.string)) != 1) { TRUE } else { { x<-rep(1, 100); xi1<-.fastr.identity(x); f<-function(x) { y<-x; y }; f(x); x[1]<-7; xi2<-.fastr.identity(x); xi1 == xi2 } } [1] TRUE +##com.oracle.truffle.r.test.library.stats.TestExternal_covcor.testCovcor# +#.Call(stats:::C_cov, 1:5, 1:5, 4, FALSE) +[1] 2.5 + +##com.oracle.truffle.r.test.library.stats.TestExternal_covcor.testCovcor# +#.Call(stats:::C_cov, 1:5, c(1,5,1,5,10), 4, FALSE) +[1] 4.5 + +##com.oracle.truffle.r.test.library.stats.TestExternal_covcor.testCovcorArgsCasts# +#.Call(stats:::C_cov, 1:3, 1:5, 4, FALSE) +Error: incompatible dimensions + +##com.oracle.truffle.r.test.library.stats.TestExternal_covcor.testCovcorArgsCasts# +#.Call(stats:::C_cov, NULL, 1:5, 4, FALSE) +Error: 'x' is NULL + +##com.oracle.truffle.r.test.library.stats.TestExternal_covcor.testCovcorArgsCasts# +#.Call(stats:::C_cov, c('1','2','3','4','5'), 1:5, 4, FALSE) +[1] 2.5 + ##com.oracle.truffle.r.test.library.stats.TestFitting.testLm#Output.IgnoreWhitespace# #y <- c(26.55, 37.21, 57.28, 90.82, 20.16, 89.838, 94.46, 20.5, 17.6, 68.7, 38.41, 76.9, 49.7, 71, 99.19, 16); x <- c(26.55, 37.21, 57.28, 90.82, 20.16, 89.838, 94.46, 20.5, 17.6, 68.7, 38.41, 76.9, 49.7, 71, 99.19, 16); res <- lm(y~x); print(res$coefficients);print(res$fitted.values);print(res$xlevels);print(res$residuals);print(res$assign);print(res$effects);print(res$qr$qr);print(res$rank);print(res$model); (Intercept) x diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/TestRBase.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/TestRBase.java index c3e3ee014015f9dca26c83386d2265b0c686bd61..69a51571c41f298b060439b7061e463ee4d54dc4 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/TestRBase.java +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/TestRBase.java @@ -83,6 +83,8 @@ public class TestRBase extends TestBase { testTrait = Output.IgnoreErrorContext; } else if (l.contains("IgnoreWarningContext")) { testTrait = Output.IgnoreWarningContext; + } else if (l.contains("Ignored")) { + testTrait = Ignored.Unknown; } } } diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/functions/S3/R/argMatching.R b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/functions/S3/R/argMatching.R index 0d8b326a796201ba6725be2f369da52e4e786c6f..36069c8bce2974bcdd794df5f161d7aee1daba8a 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/functions/S3/R/argMatching.R +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/functions/S3/R/argMatching.R @@ -1,3 +1,24 @@ +# Copyright (c) 2013, 2016, 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. + g.default <- function(y,...) { cat('g.default args:\n'); print(list(if(missing(y)) NULL else y,...)); } g.c <- function(x,...) { cat('g.c args:\n'); print(list(if(missing(x)) NULL else x,...)); } g <- function(x,...) { cat('dispatch\n'); UseMethod('g') } diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/functions/S3/R/nextMethod.R b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/functions/S3/R/nextMethod.R new file mode 100644 index 0000000000000000000000000000000000000000..040d76962e143d55c3fab1641b42c40ada6c5dc0 --- /dev/null +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/functions/S3/R/nextMethod.R @@ -0,0 +1,36 @@ +# Copyright (c) 2013, 2016, 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. + +foo <- function(x) UseMethod("foo") +foo.default <- function(x) cat("called foo.default with ", x, "\n") +foo.bar <- function(x) cat("called foo.bar with ", x, "\n") +foo.baz <- function(x) { cat("called foo.baz with ", x, "\n"); NextMethod(unclass(x)); } + +val <- 42 +foo(val) + +cat("with '' as class\n") +class(val) <- c('') +foo(val) + +cat("with classes baz and bar:\n") +class(val) <- c('baz', 'bar') +foo(val) \ No newline at end of file diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/functions/S3/R/nextMethodAdditionalArgs1.R b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/functions/S3/R/nextMethodAdditionalArgs1.R new file mode 100644 index 0000000000000000000000000000000000000000..d5bc3014322c8ae183cbf5063b35c56085f3379b --- /dev/null +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/functions/S3/R/nextMethodAdditionalArgs1.R @@ -0,0 +1,32 @@ +# Copyright (c) 2013, 2016, 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. + +foo <- function(x, ...) UseMethod("foo") +foo.default <- function(x, ...) { cat("called foo.default with ", x, "\n"); print(list(...)); } +foo.bar <- function(x, ...) { cat("called foo.bar with ", x, "\n"); print(list(...)); } + +# in foo.baz NextMethod gets additional argument "3" that should be passed to foo.bar. +# However, the first argument: vector of characters, is ignored +foo.baz <- function(x, ...) { cat("called foo.baz with ", x, "\n"); print(list(...)); NextMethod(c('something', 'foo'), x, 3); } + +val <- 42 +class(val) <- c('baz', 'bar') +foo(val) \ No newline at end of file diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/functions/S3/R/nextMethodAdditionalArgs2.R b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/functions/S3/R/nextMethodAdditionalArgs2.R new file mode 100644 index 0000000000000000000000000000000000000000..21e0f894291d9d04e741bc14b50a080a62fc78ae --- /dev/null +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/functions/S3/R/nextMethodAdditionalArgs2.R @@ -0,0 +1,41 @@ +# Copyright (c) 2013, 2016, 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. + +# tests the scenario when NextMethod has some additional arguments. +# Note: it seems that those additional arguments are taken into account only +# if named, otherwise the value is ignored (see 'matched-positionally?') +# see also nextMethodArgsMatching.R + +withClass <- function(x, cls) { class(x) <- cls; x } + +foo <- function(x, ...) UseMethod("foo") +foo.bar <- function(x, a, b, cc, c='def-bar', ...) { + cat("foo.bar with: \n"); + print(list(x=x, a=a, b=b, c=c, cc=cc, named=list(...))) +} + +foo.baz <- function(x, d, a=1, c='def-baz', cc='def-cc-baz', ...) NextMethod(x, 'matched-positionally?', c='explicit-from-baz', f='named-from-baz') +foo.bazz <- function(x, d, a=1, c='def-baz', ...) NextMethod(c='explicit-from-baz', f='named-from-baz') +foo.bazzz <- function(x, d, a=1, c='def-baz', ...) NextMethod('foo', x, 'matched-positionally?', c='explicit-from-baz', f='named-from-baz') + +foo(withClass(42, c('baz', 'bar')), 'caller-d', a='caller-a', b='caller-b', e='caller-e') +foo(withClass(42, c('bazz', 'bar')), 'caller-d', a='caller-a', b='caller-b', e='caller-e') +foo(withClass(42, c('bazzz', 'bar')), 'caller-d', a='caller-a', b='caller-b', e='caller-e') diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/functions/S3/R/nextMethodAdditionalArgs3.R b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/functions/S3/R/nextMethodAdditionalArgs3.R new file mode 100644 index 0000000000000000000000000000000000000000..0b2523a798dca0b1d41334494aff07b5d929fd74 --- /dev/null +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/functions/S3/R/nextMethodAdditionalArgs3.R @@ -0,0 +1,40 @@ +# Copyright (c) 2013, 2016, 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. + +# tests the execution of promises when NextMethod has some additional arguments +# that may override the original arguments. See also nextMethodArgsMatching.R + +withClass <- function(x, cls) { class(x) <- cls; x } +side <- function(x) { cat("evaluated ", x, "\n"); x } + +foo <- function(x, ...) UseMethod("foo") +foo.bar <- function(x, a, b, ...) { + cat("foo.bar with: \n"); + print(list(x=x, a=a, b=b, named=list(...))) +} + +foo.baz <- function(x, ...) { cat("foo.baz\n"); NextMethod(x, a='a-from-baz') } +foo.bazz <- function(x, a, b, ...) NextMethod(x, a='a-from-bazz') +foo.bazzz <- function(x, a='a-default', b, ...) NextMethod(x, a='a-from-bazz') + +foo(withClass(42, c('baz', 'bar')), a=side('a-from-caller'), b=side('b-from-caller')) +foo(withClass(42, c('bazz', 'bar')), a=side('a-from-caller'), b=side('b-from-caller')) +foo(withClass(42, c('bazzz', 'bar')), b='b-from-caller') diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/functions/S3/R/nextMethodAdditionalArgs4.R b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/functions/S3/R/nextMethodAdditionalArgs4.R new file mode 100644 index 0000000000000000000000000000000000000000..da15897fab5c29fa350ddbb876c6b94edfe44876 --- /dev/null +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/functions/S3/R/nextMethodAdditionalArgs4.R @@ -0,0 +1,34 @@ +# Copyright (c) 2013, 2016, 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. + +# Tests the scenario when NextMethod has some additional arguments. +# Tests the overriding of arguments passed to S3 dispatch method (foo) by explicit arguments given to NextMethod. + +withClass <- function(x, cls) { class(x) <- cls; x } + +foo <- function(x, ...) UseMethod("foo") +foo.bar <- function(x, a, b, mynamed) { + cat("foo.bar with: \n"); + print(list(x=x, a=a, b=b, mynamed=mynamed)) +} +foo.baz <- function(x, ...) { cat("foo.baz\n"); NextMethod('foo', x, 'positional-explicit', mynamed='named-explicit') } + +foo(withClass(42, c('baz', 'bar')), 'positional-from-caller', mynamed='named-from-caller') diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/functions/S3/R/nextMethodAdditionalArgsPromises1.R b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/functions/S3/R/nextMethodAdditionalArgsPromises1.R new file mode 100644 index 0000000000000000000000000000000000000000..adb4873d768596701b92afc80042515629442ba1 --- /dev/null +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/functions/S3/R/nextMethodAdditionalArgsPromises1.R @@ -0,0 +1,47 @@ +# Ignored +# Note: once this works, we can remove nextMethodAdditionalArgs2.R, +# which is the same test without promises evaluation check + +# Copyright (c) 2013, 2016, 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. + +# tests the scenario when NextMethod has some additional arguments. +# Note: it seems that those additional arguments are taken into account only +# if named, otherwise the value is ignored (see 'matched-positionally?') +# see also nextMethodArgsMatching.R + +withClass <- function(x, cls) { class(x) <- cls; x } + +side <- function(x) { cat("evaluated ", x, "\n"); x } + +foo <- function(x, ...) UseMethod("foo") +foo.bar <- function(x, a, b, cc, c='def-bar', ...) { + cat("foo.bar with: \n"); + print(list(x=x, a=a, b=b, c=c, cc=cc, named=list(...))) +} + +foo.baz <- function(x, d, a=1, c='def-baz', cc='def-cc-baz', ...) NextMethod(x, side('matched-positionally?'), c='explicit-from-baz', f='named-from-baz') +foo.bazz <- function(x, d, a=1, c='def-baz', ...) NextMethod(c='explicit-from-baz', f='named-from-baz') +foo.bazzz <- function(x, d, a=1, c='def-baz', ...) NextMethod('foo', x, side('matched-positionally?'), c='explicit-from-baz', f='named-from-baz') + +foo(withClass(42, c('baz', 'bar')), 'caller-d', a='caller-a', b='caller-b', e='caller-e') +foo(withClass(42, c('bazz', 'bar')), 'caller-d', a='caller-a', b='caller-b', e='caller-e') +foo(withClass(42, c('bazzz', 'bar')), 'caller-d', a='caller-a', b='caller-b', e='caller-e') diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/functions/S3/R/nextMethodArgsMatching.R b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/functions/S3/R/nextMethodArgsMatching.R new file mode 100644 index 0000000000000000000000000000000000000000..bb2b5db25f938199f5c44f2fafa009a5dac09dff --- /dev/null +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/functions/S3/R/nextMethodArgsMatching.R @@ -0,0 +1,47 @@ +# Copyright (c) 2013, 2016, 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. + +# tests that the signature of the call of the S3 dispatch function is matched to +# the signature of the target of NextMethod. Note that default value of 'c' in +# foo.baz is ignored and 'cc' in foo.bar is matched positionally with 'caller-d'. + +withClass <- function(x, cls) { class(x) <- cls; x } + +# a - matched by name param +# b - unmatched named param, but matched named in foo.bar +# c - default param (no value given), default (no value given) in foo.bar +# cc - default param (no value given), not default default in foo.bar +# d - matched positional param +# e - unmatched named param, stays unmatched named in foo.bar + +foo <- function(x, ...) UseMethod("foo") +foo.bar <- function(x, a, b, cc, c='def-bar', ...) { + cat("foo.bar with: \n"); + print(list(x=x, a=a, b=b, c=c, cc=cc, named=list(...))) +} + +foo.baz <- function(x, d, a=1, c='def-baz', cc='def-cc-baz', ...) NextMethod(x) +foo.bazz <- function(x, d, a=1, c='def-baz', ...) NextMethod() +foo.bazzz <- function(x, d, a=1, c='def-baz', ...) NextMethod('foo') + +foo(withClass(42, c('baz', 'bar')), 'caller-d', a='caller-a', b='caller-b', e='caller-e') +foo(withClass(42, c('bazz', 'bar')), 'caller-d', a='caller-a', b='caller-b', e='caller-e') +foo(withClass(42, c('bazzz', 'bar')), 'caller-d', a='caller-a', b='caller-b', e='caller-e') diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/base/TestSimpleLists.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/base/TestSimpleLists.java index 8529d785457ec4aa1c7bf389d1606eead86c753e..cfcffb27f3dbbe436ce32644f678d54bc4fb0885 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/base/TestSimpleLists.java +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/base/TestSimpleLists.java @@ -73,6 +73,10 @@ public class TestSimpleLists extends TestBase { @Test public void testListUpdate() { assertEval("{ l <- list(c(1,2,3),c(4,5,6)) ; l[[1]] <- c(7,8,9) ; l[[1]] }"); + assertEval("{ l <- list(42); l[1][1] <- 7; l }"); + assertEval("{ l <- list(c(42)); l[1][1] <- 7; l }"); + assertEval("{ l <- list(c(42, 43)); l[[1]][1] <- 7; l }"); + assertEval("{ l <- list(c(42)); idx <- TRUE; l[idx] <- list(c(1,2,3)); l }"); } @Test diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/stats/TestExternal_covcor.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/stats/TestExternal_covcor.java new file mode 100644 index 0000000000000000000000000000000000000000..5f548a2355f67085dc227c71caed04103bece6e7 --- /dev/null +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/library/stats/TestExternal_covcor.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2016, 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.test.library.stats; + +import org.junit.Test; + +import com.oracle.truffle.r.test.TestBase; + +public class TestExternal_covcor extends TestBase { + @Test + public void testCovcor() { + assertEval(".Call(stats:::C_cov, 1:5, 1:5, 4, FALSE)"); + assertEval(".Call(stats:::C_cov, 1:5, c(1,5,1,5,10), 4, FALSE)"); + } + + @Test + public void testCovcorArgsCasts() { + assertEval(".Call(stats:::C_cov, c('1','2','3','4','5'), 1:5, 4, FALSE)"); + assertEval(".Call(stats:::C_cov, NULL, 1:5, 4, FALSE)"); + assertEval(".Call(stats:::C_cov, 1:3, 1:5, 4, FALSE)"); + } +} diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/tools/cmpr/CompareLibR.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/tools/cmpr/CompareLibR.java deleted file mode 100644 index 8d03d05af1ed778b56a9e4a412d0252eef883ae2..0000000000000000000000000000000000000000 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/tools/cmpr/CompareLibR.java +++ /dev/null @@ -1,245 +0,0 @@ -/* - * Copyright (c) 2014, 2016, 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.test.tools.cmpr; - -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; -import java.io.BufferedReader; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.net.URL; -import java.nio.file.DirectoryStream; -import java.nio.file.FileSystem; -import java.nio.file.FileSystems; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.HashMap; -import java.util.Map; - -import com.oracle.truffle.r.runtime.ResourceHandlerFactory; -import com.oracle.truffle.r.runtime.Utils; - -/** - * Compare the FastR versions of .R files in the standard packages against GnuR. Removes all - * formatting to perform the check, replacing all whitespace (including newlines) with exactly one - * space. - * <p> - * Usage: - * - * <pre> - * --gnurhome path --package pkg | --files path1 path2 - * </pre> - * - * {@code gnurhome} is the path to the GnuR distribution. {@cpde package} gibes the package to - * compare,e.g. {@code base}. The second form just compares the two files. - */ -public class CompareLibR { - - private static class FileContent { - Path path; - String content; - String flattened; - - FileContent(Path path, String content) { - this.path = path; - this.content = content; - } - - @Override - public String toString() { - return path.toString(); - } - } - - public static void main(String[] args) throws Exception { - // Checkstyle: stop system print check - String gnurHome = null; - String pkg = null; - String path1 = null; - String path2 = null; - boolean printPaths = false; - String diffApp = "diff"; - int i = 0; - while (i < args.length) { - String arg = args[i]; - switch (arg) { - case "--gnurhome": - i++; - gnurHome = args[i]; - break; - case "--package": - i++; - pkg = args[i]; - break; - case "--files": - if (args.length == 3) { - i++; - path1 = args[i]; - i++; - path2 = args[i]; - } else { - usage(); - } - break; - case "--paths": - printPaths = true; - break; - - case "--diffapp": - i++; - diffApp = args[i]; - break; - default: - usage(); - } - i++; - } - - if (gnurHome == null && path1 == null) { - usage(); - } - - if (path1 != null) { - compareFiles(path1, path2); - } else { - - Map<String, FileContent> fastRFiles = getFastR(pkg); - Map<String, FileContent> gnuRFiles = getGnuR(gnurHome, pkg, fastRFiles); - deformat(gnuRFiles); - deformat(fastRFiles); - for (Map.Entry<String, FileContent> entry : fastRFiles.entrySet()) { - FileContent fastR = entry.getValue(); - String fileName = entry.getKey(); - FileContent gnuR = gnuRFiles.get(fileName); - if (gnuR == null) { - System.out.println("FastR has file: " + fileName + " not found in GnuR"); - } else { - if (!fastR.flattened.equals(gnuR.flattened)) { - if (printPaths) { - System.out.printf("%s %s %s%n", diffApp, gnuR.toString(), replaceBin(fastR.toString())); - } else { - System.out.println(fileName + " differs"); - } - } else { - System.out.println(fileName + " is identical (modulo formatting)"); - } - } - } - } - } - - private static String deformat(String s) { - return s.replaceAll("\\s+", " "); - } - - private static void deformat(Map<String, FileContent> map) { - for (Map.Entry<String, FileContent> entry : map.entrySet()) { - FileContent fc = entry.getValue(); - fc.flattened = deformat(fc.content); - } - } - - private static Map<String, FileContent> getGnuR(String gnurHome, String lib, Map<String, FileContent> filter) throws IOException { - FileSystem fs = FileSystems.getDefault(); - Path baseR = fs.getPath(lib, "R"); - Path library = fs.getPath(gnurHome, "src", "library"); - baseR = library.resolve(baseR); - Map<String, FileContent> result = new HashMap<>(); - try (DirectoryStream<Path> stream = Files.newDirectoryStream(baseR)) { - for (Path entry : stream) { - String entryName = entry.getFileName().toString(); - if (entryName.endsWith(".R") && (filter.get(entryName) != null)) { - File file = entry.toFile(); - result.put(entryName, new FileContent(entry, readFileContent(file))); - } - } - } - return result; - } - - private static String toFirstUpper(String input) { - return input.substring(0, 1).toUpperCase() + input.substring(1); - } - - private static Map<String, FileContent> getFastR(String lib) throws Exception { - Class<?> klass = Class.forName("com.oracle.truffle.r.nodes.builtin." + lib + "." + toFirstUpper(lib) + "Package"); - InputStream is = ResourceHandlerFactory.getHandler().getResourceAsStream(klass, "R"); - Map<String, FileContent> result = new HashMap<>(); - if (is == null) { - return result; - } - try (BufferedReader r = new BufferedReader(new InputStreamReader(is))) { - String line; - while ((line = r.readLine()) != null) { - if (line.endsWith(".r") || line.endsWith(".R")) { - String fileName = line.trim(); - final String rResource = "R/" + fileName; - URL url = klass.getResource(rResource); - String content = Utils.getResourceAsString(klass, rResource, true); - result.put(fileName, new FileContent(FileSystems.getDefault().getPath(url.getPath()), content)); - } - } - } - return result; - } - - private static String replaceBin(String s) { - return s.replace("/bin/", "/src/"); - } - - private static String readFileContent(File file) throws IOException { - byte[] buf = new byte[(int) file.length()]; - try (BufferedInputStream bs = new BufferedInputStream(new FileInputStream(file))) { - bs.read(buf); - } - return new String(buf); - } - - private static void writeFile(File file, String s) throws IOException { - try (BufferedOutputStream bs = new BufferedOutputStream(new FileOutputStream(file))) { - bs.write(s.getBytes()); - } - } - - private static void compareFiles(String path1, String path2) throws IOException { - String c1 = deformat(readFileContent(new File(path1))); - String c2 = deformat(readFileContent(new File(path2))); - if (c1.equals(c2)) { - System.out.println("files are identical (modulo formatting)"); - } else { - System.out.println("files differ"); - writeFile(new File(path1 + ".deformat"), c1); - writeFile(new File(path2 + ".deformat"), c2); - } - } - - private static void usage() { - // Checkstyle: stop system print check - System.err.println("usage: --gnurhome path --package pkg | --files path1 path2"); - System.exit(1); - } -} diff --git a/mx.fastr/mx_fastr.py b/mx.fastr/mx_fastr.py index 55843f099afd0f4dc8bf7c64fbcac7a7d3caa15f..eb145f15c27e02aedfa50ffe523d45476ad36c99 100644 --- a/mx.fastr/mx_fastr.py +++ b/mx.fastr/mx_fastr.py @@ -27,7 +27,7 @@ import mx import mx_gate import mx_fastr_pkgs import mx_fastr_dists -from mx_fastr_dists import FastRNativeProject, FastRTestNativeProject, FastRReleaseProject #pylint: disable=unused-import +from mx_fastr_dists import FastRNativeProject, FastRTestNativeProject, FastRReleaseProject, FastRNativeRecommendedProject #pylint: disable=unused-import import mx_copylib import mx_fastr_mkgramrd @@ -90,15 +90,9 @@ def do_run_r(args, command, extraVmArgs=None, jdk=None, **kwargs): if not jdk: jdk = get_default_jdk() - vmArgs = ['-cp', mx.classpath(jdk=jdk)] + vmArgs = ['-cp', mx.classpath('FASTR', jdk=jdk)] - if 'nocompile' in kwargs: - nocompile = True - del kwargs['nocompile'] - else: - nocompile = False - - vmArgs += set_graal_options(nocompile) + vmArgs += set_graal_options() if extraVmArgs is None or not '-da' in extraVmArgs: # unless explicitly disabled we enable assertion checking @@ -113,7 +107,7 @@ def do_run_r(args, command, extraVmArgs=None, jdk=None, **kwargs): return mx.run_java(vmArgs + args, jdk=jdk, **kwargs) def r_classpath(args): - print mx.classpath(jdk=mx.get_jdk()) + print mx.classpath('FASTR', jdk=mx.get_jdk()) def _sanitize_vmArgs(jdk, vmArgs): ''' @@ -137,11 +131,12 @@ def _sanitize_vmArgs(jdk, vmArgs): i = i + 1 return xargs -def set_graal_options(nocompile=False): +def set_graal_options(): + ''' + If Graal is enabled, set some options specific to FastR + ''' if _mx_graal: result = ['-Dgraal.InliningDepthError=500', '-Dgraal.EscapeAnalysisIterations=3', '-XX:JVMCINMethodSizeLimit=1000000'] - if nocompile: - result += ['-Dgraal.TruffleCompilationThreshold=100000'] return result else: return [] @@ -345,8 +340,6 @@ def _junit_r_harness(args, vmArgs, jdk, junitArgs): # no point in printing errors to file when running tests (that contain errors on purpose) vmArgs += ['-DR:-PrintErrorStacktracesToFile'] - vmArgs += set_graal_options(nocompile=True) - setREnvironment() return mx.run_java(vmArgs + junitArgs, nonZeroIsFatal=False, jdk=jdk) @@ -511,27 +504,6 @@ def rbdiag(args): mx.run_java(['-cp', cp, 'com.oracle.truffle.r.nodes.test.RBuiltinDiagnostics'] + args) -def rcmplib(args): - '''compare FastR library R sources against GnuR''' - parser = ArgumentParser(prog='mx rcmplib') - parser.add_argument('--gnurhome', action='store', help='path to GnuR sources', required=True) - parser.add_argument('--package', action='store', help='package to check', default="base") - parser.add_argument('--paths', action='store_true', help='print full paths of files that differ') - parser.add_argument('--diffapp', action='store', help='diff application', default="diff") - args = parser.parse_args(args) - cmpArgs = [] - cmpArgs.append("--gnurhome") - cmpArgs.append(args.gnurhome) - cmpArgs.append("--package") - cmpArgs.append(args.package) - if args.paths: - cmpArgs.append("--paths") - cmpArgs.append("--diffapp") - cmpArgs.append(args.diffapp) - - cp = mx.classpath([pcp.name for pcp in mx.projects_opt_limit_to_suites()]) - mx.run_java(['-cp', cp, 'com.oracle.truffle.r.test.tools.cmpr.CompareLibR'] + cmpArgs) - def _gnur_path(): np = mx.project('com.oracle.truffle.r.native') return join(np.dir, 'gnur', r_version(), 'bin') @@ -570,7 +542,6 @@ _commands = { 'unittest' : [unittest, ['options']], 'rbcheck' : [rbcheck, '--filter [gnur-only,fastr-only,both,both-diff]'], 'rbdiag' : [rbdiag, '(builtin)* [-v] [-n] [-m] [--sweep | --sweep=lite | --sweep=total] [--mnonly] [--noSelfTest] [--matchLevel=same | --matchLevel=error] [--maxSweeps=N] [--outMaxLev=N]'], - 'rcmplib' : [rcmplib, ['options']], 'rrepl' : [rrepl, '[options]'], 'rembed' : [rembed, '[options]'], 'r-cp' : [r_classpath, '[options]'], diff --git a/mx.fastr/mx_fastr_dists.py b/mx.fastr/mx_fastr_dists.py index 39961a6f024e63d1f9f7d17744e708655efc5abd..3892b38573639e87efb45c1d060868e6b15934f2 100644 --- a/mx.fastr/mx_fastr_dists.py +++ b/mx.fastr/mx_fastr_dists.py @@ -228,8 +228,9 @@ class ReleaseBuildTask(mx.NativeBuildTask): if isinstance(dep, mx.JARDistribution): shutil.copy(join(dep.suite.dir, dep.path), jars_dir) elif isinstance(dep, mx.Library): - jar_name = dep.name.lower() + '.jar' - shutil.copyfile(join(dep.suite.dir, dep.path), join(jars_dir, jar_name)) + if not dep.name.lower() == 'jdk_tools': + jar_name = dep.name.lower() + '.jar' + shutil.copyfile(join(dep.suite.dir, dep.path), join(jars_dir, jar_name)) elif isinstance(dep, mx.JavaProject): if 'com.oracle.truffle.r' in dep.name: classfiles_dir = dep.output_dir() @@ -263,6 +264,30 @@ class ReleaseBuildTask(mx.NativeBuildTask): rscript_launcher = join(self.subject.dir, 'src', 'Rscript_launcher') self._template(rscript_launcher, join(bin_dir, 'Rscript'), template_dict) +class FastRNativeRecommendedProject(mx.NativeProject): + ''' + This finesses an ordering problem on installing the recommended R packages. + These must be installed by FastR using bin/R CMD INSTALL. That will invoke a + nested 'mx R' invocation which requires the FASTR distribution to be available. + However, this dependency cannt be specified in the suite.py file so we achieve + it here by ensuring that it is built prior to the native.recommended project. + ''' + def __init__(self, suite, name, deps, workingSets, theLicense, **args): + mx.NativeProject.__init__(self, suite, name, None, [], deps, workingSets, None, None, join(suite.dir, name), theLicense) + + def getBuildTask(self, args): + return NativeRecommendedBuildTask(self, args) + +class NativeRecommendedBuildTask(mx.NativeBuildTask): + def __init__(self, project, args): + mx.NativeBuildTask.__init__(self, args, project) + + def build(self): + # must archive FASTR before build so that nested mx R CMD INSTALL can execute + mx.archive(['@FASTR']) + mx.NativeBuildTask.build(self) + + class FastRArchiveParticipant: def __init__(self, dist): self.dist = dist @@ -292,7 +317,6 @@ class FastRArchiveParticipant: include_dir = join(self.release_project.dir, 'include') shutil.rmtree(include_dir) - def mx_post_parse_cmd_line(opts): for dist in mx_fastr._fastr_suite.dists: dist.set_archiveparticipant(FastRArchiveParticipant(dist)) diff --git a/mx.fastr/suite.py b/mx.fastr/suite.py index d83edbd3277c23b04c13aa0f967045acc91b452f..63ef682bce091569d03d3224022cd192b0b5428a 100644 --- a/mx.fastr/suite.py +++ b/mx.fastr/suite.py @@ -261,22 +261,19 @@ suite = { "com.oracle.truffle.r.release" : { "sourceDirs" : ["src"], - "dependencies" : ["com.oracle.truffle.r.engine", "com.oracle.truffle.r.runtime.ffi", "com.oracle.truffle.r.native"], + "dependencies" : ["com.oracle.truffle.r.native.recommended"], "class" : "FastRReleaseProject", "output" : "com.oracle.truffle.r.release" }, "com.oracle.truffle.r.native.recommended" : { - "sourceDirs" : [], - # these dependencies ensure that all distributions are built - # before the nested mx that does the CMD INSTALL runs "dependencies" : [ - "com.oracle.truffle.r.release", - "com.oracle.truffle.r.test", - "com.oracle.truffle.r.test.native" + "com.oracle.truffle.r.native", + "com.oracle.truffle.r.engine", + "com.oracle.truffle.r.runtime.ffi" ], + "class" : "FastRNativeRecommendedProject", "native" : "true", - "output" : "com.oracle.truffle.r.native.recommended", "workingSets" : "FastR", },