diff --git a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RListMR.java b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RListMR.java index be2017c3720fd630b50e3a9a8e8394c019fa9a4a..e48a1f20d3c70161883f60dc4a70ff79624ac4b9 100644 --- a/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RListMR.java +++ b/com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RListMR.java @@ -22,12 +22,14 @@ */ package com.oracle.truffle.r.engine.interop; +import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.interop.CanResolve; import com.oracle.truffle.api.interop.MessageResolution; import com.oracle.truffle.api.interop.Resolve; import com.oracle.truffle.api.interop.TruffleObject; import com.oracle.truffle.api.nodes.Node; +import com.oracle.truffle.api.profiles.ConditionProfile; import com.oracle.truffle.r.engine.TruffleRLanguage; import com.oracle.truffle.r.nodes.access.vector.ElementAccessMode; import com.oracle.truffle.r.nodes.access.vector.ExtractVectorNode; @@ -36,7 +38,9 @@ import com.oracle.truffle.r.nodes.attributes.SpecialAttributesFunctions.GetNames import com.oracle.truffle.r.runtime.RRuntime; import com.oracle.truffle.r.runtime.context.RContext; import com.oracle.truffle.r.runtime.context.RContext.RCloseable; +import com.oracle.truffle.r.runtime.data.RFunction; import com.oracle.truffle.r.runtime.data.RList; +import com.oracle.truffle.r.runtime.data.RStringVector; @MessageResolution(receiverType = RList.class, language = TruffleRLanguage.class) public class RListMR { @@ -77,7 +81,7 @@ public class RListMR { @Resolve(message = "WRITE") public abstract static class RListWriteNode extends Node { - @Child private ReplaceVectorNode extract = ReplaceVectorNode.create(ElementAccessMode.SUBSCRIPT, true); + @Child private ReplaceVectorNode replace = ReplaceVectorNode.create(ElementAccessMode.SUBSCRIPT, true); @Child private Node findContext = TruffleRLanguage.INSTANCE.actuallyCreateFindContextNode(); @SuppressWarnings("try") @@ -97,7 +101,7 @@ public class RListMR { } else if (value instanceof Byte) { value = (int) ((Byte) value).byteValue(); } - Object x = extract.apply(frame, receiver, new Object[]{field}, value); + Object x = replace.apply(frame, receiver, new Object[]{field}, value); return x; } } @@ -116,6 +120,48 @@ public class RListMR { } } + @Resolve(message = "KEY_INFO") + public abstract static class RListKeyInfoNode extends Node { + @Child private Node findContext = TruffleRLanguage.INSTANCE.actuallyCreateFindContextNode(); + @Child private GetNamesAttributeNode getNamesNode = GetNamesAttributeNode.create(); + @Child private ExtractVectorNode extractNode; + + private final ConditionProfile unknownIdentifier = ConditionProfile.createBinaryProfile(); + + private static final int READABLE = 1 << 1; + private static final int WRITABLE = 1 << 2; + private static final int INVOCABLE = 1 << 3; + private static final int INTERNAL = 1 << 4; + + @SuppressWarnings("try") + protected Object access(VirtualFrame frame, RList receiver, String identifier) { + try (RCloseable c = RContext.withinContext(TruffleRLanguage.INSTANCE.actuallyFindContext0(findContext))) { + int info = 0; + RStringVector names = getNamesNode.getNames(receiver); + for (int i = 0; i < names.getLength(); i++) { + if (identifier.equals(names.getDataAt(i))) { + info = 1; + break; + } + } + if (unknownIdentifier.profile(info == 0)) { + return info; + } + + info = info + READABLE + WRITABLE; + if (extractNode == null) { + CompilerDirectives.transferToInterpreterAndInvalidate(); + extractNode = insert(ExtractVectorNode.create(ElementAccessMode.SUBSCRIPT, true)); + } + Object value = extractNode.applyAccessField(frame, receiver, identifier); + if (value instanceof RFunction) { + info = info + INVOCABLE; + } + return info; + } + } + } + @CanResolve public abstract static class RListCheck extends Node { diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/engine/interop/RListMRTest.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/engine/interop/RListMRTest.java new file mode 100644 index 0000000000000000000000000000000000000000..a1af763c7576321ec53e89030ecfa14f34125a5b --- /dev/null +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/engine/interop/RListMRTest.java @@ -0,0 +1,100 @@ +/* + * 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. + */ +package com.oracle.truffle.r.test.engine.interop; + +import com.oracle.truffle.api.interop.ForeignAccess; +import com.oracle.truffle.api.interop.KeyInfo; +import com.oracle.truffle.api.interop.Message; +import com.oracle.truffle.api.interop.TruffleObject; +import com.oracle.truffle.api.interop.UnknownIdentifierException; +import com.oracle.truffle.api.interop.UnsupportedMessageException; +import com.oracle.truffle.api.interop.UnsupportedTypeException; + +import org.junit.Test; + +import com.oracle.truffle.api.source.Source; +import com.oracle.truffle.api.vm.PolyglotEngine; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class RListMRTest { + + @Test + public void testKeysReadWrite() throws UnsupportedMessageException, UnknownIdentifierException, UnsupportedTypeException { + TruffleObject l = createRListTruffleObject("n1=1L, n2=2L, 3, 4"); + + TruffleObject keys = ForeignAccess.sendKeys(Message.KEYS.createNode(), l); + assertEquals("n1", ForeignAccess.sendRead(Message.READ.createNode(), keys, 0)); + assertEquals("n2", ForeignAccess.sendRead(Message.READ.createNode(), keys, 1)); + assertEquals("", ForeignAccess.sendRead(Message.READ.createNode(), keys, 2)); + assertEquals("", ForeignAccess.sendRead(Message.READ.createNode(), keys, 3)); + + assertEquals(4, ForeignAccess.sendGetSize(Message.GET_SIZE.createNode(), keys)); + assertEquals(1, ForeignAccess.sendRead(Message.READ.createNode(), l, "n1")); + assertEquals(2, ForeignAccess.sendRead(Message.READ.createNode(), l, "n2")); + + // TODO fails - should this return NULL as in l[""] or 3 as in l$"" + // ElementAccessMode.FIELD_SUBSCRIPT vs .SUBSCRIPT doesn't seem to have any effect + // assertEquals(3, ForeignAccess.sendRead(Message.READ.createNode(), l, "")); + + // TODO - more tests for NA, ... ? + + l = (TruffleObject) ForeignAccess.sendWrite(Message.WRITE.createNode(), l, "n2", 123); + assertEquals(123, ForeignAccess.sendRead(Message.READ.createNode(), l, "n2")); + } + + @Test + public void testKeysInfo() throws UnsupportedMessageException, UnknownIdentifierException, UnsupportedTypeException { + TruffleObject l = createRListTruffleObject("n1=1, n2=2"); + int info = ForeignAccess.sendKeyInfo(Message.KEY_INFO.createNode(), l, "nnoonnee"); + assertFalse(KeyInfo.isExisting(info)); + assertFalse(KeyInfo.isReadable(info)); + assertFalse(KeyInfo.isWritable(info)); + assertFalse(KeyInfo.isInvocable(info)); + assertFalse(KeyInfo.isInternal(info)); + + info = ForeignAccess.sendKeyInfo(Message.KEY_INFO.createNode(), l, "n2"); + assertTrue(KeyInfo.isExisting(info)); + assertTrue(KeyInfo.isReadable(info)); + assertTrue(KeyInfo.isWritable(info)); + assertFalse(KeyInfo.isInvocable(info)); + assertFalse(KeyInfo.isInternal(info)); + + l = createRListTruffleObject("f=function() {}"); + info = ForeignAccess.sendKeyInfo(Message.KEY_INFO.createNode(), l, "f"); + assertTrue(KeyInfo.isExisting(info)); + assertTrue(KeyInfo.isReadable(info)); + assertTrue(KeyInfo.isWritable(info)); + assertTrue(KeyInfo.isInvocable(info)); + assertFalse(KeyInfo.isInternal(info)); + } + + private TruffleObject createRListTruffleObject(String values) { + PolyglotEngine engine = PolyglotEngine.newBuilder().build(); + Source src = Source.newBuilder("list(" + values + ")").mimeType("text/x-r").name("test.R").build(); + PolyglotEngine.Value result = engine.eval(src); + return result.as(TruffleObject.class); + } + +} diff --git a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/tck/FastRTckTest.java b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/tck/FastRTckTest.java index 17436048570d30bbb17caa57a2451b7d87743e50..967e2d5595db799f6d1657c3975ef1587383b813 100644 --- a/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/tck/FastRTckTest.java +++ b/com.oracle.truffle.r.test/src/com/oracle/truffle/r/test/tck/FastRTckTest.java @@ -152,6 +152,7 @@ public class FastRTckTest extends TruffleTCK { "builtinFunctionValue <- function() `+`\n" + "builtinFunctionType <- function() 'builtin'\n" + "valueWithSource <- function() intValue\n" + + "objectWithKeyInfoAttributes <- function() { list(rw=1, invocable=function(){ 'invoked' }) }\n" + "for (name in ls()) .fastr.interop.export(name, get(name))\n", RSource.Internal.TCK_INIT ); @@ -285,12 +286,6 @@ public class FastRTckTest extends TruffleTCK { return "objectWithValueAndAddProperty"; } - @Override - protected String objectWithKeyInfoAttributes() { - // TODO - return null; - } - @Override protected String callMethod() { return "callMethod"; @@ -511,4 +506,8 @@ public class FastRTckTest extends TruffleTCK { return "valueWithSource"; } + protected String objectWithKeyInfoAttributes() { + return "objectWithKeyInfoAttributes"; + } + } diff --git a/mx.fastr/suite.py b/mx.fastr/suite.py index 7c2d28b476c2d25f8803975ed3497d7e4a23a818..3b6c062c36784b5e72112623a449bf8ef2e801dd 100644 --- a/mx.fastr/suite.py +++ b/mx.fastr/suite.py @@ -29,7 +29,7 @@ suite = { { "name" : "truffle", "subdir" : True, - "version" : "8ee92107d834749dc2355cf48062e08d49e02a67", + "version" : "7d960d6682ef3636cc7455e28132c1e537ea81f0", "urls" : [ {"url" : "https://github.com/graalvm/graal", "kind" : "git"}, {"url" : "https://curio.ssw.jku.at/nexus/content/repositories/snapshots", "kind" : "binary"},