Atom fields are visible in debugger (#10661)

Atom fields are now visible in the debugger (both chrome inspector and VSCode's debug adapter protocol):
![image](https://github.com/user-attachments/assets/c3d19475-c271-46b6-a44e-e9aebf414b8d)
This commit is contained in:
Pavel Marek 2024-07-26 08:54:39 +02:00 committed by GitHub
parent f31c084f43
commit f849634db3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 223 additions and 2 deletions

View File

@ -0,0 +1,131 @@
package org.enso.interpreter.test;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import org.enso.test.utils.ContextUtils;
import org.graalvm.polyglot.Context;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
/**
* Tests various {@link com.oracle.truffle.api.interop.InteropLibrary interop} contracts for {@link
* org.enso.interpreter.runtime.data.atom.Atom atoms}.
*/
public class AtomInteropTest {
private Context ctx;
@Before
public void initCtx() {
ctx = ContextUtils.createDefaultContext();
}
@After
public void disposeCtx() {
ctx.close();
}
@Test
public void atomMembersAreConstructorFields_SingleConstructor() {
var myTypeAtom =
ContextUtils.evalModule(
ctx,
"""
type My_Type
Cons field_1 field_2
main =
My_Type.Cons 1 2
""");
assertThat(myTypeAtom.hasMembers(), is(true));
var memberNames = myTypeAtom.getMemberKeys();
assertThat("Has two fields", memberNames.size(), is(2));
assertThat(
"Member names are not qualified", memberNames, containsInAnyOrder("field_1", "field_2"));
for (var memberName : memberNames) {
var member = myTypeAtom.getMember(memberName);
assertThat("Member " + memberName + " should be readable", member, is(notNullValue()));
assertThat("All fields are numbers", member.isNumber(), is(true));
}
}
@Test
public void atomIsNotMetaObject() {
var myTypeAtom =
ContextUtils.evalModule(
ctx,
"""
type My_Type
Cons field_1 field_2
main =
My_Type.Cons 1 2
""");
assertThat(myTypeAtom.isMetaObject(), is(false));
assertThat(myTypeAtom.getMetaObject().getMetaSimpleName(), is("My_Type"));
}
@Test
public void typeHasAnyAsSuperType() {
var myTypeAtom =
ContextUtils.evalModule(
ctx,
"""
type My_Type
Cons
main = My_Type.Cons
""");
var myType = myTypeAtom.getMetaObject();
assertThat(myType.hasMetaParents(), is(true));
var metaParents = myType.getMetaParents();
assertThat(metaParents.hasArrayElements(), is(true));
assertThat("Has just one meta parent - Any", metaParents.getArraySize(), is(1L));
var anyType = metaParents.getArrayElement(0);
assertThat(anyType.getMetaSimpleName(), is("Any"));
}
@Ignore("https://github.com/enso-org/enso/issues/10675")
@Test
public void atomMembersAreConstructorFields_ManyConstructors() {
var myTypeAtom =
ContextUtils.evalModule(
ctx,
"""
type My_Type
Cons_1 f1 f2 f3 f4 f5 f6
Cons_2 g1 g2 g3
Cons_3 h1 h2 h3 h4 h5 h6 h7 h8 h9
main = My_Type.Cons_2 "g1" "g2" "g3"
""");
assertThat(
"Member names correspond to constructor field names for a single constructor",
myTypeAtom.getMemberKeys(),
containsInAnyOrder("g1", "g2", "g3"));
}
@Test
public void typeMembersAreConstructors() {
var myType =
ContextUtils.evalModule(
ctx,
"""
type My_Type
Cons_1
Cons_2
main = My_Type
""");
assertThat("type has constructors as members", myType.hasMembers(), is(true));
assertThat(myType.getMemberKeys(), containsInAnyOrder("Cons_1", "Cons_2"));
assertThat(
"Constructor (type member) is instantiable",
myType.getMember("Cons_1").canInstantiate(),
is(true));
}
}

View File

@ -42,6 +42,7 @@ import org.graalvm.polyglot.io.IOAccess;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
public class DebuggingEnsoTest {
@ -434,6 +435,95 @@ public class DebuggingEnsoTest {
}
}
@Test
public void testAtomFieldsAreReadable() {
var fooFunc =
createEnsoMethod(
"""
type My_Type
Cons field_1 field_2
foo x =
obj = My_Type.Cons 1 2
obj
""",
"foo");
try (DebuggerSession session =
debugger.startSession(
(SuspendedEvent event) -> {
switch (event.getSourceSection().getCharacters().toString().strip()) {
case "obj" -> {
DebugScope scope = event.getTopStackFrame().getScope();
DebugValue objValue = scope.getDeclaredValue("obj");
assertThat(objValue.isReadable(), is(true));
assertThat(objValue.isInternal(), is(false));
assertThat(objValue.hasReadSideEffects(), is(false));
var field1Prop = objValue.getProperty("field_1");
assertThat(field1Prop.isReadable(), is(true));
assertThat(field1Prop.isNumber(), is(true));
assertThat(field1Prop.asInt(), is(1));
assertThat(objValue.getProperties().size(), is(2));
for (var prop : objValue.getProperties()) {
assertThat(
"Property '" + prop.getName() + "' should be readable",
prop.isReadable(),
is(true));
assertThat(prop.isNumber(), is(true));
}
}
}
event.getSession().suspendNextExecution();
})) {
session.suspendNextExecution();
fooFunc.execute(0);
}
}
@Ignore("https://github.com/enso-org/enso/issues/10675")
@Test
public void testAtomFieldAreReadable_MultipleConstructors() {
var fooFunc =
createEnsoMethod(
"""
type My_Type
Cons_1 f1 f2
Cons_2 g1 g2 g3
foo x =
obj = My_Type.Cons_1 1 2
obj
""",
"foo");
try (DebuggerSession session =
debugger.startSession(
(SuspendedEvent event) -> {
switch (event.getSourceSection().getCharacters().toString().strip()) {
case "obj" -> {
DebugScope scope = event.getTopStackFrame().getScope();
DebugValue objValue = scope.getDeclaredValue("obj");
assertThat(objValue.isReadable(), is(true));
assertThat(objValue.isInternal(), is(false));
assertThat(objValue.hasReadSideEffects(), is(false));
assertThat("Has fields f1 and f2", objValue.getProperties().size(), is(2));
for (var prop : objValue.getProperties()) {
assertThat(
"Property '" + prop.getName() + "' should be readable",
prop.isReadable(),
is(true));
assertThat(prop.isNumber(), is(true));
}
}
}
event.getSession().suspendNextExecution();
})) {
session.suspendNextExecution();
fooFunc.execute(0);
}
}
/**
* Tests stepping through the given source.
*

View File

@ -142,7 +142,7 @@ public abstract class Atom implements EnsoObject {
*/
@ExportMessage
@CompilerDirectives.TruffleBoundary
EnsoObject getMembers(boolean includeInternal) throws UnsupportedMessageException {
EnsoObject getMembers(boolean includeInternal) {
Set<Function> members =
constructor.getDefinitionScope().getMethodsForType(constructor.getType());
Set<Function> allFuncMembers = new HashSet<>();

View File

@ -50,7 +50,7 @@ final class GetFieldNode extends EnsoRootNode {
@Override
public String getName() {
return type.getName() + "." + name;
return name;
}
@Override