Convert no such method Panic to UnknownIdentifierException (#7487)

This commit is contained in:
Jaroslav Tulach 2023-08-07 14:58:08 +02:00 committed by GitHub
parent 8e49255d92
commit 3b3dbc352d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 211 additions and 47 deletions

View File

@ -19,7 +19,6 @@ import org.enso.interpreter.runtime.callable.UnresolvedSymbol;
import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo;
import org.enso.interpreter.runtime.state.State;
/** A helper node to handle method application for the interop library. */
@GenerateUncached
@NodeInfo(description = "Helper node to handle method application through the interop library.")
public abstract class InteropMethodCallNode extends Node {
@ -60,7 +59,8 @@ public abstract class InteropMethodCallNode extends Node {
args,
InvokeCallableNode.DefaultsExecutionMode.EXECUTE,
InvokeCallableNode.ArgumentsExecutionMode.PRE_EXECUTED,
0);
0,
true);
}
@NonIdempotent

View File

@ -114,7 +114,7 @@ public abstract class InvokeCallableNode extends BaseNode {
InvokeFunctionNode.build(schema, defaultsExecutionMode, argumentsExecutionMode);
this.invokeMethodNode =
InvokeMethodNode.build(
schema, defaultsExecutionMode, argumentsExecutionMode, thisArgumentPosition);
schema, defaultsExecutionMode, argumentsExecutionMode, thisArgumentPosition, false);
this.invokeConversionNode =
InvokeConversionNode.build(
schema, defaultsExecutionMode, argumentsExecutionMode, thatArgumentPosition);

View File

@ -52,6 +52,7 @@ import com.oracle.truffle.api.dsl.NonIdempotent;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.UnknownIdentifierException;
import com.oracle.truffle.api.interop.UnsupportedMessageException;
import com.oracle.truffle.api.library.CachedLibrary;
import com.oracle.truffle.api.nodes.ExplodeLoop;
@ -77,6 +78,7 @@ public abstract class InvokeMethodNode extends BaseNode {
private final int argumentCount;
private final int thisArgumentPosition;
private final boolean onBoundary;
/**
* Creates a new node for method invocation.
@ -84,26 +86,30 @@ public abstract class InvokeMethodNode extends BaseNode {
* @param schema a description of the arguments being applied to the callable
* @param defaultsExecutionMode the defaulted arguments handling mode for this call
* @param argumentsExecutionMode the arguments execution mode for this call
* @param thisArgumentPosition position
* @param onBoundary shall we emit plain {@code PanicException} or also attach {@code UnknownIdentifierException} cause
* @return a new invoke method node
*/
public static InvokeMethodNode build(
CallArgumentInfo[] schema,
InvokeCallableNode.DefaultsExecutionMode defaultsExecutionMode,
InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode,
int thisArgumentPosition) {
int thisArgumentPosition, boolean onBoundary) {
return InvokeMethodNodeGen.create(
schema, defaultsExecutionMode, argumentsExecutionMode, thisArgumentPosition);
schema, defaultsExecutionMode, argumentsExecutionMode, thisArgumentPosition, onBoundary);
}
InvokeMethodNode(
CallArgumentInfo[] schema,
InvokeCallableNode.DefaultsExecutionMode defaultsExecutionMode,
InvokeCallableNode.ArgumentsExecutionMode argumentsExecutionMode,
int thisArgumentPosition) {
int thisArgumentPosition, boolean onBoundary
) {
this.invokeFunctionNode =
InvokeFunctionNode.build(schema, defaultsExecutionMode, argumentsExecutionMode);
this.argumentCount = schema.length;
this.thisArgumentPosition = thisArgumentPosition;
this.onBoundary = onBoundary;
}
InvokeFunctionNode getInvokeFunctionNode() {
@ -202,8 +208,9 @@ public abstract class InvokeMethodNode extends BaseNode {
Type selfTpe = typesLibrary.getType(self);
Function function = resolveFunction(symbol, selfTpe, methodResolverNode);
if (function == null) {
throw new PanicException(
EnsoContext.get(this).getBuiltins().error().makeNoSuchMethod(self, symbol), this);
var cause = onBoundary ? UnknownIdentifierException.create(symbol.getName()) : null;
var payload = EnsoContext.get(this).getBuiltins().error().makeNoSuchMethod(self, symbol);
throw new PanicException(payload, cause, this);
}
var resolvedFuncArgCount = function.getSchema().getArgumentsCount();
CallArgumentInfo[] invokeFuncSchema = invokeFunctionNode.getSchema();
@ -410,7 +417,7 @@ public abstract class InvokeMethodNode extends BaseNode {
invokeFunctionNode.getSchema(),
invokeFunctionNode.getDefaultsExecutionMode(),
invokeFunctionNode.getArgumentsExecutionMode(),
thisArgumentPosition));
thisArgumentPosition, false));
childDispatch.setTailStatus(getTailStatus());
childDispatch.setId(invokeFunctionNode.getId());
notifyInserted(childDispatch);

