mirror of
https://github.com/enso-org/enso.git
synced 2024-12-23 19:21:54 +03:00
Convert no such method Panic to UnknownIdentifierException (#7487)
This commit is contained in:
parent
8e49255d92
commit
3b3dbc352d
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
24
engine/runtime/src/test/java/org/enso/example/ToString.java
Normal file
24
engine/runtime/src/test/java/org/enso/example/ToString.java
Normal 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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user