-
Notifications
You must be signed in to change notification settings - Fork 326
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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.
- Loading branch information
Showing
21 changed files
with
366 additions
and
391 deletions.
There are no files selected for viewing
20 changes: 20 additions & 0 deletions
20
engine/runtime-integration-tests/src/test/java/org/enso/example/PolyglotTestClass.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package org.enso.example; | ||
|
||
import java.time.LocalDate; | ||
import org.graalvm.polyglot.Value; | ||
|
||
public final class PolyglotTestClass { | ||
private PolyglotTestClass() {} | ||
|
||
public static boolean isPolyglotDate_Object(Object obj) { | ||
return obj instanceof Value polyglotVal && polyglotVal.isDate(); | ||
} | ||
|
||
public static boolean isPolyglotDate_LocalDate(LocalDate date) { | ||
return date != null; | ||
} | ||
|
||
public static boolean isPolyglotDate_Value(Value val) { | ||
return val != null && val.isDate(); | ||
} | ||
} |
88 changes: 88 additions & 0 deletions
88
...ation-tests/src/test/java/org/enso/interpreter/test/builtins/BuiltinsJavaInteropTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
package org.enso.interpreter.test.builtins; | ||
|
||
import static org.hamcrest.MatcherAssert.assertThat; | ||
import static org.hamcrest.Matchers.is; | ||
|
||
import java.io.ByteArrayOutputStream; | ||
import org.enso.test.utils.ContextUtils; | ||
import org.graalvm.polyglot.Context; | ||
import org.junit.After; | ||
import org.junit.AfterClass; | ||
import org.junit.BeforeClass; | ||
import org.junit.Test; | ||
|
||
/** | ||
* In these tests, we call Java methods from Enso. Java methods have different signatures that | ||
* accept Enso values in different ways. | ||
*/ | ||
public class BuiltinsJavaInteropTest { | ||
private static Context ctx; | ||
private static final ByteArrayOutputStream out = new ByteArrayOutputStream(); | ||
|
||
@BeforeClass | ||
public static void prepareCtx() { | ||
ctx = ContextUtils.createDefaultContext(out); | ||
} | ||
|
||
@AfterClass | ||
public static void disposeCtx() { | ||
ctx.close(); | ||
ctx = null; | ||
} | ||
|
||
@After | ||
public void resetOutput() { | ||
out.reset(); | ||
} | ||
|
||
/** | ||
* This test reflects the state of many Java methods in stdlibs that accept Enso values as {@link | ||
* java.lang.Object}. If the Java method has a single argument of type {@link java.lang.Object}, | ||
* and we pass {@code Date_Time} in it, we expect the host interop conversion to convert it to | ||
* {@link java.time.LocalDateTime}. | ||
*/ | ||
@Test | ||
public void javaMethodAcceptsEnsoTimeOfDay_AsObject() { | ||
var src = | ||
""" | ||
from Standard.Base import Date_Time | ||
polyglot java import org.enso.example.PolyglotTestClass | ||
main = | ||
dt = Date_Time.now | ||
PolyglotTestClass.isPolyglotDate_Object dt | ||
"""; | ||
var result = ContextUtils.evalModule(ctx, src); | ||
assertThat(result.asBoolean(), is(true)); | ||
} | ||
|
||
@Test | ||
public void javaMethodAcceptsEnsoTimeOfDay_AsLocalDate() { | ||
var src = | ||
""" | ||
from Standard.Base import Date_Time | ||
polyglot java import org.enso.example.PolyglotTestClass | ||
main = | ||
dt = Date_Time.now | ||
PolyglotTestClass.isPolyglotDate_LocalDate dt | ||
"""; | ||
var result = ContextUtils.evalModule(ctx, src); | ||
assertThat(result.asBoolean(), is(true)); | ||
} | ||
|
||
@Test | ||
public void javaMethodAcceptsEnsoTimeOfDay_AsValue() { | ||
var src = | ||
""" | ||
from Standard.Base import Date_Time | ||
polyglot java import org.enso.example.PolyglotTestClass | ||
main = | ||
dt = Date_Time.now | ||
PolyglotTestClass.isPolyglotDate_Value dt | ||
"""; | ||
var result = ContextUtils.evalModule(ctx, src); | ||
assertThat(result.asBoolean(), is(true)); | ||
} | ||
} |
111 changes: 111 additions & 0 deletions
111
engine/runtime/src/main/java/org/enso/interpreter/runtime/builtin/BuiltinObject.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
package org.enso.interpreter.runtime.builtin; | ||
|
||
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; | ||
import com.oracle.truffle.api.dsl.Bind; | ||
import com.oracle.truffle.api.dsl.Cached; | ||
import com.oracle.truffle.api.dsl.Idempotent; | ||
import com.oracle.truffle.api.dsl.Specialization; | ||
import com.oracle.truffle.api.interop.InteropLibrary; | ||
import com.oracle.truffle.api.interop.UnsupportedMessageException; | ||
import com.oracle.truffle.api.library.ExportLibrary; | ||
import com.oracle.truffle.api.library.ExportMessage; | ||
import com.oracle.truffle.api.nodes.Node; | ||
import org.enso.interpreter.node.expression.builtin.Builtin; | ||
import org.enso.interpreter.runtime.EnsoContext; | ||
import org.enso.interpreter.runtime.data.EnsoObject; | ||
import org.enso.interpreter.runtime.data.Type; | ||
import org.enso.interpreter.runtime.library.dispatch.TypesLibrary; | ||
|
||
/** | ||
* Base class for every Enso builtin object. Not type. Note that base class for a builtin type is | ||
* {@link Builtin}. | ||
* | ||
* <p>In other words, this class represents an object of builtin type in a similar way that {@link | ||
* org.enso.interpreter.runtime.data.atom.Atom} represents an object of a non-builtin type. | ||
*/ | ||
@ExportLibrary(InteropLibrary.class) | ||
@ExportLibrary(TypesLibrary.class) | ||
public abstract class BuiltinObject extends EnsoObject { | ||
|
||
@ExportMessage | ||
public final boolean hasType() { | ||
return true; | ||
} | ||
|
||
/** | ||
* Returns the name of the builtin as saved inside {@link Builtins#builtinsByName}. Not fully | ||
* qualified. | ||
* | ||
* @return | ||
*/ | ||
protected abstract String builtinName(); | ||
|
||
protected final Type getBuiltinType(Node node) { | ||
return GetType.uncached(this, node); | ||
} | ||
|
||
/** | ||
* Must return false, otherwise if a builtin object is passed to a host method that has a single | ||
* {@code Object} argument, host interop would convert the builtin object to a {@code Map} with | ||
* all its members. Even if the builtin object is, e.g., a number of a date. | ||
* | ||
* <p>Must return false as long as all our stdlib Java methods accept {@code Object} and not | ||
* {@link org.graalvm.polyglot.Value} as arguments comming from Enso. | ||
*/ | ||
@ExportMessage | ||
public final boolean hasMembers() { | ||
return false; | ||
} | ||
|
||
@ExportMessage | ||
public final Object getMembers(boolean includeInternal) throws UnsupportedMessageException { | ||
throw UnsupportedMessageException.create(); | ||
} | ||
|
||
@ExportMessage | ||
public final boolean hasMetaObject() { | ||
return true; | ||
} | ||
|
||
@ExportMessage(name = "getType", library = TypesLibrary.class) | ||
@ExportMessage(name = "getMetaObject", library = InteropLibrary.class) | ||
public static final class GetType { | ||
|
||
GetType() {} | ||
|
||
/** | ||
* Caching on class of the receiver - as long as there is the same class, its {@link | ||
* #builtinName()} method will return the same value. Note that we don't want to cache on the | ||
* builtin name, as that would create a separate polymorph cache for every instance of the | ||
* receiver. | ||
*/ | ||
@Specialization( | ||
guards = {"cachedReceiverClass == receiver.getClass()", "getCtx(node) == cachedCtx"}, | ||
limit = "1") | ||
public static Type doItCached( | ||
BuiltinObject receiver, | ||
@Bind("$node") Node node, | ||
@Cached("receiver.getClass()") Class<? extends BuiltinObject> cachedReceiverClass, | ||
@Cached(value = "getCtx(node)", allowUncached = true) EnsoContext cachedCtx, | ||
@Cached(value = "getBuiltinType(receiver, cachedCtx)", allowUncached = true) | ||
Builtin cachedBuiltinType) { | ||
return cachedBuiltinType.getType(); | ||
} | ||
|
||
@Specialization(replaces = "doItCached") | ||
public static Type uncached(BuiltinObject receiver, @Bind("$node") Node node) { | ||
var ctx = getCtx(node); | ||
return getBuiltinType(receiver, ctx).getType(); | ||
} | ||
|
||
@TruffleBoundary | ||
public static Builtin getBuiltinType(BuiltinObject receiver, EnsoContext ctx) { | ||
return ctx.getBuiltins().getBuiltinType(receiver.builtinName()); | ||
} | ||
|
||
@Idempotent | ||
public static EnsoContext getCtx(Node node) { | ||
return EnsoContext.get(node); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.