Avoid nulls in Array.items (#5736)

Put `Nothing` into an empty array rather than `null`. When running with `assert` on (unit tests), check the content of the array and `AssertError` quickly when a `null` is found.
This commit is contained in:
Jaroslav Tulach 2023-02-22 18:53:36 +01:00 committed by GitHub
parent dfea59c24d
commit 78aab133c7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 76 additions and 7 deletions

View File

@ -21,7 +21,7 @@ public class GetStackTraceNode extends Node {
public static Array stackTraceToArray(Throwable exception) { public static Array stackTraceToArray(Throwable exception) {
var elements = TruffleStackTrace.getStackTrace(exception); var elements = TruffleStackTrace.getStackTrace(exception);
if (elements == null) return new Array(); if (elements == null) return new Array();
var ret = new Array(elements.size()); var ret = Array.allocate(elements.size());
for (int i = 0; i < elements.size(); i++) { for (int i = 0; i < elements.size(); i++) {
var element = elements.get(i); var element = elements.get(i);
ret.getItems()[i] = element.getGuestObject(); ret.getItems()[i] = element.getGuestObject();

View File

@ -133,7 +133,7 @@ public abstract class Atom implements TruffleObject {
public Array getMembers(boolean includeInternal) { public Array getMembers(boolean includeInternal) {
Map<String, Function> members = constructor.getDefinitionScope().getMethods().get(constructor.getType()); Map<String, Function> members = constructor.getDefinitionScope().getMethods().get(constructor.getType());
if (members == null) { if (members == null) {
return new Array(0); return Array.allocate(0);
} }
Object[] mems = members.keySet().toArray(); Object[] mems = members.keySet().toArray();
return new Array(mems); return new Array(mems);

View File

@ -39,6 +39,7 @@ public final class Array implements TruffleObject {
description = "Creates an array with given elements.", description = "Creates an array with given elements.",
autoRegister = false) autoRegister = false)
public Array(Object... items) { public Array(Object... items) {
assert noNulls(items);
this.items = items; this.items = items;
} }
@ -49,9 +50,25 @@ public final class Array implements TruffleObject {
*/ */
@Builtin.Method( @Builtin.Method(
description = "Creates an uninitialized array of a given size.", description = "Creates an uninitialized array of a given size.",
autoRegister = false) autoRegister = false,
public Array(long size) { name = "new")
this.items = new Object[(int) size]; public static Array allocate(long size) {
var arr = new Object[(int) size];
var ctx = EnsoContext.get(null);
var nothing = ctx.getBuiltins().nothing();
for (int i = 0; i < arr.length; i++) {
arr[i] = nothing;
}
return new Array(arr);
}
private static final boolean noNulls(Object[] arr) {
for (int i = 0; i < arr.length; i++) {
if (arr[i] == null) {
return false;
}
}
return true;
} }
/** @return the elements of this array as a java array. */ /** @return the elements of this array as a java array. */
@ -107,7 +124,7 @@ public final class Array implements TruffleObject {
/** @return an empty array */ /** @return an empty array */
@Builtin.Method(description = "Creates an empty Array", autoRegister = false) @Builtin.Method(description = "Creates an empty Array", autoRegister = false)
public static Object empty() { public static Object empty() {
return new Array(); return allocate(0);
} }
/** @return an identity array */ /** @return an identity array */

View File

@ -74,7 +74,7 @@ public final class Vector implements TruffleObject {
long slice_end = Math.min(this_length, end); long slice_end = Math.min(this_length, end);
if (slice_start >= slice_end) { if (slice_start >= slice_end) {
return new Vector(new Array(0)); return new Vector(Array.allocate(0));
} }
if ((slice_start == 0) && (slice_end == this_length)) { if ((slice_start == 0) && (slice_end == this_length)) {

View File

@ -0,0 +1,52 @@
package org.enso.interpreter.test;
import java.io.ByteArrayOutputStream;
import java.net.URI;
import java.nio.file.Paths;
import java.util.Map;
import org.enso.polyglot.RuntimeOptions;
import org.graalvm.polyglot.Context;
import org.graalvm.polyglot.Language;
import org.graalvm.polyglot.Source;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import org.junit.Before;
import org.junit.Test;
public class ArrayTest {
private Context ctx;
@Before
public void prepareCtx() {
this.ctx = Context.newBuilder()
.allowExperimentalOptions(true)
.allowIO(true)
.allowAllAccess(true)
.logHandler(new ByteArrayOutputStream())
.option(
RuntimeOptions.LANGUAGE_HOME_OVERRIDE,
Paths.get("../../distribution/component").toFile().getAbsolutePath()
).build();
final Map<String, Language> langs = ctx.getEngine().getLanguages();
assertNotNull("Enso found: " + langs, langs.get("enso"));
}
@Test
public void dontExposeNullsOutOfArray() throws Exception {
final URI facUri = new URI("memory://choose.enso");
final Source facSrc = Source.newBuilder("enso", """
from Standard.Base import all
null =
a = Array.new 1
b = a.at 0
b
""", "nulls.enso")
.uri(facUri)
.buildLiteral();
var module = ctx.eval(facSrc);
var res = module.invokeMember("eval_expression", "null");
assertTrue("i null", res.isNull());
}
}