-
Notifications
You must be signed in to change notification settings - Fork 327
Builtins expose Enso methods #11687
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Akirathan
merged 58 commits into
develop
from
wip/akirathan/11589-builtins-expose-methods
Jan 13, 2025
Merged
Builtins expose Enso methods #11687
Changes from 24 commits
Commits
Show all changes
58 commits
Select commit
Hold shift + click to select a range
f9a00c6
Add BuiltinsExposeMethodsTest
Akirathan 587904a
typo in docs
Akirathan bdbe60f
Test iteartes all the builtins and checks that all methods are invoca…
Akirathan 7f0d4bf
Add TypesExposeConstructorsTest
Akirathan 59710b9
Merge branch 'develop' into wip/akirathan/11589-builtins-expose-methods
Akirathan 48005e6
Naive implementation of BuiltinObject base class.
Akirathan 477de23
Ref extends BuiltinObject
Akirathan 86c360b
Builtin type anot processor ensures that a class must extend BuiltinO…
Akirathan bac459e
Enrich BuiltinsExposeMethodsTest
Akirathan a9681e3
All builtin types extend BuiltinObject
Akirathan ab7a246
Text is BuiltinObject
Akirathan 4c9ea78
EnsoBigInteger is BuiltinObject
Akirathan e806f9f
BuiltinObject does no asserts in constructor
Akirathan 3b98c60
ArrayProxy is BuiltinObject
Akirathan 643aa36
Test skips host values and Nothing
Akirathan 5a8ef4e
fmt
Akirathan 7cd6469
Remove outdated test.
Akirathan 475819a
EqualsComplexNode: Timezone and duration are not treated as object wi…
Akirathan 7856324
Fix DebuggingEnsoTest - Date in js is date time, not date
Akirathan a00ef60
Fix DateTest - ensoDate now has members
Akirathan 0658ca5
Add interop.readMember test to BuiltinsExposeMethodsTest
Akirathan c1d8f41
VectorSortTest is executed in context
Akirathan 47125a7
Add tests that invoke builtin methods on particular builtin types
Akirathan e8ed818
member methods on BuiltinObject are behind TruffleBoundary
Akirathan 7dfb427
Cache builtin type in BuiltinObject.
Akirathan 193db45
Reuse context in DebuggingEnsoTest.
Akirathan 934afe1
Merge branch 'develop' into wip/akirathan/11589-builtins-expose-methods
Akirathan 8acb04a
Reuse hardcoded builtinName constants from annotation
Akirathan f187211
Move BuiltinObject to package org.enso.interpreter.runtime.builtin
Akirathan 0cc0b72
Fix FQN of BuiltinObject in the annotation processor
Akirathan 6f31ebf
Make exported messages on BuiltinObject final
Akirathan 2462523
Update generated class names in Builtins.
Akirathan e441fab
Exported messages in BuiltinObject are not behind TruffleBoundary
Akirathan a77691c
[WIP] Add BuiltinsJavaInteropTest
Akirathan b6ec2ae
Storage.vectorizedOrFallbackBinaryMap accepts Value as parameter
Akirathan 752ac90
Builtin types expose methods, not BuiltinObject
Akirathan 3a3f302
BuiltinObject has no members
Akirathan 6f40c65
Remove test Text.is_empty
Akirathan c34408b
Fix tests - invocation is done via static methods
Akirathan b21384a
Type.InvokeMember uses InvokeFunctionNode instead of UnresolvedSymbol
Akirathan 70a18c6
fmt
Akirathan 3969177
Type.InvokeMember prepends receiver argument
Akirathan a001eab
Test invocation of File.path
Akirathan def1f6b
Test invocation of Vector.to_text
Akirathan eb125ba
Method fetching is done behind truffle boundary
Akirathan 0b5758c
Reassigning @CompilationFinal field should deoptimize
JaroslavTulach 7f8b62c
Merge branch 'develop' into wip/akirathan/11589-builtins-expose-methods
Akirathan 50b43cd
Fixes after merge
Akirathan 8ee8ea2
Add TruffleBoundary
Akirathan 139952e
Revert DateTest
Akirathan 3edfc9c
Fix PrivateConstructor test
Akirathan 82701e7
Methods on Type are internal members
Akirathan 29de01a
fmt
Akirathan 83f4bcd
Add TruffleBoundary
Akirathan bfcaca9
Revert Storage to develop
Akirathan 4d6fce1
Remove FIXME comment
Akirathan 0c97057
Simplify usages of Ref.get and Pth.root
Akirathan 523321c
Merge branch 'develop' into wip/akirathan/11589-builtins-expose-methods
Akirathan File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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 hidden or 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
147 changes: 147 additions & 0 deletions
147
...ion-tests/src/test/java/org/enso/interpreter/test/builtins/BuiltinsExposeMethodsTest.java
This file contains hidden or 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,147 @@ | ||
package org.enso.interpreter.test.builtins; | ||
|
||
import static org.hamcrest.MatcherAssert.assertThat; | ||
import static org.hamcrest.Matchers.is; | ||
import static org.hamcrest.Matchers.notNullValue; | ||
|
||
import com.oracle.truffle.api.interop.InteropLibrary; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
import org.enso.interpreter.runtime.data.Type; | ||
import org.enso.interpreter.runtime.library.dispatch.TypeOfNode; | ||
import org.enso.interpreter.test.ValuesGenerator; | ||
import org.enso.interpreter.test.ValuesGenerator.Language; | ||
import org.enso.test.utils.ContextUtils; | ||
import org.graalvm.polyglot.Context; | ||
import org.graalvm.polyglot.Value; | ||
import org.junit.AfterClass; | ||
import org.junit.Test; | ||
import org.junit.runner.RunWith; | ||
import org.junit.runners.Parameterized; | ||
import org.junit.runners.Parameterized.Parameters; | ||
|
||
/** | ||
* Gathers all the builtin objects from {@link ValuesGenerator}. From their types, gathers all their | ||
* methods via their {@link org.enso.interpreter.runtime.scope.ModuleScope definition scope} and | ||
* checks that {@link Value#canInvokeMember(String)} returns true. | ||
*/ | ||
@RunWith(Parameterized.class) | ||
public class BuiltinsExposeMethodsTest { | ||
private static Context ctx; | ||
|
||
private final ValueWithType valueWithType; | ||
|
||
public BuiltinsExposeMethodsTest(ValueWithType valueWithType) { | ||
this.valueWithType = valueWithType; | ||
} | ||
|
||
private static Context ctx() { | ||
if (ctx == null) { | ||
ctx = ContextUtils.createDefaultContext(); | ||
} | ||
return ctx; | ||
} | ||
|
||
@Parameters(name = "{index}: {0}") | ||
public static Iterable<ValueWithType> generateBuiltinObjects() { | ||
var valuesGenerator = ValuesGenerator.create(ctx(), Language.ENSO); | ||
var builtinObjectsWithTypes = new ArrayList<ValueWithType>(); | ||
ContextUtils.executeInContext( | ||
ctx(), | ||
() -> { | ||
valuesGenerator.allValues().stream() | ||
.map(val -> new ValueWithType(val, getType(val))) | ||
.filter(valWithType -> !shouldSkipType(valWithType.type)) | ||
.filter(valWithType -> !isPrimitive(valWithType.value)) | ||
.filter(valWithType -> !isHostValue(valWithType.value)) | ||
.forEach(builtinObjectsWithTypes::add); | ||
return null; | ||
}); | ||
return builtinObjectsWithTypes; | ||
} | ||
|
||
private static Type getType(Value object) { | ||
var unwrapped = ContextUtils.unwrapValue(ctx(), object); | ||
return TypeOfNode.getUncached().findTypeOrNull(unwrapped); | ||
} | ||
|
||
@AfterClass | ||
public static void disposeCtx() { | ||
if (ctx != null) { | ||
ctx.close(); | ||
ctx = null; | ||
} | ||
} | ||
|
||
@Test | ||
public void builtinExposeMethods() { | ||
ContextUtils.executeInContext( | ||
ctx(), | ||
() -> { | ||
assertThat(valueWithType, is(notNullValue())); | ||
assertThat(valueWithType.type.isBuiltin(), is(true)); | ||
var typeDefScope = valueWithType.type.getDefinitionScope(); | ||
var methodsDefinedInScope = typeDefScope.getMethodsForType(valueWithType.type); | ||
if (methodsDefinedInScope != null) { | ||
for (var methodInScope : methodsDefinedInScope) { | ||
var methodName = methodInScope.getName(); | ||
if (methodName.contains(".")) { | ||
var items = methodName.split("\\."); | ||
methodName = items[items.length - 1]; | ||
} | ||
assertThat( | ||
"Builtin type " + valueWithType.type.getQualifiedName() + " should have members", | ||
valueWithType.value.hasMembers(), | ||
is(true)); | ||
assertThat( | ||
"Member " + methodName + " should be present", | ||
valueWithType.value.hasMember(methodName), | ||
is(true)); | ||
assertThat( | ||
"Member " + methodName + " should be invocable", | ||
valueWithType.value.canInvokeMember(methodName), | ||
is(true)); | ||
var interop = InteropLibrary.getUncached(); | ||
var unwrappedValue = ContextUtils.unwrapValue(ctx(), valueWithType.value); | ||
var memberViaInterop = interop.readMember(unwrappedValue, methodName); | ||
assertThat( | ||
"Member via interop should be the same as in scope, but was: " + memberViaInterop, | ||
memberViaInterop == methodInScope, | ||
is(true)); | ||
} | ||
} | ||
return null; | ||
}); | ||
} | ||
|
||
private static boolean isPrimitive(Value object) { | ||
var unwrapped = ContextUtils.unwrapValue(ctx(), object); | ||
return unwrapped instanceof Long | ||
|| unwrapped instanceof Boolean | ||
|| unwrapped instanceof Integer | ||
|| unwrapped instanceof Double; | ||
} | ||
|
||
private static boolean shouldSkipType(Type type) { | ||
if (type == null) { | ||
return true; | ||
} | ||
if (!type.isBuiltin()) { | ||
return true; | ||
} | ||
var builtins = ContextUtils.leakContext(ctx()).getBuiltins(); | ||
var typesToSkip = | ||
List.of( | ||
builtins.function(), builtins.dataflowError(), builtins.warning(), builtins.nothing()); | ||
var shouldBeSkipped = typesToSkip.stream().anyMatch(toSkip -> toSkip == type); | ||
return shouldBeSkipped; | ||
} | ||
|
||
private static boolean isHostValue(Value value) { | ||
var unwrapped = ContextUtils.unwrapValue(ctx(), value); | ||
var ensoCtx = ContextUtils.leakContext(ctx()); | ||
return ensoCtx.isJavaPolyglotObject(unwrapped); | ||
} | ||
|
||
public record ValueWithType(Value value, Type type) {} | ||
} |
120 changes: 120 additions & 0 deletions
120
...s/src/test/java/org/enso/interpreter/test/builtins/InvokeBuiltinMethodViaInteropTest.java
This file contains hidden or 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,120 @@ | ||
package org.enso.interpreter.test.builtins; | ||
|
||
import static org.hamcrest.MatcherAssert.assertThat; | ||
import static org.hamcrest.Matchers.is; | ||
|
||
import com.oracle.truffle.api.interop.InteropLibrary; | ||
import org.enso.test.utils.ContextUtils; | ||
import org.graalvm.polyglot.Context; | ||
import org.junit.AfterClass; | ||
import org.junit.BeforeClass; | ||
import org.junit.Test; | ||
|
||
/** | ||
* This test tries to invoke some builtin methods on builtin types via the {@link | ||
* com.oracle.truffle.api.interop.InteropLibrary interop} protocol. | ||
*/ | ||
public class InvokeBuiltinMethodViaInteropTest { | ||
private static Context ctx; | ||
|
||
@BeforeClass | ||
public static void setUp() { | ||
ctx = ContextUtils.createDefaultContext(); | ||
} | ||
|
||
@AfterClass | ||
public static void tearDown() { | ||
ctx.close(); | ||
ctx = null; | ||
} | ||
|
||
@Test | ||
public void invokeGetMethodOnRef() { | ||
var code = | ||
""" | ||
import Standard.Base.Runtime.Ref.Ref | ||
|
||
main = Ref.new 42 | ||
"""; | ||
var ref = ContextUtils.evalModule(ctx, code); | ||
ContextUtils.executeInContext( | ||
ctx, | ||
() -> { | ||
var interop = InteropLibrary.getUncached(); | ||
var refUnwrapped = ContextUtils.unwrapValue(ctx, ref); | ||
assertThat( | ||
"Ref should have a 'get' method", | ||
interop.isMemberInvocable(refUnwrapped, "get"), | ||
is(true)); | ||
var res = interop.invokeMember(refUnwrapped, "get"); | ||
assertThat("Ref.get should return a number", interop.isNumber(res), is(true)); | ||
assertThat("Ref.get should return 42", interop.asInt(res), is(42)); | ||
return null; | ||
}); | ||
} | ||
|
||
/** | ||
* 'Text.reverse' is an extension method defined outside builtins module scope, so it cannot be | ||
* resolved. | ||
*/ | ||
@Test | ||
public void extensionMethodOnBuiltinTypeIsNotResolved() { | ||
var text = ContextUtils.evalModule(ctx, "main = 'Hello'"); | ||
ContextUtils.executeInContext( | ||
ctx, | ||
() -> { | ||
var interop = InteropLibrary.getUncached(); | ||
var textUnwrapped = ContextUtils.unwrapValue(ctx, text); | ||
assertThat( | ||
"Text should not be able to resolve 'reverse' method", | ||
interop.isMemberInvocable(textUnwrapped, "reverse"), | ||
is(false)); | ||
return null; | ||
}); | ||
} | ||
|
||
@Test | ||
public void invokePlusOnTextWithParameter() { | ||
var text1 = ContextUtils.evalModule(ctx, "main = 'First'"); | ||
var text2 = ContextUtils.evalModule(ctx, "main = 'Second'"); | ||
ContextUtils.executeInContext( | ||
ctx, | ||
() -> { | ||
var interop = InteropLibrary.getUncached(); | ||
var text1Unwrapped = ContextUtils.unwrapValue(ctx, text1); | ||
var text2Unwrapped = ContextUtils.unwrapValue(ctx, text2); | ||
assertThat( | ||
"Text should have a '+' method", | ||
interop.isMemberInvocable(text1Unwrapped, "+"), | ||
is(true)); | ||
var res = interop.invokeMember(text1Unwrapped, "+", text2Unwrapped); | ||
assertThat("Text.+ should return a text", interop.isString(res), is(true)); | ||
assertThat( | ||
"Text.+ should return 'FirstSecond'", interop.asString(res), is("FirstSecond")); | ||
return null; | ||
}); | ||
} | ||
|
||
/** | ||
* 'Text.is_empty' is not a builtin method, defined on a builtin type. It should be treated as a | ||
* builtin method, thus be invocable via interop. | ||
*/ | ||
@Test | ||
public void invokeNonBuiltinMethodOnBuiltinType() { | ||
var text = ContextUtils.evalModule(ctx, "main = 'Hello'"); | ||
ContextUtils.executeInContext( | ||
ctx, | ||
() -> { | ||
var interop = InteropLibrary.getUncached(); | ||
var textUnwrapped = ContextUtils.unwrapValue(ctx, text); | ||
assertThat( | ||
"Text should have a 'is_empty' method", | ||
interop.isMemberInvocable(textUnwrapped, "is_empty"), | ||
is(true)); | ||
var res = interop.invokeMember(textUnwrapped, "is_empty"); | ||
assertThat("Text.is_empty should return a boolean", interop.isBoolean(res), is(true)); | ||
assertThat("Text.is_empty should return false", interop.asBoolean(res), is(false)); | ||
return null; | ||
}); | ||
} | ||
} |
This file contains hidden or 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 hidden or 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.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, so this is result of
Ref.new 42
.