diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java index f3119ba912260d9230d786fa75254fe960238358..987209e766adde27b169110c9a96055d26585c35 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/BasePackage.java @@ -99,6 +99,8 @@ import com.oracle.truffle.r.nodes.builtin.fastr.FastRPkgSource; import com.oracle.truffle.r.nodes.builtin.fastr.FastRPkgSourceNodeGen; import com.oracle.truffle.r.nodes.builtin.fastr.FastRRefCountInfo; import com.oracle.truffle.r.nodes.builtin.fastr.FastRRefCountInfoNodeGen; +import com.oracle.truffle.r.nodes.builtin.fastr.FastRSlotAssign; +import com.oracle.truffle.r.nodes.builtin.fastr.FastRSlotAssignNodeGen; import com.oracle.truffle.r.nodes.builtin.fastr.FastRStackTrace; import com.oracle.truffle.r.nodes.builtin.fastr.FastRStackTraceNodeGen; import com.oracle.truffle.r.nodes.builtin.fastr.FastRStats.FastRProfAttr; @@ -375,6 +377,7 @@ public class BasePackage extends RBuiltinPackage { add(FastRProfAttr.class, FastRStatsFactory.FastRProfAttrNodeGen::create); add(FastRProfTypecounts.class, FastRStatsFactory.FastRProfTypecountsNodeGen::create); add(FastRProfFuncounts.class, FastRStatsFactory.FastRProfFuncountsNodeGen::create); + add(FastRSlotAssign.class, FastRSlotAssignNodeGen::create); add(FastRSyntaxTree.class, FastRSyntaxTreeNodeGen::create); add(FastRThrowIt.class, FastRThrowItNodeGen::create); add(FastRTrace.Trace.class, FastRTraceFactory.TraceNodeGen::create); diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateSlot.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateSlot.java index 97584d822ee6a358ed5739eb6d9eee1ac067826b..684d9ce1e776ce18d07cfc59f4bf3415c079d7b4 100644 --- a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateSlot.java +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/base/UpdateSlot.java @@ -22,14 +22,12 @@ import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.profiles.ConditionProfile; -import com.oracle.truffle.r.nodes.access.ConstantNode; import com.oracle.truffle.r.nodes.access.UpdateSlotNode; import com.oracle.truffle.r.nodes.access.variables.ReadVariableNode; +import com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef; import com.oracle.truffle.r.nodes.builtin.RBuiltinNode; import com.oracle.truffle.r.nodes.function.ClassHierarchyNode; import com.oracle.truffle.r.nodes.function.ClassHierarchyNodeGen; -import com.oracle.truffle.r.nodes.function.RCallNode; -import com.oracle.truffle.r.nodes.function.WrapArgumentNode; import com.oracle.truffle.r.nodes.function.call.CallRFunctionNode; import com.oracle.truffle.r.runtime.ArgumentsSignature; import com.oracle.truffle.r.runtime.RCaller; @@ -38,105 +36,91 @@ import com.oracle.truffle.r.runtime.builtins.RBuiltin; import com.oracle.truffle.r.runtime.context.RContext; import com.oracle.truffle.r.runtime.data.RFunction; import com.oracle.truffle.r.runtime.data.RPromise; +import com.oracle.truffle.r.runtime.data.RPromise.Closure; import com.oracle.truffle.r.runtime.data.RStringVector; -import com.oracle.truffle.r.runtime.data.RSymbol; +import com.oracle.truffle.r.runtime.nodes.RBaseNode; +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; @RBuiltin(name = "@<-", kind = PRIMITIVE, parameterNames = {"", "", "value"}, nonEvalArgs = 1, behavior = COMPLEX) public abstract class UpdateSlot extends RBuiltinNode { - private static final ArgumentsSignature SIGNATURE = ArgumentsSignature.get("cl", "name", "valueClass"); - - @CompilationFinal private RFunction checkSlotAssignFunction; - @Child private ClassHierarchyNode objClassHierarchy; - @Child private ClassHierarchyNode valClassHierarchy; @Child private UpdateSlotNode updateSlotNode = com.oracle.truffle.r.nodes.access.UpdateSlotNodeGen.create(); - @Child private ReadVariableNode checkAtAssignmentFind = ReadVariableNode.createFunctionLookup(RSyntaxNode.INTERNAL, "checkAtAssignment"); - @Child private CallRFunctionNode checkAtAssignmentCall; - private final ConditionProfile cached = ConditionProfile.createBinaryProfile(); static { Casts casts = new Casts(UpdateSlot.class); casts.arg(0).asAttributable(true, true, true); } - protected String getName(Object nameObj) { - assert nameObj instanceof RPromise; - Object rep = ((RPromise) nameObj).getRep(); - if (rep instanceof WrapArgumentNode) { - rep = ((WrapArgumentNode) rep).getOperand(); - } - if (rep instanceof ConstantNode) { - Object val = ((ConstantNode) rep).getValue(); - if (val instanceof String) { - return (String) val; - } - if (val instanceof RSymbol) { - return ((RSymbol) val).getName(); + protected String getName(RPromise nameObj) { + Closure closure = nameObj.getClosure(); + if (closure.asSymbol() != null) { + return closure.asSymbol(); + } else if (closure.asStringConstant() != null) { + return closure.asStringConstant(); + } else { + CompilerDirectives.transferToInterpreter(); + RSyntaxElement element = closure.getExpr().asRSyntaxNode(); + assert !(element instanceof RSyntaxLookup); + if (element instanceof RSyntaxConstant) { + throw error(RError.Message.SLOT_INVALID_TYPE, Predef.typeName().apply(((RSyntaxConstant) element).getValue())); + } else { + throw error(RError.Message.SLOT_INVALID_TYPE, "language"); } - } else if (rep instanceof ReadVariableNode) { - return ((ReadVariableNode) rep).getIdentifier(); - } else if (rep instanceof RCallNode) { - throw error(RError.Message.SLOT_INVALID_TYPE, "language"); } - // TODO: this is not quite correct, but I wonder if we even reach here (can also be - // augmented on demand) - throw error(RError.Message.SLOT_INVALID_TYPE, nameObj.getClass().toString()); } - private void checkSlotAssign(VirtualFrame frame, Object object, String name, Object value) { - // TODO: optimize using a mechanism similar to overrides? - if (checkSlotAssignFunction == null) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - checkSlotAssignFunction = (RFunction) checkAtAssignmentFind.execute(frame); - } - if (checkAtAssignmentCall == null) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - checkAtAssignmentCall = insert(CallRFunctionNode.create(checkSlotAssignFunction.getTarget())); - } - if (objClassHierarchy == null) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - objClassHierarchy = insert(ClassHierarchyNodeGen.create(true, false)); - } - if (valClassHierarchy == null) { - CompilerDirectives.transferToInterpreterAndInvalidate(); - valClassHierarchy = insert(ClassHierarchyNodeGen.create(true, false)); - } - RStringVector objClass = objClassHierarchy.execute(object); - RStringVector valClass = valClassHierarchy.execute(value); - RFunction currentFunction = (RFunction) checkAtAssignmentFind.execute(frame); - if (cached.profile(currentFunction == checkSlotAssignFunction)) { - // TODO: technically, someone could override checkAtAssignment function and access the - // caller, but it's rather unlikely - checkAtAssignmentCall.execute(frame, checkSlotAssignFunction, RCaller.create(frame, getOriginalCall()), null, new Object[]{objClass, name, valClass}, SIGNATURE, - checkSlotAssignFunction.getEnclosingFrame(), null); - } else { - // slow path - RContext.getEngine().evalFunction(currentFunction, frame.materialize(), RCaller.create(frame, getOriginalCall()), null, objClass, name, valClass); - } - } + public static final class CheckSlotAssignNode extends RBaseNode { - /* - * Motivation for cached version is that in the operator form (foo@bar<-baz), the name is an - * interned string which allows us to avoid longer lookup - */ - @Specialization(guards = "sameName(nameObj, nameObjCached)") - protected Object updateSlotCached(VirtualFrame frame, Object object, @SuppressWarnings("unused") Object nameObj, Object value, @SuppressWarnings("unused") @Cached("nameObj") Object nameObjCached, - @Cached("getName(nameObjCached)") String name) { - checkSlotAssign(frame, object, name, value); - return updateSlotNode.executeUpdate(object, name, value); + private static final ArgumentsSignature SIGNATURE = ArgumentsSignature.get("cl", "name", "valueClass"); + + @CompilationFinal private RFunction checkSlotAssignFunction; + @Child private ClassHierarchyNode objClassHierarchy; + @Child private ClassHierarchyNode valClassHierarchy; + @Child private ReadVariableNode checkAtAssignmentFind = ReadVariableNode.createFunctionLookup(RSyntaxNode.INTERNAL, "checkAtAssignment"); + @Child private CallRFunctionNode checkAtAssignmentCall; + + private final ConditionProfile cached = ConditionProfile.createBinaryProfile(); + + public void execute(VirtualFrame frame, Object object, String name, Object value) { + if (checkSlotAssignFunction == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + checkSlotAssignFunction = (RFunction) checkAtAssignmentFind.execute(frame); + } + if (checkAtAssignmentCall == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + checkAtAssignmentCall = insert(CallRFunctionNode.create(checkSlotAssignFunction.getTarget())); + } + if (objClassHierarchy == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + objClassHierarchy = insert(ClassHierarchyNodeGen.create(true, false)); + } + if (valClassHierarchy == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + valClassHierarchy = insert(ClassHierarchyNodeGen.create(true, false)); + } + RStringVector objClass = objClassHierarchy.execute(object); + RStringVector valClass = valClassHierarchy.execute(value); + RFunction currentFunction = (RFunction) checkAtAssignmentFind.execute(frame); + if (cached.profile(currentFunction == checkSlotAssignFunction)) { + // TODO: technically, someone could override checkAtAssignment function and access + // the caller, but it's rather unlikely + checkAtAssignmentCall.execute(frame, checkSlotAssignFunction, RCaller.createInvalid(frame), null, new Object[]{objClass, name, valClass}, SIGNATURE, + checkSlotAssignFunction.getEnclosingFrame(), null); + } else { + // slow path + RContext.getEngine().evalFunction(currentFunction, frame.materialize(), RCaller.createInvalid(frame), null, objClass, name, valClass); + } + } } - @Specialization(replaces = "updateSlotCached") - protected Object updateSlot(VirtualFrame frame, Object object, Object nameObj, Object value) { + @Specialization + protected Object updateSlot(VirtualFrame frame, Object object, RPromise nameObj, Object value, + @Cached("new()") CheckSlotAssignNode check) { String name = getName(nameObj); - checkSlotAssign(frame, object, name, value); + check.execute(frame, object, name, value); return updateSlotNode.executeUpdate(object, name, value); } - - protected boolean sameName(Object nameObj, Object nameObjCached) { - assert nameObj instanceof RPromise; - assert nameObjCached instanceof RPromise; - return ((RPromise) nameObj).getRep() == ((RPromise) nameObjCached).getRep(); - } } diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRSlotAssign.java b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRSlotAssign.java new file mode 100644 index 0000000000000000000000000000000000000000..4bf687e14fe56a99ad7892c2f024656429c40541 --- /dev/null +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRSlotAssign.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2015, 2017, 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.builtin.fastr; + +import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.singleElement; +import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.stringValue; +import static com.oracle.truffle.r.nodes.builtin.CastBuilder.Predef.toBoolean; +import static com.oracle.truffle.r.runtime.RVisibility.ON; +import static com.oracle.truffle.r.runtime.builtins.RBehavior.COMPLEX; +import static com.oracle.truffle.r.runtime.builtins.RBuiltinKind.PRIMITIVE; + +import com.oracle.truffle.api.dsl.Cached; +import com.oracle.truffle.api.dsl.Specialization; +import com.oracle.truffle.api.frame.VirtualFrame; +import com.oracle.truffle.r.nodes.access.UpdateSlotNode; +import com.oracle.truffle.r.nodes.access.UpdateSlotNodeGen; +import com.oracle.truffle.r.nodes.builtin.RBuiltinNode; +import com.oracle.truffle.r.nodes.builtin.base.UpdateAttr.InternStringNode; +import com.oracle.truffle.r.nodes.builtin.base.UpdateAttrNodeGen.InternStringNodeGen; +import com.oracle.truffle.r.nodes.builtin.base.UpdateSlot.CheckSlotAssignNode; +import com.oracle.truffle.r.runtime.RError.Message; +import com.oracle.truffle.r.runtime.RRuntime; +import com.oracle.truffle.r.runtime.builtins.RBuiltin; +import com.oracle.truffle.r.runtime.data.RMissing; + +@RBuiltin(name = ".fastr.methods.slotassign", visibility = ON, kind = PRIMITIVE, parameterNames = {"object", "name", "check", "value"}, behavior = COMPLEX) +public abstract class FastRSlotAssign extends RBuiltinNode { + + static { + Casts casts = new Casts(FastRSlotAssign.class); + casts.arg("object").mustNotBeNull(); + casts.arg("name").defaultError(Message.SLOT_INVALID_TYPE_OR_LEN).mustNotBeMissing().mustBe(stringValue()).asStringVector().mustBe(singleElement()).findFirst(); + casts.arg("check").mustNotBeMissing().asLogicalVector().findFirst().mustNotBeNA(Message.NA_UNEXP).map(toBoolean()); + casts.arg("value").mustNotBeMissing(); + } + + @Child private InternStringNode intern = InternStringNodeGen.create(); + @Child private UpdateSlotNode updateSlotNode = UpdateSlotNodeGen.create(); + + @Override + public Object[] getDefaultParameterValues() { + return new Object[]{RMissing.instance, RMissing.instance, RRuntime.LOGICAL_TRUE, RMissing.instance}; + } + + @Specialization(guards = "check") + public Object assign(VirtualFrame frame, Object object, String name, @SuppressWarnings("unused") boolean check, Object value, + @Cached("new()") CheckSlotAssignNode checkNode) { + String interned = intern.execute(name); + checkNode.execute(frame, object, interned, value); + return updateSlotNode.executeUpdate(object, interned, value); + } + + @Specialization(guards = "!check") + public Object assign(Object object, String name, @SuppressWarnings("unused") boolean check, Object value) { + String interned = intern.execute(name); + return updateSlotNode.executeUpdate(object, interned, value); + } +} diff --git a/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/methods/R/methods_overrides.R b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/methods/R/methods_overrides.R new file mode 100644 index 0000000000000000000000000000000000000000..99c947a62dbc8ce4e07359f7873abf9baab63558 --- /dev/null +++ b/com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/methods/R/methods_overrides.R @@ -0,0 +1,26 @@ +# Copyright (c) 2017, 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. + +eval(expression({ +# this function is replaced with a primitive because it is expected to +# modify its argument in-place, which can clash with argument refcount handling +`slot<-` <- .fastr.methods.slotassign +}), asNamespace("methods")) diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/UpdateSlotNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/UpdateSlotNode.java index 4cd1ffa866ada9f3ed5cb3396c0ca55b103a22d6..59d8ab2c49c12060a5c96d63205779293f1f01e2 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/UpdateSlotNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/access/UpdateSlotNode.java @@ -15,7 +15,6 @@ package com.oracle.truffle.r.nodes.access; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.dsl.Cached; import com.oracle.truffle.api.dsl.Specialization; -import com.oracle.truffle.r.nodes.RASTUtils; import com.oracle.truffle.r.nodes.attributes.InitAttributesNode; import com.oracle.truffle.r.nodes.attributes.SetAttributeNode; import com.oracle.truffle.r.runtime.RCaller; @@ -32,6 +31,8 @@ import com.oracle.truffle.r.runtime.nodes.RBaseNode; public abstract class UpdateSlotNode extends RBaseNode { + private static final String SET_DATA_PART = "setDataPart"; + public abstract Object executeUpdate(Object object, String name, Object value); protected SetAttributeNode createAttrUpdate() { @@ -56,12 +57,9 @@ public abstract class UpdateSlotNode extends RBaseNode { protected Object updateSlotS4Data(RAttributable object, @SuppressWarnings("unused") String name, Object value) { // TODO: any way to cache it or use a mechanism similar to overrides? REnvironment methodsNamespace = REnvironment.getRegisteredNamespace("methods"); - String identifier = "setDataPart"; - Object f = methodsNamespace.findFunction(identifier); - RFunction dataPart = (RFunction) RContext.getRRuntimeASTAccess().forcePromise(identifier, f); - return RContext.getEngine().evalFunction(dataPart, methodsNamespace.getFrame(), RCaller.create(null, RASTUtils.getOriginalCall(this)), null, object, - prepareValue(value), - RRuntime.LOGICAL_TRUE); + Object f = methodsNamespace.findFunction(SET_DATA_PART); + RFunction dataPart = (RFunction) RContext.getRRuntimeASTAccess().forcePromise(SET_DATA_PART, f); + return RContext.getEngine().evalFunction(dataPart, methodsNamespace.getFrame(), RCaller.createInvalid(null), null, object, prepareValue(value), RRuntime.LOGICAL_TRUE); } protected boolean isData(String name) { 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 a6bb49983999b7705e5efda6a6bb4a038a92e19e..13c607e358851a5b39313b9fc650e64b2d2aa620 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 @@ -80,8 +80,12 @@ abstract class ReplacementNode extends OperatorNode { } } - private static String getTargetTmpName(int tempNamesStartIndex) { - return "*tmp*" + tempNamesStartIndex; + private static String getTargetTemp(int index) { + return "*tmp*" + index; + } + + private static String getRHSTemp(int index) { + return "*rhs*" + index; } private static boolean hasOnlySpecialCalls(List<RSyntaxCall> calls) { @@ -133,11 +137,6 @@ abstract class ReplacementNode extends OperatorNode { 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] = new GetNonSharedNode.GetNonSharedSyntaxNode(argNodes[0].asRNode()); - } newSyntaxLHS = builder.lookup(lookupLHS.getLazySourceSection(), symbol + "<-", true); } else { // data types (and lengths) are verified in isNamespaceLookupCall @@ -171,8 +170,8 @@ abstract class ReplacementNode extends OperatorNode { super(source, operator, lhs); this.rhs = rhs; - this.storeRhs = WriteVariableNode.createAnonymous("*rhs*" + tempNamesStartIndex, WriteVariableNode.Mode.INVISIBLE, rhs); - this.removeRhs = RemoveAndAnswerNode.create("*rhs*" + tempNamesStartIndex); + this.storeRhs = WriteVariableNode.createAnonymous(getRHSTemp(tempNamesStartIndex), WriteVariableNode.Mode.INVISIBLE, rhs); + this.removeRhs = RemoveAndAnswerNode.create(getRHSTemp(tempNamesStartIndex)); } @Override @@ -243,7 +242,7 @@ abstract class ReplacementNode extends OperatorNode { extractFunc = createSpecialFunctionQuery(calls.get(i), extractFunc.asRSyntaxNode(), codeBuilderContext); ((RCallSpecialNode) extractFunc).setPropagateFullCallNeededException(); } - this.replaceCall = (RCallSpecialNode) createFunctionUpdate(source, extractFunc.asRSyntaxNode(), ReadVariableNode.create("*rhs*" + tempNamesStartIndex), calls.get(0), codeBuilderContext); + this.replaceCall = (RCallSpecialNode) createFunctionUpdate(source, extractFunc.asRSyntaxNode(), ReadVariableNode.create(getRHSTemp(tempNamesStartIndex)), calls.get(0), codeBuilderContext); this.replaceCall.setPropagateFullCallNeededException(); } @@ -341,10 +340,9 @@ abstract class ReplacementNode extends OperatorNode { */ private static final class GenericReplacementNode extends ReplacementWithRhsNode { - @Child private WriteVariableNode targetTmpWrite; @Child private RemoveAndAnswerNode targetTmpRemove; - @Children private final RNode[] updates; + @Children private final WriteVariableNode[] updates; GenericReplacementNode(SourceSection source, RSyntaxLookup operator, RNode target, RSyntaxElement lhs, RNode rhs, List<RSyntaxCall> calls, String targetVarName, boolean isSuper, int tempNamesStartIndex) { @@ -353,8 +351,11 @@ abstract class ReplacementNode extends OperatorNode { * 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. */ - List<RNode> instructions = new ArrayList<>(); + List<WriteVariableNode> instructions = new ArrayList<>(); CodeBuilderContext codeBuilderContext = new CodeBuilderContext(tempNamesStartIndex + calls.size() + 1); + + int targetIndex = tempNamesStartIndex; + instructions.add(WriteVariableNode.createAnonymous(getTargetTemp(targetIndex), WriteVariableNode.Mode.INVISIBLE, wrapForSlotUpdate(target, calls.get(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 @@ -362,35 +363,49 @@ abstract class ReplacementNode extends OperatorNode { * + 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 newFirstArg = ReadVariableNode.create("*tmp*" + (tempNamesStartIndex + tmpIndex)); - RNode update = createSpecialFunctionQuery(calls.get(i), newFirstArg, codeBuilderContext); - instructions.add(WriteVariableNode.createAnonymous("*tmp*" + (tempNamesStartIndex + tmpIndex + 1), WriteVariableNode.Mode.INVISIBLE, update)); + for (int i = calls.size() - 1; i >= 1; i--) { + ReadVariableNode newFirstArg = ReadVariableNode.create(getTargetTemp(targetIndex)); + RNode extract = createSpecialFunctionQuery(calls.get(i), newFirstArg, codeBuilderContext); + instructions.add(WriteVariableNode.createAnonymous(getTargetTemp(++targetIndex), WriteVariableNode.Mode.INVISIBLE, wrapForSlotUpdate(extract, calls.get(i - 1)))); } /* * 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}. + * intermediate results are stored to temporary variables *rhs*{index}. */ + int replacementIndex = tempNamesStartIndex; for (int i = 0; i < calls.size(); i++) { - int tmpIndex = tempNamesStartIndex + calls.size() - i - 1; - String tmprName = i == 0 ? ("*rhs*" + tempNamesStartIndex) : ("*tmpr*" + (tempNamesStartIndex + i - 1)); - RNode update = createFunctionUpdate(source, ReadVariableNode.create("*tmp*" + tmpIndex), ReadVariableNode.create(tmprName), calls.get(i), codeBuilderContext); + RNode update = createFunctionUpdate(source, ReadVariableNode.create(getTargetTemp(targetIndex--)), ReadVariableNode.create(getRHSTemp(replacementIndex)), calls.get(i), + codeBuilderContext); if (i < calls.size() - 1) { - instructions.add(WriteVariableNode.createAnonymous("*tmpr*" + (tempNamesStartIndex + i), WriteVariableNode.Mode.INVISIBLE, update)); + instructions.add(WriteVariableNode.createAnonymous(getRHSTemp(++replacementIndex), WriteVariableNode.Mode.INVISIBLE, update)); } else { instructions.add(WriteVariableNode.createAnonymous(targetVarName, WriteVariableNode.Mode.REGULAR, update, isSuper)); } } - this.updates = instructions.toArray(new RNode[instructions.size()]); - this.targetTmpWrite = WriteVariableNode.createAnonymous(getTargetTmpName(tempNamesStartIndex), WriteVariableNode.Mode.INVISIBLE, target); - this.targetTmpRemove = RemoveAndAnswerNode.create(getTargetTmpName(tempNamesStartIndex)); + this.updates = instructions.toArray(new WriteVariableNode[instructions.size()]); + this.targetTmpRemove = RemoveAndAnswerNode.create(getTargetTemp(tempNamesStartIndex)); + } + + /* + * This is complicated, but at this point seems like the only way to get setClass to work + * properly. the underlying problem is that slot<- and @<- modify shared values, whereas, + * e.g., [[<- does not. + */ + private static RNode wrapForSlotUpdate(RNode target, RSyntaxCall call) { + RSyntaxElement syntaxLHS = call.getSyntaxLHS(); + if (syntaxLHS instanceof RSyntaxLookup) { + String symbol = ((RSyntaxLookup) syntaxLHS).getIdentifier(); + if ("slot".equals(symbol) || "@".equals(symbol)) { + return new GetNonSharedNode.GetNonSharedSyntaxNode(target); + } + } + return target; } @Override @ExplodeLoop protected void executeReplacement(VirtualFrame frame) { - targetTmpWrite.execute(frame); for (RNode update : updates) { update.execute(frame); } diff --git a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/GetNonSharedNode.java b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/GetNonSharedNode.java index deef4720e12707573f72766e9ef1613cb097c997..9e70abd64665b293003018354688c1fc2cbe9285 100644 --- a/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/GetNonSharedNode.java +++ b/com.oracle.truffle.r.nodes/src/com/oracle/truffle/r/nodes/unary/GetNonSharedNode.java @@ -27,8 +27,6 @@ import com.oracle.truffle.api.dsl.Fallback; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.nodes.Node; -import com.oracle.truffle.api.source.SourceSection; -import com.oracle.truffle.r.runtime.RInternalError; import com.oracle.truffle.r.runtime.data.RSharingAttributeStorage; import com.oracle.truffle.r.runtime.data.RTypedValue; import com.oracle.truffle.r.runtime.nodes.RNode; @@ -36,7 +34,7 @@ import com.oracle.truffle.r.runtime.nodes.RSyntaxNode; public abstract class GetNonSharedNode extends Node { - public static final class GetNonSharedSyntaxNode extends RNode implements RSyntaxNode { + public static final class GetNonSharedSyntaxNode extends RNode { @Child private RNode delegate; @Child private GetNonSharedNode nonShared = GetNonSharedNodeGen.create(); @@ -54,21 +52,6 @@ public abstract class GetNonSharedNode extends Node { protected RSyntaxNode getRSyntaxNode() { return delegate.asRSyntaxNode(); } - - @Override - public void setSourceSection(SourceSection sourceSection) { - throw RInternalError.shouldNotReachHere(); - } - - @Override - public SourceSection getLazySourceSection() { - return RSyntaxNode.INTERNAL; - } - - @Override - public SourceSection getSourceSection() { - return RSyntaxNode.INTERNAL; - } } public abstract Object execute(Object value); diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java index fa016d322eb30702abf9c64f4c33024ee8729108..6e8c5a12079d94ce95fab22b2ba509ba6cdf1054 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/RError.java @@ -750,6 +750,7 @@ public final class RError extends RuntimeException { SLOT_NON_S4("trying to get slot \"%s\" from an object (class \"%s\") that is not an S4 object "), SLOT_CANNOT_GET("cannot get a slot (\"%s\") from an object of type \"%s\""), SLOT_NONE("no slot of name \"%s\" for this object of class \"%s\""), + SLOT_INVALID_TYPE_OR_LEN("invalid type or length for slot name"), S4OBJECT_NX_ENVIRONMENT("S4 object does not extend class \"environment\""), NOT_A_SLOT("'%s' is not a slot in class ā%sā"), NS_ALREADY_REG("namespace already registered"), diff --git a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RPromise.java b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RPromise.java index 3e4b0807dd59742ab524841ad766da70654a62a2..24a4df04d697784f8f27accb67bd25cd15ab0fbd 100644 --- a/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RPromise.java +++ b/com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RPromise.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2016, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2017, 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 @@ -39,8 +39,10 @@ import com.oracle.truffle.r.runtime.RCaller; import com.oracle.truffle.r.runtime.RType; import com.oracle.truffle.r.runtime.Utils; import com.oracle.truffle.r.runtime.context.RContext; +import com.oracle.truffle.r.runtime.data.model.RAbstractStringVector; import com.oracle.truffle.r.runtime.nodes.RBaseNode; import com.oracle.truffle.r.runtime.nodes.RNode; +import com.oracle.truffle.r.runtime.nodes.RSyntaxConstant; import com.oracle.truffle.r.runtime.nodes.RSyntaxLookup; /** @@ -445,14 +447,27 @@ public class RPromise implements RTypedValue { private final RBaseNode expr; private final String symbol; + private final String stringConstant; private Closure(RBaseNode expr) { this.expr = expr; if (expr.asRSyntaxNode() instanceof RSyntaxLookup) { - this.symbol = ((RSyntaxLookup) expr.asRSyntaxNode()).getIdentifier(); + this.symbol = ((RSyntaxLookup) expr.asRSyntaxNode()).getIdentifier().intern(); } else { this.symbol = null; } + if (expr.asRSyntaxNode() instanceof RSyntaxConstant) { + Object constant = ((RSyntaxConstant) expr.asRSyntaxNode()).getValue(); + if (constant instanceof String) { + this.stringConstant = (String) constant; + } else if (constant instanceof RAbstractStringVector && ((RAbstractStringVector) constant).getLength() == 1) { + this.stringConstant = ((RAbstractStringVector) constant).getDataAt(0); + } else { + this.stringConstant = null; + } + } else { + this.stringConstant = null; + } } public static Closure create(RBaseNode expr) { @@ -491,6 +506,10 @@ public class RPromise implements RTypedValue { public String asSymbol() { return symbol; } + + public String asStringConstant() { + return stringConstant; + } } @Override