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); checkAtAssignmentCall = insert(CallRFunctionNode.create(checkSlotAssignFunction.getTarget())); assert objClassHierarchy == null && valClassHierarchy == null; objClassHierarchy = insert(ClassHierarchyNodeGen.create(true, false)); valClassHierarchy = insert(ClassHierarchyNodeGen.create(true, false)); } RStringVector objClass = objClassHierarchy.execute(object); RStringVector valClass = objClassHierarchy.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); } }
@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(null, null, null); @Child private ReadVariableNode checkAtAssignmentFind = ReadVariableNode.createFunctionLookup(RSyntaxNode.INTERNAL, "checkAtAssignment"); @Child private CallRFunctionNode checkAtAssignmentCall; private final ConditionProfile cached = ConditionProfile.createBinaryProfile(); @Override protected void createCasts(CastBuilder casts) { casts.arg(0).allowNull().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(); } } else if (rep instanceof ReadVariableNode) { return ((ReadVariableNode) rep).getIdentifier(); } else if (rep instanceof RCallNode) { throw RError.error(this, 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 RError.error(this, 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); checkAtAssignmentCall = insert(CallRFunctionNode.create(checkSlotAssignFunction.getTarget())); assert objClassHierarchy == null && valClassHierarchy == null; objClassHierarchy = insert(ClassHierarchyNodeGen.create(true, false)); valClassHierarchy = insert(ClassHierarchyNodeGen.create(true, false)); } RStringVector objClass = objClassHierarchy.execute(object); RStringVector valClass = objClassHierarchy.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); } } /* * 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); } @Specialization(contains = "updateSlotCached") protected Object updateSlot(VirtualFrame frame, Object object, Object nameObj, Object value) { String name = getName(nameObj); checkSlotAssign(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(); } }
@TruffleBoundary static void printNoDimList(RAbstractContainer s, PrintContext printCtx) throws IOException { final PrintParameters pp = printCtx.parameters(); final PrintWriter out = printCtx.output(); final StringBuilder tagbuf = printCtx.getOrCreateTagBuffer(); // save the original length so that we can restore the original value int taglen = tagbuf.length(); int ns = s.getLength(); RAbstractStringVector names; names = Utils.castTo(RRuntime.asAbstractVector(s.getNames(dummyAttrProfiles))); if (ns > 0) { int npr = (ns <= pp.getMax() + 1) ? ns : pp.getMax(); /* '...max +1' ==> will omit at least 2 ==> plural in msg below */ for (int i = 0; i < npr; i++) { if (i > 0) { out.println(); } String ss = names == null ? null : Utils.<String>getDataAt(names, i); if (ss != null && !ss.isEmpty()) { /* * Bug for L <- list(`a\\b` = 1, `a\\c` = 2) : const char *ss = * translateChar(STRING_ELT(names, i)); */ if (taglen + ss.length() > TAGBUFLEN) { if (taglen <= TAGBUFLEN) { tagbuf.append("$..."); } } else { /* * we need to distinguish character NA from "NA", which is a valid (if * non-syntactic) name */ if (ss == RRuntime.STRING_NA) { tagbuf.append("$<NA>"); } else if (RDeparse.isValidName(ss)) { tagbuf.append(String.format("$%s", ss)); } else { tagbuf.append(String.format("$`%s`", ss)); } } } else { if (taglen + indexWidth(i) > TAGBUFLEN) { if (taglen <= TAGBUFLEN) { tagbuf.append("$..."); } } else { tagbuf.append(String.format("[[%d]]", i + 1)); } } out.println(tagbuf); Object si = s.getDataAtAsObject(i); if (si instanceof RAttributable && ((RAttributable) si).isObject()) { RContext.getEngine().printResult(si); } else { ValuePrinters.INSTANCE.print(si, printCtx); ValuePrinters.printNewLine(printCtx); } tagbuf.setLength(taglen); // reset tag buffer to the original value } if (npr < ns) { out.printf("\n [ reached getOption(\"max.print\") -- omitted %d entries ]", ns - npr); } } else { /* ns = length(s) == 0 */ /* Formal classes are represented as empty lists */ String className = null; if (printCtx.printerNode().isObject(s) && printCtx.printerNode().isMethodDispatchOn()) { RAbstractStringVector klass = Utils.castTo(RRuntime.asAbstractVector(s.getAttr(RRuntime.CLASS_ATTR_KEY))); if (klass != null && klass.getLength() == 1) { String ss = klass.getDataAt(0); String str = snprintf(200, ".__C__%s", ss); Frame frame = com.oracle.truffle.r.runtime.Utils.getActualCurrentFrame(); if (ReadVariableNode.lookupAny(str, frame, false) != null) { className = ss; } } } if (className != null) { out.printf("An object of class \"%s\"", className); } else { if (names != null) { out.print("named "); } out.print("list()"); } } }