Make sure Enso FrameDescriptors have non-null default value (#6977)

This commit is contained in:
Jaroslav Tulach 2023-06-08 16:55:40 +02:00 committed by GitHub
parent e6652b8c9f
commit 9f39fb30b7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 180 additions and 36 deletions

View File

@ -16,7 +16,7 @@ import org.enso.polyglot.runtime.Runtime$Api$BackgroundJobsStartedNotification;
import org.enso.polyglot.runtime.Runtime$Api$CreateContextRequest;
import org.enso.polyglot.runtime.Runtime$Api$CreateContextResponse;
import org.enso.polyglot.runtime.Runtime$Api$EditFileNotification;
import org.enso.polyglot.runtime.Runtime$Api$ExecutionFailed;
import org.enso.polyglot.runtime.Runtime$Api$ExecutionComplete;
import org.enso.polyglot.runtime.Runtime$Api$ExpressionUpdates;
import org.enso.polyglot.runtime.Runtime$Api$InitializedNotification;
import org.enso.polyglot.runtime.Runtime$Api$MethodCall;
@ -107,8 +107,11 @@ public class IncrementalUpdatesTest {
@Test
public void sendNotANumberChange() {
var failed = sendUpdatesWhenFunctionBodyIsChangedBySettingValue("4", ConstantsGen.INTEGER, "4", "x", null, LiteralNode.class);
assertTrue("Execution failed: " + failed, failed.head().payload() instanceof Runtime$Api$ExecutionFailed);
var result = sendUpdatesWhenFunctionBodyIsChangedBySettingValue("4", ConstantsGen.INTEGER, "4", "x", null, LiteralNode.class);
assertTrue("Execution succeeds: " + result, result.head().payload() instanceof Runtime$Api$ExecutionComplete);
assertEquals("Error is printed as a result",
List.newBuilder().addOne("(Error: Uninitialized value)"), context.consumeOut()
);
}
private static String extractPositions(String code, String chars, Map<Character, int[]> beginAndLength) {

View File

@ -0,0 +1,94 @@
package org.enso.interpreter.test;
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.nio.file.Paths;
import java.util.Map;
import java.util.function.Function;
import org.enso.polyglot.RuntimeOptions;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Language;
import org.graalvm.polyglot.Source;
import org.junit.After;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import org.junit.Before;
import org.junit.Test;
public class InsightForEnsoTest {
private Context ctx;
private AutoCloseable insightHandle;
private final ByteArrayOutputStream out = new ByteArrayOutputStream();
@Before
public void initContext() throws Exception {
this.ctx = Context.newBuilder()
.allowExperimentalOptions(true)
.option(
RuntimeOptions.LANGUAGE_HOME_OVERRIDE,
Paths.get("../../distribution/component").toFile().getAbsolutePath()
)
.logHandler(OutputStream.nullOutputStream())
.allowExperimentalOptions(true)
.allowIO(true)
.out(out)
.allowAllAccess(true)
.build();
var engine = ctx.getEngine();
Map<String, Language> langs = engine.getLanguages();
assertNotNull("Enso found: " + langs, langs.get("enso"));
@SuppressWarnings("unchecked")
var fn = (Function<Source,AutoCloseable>) engine.getInstruments().get("insight").lookup(Function.class);
assertNotNull(fn);
var insightScript = Source.newBuilder("js", """
insight.on('enter', (ctx, frame) => {
print(`${ctx.name} at ${ctx.source.name}:${ctx.line}:`);
let dump = "";
for (let p in frame) {
dump += ` ${p}=${frame[p]}`;
}
print(dump);
}, {
roots : true
});
""", "trace.js").build();
this.insightHandle = fn.apply(insightScript);
}
@After
public void disposeContext() throws Exception {
this.insightHandle.close();
this.ctx.close();
}
@Test
public void computeFactorial() throws Exception {
var code = Source.newBuilder("enso", """
fac n =
acc n v = if n <= 1 then v else
@Tail_Call acc n-1 n*v
acc n 1
""", "factorial.enso").build();
var m = ctx.eval(code);
var fac = m.invokeMember("eval_expression", "fac");
var res = fac.execute(5);
assertEquals(120, res.asInt());
var msgs = out.toString();
assertNotEquals("Step one: " + msgs, -1, msgs.indexOf("n=5 v=1 acc=function"));
assertNotEquals("Step two: " + msgs, -1, msgs.indexOf("n=4 v=5 acc=function"));
assertNotEquals("3rd step: " + msgs, -1, msgs.indexOf("n=3 v=20 acc=function"));
assertNotEquals("4th step: " + msgs, -1, msgs.indexOf("n=2 v=60 acc=function"));
assertNotEquals(
"Uninitialized variables are seen as JavaScript null: " + msgs,
-1, msgs.indexOf("n=null v=null acc=function")
);
}
}

View File

@ -9,10 +9,7 @@ import java.io.File;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Deque;
import java.util.List;
import java.util.Optional;
import java.util.UUID;

View File

@ -1,8 +1,14 @@
package org.enso.interpreter.runtime.builtin;
import com.oracle.truffle.api.CompilerDirectives;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Arrays;
@ -10,8 +16,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import org.enso.compiler.Passes;
import org.enso.compiler.context.FreshNameSupply;
import org.enso.compiler.exception.CompilerError;
@ -19,16 +24,20 @@ import org.enso.compiler.phase.BuiltinsIrBuilder;
import org.enso.interpreter.EnsoLanguage;
import org.enso.interpreter.dsl.TypeProcessor;
import org.enso.interpreter.dsl.model.MethodDefinition;
import org.enso.interpreter.node.expression.builtin.Any;
import org.enso.interpreter.node.expression.builtin.Boolean;
import org.enso.interpreter.node.expression.builtin.*;
import org.enso.interpreter.node.expression.builtin.Builtin;
import org.enso.interpreter.node.expression.builtin.BuiltinRootNode;
import org.enso.interpreter.node.expression.builtin.Nothing;
import org.enso.interpreter.node.expression.builtin.Polyglot;
import org.enso.interpreter.node.expression.builtin.debug.Debug;
import org.enso.interpreter.node.expression.builtin.error.CaughtPanic;
import org.enso.interpreter.node.expression.builtin.error.Warning;
import org.enso.interpreter.node.expression.builtin.immutable.Vector;
import org.enso.interpreter.node.expression.builtin.io.File;
import org.enso.interpreter.node.expression.builtin.meta.ProjectDescription;
import org.enso.interpreter.node.expression.builtin.mutable.Array;
import org.enso.interpreter.node.expression.builtin.mutable.Ref;
import org.enso.interpreter.node.expression.builtin.immutable.Vector;
import org.enso.interpreter.node.expression.builtin.ordering.Comparable;
import org.enso.interpreter.node.expression.builtin.ordering.DefaultComparator;
import org.enso.interpreter.node.expression.builtin.ordering.Ordering;
@ -37,24 +46,18 @@ import org.enso.interpreter.node.expression.builtin.runtime.Context;
import org.enso.interpreter.node.expression.builtin.text.Text;
import org.enso.interpreter.runtime.EnsoContext;
import org.enso.interpreter.runtime.Module;
import org.enso.interpreter.runtime.builtin.Error;
import org.enso.interpreter.runtime.builtin.Number;
import org.enso.interpreter.runtime.builtin.System;
import org.enso.interpreter.runtime.callable.atom.Atom;
import org.enso.interpreter.runtime.callable.function.Function;
import org.enso.interpreter.runtime.data.Type;
import org.enso.interpreter.runtime.callable.atom.Atom;
import org.enso.interpreter.runtime.scope.ModuleScope;
import org.enso.interpreter.runtime.type.TypesFromProxy;
import org.enso.pkg.QualifiedName;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.util.stream.Collectors;
/** Container class for static predefined atoms, methods, and their containing scope. */
public class Builtins {
public final class Builtins {
private static final List<Constructor<? extends Builtin>> loadedBuiltinConstructors;
private static final Map<String, LoadedBuiltinMethod> loadedBuiltinMethods;
@ -133,12 +136,9 @@ public class Builtins {
builtinMethodNodes = readBuiltinMethodsMetadata(loadedBuiltinMethods, scope);
registerBuiltinMethods(scope, language);
error = new Error(this, context);
ordering = getBuiltinType(Ordering.class);
comparable = getBuiltinType(Comparable.class);
defaultComparator = getBuiltinType(DefaultComparator.class);
system = new System(this);
number = new Number(this);
bool = this.getBuiltinType(Boolean.class);
contexts = this.getBuiltinType(Context.class);
@ -161,8 +161,12 @@ public class Builtins {
duration = builtins.get(org.enso.interpreter.node.expression.builtin.date.Duration.class);
timeOfDay = builtins.get(org.enso.interpreter.node.expression.builtin.date.TimeOfDay.class);
timeZone = builtins.get(org.enso.interpreter.node.expression.builtin.date.TimeZone.class);
special = new Special(language);
warning = builtins.get(Warning.class);
error = new Error(this, context);
system = new System(this);
number = new Number(this);
special = new Special(language);
}
/**

View File

@ -15,7 +15,7 @@ import org.enso.interpreter.runtime.data.text.Text;
import static com.oracle.truffle.api.CompilerDirectives.transferToInterpreterAndInvalidate;
/** Container for builtin Error types */
public class Error {
public final class Error {
private final EnsoContext context;
private final SyntaxError syntaxError;
private final TypeError typeError;

View File

@ -9,6 +9,7 @@ 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.Node;
import java.util.Objects;
import org.enso.interpreter.runtime.EnsoContext;
import org.enso.interpreter.runtime.data.Type;
import org.enso.interpreter.runtime.library.dispatch.TypesLibrary;
@ -21,7 +22,10 @@ import org.enso.interpreter.runtime.library.dispatch.TypesLibrary;
*/
@ExportLibrary(InteropLibrary.class)
@ExportLibrary(TypesLibrary.class)
public class DataflowError extends AbstractTruffleException {
public final class DataflowError extends AbstractTruffleException {
/** Signals (local) values that haven't yet been initialized */
public static final DataflowError UNINITIALIZED = new DataflowError(null, (Node) null);
private final Object payload;
/**
@ -34,6 +38,7 @@ public class DataflowError extends AbstractTruffleException {
* @return a new dataflow error
*/
public static DataflowError withoutTrace(Object payload, Node location) {
assert payload != null;
DataflowError result = new DataflowError(payload, location);
TruffleStackTrace.fillIn(result);
return result;
@ -50,6 +55,7 @@ public class DataflowError extends AbstractTruffleException {
* @return a new dataflow error
*/
public static DataflowError withTrace(Object payload, AbstractTruffleException prototype) {
assert payload != null;
return new DataflowError(payload, prototype);
}
@ -69,7 +75,7 @@ public class DataflowError extends AbstractTruffleException {
* @return the payload object
*/
public Object getPayload() {
return payload;
return payload != null ? payload : "Uninitialized value";
}
/**
@ -78,18 +84,17 @@ public class DataflowError extends AbstractTruffleException {
* @return a string representation of this object
*/
@Override
@TruffleBoundary
public String toString() {
return "Error:" + getPayload().toString();
return "Error:" + Objects.toString(getPayload());
}
@ExportMessage
@TruffleBoundary
public String toDisplayString(
boolean allowSideEffects,
@CachedLibrary(limit = "3") InteropLibrary displays,
@CachedLibrary(limit = "3") InteropLibrary strings) {
public String toDisplayString(boolean allowSideEffects) {
try {
return "(Error: " + strings.asString(displays.toDisplayString(payload)) + ")";
var iop = InteropLibrary.getUncached();
return "(Error: " + iop.asString(iop.toDisplayString(getPayload())) + ")";
} catch (UnsupportedMessageException e) {
return "Error";
}
@ -110,6 +115,11 @@ public class DataflowError extends AbstractTruffleException {
return true;
}
@ExportMessage
boolean isNull() {
return payload == null;
}
@ExportMessage
RuntimeException throwException() throws UnsupportedMessageException {
return this;

View File

@ -8,6 +8,7 @@ import org.enso.compiler.pass.analyse.AliasAnalysis.Graph.{
Scope => AliasScope
}
import org.enso.compiler.pass.analyse.{AliasAnalysis, DataflowAnalysis}
import org.enso.interpreter.runtime.error.DataflowError
import org.enso.interpreter.runtime.scope.LocalScope.{
internalSlots,
monadicStateSlotName
@ -162,6 +163,7 @@ class LocalScope(
)
assert(localFrameSlotIdxs(definition.id) == returnedFrameIdx)
}
descriptorBuilder.defaultValue(DataflowError.UNINITIALIZED)
val frameDescriptor = descriptorBuilder.build()
assert(
internalSlots.length + localFrameSlotIdxs.size == frameDescriptor.getNumberOfSlots

View File

@ -6,9 +6,12 @@ import org.enso.polyglot.RuntimeOptions;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.PolyglotException;
import org.junit.AfterClass;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.junit.Assert.*;
public class ExecCompilerTest {
private static Context ctx;
@ -66,6 +69,37 @@ public class ExecCompilerTest {
}
}
@Test
public void testSelfAssignment() throws Exception {
var module = ctx.eval("enso", """
from Standard.Base.Errors.Common import all
run value =
meta1 = meta1
meta1
""");
var run = module.invokeMember("eval_expression", "run");
var error = run.execute(-1);
assertTrue("We get an error value back", error.isException());
assertTrue("The error value also represents null", error.isNull());
assertEquals("(Error: Uninitialized value)", error.toString());
}
@Test
public void testRecursiveDefinition() throws Exception {
var module = ctx.eval("enso", """
from Standard.Base import all
run prefix =
op = if False then 42 else prefix+op
op
""");
var run = module.invokeMember("eval_expression", "run");
var error = run.execute("Nope: ");
assertTrue("We get an error value back", error.isException());
assertTrue("The error value also represents null", error.isNull());
assertEquals("(Error: Uninitialized value)", error.toString());
}
@Test
public void testInvalidEnsoProjectRef() throws Exception {
var module =