diff --git a/build.sbt b/build.sbt index 67bb9982397..3f7f8f9d322 100644 --- a/build.sbt +++ b/build.sbt @@ -959,6 +959,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 + ), commands += WithDebugCommand.withDebug, libraryDependencies ++= Seq( "org.graalvm.truffle" % "truffle-api" % graalVersion % "provided", diff --git a/distribution/lib/Standard/Base/0.0.0-dev/src/System/File.enso b/distribution/lib/Standard/Base/0.0.0-dev/src/System/File.enso index 4563d0f22a6..c16e34e7983 100644 --- a/distribution/lib/Standard/Base/0.0.0-dev/src/System/File.enso +++ b/distribution/lib/Standard/Base/0.0.0-dev/src/System/File.enso @@ -36,6 +36,7 @@ polyglot java import java.io.OutputStream as Java_Output_Stream polyglot java import java.nio.file.FileSystems polyglot java import java.nio.file.Path polyglot java import java.nio.file.StandardCopyOption +polyglot java import java.nio.file.StandardOpenOption polyglot java import java.time.ZonedDateTime polyglot java import org.enso.base.DryRunFileManager polyglot java import org.enso.base.encoding.ReportingStreamDecoder @@ -145,9 +146,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 @@ -180,9 +181,10 @@ type File `File.with_input_stream` instead, which does resource management. Arguments: - - open_options: A vector of `File_Access` objects determining how to open - the stream. These options set the access properties of the stream. - input_stream : Vector File_Access -> Input_Stream + - open_options: A vector of `StandardOpenOption` polyglot objects + determining how to open the stream. These options set the access + properties of the stream. + input_stream : Vector StandardOpenOption -> Input_Stream input_stream self options = @Builtin_Method "File.input_stream" ## PRIVATE @@ -564,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. @@ -579,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. @@ -609,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 diff --git a/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/EnsoFile.java b/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/EnsoFile.java index 3d73e1e2eb2..b4270b5263f 100644 --- a/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/EnsoFile.java +++ b/engine/runtime/src/main/java/org/enso/interpreter/runtime/data/EnsoFile.java @@ -4,12 +4,16 @@ import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.TruffleFile; import com.oracle.truffle.api.interop.InteropLibrary; +import com.oracle.truffle.api.interop.InvalidArrayIndexException; import com.oracle.truffle.api.interop.TruffleObject; +import com.oracle.truffle.api.interop.UnsupportedMessageException; import com.oracle.truffle.api.library.CachedLibrary; import com.oracle.truffle.api.library.ExportLibrary; import com.oracle.truffle.api.library.ExportMessage; +import java.util.function.IntFunction; import org.enso.interpreter.dsl.Builtin; import org.enso.interpreter.runtime.EnsoContext; +import org.enso.interpreter.runtime.error.PanicException; import org.enso.interpreter.runtime.library.dispatch.TypesLibrary; import java.io.IOException; @@ -45,17 +49,58 @@ public final class EnsoFile implements TruffleObject { @Builtin.Method(name = "output_stream_builtin") @Builtin.WrapException(from = IOException.class) @Builtin.ReturningGuestObject + @Builtin.Specialize @CompilerDirectives.TruffleBoundary - public OutputStream outputStream(OpenOption[] opts) throws IOException { - return this.truffleFile.newOutputStream(opts); + public OutputStream outputStream(Object opts, + EnsoContext ctx, + @CachedLibrary(limit = "5") InteropLibrary interop) throws IOException { + OpenOption[] openOptions = convertInteropArray(opts, interop, ctx, OpenOption[]::new); + return this.truffleFile.newOutputStream(openOptions); } @Builtin.Method @Builtin.WrapException(from = IOException.class) + @Builtin.Specialize @Builtin.ReturningGuestObject @CompilerDirectives.TruffleBoundary - public InputStream inputStream(OpenOption[] opts) throws IOException { - return this.truffleFile.newInputStream(opts); + public InputStream inputStream(Object opts, + EnsoContext ctx, + @CachedLibrary(limit = "5") InteropLibrary interop) throws IOException { + OpenOption[] openOptions = convertInteropArray(opts, interop, ctx, OpenOption[]::new); + return this.truffleFile.newInputStream(openOptions); + } + + @SuppressWarnings("unchecked") + private static T[] convertInteropArray(Object arr, + InteropLibrary interop, + EnsoContext ctx, + IntFunction hostArrayCtor) { + if (!interop.hasArrayElements(arr)) { + var vecType = ctx.getBuiltins().vector().getType(); + var typeError = ctx.getBuiltins().error().makeTypeError(vecType, arr, "opts"); + throw new PanicException(typeError, interop); + } + T[] hostArr; + try { + int size = Math.toIntExact(interop.getArraySize(arr)); + hostArr = hostArrayCtor.apply(size); + for (int i = 0; i < size; i++) { + Object elem = interop.readArrayElement(arr, i); + if (!ctx.getEnvironment().isHostObject(elem)) { + var err = ctx.getBuiltins().error().makeUnsupportedArgumentsError( + new Object[]{arr}, + "Arguments to opts should be host objects from java.io package" + ); + throw new PanicException(err, interop); + } + hostArr[i] = (T) ctx.getEnvironment().asHostObject(elem); + } + } catch (UnsupportedMessageException | InvalidArrayIndexException e) { + throw new IllegalStateException("Unreachable", e); + } catch (ClassCastException e) { + throw new PanicException(e, interop); + } + return hostArr; } @Builtin.Method(name = "read_last_bytes_builtin") @@ -221,16 +266,24 @@ public final class EnsoFile implements TruffleObject { @Builtin.Method(name = "copy_builtin", description = "Copy this file to a target destination") @Builtin.WrapException(from = IOException.class) + @Builtin.Specialize @CompilerDirectives.TruffleBoundary - public void copy(EnsoFile target, CopyOption[] options) throws IOException { - truffleFile.copy(target.truffleFile, options); + public void copy(EnsoFile target, Object options, + @CachedLibrary(limit = "5") InteropLibrary interop, + EnsoContext ctx) throws IOException { + CopyOption[] copyOptions = convertInteropArray(options, interop, ctx, CopyOption[]::new); + truffleFile.copy(target.truffleFile, copyOptions); } @Builtin.Method(name = "move_builtin", description = "Move this file to a target destination") @Builtin.WrapException(from = IOException.class) + @Builtin.Specialize @CompilerDirectives.TruffleBoundary - public void move(EnsoFile target, CopyOption[] options) throws IOException { - truffleFile.move(target.truffleFile, options); + public void move(EnsoFile target, Object options, + @CachedLibrary(limit = "5") InteropLibrary interop, + EnsoContext ctx) throws IOException { + CopyOption[] copyOptions = convertInteropArray(options, interop, ctx, CopyOption[]::new); + truffleFile.move(target.truffleFile, copyOptions); } @Builtin.Method diff --git a/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/BuiltinsProcessor.java b/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/BuiltinsProcessor.java index a6508d59db5..c710e643810 100644 --- a/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/BuiltinsProcessor.java +++ b/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/BuiltinsProcessor.java @@ -178,7 +178,7 @@ public class BuiltinsProcessor extends AbstractProcessor { try { MethodNodeClassGenerator classGenerator = new NoSpecializationClassGenerator( - method, builtinMethodNode, ownerClass, stdLibOwnerClass, i); + processingEnv, method, builtinMethodNode, ownerClass, stdLibOwnerClass, i); classGenerator.generate( processingEnv, methodName, @@ -232,7 +232,7 @@ public class BuiltinsProcessor extends AbstractProcessor { if (encountered.size() == expected) { MethodNodeClassGenerator classGenerator = new SpecializationClassGenerator( - encountered, builtinMethodNode, ownerClass, stdLibOwnerClass); + processingEnv, encountered, builtinMethodNode, ownerClass, stdLibOwnerClass); classGenerator.generate( processingEnv, builtinMethodName, @@ -243,7 +243,7 @@ public class BuiltinsProcessor extends AbstractProcessor { } else { MethodNodeClassGenerator classGenerator = new NoSpecializationClassGenerator( - method, builtinMethodNode, ownerClass, stdLibOwnerClass); + processingEnv, method, builtinMethodNode, ownerClass, stdLibOwnerClass); classGenerator.generate( processingEnv, builtinMethodName, diff --git a/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/MethodProcessor.java b/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/MethodProcessor.java index 1347a1eed1b..0c70affe881 100644 --- a/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/MethodProcessor.java +++ b/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/MethodProcessor.java @@ -144,7 +144,6 @@ public class MethodProcessor extends BuiltinsMetadataProcessor allImports = new HashSet<>(necessaryImports); allImports.addAll(methodDefinition.getImports()); @@ -156,6 +155,10 @@ public class MethodProcessor extends BuiltinsMetadataProcessor 0); @@ -112,6 +122,15 @@ public final class ExecuteMethodImplGenerator extends MethodGenerator { if (returnTpe.isValidGuestType()) { return new String[] {" return " + qual + "." + name + "(" + paramsApplied + ");"}; } else { + if (!convertToGuestValue) { + processingEnvironment + .getMessager() + .printMessage( + javax.tools.Diagnostic.Kind.ERROR, + "Cannot generate method body for " + + method + + " because it returns a host object and convertToGuestValue is false"); + } return new String[] { " return context", " .getEnvironment()", @@ -134,14 +153,16 @@ public final class ExecuteMethodImplGenerator extends MethodGenerator { return result || params.stream().anyMatch(p -> p.needsToHostTranslation()); } - public List generate(ProcessingEnvironment processingEnv, String name, String owner) { - - SafeWrapException[] exceptionWrappers = wrapExceptions(processingEnv, method); + public List generate(String name, String owner) { + SafeWrapException[] exceptionWrappers = wrapExceptions(processingEnvironment, method); boolean wrapsExceptions = exceptionWrappers.length != 0; List rawParams = method.getParameters(); List params = IntStream.range(0, method.getParameters().size()) - .mapToObj(i -> fromVariableElementToMethodParameter(i, rawParams.get(i))) + .mapToObj( + i -> + fromVariableElementToMethodParameter( + processingEnvironment, i, rawParams.get(i))) .collect(Collectors.toList()); String[] body = bodyBase(name, owner, params); diff --git a/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/builtins/MethodGenerator.java b/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/builtins/MethodGenerator.java index e872a83ed3c..e9c939f93ec 100644 --- a/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/builtins/MethodGenerator.java +++ b/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/builtins/MethodGenerator.java @@ -14,14 +14,16 @@ import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.*; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.Types; +import javax.tools.Diagnostic.Kind; import org.apache.commons.lang3.StringUtils; import org.enso.interpreter.dsl.Builtin; public abstract class MethodGenerator { protected final boolean isStatic; protected final boolean isConstructor; - private final boolean convertToGuestValue; + protected final boolean convertToGuestValue; protected final TypeWithKind returnTpe; + protected final ProcessingEnvironment processingEnvironment; private static final WrapExceptionExtractor wrapExceptionsExtractor = new WrapExceptionExtractor(Builtin.WrapException.class, Builtin.WrapExceptions.class); @@ -32,18 +34,19 @@ public abstract class MethodGenerator { } public MethodGenerator( + ProcessingEnvironment processingEnvironment, boolean isStatic, boolean isConstructor, boolean convertToGuestValue, TypeWithKind returnTpe) { + this.processingEnvironment = processingEnvironment; this.isStatic = isStatic; this.isConstructor = isConstructor; this.convertToGuestValue = convertToGuestValue; this.returnTpe = returnTpe; } - public abstract List generate( - ProcessingEnvironment processingEnv, String name, String owner); + public abstract List generate(String name, String owner); /** * Generate node's `execute` method definition (return type and necessary parameters). ' @@ -91,10 +94,13 @@ public abstract class MethodGenerator { return tpe.baseType(); } else { if (!convertToGuestValue) { - throw new RuntimeException( - "If intended, automatic conversion of value of type " - + tpe.baseType() - + " to guest value requires explicit '@Builtin.ReturningGuestObject' annotation"); + processingEnvironment + .getMessager() + .printMessage( + Kind.ERROR, + "Automatic conversion of value of type " + + tpe.baseType() + + " to guest value requires explicit '@Builtin.ReturningGuestObject' annotation"); } return "Object"; } @@ -111,14 +117,28 @@ public abstract class MethodGenerator { * @param v variable element representing the parameter * @return MethodParameter encapsulating the method's parameter info */ - protected MethodParameter fromVariableElementToMethodParameter(int i, VariableElement v) { + protected MethodParameter fromVariableElementToMethodParameter( + ProcessingEnvironment processingEnv, int i, VariableElement v) { String ensoName = CaseFormat.LOWER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, v.getSimpleName().toString()); + TypeWithKind tpe = TypeWithKind.createFromTpe(v.asType().toString()); + if (tpe.kind() == TypeKind.ARRAY && !tpe.isValidGuestType()) { + processingEnv + .getMessager() + .printMessage( + Kind.ERROR, + "Parameter " + + v + + " is an array of host objects, which " + + "is not supported by the MethodGenerator. Either use array of primitive, or valid guest objects, " + + "or accept the array as Object and transform it in the method body.", + v); + } return new MethodParameter( i, ensoName, v.asType().toString(), - v.getAnnotationMirrors().stream().map(am -> am.toString()).collect(Collectors.toList())); + v.getAnnotationMirrors().stream().map(Object::toString).collect(Collectors.toList())); } protected abstract Optional expandVararg(int paramsLen, int paramIndex); diff --git a/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/builtins/MethodNodeClassGenerator.java b/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/builtins/MethodNodeClassGenerator.java index a41181c8c29..807a1147d2e 100644 --- a/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/builtins/MethodNodeClassGenerator.java +++ b/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/builtins/MethodNodeClassGenerator.java @@ -42,7 +42,6 @@ public abstract class MethodNodeClassGenerator { 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"); @@ -60,6 +59,12 @@ public abstract class MethodNodeClassGenerator { 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 @@ -83,7 +88,7 @@ public abstract class MethodNodeClassGenerator { out.println("public class " + builtinNode.jvmFriendlyName() + " extends Node {"); out.println(); } - for (String line : methodsGen().generate(processingEnv, ownerMethodName, ownerClazz.name())) { + for (String line : methodsGen().generate(ownerMethodName, ownerClazz.name())) { out.println(" " + line); } out.println(); diff --git a/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/builtins/NoSpecializationClassGenerator.java b/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/builtins/NoSpecializationClassGenerator.java index 3df6250c495..7d250f4008d 100644 --- a/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/builtins/NoSpecializationClassGenerator.java +++ b/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/builtins/NoSpecializationClassGenerator.java @@ -1,5 +1,6 @@ package org.enso.interpreter.dsl.builtins; +import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.ExecutableElement; /** Generator for builtin method class with no specialization. */ @@ -7,8 +8,10 @@ public final class NoSpecializationClassGenerator extends MethodNodeClassGenerat ExecutableElement origin; int varArgExpansion; + private final ProcessingEnvironment processingEnvironment; public NoSpecializationClassGenerator( + ProcessingEnvironment processingEnvironment, ExecutableElement origin, ClassName builtinNode, ClassName ownerClazz, @@ -17,20 +20,22 @@ public final class NoSpecializationClassGenerator extends MethodNodeClassGenerat super(builtinNode, ownerClazz, stdlibOwner); this.origin = origin; this.varArgExpansion = varArgExpansion; + this.processingEnvironment = processingEnvironment; } public NoSpecializationClassGenerator( + ProcessingEnvironment processingEnvironment, ExecutableElement origin, ClassName builtinNode, ClassName ownerClazz, ClassName stdlibOwner) { - this(origin, builtinNode, ownerClazz, stdlibOwner, 0); + this(processingEnvironment, origin, builtinNode, ownerClazz, stdlibOwner, 0); } @Override protected MethodGenerator methodsGen() { return new ExecuteMethodImplGenerator( - origin, needsGuestValueConversion(origin), varArgExpansion); + processingEnvironment, origin, needsGuestValueConversion(origin), varArgExpansion); } @Override diff --git a/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/builtins/SpecializationClassGenerator.java b/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/builtins/SpecializationClassGenerator.java index a3e91b545b4..fb0df0aca56 100644 --- a/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/builtins/SpecializationClassGenerator.java +++ b/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/builtins/SpecializationClassGenerator.java @@ -1,6 +1,7 @@ package org.enso.interpreter.dsl.builtins; import java.util.List; +import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.ExecutableElement; /** @@ -10,19 +11,22 @@ import javax.lang.model.element.ExecutableElement; */ public class SpecializationClassGenerator extends MethodNodeClassGenerator { List elements; + private final ProcessingEnvironment processingEnvironment; public SpecializationClassGenerator( + ProcessingEnvironment processingEnvironment, List methodElements, ClassName builtinNode, ClassName ownerClazz, ClassName stdlibOwner) { super(builtinNode, ownerClazz, stdlibOwner); this.elements = methodElements; + this.processingEnvironment = processingEnvironment; } @Override protected MethodGenerator methodsGen() { - return new SpecializedMethodsGenerator(elements); + return new SpecializedMethodsGenerator(processingEnvironment, elements); } @Override diff --git a/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/builtins/SpecializedMethodsGenerator.java b/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/builtins/SpecializedMethodsGenerator.java index 2495990fe23..492801031f4 100644 --- a/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/builtins/SpecializedMethodsGenerator.java +++ b/lib/scala/interpreter-dsl/src/main/java/org/enso/interpreter/dsl/builtins/SpecializedMethodsGenerator.java @@ -13,6 +13,7 @@ import javax.lang.model.element.ElementKind; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Modifier; import javax.lang.model.element.VariableElement; +import javax.tools.Diagnostic; import org.apache.commons.lang3.StringUtils; import org.enso.interpreter.dsl.AcceptsWarning; import org.enso.interpreter.dsl.Builtin; @@ -23,12 +24,17 @@ public final class SpecializedMethodsGenerator extends MethodGenerator { private static final String WithWarningsClassName = "org.enso.interpreter.runtime.error.WithWarnings"; - public SpecializedMethodsGenerator(List elements) { - this(elements, elements.get(0)); + public SpecializedMethodsGenerator( + ProcessingEnvironment processingEnvironment, List elements) { + this(processingEnvironment, elements, elements.get(0)); } - private SpecializedMethodsGenerator(List elements, ExecutableElement first) { + private SpecializedMethodsGenerator( + ProcessingEnvironment processingEnvironment, + List elements, + ExecutableElement first) { this( + processingEnvironment, elements, first.getModifiers().contains(Modifier.STATIC), first.getKind() == ElementKind.CONSTRUCTOR, @@ -45,12 +51,13 @@ public final class SpecializedMethodsGenerator extends MethodGenerator { } public SpecializedMethodsGenerator( + ProcessingEnvironment processingEnvironment, List elements, boolean isStatic, boolean isConstructor, boolean convertToGuestValue, TypeWithKind returnTpe) { - super(isStatic, isConstructor, convertToGuestValue, returnTpe); + super(processingEnvironment, isStatic, isConstructor, convertToGuestValue, returnTpe); this.elements = elements; } @@ -59,14 +66,14 @@ public final class SpecializedMethodsGenerator extends MethodGenerator { } @Override - public List generate(ProcessingEnvironment processingEnv, String name, String owner) { - SpecializationMeta meta = inferExecuteParameters(); + public List generate(String name, String owner) { + SpecializationMeta meta = inferExecuteParameters(processingEnvironment); List result = new ArrayList<>(); result.add(methodSigDef(owner, meta.execParams(), true)); result.add(""); result.addAll( - paramsOfSpecializedMethods(processingEnv, meta.diffParam) + paramsOfSpecializedMethods(processingEnvironment, meta.diffParam) .flatMap( specializeMethod -> specialize(owner, name, specializeMethod, meta.diffParam).stream()) @@ -98,7 +105,10 @@ public final class SpecializedMethodsGenerator extends MethodGenerator { return new SpecializeMethodInfo( method, IntStream.range(0, params.size()) - .mapToObj(i -> fromVariableElementToMethodParameter(i, params.get(i))) + .mapToObj( + i -> + fromVariableElementToMethodParameter( + processingEnv, i, params.get(i))) .collect(Collectors.toList()), wrapExceptions(processingEnv, method)); }); @@ -132,14 +142,17 @@ public final class SpecializedMethodsGenerator extends MethodGenerator { return p.tpe().equals("java.lang.Object") || p.tpe().equals("java.lang.String"); } - private SpecializationMeta inferExecuteParameters() { + private SpecializationMeta inferExecuteParameters(ProcessingEnvironment processingEnv) { Map> paramss = elements.stream() .flatMap( method -> { List params = method.getParameters(); return IntStream.range(0, params.size()) - .mapToObj(i -> fromVariableElementToMethodParameter(i, params.get(i))); + .mapToObj( + i -> + fromVariableElementToMethodParameter( + processingEnv, i, params.get(i))); }) .collect(Collectors.groupingBy(p -> p.index())); @@ -268,7 +281,24 @@ public final class SpecializedMethodsGenerator extends MethodGenerator { " return new Array((Object[]) " + qual + "." + name + "(" + paramsApplied + "));"); break; default: - methodBody.add(" return " + qual + "." + name + "(" + paramsApplied + ");"); + if (returnTpe.isValidGuestType()) { + methodBody.add(" return " + qual + "." + name + "(" + paramsApplied + ");"); + } else if (convertToGuestValue) { + methodBody.add(" var result = " + qual + "." + name + "(" + paramsApplied + ");"); + methodBody.add(" return EnsoContext.get(this).getEnvironment().asGuestValue(result);"); + } else { + processingEnvironment + .getMessager() + .printMessage( + Diagnostic.Kind.ERROR, + "Cannot convert return type of " + + owner + + "." + + name + + " to guest value." + + " Specify @ReturningGuestValue annotation", + methodInfo.origin); + } } }