Skip to content

Commit 6fe253f

Browse files
authored
Every builtin type has a common super class (#11861)
This PR is separated from #11589. It only introduces [BuiltinObject](https://github.com/enso-org/enso/blob/8feab15290c45a485815619972a93ab69f34e78a/engine/runtime/src/main/java/org/enso/interpreter/runtime/builtin/BuiltinObject.java), a common supertype for all the builtin types. It does not change any behavior. `BuiltinObject` defines `hasMetaObject`, `getMetaObject`, `hasType` and `getType` messages, so they no longer have to be implemented in subclasses. # Important Notes - Introduce also test [BuiltinsJavaInteropTest](https://github.com/enso-org/enso/blob/0d92891b8eecd3071f0f4f1d8c55524b637d14a8/engine/runtime-integration-tests/src/test/java/org/enso/interpreter/test/builtins/BuiltinsJavaInteropTest.java) - Builtin annotation processor [enforces](1fe2f3e) that every builtin class extend `BuiltinObject` class.
1 parent 910d5a7 commit 6fe253f

File tree

21 files changed

+366
-391
lines changed

21 files changed

+366
-391
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package org.enso.example;
2+
3+
import java.time.LocalDate;
4+
import org.graalvm.polyglot.Value;
5+
6+
public final class PolyglotTestClass {
7+
private PolyglotTestClass() {}
8+
9+
public static boolean isPolyglotDate_Object(Object obj) {
10+
return obj instanceof Value polyglotVal && polyglotVal.isDate();
11+
}
12+
13+
public static boolean isPolyglotDate_LocalDate(LocalDate date) {
14+
return date != null;
15+
}
16+
17+
public static boolean isPolyglotDate_Value(Value val) {
18+
return val != null && val.isDate();
19+
}
20+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package org.enso.interpreter.test.builtins;
2+
3+
import static org.hamcrest.MatcherAssert.assertThat;
4+
import static org.hamcrest.Matchers.is;
5+
6+
import java.io.ByteArrayOutputStream;
7+
import org.enso.test.utils.ContextUtils;
8+
import org.graalvm.polyglot.Context;
9+
import org.junit.After;
10+
import org.junit.AfterClass;
11+
import org.junit.BeforeClass;
12+
import org.junit.Test;
13+
14+
/**
15+
* In these tests, we call Java methods from Enso. Java methods have different signatures that
16+
* accept Enso values in different ways.
17+
*/
18+
public class BuiltinsJavaInteropTest {
19+
private static Context ctx;
20+
private static final ByteArrayOutputStream out = new ByteArrayOutputStream();
21+
22+
@BeforeClass
23+
public static void prepareCtx() {
24+
ctx = ContextUtils.createDefaultContext(out);
25+
}
26+
27+
@AfterClass
28+
public static void disposeCtx() {
29+
ctx.close();
30+
ctx = null;
31+
}
32+
33+
@After
34+
public void resetOutput() {
35+
out.reset();
36+
}
37+
38+
/**
39+
* This test reflects the state of many Java methods in stdlibs that accept Enso values as {@link
40+
* java.lang.Object}. If the Java method has a single argument of type {@link java.lang.Object},
41+
* and we pass {@code Date_Time} in it, we expect the host interop conversion to convert it to
42+
* {@link java.time.LocalDateTime}.
43+
*/
44+
@Test
45+
public void javaMethodAcceptsEnsoTimeOfDay_AsObject() {
46+
var src =
47+
"""
48+
from Standard.Base import Date_Time
49+
polyglot java import org.enso.example.PolyglotTestClass
50+
51+
main =
52+
dt = Date_Time.now
53+
PolyglotTestClass.isPolyglotDate_Object dt
54+
""";
55+
var result = ContextUtils.evalModule(ctx, src);
56+
assertThat(result.asBoolean(), is(true));
57+
}
58+
59+
@Test
60+
public void javaMethodAcceptsEnsoTimeOfDay_AsLocalDate() {
61+
var src =
62+
"""
63+
from Standard.Base import Date_Time
64+
polyglot java import org.enso.example.PolyglotTestClass
65+
66+
main =
67+
dt = Date_Time.now
68+
PolyglotTestClass.isPolyglotDate_LocalDate dt
69+
""";
70+
var result = ContextUtils.evalModule(ctx, src);
71+
assertThat(result.asBoolean(), is(true));
72+
}
73+
74+
@Test
75+
public void javaMethodAcceptsEnsoTimeOfDay_AsValue() {
76+
var src =
77+
"""
78+
from Standard.Base import Date_Time
79+
polyglot java import org.enso.example.PolyglotTestClass
80+
81+
main =
82+
dt = Date_Time.now
83+
PolyglotTestClass.isPolyglotDate_Value dt
84+
""";
85+
var result = ContextUtils.evalModule(ctx, src);
86+
assertThat(result.asBoolean(), is(true));
87+
}
88+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
package org.enso.interpreter.runtime.builtin;
2+
3+
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
4+
import com.oracle.truffle.api.dsl.Bind;
5+
import com.oracle.truffle.api.dsl.Cached;
6+
import com.oracle.truffle.api.dsl.Idempotent;
7+
import com.oracle.truffle.api.dsl.Specialization;
8+
import com.oracle.truffle.api.interop.InteropLibrary;
9+
import com.oracle.truffle.api.interop.UnsupportedMessageException;
10+
import com.oracle.truffle.api.library.ExportLibrary;
11+
import com.oracle.truffle.api.library.ExportMessage;
12+
import com.oracle.truffle.api.nodes.Node;
13+
import org.enso.interpreter.node.expression.builtin.Builtin;
14+
import org.enso.interpreter.runtime.EnsoContext;
15+
import org.enso.interpreter.runtime.data.EnsoObject;
16+
import org.enso.interpreter.runtime.data.Type;
17+
import org.enso.interpreter.runtime.library.dispatch.TypesLibrary;
18+
19+
/**
20+
* Base class for every Enso builtin object. Not type. Note that base class for a builtin type is
21+
* {@link Builtin}.
22+
*
23+
* <p>In other words, this class represents an object of builtin type in a similar way that {@link
24+
* org.enso.interpreter.runtime.data.atom.Atom} represents an object of a non-builtin type.
25+
*/
26+
@ExportLibrary(InteropLibrary.class)
27+
@ExportLibrary(TypesLibrary.class)
28+
public abstract class BuiltinObject extends EnsoObject {
29+
30+
@ExportMessage
31+
public final boolean hasType() {
32+
return true;
33+
}
34+
35+
/**
36+
* Returns the name of the builtin as saved inside {@link Builtins#builtinsByName}. Not fully
37+
* qualified.
38+
*
39+
* @return
40+
*/
41+
protected abstract String builtinName();
42+
43+
protected final Type getBuiltinType(Node node) {
44+
return GetType.uncached(this, node);
45+
}
46+
47+
/**
48+
* Must return false, otherwise if a builtin object is passed to a host method that has a single
49+
* {@code Object} argument, host interop would convert the builtin object to a {@code Map} with
50+
* all its members. Even if the builtin object is, e.g., a number of a date.
51+
*
52+
* <p>Must return false as long as all our stdlib Java methods accept {@code Object} and not
53+
* {@link org.graalvm.polyglot.Value} as arguments comming from Enso.
54+
*/
55+
@ExportMessage
56+
public final boolean hasMembers() {
57+
return false;
58+
}
59+
60+
@ExportMessage
61+
public final Object getMembers(boolean includeInternal) throws UnsupportedMessageException {
62+
throw UnsupportedMessageException.create();
63+
}
64+
65+
@ExportMessage
66+
public final boolean hasMetaObject() {
67+
return true;
68+
}
69+
70+
@ExportMessage(name = "getType", library = TypesLibrary.class)
71+
@ExportMessage(name = "getMetaObject", library = InteropLibrary.class)
72+
public static final class GetType {
73+
74+
GetType() {}
75+
76+
/**
77+
* Caching on class of the receiver - as long as there is the same class, its {@link
78+
* #builtinName()} method will return the same value. Note that we don't want to cache on the
79+
* builtin name, as that would create a separate polymorph cache for every instance of the
80+
* receiver.
81+
*/
82+
@Specialization(
83+
guards = {"cachedReceiverClass == receiver.getClass()", "getCtx(node) == cachedCtx"},
84+
limit = "1")
85+
public static Type doItCached(
86+
BuiltinObject receiver,
87+
@Bind("$node") Node node,
88+
@Cached("receiver.getClass()") Class<? extends BuiltinObject> cachedReceiverClass,
89+
@Cached(value = "getCtx(node)", allowUncached = true) EnsoContext cachedCtx,
90+
@Cached(value = "getBuiltinType(receiver, cachedCtx)", allowUncached = true)
91+
Builtin cachedBuiltinType) {
92+
return cachedBuiltinType.getType();
93+
}
94+
95+
@Specialization(replaces = "doItCached")
96+
public static Type uncached(BuiltinObject receiver, @Bind("$node") Node node) {
97+
var ctx = getCtx(node);
98+
return getBuiltinType(receiver, ctx).getType();
99+
}
100+
101+
@TruffleBoundary
102+
public static Builtin getBuiltinType(BuiltinObject receiver, EnsoContext ctx) {
103+
return ctx.getBuiltins().getBuiltinType(receiver.builtinName());
104+
}
105+
106+
@Idempotent
107+
public static EnsoContext getCtx(Node node) {
108+
return EnsoContext.get(node);
109+
}
110+
}
111+
}

engine/runtime/src/main/java/org/enso/interpreter/runtime/data/EnsoDate.java

+7-26
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,31 @@
11
package org.enso.interpreter.runtime.data;
22

33
import com.oracle.truffle.api.CompilerDirectives;
4-
import com.oracle.truffle.api.dsl.Bind;
54
import com.oracle.truffle.api.interop.InteropLibrary;
65
import com.oracle.truffle.api.interop.UnsupportedMessageException;
76
import com.oracle.truffle.api.library.ExportLibrary;
87
import com.oracle.truffle.api.library.ExportMessage;
9-
import com.oracle.truffle.api.nodes.Node;
108
import java.time.DateTimeException;
119
import java.time.LocalDate;
1210
import java.time.LocalTime;
1311
import org.enso.interpreter.dsl.Builtin;
14-
import org.enso.interpreter.runtime.EnsoContext;
15-
import org.enso.interpreter.runtime.library.dispatch.TypesLibrary;
12+
import org.enso.interpreter.runtime.builtin.BuiltinObject;
1613
import org.enso.polyglot.common_utils.Core_Date_Utils;
1714

1815
@ExportLibrary(InteropLibrary.class)
19-
@ExportLibrary(TypesLibrary.class)
2016
@Builtin(pkg = "date", name = "Date", stdlibName = "Standard.Base.Data.Time.Date.Date")
21-
public final class EnsoDate extends EnsoObject {
17+
public final class EnsoDate extends BuiltinObject {
2218
private final LocalDate date;
2319

2420
public EnsoDate(LocalDate date) {
2521
this.date = date;
2622
}
2723

24+
@Override
25+
protected String builtinName() {
26+
return "Date";
27+
}
28+
2829
@Builtin.Method(description = "Return current Date", autoRegister = false)
2930
@CompilerDirectives.TruffleBoundary
3031
public static EnsoDate today() {
@@ -77,26 +78,6 @@ LocalTime asTime() throws UnsupportedMessageException {
7778
throw UnsupportedMessageException.create();
7879
}
7980

80-
@ExportMessage
81-
Type getMetaObject(@Bind("$node") Node node) {
82-
return EnsoContext.get(node).getBuiltins().date();
83-
}
84-
85-
@ExportMessage
86-
boolean hasMetaObject() {
87-
return true;
88-
}
89-
90-
@ExportMessage
91-
boolean hasType() {
92-
return true;
93-
}
94-
95-
@ExportMessage
96-
Type getType(@Bind("$node") Node node) {
97-
return EnsoContext.get(node).getBuiltins().date();
98-
}
99-
10081
@CompilerDirectives.TruffleBoundary
10182
@ExportMessage
10283
@Override

engine/runtime/src/main/java/org/enso/interpreter/runtime/data/EnsoDateTime.java

+7-27
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,37 @@
11
package org.enso.interpreter.runtime.data;
22

33
import com.oracle.truffle.api.CompilerDirectives;
4-
import com.oracle.truffle.api.dsl.Bind;
54
import com.oracle.truffle.api.interop.InteropLibrary;
65
import com.oracle.truffle.api.interop.UnsupportedMessageException;
7-
import com.oracle.truffle.api.library.CachedLibrary;
86
import com.oracle.truffle.api.library.ExportLibrary;
97
import com.oracle.truffle.api.library.ExportMessage;
10-
import com.oracle.truffle.api.nodes.Node;
118
import java.time.DateTimeException;
129
import java.time.LocalDate;
1310
import java.time.LocalTime;
1411
import java.time.ZoneId;
1512
import java.time.ZonedDateTime;
1613
import org.enso.interpreter.dsl.Builtin;
17-
import org.enso.interpreter.runtime.EnsoContext;
14+
import org.enso.interpreter.runtime.builtin.BuiltinObject;
1815
import org.enso.interpreter.runtime.data.text.Text;
19-
import org.enso.interpreter.runtime.library.dispatch.TypesLibrary;
2016
import org.enso.polyglot.common_utils.Core_Date_Utils;
2117

2218
@ExportLibrary(InteropLibrary.class)
23-
@ExportLibrary(TypesLibrary.class)
2419
@Builtin(
2520
pkg = "date",
2621
name = "DateTime",
2722
stdlibName = "Standard.Base.Data.Time.Date_Time.Date_Time")
28-
public final class EnsoDateTime extends EnsoObject {
23+
public final class EnsoDateTime extends BuiltinObject {
2924
private final ZonedDateTime dateTime;
3025

3126
public EnsoDateTime(ZonedDateTime dateTime) {
3227
this.dateTime = dateTime;
3328
}
3429

30+
@Override
31+
protected String builtinName() {
32+
return "Date_Time";
33+
}
34+
3535
@Builtin.Method(
3636
name = "epoch_start",
3737
description = "Return the Enso start of the Epoch",
@@ -205,26 +205,6 @@ ZoneId asTimeZone() {
205205
return dateTime.getZone();
206206
}
207207

208-
@ExportMessage
209-
Type getMetaObject(@CachedLibrary("this") InteropLibrary thisLib) {
210-
return EnsoContext.get(thisLib).getBuiltins().dateTime();
211-
}
212-
213-
@ExportMessage
214-
boolean hasMetaObject() {
215-
return true;
216-
}
217-
218-
@ExportMessage
219-
boolean hasType() {
220-
return true;
221-
}
222-
223-
@ExportMessage
224-
Type getType(@Bind("$node") Node node) {
225-
return EnsoContext.get(node).getBuiltins().dateTime();
226-
}
227-
228208
@ExportMessage
229209
@CompilerDirectives.TruffleBoundary
230210
@Override

0 commit comments

Comments
 (0)