View File

@ -3,9 +3,30 @@ package org.enso.interpreter.runtime.builtin;
import static com.oracle.truffle.api.CompilerDirectives.transferToInterpreterAndInvalidate;
import com.oracle.truffle.api.CompilerDirectives;
import org.enso.interpreter.node.expression.builtin.error.*;
import org.enso.interpreter.node.expression.builtin.error.ArithmeticError;
import org.enso.interpreter.node.expression.builtin.error.ArityError;
import org.enso.interpreter.node.expression.builtin.error.CaughtPanic;
import org.enso.interpreter.node.expression.builtin.error.CompileError;
import org.enso.interpreter.node.expression.builtin.error.ForbiddenOperation;
import org.enso.interpreter.node.expression.builtin.error.IncomparableValues;
import org.enso.interpreter.node.expression.builtin.error.IndexOutOfBounds;
import org.enso.interpreter.node.expression.builtin.error.InexhaustivePatternMatch;
import org.enso.interpreter.node.expression.builtin.error.InvalidArrayIndex;
import org.enso.interpreter.node.expression.builtin.error.InvalidConversionTarget;
import org.enso.interpreter.node.expression.builtin.error.ModuleDoesNotExist;
import org.enso.interpreter.node.expression.builtin.error.ModuleNotInPackageError;
import org.enso.interpreter.node.expression.builtin.error.NoConversionCurrying;
import org.enso.interpreter.node.expression.builtin.error.NoSuchConversion;
import org.enso.interpreter.node.expression.builtin.error.NoSuchField;
import org.enso.interpreter.node.expression.builtin.error.NoSuchMethod;
import org.enso.interpreter.node.expression.builtin.error.NotInvokable;
import org.enso.interpreter.node.expression.builtin.error.NumberParseError;
import org.enso.interpreter.node.expression.builtin.error.Panic;
import org.enso.interpreter.node.expression.builtin.error.SyntaxError;
import org.enso.interpreter.node.expression.builtin.error.TypeError;
import org.enso.interpreter.node.expression.builtin.error.Unimplemented;
import org.enso.interpreter.node.expression.builtin.error.UninitializedState;
import org.enso.interpreter.node.expression.builtin.error.UnsupportedArgumentTypes;
import org.enso.interpreter.runtime.EnsoContext;
import org.enso.interpreter.runtime.callable.UnresolvedConversion;
import org.enso.interpreter.runtime.callable.UnresolvedSymbol;

View File

