Skip to content

Commit b987e96

Browse files
committed
Normalize the values that R may return via interop
1 parent ef6ad1e commit b987e96

File tree

17 files changed

+362
-90
lines changed

17 files changed

+362
-90
lines changed

CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,17 @@
11

2+
# 1.0 RC 5
3+
4+
Updates in interop:
5+
6+
* R code evaluated via interop never returns a Java primitive type, but always a vector
7+
* Vectors of size 1 that do not contain NA can be unboxed
8+
* Sending the READ message to an atomic R vector (array subscript in most languages) gives
9+
* Java primitive type as long as the value is not `NA`
10+
* a special value that responds to `IS_NULL` with `true`. If this value is passed back to R it behaves as `NA` again
11+
* Note that sending the READ message to a list, environment, or other heterogenous data structure never gives atomic Java type but a primitive R vector
12+
13+
# 1.0 RC 4
14+
215
# 1.0 RC 3
316

417
Added missing R builtins and C API

com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RAbstractVectorAccessFactory.java

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,14 @@
5151
import com.oracle.truffle.r.nodes.access.vector.ExtractVectorNode;
5252
import com.oracle.truffle.r.nodes.access.vector.ReplaceVectorNode;
5353
import com.oracle.truffle.r.nodes.access.vector.ReplaceVectorNodeGen;
54+
import com.oracle.truffle.r.nodes.builtin.base.IsNA;
55+
import com.oracle.truffle.r.nodes.builtin.base.IsNANodeGen;
5456
import com.oracle.truffle.r.nodes.control.RLengthNode;
5557
import com.oracle.truffle.r.runtime.RRuntime;
5658
import com.oracle.truffle.r.runtime.data.NativeDataAccess;
5759
import com.oracle.truffle.r.runtime.data.RLogical;
5860
import com.oracle.truffle.r.runtime.data.RRaw;
5961
import com.oracle.truffle.r.runtime.data.RScalar;
60-
import com.oracle.truffle.r.runtime.data.RString;
6162
import com.oracle.truffle.r.runtime.data.model.RAbstractAtomicVector;
6263
import com.oracle.truffle.r.runtime.data.model.RAbstractLogicalVector;
6364
import com.oracle.truffle.r.runtime.data.model.RAbstractRawVector;
@@ -79,6 +80,9 @@ public final SourceSection getSourceSection() {
7980
}
8081
}
8182

