Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Builtin methods can support array-like arguments #7235

Merged
9 changes: 9 additions & 0 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -958,6 +958,15 @@ lazy val `interpreter-dsl-test` =
"-Dgraalvm.locatorDisabled=true",
s"--upgrade-module-path=${file("engine/runtime/build-cache/truffle-api.jar").absolutePath}"
),
Test / javacOptions ++= Seq(
"-s",
(Test / sourceManaged).value.getAbsolutePath
),
Compile / logManager :=
sbt.internal.util.CustomLogManager.excludeMsg(
"Could not determine source for class ",
Level.Warn
),
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This fixes spurious errors in IDEA - the *MethodGen sources are now put into src_managed rather than on an unknown location inside target directory.

commands += WithDebugCommand.withDebug,
libraryDependencies ++= Seq(
"org.graalvm.truffle" % "truffle-api" % graalVersion % "provided",
Expand Down
16 changes: 8 additions & 8 deletions distribution/lib/Standard/Base/0.0.0-dev/src/System/File.enso
Original file line number Diff line number Diff line change
Expand Up @@ -147,9 +147,9 @@ type File
with_output_stream self open_options action =
new_output_stream : File -> Vector File_Access -> Output_Stream ! File_Error
new_output_stream file open_options =
opts = open_options . map (_.to_java) . to_array
opts = open_options . map (_.to_java)
stream = File_Error.handle_java_exceptions file <|
file.output_stream_builtin opts.to_array
file.output_stream_builtin opts
## We re-wrap the File Not Found error to return the parent directory
instead of the file itself - because the file that is being written
may not exist and it will not be an error, it is the parent directory
Expand Down Expand Up @@ -566,8 +566,8 @@ type File
File_Error.handle_java_exceptions self <| case replace_existing of
True ->
copy_options = [StandardCopyOption.REPLACE_EXISTING]
self.copy_builtin destination copy_options.to_array
False -> self.copy_builtin destination [].to_array
self.copy_builtin destination copy_options
False -> self.copy_builtin destination []

## Moves the file to the specified destination.

Expand All @@ -581,8 +581,8 @@ type File
File_Error.handle_java_exceptions self <| case replace_existing of
True ->
copy_options = [StandardCopyOption.REPLACE_EXISTING]
self.move_builtin destination copy_options.to_array
False -> self.move_builtin destination [].to_array
self.move_builtin destination copy_options
False -> self.move_builtin destination []

## Deletes the file if it exists on disk.

Expand Down Expand Up @@ -611,8 +611,8 @@ type File
new_input_stream : Vector File_Access -> Input_Stream ! File_Error
new_input_stream self open_options =
if self.is_directory then Error.throw (File_Error.IO_Error self "File '"+self.path+"' is a directory") else
opts = open_options . map (_.to_java) . to_array
stream = File_Error.handle_java_exceptions self (self.input_stream opts.to_array)
opts = open_options . map (_.to_java)
stream = File_Error.handle_java_exceptions self (self.input_stream opts)
resource = Managed_Resource.register stream close_stream
Input_Stream.Value self resource

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.enso.interpreter.runtime.data;

import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.interop.InteropLibrary;
Expand Down Expand Up @@ -125,6 +126,21 @@ public static Vector fromArray(Object arr) {
return new Vector(arr);
}

@TruffleBoundary
public Array toEnsoArray() throws InvalidArrayIndexException {
var interop = InteropLibrary.getUncached();
try {
long size = interop.getArraySize(storage);
Object[] arr = new Object[Math.toIntExact(size)];
for (int i = 0; i < size; i++) {
arr[i] = interop.readArrayElement(storage, i);
}
return new Array(arr);
} catch (UnsupportedMessageException e) {
throw new IllegalStateException("Unreachable", e);
}
}
Akirathan marked this conversation as resolved.
Show resolved Hide resolved

/**
* Exposes an index validity check through the polyglot API.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,9 @@ private void handleTypeElement(TypeElement element, RoundEnvironment roundEnv, B
private final List<String> necessaryImports =
Arrays.asList(
"com.oracle.truffle.api.frame.VirtualFrame",
"com.oracle.truffle.api.interop.InteropLibrary",
"com.oracle.truffle.api.interop.InvalidArrayIndexException",
"com.oracle.truffle.api.interop.UnsupportedMessageException",
"com.oracle.truffle.api.nodes.NodeInfo",
"com.oracle.truffle.api.nodes.RootNode",
"com.oracle.truffle.api.nodes.UnexpectedResultException",
Expand All @@ -144,7 +147,6 @@ private void handleTypeElement(TypeElement element, RoundEnvironment roundEnv, B
private void generateCode(MethodDefinition methodDefinition) throws IOException {
JavaFileObject gen =
processingEnv.getFiler().createSourceFile(methodDefinition.getQualifiedName());

Set<String> allImports = new HashSet<>(necessaryImports);
allImports.addAll(methodDefinition.getImports());

Expand All @@ -156,6 +158,10 @@ private void generateCode(MethodDefinition methodDefinition) throws IOException

out.println();

out.println("/**");
out.println(" * Generated by {@link " + getClass().getName() + "}.");
out.println(" * From {@link " + methodDefinition.getOriginalClassName() + "}.");
out.println(" */");
out.println("@NodeInfo(");
out.println(" shortName = \"" + methodDefinition.getDeclaredName() + "\",");
out.println(" description = \"\"\"\n" + methodDefinition.getDescription() + "\"\"\")");
Expand All @@ -165,13 +171,13 @@ private void generateCode(MethodDefinition methodDefinition) throws IOException
out.println("public class " + methodDefinition.getClassName() + " extends BuiltinRootNode implements InlineableNode.Root {");
}
out.println(" private @Child " + methodDefinition.getOriginalClassName() + " bodyNode;");
out.println();
out.println(" private static final class Internals {");
out.println(" Internals(boolean s) {");
out.println(" this.staticOfInstanceMethod = s;");
out.println(" }");
out.println(" private final boolean staticOfInstanceMethod;");