@ -1,29 +1,36 @@
package org.enso.interpreter.runtime.callable.atom;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.exception.AbstractTruffleException;
import com.oracle.truffle.api.interop.*;
import com.oracle.truffle.api.library.CachedLibrary;
import com.oracle.truffle.api.library.ExportLibrary;
import com.oracle.truffle.api.library.ExportMessage;
import com.oracle.truffle.api.nodes.ExplodeLoop;
import com.oracle.truffle.api.profiles.BranchProfile;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.enso.interpreter.EnsoLanguage;
import org.enso.interpreter.runtime.callable.UnresolvedSymbol;
import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.data.Array;
import org.enso.interpreter.runtime.data.Type;
import org.enso.interpreter.runtime.data.text.Text;
import org.enso.interpreter.runtime.error.PanicException;
import org.enso.interpreter.runtime.error.WarningsLibrary;
import org.enso.interpreter.runtime.library.dispatch.TypesLibrary;
import org.enso.interpreter.runtime.type.TypesGen;
import org.enso.interpreter.EnsoLanguage;
import org.enso.interpreter.runtime.error.WarningsLibrary;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.exception.AbstractTruffleException;
import com.oracle.truffle.api.interop.ArityException;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.TruffleObject;
import com.oracle.truffle.api.interop.UnknownIdentifierException;
import com.oracle.truffle.api.interop.UnsupportedMessageException;
import com.oracle.truffle.api.interop.UnsupportedTypeException;
import com.oracle.truffle.api.library.CachedLibrary;
import com.oracle.truffle.api.library.ExportLibrary;
import com.oracle.truffle.api.library.ExportMessage;
import com.oracle.truffle.api.nodes.ExplodeLoop;
import com.oracle.truffle.api.profiles.BranchProfile;
/**
* A runtime representation of an Atom in Enso.
@ -198,11 +205,19 @@ public abstract class Atom implements TruffleObject {
@Cached(value = "member") String cachedMember,
@Cached(value = "buildSym(cachedConstructor, cachedMember)") UnresolvedSymbol cachedSym,
@CachedLibrary("cachedSym") InteropLibrary symbols)
throws UnsupportedMessageException, ArityException, UnsupportedTypeException {
throws UnsupportedMessageException, ArityException, UnsupportedTypeException, UnknownIdentifierException {
Object[] args = new Object[arguments.length + 1];
args[0] = receiver;
System.arraycopy(arguments, 0, args, 1, arguments.length);
return symbols.execute(cachedSym, args);
try {
return symbols.execute(cachedSym, args);
} catch (PanicException ex) {
if (ex.getCause() instanceof UnknownIdentifierException interopEx) {
throw interopEx;
} else {
throw ex;
}
}
}
@Specialization(replaces = "doCached")
@ -211,7 +226,7 @@ public abstract class Atom implements TruffleObject {
String member,
Object[] arguments,
@CachedLibrary(limit = "1") InteropLibrary symbols)
throws UnsupportedMessageException, ArityException, UnsupportedTypeException {
throws UnsupportedMessageException, ArityException, UnsupportedTypeException, UnknownIdentifierException {
UnresolvedSymbol symbol = buildSym(receiver.getConstructor(), member);
return doCached(
receiver, member, arguments, receiver.getConstructor(), member, symbol, symbols);

View File

@ -34,13 +34,24 @@ public final class PanicException extends AbstractTruffleException {
private String cacheMessage;
/**
* Creates an instance of this class.
* Creates new user visible panic.
*
* @param payload arbitrary, user-provided payload carried by this exception
* @param location the node throwing this exception, for use in guest stack traces
*/
public PanicException(Object payload, Node location) {
super(location);
this(payload, null, location);
}
/**
* Creates user visible panic with additional cause.
*
* @param payload arbitrary, user-provided payload carried by this exception
* @param cause additional exception to carry information about the panic
* @param location the node throwing this exception, for use in guest stack traces
*/
public PanicException(Object payload, Throwable cause, Node location) {
super(null, cause, UNLIMITED_STACK_TRACE, location);
if (!InteropLibrary.isValidValue(payload)) {
CompilerDirectives.transferToInterpreter();
throw new IllegalArgumentException("Only interop values are supported: " + payload);

View File

@ -0,0 +1,24 @@
package org.enso.example;
public class ToString {
private ToString() {}
public static interface Fooable {
public long foo();
}
public static String callFoo(Fooable f) {
long x = f.foo();
return "Fooable.foo() = " + x;
}
public static String showObject(Object obj) {
return "obj.toString() = " + obj.toString();
}
public static String callFooAndShow(Fooable f) {
long x = f.foo();
return "{" + f.toString() + "}.foo() = " + x;
}
}

View File

@ -3,15 +3,14 @@ package org.enso.interpreter.test;
import java.io.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets;
import java.util.List;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Value;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import org.junit.After;
import org.junit.AfterClass;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
@ -36,14 +35,17 @@ public class JavaInteropTest extends TestBase {
out.reset();
}
private void checkPrint(String code, List<String> expected) {
Value result = evalModule(ctx, code);
assertTrue("should return Nothing", result.isNull());
String[] logLines = out
private String[] getStdOutLines() {
return out
.toString(StandardCharsets.UTF_8)
.trim()
.split(System.lineSeparator());
assertArrayEquals(expected.toArray(), logLines);
}
private void checkPrint(String code, List<String> expected) {
Value result = evalModule(ctx, code);
assertTrue("should return Nothing", result.isNull());
assertArrayEquals(expected.toArray(), getStdOutLines());
}
@Test
@ -72,7 +74,7 @@ public class JavaInteropTest extends TestBase {
public void testImportStaticInnerClass() {
var code = """
polyglot java import org.enso.example.TestClass.StaticInnerClass
main =
instance = StaticInnerClass.new "my_data"
instance.add 1 2
@ -87,7 +89,7 @@ public class JavaInteropTest extends TestBase {
from Standard.Base import IO
polyglot java import org.enso.example.TestClass
polyglot java import org.enso.example.TestClass.InnerEnum
main =
IO.println <| TestClass.enumToString InnerEnum.ENUM_VALUE_1
IO.println <| TestClass.enumToString TestClass.InnerEnum.ENUM_VALUE_2
@ -99,7 +101,7 @@ public class JavaInteropTest extends TestBase {
public void testImportOuterClassAndReferenceInner() {
var code = """
polyglot java import org.enso.example.TestClass
main =
instance = TestClass.StaticInnerClass.new "my_data"
instance.getData
@ -114,7 +116,7 @@ public class JavaInteropTest extends TestBase {
from Standard.Base import IO
polyglot java import org.enso.example.TestClass
polyglot java import org.enso.example.TestClass.StaticInnerClass
main =
inner_value = TestClass.StaticInnerClass.new "my_data"
other_inner_value = StaticInnerClass.new "my_data"
@ -128,7 +130,7 @@ public class JavaInteropTest extends TestBase {
public void testImportNestedInnerClass() {
var code = """
polyglot java import org.enso.example.TestClass.StaticInnerClass.StaticInnerInnerClass
main =
inner_inner_value = StaticInnerInnerClass.new
inner_inner_value.mul 3 5
@ -163,7 +165,7 @@ public class JavaInteropTest extends TestBase {
public void testImportOuterClassAndAccessNestedInnerClass() {
var code = """
polyglot java import org.enso.example.TestClass
main =
instance = TestClass.StaticInnerClass.StaticInnerInnerClass.new
instance.mul 3 5
@ -171,4 +173,88 @@ public class JavaInteropTest extends TestBase {
var res = evalModule(ctx, code);
assertEquals(15, res.asInt());
}
@Test
public void testToStringBehavior() {
var code = """
from Standard.Base import all
polyglot java import org.enso.example.ToString as Foo
type My_Fooable_Implementation
Instance x
foo : Integer
foo self = 100+self.x
main =
fooable = My_Fooable_Implementation.Instance 23
a = fooable.foo
b = fooable.to_text
c = Foo.callFoo fooable
d = Foo.showObject fooable
e = Foo.callFooAndShow fooable
[a, b, c, d, e]
""";
var res = evalModule(ctx, code);
assertTrue("It is an array", res.hasArrayElements());
assertEquals("Array with five elements", 5, res.getArraySize());
assertEquals(123, res.getArrayElement(0).asInt());
assertEquals("(Instance 23)", res.getArrayElement(1).asString());
assertEquals("Fooable.foo() = 123", res.getArrayElement(2).asString());
assertEquals("obj.toString() = (Instance 23)", res.getArrayElement(3).asString());
assertEquals("{(Instance 23)}.foo() = 123", res.getArrayElement(4).asString());
}
@Test
public void testInterfaceProxyFailuresA() {
var payload = evalInterfaceProxyFailures("a");
assertEquals("My_Exc", payload.getMetaObject().getMetaSimpleName());
var stdout = getStdOutLines();
var expectedLines = List.of("Executing Fooable_Panic.foo");
assertArrayEquals(expectedLines.toArray(), stdout);
}
@Test
public void testInterfaceProxyFailuresB() {
var result = evalInterfaceProxyFailures("b");
assertEquals("nonexistent_text_method", result.asString());
var stdout = getStdOutLines();
var expectedLines = List.of("Executing Fooable_Unresolved.foo");
assertArrayEquals(expectedLines.toArray(), stdout);
}
private Value evalInterfaceProxyFailures(String methodToEval) {
var code = """
from Standard.Base import all
import Standard.Base.Errors.Common.No_Such_Method
polyglot java import org.enso.example.ToString as Foo
type My_Exc
Error
type Fooable_Panic
Value
foo : Integer
foo self =
IO.println "Executing Fooable_Panic.foo"
Panic.throw My_Exc.Error
type Fooable_Unresolved
Value
foo : Integer
foo self =
IO.println "Executing Fooable_Unresolved.foo"
"".nonexistent_text_method
a = Panic.catch My_Exc (Foo.callFoo Fooable_Panic.Value) (.payload)
b = Panic.catch No_Such_Method (Foo.callFoo Fooable_Unresolved.Value) (caught-> caught.payload.method_name)
""";
return evalModule(ctx, code + "\nmain = " + methodToEval);
}
}