83+
/**
84+
* Implements interop messages for all {@link RAbstractAtomicVector} subclasses.
85+
*/
8286
public final class RAbstractVectorAccessFactory implements StandardFactory {
8387

8488
abstract static class VectorReadImplNode extends InteropRootNode {
@@ -127,7 +131,7 @@ private Object read(Object receiver, Object[] positions) {
127131
Object value = extract.apply(receiver, positions, RLogical.TRUE, RLogical.TRUE);
128132
if (r2Foreign == null) {
129133
CompilerDirectives.transferToInterpreterAndInvalidate();
130-
r2Foreign = insert(R2ForeignNodeGen.create());
134+
r2Foreign = insert(R2Foreign.createNoBox());
131135
}
132136
return r2Foreign.execute(value);
133137
}
@@ -324,26 +328,23 @@ public Object execute(VirtualFrame frame) {
324328
@Override
325329
public CallTarget accessIsBoxed() {
326330
return Truffle.getRuntime().createCallTarget(new InteropRootNode() {
331+
@Child IsNA isNANode = IsNANodeGen.create();
332+
private final ConditionProfile isEmpty = ConditionProfile.createBinaryProfile();
333+
327334
@Override
328335
public Object execute(VirtualFrame frame) {
329336
RAbstractVector arg = (RAbstractVector) ForeignAccess.getReceiver(frame);
330-
return arg.getLength() == 1 && isUnBoxable(arg);
337+
if (isEmpty.profile(arg.getLength() == 0)) {
338+
return false;
339+
}
340+
Object o = arg.getDataAtAsObject(0);
341+
return arg.getLength() == 1 && !isNA(isNANode, o);
331342
}
332343
});
333344
}
334345

335-
private static boolean isUnBoxable(RAbstractVector vector) {
336-
Object o = vector.getDataAtAsObject(0);
337-
return isPrimitive(o);
338-
}
339-
340-
private static boolean isPrimitive(Object element) {
341-
if (element == null) {
342-
return false;
343-
}
344-
final Class<?> elementType = element.getClass();
345-
return elementType == String.class || elementType == Character.class || elementType == Boolean.class || elementType == Byte.class || elementType == Short.class ||
346-
elementType == Integer.class || elementType == Long.class || elementType == Float.class || elementType == Double.class;
346+
private static boolean isNA(IsNA isNANode, Object value) {
347+
return RRuntime.fromLogical((Byte) isNANode.execute(value));
347348
}
348349

349350
@Override
@@ -382,14 +383,21 @@ public Object execute(VirtualFrame frame) {
382383
@Override
383384
public CallTarget accessUnbox() {
384385
return Truffle.getRuntime().createCallTarget(new InteropRootNode() {
386+
@Child IsNA isNANode = IsNANodeGen.create();
387+
private final ConditionProfile isLogical = ConditionProfile.createBinaryProfile();
388+
385389
@Override
386390
public Object execute(VirtualFrame frame) {
387391
RAbstractVector arg = (RAbstractVector) ForeignAccess.getReceiver(frame);
388392
if (arg.getLength() == 1) {
389-
return arg.getDataAtAsObject(0);
390-
} else {
391-
throw UnsupportedMessageException.raise(Message.UNBOX);
393+
Object value = arg.getDataAtAsObject(0);
394+
if (isLogical.profile(arg instanceof RAbstractLogicalVector)) {
395+
return RLogicalMR.unboxLogical((Byte) value);
396+
} else if (!isNA(isNANode, value)) {
397+
return value;
398+
}
392399
}
400+
throw UnsupportedMessageException.raise(Message.UNBOX);
393401
}
394402
});
395403
}

com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RForeignAccessFactoryImpl.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import com.oracle.truffle.r.runtime.data.RFunction;
3939
import com.oracle.truffle.r.runtime.data.RInteger;
4040
import com.oracle.truffle.r.runtime.data.RInteropScalar;
41+
import com.oracle.truffle.r.runtime.data.RInteropScalar.RInteropNA;
4142
import com.oracle.truffle.r.runtime.data.RPairList;
4243
import com.oracle.truffle.r.runtime.data.RList;
4344
import com.oracle.truffle.r.runtime.data.RLogical;
@@ -118,6 +119,8 @@ public ForeignAccess getForeignAccess(RTruffleObject obj) {
118119
return RMissingMRForeign.ACCESS;
119120
} else if (obj instanceof REmpty) {
120121
return REmptyMRForeign.ACCESS;
122+
} else if (obj instanceof RInteropNA) {
123+
return RInteropNAMRForeign.ACCESS;
121124
} else if (obj instanceof RAbstractAtomicVector) {
122125
return ForeignAccess.create(new RAbstractVectorAccessFactory(), new RAbstractVectorAccessFactory.Check());
123126
} else {
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 3 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 3 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 3 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
package com.oracle.truffle.r.engine.interop;
24+
25+
import com.oracle.truffle.api.interop.CanResolve;
26+
import com.oracle.truffle.api.interop.MessageResolution;
27+
import com.oracle.truffle.api.interop.Resolve;
28+
import com.oracle.truffle.api.interop.TruffleObject;
29+
import com.oracle.truffle.api.nodes.Node;
30+
import com.oracle.truffle.r.runtime.data.RInteropScalar.RInteropNA;
31+
32+
@MessageResolution(receiverType = RInteropNA.class)
33+
public class RInteropNAMR {
34+
@Resolve(message = "IS_NULL")
35+
public abstract static class RInteropNAIsNullNode extends Node {
36+
protected Object access(@SuppressWarnings("unused") RInteropNA receiver) {
37+
return true;
38+
}
39+
}
40+
41+
@CanResolve
42+
public abstract static class RInteropNACheck extends Node {
43+
protected static boolean test(TruffleObject receiver) {
44+
return receiver instanceof RInteropNA;
45+
}
46+
}
47+
}

com.oracle.truffle.r.engine/src/com/oracle/truffle/r/engine/interop/RLogicalMR.java

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,22 @@
2323
package com.oracle.truffle.r.engine.interop;
2424

2525
import com.oracle.truffle.api.interop.CanResolve;
26+
import com.oracle.truffle.api.interop.Message;
2627
import com.oracle.truffle.api.interop.MessageResolution;
2728
import com.oracle.truffle.api.interop.Resolve;
2829
import com.oracle.truffle.api.interop.TruffleObject;
30+
import com.oracle.truffle.api.interop.UnsupportedMessageException;
2931
import com.oracle.truffle.api.nodes.Node;
32+
import com.oracle.truffle.r.runtime.RRuntime;
3033
import com.oracle.truffle.r.runtime.data.RLogical;
34+
import com.oracle.truffle.r.runtime.data.model.RAbstractLogicalVector;
3135

3236
@MessageResolution(receiverType = RLogical.class)
3337
public class RLogicalMR {
3438
@Resolve(message = "IS_BOXED")
3539
public abstract static class RLogicalIsBoxedNode extends Node {
3640
protected Object access(@SuppressWarnings("unused") RLogical receiver) {
37-
return true;
41+
return !RRuntime.isNA(receiver.getValue());
3842
}
3943
}
4044

@@ -47,8 +51,8 @@ protected Object access(@SuppressWarnings("unused") RLogical receiver, @Suppress
4751

4852
@Resolve(message = "UNBOX")
4953
public abstract static class RLogicalUnboxNode extends Node {
50-
protected byte access(RLogical receiver) {
51-
return receiver.getValue();
54+
protected Object access(RLogical receiver) {
55+
return unboxLogical(receiver.getValue());
5256
}
5357
}
5458

@@ -58,4 +62,15 @@ protected static boolean test(TruffleObject receiver) {
5862
return receiver instanceof RLogical;
5963
}
6064
}
65+
66+
public static boolean isUnboxable(byte value) {
67+
return !RRuntime.isNA(value);
68+
}
69+
70+
public static Object unboxLogical(byte value) {
71+
if (!isUnboxable(value)) {
72+
throw UnsupportedMessageException.raise(Message.UNBOX);
73+
}
74+
return RRuntime.fromLogical(value);
75+
}
6176
}

com.oracle.truffle.r.nodes.builtin/src/com/oracle/truffle/r/nodes/builtin/fastr/FastRInterop.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,6 @@
104104
import com.oracle.truffle.r.runtime.interop.Foreign2R;
105105
import com.oracle.truffle.r.runtime.interop.ForeignArray2R;
106106
import com.oracle.truffle.r.runtime.interop.R2Foreign;
107-
import com.oracle.truffle.r.runtime.interop.R2ForeignNodeGen;
108107

109108
public class FastRInterop {
110109

@@ -818,7 +817,7 @@ public Object toArray(RInteropScalar ri, @SuppressWarnings("unused") RMissing cl
818817
}
819818

820819
protected R2Foreign createR2Foreign() {
821-
return R2Foreign.create();
820+
return R2Foreign.createNoBox();
822821
}
823822

824823
private static int[] getDim(boolean flat, RAbstractVector vec) {

com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/data/RInteropScalar.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
import com.oracle.truffle.api.CompilerAsserts;
2626
import com.oracle.truffle.api.CompilerDirectives.ValueType;
27+
import com.oracle.truffle.r.runtime.RRuntime;
2728
import com.oracle.truffle.r.runtime.RType;
2829

2930
@ValueType
@@ -227,4 +228,26 @@ public Class<?> getJavaType() {
227228
return Short.TYPE;
228229
}
229230
}
231+
232+
/**
233+
* Represents an {@code NA} value passed to the interop. This value should never appear in the
234+
* FastR execution, it is only passed to interop and converted back to primitive value if passed
235+
* back to FastR.
236+
*/
237+
public static final class RInteropNA implements RTruffleObject {
238+
public static final RInteropNA INT = new RInteropNA(RRuntime.INT_NA);
239+
public static final RInteropNA DOUBLE = new RInteropNA(RRuntime.DOUBLE_NA);
240+
public static final RInteropNA STRING = new RInteropNA(RRuntime.STRING_NA);
241+
public static final RInteropNA LOGICAL = new RInteropNA(RRuntime.LOGICAL_NA);
242+
243+
private final Object value;
244+
245+
public RInteropNA(Object value) {
246+
this.value = value;
247+
}
248+
249+
public Object getValue() {
250+
return value;
251+
}
252+
}
230253
}

com.oracle.truffle.r.runtime/src/com/oracle/truffle/r/runtime/interop/Foreign2R.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import com.oracle.truffle.r.runtime.RInternalError;
3434
import com.oracle.truffle.api.nodes.Node;
3535
import com.oracle.truffle.r.runtime.RRuntime;
36+
import com.oracle.truffle.r.runtime.data.RInteropScalar.RInteropNA;
3637
import com.oracle.truffle.r.runtime.data.RNull;
3738
import com.oracle.truffle.r.runtime.nodes.RBaseNode;
3839

@@ -80,6 +81,11 @@ public String doChar(char obj) {
8081
return ((Character) obj).toString();
8182
}
8283

84+
@Specialization
85+
public Object doInteropNA(RInteropNA interopNA) {
86+
return interopNA.getValue();
87+
}
88+
8389
@Specialization(guards = "isNull(obj)")
8490
public RNull doNull(@SuppressWarnings("unused") Object obj) {
8591
return RNull.instance;

0 commit comments

Comments
 (0)