out.println();
out.println(" private final boolean staticOfInstanceMethod;");

for (MethodDefinition.ArgumentDefinition arg : methodDefinition.getArguments()) {
if (arg.shouldCheckErrors()) {
Expand Down Expand Up @@ -278,6 +284,7 @@ private void generateCode(MethodDefinition methodDefinition) throws IOException
generateWarningsCheck(out, methodDefinition.getArguments(), "arguments");
for (MethodDefinition.ArgumentDefinition argumentDefinition :
methodDefinition.getArguments()) {
out.println(" /*** Start of processing argument " + argumentDefinition.getPosition() + " ***/");
if (argumentDefinition.isImplicit()) {
} else if (argumentDefinition.isState()) {
callArgNames.add("state");
Expand All @@ -289,6 +296,7 @@ private void generateCode(MethodDefinition methodDefinition) throws IOException
callArgNames.add(mkArgumentInternalVarName(argumentDefinition));
generateArgumentRead(out, argumentDefinition, "arguments");
}
out.println(" /*** End of processing argument " + argumentDefinition.getPosition() + " ***/");
}
String executeCall = "bodyNode.execute(" + String.join(", ", callArgNames) + ")";
if (warningsPossible) {
Expand Down Expand Up @@ -458,36 +466,73 @@ private void generateUncheckedArrayCast(
private void generateCheckedArgumentRead(
PrintWriter out, MethodDefinition.ArgumentDefinition arg, String argsArray) {
String builtinName = capitalize(arg.getTypeName());
String castName = "TypesGen.expect" + builtinName;
String varName = mkArgumentInternalVarName(arg);
out.println(" " + arg.getTypeName() + " " + varName + ";");
out.println(" try {");
out.println(
" " + varName + " = " + castName + "(" + argsArray + "[arg" + arg.getPosition() + "Idx]);");
out.println(" } catch (UnexpectedResultException e) {");
out.println(" com.oracle.truffle.api.CompilerDirectives.transferToInterpreter();");
out.println(" var builtins = EnsoContext.get(bodyNode).getBuiltins();");
out.println(" var ensoTypeName = org.enso.interpreter.runtime.type.ConstantsGen.getEnsoTypeName(\"" + builtinName + "\");");
out.println(" var error = (ensoTypeName != null)");
out.println(" ? builtins.error().makeTypeError(builtins.fromTypeSystem(ensoTypeName), arguments[arg"
+ arg.getPosition()
+ "Idx], \""
+ varName
+ "\")");
out.println(" : builtins.error().makeUnsupportedArgumentsError(new Object[] { arguments[arg"
+ arg.getPosition()
+ "Idx] }, \"Unsupported argument for "
+ varName
+ " expected a '"
+ builtinName
+ "' but got a '\""
+ " + arguments[arg" + arg.getPosition() + "Idx]"
+ " + \"' [\""
+ " + arguments[arg" + arg.getPosition() + "Idx].getClass()"
+ " + \"]\""
+ ");");
out.println(" throw new PanicException(error, bodyNode);");
out.println(" }");
if (arg.getTypeName().equals("Array")) {
String argFromArray = argsArray + "[arg" + arg.getPosition() + "Idx]";
String uncachedInterop = "InteropLibrary.getUncached()";
String errBuiltin = "EnsoContext.get(bodyNode).getBuiltins().error()";
out.println(" // " + argFromArray + " is expected to be an Array-like");
out.println(" if (TypesGen.isArray(" + argFromArray + ")) {");
out.println(" " + varName + " = TypesGen.asArray(" + argFromArray + ");");
out.println(" } else if (TypesGen.isVector(" + argFromArray + ")) {");
out.println(" try {");
out.println(" " + varName + " = TypesGen.asVector(" + argFromArray + ").toEnsoArray();");
out.println(" } catch (InvalidArrayIndexException e) {");
out.println(" var err = " + errBuiltin + ".makeInvalidArrayIndex(" + argFromArray + ", e.getInvalidIndex());");
out.println(" throw new PanicException(err, bodyNode);");
out.println(" }");
out.println(" } else {");
out.println(" com.oracle.truffle.api.CompilerDirectives.transferToInterpreter();");
out.println(" try {");
out.println(" // Try unwrapping the interop array elements into plain Java array");
out.println(" long arrSize = " + uncachedInterop + ".getArraySize(" + argFromArray + ");");
out.println(" Object[] storage = new Object[Math.toIntExact(arrSize)];");
out.println(" for (int i = 0; i < arrSize; i++) {");
out.println(" if (" + uncachedInterop + ".isArrayElementReadable(" + argFromArray + ", i)) {");
out.println(" Object elem = " + uncachedInterop + ".readArrayElement(" + argFromArray + ", i);");
out.println(" storage[i] = elem;");
out.println(" } else {");
out.println(" com.oracle.truffle.api.CompilerDirectives.transferToInterpreter();");
out.println(" var err = " + errBuiltin + ".makeInvalidArrayIndex(" + argFromArray + ", i);");
out.println(" throw new PanicException(err, bodyNode);");
out.println(" }"); // end else
out.println(" }"); // end for
out.println(" " + varName + " = new Array(storage);");
out.println(" } catch (UnsupportedMessageException | InvalidArrayIndexException e) {");
out.println(" throw new IllegalStateException(\"Unreachable: Failed to unwrap interop array\", e);");
out.println(" }");
out.println(" }"); // end else
} else {
String castName = "TypesGen.expect" + builtinName;
out.println(" try {");
out.println(
" " + varName + " = " + castName + "(" + argsArray + "[arg" + arg.getPosition() + "Idx]);");
out.println(" } catch (UnexpectedResultException e) {");
out.println(" com.oracle.truffle.api.CompilerDirectives.transferToInterpreter();");
out.println(" var builtins = EnsoContext.get(bodyNode).getBuiltins();");
out.println(" var ensoTypeName = org.enso.interpreter.runtime.type.ConstantsGen.getEnsoTypeName(\"" + builtinName + "\");");
out.println(" var error = (ensoTypeName != null)");
out.println(" ? builtins.error().makeTypeError(builtins.fromTypeSystem(ensoTypeName), arguments[arg"
+ arg.getPosition()
+ "Idx], \""
+ varName
+ "\")");
out.println(" : builtins.error().makeUnsupportedArgumentsError(new Object[] { arguments[arg"
+ arg.getPosition()
+ "Idx] }, \"Unsupported argument for "
+ varName
+ " expected a '"
+ builtinName
+ "' but got a '\""
+ " + arguments[arg" + arg.getPosition() + "Idx]"
+ " + \"' [\""
+ " + arguments[arg" + arg.getPosition() + "Idx].getClass()"
+ " + \"]\""
+ ");");
out.println(" throw new PanicException(error, bodyNode);");
out.println(" }");
}
}

private boolean generateWarningsCheck(
Expand Down
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK.

Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ public void generate(
throws IOException {
JavaFileObject gen =
processingEnv.getFiler().createSourceFile(builtinNode.jvmFriendlyFullyQualifiedName());
;
try (PrintWriter out = new PrintWriter(gen.openWriter())) {
String ensoMethodName = CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, methodName);
String ensoTypeName = stdlibOwner.name().replaceAll("([a-z])([A-Z])", "$1_$2");
Expand All @@ -60,6 +59,10 @@ public void generate(
if (needsFrame != null) {
moduleOwnerInfo = moduleOwnerInfo + ", needsFrame = " + needsFrame;
}
out.println("/**");
out.println(" * Generated by {@link org.enso.interpreter.dsl.builtins.MethodNodeClassGenerator}.");
out.println(" * From {@link " + ownerClazz.fullyQualifiedName() + "#" + ownerMethodName + "}.");
out.println(" */");
out.println(
"@BuiltinMethod(type = \""
+ ensoTypeName
Expand Down
Loading