Run whole test/Base_Tests in native image runner (#10296)

This commit is contained in:
Jaroslav Tulach 2024-06-21 06:03:53 +02:00 committed by GitHub
parent b5641aa3bd
commit fe2cf49568
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
48 changed files with 1562 additions and 270 deletions

2
.vscode/launch.json vendored
View File

@ -33,7 +33,7 @@
"request": "launch", "request": "launch",
"name": "Launch Native Image", "name": "Launch Native Image",
"nativeImagePath": "${workspaceFolder}/runner", "nativeImagePath": "${workspaceFolder}/runner",
"args": "--help" "args": "--run ${file}"
} }
] ]
} }

View File

@ -2549,8 +2549,10 @@ lazy val `engine-runner` = project
if (smallJdkDirectory.exists()) { if (smallJdkDirectory.exists()) {
IO.delete(smallJdkDirectory) IO.delete(smallJdkDirectory)
} }
val JS_MODULES = val NI_MODULES =
"org.graalvm.nativeimage,org.graalvm.nativeimage.builder,org.graalvm.nativeimage.base,org.graalvm.nativeimage.driver,org.graalvm.nativeimage.librarysupport,org.graalvm.nativeimage.objectfile,org.graalvm.nativeimage.pointsto,com.oracle.graal.graal_enterprise,com.oracle.svm.svm_enterprise,jdk.compiler.graal,jdk.httpserver,java.naming,java.net.http" "org.graalvm.nativeimage,org.graalvm.nativeimage.builder,org.graalvm.nativeimage.base,org.graalvm.nativeimage.driver,org.graalvm.nativeimage.librarysupport,org.graalvm.nativeimage.objectfile,org.graalvm.nativeimage.pointsto,com.oracle.graal.graal_enterprise,com.oracle.svm.svm_enterprise"
val JDK_MODULES =
"jdk.localedata,jdk.compiler.graal,jdk.httpserver,java.naming,java.net.http"
val DEBUG_MODULES = "jdk.jdwp.agent" val DEBUG_MODULES = "jdk.jdwp.agent"
val PYTHON_MODULES = "jdk.security.auth,java.naming" val PYTHON_MODULES = "jdk.security.auth,java.naming"
@ -2578,7 +2580,7 @@ lazy val `engine-runner` = project
} }
val exec = val exec =
s"$jlink --module-path ${modules.mkString(":")} --output $smallJdkDirectory --add-modules $JS_MODULES,$DEBUG_MODULES,$PYTHON_MODULES" s"$jlink --module-path ${modules.mkString(":")} --output $smallJdkDirectory --add-modules $NI_MODULES,$JDK_MODULES,$DEBUG_MODULES,$PYTHON_MODULES"
val exitCode = scala.sys.process.Process(exec).! val exitCode = scala.sys.process.Process(exec).!
if (exitCode != 0) { if (exitCode != 0) {
@ -2610,6 +2612,9 @@ lazy val `engine-runner` = project
additionalOptions = Seq( additionalOptions = Seq(
"-Dorg.apache.commons.logging.Log=org.apache.commons.logging.impl.NoOpLog", "-Dorg.apache.commons.logging.Log=org.apache.commons.logging.impl.NoOpLog",
"-H:IncludeResources=.*Main.enso$", "-H:IncludeResources=.*Main.enso$",
"-H:+AddAllCharsets",
"-H:+IncludeAllLocales",
"-ea",
// useful perf & debug switches: // useful perf & debug switches:
// "-g", // "-g",
// "-H:+SourceLevelDebug", // "-H:+SourceLevelDebug",
@ -2660,6 +2665,7 @@ lazy val `engine-runner` = project
.dependsOn(`logging-service`) .dependsOn(`logging-service`)
.dependsOn(`logging-service-logback` % Runtime) .dependsOn(`logging-service-logback` % Runtime)
.dependsOn(`polyglot-api`) .dependsOn(`polyglot-api`)
.dependsOn(`enso-test-java-helpers`)
lazy val buildSmallJdk = lazy val buildSmallJdk =
taskKey[File]("Build a minimal JDK used for native image generation") taskKey[File]("Build a minimal JDK used for native image generation")
@ -3567,7 +3573,7 @@ ThisBuild / buildEngineDistributionNoIndex := {
lazy val runEngineDistribution = lazy val runEngineDistribution =
inputKey[Unit]("Run or --debug the engine distribution with arguments") inputKey[Unit]("Run or --debug the engine distribution with arguments")
runEngineDistribution := { runEngineDistribution := {
buildEngineDistribution.value buildEngineDistributionNoIndex.value
val args: Seq[String] = spaceDelimited("<arg>").parsed val args: Seq[String] = spaceDelimited("<arg>").parsed
DistributionPackage.runEnginePackage( DistributionPackage.runEnginePackage(
engineDistributionRoot.value, engineDistributionRoot.value,

View File

@ -630,28 +630,12 @@ pub async fn runner_sanity_test(
repo_root: &crate::paths::generated::RepoRoot, repo_root: &crate::paths::generated::RepoRoot,
enso_java: Option<&str>, enso_java: Option<&str>,
) -> Result { ) -> Result {
let factorial_input = "6";
let factorial_expected_output = "720";
let engine_package = repo_root.built_distribution.enso_engine_triple.engine_package.as_path(); let engine_package = repo_root.built_distribution.enso_engine_triple.engine_package.as_path();
// The engine package is necessary for running the native runner. // The engine package is necessary for running the native runner.
ide_ci::fs::tokio::require_exist(engine_package).await?; ide_ci::fs::tokio::require_exist(engine_package).await?;
let output = Command::new(&repo_root.runner)
.args([
"--run",
repo_root.engine.runner.src.test.resources.factorial_enso.as_str(),
factorial_input,
])
.set_env_opt(ENSO_JAVA, enso_java)?
.set_env(ENSO_DATA_DIRECTORY, engine_package)?
.run_stdout()
.await?;
ensure!(
output.contains(factorial_expected_output),
"Native runner output does not contain expected result '{factorial_expected_output}'. Output:\n{output}",
);
if enso_java.is_none() { if enso_java.is_none() {
let test_base = Command::new(&repo_root.runner) let test_base = Command::new(&repo_root.runner)
.args(["--run", repo_root.test.join("Base_Tests").as_str(), "^Text"]) .args(["--run", repo_root.test.join("Base_Tests").as_str()])
.set_env_opt(ENSO_JAVA, enso_java)? .set_env_opt(ENSO_JAVA, enso_java)?
.set_env(ENSO_DATA_DIRECTORY, engine_package)? .set_env(ENSO_DATA_DIRECTORY, engine_package)?
.run_stdout() .run_stdout()

View File

@ -4,6 +4,8 @@ import project.Errors.Encoding_Error.Encoding_Error
from project.Data.Text.Extensions import all from project.Data.Text.Extensions import all
polyglot java import java.util.Base64 polyglot java import java.util.Base64
polyglot java import java.util.Base64.Decoder
polyglot java import java.util.Base64.Encoder
## A helper utility for handling base64 encoding. ## A helper utility for handling base64 encoding.
type Base_64 type Base_64

View File

@ -30,6 +30,7 @@ from project.Data.Text.Extensions import all
from project.Metadata.Choice import Option from project.Metadata.Choice import Option
from project.Metadata.Widget import Single_Choice from project.Metadata.Widget import Single_Choice
polyglot java import com.fasterxml.jackson.core.JsonLocation
polyglot java import com.fasterxml.jackson.core.JsonProcessingException polyglot java import com.fasterxml.jackson.core.JsonProcessingException
polyglot java import com.fasterxml.jackson.databind.JsonNode polyglot java import com.fasterxml.jackson.databind.JsonNode
polyglot java import com.fasterxml.jackson.databind.node.ArrayNode polyglot java import com.fasterxml.jackson.databind.node.ArrayNode

View File

@ -9,6 +9,7 @@ import project.Nothing.Nothing
import project.Panic.Panic import project.Panic.Panic
polyglot java import org.enso.base.statistics.FitError polyglot java import org.enso.base.statistics.FitError
polyglot java import org.enso.base.statistics.LinearModel
polyglot java import org.enso.base.statistics.Regression polyglot java import org.enso.base.statistics.Regression
type Model type Model

View File

@ -544,7 +544,7 @@ Text.replace self term:(Text | Regex) replacement:Text (case_sensitivity:Case_Se
Applies the specified cleansings to the text. Applies the specified cleansings to the text.
Arguments: Arguments:
- remove: A vector of the named patterns to cleanse from the text. The named patterns are - remove: A vector of the named patterns to cleanse from the text. The named patterns are
applied in the order they are provided. The same named pattern can be used multiple applied in the order they are provided. The same named pattern can be used multiple
times. The named patterns are: times. The named patterns are:
- ..Leading_Whitespace: Removes all whitespace from the start of the string. - ..Leading_Whitespace: Removes all whitespace from the start of the string.

View File

@ -34,6 +34,7 @@ polyglot java import java.time.DateTimeException
polyglot java import java.time.temporal.ChronoField polyglot java import java.time.temporal.ChronoField
polyglot java import java.time.temporal.IsoFields polyglot java import java.time.temporal.IsoFields
polyglot java import org.enso.base.Time_Utils polyglot java import org.enso.base.Time_Utils
polyglot java import org.enso.base.Time_Utils.AdjustOp
## PRIVATE ## PRIVATE
Constructs a new Date from a year, month, and day. Constructs a new Date from a year, month, and day.

View File

@ -11,7 +11,10 @@ polyglot java import java.time.temporal.TemporalAdjuster
polyglot java import java.time.temporal.TemporalAdjusters polyglot java import java.time.temporal.TemporalAdjusters
polyglot java import java.time.temporal.TemporalUnit polyglot java import java.time.temporal.TemporalUnit
polyglot java import org.enso.base.time.CustomTemporalUnits polyglot java import org.enso.base.time.CustomTemporalUnits
polyglot java import org.enso.base.time.Date_Utils
polyglot java import org.enso.base.time.Date_Period_Utils polyglot java import org.enso.base.time.Date_Period_Utils
polyglot java import org.enso.base.time.Date_Time_Utils
polyglot java import org.enso.base.time.Time_Of_Day_Utils
polyglot java import org.enso.base.Time_Utils polyglot java import org.enso.base.Time_Utils
## Represents a unit of time longer on the scale of days (longer than a day). ## Represents a unit of time longer on the scale of days (longer than a day).

View File

@ -25,19 +25,18 @@ from project.Data.Range.Extensions import all
from project.Data.Text.Extensions import all from project.Data.Text.Extensions import all
from project.Metadata import Display, make_single_choice, Widget from project.Metadata import Display, make_single_choice, Widget
polyglot java import java.io.StringReader
polyglot java import java.lang.Exception as JException polyglot java import java.lang.Exception as JException
polyglot java import javax.xml.parsers.DocumentBuilder polyglot java import javax.xml.xpath.XPath
polyglot java import javax.xml.parsers.DocumentBuilderFactory
polyglot java import javax.xml.xpath.XPathConstants polyglot java import javax.xml.xpath.XPathConstants
polyglot java import javax.xml.xpath.XPathFactory polyglot java import javax.xml.xpath.XPathFactory
polyglot java import org.enso.base.XML_Utils polyglot java import org.enso.base.XML_Utils
polyglot java import org.w3c.dom.Attr
polyglot java import org.w3c.dom.Document polyglot java import org.w3c.dom.Document
polyglot java import org.w3c.dom.Element polyglot java import org.w3c.dom.Element
polyglot java import org.w3c.dom.Node polyglot java import org.w3c.dom.Node
polyglot java import org.w3c.dom.NodeList polyglot java import org.w3c.dom.NodeList
polyglot java import org.w3c.dom.NamedNodeMap
polyglot java import org.w3c.dom.Text as Java_Text polyglot java import org.w3c.dom.Text as Java_Text
polyglot java import org.xml.sax.InputSource
polyglot java import org.xml.sax.SAXException polyglot java import org.xml.sax.SAXException
polyglot java import org.xml.sax.SAXParseException polyglot java import org.xml.sax.SAXParseException
@ -75,7 +74,8 @@ type XML_Document
from_stream : Input_Stream -> XML_Document ! XML_Error from_stream : Input_Stream -> XML_Document ! XML_Error
from_stream input_stream:Input_Stream = from_stream input_stream:Input_Stream =
XML_Error.handle_java_exceptions <| XML_Error.handle_java_exceptions <|
input_stream.with_java_stream java_stream-> XML_Document.from_source java_stream input_stream.with_java_stream java_stream->
XML_Document.Value (XML_Utils.parseStream java_stream)
## GROUP Conversions ## GROUP Conversions
ICON convert ICON convert
@ -93,20 +93,13 @@ type XML_Document
from_text : Text -> XML_Document ! XML_Error from_text : Text -> XML_Document ! XML_Error
from_text xml_string:Text = from_text xml_string:Text =
XML_Error.handle_java_exceptions <| XML_Error.handle_java_exceptions <|
string_reader = StringReader.new xml_string XML_Document.Value (XML_Utils.parseString xml_string)
XML_Document.from_source (InputSource.new string_reader)
## PRIVATE ## PRIVATE
Read XML from an input source. Wrap Java's Document to XML_Document
from_source : Any -> XML_Document ! XML_Error new doc:Document = XML_Document.Value doc
from_source input_source =
document_builder_factory = DocumentBuilderFactory.newInstance
document_builder = document_builder_factory.newDocumentBuilder
XML_Utils.setCustomErrorHandler document_builder
XML_Document.Value (document_builder.parse input_source)
## PRIVATE private Value (java_document:Document)
Value (java_document:Document)
## GROUP Metadata ## GROUP Metadata

View File

@ -24,6 +24,10 @@ from project.Metadata import make_single_choice
polyglot java import org.enso.base.enso_cloud.EnsoSecretHelper polyglot java import org.enso.base.enso_cloud.EnsoSecretHelper
polyglot java import org.enso.base.enso_cloud.HideableValue polyglot java import org.enso.base.enso_cloud.HideableValue
polyglot java import org.enso.base.enso_cloud.HideableValue.PlainValue
polyglot java import org.enso.base.enso_cloud.HideableValue.SecretValue
polyglot java import org.enso.base.enso_cloud.HideableValue.ConcatValues
polyglot java import org.enso.base.enso_cloud.HideableValue.Base64EncodeValue
## A reference to a secret stored in the Enso Cloud. ## A reference to a secret stored in the Enso Cloud.
type Enso_Secret type Enso_Secret

View File

@ -78,7 +78,7 @@ slice vector start end = @Builtin_Method "Array_Like_Helpers.slice"
`Map_Error`. `Map_Error`.
- No_Wrap: The first error is thrown, and is not wrapped in - No_Wrap: The first error is thrown, and is not wrapped in
`Map_Error`. `Map_Error`.
- Report_Warning: The result for that element is `Nothing`, - Report_Warning: The result for that element is `Nothing`,
the error is attached as a warning. Currently unimplemented. the error is attached as a warning. Currently unimplemented.
- Ignore: The result is `Nothing`, and the error is - Ignore: The result is `Nothing`, and the error is
ignored. ignored.

View File

@ -14,3 +14,18 @@ polyglot java import org.enso.base.text.CaseFoldedString.Grapheme
# needed by Comparator_Spec: # needed by Comparator_Spec:
polyglot java import org.enso.base.ObjectComparator polyglot java import org.enso.base.ObjectComparator
# often used in tests
polyglot java import java.util.ArrayList
polyglot java import java.util.Map
polyglot java import java.nio.CharBuffer
polyglot java import java.nio.file.Path
polyglot java import java.io.FileInputStream
polyglot java import java.io.FileOutputStream
polyglot java import java.math.BigInteger
polyglot java import java.time.LocalDate
polyglot java import java.time.LocalDateTime
polyglot java import java.util.function.Function
polyglot java import java.lang.Thread
polyglot java import java.lang.Thread.State
polyglot java import java.lang.Float

View File

@ -45,7 +45,7 @@ type Project_Description
Arguments: Arguments:
- prim_root_file: The primitive root file of the project. - prim_root_file: The primitive root file of the project.
- prim_config: The primitive config of the project. - prim_config: The primitive config of the project.
private Value prim_root_file prim_config private Value prim_root_file ns:Text n:Text
## GROUP Metadata ## GROUP Metadata
ICON folder ICON folder
@ -78,7 +78,7 @@ type Project_Description
enso_project.name enso_project.name
name : Text name : Text
name self = self.prim_config.name name self = self.n
## GROUP Metadata ## GROUP Metadata
ICON metadata ICON metadata
@ -89,7 +89,7 @@ type Project_Description
enso_project.namespace enso_project.namespace
namespace : Text namespace : Text
namespace self = self.prim_config.namespace namespace self = self.ns
## ICON enso_icon ## ICON enso_icon
Returns the Enso project description for the project that the engine was Returns the Enso project description for the project that the engine was

View File

@ -30,12 +30,17 @@ from project.Data.Json.Extensions import all
polyglot java import java.lang.Exception as JException polyglot java import java.lang.Exception as JException
polyglot java import java.net.http.HttpClient polyglot java import java.net.http.HttpClient
polyglot java import java.net.http.HttpClient.Redirect
polyglot java import java.net.http.HttpClient.Version
polyglot java import java.net.http.HttpClient.Builder as ClientBuilder
polyglot java import java.net.http.HttpRequest polyglot java import java.net.http.HttpRequest
polyglot java import java.net.http.HttpRequest.BodyPublisher polyglot java import java.net.http.HttpRequest.BodyPublisher
polyglot java import java.net.http.HttpRequest.BodyPublishers
polyglot java import java.net.http.HttpRequest.Builder
polyglot java import java.net.InetSocketAddress polyglot java import java.net.InetSocketAddress
polyglot java import java.net.ProxySelector polyglot java import java.net.ProxySelector
polyglot java import java.nio.file.Path
polyglot java import javax.net.ssl.SSLContext polyglot java import javax.net.ssl.SSLContext
polyglot java import org.enso.base.file_system.File_Utils
polyglot java import org.enso.base.enso_cloud.EnsoSecretHelper polyglot java import org.enso.base.enso_cloud.EnsoSecretHelper
polyglot java import org.enso.base.net.http.MultipartBodyBuilder polyglot java import org.enso.base.net.http.MultipartBodyBuilder
polyglot java import org.enso.base.net.http.UrlencodedBodyBuilder polyglot java import org.enso.base.net.http.UrlencodedBodyBuilder
@ -242,7 +247,7 @@ resolve_body_to_publisher_and_boundary body:Request_Body =
json.if_not_error <| json.if_not_error <|
Pair.new (body_publishers.ofString json) Nothing Pair.new (body_publishers.ofString json) Nothing
Request_Body.Binary file -> Request_Body.Binary file ->
path = Path.of file.path path = File_Utils.toPath file.path
Pair.new (body_publishers.ofFile path) Nothing Pair.new (body_publishers.ofFile path) Nothing
Request_Body.Form_Data form_data url_encoded -> Request_Body.Form_Data form_data url_encoded ->
build_form_body_publisher form_data url_encoded build_form_body_publisher form_data url_encoded

View File

@ -25,6 +25,10 @@ from project.Data.Text.Extensions import all
from project.Metadata import Display, Widget from project.Metadata import Display, Widget
from project.Network.HTTP.Response_Body import decode_format_selector from project.Network.HTTP.Response_Body import decode_format_selector
polyglot java import java.net.http.HttpHeaders
polyglot java import org.enso.base.enso_cloud.EnsoHttpResponse
polyglot java import java.util.Optional
type Response type Response
## PRIVATE ## PRIVATE

View File

@ -42,13 +42,12 @@ from project.System.File_Format import Auto_Detect, File_Format
polyglot java import java.io.File as Java_File polyglot java import java.io.File as Java_File
polyglot java import java.io.InputStream as Java_Input_Stream polyglot java import java.io.InputStream as Java_Input_Stream
polyglot java import java.io.OutputStream as Java_Output_Stream 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.StandardCopyOption
polyglot java import java.nio.file.StandardOpenOption polyglot java import java.nio.file.StandardOpenOption
polyglot java import java.time.ZonedDateTime polyglot java import java.time.ZonedDateTime
polyglot java import org.enso.base.DryRunFileManager polyglot java import org.enso.base.DryRunFileManager
polyglot java import org.enso.base.file_system.FileSystemSPI polyglot java import org.enso.base.file_system.FileSystemSPI
polyglot java import org.enso.base.file_system.File_Utils
## PRIVATE ## PRIVATE
file_types : Vector file_types : Vector
@ -795,11 +794,10 @@ type File
_ -> _ ->
used_filter = if recursive.not || name_filter.contains "**" then name_filter else used_filter = if recursive.not || name_filter.contains "**" then name_filter else
(if name_filter.starts_with "*" then "*" else "**/") + name_filter (if name_filter.starts_with "*" then "*" else "**/") + name_filter
fs = FileSystems.getDefault matcher = File_Utils.matchPath "glob:"+used_filter
matcher = fs.getPathMatcher "glob:"+used_filter
all_files.filter file-> all_files.filter file->
pathStr = self.relativize file . path pathStr = self.relativize file . path
matcher.matches (Path.of pathStr) File_Utils.matches matcher pathStr
## GROUP Metadata ## GROUP Metadata
ICON metadata ICON metadata
@ -894,11 +892,11 @@ Writable_File.from (that : File) = if Data_Link.is_data_link that then Data_Link
## PRIVATE ## PRIVATE
local_file_copy (source : File) (destination : File) (replace_existing : Boolean) -> Nothing = local_file_copy (source : File) (destination : File) (replace_existing : Boolean) -> Nothing =
File_Error.handle_java_exceptions source <| File_Error.handle_java_exceptions source <|
copy_options = if replace_existing then [StandardCopyOption.REPLACE_EXISTING] else [] copy_options = if replace_existing then [StandardCopyOption.REPLACE_EXISTING.to_text] else []
source.copy_builtin destination copy_options source.copy_builtin destination copy_options
## PRIVATE ## PRIVATE
local_file_move (source : File) (destination : File) (replace_existing : Boolean) -> Nothing = local_file_move (source : File) (destination : File) (replace_existing : Boolean) -> Nothing =
File_Error.handle_java_exceptions source <| File_Error.handle_java_exceptions source <|
copy_options = if replace_existing then [StandardCopyOption.REPLACE_EXISTING] else [] copy_options = if replace_existing then [StandardCopyOption.REPLACE_EXISTING.to_text] else []
source.move_builtin destination copy_options source.move_builtin destination copy_options

View File

@ -49,18 +49,20 @@ type File_Access
## PRIVATE ## PRIVATE
Convert this object into a representation understandable by the JVM. Convert this object into a representation understandable by the JVM.
to_java : StandardOpenOption to_java : Text
to_java self = case self of to_java self =
File_Access.Append -> StandardOpenOption.APPEND java_option = case self of
File_Access.Create -> StandardOpenOption.CREATE File_Access.Append -> StandardOpenOption.APPEND
File_Access.Create_New -> StandardOpenOption.CREATE_NEW File_Access.Create -> StandardOpenOption.CREATE
File_Access.Delete_On_Close -> StandardOpenOption.DELETE_ON_CLOSE File_Access.Create_New -> StandardOpenOption.CREATE_NEW
File_Access.Dsync -> StandardOpenOption.DSYNC File_Access.Delete_On_Close -> StandardOpenOption.DELETE_ON_CLOSE
File_Access.Read -> StandardOpenOption.READ File_Access.Dsync -> StandardOpenOption.DSYNC
File_Access.Sparse -> StandardOpenOption.SPARSE File_Access.Read -> StandardOpenOption.READ
File_Access.Sync -> StandardOpenOption.SYNC File_Access.Sparse -> StandardOpenOption.SPARSE
File_Access.Truncate_Existing -> StandardOpenOption.TRUNCATE_EXISTING File_Access.Sync -> StandardOpenOption.SYNC
File_Access.Write -> StandardOpenOption.WRITE File_Access.Truncate_Existing -> StandardOpenOption.TRUNCATE_EXISTING
File_Access.Write -> StandardOpenOption.WRITE
java_option.to_text
## PRIVATE ## PRIVATE
ensure_only_allowed_options (operation_name : Text) (allowed_options : Vector) (got_options : Vector) ~action = ensure_only_allowed_options (operation_name : Text) (allowed_options : Vector) (got_options : Vector) ~action =

View File

@ -3,6 +3,8 @@ import project.Data.Text.Text
import project.Data.Vector.Vector import project.Data.Vector.Vector
polyglot java import java.nio.file.attribute.PosixFilePermission polyglot java import java.nio.file.attribute.PosixFilePermission
polyglot java import java.nio.file.attribute.PosixFilePermissions
polyglot java import java.util.Set
type Permission type Permission
## Permission for read access for a given entity. ## Permission for read access for a given entity.
@ -101,7 +103,8 @@ type File_Permissions
## PRIVATE ## PRIVATE
ADVANCED ADVANCED
Converts a Java `Set` of Java `PosixFilePermission` to `File_Permissions`. Converts a Java `Set` of Java `PosixFilePermission` to `File_Permissions`.
from_java_set java_set = from_java_set permissions:Text =
java_set = PosixFilePermissions.fromString permissions
vecs = Vector.build_multiple 3 builders-> vecs = Vector.build_multiple 3 builders->
owner = builders.at 0 owner = builders.at 0
group = builders.at 1 group = builders.at 1

View File

@ -23,6 +23,7 @@ polyglot java import java.io.InputStream as Java_Input_Stream
polyglot java import org.enso.base.encoding.Encoding_Utils polyglot java import org.enso.base.encoding.Encoding_Utils
polyglot java import org.enso.base.encoding.ReportingStreamDecoder polyglot java import org.enso.base.encoding.ReportingStreamDecoder
polyglot java import org.enso.base.Stream_Utils polyglot java import org.enso.base.Stream_Utils
polyglot java import org.enso.base.Stream_Utils.InputStreamLike
## PRIVATE ## PRIVATE
An input stream, allowing for interactive reading of contents. An input stream, allowing for interactive reading of contents.
@ -113,7 +114,8 @@ type Input_Stream
Arguments: Arguments:
- f: Applies a function over the internal java stream. - f: Applies a function over the internal java stream.
with_java_stream : (Java_Input_Stream -> Any) -> Any with_java_stream : (Java_Input_Stream -> Any) -> Any
with_java_stream self f = self.stream_resource . with java_stream-> with_java_stream self f = self.stream_resource . with java_like_stream->
java_stream = Stream_Utils.asInputStream java_like_stream
self.error_handler <| f java_stream self.error_handler <| f java_stream
## PRIVATE ## PRIVATE
@ -158,7 +160,8 @@ type Input_Stream
The current stream may be invalidated after the conversion, and it should The current stream may be invalidated after the conversion, and it should
no longer be used - only the returned stream should be used. no longer be used - only the returned stream should be used.
as_peekable_stream self -> Input_Stream = if self.is_peekable then self else as_peekable_stream self -> Input_Stream = if self.is_peekable then self else
raw_java_stream = self.stream_resource.take raw_stream = self.stream_resource.take
raw_java_stream = Stream_Utils.asInputStream raw_stream
buffered_stream = BufferedInputStream.new raw_java_stream buffered_stream = BufferedInputStream.new raw_java_stream
Input_Stream.new buffered_stream self.error_handler self.associated_source Input_Stream.new buffered_stream self.error_handler self.associated_source

View File

@ -13,6 +13,7 @@ from project.Data.Boolean import Boolean, False, True
from project.Runtime import assert from project.Runtime import assert
polyglot java import org.enso.base.encoding.DecodingProblemAggregator polyglot java import org.enso.base.encoding.DecodingProblemAggregator
polyglot java import org.enso.base.encoding.DecodingProblem
polyglot java import org.enso.base.encoding.Encoding_Utils polyglot java import org.enso.base.encoding.Encoding_Utils
polyglot java import org.enso.base.encoding.ReportingStreamDecoder polyglot java import org.enso.base.encoding.ReportingStreamDecoder

View File

@ -15,6 +15,9 @@ from project.System.Input_Stream import close_stream
polyglot java import java.io.ByteArrayOutputStream polyglot java import java.io.ByteArrayOutputStream
polyglot java import java.io.OutputStream as Java_Output_Stream polyglot java import java.io.OutputStream as Java_Output_Stream
polyglot java import org.enso.base.encoding.Encoding_Utils polyglot java import org.enso.base.encoding.Encoding_Utils
polyglot java import org.enso.base.Stream_Utils
polyglot java import org.enso.base.Stream_Utils.OutputStreamLike
polyglot java import org.enso.base.encoding.ReportingStreamEncoder polyglot java import org.enso.base.encoding.ReportingStreamEncoder
## PRIVATE ## PRIVATE
@ -57,7 +60,7 @@ type Output_Stream
- stream_resource: The internal resource that represents the underlying - stream_resource: The internal resource that represents the underlying
stream. stream.
- error_handler: An error handler for IOExceptions thrown when writing. - error_handler: An error handler for IOExceptions thrown when writing.
Value stream_resource error_handler private Value stream_resource error_handler
## PRIVATE ## PRIVATE
ADVANCED ADVANCED
@ -66,7 +69,8 @@ type Output_Stream
Arguments: Arguments:
- contents: A vector of bytes to write. - contents: A vector of bytes to write.
write_bytes : Vector Integer -> Nothing write_bytes : Vector Integer -> Nothing
write_bytes self contents = self.stream_resource . with java_stream-> write_bytes self contents = self.stream_resource . with raw_stream->
java_stream = Stream_Utils.asOutputStream raw_stream
self.error_handler <| self.error_handler <|
java_stream.write contents java_stream.write contents
java_stream.flush java_stream.flush
@ -80,7 +84,8 @@ type Output_Stream
- input_stream: An Input_Stream to write to this stream. - input_stream: An Input_Stream to write to this stream.
write_stream : Input_Stream -> Nothing write_stream : Input_Stream -> Nothing
write_stream self input_stream:Input_Stream = write_stream self input_stream:Input_Stream =
self.stream_resource . with java_output_stream-> self.stream_resource . with raw_stream->
java_output_stream = Stream_Utils.asOutputStream raw_stream
self.error_handler <| self.error_handler <|
input_stream.with_java_stream java_input_stream-> input_stream.with_java_stream java_input_stream->
java_input_stream.transferTo java_output_stream java_input_stream.transferTo java_output_stream
@ -107,7 +112,9 @@ type Output_Stream
Arguments: Arguments:
- f: Applies a function over the internal java stream. - f: Applies a function over the internal java stream.
with_java_stream : (Java_Output_Stream -> Any) -> Any with_java_stream : (Java_Output_Stream -> Any) -> Any
with_java_stream self f = self.stream_resource . with f with_java_stream self f = self.stream_resource . with raw_stream->
java_stream = Stream_Utils.asOutputStream raw_stream
f java_stream
## PRIVATE ## PRIVATE
ADVANCED ADVANCED

View File

@ -2721,13 +2721,13 @@ type Table
- root_name: The name of the root tag in the XML. - root_name: The name of the root tag in the XML.
- row_name: The name of the row tag in the XML. - row_name: The name of the row tag in the XML.
- on_problems: Specifies how to handle warnings if they occur, reporting - on_problems: Specifies how to handle warnings if they occur, reporting
them as warnings by default. them as warnings by default.
! Error Conditions ! Error Conditions
- If a column in `element_columns`, `attribute_columns` or `value_column` is not in - If a column in `element_columns`, `attribute_columns` or `value_column` is not in
the input table, a `Missing_Input_Columns` is raised as an error. the input table, a `Missing_Input_Columns` is raised as an error.
- If any incomming columns aree not specified in one of `element_columns`, - If any incomming columns aree not specified in one of `element_columns`,
`attribute_columns` or `value_column`, a `Unexpected_Extra_Columns` `attribute_columns` or `value_column`, a `Unexpected_Extra_Columns`
is reported according to the `on_problems` setting. is reported according to the `on_problems` setting.
@ -2736,10 +2736,10 @@ type Table
Input Table `table`: Input Table `table`:
Title | Author | Price | Year Title | Author | Price | Year
------------------------+---------------------+-------+------ ------------------------+---------------------+-------+------
A Tale Of Two Cities | Charles Dickens | 9.99 | 1859 A Tale Of Two Cities | Charles Dickens | 9.99 | 1859
The Great Gatsby | F. Scott Fitzgerald | 5.99 | 1925 The Great Gatsby | F. Scott Fitzgerald | 5.99 | 1925
Result `r = t.to_xml ["Year"] ["Author", "Price"] "Title" "Books" "Book"`: Result `r = t.to_xml ["Year"] ["Author", "Price"] "Title" "Books" "Book"`:
@ -2770,9 +2770,9 @@ type Table
unused_columns = columns_helper.internal_columns.filter (Filter_Condition.Is_In resolved_element_columns+resolved_attribute_columns+[resolved_value_column] Filter_Action.Remove) . map .name unused_columns = columns_helper.internal_columns.filter (Filter_Condition.Is_In resolved_element_columns+resolved_attribute_columns+[resolved_value_column] Filter_Action.Remove) . map .name
if unused_columns.length > 0 then problem_builder.report_other_warning (Unexpected_Extra_Columns.Warning unused_columns) if unused_columns.length > 0 then problem_builder.report_other_warning (Unexpected_Extra_Columns.Warning unused_columns)
problem_builder.attach_problems_before on_problems <| problem_builder.attach_problems_before on_problems <|
XML_Document.Value (Java_TableToXml.to_xml self.row_count java_element_columns java_attribute_column java_value_column root_name row_name) XML_Document.new (Java_TableToXml.to_xml self.row_count java_element_columns java_attribute_column java_value_column root_name row_name)
## PRIVATE ## PRIVATE
columns_helper : Table_Column_Helper columns_helper : Table_Column_Helper

View File

@ -205,8 +205,5 @@ type Test
if i % 10 == 0 then if i % 10 == 0 then
IO.println 'Still failing after '+i.to_text+' retries ('+loc.to_display_text+'):\n'+caught_panic.payload.to_display_text IO.println 'Still failing after '+i.to_text+' retries ('+loc.to_display_text+'):\n'+caught_panic.payload.to_display_text
Thread.sleep milliseconds_between_attempts Thread.sleep milliseconds_between_attempts
## TODO This used to be @Tail_Call go (i+1)
@Tail_Call go (i+1)
We should re-add the tail call once https://github.com/enso-org/enso/issues/9251 is fixed.
go (i+1)
go 1 go 1

View File

@ -207,17 +207,16 @@ state. To generate the Native Image for runner simply execute
sbt> engine-runner/buildNativeImage sbt> engine-runner/buildNativeImage
``` ```
and execute the binary on a sample factorial test program and execute any program with that binary - for example `test/Base_Tests`
```bash ```bash
> runner --run engine/runner/src/test/resources/Factorial.enso 6 $ runner --run test/Base_Tests
``` ```
The task that generates the Native Image, along with all the necessary The task that generates the Native Image, along with all the necessary
configuration, reside in a separate project due to a bug in the currently used configuration, makes sure that `Standard.Base` library calls into Java via
GraalVM version. As September 2023 it can execute all Enso code, but cannot [polyglot java import](../../docs/polyglot/java.md) are compiled into the binary
invoke `IO.println` or other library functions that require and ready to be used.
[polyglot java import](../../docs/polyglot/java.md), but read on...
### Engine with Espresso ### Engine with Espresso

View File

@ -9,6 +9,7 @@ import java.util.TreeSet;
import org.enso.compiler.core.ir.module.scope.imports.Polyglot; import org.enso.compiler.core.ir.module.scope.imports.Polyglot;
import org.enso.pkg.PackageManager$; import org.enso.pkg.PackageManager$;
import org.graalvm.nativeimage.hosted.Feature; import org.graalvm.nativeimage.hosted.Feature;
import org.graalvm.nativeimage.hosted.RuntimeProxyCreation;
import org.graalvm.nativeimage.hosted.RuntimeReflection; import org.graalvm.nativeimage.hosted.RuntimeReflection;
public final class EnsoLibraryFeature implements Feature { public final class EnsoLibraryFeature implements Feature {
@ -75,6 +76,9 @@ public final class EnsoLibraryFeature implements Feature {
RuntimeReflection.registerAllConstructors(clazz); RuntimeReflection.registerAllConstructors(clazz);
RuntimeReflection.registerAllFields(clazz); RuntimeReflection.registerAllFields(clazz);
RuntimeReflection.registerAllMethods(clazz); RuntimeReflection.registerAllMethods(clazz);
if (clazz.isInterface()) {
RuntimeProxyCreation.register(clazz);
}
} }
} }
} }

View File

@ -1,6 +1,8 @@
{ {
"resources":{ "resources":{
"includes":[{ "includes":[{
"pattern":"\\QMETA-INF/native-image/com.oracle.truffle.espresso/native-image.properties\\E"
}, {
"pattern":"\\QMETA-INF/org/enso/interpreter/node/expression/builtin/BuiltinMethods.metadata\\E" "pattern":"\\QMETA-INF/org/enso/interpreter/node/expression/builtin/BuiltinMethods.metadata\\E"
}, { }, {
"pattern":"\\QMETA-INF/org/enso/interpreter/node/expression/builtin/BuiltinTypes.metadata\\E" "pattern":"\\QMETA-INF/org/enso/interpreter/node/expression/builtin/BuiltinTypes.metadata\\E"
@ -52,6 +54,8 @@
"pattern":"\\QMETA-INF/services/java.time.zone.ZoneRulesProvider\\E" "pattern":"\\QMETA-INF/services/java.time.zone.ZoneRulesProvider\\E"
}, { }, {
"pattern":"\\QMETA-INF/services/javax.xml.parsers.DocumentBuilderFactory\\E" "pattern":"\\QMETA-INF/services/javax.xml.parsers.DocumentBuilderFactory\\E"
}, {
"pattern":"\\QMETA-INF/services/javax.xml.parsers.SAXParserFactory\\E"
}, { }, {
"pattern":"\\QMETA-INF/services/javax.xml.xpath.XPathFactory\\E" "pattern":"\\QMETA-INF/services/javax.xml.xpath.XPathFactory\\E"
}, { }, {
@ -166,6 +170,8 @@
"pattern":"\\Qversion.json\\E" "pattern":"\\Qversion.json\\E"
}, { }, {
"pattern":"\\Qversion.properties\\E" "pattern":"\\Qversion.properties\\E"
}, {
"pattern":"java.base:\\Qjdk/internal/icu/impl/data/icudt72b/nfkc.nrm\\E"
}, { }, {
"pattern":"java.base:\\Qjdk/internal/icu/impl/data/icudt72b/uprops.icu\\E" "pattern":"java.base:\\Qjdk/internal/icu/impl/data/icudt72b/uprops.icu\\E"
}, { }, {
@ -597,13 +603,13 @@
}]}, }]},
"bundles":[{ "bundles":[{
"name":"com.oracle.js.parser.resources.Messages", "name":"com.oracle.js.parser.resources.Messages",
"locales":[""] "locales":["", "und"]
}, { }, {
"name":"com.sun.org.apache.xerces.internal.impl.msg.XMLMessages", "name":"com.sun.org.apache.xerces.internal.impl.msg.XMLMessages",
"locales":[""] "locales":["", "und"]
}, { }, {
"name":"com.sun.org.apache.xml.internal.serializer.XMLEntities", "name":"com.sun.org.apache.xml.internal.serializer.XMLEntities",
"locales":[""] "locales":["", "und"]
}, { }, {
"name":"com.sun.org.apache.xml.internal.serializer.utils.SerializerMessages", "name":"com.sun.org.apache.xml.internal.serializer.utils.SerializerMessages",
"classNames":["com.sun.org.apache.xml.internal.serializer.utils.SerializerMessages", "com.sun.org.apache.xml.internal.serializer.utils.SerializerMessages_en"] "classNames":["com.sun.org.apache.xml.internal.serializer.utils.SerializerMessages", "com.sun.org.apache.xml.internal.serializer.utils.SerializerMessages_en"]

View File

@ -1,12 +0,0 @@
import Standard.Base.Data.Numbers
import Standard.Base.IO
fac n =
facacc n v = if n <= 1 then v else @Tail_Call facacc n-1 n*v
res = facacc n 1
res
main number=5 =
v = fac number
IO.println v

View File

@ -183,13 +183,17 @@ public abstract class HostMethodCallNode extends Node {
return PolyglotCallType.CONVERT_TO_HASH_MAP; return PolyglotCallType.CONVERT_TO_HASH_MAP;
} }
String methodName = symbol.getName(); try {
if (library.isMemberInvocable(self, methodName)) { String methodName = symbol.getName();
return PolyglotCallType.CALL_METHOD; if (library.isMemberInvocable(self, methodName)) {
} else if (library.isMemberReadable(self, methodName)) { return PolyglotCallType.CALL_METHOD;
return PolyglotCallType.GET_MEMBER; } else if (library.isMemberReadable(self, methodName)) {
} else if (library.isInstantiable(self) && methodName.equals(NEW_NAME)) { return PolyglotCallType.GET_MEMBER;
return PolyglotCallType.INSTANTIATE; } else if (library.isInstantiable(self) && methodName.equals(NEW_NAME)) {
return PolyglotCallType.INSTANTIATE;
}
} catch (TypeNotPresentException ex) {
// no call, get or instantiate is possible
} }
return PolyglotCallType.NOT_SUPPORTED; return PolyglotCallType.NOT_SUPPORTED;
} }

View File

@ -98,10 +98,11 @@ public abstract class EnsoProjectNode extends Node {
private static Atom createProjectDescriptionAtom(EnsoContext ctx, Package<TruffleFile> pkg) { private static Atom createProjectDescriptionAtom(EnsoContext ctx, Package<TruffleFile> pkg) {
var rootPath = new EnsoFile(pkg.root().normalize()); var rootPath = new EnsoFile(pkg.root().normalize());
var cfg = ctx.asGuestValue(pkg.getConfig()); var namespace = pkg.getConfig().namespace();
var name = pkg.getConfig().name();
var cons = ctx.getBuiltins().getProjectDescription().getUniqueConstructor(); var cons = ctx.getBuiltins().getProjectDescription().getUniqueConstructor();
return AtomNewInstanceNode.getUncached().newInstance(cons, rootPath, cfg); return AtomNewInstanceNode.getUncached().newInstance(cons, rootPath, namespace, name);
} }
private DataflowError unsupportedArgsError(Object moduleActual) { private DataflowError unsupportedArgsError(Object moduleActual) {

View File

@ -13,6 +13,6 @@ public class ProjectDescription extends UniquelyConstructibleBuiltin {
@Override @Override
protected List<String> getConstructorParamNames() { protected List<String> getConstructorParamNames() {
return List.of("prim_root_file", "prim_config"); return List.of("prim_root_file", "ns", "n");
} }
} }

View File

@ -4,9 +4,13 @@ import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.TruffleFile; import com.oracle.truffle.api.TruffleFile;
import com.oracle.truffle.api.dsl.Bind; import com.oracle.truffle.api.dsl.Bind;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.interop.ArityException;
import com.oracle.truffle.api.interop.InteropLibrary; import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.InvalidArrayIndexException; import com.oracle.truffle.api.interop.InvalidArrayIndexException;
import com.oracle.truffle.api.interop.UnknownIdentifierException;
import com.oracle.truffle.api.interop.UnsupportedMessageException; 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.CachedLibrary;
import com.oracle.truffle.api.library.ExportLibrary; import com.oracle.truffle.api.library.ExportLibrary;
import com.oracle.truffle.api.library.ExportMessage; import com.oracle.truffle.api.library.ExportMessage;
@ -22,16 +26,22 @@ import java.nio.file.LinkOption;
import java.nio.file.NoSuchFileException; import java.nio.file.NoSuchFileException;
import java.nio.file.NotDirectoryException; import java.nio.file.NotDirectoryException;
import java.nio.file.OpenOption; import java.nio.file.OpenOption;
import java.nio.file.StandardCopyOption;
import java.nio.file.StandardOpenOption; import java.nio.file.StandardOpenOption;
import java.nio.file.attribute.PosixFilePermission; import java.nio.file.attribute.PosixFilePermissions;
import java.time.ZoneOffset; import java.time.ZoneOffset;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.function.IntFunction; import java.util.function.Function;
import org.enso.interpreter.dsl.Builtin; import org.enso.interpreter.dsl.Builtin;
import org.enso.interpreter.runtime.EnsoContext; import org.enso.interpreter.runtime.EnsoContext;
import org.enso.interpreter.runtime.data.text.Text; import org.enso.interpreter.runtime.data.text.Text;
import org.enso.interpreter.runtime.data.vector.ArrayLikeAtNode;
import org.enso.interpreter.runtime.data.vector.ArrayLikeHelpers; import org.enso.interpreter.runtime.data.vector.ArrayLikeHelpers;
import org.enso.interpreter.runtime.data.vector.ArrayLikeLengthNode;
import org.enso.interpreter.runtime.error.PanicException; import org.enso.interpreter.runtime.error.PanicException;
import org.enso.interpreter.runtime.library.dispatch.TypesLibrary; import org.enso.interpreter.runtime.library.dispatch.TypesLibrary;
@ -54,60 +64,273 @@ public final class EnsoFile implements EnsoObject {
@Builtin.Method(name = "output_stream_builtin") @Builtin.Method(name = "output_stream_builtin")
@Builtin.WrapException(from = IOException.class) @Builtin.WrapException(from = IOException.class)
@Builtin.ReturningGuestObject
@Builtin.Specialize @Builtin.Specialize
@CompilerDirectives.TruffleBoundary @TruffleBoundary
public OutputStream outputStream(Object opts, EnsoContext ctx) throws IOException { public EnsoObject outputStream(
OpenOption[] openOptions = Object opts,
convertInteropArray(opts, InteropLibrary.getUncached(), ctx, OpenOption[]::new); @Cached ArrayLikeLengthNode lengthNode,
return this.truffleFile.newOutputStream(openOptions); @Cached ArrayLikeAtNode atNode,
EnsoContext ctx)
throws IOException {
var options = namesToValues(opts, lengthNode, atNode, ctx, StandardOpenOption::valueOf);
var os = this.truffleFile.newOutputStream(options.toArray(OpenOption[]::new));
return new EnsoOutputStream(os);
}
@ExportLibrary(InteropLibrary.class)
static final class EnsoOutputStream implements EnsoObject {
private static final String[] MEMBERS = new String[] {"write", "flush", "close"};
private final OutputStream os;
EnsoOutputStream(OutputStream os) {
this.os = os;
}
@ExportMessage
boolean hasMembers() {
return true;
}
@TruffleBoundary
@ExportMessage
boolean isMemberInvocable(String member) {
return Arrays.asList(MEMBERS).contains(member);
}
@ExportMessage
Object getMembers(boolean includeInternal) throws UnsupportedMessageException {
return ArrayLikeHelpers.wrapStrings(MEMBERS);
}
@TruffleBoundary
@ExportMessage
Object invokeMember(
String name,
Object[] args,
@Cached ArrayLikeLengthNode lengthNode,
@Cached ArrayLikeAtNode atNode,
@CachedLibrary(limit = "3") InteropLibrary iop)
throws ArityException, UnsupportedMessageException, UnknownIdentifierException {
try {
return switch (name) {
case "write" -> {
long from;
long to;
switch (args.length) {
case 1 -> {
from = 0;
to = lengthNode.executeLength(args[0]);
}
case 3 -> {
from = iop.asLong(args[1]);
to = from + iop.asLong(args[2]);
}
default -> {
throw ArityException.create(1, 3, args.length);
}
}
for (long i = from; i < to; i++) {
var elem = atNode.executeAt(args[0], i);
var byt = iop.asInt(elem);
os.write(byt);
}
yield this;
}
case "flush" -> {
os.flush();
yield this;
}
case "close" -> {
os.close();
yield this;
}
default -> throw UnknownIdentifierException.create(name);
};
} catch (IOException ex) {
throw raiseIOException(iop, ex);
} catch (InvalidArrayIndexException ex) {
var ctx = EnsoContext.get(iop);
throw ctx.raiseAssertionPanic(iop, name, ex);
}
}
@Override
public String toString() {
return "EnsoOutputStream";
}
} }
@Builtin.Method(name = "input_stream_builtin") @Builtin.Method(name = "input_stream_builtin")
@Builtin.WrapException(from = IOException.class) @Builtin.WrapException(from = IOException.class)
@Builtin.Specialize @Builtin.Specialize
@Builtin.ReturningGuestObject @TruffleBoundary
@CompilerDirectives.TruffleBoundary public EnsoObject inputStream(
public InputStream inputStream(Object opts, EnsoContext ctx) throws IOException { Object opts,
OpenOption[] openOptions = @Cached ArrayLikeLengthNode lengthNode,
convertInteropArray(opts, InteropLibrary.getUncached(), ctx, OpenOption[]::new); @Cached ArrayLikeAtNode atNode,
return this.truffleFile.newInputStream(openOptions); EnsoContext ctx)
throws IOException {
var options = namesToValues(opts, lengthNode, atNode, ctx, StandardOpenOption::valueOf);
var is = this.truffleFile.newInputStream(options.toArray(OpenOption[]::new));
return new EnsoInputStream(is);
}
@ExportLibrary(InteropLibrary.class)
static final class EnsoInputStream implements EnsoObject {
private static final String[] MEMBERS =
new String[] {
"read", "readAllBytes", "readNBytes", "skipNBytes", "markSupported", "available", "close"
};
private final InputStream is;
EnsoInputStream(InputStream is) {
this.is = is;
}
@ExportMessage
boolean hasMembers() {
return true;
}
@TruffleBoundary
@ExportMessage
boolean isMemberInvocable(String member) {
return Arrays.asList(MEMBERS).contains(member);
}
@ExportMessage
Object getMembers(boolean includeInternal) throws UnsupportedMessageException {
return ArrayLikeHelpers.wrapStrings(MEMBERS);
}
@TruffleBoundary
@ExportMessage
Object invokeMember(String name, Object[] args, @CachedLibrary(limit = "3") InteropLibrary iop)
throws UnknownIdentifierException,
UnsupportedMessageException,
ArityException,
UnsupportedTypeException {
try {
return switch (name) {
case "read" -> {
if (args.length == 0) {
yield is.read();
}
long from;
long to;
switch (args.length) {
case 1 -> {
from = 0;
to = iop.getArraySize(args[0]);
}
case 3 -> {
from = iop.asLong(args[1]);
to = from + iop.asLong(args[2]);
}
default -> throw ArityException.create(0, 3, args.length);
}
for (var i = from; i < to; i++) {
var b = is.read();
if (b == -1) {
var count = i - from;
yield count > 0 ? count : -1;
}
iop.writeArrayElement(args[0], i, (byte) b);
}
yield to - from;
}
case "readAllBytes" -> {
if (args.length != 0) {
throw ArityException.create(0, 0, args.length);
}
var arr = is.readAllBytes();
var buf = ByteBuffer.wrap(arr);
yield ArrayLikeHelpers.wrapBuffer(buf);
}
case "readNBytes" -> {
if (args.length != 1) {
throw ArityException.create(1, 1, args.length);
}
var len = iop.asInt(args[0]);
var arr = is.readNBytes(len);
var buf = ByteBuffer.wrap(arr);
yield ArrayLikeHelpers.wrapBuffer(buf);
}
case "skipNBytes" -> {
if (args.length != 1) {
throw ArityException.create(1, 1, args.length);
}
var len = iop.asInt(args[0]);
is.skipNBytes(len);
yield this;
}
case "markSupported" -> {
if (args.length != 0) {
throw ArityException.create(0, 0, args.length);
}
yield is.markSupported();
}
case "available" -> {
if (args.length != 0) {
throw ArityException.create(0, 0, args.length);
}
yield is.available();
}
case "close" -> {
if (args.length != 0) {
throw ArityException.create(0, 0, args.length);
}
is.close();
yield this;
}
default -> throw UnknownIdentifierException.create(name);
};
} catch (IOException ex) {
throw raiseIOException(iop, ex);
} catch (InvalidArrayIndexException ex) {
var ctx = EnsoContext.get(iop);
throw ctx.raiseAssertionPanic(iop, name, ex);
}
}
@Override
public String toString() {
return "EnsoInputStream";
}
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private static <T> T[] convertInteropArray( @TruffleBoundary
Object arr, InteropLibrary interop, EnsoContext ctx, IntFunction<T[]> hostArrayCtor) { private static <T> List<T> namesToValues(
if (!interop.hasArrayElements(arr)) { Object arr,
var vecType = ctx.getBuiltins().vector().getType(); ArrayLikeLengthNode lengthNode,
var typeError = ctx.getBuiltins().error().makeTypeError(vecType, arr, "opts"); ArrayLikeAtNode atNode,
throw new PanicException(typeError, interop); EnsoContext ctx,
} Function<String, T> convertor) {
T[] hostArr; var size = (int) lengthNode.executeLength(arr);
List<T> hostArr = new ArrayList<>();
try { try {
int size = Math.toIntExact(interop.getArraySize(arr)); for (var i = 0; i < size; i++) {
hostArr = hostArrayCtor.apply(size); var elem = atNode.executeAt(arr, i);
for (int i = 0; i < size; i++) { if (elem instanceof Text name) {
Object elem = interop.readArrayElement(arr, i); hostArr.add(convertor.apply(name.toString()));
if (!ctx.isJavaPolyglotObject(elem)) { } else {
var err = var err =
ctx.getBuiltins() ctx.getBuiltins()
.error() .error()
.makeUnsupportedArgumentsError( .makeTypeError(ctx.getBuiltins().text(), elem, "File_Access permissions");
new Object[] {arr}, throw new PanicException(err, lengthNode);
"Arguments to opts should be host objects from java.io package");
throw new PanicException(err, interop);
} }
hostArr[i] = (T) ctx.asJavaPolyglotObject(elem);
} }
} catch (ClassCastException | UnsupportedMessageException | InvalidArrayIndexException e) { } catch (ClassCastException | InvalidArrayIndexException e) {
throw EnsoContext.get(interop).raiseAssertionPanic(interop, null, e); throw EnsoContext.get(lengthNode).raiseAssertionPanic(lengthNode, null, e);
} }
return hostArr; return hostArr;
} }
@Builtin.Method(name = "read_last_bytes_builtin") @Builtin.Method(name = "read_last_bytes_builtin")
@Builtin.WrapException(from = IOException.class) @Builtin.WrapException(from = IOException.class)
@CompilerDirectives.TruffleBoundary @TruffleBoundary
public EnsoObject readLastBytes(long n) throws IOException { public EnsoObject readLastBytes(long n) throws IOException {
try (SeekableByteChannel channel = try (SeekableByteChannel channel =
this.truffleFile.newByteChannel(Set.of(StandardOpenOption.READ))) { this.truffleFile.newByteChannel(Set.of(StandardOpenOption.READ))) {
@ -136,7 +359,7 @@ public final class EnsoFile implements EnsoObject {
@Builtin.Method(name = "creation_time_builtin") @Builtin.Method(name = "creation_time_builtin")
@Builtin.WrapException(from = IOException.class) @Builtin.WrapException(from = IOException.class)
@CompilerDirectives.TruffleBoundary @TruffleBoundary
public EnsoDateTime getCreationTime() throws IOException { public EnsoDateTime getCreationTime() throws IOException {
return new EnsoDateTime( return new EnsoDateTime(
ZonedDateTime.ofInstant(truffleFile.getCreationTime().toInstant(), ZoneOffset.UTC)); ZonedDateTime.ofInstant(truffleFile.getCreationTime().toInstant(), ZoneOffset.UTC));
@ -144,7 +367,7 @@ public final class EnsoFile implements EnsoObject {
@Builtin.Method(name = "last_modified_time_builtin") @Builtin.Method(name = "last_modified_time_builtin")
@Builtin.WrapException(from = IOException.class) @Builtin.WrapException(from = IOException.class)
@CompilerDirectives.TruffleBoundary @TruffleBoundary
public EnsoDateTime getLastModifiedTime() throws IOException { public EnsoDateTime getLastModifiedTime() throws IOException {
return new EnsoDateTime( return new EnsoDateTime(
ZonedDateTime.ofInstant(truffleFile.getLastModifiedTime().toInstant(), ZoneOffset.UTC)); ZonedDateTime.ofInstant(truffleFile.getLastModifiedTime().toInstant(), ZoneOffset.UTC));
@ -152,14 +375,13 @@ public final class EnsoFile implements EnsoObject {
@Builtin.Method(name = "posix_permissions_builtin") @Builtin.Method(name = "posix_permissions_builtin")
@Builtin.WrapException(from = IOException.class) @Builtin.WrapException(from = IOException.class)
@Builtin.ReturningGuestObject @TruffleBoundary
@CompilerDirectives.TruffleBoundary public Text getPosixPermissions() throws IOException {
public Set<PosixFilePermission> getPosixPermissions() throws IOException { return Text.create(PosixFilePermissions.toString(truffleFile.getPosixPermissions()));
return truffleFile.getPosixPermissions();
} }
@Builtin.Method(name = "parent") @Builtin.Method(name = "parent")
@CompilerDirectives.TruffleBoundary @TruffleBoundary
public EnsoObject getParent() { public EnsoObject getParent() {
// Normalization is needed to correctly handle paths containing `..` and `.`. // Normalization is needed to correctly handle paths containing `..` and `.`.
var parentOrNull = this.normalize().truffleFile.getParent(); var parentOrNull = this.normalize().truffleFile.getParent();
@ -179,32 +401,32 @@ public final class EnsoFile implements EnsoObject {
} }
@Builtin.Method(name = "absolute") @Builtin.Method(name = "absolute")
@CompilerDirectives.TruffleBoundary @TruffleBoundary
public EnsoFile getAbsoluteFile() { public EnsoFile getAbsoluteFile() {
return new EnsoFile(this.truffleFile.getAbsoluteFile()); return new EnsoFile(this.truffleFile.getAbsoluteFile());
} }
@Builtin.Method(name = "path") @Builtin.Method(name = "path")
@CompilerDirectives.TruffleBoundary @TruffleBoundary
public Text getPath() { public Text getPath() {
return Text.create(this.truffleFile.getPath()); return Text.create(this.truffleFile.getPath());
} }
@Builtin.Method @Builtin.Method
@CompilerDirectives.TruffleBoundary @TruffleBoundary
public boolean isAbsolute() { public boolean isAbsolute() {
return this.truffleFile.isAbsolute(); return this.truffleFile.isAbsolute();
} }
@Builtin.Method @Builtin.Method
@CompilerDirectives.TruffleBoundary @TruffleBoundary
public boolean isDirectory() { public boolean isDirectory() {
return this.truffleFile.isDirectory(); return this.truffleFile.isDirectory();
} }
@Builtin.Method(name = "create_directory_builtin") @Builtin.Method(name = "create_directory_builtin")
@Builtin.WrapException(from = IOException.class) @Builtin.WrapException(from = IOException.class)
@CompilerDirectives.TruffleBoundary @TruffleBoundary
public void createDirectories() throws IOException { public void createDirectories() throws IOException {
try { try {
this.truffleFile.createDirectories(); this.truffleFile.createDirectories();
@ -282,32 +504,32 @@ public final class EnsoFile implements EnsoObject {
@Builtin.Method(name = "list_immediate_children_array") @Builtin.Method(name = "list_immediate_children_array")
@Builtin.WrapException(from = IOException.class) @Builtin.WrapException(from = IOException.class)
@CompilerDirectives.TruffleBoundary @TruffleBoundary
public EnsoObject list() throws IOException { public EnsoObject list() throws IOException {
return ArrayLikeHelpers.wrapEnsoObjects( return ArrayLikeHelpers.wrapEnsoObjects(
this.truffleFile.list().stream().map(EnsoFile::new).toArray(EnsoFile[]::new)); this.truffleFile.list().stream().map(EnsoFile::new).toArray(EnsoFile[]::new));
} }
@Builtin.Method @Builtin.Method
@CompilerDirectives.TruffleBoundary @TruffleBoundary
public EnsoFile relativize(EnsoFile other) { public EnsoFile relativize(EnsoFile other) {
return new EnsoFile(this.truffleFile.relativize(other.truffleFile)); return new EnsoFile(this.truffleFile.relativize(other.truffleFile));
} }
@Builtin.Method @Builtin.Method
@CompilerDirectives.TruffleBoundary @TruffleBoundary
public boolean isRegularFile() { public boolean isRegularFile() {
return this.truffleFile.isRegularFile(); return this.truffleFile.isRegularFile();
} }
@Builtin.Method @Builtin.Method
@CompilerDirectives.TruffleBoundary @TruffleBoundary
public boolean isWritable() { public boolean isWritable() {
return this.truffleFile.isWritable(); return this.truffleFile.isWritable();
} }
@Builtin.Method(name = "name") @Builtin.Method(name = "name")
@CompilerDirectives.TruffleBoundary @TruffleBoundary
public Text getName() { public Text getName() {
var name = this.normalize().truffleFile.getName(); var name = this.normalize().truffleFile.getName();
return Text.create(name == null ? "/" : name); return Text.create(name == null ? "/" : name);
@ -315,7 +537,7 @@ public final class EnsoFile implements EnsoObject {
@Builtin.Method(name = "size_builtin") @Builtin.Method(name = "size_builtin")
@Builtin.WrapException(from = IOException.class) @Builtin.WrapException(from = IOException.class)
@CompilerDirectives.TruffleBoundary @TruffleBoundary
public long getSize() throws IOException { public long getSize() throws IOException {
if (this.truffleFile.isDirectory()) { if (this.truffleFile.isDirectory()) {
throw new IOException("size can only be called on files."); throw new IOException("size can only be called on files.");
@ -334,7 +556,7 @@ public final class EnsoFile implements EnsoObject {
} }
@Builtin.Method @Builtin.Method
@CompilerDirectives.TruffleBoundary @TruffleBoundary
public EnsoFile normalize() { public EnsoFile normalize() {
TruffleFile simplyNormalized = truffleFile.normalize(); TruffleFile simplyNormalized = truffleFile.normalize();
String name = simplyNormalized.getName(); String name = simplyNormalized.getName();
@ -347,7 +569,7 @@ public final class EnsoFile implements EnsoObject {
@Builtin.Method(name = "delete_builtin") @Builtin.Method(name = "delete_builtin")
@Builtin.WrapException(from = IOException.class) @Builtin.WrapException(from = IOException.class)
@CompilerDirectives.TruffleBoundary @TruffleBoundary
public void delete(boolean recursive) throws IOException { public void delete(boolean recursive) throws IOException {
if (recursive && truffleFile.isDirectory(LinkOption.NOFOLLOW_LINKS)) { if (recursive && truffleFile.isDirectory(LinkOption.NOFOLLOW_LINKS)) {
deleteRecursively(truffleFile); deleteRecursively(truffleFile);
@ -368,25 +590,35 @@ public final class EnsoFile implements EnsoObject {
@Builtin.Method(name = "copy_builtin", description = "Copy this file to a target destination") @Builtin.Method(name = "copy_builtin", description = "Copy this file to a target destination")
@Builtin.WrapException(from = IOException.class) @Builtin.WrapException(from = IOException.class)
@Builtin.Specialize @Builtin.Specialize
@CompilerDirectives.TruffleBoundary @TruffleBoundary
public void copy(EnsoFile target, Object options, EnsoContext ctx) throws IOException { public void copy(
CopyOption[] copyOptions = EnsoFile target,
convertInteropArray(options, InteropLibrary.getUncached(), ctx, CopyOption[]::new); Object options,
truffleFile.copy(target.truffleFile, copyOptions); @Cached ArrayLikeLengthNode lengthNode,
@Cached ArrayLikeAtNode atNode,
EnsoContext ctx)
throws IOException {
var copyOptions = namesToValues(options, lengthNode, atNode, ctx, StandardCopyOption::valueOf);
truffleFile.copy(target.truffleFile, copyOptions.toArray(CopyOption[]::new));
} }
@Builtin.Method(name = "move_builtin", description = "Move this file to a target destination") @Builtin.Method(name = "move_builtin", description = "Move this file to a target destination")
@Builtin.WrapException(from = IOException.class) @Builtin.WrapException(from = IOException.class)
@Builtin.Specialize @Builtin.Specialize
@CompilerDirectives.TruffleBoundary @TruffleBoundary
public void move(EnsoFile target, Object options, EnsoContext ctx) throws IOException { public void move(
CopyOption[] copyOptions = EnsoFile target,
convertInteropArray(options, InteropLibrary.getUncached(), ctx, CopyOption[]::new); Object options,
truffleFile.move(target.truffleFile, copyOptions); @Cached ArrayLikeLengthNode lengthNode,
@Cached ArrayLikeAtNode atNode,
EnsoContext ctx)
throws IOException {
var copyOptions = namesToValues(options, lengthNode, atNode, ctx, StandardCopyOption::valueOf);
truffleFile.move(target.truffleFile, copyOptions.toArray(CopyOption[]::new));
} }
@Builtin.Method @Builtin.Method
@CompilerDirectives.TruffleBoundary @TruffleBoundary
public boolean startsWith(EnsoFile parent) { public boolean startsWith(EnsoFile parent) {
return truffleFile.startsWith(parent.truffleFile); return truffleFile.startsWith(parent.truffleFile);
} }
@ -397,7 +629,7 @@ public final class EnsoFile implements EnsoObject {
"Takes the text representation of a path and returns a TruffleFile corresponding to it.", "Takes the text representation of a path and returns a TruffleFile corresponding to it.",
autoRegister = false) autoRegister = false)
@Builtin.Specialize @Builtin.Specialize
@CompilerDirectives.TruffleBoundary @TruffleBoundary
public static EnsoFile fromString(EnsoContext context, String path) { public static EnsoFile fromString(EnsoContext context, String path) {
TruffleFile file = context.getPublicTruffleFile(path); TruffleFile file = context.getPublicTruffleFile(path);
return new EnsoFile(file); return new EnsoFile(file);
@ -408,7 +640,7 @@ public final class EnsoFile implements EnsoObject {
description = "A file corresponding to the current working directory.", description = "A file corresponding to the current working directory.",
autoRegister = false) autoRegister = false)
@Builtin.Specialize @Builtin.Specialize
@CompilerDirectives.TruffleBoundary @TruffleBoundary
public static EnsoFile currentDirectory(EnsoContext context) { public static EnsoFile currentDirectory(EnsoContext context) {
TruffleFile file = context.getCurrentWorkingDirectory(); TruffleFile file = context.getCurrentWorkingDirectory();
return new EnsoFile(file); return new EnsoFile(file);
@ -419,13 +651,13 @@ public final class EnsoFile implements EnsoObject {
description = "Gets the user's system-defined home directory.", description = "Gets the user's system-defined home directory.",
autoRegister = false) autoRegister = false)
@Builtin.Specialize @Builtin.Specialize
@CompilerDirectives.TruffleBoundary @TruffleBoundary
public static EnsoFile userHome(EnsoContext context) { public static EnsoFile userHome(EnsoContext context) {
return fromString(context, System.getProperty("user.home")); return fromString(context, System.getProperty("user.home"));
} }
@Override @Override
@CompilerDirectives.TruffleBoundary @TruffleBoundary
public String toString() { public String toString() {
return "(File " + truffleFile.getPath() + ")"; return "(File " + truffleFile.getPath() + ")";
} }
@ -449,4 +681,10 @@ public final class EnsoFile implements EnsoObject {
Type getType(@Bind("$node") Node node) { Type getType(@Bind("$node") Node node) {
return EnsoContext.get(node).getBuiltins().file(); return EnsoContext.get(node).getBuiltins().file();
} }
static RuntimeException raiseIOException(Node where, IOException ex) {
var ctx = EnsoContext.get(where);
var guestEx = ctx.asGuestValue(ex);
throw new PanicException(guestEx, where);
}
} }

View File

@ -111,8 +111,12 @@ public final class PanicException extends AbstractTruffleException implements En
} }
@NeverDefault @NeverDefault
static UnresolvedSymbol toDisplayText(IndirectInvokeMethodNode payloads) { static UnresolvedSymbol toDisplayText(IndirectInvokeMethodNode payloads)
throws UnsupportedMessageException {
var ctx = EnsoContext.get(payloads); var ctx = EnsoContext.get(payloads);
if (ctx == null) {
throw UnsupportedMessageException.create();
}
var scope = ctx.getBuiltins().panic().getDefinitionScope(); var scope = ctx.getBuiltins().panic().getDefinitionScope();
return UnresolvedSymbol.build("to_display_text", scope); return UnresolvedSymbol.build("to_display_text", scope);
} }

View File

@ -24,26 +24,31 @@ public final class Parser implements AutoCloseable {
name = "libenso_parser.so"; name = "libenso_parser.so";
} }
File parser = null; var whereAmI = Parser.class.getProtectionDomain().getCodeSource().getLocation();
File root;
try { try {
var whereAmI = Parser.class.getProtectionDomain().getCodeSource().getLocation(); root = new File(whereAmI.toURI()).getParentFile();
var d = new File(whereAmI.toURI()).getParentFile(); } catch (URISyntaxException ex) {
root = new File(".").getAbsoluteFile();
}
try {
var d = root;
File path = null; File path = null;
while (d != null) { while (d != null) {
path = new File(d, name); path = new File(d, name);
if (path.exists()) break; if (path.exists()) break;
d = d.getParentFile(); d = d.getParentFile();
} }
if (d == null) { if (d == null || path == null) {
throw new LinkageError( throw new LinkageError("Cannot find parser in " + root);
"Cannot find parser in " + new File(whereAmI.toURI()).getParentFile());
} }
parser = path; System.load(path.getAbsolutePath());
System.load(parser.getAbsolutePath()); } catch (NullPointerException | IllegalArgumentException | LinkageError e) {
} catch (IllegalArgumentException | URISyntaxException | LinkageError e) {
File root = new File(".").getAbsoluteFile();
if (!searchFromDirToTop(e, root, "target", "rust", "debug", name)) { if (!searchFromDirToTop(e, root, "target", "rust", "debug", name)) {
throw new IllegalStateException("Cannot load parser from " + parser, e); if (!searchFromDirToTop(
e, new File(".").getAbsoluteFile(), "target", "rust", "debug", name)) {
throw new IllegalStateException("Cannot load parser from " + root, e);
}
} }
} }
} }

View File

@ -289,48 +289,6 @@ public @interface Builtin {
WrapException[] value() default {}; WrapException[] value() default {};
} }
/**
* Annotation approving implicit {@link
* com.oracle.truffle.api.TruffleLanguage#asGuestValue(Object)} translation done on the return
* object. The conversion is generated automatically, depending on the type of the value.
*
* <p>Note that while explicit translations to interop value are discouraged, we still want to
* occasionally support it to easy builtins-writing process. The presence of the {@link
* ReturningGuestObject} only ensures that it is intentional.
*
* <p>Consider a method returning an {@link java.io.OutputStream} which is not an interop value,
* for the sake of the example:
*
* <pre>
* class Foo {
* {@link Builtin.Method @Builtin.Method}
* {@link Builtin.ReturningGuestObject @Builtin.ReturningGuestObject}
* java.lang.OutputStream foo(Object item) {
* return // ...
* }
* }
* </pre>
*
* The processor will detect the return type of method {@code foo} and perform an automatic
* conversion:
*
* <pre>
* {@link BuiltinMethod @BuiltinMethod}(type = "Foo", name = "create")
* public class CreateFooNode extends Node {
* java.lang.Object execute(Foo self, Object item) {
* return context
* .asGuestValue(self.foo(item));
* }
* }
* </pre>
*
* Without converting the object to the guest language value, it would crash during runtime.
* Without the presence of the annotation, the processor would detect the potential value
* requiring {@link com.oracle.truffle.api.TruffleLanguage#asGuestValue(Object)} translation but
* stop and report the error since it didn't seem to be intended by the user.
*/
@interface ReturningGuestObject {}
/** /**
* A Method marked with {@link Builtin.Specialize} annotation will generate specializations for * A Method marked with {@link Builtin.Specialize} annotation will generate specializations for
* overloaded and non-overloaded methods. The annotation requires presence of {@link * overloaded and non-overloaded methods. The annotation requires presence of {@link

View File

@ -180,10 +180,6 @@ public class BuiltinsProcessor extends AbstractProcessor {
OK: OK:
if (!TypeWithKind.isValidGuestType(processingEnv, method.getReturnType())) { if (!TypeWithKind.isValidGuestType(processingEnv, method.getReturnType())) {
if (method.getAnnotation(Builtin.ReturningGuestObject.class) != null) {
// guest objects can be of any type
break OK;
}
if (method.getAnnotation(SuppressWarnings.class) instanceof SuppressWarnings sw if (method.getAnnotation(SuppressWarnings.class) instanceof SuppressWarnings sw
&& Arrays.asList(sw.value()).contains("generic-enso-builtin-type")) { && Arrays.asList(sw.value()).contains("generic-enso-builtin-type")) {
// assume the case was review // assume the case was review

View File

@ -105,8 +105,7 @@ public abstract class MethodGenerator {
Kind.ERROR, Kind.ERROR,
"Automatic conversion of value of type " "Automatic conversion of value of type "
+ tpe.baseType() + tpe.baseType()
+ " to guest value requires explicit '@Builtin.ReturningGuestObject'" + " is no longer supported.");
+ " annotation");
} }
return "Object"; return "Object";
} }

View File

@ -8,7 +8,6 @@ import java.util.List;
import javax.annotation.processing.ProcessingEnvironment; import javax.annotation.processing.ProcessingEnvironment;
import javax.lang.model.element.Element; import javax.lang.model.element.Element;
import javax.tools.JavaFileObject; import javax.tools.JavaFileObject;
import org.enso.interpreter.dsl.Builtin;
public abstract class MethodNodeClassGenerator { public abstract class MethodNodeClassGenerator {
ClassName builtinNode; ClassName builtinNode;
@ -29,7 +28,7 @@ public abstract class MethodNodeClassGenerator {
* @return true if the annotation exists, false otherwise * @return true if the annotation exists, false otherwise
*/ */
protected boolean needsGuestValueConversion(Element origin) { protected boolean needsGuestValueConversion(Element origin) {
return origin.getAnnotation(Builtin.ReturningGuestObject.class) != null; return false;
} }
public void generate( public void generate(

View File

@ -41,9 +41,7 @@ public final class NoSpecializationClassGenerator extends MethodNodeClassGenerat
processingEnvironment processingEnvironment
.getMessager() .getMessager()
.printMessage( .printMessage(
Kind.ERROR, Kind.ERROR, "Value is already TruffleObject, don't need any conversions", origin);
"Value is already TruffleObject, don't use @Builtin.ReturningGuestObject",
origin);
} }
return new ExecuteMethodImplGenerator( return new ExecuteMethodImplGenerator(
processingEnvironment, origin, asGuestValue, varArgExpansion); processingEnvironment, origin, asGuestValue, varArgExpansion);

View File

@ -38,14 +38,12 @@ public final class SpecializedMethodsGenerator extends MethodGenerator {
elements, elements,
first.getModifiers().contains(Modifier.STATIC), first.getModifiers().contains(Modifier.STATIC),
first.getKind() == ElementKind.CONSTRUCTOR, first.getKind() == ElementKind.CONSTRUCTOR,
first.getAnnotation(Builtin.ReturningGuestObject.class) != null, false,
TypeWithKind.createFromTpe(first.getReturnType().toString())); TypeWithKind.createFromTpe(first.getReturnType().toString()));
// Make sure all methods were defined the same way, except for paramters' types // Make sure all methods were defined the same way, except for paramters' types
assert (allEqual(elements.stream().map(e -> e.getModifiers().contains(Modifier.STATIC)))); assert (allEqual(elements.stream().map(e -> e.getModifiers().contains(Modifier.STATIC))));
assert (allEqual(elements.stream().map(e -> e.getKind() == ElementKind.CONSTRUCTOR))); assert (allEqual(elements.stream().map(e -> e.getKind() == ElementKind.CONSTRUCTOR)));
assert (allEqual(
elements.stream().map(e -> e.getAnnotation(Builtin.ReturningGuestObject.class) != null)));
assert (allEqual( assert (allEqual(
elements.stream().map(e -> TypeWithKind.createFromTpe(e.getReturnType().toString())))); elements.stream().map(e -> TypeWithKind.createFromTpe(e.getReturnType().toString()))));
} }

View File

@ -2,9 +2,84 @@ package org.enso.base;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays; import java.util.Arrays;
public class Stream_Utils { public final class Stream_Utils {
private Stream_Utils() {}
/**
* Conversion interface. Any Enso/Truffle object with invocable {@read} member that takes three
* arguments is eligible for being treated as host Java {@link InputStream}. There are two
* overloaded {@link #asInputStream conversion methods}. The <em>hosted Java interop</em> system
* of Truffle will pick the more suitable one depending on the type of argument.
*
* @see #asInputStream
*/
public static interface InputStreamLike {
public int read(byte[] arr, int off, int len) throws IOException;
public default int available() {
return 0;
}
}
/**
* No conversion conversion. When the argument is already {@link InputStream} there is no need for
* doing any further conversions.
*
* @param is
* @return the {@code is} itself
*/
public static InputStream asInputStream(InputStream is) {
return is;
}
/**
* Conversion to {@link InputStream}. When the argument <em>looks like</em> an input stream, let's
* wrap it.
*
* @param inputStreamLike any guest object with {@code read} method
* @return proper
*/
public static InputStream asInputStream(InputStreamLike inputStreamLike) {
return new GuestInputStream(inputStreamLike);
}
/**
* Conversion interface. Any Enso/Truffle object with invocable {@write} member that takes three
* arguments is eligible for being treated as host Java {@link OutputStream}. There are two
* overloaded {@link #asOutputStream conversion methods}. The <em>hosted Java interop</em> system
* of Truffle will pick the more suitable one depending on the type of argument.
*
* @see #asOutputStream
*/
public static interface OutputStreamLike {
public void write(byte[] arr, int off, int len) throws IOException;
}
/**
* No conversion conversion. When the argument is already {@link OutputStream} there is no need
* for doing any further conversions.
*
* @param os
* @return the {@code is} itself
*/
public static OutputStream asOutputStream(OutputStream os) {
return os;
}
/**
* Conversion to {@link OutputStream}. When the argument <em>looks like</em> an output stream,
* let's wrap it.
*
* @param outputStreamLike any guest object with {@code write} method
* @return proper
*/
public static OutputStream asOutputStream(OutputStreamLike outputStreamLike) {
return new GuestOutputStream(outputStreamLike);
}
public static byte[] peek(InputStream stream, int n) throws IOException { public static byte[] peek(InputStream stream, int n) throws IOException {
assert n >= 0; assert n >= 0;
assert stream.markSupported(); assert stream.markSupported();
@ -25,4 +100,64 @@ public class Stream_Utils {
} }
return buffer; return buffer;
} }
private static class GuestInputStream extends InputStream {
private final InputStreamLike inputStreamLike;
private GuestInputStream(InputStreamLike inputStreamLike) {
this.inputStreamLike = inputStreamLike;
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
return inputStreamLike.read(b, off, len);
}
@Override
public int read() throws IOException {
byte[] arr = new byte[1];
int read = read(arr, 0, 1);
if (read == -1) {
return -1;
}
if (read != 1) {
throw new IOException();
}
return arr[0];
}
@Override
public int read(byte[] b) throws IOException {
return read(b, 0, b.length);
}
@Override
public int available() throws IOException {
try {
return inputStreamLike.available();
} catch (Error | Exception e) {
return 0;
}
}
}
private static final class GuestOutputStream extends OutputStream {
private final OutputStreamLike outputStreamLike;
private GuestOutputStream(OutputStreamLike os) {
this.outputStreamLike = os;
}
@Override
public void write(int b) throws IOException {
byte[] arr = new byte[] {(byte) b};
write(arr, 0, 1);
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
outputStreamLike.write(b, off, len);
}
}
} }

View File

@ -1,8 +1,14 @@
package org.enso.base; package org.enso.base;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.DOMConfiguration; import org.w3c.dom.DOMConfiguration;
import org.w3c.dom.Document;
import org.w3c.dom.Node; import org.w3c.dom.Node;
import org.w3c.dom.NodeList; import org.w3c.dom.NodeList;
import org.w3c.dom.bootstrap.DOMImplementationRegistry; import org.w3c.dom.bootstrap.DOMImplementationRegistry;
@ -10,6 +16,7 @@ import org.w3c.dom.ls.DOMImplementationLS;
import org.w3c.dom.ls.LSOutput; import org.w3c.dom.ls.LSOutput;
import org.w3c.dom.ls.LSSerializer; import org.w3c.dom.ls.LSSerializer;
import org.xml.sax.ErrorHandler; import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException; import org.xml.sax.SAXParseException;
@ -62,7 +69,25 @@ public class XML_Utils {
return out.toString(); return out.toString();
} }
public static void setCustomErrorHandler(DocumentBuilder documentBuilder) { public static Document parseStream(InputStream is)
throws ParserConfigurationException, SAXException, IOException {
return doParse(new InputSource(is));
}
public static Document parseString(String text)
throws ParserConfigurationException, SAXException, IOException {
return doParse(new InputSource(new StringReader(text)));
}
private static Document doParse(InputSource is)
throws ParserConfigurationException, SAXException, IOException {
var factory = DocumentBuilderFactory.newInstance();
var builder = factory.newDocumentBuilder();
configureErrorHandler(builder);
return builder.parse(is);
}
private static void configureErrorHandler(DocumentBuilder documentBuilder) {
documentBuilder.setErrorHandler( documentBuilder.setErrorHandler(
new ErrorHandler() { new ErrorHandler() {
@Override @Override

View File

@ -0,0 +1,23 @@
package org.enso.base.file_system;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.PathMatcher;
public final class File_Utils {
private File_Utils() {}
public static Path toPath(String path) {
return Path.of(path);
}
public static PathMatcher matchPath(String filter) {
var fs = FileSystems.getDefault();
var matcher = fs.getPathMatcher(filter);
return matcher;
}
public static boolean matches(PathMatcher matcher, String pathStr) {
return matcher.matches(Path.of(pathStr));
}
}

View File

@ -5,8 +5,8 @@ import Standard.Base.Errors.No_Such_Key.No_Such_Key
from Standard.Test import all from Standard.Test import all
polyglot java import java.nio.file.Path as JavaPath
polyglot java import java.util.Map as JavaMap polyglot java import java.util.Map as JavaMap
polyglot java import org.enso.base.file_system.File_Utils
## Type that violates reflexivity ## Type that violates reflexivity
type My_Nan type My_Nan
@ -105,7 +105,7 @@ add_specs suite_builder =
empty_map_fn = entry.get 1 empty_map_fn = entry.get 1
pending = entry.get 2 pending = entry.get 2
add_common_specs suite_builder lang pending empty_map_fn add_common_specs suite_builder lang pending empty_map_fn
suite_builder.group "Enso maps" group_builder-> suite_builder.group "Enso maps" group_builder->
@ -278,11 +278,11 @@ add_specs suite_builder =
map.get (js_str "A") . should_equal 42 map.get (js_str "A") . should_equal 42
group_builder.specify "should support host objects as keys" <| group_builder.specify "should support host objects as keys" <|
# JavaPath has proper implementation of hashCode # java.nio.path.Path has proper implementation of hashCode
map = Map.singleton (JavaPath.of "/home/user/file.txt") 42 map = Map.singleton (File_Utils.toPath "/home/user/file.txt") 42
map.get "X" . should_equal Nothing map.get "X" . should_equal Nothing
map.get "A" . should_equal Nothing map.get "A" . should_equal Nothing
map.get (JavaPath.of "/home/user/file.txt") . should_equal 42 map.get (File_Utils.toPath "/home/user/file.txt") . should_equal 42
group_builder.specify "should support Python objects as keys" pending=pending_python_missing <| group_builder.specify "should support Python objects as keys" pending=pending_python_missing <|
py_obj = py_wrapper 42 py_obj = py_wrapper 42
@ -464,14 +464,14 @@ add_common_specs suite_builder prefix:Text (pending : (Text | Nothing)) (empty_m
suite_builder.group prefix+": Common polyglot Map operations" pending=pending group_builder-> suite_builder.group prefix+": Common polyglot Map operations" pending=pending group_builder->
group_builder.specify "should get the default comparator for polyglot maps" <| group_builder.specify "should get the default comparator for polyglot maps" <|
Comparable.from empty_map . should_equal Default_Comparator Comparable.from empty_map . should_equal Default_Comparator
group_builder.specify "should compare two hash maps" <| group_builder.specify "should compare two hash maps" <|
(empty_map.insert "a" 1).should_equal (empty_map.insert "a" 1) (empty_map.insert "a" 1).should_equal (empty_map.insert "a" 1)
(empty_map.insert "b" 2).should_not_equal (empty_map.insert "a" 1) (empty_map.insert "b" 2).should_not_equal (empty_map.insert "a" 1)
empty_map.should_equal empty_map empty_map.should_equal empty_map
empty_map.should_not_equal (empty_map.insert "a" 1) empty_map.should_not_equal (empty_map.insert "a" 1)
(empty_map.insert "a" 1 . insert "b" 2).should_equal (empty_map.insert "b" 2 . insert "a" 1) (empty_map.insert "a" 1 . insert "b" 2).should_equal (empty_map.insert "b" 2 . insert "a" 1)
group_builder.specify "should allow checking for non emptiness" <| group_builder.specify "should allow checking for non emptiness" <|
non_empty = empty_map . insert "foo" 1234 non_empty = empty_map . insert "foo" 1234
empty_map.not_empty . should_be_false empty_map.not_empty . should_be_false
@ -481,7 +481,7 @@ add_common_specs suite_builder prefix:Text (pending : (Text | Nothing)) (empty_m
non_empty = empty_map.insert "a" "b" . insert "x" "y" non_empty = empty_map.insert "a" "b" . insert "x" "y"
empty_map.size . should_equal 0 empty_map.size . should_equal 0
non_empty.size . should_equal 2 non_empty.size . should_equal 2
group_builder.specify "should allow checking for emptiness" <| group_builder.specify "should allow checking for emptiness" <|
non_empty = empty_map . insert "foo" 1234 non_empty = empty_map . insert "foo" 1234
empty_map.is_empty . should_be_true empty_map.is_empty . should_be_true
@ -558,7 +558,11 @@ add_common_specs suite_builder prefix:Text (pending : (Text | Nothing)) (empty_m
empty_map.insert Nothing 1 . insert Nothing 2 . get Nothing . should_equal 2 empty_map.insert Nothing 1 . insert Nothing 2 . get Nothing . should_equal 2
empty_map.insert Nothing 1 . should_equal (empty_map.insert Nothing 1) empty_map.insert Nothing 1 . should_equal (empty_map.insert Nothing 1)
empty_map.insert Nothing 1 . insert Nothing 2 . at Nothing . should_equal 2 empty_map.insert Nothing 1 . insert Nothing 2 . at Nothing . should_equal 2
group_builder.specify "should handle JavaScript null as keys" <|
empty_map.insert js_null 1 . at Nothing . should_equal 1 empty_map.insert js_null 1 . at Nothing . should_equal 1
group_builder.specify "should handle Python None as keys" pending=pending_python_missing <|
empty_map.insert py_none 1 . at Nothing . should_equal 1 empty_map.insert py_none 1 . at Nothing . should_equal 1
group_builder.specify "should define a well-defined text conversion" <| group_builder.specify "should define a well-defined text conversion" <|

View File

@ -85,6 +85,7 @@ import project.Runtime.Stack_Traces_Spec
import project.System.Environment_Spec import project.System.Environment_Spec
import project.System.File_Spec import project.System.File_Spec
import project.System.File_Read_Spec import project.System.File_Read_Spec
import project.System.Input_Stream_Spec
import project.System.Process_Spec import project.System.Process_Spec
import project.System.Reporting_Stream_Decoder_Spec import project.System.Reporting_Stream_Decoder_Spec
import project.System.Reporting_Stream_Encoder_Spec import project.System.Reporting_Stream_Encoder_Spec
@ -112,6 +113,7 @@ main filter=Nothing =
File_Spec.add_specs suite_builder File_Spec.add_specs suite_builder
Temporary_File_Spec.add_specs suite_builder Temporary_File_Spec.add_specs suite_builder
File_Read_Spec.add_specs suite_builder File_Read_Spec.add_specs suite_builder
Input_Stream_Spec.add_specs suite_builder
Reporting_Stream_Decoder_Spec.add_specs suite_builder Reporting_Stream_Decoder_Spec.add_specs suite_builder
Reporting_Stream_Encoder_Spec.add_specs suite_builder Reporting_Stream_Encoder_Spec.add_specs suite_builder
Http_Header_Spec.add_specs suite_builder Http_Header_Spec.add_specs suite_builder

View File

@ -5,8 +5,8 @@ from Standard.Test import all
polyglot java import java.math.BigInteger as Java_Big_Integer polyglot java import java.math.BigInteger as Java_Big_Integer
polyglot java import java.nio.file.Path as Java_Path
polyglot java import java.util.Random as Java_Random polyglot java import java.util.Random as Java_Random
polyglot java import org.enso.base.file_system.File_Utils
polyglot java import org.enso.base_test_helpers.IntHolder polyglot java import org.enso.base_test_helpers.IntHolder
polyglot java import org.enso.base_test_helpers.IntHolderEquals polyglot java import org.enso.base_test_helpers.IntHolderEquals
@ -180,8 +180,8 @@ add_specs suite_builder =
((CustomEqType.C1 0) == (CustomEqType.C2 7 3)).should_be_false ((CustomEqType.C1 0) == (CustomEqType.C2 7 3)).should_be_false
group_builder.specify "should dispatch to equals on host values" <| group_builder.specify "should dispatch to equals on host values" <|
path1 = Java_Path.of "home" "user" . resolve "file.txt" path1 = File_Utils.toPath "home" . resolve "user" . resolve "file.txt"
path2 = Java_Path.of "home" "user" "file.txt" path2 = File_Utils.toPath "home" . resolve "user" . resolve "file.txt"
(path1 == path2).should_be_true (path1 == path2).should_be_true
path3 = path1.resolve "subfile.txt" path3 = path1.resolve "subfile.txt"
(path3 == path2).should_be_false (path3 == path2).should_be_false

View File

@ -6,12 +6,33 @@ import Standard.Base.System.Input_Stream.Input_Stream
from Standard.Test import all from Standard.Test import all
polyglot java import org.enso.base_test_helpers.RangeStream polyglot java import org.enso.base_test_helpers.RangeStream
polyglot java import org.enso.base.Stream_Utils
main filter=Nothing = main filter=Nothing =
suite = Test.build suite_builder-> suite = Test.build suite_builder->
add_specs suite_builder add_specs suite_builder
suite.run_with_filter filter suite.run_with_filter filter
foreign js is_like data available = """
let at = 0
let is = {
read : function(arr, off, len) {
let cnt = 0;
while (len-- > 0) {
arr[off++] = data[at++];
cnt++;
}
return cnt;
}
}
if (available) {
is.available = function() {
return data.length - at;
};
}
return is;
add_specs suite_builder = suite_builder.group "Input Stream" group_builder-> add_specs suite_builder = suite_builder.group "Input Stream" group_builder->
group_builder.specify "should be peekable if backed by memory" <| group_builder.specify "should be peekable if backed by memory" <|
Managed_Resource.bracket (Input_Stream.from_bytes [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) (.close) stream-> Managed_Resource.bracket (Input_Stream.from_bytes [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) (.close) stream->
@ -61,3 +82,33 @@ add_specs suite_builder = suite_builder.group "Input Stream" group_builder->
promoted_stream.peek_bytes 10 . should_equal [100, 101, 102, 103, 104] promoted_stream.peek_bytes 10 . should_equal [100, 101, 102, 103, 104]
# The read still succeeds - ensuring there isn't some early EOF # The read still succeeds - ensuring there isn't some early EOF
promoted_stream.read_n_bytes 10 . should_equal [100, 101, 102, 103, 104] promoted_stream.read_n_bytes 10 . should_equal [100, 101, 102, 103, 104]
group_builder.specify "read without available" <|
stream_like = is_like [20, 5, 1, 10] False
is = Stream_Utils.asInputStream stream_like
is.available . should_equal 0
is.read . should_equal 20
is.read . should_equal 5
is.available . should_equal 0
is.read . should_equal 1
is.read . should_equal 10
is.available . should_equal 0
group_builder.specify "read with available" <|
stream_like = is_like [20, 6, 8, 23] True
is = Stream_Utils.asInputStream stream_like
is.available . should_equal 4
is.read . should_equal 20
is.read . should_equal 6
is.available . should_equal 2
is.read . should_equal 8
is.read . should_equal 23
is.available . should_equal 0