mirror of
https://github.com/enso-org/enso.git
synced 2024-09-11 13:15:52 +03:00
Speeding up "hello world" example by 16%
This commit is contained in:
parent
5a7ad6bfe4
commit
ba19813511
94
build.sbt
94
build.sbt
@ -254,6 +254,8 @@ lazy val buildNativeImage =
|
|||||||
lazy val enso = (project in file("."))
|
lazy val enso = (project in file("."))
|
||||||
.settings(version := "0.1")
|
.settings(version := "0.1")
|
||||||
.aggregate(
|
.aggregate(
|
||||||
|
`persistance-dsl`,
|
||||||
|
`persistance`,
|
||||||
`interpreter-dsl`,
|
`interpreter-dsl`,
|
||||||
`interpreter-dsl-test`,
|
`interpreter-dsl-test`,
|
||||||
`json-rpc-server-test`,
|
`json-rpc-server-test`,
|
||||||
@ -1074,6 +1076,35 @@ lazy val searcher = project
|
|||||||
.dependsOn(testkit % Test)
|
.dependsOn(testkit % Test)
|
||||||
.dependsOn(`polyglot-api`)
|
.dependsOn(`polyglot-api`)
|
||||||
|
|
||||||
|
lazy val `persistance` = (project in file("lib/java/persistance"))
|
||||||
|
.settings(
|
||||||
|
version := "0.1",
|
||||||
|
frgaalJavaCompilerSetting,
|
||||||
|
Compile / javacOptions := ((Compile / javacOptions).value),
|
||||||
|
libraryDependencies ++= Seq(
|
||||||
|
"org.netbeans.api" % "org-openide-util-lookup" % netbeansApiVersion,
|
||||||
|
"junit" % "junit" % junitVersion % Test,
|
||||||
|
"com.github.sbt" % "junit-interface" % junitIfVersion % Test
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.dependsOn(`persistance-dsl` % Test)
|
||||||
|
|
||||||
|
lazy val `persistance-dsl` = (project in file("lib/java/persistance-dsl"))
|
||||||
|
.settings(
|
||||||
|
version := "0.1",
|
||||||
|
frgaalJavaCompilerSetting,
|
||||||
|
Compile / javacOptions := ((Compile / javacOptions).value ++
|
||||||
|
// Only run ServiceProvider processor and ignore those defined in META-INF, thus
|
||||||
|
// fixing incremental compilation setup
|
||||||
|
Seq(
|
||||||
|
"-processor",
|
||||||
|
"org.netbeans.modules.openide.util.ServiceProviderProcessor"
|
||||||
|
)),
|
||||||
|
libraryDependencies ++= Seq(
|
||||||
|
"org.netbeans.api" % "org-openide-util-lookup" % netbeansApiVersion % "provided"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
lazy val `interpreter-dsl` = (project in file("lib/scala/interpreter-dsl"))
|
lazy val `interpreter-dsl` = (project in file("lib/scala/interpreter-dsl"))
|
||||||
.settings(
|
.settings(
|
||||||
version := "0.1",
|
version := "0.1",
|
||||||
@ -1087,7 +1118,7 @@ lazy val `interpreter-dsl` = (project in file("lib/scala/interpreter-dsl"))
|
|||||||
)),
|
)),
|
||||||
libraryDependencies ++= Seq(
|
libraryDependencies ++= Seq(
|
||||||
"org.apache.commons" % "commons-lang3" % commonsLangVersion,
|
"org.apache.commons" % "commons-lang3" % commonsLangVersion,
|
||||||
"org.netbeans.api" % "org-openide-util-lookup" % netbeansApiVersion,
|
"org.netbeans.api" % "org-openide-util-lookup" % netbeansApiVersion % "provided",
|
||||||
"com.google.guava" % "guava" % guavaVersion exclude ("com.google.code.findbugs", "jsr305")
|
"com.google.guava" % "guava" % guavaVersion exclude ("com.google.code.findbugs", "jsr305")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -1373,23 +1404,24 @@ lazy val runtime = (project in file("engine/runtime"))
|
|||||||
scalacOptions += "-Ymacro-annotations",
|
scalacOptions += "-Ymacro-annotations",
|
||||||
scalacOptions ++= Seq("-Ypatmat-exhaust-depth", "off"),
|
scalacOptions ++= Seq("-Ypatmat-exhaust-depth", "off"),
|
||||||
libraryDependencies ++= jmh ++ jaxb ++ circe ++ GraalVM.langsPkgs ++ Seq(
|
libraryDependencies ++= jmh ++ jaxb ++ circe ++ GraalVM.langsPkgs ++ Seq(
|
||||||
"org.apache.commons" % "commons-lang3" % commonsLangVersion,
|
"org.apache.commons" % "commons-lang3" % commonsLangVersion,
|
||||||
"org.apache.tika" % "tika-core" % tikaVersion,
|
"org.apache.tika" % "tika-core" % tikaVersion,
|
||||||
"org.graalvm.polyglot" % "polyglot" % graalMavenPackagesVersion % "provided",
|
"org.graalvm.polyglot" % "polyglot" % graalMavenPackagesVersion % "provided",
|
||||||
"org.graalvm.sdk" % "polyglot-tck" % graalMavenPackagesVersion % "provided",
|
"org.graalvm.sdk" % "polyglot-tck" % graalMavenPackagesVersion % "provided",
|
||||||
"org.graalvm.truffle" % "truffle-api" % graalMavenPackagesVersion % "provided",
|
"org.graalvm.truffle" % "truffle-api" % graalMavenPackagesVersion % "provided",
|
||||||
"org.graalvm.truffle" % "truffle-dsl-processor" % graalMavenPackagesVersion % "provided",
|
"org.graalvm.truffle" % "truffle-dsl-processor" % graalMavenPackagesVersion % "provided",
|
||||||
"org.graalvm.truffle" % "truffle-tck" % graalMavenPackagesVersion % "provided",
|
"org.graalvm.truffle" % "truffle-tck" % graalMavenPackagesVersion % "provided",
|
||||||
"org.graalvm.truffle" % "truffle-tck-common" % graalMavenPackagesVersion % "provided",
|
"org.graalvm.truffle" % "truffle-tck-common" % graalMavenPackagesVersion % "provided",
|
||||||
"org.scalacheck" %% "scalacheck" % scalacheckVersion % Test,
|
"org.netbeans.api" % "org-openide-util-lookup" % netbeansApiVersion % "provided",
|
||||||
"org.scalactic" %% "scalactic" % scalacticVersion % Test,
|
"org.scalacheck" %% "scalacheck" % scalacheckVersion % Test,
|
||||||
"org.scalatest" %% "scalatest" % scalatestVersion % Test,
|
"org.scalactic" %% "scalactic" % scalacticVersion % Test,
|
||||||
"org.graalvm.truffle" % "truffle-api" % graalMavenPackagesVersion % Benchmark,
|
"org.scalatest" %% "scalatest" % scalatestVersion % Test,
|
||||||
"org.typelevel" %% "cats-core" % catsVersion,
|
"org.graalvm.truffle" % "truffle-api" % graalMavenPackagesVersion % Benchmark,
|
||||||
"junit" % "junit" % junitVersion % Test,
|
"org.typelevel" %% "cats-core" % catsVersion,
|
||||||
"com.github.sbt" % "junit-interface" % junitIfVersion % Test,
|
"junit" % "junit" % junitVersion % Test,
|
||||||
"org.hamcrest" % "hamcrest-all" % hamcrestVersion % Test,
|
"com.github.sbt" % "junit-interface" % junitIfVersion % Test,
|
||||||
"org.slf4j" % "slf4j-nop" % slf4jVersion % Benchmark
|
"org.hamcrest" % "hamcrest-all" % hamcrestVersion % Test,
|
||||||
|
"org.slf4j" % "slf4j-nop" % slf4jVersion % Benchmark
|
||||||
),
|
),
|
||||||
// Add all GraalVM packages with Runtime scope - we don't need them for compilation,
|
// Add all GraalVM packages with Runtime scope - we don't need them for compilation,
|
||||||
// just provide them at runtime (in module-path).
|
// just provide them at runtime (in module-path).
|
||||||
@ -1454,6 +1486,7 @@ lazy val runtime = (project in file("engine/runtime"))
|
|||||||
.dependsOn(`common-polyglot-core-utils`)
|
.dependsOn(`common-polyglot-core-utils`)
|
||||||
.dependsOn(`edition-updater`)
|
.dependsOn(`edition-updater`)
|
||||||
.dependsOn(`interpreter-dsl`)
|
.dependsOn(`interpreter-dsl`)
|
||||||
|
.dependsOn(`persistance-dsl` % "provided")
|
||||||
.dependsOn(`library-manager`)
|
.dependsOn(`library-manager`)
|
||||||
.dependsOn(`logging-truffle-connector`)
|
.dependsOn(`logging-truffle-connector`)
|
||||||
.dependsOn(`polyglot-api`)
|
.dependsOn(`polyglot-api`)
|
||||||
@ -1468,14 +1501,23 @@ lazy val `runtime-parser` =
|
|||||||
.settings(
|
.settings(
|
||||||
frgaalJavaCompilerSetting,
|
frgaalJavaCompilerSetting,
|
||||||
instrumentationSettings,
|
instrumentationSettings,
|
||||||
|
commands += WithDebugCommand.withDebug,
|
||||||
|
fork := true,
|
||||||
|
Test / javaOptions ++= Seq(
|
||||||
|
"-Dgraalvm.locatorDisabled=true",
|
||||||
|
s"--upgrade-module-path=${file("engine/runtime/build-cache/truffle-api.jar").absolutePath}"
|
||||||
|
),
|
||||||
libraryDependencies ++= Seq(
|
libraryDependencies ++= Seq(
|
||||||
"junit" % "junit" % junitVersion % Test,
|
"junit" % "junit" % junitVersion % Test,
|
||||||
"com.github.sbt" % "junit-interface" % junitIfVersion % Test,
|
"com.github.sbt" % "junit-interface" % junitIfVersion % Test,
|
||||||
"org.scalatest" %% "scalatest" % scalatestVersion % Test
|
"org.scalatest" %% "scalatest" % scalatestVersion % Test,
|
||||||
|
"org.netbeans.api" % "org-openide-util-lookup" % netbeansApiVersion % "provided"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.dependsOn(syntax)
|
.dependsOn(syntax)
|
||||||
.dependsOn(`syntax-rust-definition`)
|
.dependsOn(`syntax-rust-definition`)
|
||||||
|
.dependsOn(`persistance`)
|
||||||
|
.dependsOn(`persistance-dsl` % "provided")
|
||||||
|
|
||||||
lazy val `runtime-compiler` =
|
lazy val `runtime-compiler` =
|
||||||
(project in file("engine/runtime-compiler"))
|
(project in file("engine/runtime-compiler"))
|
||||||
@ -1483,16 +1525,18 @@ lazy val `runtime-compiler` =
|
|||||||
frgaalJavaCompilerSetting,
|
frgaalJavaCompilerSetting,
|
||||||
instrumentationSettings,
|
instrumentationSettings,
|
||||||
libraryDependencies ++= Seq(
|
libraryDependencies ++= Seq(
|
||||||
"junit" % "junit" % junitVersion % Test,
|
"junit" % "junit" % junitVersion % Test,
|
||||||
"com.github.sbt" % "junit-interface" % junitIfVersion % Test,
|
"com.github.sbt" % "junit-interface" % junitIfVersion % Test,
|
||||||
"org.scalatest" %% "scalatest" % scalatestVersion % Test,
|
"org.scalatest" %% "scalatest" % scalatestVersion % Test,
|
||||||
"com.lihaoyi" %% "fansi" % fansiVersion
|
"org.netbeans.api" % "org-openide-util-lookup" % netbeansApiVersion % "provided",
|
||||||
|
"com.lihaoyi" %% "fansi" % fansiVersion
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
.dependsOn(`runtime-parser`)
|
.dependsOn(`runtime-parser`)
|
||||||
.dependsOn(pkg)
|
.dependsOn(pkg)
|
||||||
.dependsOn(`polyglot-api`)
|
.dependsOn(`polyglot-api`)
|
||||||
.dependsOn(editions)
|
.dependsOn(editions)
|
||||||
|
.dependsOn(`persistance-dsl` % "provided")
|
||||||
|
|
||||||
lazy val `runtime-instrument-common` =
|
lazy val `runtime-instrument-common` =
|
||||||
(project in file("engine/runtime-instrument-common"))
|
(project in file("engine/runtime-instrument-common"))
|
||||||
|
@ -13,6 +13,7 @@ import project.Extensions.Prefix_Name.Prefix_Name
|
|||||||
import project.Internal.Fan_Out
|
import project.Internal.Fan_Out
|
||||||
import project.Internal.Java_Exports
|
import project.Internal.Java_Exports
|
||||||
import project.Internal.Java_Problems
|
import project.Internal.Java_Problems
|
||||||
|
import project.Internal.Widget_Helpers
|
||||||
from project.Internal.Java_Exports import make_inferred_builder
|
from project.Internal.Java_Exports import make_inferred_builder
|
||||||
|
|
||||||
## PRIVATE
|
## PRIVATE
|
||||||
|
@ -12,84 +12,82 @@ One of the largest pain points for users of Enso at the moment is the fact that
|
|||||||
it has to precompile the entire standard library on every project load. This is,
|
it has to precompile the entire standard library on every project load. This is,
|
||||||
in essence, due to the fact that the current parser is abysmally slow, and
|
in essence, due to the fact that the current parser is abysmally slow, and
|
||||||
incredibly demanding. The obvious solution to improve this is to take the parser
|
incredibly demanding. The obvious solution to improve this is to take the parser
|
||||||
out of the equation in its entirety, by serialising the parser's output.
|
out of the equation in its entirety, by serializing the parser's output.
|
||||||
|
|
||||||
To that end, we want to serialise the Enso IR to a format that can later be read
|
To that end, we want to serialize the Enso IR to a format that can later be read
|
||||||
back in, bypassing the parser entirely. Furthermore, we can move the boundary at
|
back in, bypassing the parser entirely. Furthermore, we can move the boundary at
|
||||||
which this serialisation takes place to the end of the compiler pipeline,
|
which this serialization takes place to the end of the compiler pipeline,
|
||||||
thereby bypassing doing most of the compilation work, and further improving
|
thereby bypassing doing most of the compilation work, and further improving
|
||||||
startup performance.
|
startup performance.
|
||||||
|
|
||||||
<!-- MarkdownTOC levels="2,3" autolink="true" -->
|
<!-- MarkdownTOC levels="2,3" autolink="true" -->
|
||||||
|
|
||||||
- [Serialising the IR](#serialising-the-ir)
|
- [Serializing the IR](#serializing-the-ir)
|
||||||
- [Breaking Links](#breaking-links)
|
- [Breaking Links](#breaking-links)
|
||||||
- [Storing the IR](#storing-the-ir)
|
- [Storing the IR](#storing-the-ir)
|
||||||
- [Metadata Format](#metadata-format)
|
- [Metadata Format](#metadata-format)
|
||||||
- [Portability Guarantees](#portability-guarantees)
|
- [Portability and Versioning](#portability-and-versioning)
|
||||||
- [Loading the IR](#loading-the-ir)
|
- [Loading the IR](#loading-the-ir)
|
||||||
- [Integrity Checking](#integrity-checking)
|
- [Integrity Checking](#integrity-checking)
|
||||||
- [Error Handling](#error-handling)
|
- [Error Handling](#error-handling)
|
||||||
- [Imports](#imports)
|
- [Imports](#imports)
|
||||||
- [Testing the Serialisation](#testing-the-serialisation)
|
- [Testing the Serialization](#testing-the-serialization)
|
||||||
- [Future Directions](#future-directions)
|
- [Future Directions](#future-directions)
|
||||||
|
|
||||||
<!-- /MarkdownTOC -->
|
<!-- /MarkdownTOC -->
|
||||||
|
|
||||||
## Serialising the IR
|
## Serializing the IR
|
||||||
|
|
||||||
As the serialised IR doesn't need to be read by anything other than Enso, we
|
Using classical Java Serialization turned out to be unsuitably slow. Rather than
|
||||||
need not use a representation that is portable between platforms. As a result,
|
switching to other serialization framework that does the same, but faster we
|
||||||
we have picked the `Serializable` infrastructure that is _already present_ on
|
desided in [PR-8207](https://github.com/enso-org/enso/pull/8207) to create _own
|
||||||
the JVM. It has the following benefits:
|
persistance framework_ that radically changes the way we can read the caches.
|
||||||
|
Rather than loading all the megabytes of stored data, it reads them _lazily on
|
||||||
|
demand_.
|
||||||
|
|
||||||
- It is able to serialise arbitrary object graphs while maintaining object
|
Use following command to generate the Javadoc for the `org.enso.persist`
|
||||||
identity and tracking references. This cannot be disabled for `Serializable`,
|
package:
|
||||||
but that is fine as we want it.
|
|
||||||
- It is built into the JVM and is hence guaranteed to be portable between
|
|
||||||
instances of the same JVM.
|
|
||||||
- It copes fine with highly-nested scala types, like our IR.
|
|
||||||
|
|
||||||
In order to maximise the benefits of this process, we want to serialise the IR
|
```bash
|
||||||
as _late_ in the compiler pipeline as possible. This means serialising it just
|
enso$ find lib/java/persistance/src/main/java/ | grep java$ | xargs ~/bin/graalvm-21/bin/javadoc -d target/javadoc/ --snippet-path lib/java/persistance/src/test/java/
|
||||||
|
enso$ links target/javadoc/index.html
|
||||||
|
```
|
||||||
|
|
||||||
|
In order to maximize the benefits of this process, we want to serialize the IR
|
||||||
|
as _late_ in the compiler pipeline as possible. This means serializing it just
|
||||||
before the code generation step that generates Truffle nodes (before the
|
before the code generation step that generates Truffle nodes (before the
|
||||||
`RuntimeStubsGenerator` and `IrToTruffle` run).
|
`RuntimeStubsGenerator` and `IrToTruffle` run).
|
||||||
|
|
||||||
This serialisation should take place in an _offloaded thread_ so that it doesn't
|
This serialization should take place in an _offloaded thread_ so that it doesn't
|
||||||
block the compiler from continuing.
|
block the compiler from continuing.
|
||||||
|
|
||||||
### Breaking Links
|
### Breaking Links
|
||||||
|
|
||||||
Doing this naïvely, however, means that we can inadvertently end up serialising
|
Doing this naïvely, however, means that we can inadvertently end up serializing
|
||||||
the entire module graph. This is due to the `BindingsMap`, which contains a
|
the entire module graph. This is due to the `BindingsMap`, which contains a
|
||||||
reference to the associated `runtime.Module`, from which there is a reference to
|
reference to the associated `runtime.Module`, from which there is a reference to
|
||||||
the `ModuleScope`. The `ModuleScope` may then reference other `runtime.Module`s
|
the `ModuleScope`. The `ModuleScope` may then reference other `runtime.Module`s
|
||||||
which all contain `IR.Module`s. Therefore, done in a silly fashion, we end up
|
which all contain `IR.Module`s. Therefore, done in a silly fashion, we end up
|
||||||
serialising the entire reachable module graph. This is not what we want.
|
serializing the entire reachable module graph. This is not what we want.
|
||||||
|
|
||||||
While the ideal way of solving this problem would be to customise the
|
The `Persistance.write` method contains additional `writeReplace` function which
|
||||||
serialisation and deserialisation process for the `BindingsMap`, the JVM's
|
our cache system uses to perform following modification just before
|
||||||
`Serializable` does not provide the ability to customise it enough to solve this
|
`ProcessingPass.Metadata` are stored down:
|
||||||
problem. Instead, we solve it using a preprocessing step:
|
|
||||||
|
|
||||||
- We can modify `BindingsMap` and its child types to be able to contain an
|
- modify `BindingsMap` and its child types to be able to contain an unlinked
|
||||||
unlinked module pointer
|
module pointer `case class ModulePointer(qualifiedName: List[String])` in
|
||||||
`case class ModulePointer(qualifiedName: List[String])` in place of a
|
place of a `Module`.
|
||||||
`Module`.
|
- As the `MetadataStorage` type that holds the `BindingsMap` is mutable it might
|
||||||
- As the `MetadataStorage` type that holds the `BindingsMap` is mutable it can
|
be tempting to update it in place, but relying on `writeReplace` mechanism is
|
||||||
be updated in place without having to reassemble the entire IR graph.
|
safer as it only changes the format of object being written down, rather than
|
||||||
- Hence, we can traverse all the nodes in the `ir.preorder` that have metadata
|
modifying objects of live `IR` - potentially shared with other parts of the
|
||||||
consisting of either the `BindingsMap` or `ResolvedName` types (provided by
|
system.
|
||||||
the following passes: `BindingAnalysis`, `MethodDefinitions`, `GlobalNames`,
|
|
||||||
`VectorLiterals`, `Patterns`), and perform a replacement.
|
|
||||||
|
|
||||||
Having done this, we have broken any links that the IR may hold between modules,
|
Having done this, we have broken any links that the IR may hold between modules,
|
||||||
and can serialise each module individually.
|
and can serialize each module individually.
|
||||||
|
|
||||||
This serialisation must take place _after_ codegen has happened as it modifies
|
It _may_ be safer to `duplicate` the IR before handing it to serialization, but
|
||||||
the IR in place. The compiler can handle giving it to the offloaded
|
it shouldn't be necessary if the `writeReplace` function is written correctly.
|
||||||
serialisation thread. It _may_ be necessary to `duplicate` the IR before handing
|
|
||||||
it to this thread, but this should be checked during development.
|
|
||||||
|
|
||||||
## Storing the IR
|
## Storing the IR
|
||||||
|
|
||||||
@ -146,12 +144,28 @@ All hashes are encoded in SHA1 format, for performance reasons. The engine
|
|||||||
version is encoded in the cache path, and hence does not need to be explicitly
|
version is encoded in the cache path, and hence does not need to be explicitly
|
||||||
specified in the metadata.
|
specified in the metadata.
|
||||||
|
|
||||||
### Portability Guarantees
|
### Portability and Versioning
|
||||||
|
|
||||||
As part of this design we provide only the following portability guarantees:
|
These are two static methods in `Persistance` class to help creating a `byte[]`
|
||||||
|
from a single object and then read it back. The array is identified with
|
||||||
|
following header:
|
||||||
|
|
||||||
- The serialised IR must be able to be deserialised by _the same version of
|
- 4 bytes fixed header
|
||||||
Enso_ that wrote the original blob.
|
- 4 bytes describing the version
|
||||||
|
- 4 bytes to locate the beginning of the object (the objects aren't written
|
||||||
|
linearly)
|
||||||
|
|
||||||
|
E.g. 12 bytes overhead before the actual data start. Following versioning is
|
||||||
|
recommended when making a change:
|
||||||
|
|
||||||
|
- when you change something really core in the `Persitance` implementation -
|
||||||
|
change the builtin header first four bytes
|
||||||
|
- when you add or remove a Persistance implementation the version changes (as it
|
||||||
|
is computed from all the IDs present in the system)
|
||||||
|
- when you change format of some `Persitance.writeObject` method - change its ID
|
||||||
|
|
||||||
|
That way the same version of Enso will recognize its `.ir` files. Different
|
||||||
|
versions of Enso will realize that the files aren't in suitable form.
|
||||||
|
|
||||||
## Loading the IR
|
## Loading the IR
|
||||||
|
|
||||||
@ -165,10 +179,12 @@ checking on the loaded cache. It works as follows.
|
|||||||
2. **Check Integrity:** Check the module's [metadata](#metadata-format) for
|
2. **Check Integrity:** Check the module's [metadata](#metadata-format) for
|
||||||
validity according to the [integrity rules](#integrity-checking).
|
validity according to the [integrity rules](#integrity-checking).
|
||||||
3. **Load:** If the cache passes the integrity check, load the `.ir` file. If
|
3. **Load:** If the cache passes the integrity check, load the `.ir` file. If
|
||||||
deserialisation fails in any way, immediately fall back to parsing the source
|
deserialization fails in any way, immediately fall back to parsing the source
|
||||||
file.
|
file.
|
||||||
4. **Re-Link:** If loading completed successfully, re-link the `BindingsMap`
|
4. **Re-Link:** Relinking is part of **Load**. When using `Persistance.read`
|
||||||
metadata to the proper modules in question.
|
provide own `readResolve` function. Such a function gets a chance to change
|
||||||
|
and replace each object read-in with appropriate variant respecting the whole
|
||||||
|
compiler environment.
|
||||||
|
|
||||||
The main subtlety here is handling the dependencies between modules. We need to
|
The main subtlety here is handling the dependencies between modules. We need to
|
||||||
ensure that, when loading multiple cached libraries, we properly handle them
|
ensure that, when loading multiple cached libraries, we properly handle them
|
||||||
@ -177,8 +193,8 @@ setting `AFTER_STATIC_PASSES` as the compilation state after loading the module.
|
|||||||
This will tie into the current `ImportsResolver` and `ExportsResolver` which are
|
This will tie into the current `ImportsResolver` and `ExportsResolver` which are
|
||||||
run in an un-gated fashion in `Compiler::run`.
|
run in an un-gated fashion in `Compiler::run`.
|
||||||
|
|
||||||
In order to prevent the execution of malicious code when deserialising we should
|
Unlike classical Java deserialization nly registered `Persistance` subclasses
|
||||||
employ a deserialisation filter as built into the JDK.
|
may participate in deserialization making it much safer and less vulnerable.
|
||||||
|
|
||||||
### Integrity Checking
|
### Integrity Checking
|
||||||
|
|
||||||
@ -195,8 +211,8 @@ ignored if it is in a read-only location.
|
|||||||
It is important, as part of this, that we fail under all circumstances into a
|
It is important, as part of this, that we fail under all circumstances into a
|
||||||
working state. This means that:
|
working state. This means that:
|
||||||
|
|
||||||
- If serialisation fails, we report a low-priority error message and continue.
|
- If serialization fails, we report a low-priority error message and continue.
|
||||||
- If deserialisation fails, we fall back to loading and parsing the original
|
- If deserialization fails, we fall back to loading and parsing the original
|
||||||
source file.
|
source file.
|
||||||
|
|
||||||
At no point should this mechanism be exposed to the user in any visible way,
|
At no point should this mechanism be exposed to the user in any visible way,
|
||||||
@ -215,12 +231,15 @@ until a complete cache invalidation was forced.
|
|||||||
Therefore, the compiler performs an additional check by invalidating module's
|
Therefore, the compiler performs an additional check by invalidating module's
|
||||||
cache if any of its imported modules have been invalidated.
|
cache if any of its imported modules have been invalidated.
|
||||||
|
|
||||||
## Testing the Serialisation
|
## Testing the Serialization
|
||||||
|
|
||||||
There are two main elements that need to be tested as part of this feature.
|
There are two main elements that need to be tested as part of this feature.
|
||||||
|
|
||||||
- Firstly, we need to test the serialisation and deserialisation process,
|
- `persistance` project comes with its own unit tests
|
||||||
including the rewrite of `BindingsMap` to work properly.
|
- `runtime-parser` project adds tests of various core classes used during `IR`
|
||||||
|
serialization - like Scala `List` or checks of the _laziness_ of Scala `Seq`
|
||||||
|
- We need to test the serialization and deserialization process, including the
|
||||||
|
rewrite of `BindingsMap` to work properly.
|
||||||
- We also need to test the discovery of cache locations on the filesystem and
|
- We also need to test the discovery of cache locations on the filesystem and
|
||||||
cache eviction strategies. The best way to do this is to set `$ENSO_DATA` to a
|
cache eviction strategies. The best way to do this is to set `$ENSO_DATA` to a
|
||||||
temporary directory and then directly interact with the filesystem. Caching
|
temporary directory and then directly interact with the filesystem. Caching
|
||||||
@ -243,16 +262,23 @@ library in a single pass.
|
|||||||
The bindings are serialized along with the library caches in a file with a
|
The bindings are serialized along with the library caches in a file with a
|
||||||
`.bindings` suffix.
|
`.bindings` suffix.
|
||||||
|
|
||||||
|
Further more the storage of `.ir` files contains usage of _lazy_ `Seq`
|
||||||
|
references to separate the general part of the `IR` tree from elements
|
||||||
|
representing method bodies. As such the compiler can process the structure of
|
||||||
|
`.ir` files, but avoid loading in `IR` for methods that aren't being executed.
|
||||||
|
|
||||||
## Future Directions
|
## Future Directions
|
||||||
|
|
||||||
Due to the less than ideal platform situation we're in, we're limited to using
|
The `Persistance` framework gives us _laziness_ opportunities and we should use
|
||||||
Java's `Serializable`. It is not as performant as other options.
|
them more:
|
||||||
|
|
||||||
- [FST](https://github.com/RuedigerMoeller/fast-serialization) is around 10x
|
- have a single _blob_ with all `IR`s per a library and read only the parts that
|
||||||
faster than the JVM's serialization, and is a drop-in replacement.
|
are needed
|
||||||
- However, the version that supports Java 11 utilises reflection that trips
|
|
||||||
warnings that will be disallowed with Java 17 (the next LTS version for
|
- experiement with GC - being able to release parts of unused `IR` once they
|
||||||
GraalVM).
|
were used (for code generation or co.)
|
||||||
- The version that fixes this relies on the foreign memory API which is
|
|
||||||
available in Java 17. I recommend that once we're on Java 17 builds the
|
- make the `.ir` files smaller where possible
|
||||||
serialization is updated to work using FST.
|
|
||||||
|
The use of `Persistance` has already sped up the execution time of simple
|
||||||
|
`IO.println "Hello!"` by 16% - let's use it to speed things up even more.
|
||||||
|
@ -572,7 +572,8 @@ object Main {
|
|||||||
topScope.compile(shouldCompileDependencies)
|
topScope.compile(shouldCompileDependencies)
|
||||||
exitSuccess()
|
exitSuccess()
|
||||||
} catch {
|
} catch {
|
||||||
case _: Throwable =>
|
case t: Throwable =>
|
||||||
|
logger.error("Unexpected internal error", t)
|
||||||
exitFail()
|
exitFail()
|
||||||
} finally {
|
} finally {
|
||||||
context.context.close()
|
context.context.close()
|
||||||
|
@ -81,8 +81,6 @@ public interface CompilerContext extends CompilerStub {
|
|||||||
|
|
||||||
boolean wasLoadedFromCache(Module module);
|
boolean wasLoadedFromCache(Module module);
|
||||||
|
|
||||||
boolean hasCrossModuleLinks(Module module);
|
|
||||||
|
|
||||||
org.enso.compiler.core.ir.Module getIr(Module module);
|
org.enso.compiler.core.ir.Module getIr(Module module);
|
||||||
|
|
||||||
CompilationStage getCompilationStage(Module module);
|
CompilationStage getCompilationStage(Module module);
|
||||||
@ -108,8 +106,6 @@ public interface CompilerContext extends CompilerStub {
|
|||||||
|
|
||||||
void loadedFromCache(boolean b);
|
void loadedFromCache(boolean b);
|
||||||
|
|
||||||
void hasCrossModuleLinks(boolean b);
|
|
||||||
|
|
||||||
void resetScope();
|
void resetScope();
|
||||||
|
|
||||||
void invalidateCache();
|
void invalidateCache();
|
||||||
@ -134,8 +130,6 @@ public interface CompilerContext extends CompilerStub {
|
|||||||
|
|
||||||
public abstract boolean isSynthetic();
|
public abstract boolean isSynthetic();
|
||||||
|
|
||||||
public abstract boolean hasCrossModuleLinks();
|
|
||||||
|
|
||||||
public abstract org.enso.compiler.core.ir.Module getIr();
|
public abstract org.enso.compiler.core.ir.Module getIr();
|
||||||
|
|
||||||
public abstract boolean isPrivate();
|
public abstract boolean isPrivate();
|
||||||
|
@ -0,0 +1,155 @@
|
|||||||
|
package org.enso.compiler.pass.analyse;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import org.enso.compiler.pass.analyse.AliasAnalysis.Graph;
|
||||||
|
import org.enso.compiler.pass.resolve.DocumentationComments;
|
||||||
|
import org.enso.compiler.pass.resolve.FullyQualifiedNames;
|
||||||
|
import org.enso.compiler.pass.resolve.IgnoredBindings;
|
||||||
|
import org.enso.compiler.pass.resolve.ModuleAnnotations;
|
||||||
|
import org.enso.compiler.pass.resolve.TypeSignatures;
|
||||||
|
import org.enso.persist.Persistable;
|
||||||
|
import org.enso.persist.Persistance;
|
||||||
|
import org.openide.util.lookup.ServiceProvider;
|
||||||
|
import scala.Option;
|
||||||
|
|
||||||
|
@Persistable(clazz = CachePreferenceAnalysis.WeightInfo.class, id = 1111)
|
||||||
|
@Persistable(clazz = DataflowAnalysis.DependencyInfo.class, id = 1112)
|
||||||
|
@Persistable(clazz = DataflowAnalysis.DependencyMapping.class, id = 1113)
|
||||||
|
@Persistable(clazz = GatherDiagnostics.DiagnosticsMeta.class, id = 1114)
|
||||||
|
@Persistable(clazz = DocumentationComments.Doc.class, id = 1115)
|
||||||
|
@Persistable(clazz = AliasAnalysis$Info$Occurrence.class, id = 1116)
|
||||||
|
@Persistable(clazz = TypeSignatures.Signature.class, id = 1117)
|
||||||
|
@Persistable(clazz = ModuleAnnotations.Annotations.class, id = 1118)
|
||||||
|
@Persistable(clazz = AliasAnalysis$Info$Scope$Root.class, id = 1120)
|
||||||
|
@Persistable(clazz = DataflowAnalysis$DependencyInfo$Type$Static.class, id = 1121)
|
||||||
|
@Persistable(clazz = DataflowAnalysis$DependencyInfo$Type$Dynamic.class, id = 1122)
|
||||||
|
@Persistable(clazz = AliasAnalysis$Info$Scope$Child.class, id = 1123)
|
||||||
|
@Persistable(clazz = AliasAnalysis$Graph$Occurrence$Use.class, id = 1125)
|
||||||
|
@Persistable(clazz = AliasAnalysis$Graph$Occurrence$Def.class, id = 1126)
|
||||||
|
@Persistable(clazz = AliasAnalysis$Graph$Link.class, id = 1127)
|
||||||
|
@Persistable(clazz = FullyQualifiedNames.FQNResolution.class, id = 1128)
|
||||||
|
@Persistable(clazz = FullyQualifiedNames.ResolvedLibrary.class, id = 1129)
|
||||||
|
@Persistable(clazz = FullyQualifiedNames.ResolvedModule.class, id = 1130)
|
||||||
|
public final class PassPersistance {
|
||||||
|
private PassPersistance() {}
|
||||||
|
|
||||||
|
@ServiceProvider(service = Persistance.class)
|
||||||
|
public static final class PersistState extends Persistance<IgnoredBindings.State> {
|
||||||
|
public PersistState() {
|
||||||
|
super(IgnoredBindings.State.class, true, 1101);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void writeObject(IgnoredBindings.State obj, Output out) throws IOException {
|
||||||
|
out.writeBoolean(obj.isIgnored());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected IgnoredBindings.State readObject(Input in)
|
||||||
|
throws IOException, ClassNotFoundException {
|
||||||
|
var b = in.readBoolean();
|
||||||
|
return b
|
||||||
|
? org.enso.compiler.pass.resolve.IgnoredBindings$State$Ignored$.MODULE$
|
||||||
|
: org.enso.compiler.pass.resolve.IgnoredBindings$State$NotIgnored$.MODULE$;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ServiceProvider(service = Persistance.class)
|
||||||
|
public static final class PersistTail extends Persistance<TailCall.TailPosition> {
|
||||||
|
public PersistTail() {
|
||||||
|
super(TailCall.TailPosition.class, true, 1102);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void writeObject(TailCall.TailPosition obj, Output out) throws IOException {
|
||||||
|
out.writeBoolean(obj.isTail());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected TailCall.TailPosition readObject(Input in)
|
||||||
|
throws IOException, ClassNotFoundException {
|
||||||
|
var b = in.readBoolean();
|
||||||
|
return b
|
||||||
|
? org.enso.compiler.pass.analyse.TailCall$TailPosition$Tail$.MODULE$
|
||||||
|
: org.enso.compiler.pass.analyse.TailCall$TailPosition$NotTail$.MODULE$;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@org.openide.util.lookup.ServiceProvider(service = Persistance.class)
|
||||||
|
public static final class PersistAliasAnalysisGraphScope
|
||||||
|
extends Persistance<org.enso.compiler.pass.analyse.AliasAnalysis$Graph$Scope> {
|
||||||
|
public PersistAliasAnalysisGraphScope() {
|
||||||
|
super(org.enso.compiler.pass.analyse.AliasAnalysis$Graph$Scope.class, false, 1124);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
protected org.enso.compiler.pass.analyse.AliasAnalysis$Graph$Scope readObject(Input in)
|
||||||
|
throws IOException {
|
||||||
|
var childScopes = in.readInline(scala.collection.immutable.List.class);
|
||||||
|
var occurrences = (scala.collection.immutable.Set) in.readObject();
|
||||||
|
var allDefinitions = in.readInline(scala.collection.immutable.List.class);
|
||||||
|
var parent =
|
||||||
|
new org.enso.compiler.pass.analyse.AliasAnalysis$Graph$Scope(
|
||||||
|
childScopes, occurrences, allDefinitions);
|
||||||
|
var optionParent = Option.apply(parent);
|
||||||
|
childScopes.forall(
|
||||||
|
(object) -> {
|
||||||
|
var ch = (org.enso.compiler.pass.analyse.AliasAnalysis$Graph$Scope) object;
|
||||||
|
ch.parent_$eq(optionParent);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
protected void writeObject(
|
||||||
|
org.enso.compiler.pass.analyse.AliasAnalysis$Graph$Scope obj, Output out)
|
||||||
|
throws IOException {
|
||||||
|
out.writeInline(scala.collection.immutable.List.class, obj.childScopes());
|
||||||
|
out.writeObject(obj.occurrences());
|
||||||
|
out.writeInline(scala.collection.immutable.List.class, obj.allDefinitions());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@org.openide.util.lookup.ServiceProvider(service = Persistance.class)
|
||||||
|
public static final class PersistAliasAnalysisGraph extends Persistance<Graph> {
|
||||||
|
public PersistAliasAnalysisGraph() {
|
||||||
|
super(Graph.class, false, 1119);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
protected Graph readObject(Input in) throws IOException {
|
||||||
|
var g = new Graph();
|
||||||
|
|
||||||
|
var rootScope = (AliasAnalysis$Graph$Scope) in.readObject();
|
||||||
|
assignParents(rootScope);
|
||||||
|
g.rootScope_$eq(rootScope);
|
||||||
|
|
||||||
|
var links =
|
||||||
|
(scala.collection.immutable.Set) in.readInline(scala.collection.immutable.Set.class);
|
||||||
|
g.links_$eq(links);
|
||||||
|
return g;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
protected void writeObject(Graph obj, Output out) throws IOException {
|
||||||
|
out.writeObject(obj.rootScope());
|
||||||
|
out.writeInline(scala.collection.immutable.Set.class, obj.links());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void assignParents(AliasAnalysis$Graph$Scope scope) {
|
||||||
|
var option = Option.apply(scope);
|
||||||
|
scope
|
||||||
|
.childScopes()
|
||||||
|
.foreach(
|
||||||
|
(ch) -> {
|
||||||
|
assignParents(ch);
|
||||||
|
ch.parent_$eq(option);
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -257,7 +257,7 @@ class Compiler(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
var requiredModules = modules.flatMap { module =>
|
val requiredModules = modules.flatMap { module =>
|
||||||
val importedModules = runImportsAndExportsResolution(module, generateCode)
|
val importedModules = runImportsAndExportsResolution(module, generateCode)
|
||||||
val isLoadedFromSource =
|
val isLoadedFromSource =
|
||||||
(m: Module) => !context.wasLoadedFromCache(m) && !context.isSynthetic(m)
|
(m: Module) => !context.wasLoadedFromCache(m) && !context.isSynthetic(m)
|
||||||
@ -286,47 +286,11 @@ class Compiler(
|
|||||||
}
|
}
|
||||||
}.distinct
|
}.distinct
|
||||||
|
|
||||||
var hasInvalidModuleRelink = false
|
|
||||||
if (irCachingEnabled) {
|
if (irCachingEnabled) {
|
||||||
requiredModules.foreach { module =>
|
requiredModules.foreach { module =>
|
||||||
ensureParsed(module)
|
ensureParsed(module)
|
||||||
if (!context.hasCrossModuleLinks(module)) {
|
|
||||||
val flags =
|
|
||||||
context
|
|
||||||
.getIr(module)
|
|
||||||
.preorder
|
|
||||||
.map(_.passData.restoreFromSerialization(this.context))
|
|
||||||
|
|
||||||
if (!flags.contains(false)) {
|
|
||||||
context.log(
|
|
||||||
Compiler.defaultLogLevel,
|
|
||||||
"Restored links (late phase) for module [{0}].",
|
|
||||||
context.getModuleName(module)
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
hasInvalidModuleRelink = true
|
|
||||||
context.log(
|
|
||||||
Compiler.defaultLogLevel,
|
|
||||||
"Failed to restore links (late phase) for module [{0}].",
|
|
||||||
context.getModuleName(module)
|
|
||||||
)
|
|
||||||
uncachedParseModule(module, isGenDocs = false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasInvalidModuleRelink) {
|
|
||||||
context.log(
|
|
||||||
Compiler.defaultLogLevel,
|
|
||||||
s"Some modules failed to relink. Re-running import and " +
|
|
||||||
s"export resolution."
|
|
||||||
)
|
|
||||||
|
|
||||||
requiredModules =
|
|
||||||
modules.flatMap(runImportsAndExportsResolution(_, generateCode))
|
|
||||||
}
|
|
||||||
|
|
||||||
requiredModules.foreach { module =>
|
requiredModules.foreach { module =>
|
||||||
if (
|
if (
|
||||||
!context
|
!context
|
||||||
@ -629,7 +593,6 @@ class Compiler(
|
|||||||
u.ir(discoveredModule)
|
u.ir(discoveredModule)
|
||||||
u.compilationStage(CompilationStage.AFTER_PARSING)
|
u.compilationStage(CompilationStage.AFTER_PARSING)
|
||||||
u.loadedFromCache(false)
|
u.loadedFromCache(false)
|
||||||
u.hasCrossModuleLinks(true)
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -892,7 +855,9 @@ class Compiler(
|
|||||||
): Unit = {
|
): Unit = {
|
||||||
if (config.isStrictErrors) {
|
if (config.isStrictErrors) {
|
||||||
val diagnostics = modules.flatMap { module =>
|
val diagnostics = modules.flatMap { module =>
|
||||||
val errors = gatherDiagnostics(module)
|
val errors =
|
||||||
|
if (context.wasLoadedFromCache(module)) List()
|
||||||
|
else gatherDiagnostics(module)
|
||||||
List((module, errors))
|
List((module, errors))
|
||||||
}
|
}
|
||||||
if (reportDiagnostics(diagnostics)) {
|
if (reportDiagnostics(diagnostics)) {
|
||||||
|
@ -84,7 +84,10 @@ class LocalScope(
|
|||||||
* @return the frame slot index for `id`.
|
* @return the frame slot index for `id`.
|
||||||
*/
|
*/
|
||||||
def getVarSlotIdx(id: Graph.Id): Int = {
|
def getVarSlotIdx(id: Graph.Id): Int = {
|
||||||
assert(localFrameSlotIdxs.contains(id))
|
assert(
|
||||||
|
localFrameSlotIdxs.contains(id),
|
||||||
|
"Cannot find " + id + " in " + localFrameSlotIdxs
|
||||||
|
)
|
||||||
localFrameSlotIdxs(id)
|
localFrameSlotIdxs(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ import org.enso.compiler.core.ir.{
|
|||||||
Function,
|
Function,
|
||||||
IdentifiedLocation,
|
IdentifiedLocation,
|
||||||
Literal,
|
Literal,
|
||||||
|
Location,
|
||||||
Name,
|
Name,
|
||||||
Type
|
Type
|
||||||
}
|
}
|
||||||
@ -28,7 +29,6 @@ import org.enso.compiler.pass.resolve.{
|
|||||||
import org.enso.pkg.QualifiedName
|
import org.enso.pkg.QualifiedName
|
||||||
import org.enso.polyglot.Suggestion
|
import org.enso.polyglot.Suggestion
|
||||||
import org.enso.polyglot.data.{Tree, TypeGraph}
|
import org.enso.polyglot.data.{Tree, TypeGraph}
|
||||||
import org.enso.syntax.text.Location
|
|
||||||
import org.enso.text.editing.IndexedSource
|
import org.enso.text.editing.IndexedSource
|
||||||
|
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
@ -8,6 +8,7 @@ import org.enso.compiler.core.ir.{
|
|||||||
Empty,
|
Empty,
|
||||||
Expression,
|
Expression,
|
||||||
Function,
|
Function,
|
||||||
|
IdentifiedLocation,
|
||||||
Literal,
|
Literal,
|
||||||
Module,
|
Module,
|
||||||
Name,
|
Name,
|
||||||
@ -185,9 +186,10 @@ case object DemandAnalysis extends IRPass {
|
|||||||
} else {
|
} else {
|
||||||
name match {
|
name match {
|
||||||
case lit: Name.Literal if isDefined(lit) =>
|
case lit: Name.Literal if isDefined(lit) =>
|
||||||
val forceLocation = name.location
|
val forceLocation = name.location
|
||||||
val newNameLocation = name.location.map(l => l.copy(id = None))
|
val newNameLocation =
|
||||||
val newName = lit.copy(location = newNameLocation)
|
name.location.map(l => new IdentifiedLocation(l.location()))
|
||||||
|
val newName = lit.copy(location = newNameLocation)
|
||||||
Application.Force(newName, forceLocation)
|
Application.Force(newName, forceLocation)
|
||||||
case _ => name
|
case _ => name
|
||||||
}
|
}
|
||||||
|
@ -111,7 +111,7 @@ case object FunctionBinding extends IRPass {
|
|||||||
val lambda = args
|
val lambda = args
|
||||||
.map(_.mapExpressions(desugarExpression))
|
.map(_.mapExpressions(desugarExpression))
|
||||||
.foldRight(desugarExpression(body))((arg, body) =>
|
.foldRight(desugarExpression(body))((arg, body) =>
|
||||||
Function.Lambda(List(arg), body, None)
|
new Function.Lambda(List(arg), body, None)
|
||||||
)
|
)
|
||||||
.asInstanceOf[Function.Lambda]
|
.asInstanceOf[Function.Lambda]
|
||||||
.copy(canBeTCO = canBeTCO, location = location)
|
.copy(canBeTCO = canBeTCO, location = location)
|
||||||
@ -155,10 +155,10 @@ case object FunctionBinding extends IRPass {
|
|||||||
val newBody = args
|
val newBody = args
|
||||||
.map(_.mapExpressions(desugarExpression))
|
.map(_.mapExpressions(desugarExpression))
|
||||||
.foldRight(desugarExpression(body))((arg, body) =>
|
.foldRight(desugarExpression(body))((arg, body) =>
|
||||||
Function.Lambda(List(arg), body, None)
|
new Function.Lambda(List(arg), body, None)
|
||||||
)
|
)
|
||||||
|
|
||||||
definition.Method.Explicit(
|
new definition.Method.Explicit(
|
||||||
methRef,
|
methRef,
|
||||||
newBody,
|
newBody,
|
||||||
loc,
|
loc,
|
||||||
@ -245,7 +245,7 @@ case object FunctionBinding extends IRPass {
|
|||||||
val newBody = (requiredArgs ::: remainingArgs)
|
val newBody = (requiredArgs ::: remainingArgs)
|
||||||
.map(_.mapExpressions(desugarExpression))
|
.map(_.mapExpressions(desugarExpression))
|
||||||
.foldRight(desugarExpression(body))((arg, body) =>
|
.foldRight(desugarExpression(body))((arg, body) =>
|
||||||
Function.Lambda(List(arg), body, None)
|
new Function.Lambda(List(arg), body, None)
|
||||||
)
|
)
|
||||||
Right(
|
Right(
|
||||||
definition.Method.Conversion(
|
definition.Method.Conversion(
|
||||||
|
@ -244,7 +244,7 @@ case object GenerateMethodBodies extends IRPass {
|
|||||||
expr: Expression,
|
expr: Expression,
|
||||||
funName: Name
|
funName: Name
|
||||||
): Expression = {
|
): Expression = {
|
||||||
Function.Lambda(
|
new Function.Lambda(
|
||||||
arguments =
|
arguments =
|
||||||
if (funName.name == MAIN_FUNCTION_NAME) Nil
|
if (funName.name == MAIN_FUNCTION_NAME) Nil
|
||||||
else genSyntheticSelf() :: Nil,
|
else genSyntheticSelf() :: Nil,
|
||||||
|
@ -7,6 +7,7 @@ import org.enso.compiler.core.ir.{
|
|||||||
DefinitionArgument,
|
DefinitionArgument,
|
||||||
Expression,
|
Expression,
|
||||||
Function,
|
Function,
|
||||||
|
IdentifiedLocation,
|
||||||
Module,
|
Module,
|
||||||
Name,
|
Name,
|
||||||
Type
|
Type
|
||||||
@ -142,7 +143,7 @@ case object LambdaShorthandToLambda extends IRPass {
|
|||||||
case blank: Name.Blank =>
|
case blank: Name.Blank =>
|
||||||
val newName = supply.newName()
|
val newName = supply.newName()
|
||||||
|
|
||||||
Function.Lambda(
|
new Function.Lambda(
|
||||||
List(
|
List(
|
||||||
DefinitionArgument.Specified(
|
DefinitionArgument.Specified(
|
||||||
name = Name.Literal(
|
name = Name.Literal(
|
||||||
@ -221,12 +222,12 @@ case object LambdaShorthandToLambda extends IRPass {
|
|||||||
// arg
|
// arg
|
||||||
val appResult =
|
val appResult =
|
||||||
actualDefArgs.foldRight(processedApp: Expression)((arg, body) =>
|
actualDefArgs.foldRight(processedApp: Expression)((arg, body) =>
|
||||||
Function.Lambda(List(arg), body, None)
|
new Function.Lambda(List(arg), body, None)
|
||||||
)
|
)
|
||||||
|
|
||||||
// If the function is shorthand, do the same
|
// If the function is shorthand, do the same
|
||||||
val resultExpr = if (functionIsShorthand) {
|
val resultExpr = if (functionIsShorthand) {
|
||||||
Function.Lambda(
|
new Function.Lambda(
|
||||||
List(
|
List(
|
||||||
DefinitionArgument.Specified(
|
DefinitionArgument.Specified(
|
||||||
Name
|
Name
|
||||||
@ -267,8 +268,9 @@ case object LambdaShorthandToLambda extends IRPass {
|
|||||||
name
|
name
|
||||||
case it => desugarExpression(it, freshNameSupply)
|
case it => desugarExpression(it, freshNameSupply)
|
||||||
}
|
}
|
||||||
val newVec = vector.copy(newItems)
|
val newVec = vector.copy(newItems)
|
||||||
val locWithoutId = newVec.location.map(_.copy(id = None))
|
val locWithoutId =
|
||||||
|
newVec.location.map(l => new IdentifiedLocation(l.location()))
|
||||||
bindings.foldLeft(newVec: Expression) { (body, bindingName) =>
|
bindings.foldLeft(newVec: Expression) { (body, bindingName) =>
|
||||||
val defArg = DefinitionArgument.Specified(
|
val defArg = DefinitionArgument.Specified(
|
||||||
bindingName,
|
bindingName,
|
||||||
@ -277,7 +279,7 @@ case object LambdaShorthandToLambda extends IRPass {
|
|||||||
suspended = false,
|
suspended = false,
|
||||||
location = None
|
location = None
|
||||||
)
|
)
|
||||||
Function.Lambda(List(defArg), body, locWithoutId)
|
new Function.Lambda(List(defArg), body, locWithoutId)
|
||||||
}
|
}
|
||||||
case tSet @ Application.Typeset(expr, _, _, _) =>
|
case tSet @ Application.Typeset(expr, _, _, _) =>
|
||||||
tSet.copy(expression = expr.map(desugarExpression(_, freshNameSupply)))
|
tSet.copy(expression = expr.map(desugarExpression(_, freshNameSupply)))
|
||||||
@ -422,7 +424,7 @@ case object LambdaShorthandToLambda extends IRPass {
|
|||||||
branches = newBranches
|
branches = newBranches
|
||||||
)
|
)
|
||||||
|
|
||||||
Function.Lambda(
|
new Function.Lambda(
|
||||||
List(lambdaArg),
|
List(lambdaArg),
|
||||||
newCaseExpr,
|
newCaseExpr,
|
||||||
caseExpr.location,
|
caseExpr.location,
|
||||||
|
@ -132,13 +132,13 @@ case object SectionsToBinOp extends IRPass {
|
|||||||
diagnostics
|
diagnostics
|
||||||
)
|
)
|
||||||
|
|
||||||
val rightLam = Function.Lambda(
|
val rightLam = new Function.Lambda(
|
||||||
List(rightDefArg),
|
List(rightDefArg),
|
||||||
opCall,
|
opCall,
|
||||||
None
|
None
|
||||||
)
|
)
|
||||||
|
|
||||||
Function.Lambda(
|
new Function.Lambda(
|
||||||
List(leftDefArg),
|
List(leftDefArg),
|
||||||
rightLam,
|
rightLam,
|
||||||
loc
|
loc
|
||||||
@ -187,13 +187,13 @@ case object SectionsToBinOp extends IRPass {
|
|||||||
diagnostics
|
diagnostics
|
||||||
)
|
)
|
||||||
|
|
||||||
val rightLambda = Function.Lambda(
|
val rightLambda = new Function.Lambda(
|
||||||
List(rightDefArg),
|
List(rightDefArg),
|
||||||
opCall,
|
opCall,
|
||||||
None
|
None
|
||||||
)
|
)
|
||||||
|
|
||||||
Function.Lambda(
|
new Function.Lambda(
|
||||||
List(leftDefArg),
|
List(leftDefArg),
|
||||||
rightLambda,
|
rightLambda,
|
||||||
loc
|
loc
|
||||||
@ -253,13 +253,13 @@ case object SectionsToBinOp extends IRPass {
|
|||||||
diagnostics
|
diagnostics
|
||||||
)
|
)
|
||||||
|
|
||||||
val leftLam = Function.Lambda(
|
val leftLam = new Function.Lambda(
|
||||||
List(leftDefArg),
|
List(leftDefArg),
|
||||||
opCall,
|
opCall,
|
||||||
None
|
None
|
||||||
)
|
)
|
||||||
|
|
||||||
Function.Lambda(
|
new Function.Lambda(
|
||||||
List(rightDefArg),
|
List(rightDefArg),
|
||||||
leftLam,
|
leftLam,
|
||||||
loc
|
loc
|
||||||
@ -274,7 +274,7 @@ case object SectionsToBinOp extends IRPass {
|
|||||||
diagnostics
|
diagnostics
|
||||||
)
|
)
|
||||||
|
|
||||||
Function.Lambda(
|
new Function.Lambda(
|
||||||
List(leftDefArg),
|
List(leftDefArg),
|
||||||
opCall,
|
opCall,
|
||||||
loc
|
loc
|
||||||
|
@ -9,6 +9,7 @@ import org.enso.compiler.core.ir.{
|
|||||||
Expression,
|
Expression,
|
||||||
Function,
|
Function,
|
||||||
IdentifiedLocation,
|
IdentifiedLocation,
|
||||||
|
Location,
|
||||||
Module,
|
Module,
|
||||||
Name
|
Name
|
||||||
}
|
}
|
||||||
@ -23,7 +24,6 @@ import org.enso.compiler.pass.analyse.{
|
|||||||
}
|
}
|
||||||
import org.enso.compiler.pass.desugar._
|
import org.enso.compiler.pass.desugar._
|
||||||
import org.enso.compiler.pass.resolve.IgnoredBindings
|
import org.enso.compiler.pass.resolve.IgnoredBindings
|
||||||
import org.enso.syntax.text.Location
|
|
||||||
|
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
@ -171,8 +171,8 @@ case object LambdaConsolidate extends IRPass {
|
|||||||
val newLocation = chainedLambdas.head.location match {
|
val newLocation = chainedLambdas.head.location match {
|
||||||
case Some(location) =>
|
case Some(location) =>
|
||||||
Some(
|
Some(
|
||||||
IdentifiedLocation(
|
IdentifiedLocation.create(
|
||||||
Location(
|
new Location(
|
||||||
location.start,
|
location.start,
|
||||||
chainedLambdas.last.location.getOrElse(location).location.end
|
chainedLambdas.last.location.getOrElse(location).location.end
|
||||||
),
|
),
|
||||||
|
@ -5,6 +5,7 @@ import org.enso.compiler.core.Implicits.AsDiagnostics
|
|||||||
import org.enso.compiler.core.ir.{
|
import org.enso.compiler.core.ir.{
|
||||||
Expression,
|
Expression,
|
||||||
IdentifiedLocation,
|
IdentifiedLocation,
|
||||||
|
Location,
|
||||||
Module,
|
Module,
|
||||||
Pattern
|
Pattern
|
||||||
}
|
}
|
||||||
@ -19,7 +20,6 @@ import org.enso.compiler.pass.analyse.{
|
|||||||
}
|
}
|
||||||
import org.enso.compiler.pass.desugar._
|
import org.enso.compiler.pass.desugar._
|
||||||
import org.enso.compiler.pass.resolve.{DocumentationComments, IgnoredBindings}
|
import org.enso.compiler.pass.resolve.{DocumentationComments, IgnoredBindings}
|
||||||
import org.enso.syntax.text.Location
|
|
||||||
|
|
||||||
import scala.annotation.unused
|
import scala.annotation.unused
|
||||||
|
|
||||||
@ -146,8 +146,8 @@ case object UnreachableMatchBranches extends IRPass {
|
|||||||
branch.location match {
|
branch.location match {
|
||||||
case Some(branchLoc) =>
|
case Some(branchLoc) =>
|
||||||
Some(
|
Some(
|
||||||
IdentifiedLocation(
|
IdentifiedLocation.create(
|
||||||
Location(loc.start, branchLoc.end),
|
new Location(loc.start, branchLoc.end),
|
||||||
loc.id
|
loc.id
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -101,7 +101,7 @@ case object MethodDefinitions extends IRPass {
|
|||||||
if canGenerateStaticWrappers(tp) =>
|
if canGenerateStaticWrappers(tp) =>
|
||||||
val dup = method.duplicate()
|
val dup = method.duplicate()
|
||||||
val static = dup.copy(body =
|
val static = dup.copy(body =
|
||||||
Function.Lambda(
|
new Function.Lambda(
|
||||||
List(
|
List(
|
||||||
DefinitionArgument
|
DefinitionArgument
|
||||||
.Specified(
|
.Specified(
|
||||||
|
@ -128,9 +128,6 @@ case object ModuleAnnotations extends IRPass {
|
|||||||
|
|
||||||
/** @inheritdoc */
|
/** @inheritdoc */
|
||||||
override def prepareForSerialization(compiler: Compiler): Annotations = {
|
override def prepareForSerialization(compiler: Compiler): Annotations = {
|
||||||
annotations.foreach(ir =>
|
|
||||||
ir.preorder.foreach(_.passData.prepareForSerialization(compiler))
|
|
||||||
)
|
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,14 +135,6 @@ case object ModuleAnnotations extends IRPass {
|
|||||||
override def restoreFromSerialization(
|
override def restoreFromSerialization(
|
||||||
compiler: Compiler
|
compiler: Compiler
|
||||||
): Option[IRPass.IRMetadata] = {
|
): Option[IRPass.IRMetadata] = {
|
||||||
annotations.foreach { ann =>
|
|
||||||
ann.preorder.foreach { ir =>
|
|
||||||
if (!ir.passData.restoreFromSerialization(compiler)) {
|
|
||||||
return None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(this)
|
Some(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -58,7 +58,7 @@ class ImportResolver(compiler: Compiler) {
|
|||||||
.getCompilationStage(current)
|
.getCompilationStage(current)
|
||||||
.isBefore(
|
.isBefore(
|
||||||
CompilationStage.AFTER_IMPORT_RESOLUTION
|
CompilationStage.AFTER_IMPORT_RESOLUTION
|
||||||
) || !context.hasCrossModuleLinks(current)
|
)
|
||||||
) {
|
) {
|
||||||
val importedModules: List[
|
val importedModules: List[
|
||||||
(Import, Option[BindingsMap.ResolvedImport])
|
(Import, Option[BindingsMap.ResolvedImport])
|
||||||
|
@ -12,11 +12,11 @@ import org.enso.compiler.core.{
|
|||||||
}
|
}
|
||||||
import org.enso.compiler.core.Implicits.AsMetadata
|
import org.enso.compiler.core.Implicits.AsMetadata
|
||||||
import org.enso.compiler.core.ir.Literal
|
import org.enso.compiler.core.ir.Literal
|
||||||
|
import org.enso.compiler.core.ir.Location
|
||||||
import org.enso.compiler.core.ir.Name
|
import org.enso.compiler.core.ir.Name
|
||||||
import org.enso.compiler.core.ir.module.scope.definition
|
import org.enso.compiler.core.ir.module.scope.definition
|
||||||
import org.enso.compiler.pass.analyse.DataflowAnalysis
|
import org.enso.compiler.pass.analyse.DataflowAnalysis
|
||||||
import org.enso.interpreter.instrument.execution.model.PendingEdit
|
import org.enso.interpreter.instrument.execution.model.PendingEdit
|
||||||
import org.enso.syntax.text.Location
|
|
||||||
import org.enso.text.editing.model.TextEdit
|
import org.enso.text.editing.model.TextEdit
|
||||||
import org.enso.text.editing.{IndexedSource, TextEditor}
|
import org.enso.text.editing.{IndexedSource, TextEditor}
|
||||||
|
|
||||||
@ -288,7 +288,7 @@ object ChangesetBuilder {
|
|||||||
* @return the node with a new location
|
* @return the node with a new location
|
||||||
*/
|
*/
|
||||||
def shift(offset: Int): Node = {
|
def shift(offset: Int): Node = {
|
||||||
val newLocation = location.copy(
|
val newLocation = new Location(
|
||||||
start = location.start + offset,
|
start = location.start + offset,
|
||||||
end = location.end + offset
|
end = location.end + offset
|
||||||
)
|
)
|
||||||
@ -359,7 +359,7 @@ object ChangesetBuilder {
|
|||||||
val nodeBetweenPreviousPositionAndNextNode =
|
val nodeBetweenPreviousPositionAndNextNode =
|
||||||
Node(
|
Node(
|
||||||
NodeId(currentIr),
|
NodeId(currentIr),
|
||||||
Location(previousPosition, nextNode.location.start),
|
new Location(previousPosition, nextNode.location.start),
|
||||||
false
|
false
|
||||||
)
|
)
|
||||||
acc += nodeBetweenPreviousPositionAndNextNode
|
acc += nodeBetweenPreviousPositionAndNextNode
|
||||||
@ -378,7 +378,7 @@ object ChangesetBuilder {
|
|||||||
if (hasRemainingTextAfterLastChild) {
|
if (hasRemainingTextAfterLastChild) {
|
||||||
val nodeAfterLastChild = Node(
|
val nodeAfterLastChild = Node(
|
||||||
NodeId(currentIr),
|
NodeId(currentIr),
|
||||||
Location(lastCoveredPosition, endOfNonLeafIr),
|
new Location(lastCoveredPosition, endOfNonLeafIr),
|
||||||
false
|
false
|
||||||
)
|
)
|
||||||
acc += nodeAfterLastChild
|
acc += nodeAfterLastChild
|
||||||
@ -491,7 +491,7 @@ object ChangesetBuilder {
|
|||||||
edit: TextEdit,
|
edit: TextEdit,
|
||||||
source: A
|
source: A
|
||||||
): Location = {
|
): Location = {
|
||||||
Location(
|
new Location(
|
||||||
IndexedSource[A].toIndex(edit.range.start, source),
|
IndexedSource[A].toIndex(edit.range.start, source),
|
||||||
IndexedSource[A].toIndex(edit.range.end, source)
|
IndexedSource[A].toIndex(edit.range.end, source)
|
||||||
)
|
)
|
||||||
|
@ -2,9 +2,9 @@ package org.enso.interpreter.instrument.execution
|
|||||||
|
|
||||||
import com.oracle.truffle.api.source.SourceSection
|
import com.oracle.truffle.api.source.SourceSection
|
||||||
import org.enso.compiler.core.{ExternalID, IR, Identifier}
|
import org.enso.compiler.core.{ExternalID, IR, Identifier}
|
||||||
|
import org.enso.compiler.core.ir.Location
|
||||||
import org.enso.compiler.core.ir.IdentifiedLocation
|
import org.enso.compiler.core.ir.IdentifiedLocation
|
||||||
import org.enso.interpreter.runtime.Module
|
import org.enso.interpreter.runtime.Module
|
||||||
import org.enso.syntax.text.Location
|
|
||||||
import org.enso.text.editing.{model, IndexedSource}
|
import org.enso.text.editing.{model, IndexedSource}
|
||||||
|
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
@ -121,7 +121,7 @@ object LocationResolver {
|
|||||||
source: A
|
source: A
|
||||||
): Location = {
|
): Location = {
|
||||||
val range = sectionToRange(section)
|
val range = sectionToRange(section)
|
||||||
Location(
|
new Location(
|
||||||
IndexedSource[A].toIndex(range.start, source),
|
IndexedSource[A].toIndex(range.start, source),
|
||||||
IndexedSource[A].toIndex(range.end, source)
|
IndexedSource[A].toIndex(range.end, source)
|
||||||
)
|
)
|
||||||
|
@ -12,6 +12,7 @@ import org.enso.compiler.core.ir.Empty;
|
|||||||
import org.enso.compiler.core.ir.Expression;
|
import org.enso.compiler.core.ir.Expression;
|
||||||
import org.enso.compiler.core.ir.Function;
|
import org.enso.compiler.core.ir.Function;
|
||||||
import org.enso.compiler.core.ir.Literal;
|
import org.enso.compiler.core.ir.Literal;
|
||||||
|
import org.enso.compiler.core.ir.Location;
|
||||||
import org.enso.compiler.core.ir.Name;
|
import org.enso.compiler.core.ir.Name;
|
||||||
import org.enso.compiler.core.ir.MetadataStorage;
|
import org.enso.compiler.core.ir.MetadataStorage;
|
||||||
import org.enso.compiler.core.ir.Module;
|
import org.enso.compiler.core.ir.Module;
|
||||||
@ -29,7 +30,6 @@ import org.enso.compiler.core.ir.module.scope.definition.Method;
|
|||||||
import org.enso.compiler.core.ir.module.scope.Export;
|
import org.enso.compiler.core.ir.module.scope.Export;
|
||||||
import org.enso.compiler.core.ir.module.scope.Import;
|
import org.enso.compiler.core.ir.module.scope.Import;
|
||||||
import org.enso.compiler.core.ir.module.scope.imports.Polyglot;
|
import org.enso.compiler.core.ir.module.scope.imports.Polyglot;
|
||||||
import org.enso.syntax.text.Location;
|
|
||||||
import org.enso.syntax2.ArgumentDefinition;
|
import org.enso.syntax2.ArgumentDefinition;
|
||||||
import org.enso.syntax2.Base;
|
import org.enso.syntax2.Base;
|
||||||
import org.enso.syntax2.DocComment;
|
import org.enso.syntax2.DocComment;
|
||||||
@ -41,6 +41,7 @@ import org.enso.syntax2.Tree;
|
|||||||
import org.enso.syntax2.Tree.Invalid;
|
import org.enso.syntax2.Tree.Invalid;
|
||||||
|
|
||||||
import org.enso.syntax2.Tree.Private;
|
import org.enso.syntax2.Tree.Private;
|
||||||
|
|
||||||
import scala.Option;
|
import scala.Option;
|
||||||
import scala.collection.immutable.LinearSeq;
|
import scala.collection.immutable.LinearSeq;
|
||||||
import scala.collection.immutable.List;
|
import scala.collection.immutable.List;
|
||||||
@ -121,7 +122,7 @@ final class TreeToIr {
|
|||||||
locations.get(1).start(),
|
locations.get(1).start(),
|
||||||
locations.get(locations.size() - 1).end()
|
locations.get(locations.size() - 1).end()
|
||||||
),
|
),
|
||||||
Option.empty()
|
null
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -842,7 +843,7 @@ final class TreeToIr {
|
|||||||
var locationWithANewLine = getIdentifiedLocation(body, 0, 0, null);
|
var locationWithANewLine = getIdentifiedLocation(body, 0, 0, null);
|
||||||
if (last != null && last.location().isDefined() && last.location().get().end() != locationWithANewLine.get().end()) {
|
if (last != null && last.location().isDefined() && last.location().get().end() != locationWithANewLine.get().end()) {
|
||||||
var patched = new Location(last.location().get().start(), locationWithANewLine.get().end() - 1);
|
var patched = new Location(last.location().get().start(), locationWithANewLine.get().end() - 1);
|
||||||
var id = new IdentifiedLocation(patched, last.location().get().id());
|
var id = IdentifiedLocation.create(patched, last.location().get().id());
|
||||||
last = last.setLocation(Option.apply(id));
|
last = last.setLocation(Option.apply(id));
|
||||||
}
|
}
|
||||||
yield new Expression.Block(list, last, locationWithANewLine, false, meta(), diag());
|
yield new Expression.Block(list, last, locationWithANewLine, false, meta(), diag());
|
||||||
@ -1693,7 +1694,7 @@ final class TreeToIr {
|
|||||||
Math.min(en.start(), in.start()),
|
Math.min(en.start(), in.start()),
|
||||||
Math.max(en.end(), in.end())
|
Math.max(en.end(), in.end())
|
||||||
);
|
);
|
||||||
return Option.apply(new IdentifiedLocation(loc, en.id()));
|
return Option.apply(IdentifiedLocation.create(loc, en.id()));
|
||||||
} else {
|
} else {
|
||||||
return encapsulating;
|
return encapsulating;
|
||||||
}
|
}
|
||||||
@ -1716,7 +1717,7 @@ final class TreeToIr {
|
|||||||
default -> {
|
default -> {
|
||||||
var begin = castToInt(ast.getStartCode()) + b;
|
var begin = castToInt(ast.getStartCode()) + b;
|
||||||
var end = castToInt(ast.getEndCode()) + e;
|
var end = castToInt(ast.getEndCode()) + e;
|
||||||
yield new IdentifiedLocation(new Location(begin, end), someId);
|
yield IdentifiedLocation.create(new Location(begin, end), someId);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -1751,7 +1752,7 @@ final class TreeToIr {
|
|||||||
end = ast.getPattern().getEndCode();
|
end = ast.getPattern().getEndCode();
|
||||||
}
|
}
|
||||||
int end_ = castToInt(end);
|
int end_ = castToInt(end);
|
||||||
return Option.apply(new IdentifiedLocation(new Location(begin_, end_), Option.empty()));
|
return Option.apply(IdentifiedLocation.create(new Location(begin_, end_), Option.empty()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Option<IdentifiedLocation> getIdentifiedLocation(Token ast) {
|
private Option<IdentifiedLocation> getIdentifiedLocation(Token ast) {
|
||||||
@ -1763,7 +1764,7 @@ final class TreeToIr {
|
|||||||
default -> {
|
default -> {
|
||||||
int begin = castToInt(ast.getStartCode());
|
int begin = castToInt(ast.getStartCode());
|
||||||
int end = castToInt(ast.getEndCode());
|
int end = castToInt(ast.getEndCode());
|
||||||
var id = Option.apply(generateId ? UUID.randomUUID() : null);
|
var id = generateId ? UUID.randomUUID() : null;
|
||||||
yield new IdentifiedLocation(new Location(begin, end), id);
|
yield new IdentifiedLocation(new Location(begin, end), id);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -0,0 +1,45 @@
|
|||||||
|
package org.enso.compiler.core.ir;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
import scala.Option;
|
||||||
|
|
||||||
|
public record IdentifiedLocation(Location location, UUID uuid) {
|
||||||
|
public IdentifiedLocation(Location location) {
|
||||||
|
this(location, (UUID)null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates new location from an optional UUID.
|
||||||
|
*/
|
||||||
|
public static IdentifiedLocation create(Location location, Option<UUID> uuid) {
|
||||||
|
return new IdentifiedLocation(location, uuid.isEmpty() ? null : uuid.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return the character index of the start of this source location.
|
||||||
|
*/
|
||||||
|
public int start() {
|
||||||
|
return location().start();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return the character index of the end of this source location.
|
||||||
|
*/
|
||||||
|
public int end() {
|
||||||
|
return location().end();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return the length in characters of this location.
|
||||||
|
*/
|
||||||
|
public int length() {
|
||||||
|
return location().length();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return option with/out UUID */
|
||||||
|
public Option<UUID> id() {
|
||||||
|
return Option.apply(uuid());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "IdentifiedLocation[location=" + this.location() + ", uuid="+ id() + "]";
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,69 @@
|
|||||||
|
package org.enso.compiler.core.ir;
|
||||||
|
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
import org.enso.persist.Persistance;
|
||||||
|
import scala.collection.Iterator;
|
||||||
|
import scala.collection.SeqFactory;
|
||||||
|
import scala.collection.immutable.AbstractSeq;
|
||||||
|
|
||||||
|
final class IrLazySeq extends AbstractSeq {
|
||||||
|
private final Persistance.Reference<?>[] arr;
|
||||||
|
private final int size;
|
||||||
|
|
||||||
|
IrLazySeq(Persistance.Reference<?>[] arr, int size) {
|
||||||
|
this.arr = arr;
|
||||||
|
this.size = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object apply(int i) throws IndexOutOfBoundsException {
|
||||||
|
return arr[i].get(Object.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int length() {
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isDefinedAt(int idx) {
|
||||||
|
return 0 <= idx && idx < size;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isDefinedAt(Object idx) {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object apply(Object i) throws IndexOutOfBoundsException {
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SeqFactory iterableFactory() {
|
||||||
|
return super.iterableFactory();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator iterator() {
|
||||||
|
return new IrIter();
|
||||||
|
}
|
||||||
|
|
||||||
|
private final class IrIter implements Iterator {
|
||||||
|
private int at;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
return at < size;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object next() throws NoSuchElementException {
|
||||||
|
if (at >= size) {
|
||||||
|
throw new NoSuchElementException();
|
||||||
|
}
|
||||||
|
return apply(at++);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,439 @@
|
|||||||
|
package org.enso.compiler.core.ir;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
import org.enso.compiler.core.ir.expression.Application;
|
||||||
|
import org.enso.compiler.core.ir.expression.Case;
|
||||||
|
import org.enso.compiler.core.ir.expression.Foreign;
|
||||||
|
import org.enso.compiler.core.ir.expression.warnings.Unused;
|
||||||
|
import org.enso.compiler.core.ir.module.scope.Definition;
|
||||||
|
import org.enso.compiler.core.ir.module.scope.Export;
|
||||||
|
import org.enso.compiler.core.ir.module.scope.Import;
|
||||||
|
import org.enso.compiler.core.ir.module.scope.definition.Method;
|
||||||
|
import org.enso.compiler.core.ir.module.scope.imports.Polyglot;
|
||||||
|
import org.enso.compiler.core.ir.type.Set;
|
||||||
|
import org.enso.persist.Persistable;
|
||||||
|
import org.enso.persist.Persistance;
|
||||||
|
import org.openide.util.lookup.ServiceProvider;
|
||||||
|
import scala.Option;
|
||||||
|
import scala.Tuple2;
|
||||||
|
import scala.collection.immutable.List;
|
||||||
|
import scala.collection.immutable.Seq;
|
||||||
|
|
||||||
|
@Persistable(clazz = Module.class, id = 201)
|
||||||
|
@Persistable(clazz = Name.Literal.class, id = 351)
|
||||||
|
@Persistable(clazz = Import.Module.class, id = 342)
|
||||||
|
@Persistable(clazz = Polyglot.class, id = 343)
|
||||||
|
@Persistable(clazz = Export.Module.class, id = 344)
|
||||||
|
@Persistable(clazz = Name.Qualified.class, id = 352)
|
||||||
|
@Persistable(clazz = Method.Explicit.class, id = 361)
|
||||||
|
@Persistable(clazz = Name.MethodReference.class, id = 362)
|
||||||
|
@Persistable(clazz = Function.Lambda.class, id = 363)
|
||||||
|
@Persistable(clazz = Polyglot.Java.class, id = 703)
|
||||||
|
@Persistable(clazz = DefinitionArgument.Specified.class, id = 704)
|
||||||
|
@Persistable(clazz = Name.Self.class, id = 705)
|
||||||
|
@Persistable(clazz = Literal.Number.class, id = 706)
|
||||||
|
@Persistable(clazz = Literal.Text.class, id = 707)
|
||||||
|
@Persistable(clazz = CallArgument.Specified.class, id = 708)
|
||||||
|
@Persistable(clazz = Definition.Type.class, id = 709)
|
||||||
|
@Persistable(clazz = Definition.Data.class, id = 710)
|
||||||
|
@Persistable(clazz = Name.Blank.class, id = 711)
|
||||||
|
@Persistable(clazz = Name.GenericAnnotation.class, id = 712)
|
||||||
|
@Persistable(clazz = Name.SelfType.class, id = 713)
|
||||||
|
@Persistable(clazz = Expression.Block.class, id = 751)
|
||||||
|
@Persistable(clazz = Expression.Binding.class, id = 752)
|
||||||
|
@Persistable(clazz = Application.Prefix.class, id = 753)
|
||||||
|
@Persistable(clazz = Application.Force.class, id = 754)
|
||||||
|
@Persistable(clazz = Application.Sequence.class, id = 755)
|
||||||
|
@Persistable(clazz = Case.Expr.class, id = 761)
|
||||||
|
@Persistable(clazz = Case.Branch.class, id = 762)
|
||||||
|
@Persistable(clazz = Pattern.Constructor.class, id = 763)
|
||||||
|
@Persistable(clazz = Pattern.Name.class, id = 764)
|
||||||
|
@Persistable(clazz = Pattern.Literal.class, id = 765)
|
||||||
|
@Persistable(clazz = Pattern.Type.class, id = 766)
|
||||||
|
@Persistable(clazz = Method.Conversion.class, id = 771)
|
||||||
|
@Persistable(clazz = Set.Union.class, id = 772)
|
||||||
|
@Persistable(clazz = Set.Intersection.class, id = 773)
|
||||||
|
@Persistable(clazz = Foreign.Definition.class, id = 781)
|
||||||
|
@Persistable(clazz = Type.Function.class, id = 782)
|
||||||
|
@Persistable(clazz = Name.BuiltinAnnotation.class, id = 783)
|
||||||
|
@Persistable(clazz = Type.Error.class, id = 784)
|
||||||
|
@Persistable(clazz = Unused.Binding.class, id = 785)
|
||||||
|
public final class IrPersistance {
|
||||||
|
private IrPersistance() {}
|
||||||
|
|
||||||
|
@ServiceProvider(service = Persistance.class)
|
||||||
|
public static final class PersistIdentifiedLocation extends Persistance<IdentifiedLocation> {
|
||||||
|
public PersistIdentifiedLocation() {
|
||||||
|
super(IdentifiedLocation.class, false, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void writeObject(IdentifiedLocation obj, Output out) throws IOException {
|
||||||
|
out.writeInline(Location.class, obj.location());
|
||||||
|
out.writeInline(Option.class, obj.id());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
protected IdentifiedLocation readObject(Input in) throws IOException, ClassNotFoundException {
|
||||||
|
var obj = in.readInline(Location.class);
|
||||||
|
var id = in.readInline(Option.class);
|
||||||
|
return IdentifiedLocation.create((Location) obj, id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ServiceProvider(service = Persistance.class)
|
||||||
|
public static final class PersistUUID extends Persistance<UUID> {
|
||||||
|
public PersistUUID() {
|
||||||
|
super(UUID.class, false, 73);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void writeObject(UUID obj, Output out) throws IOException {
|
||||||
|
out.writeLong(obj.getLeastSignificantBits());
|
||||||
|
out.writeLong(obj.getMostSignificantBits());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected UUID readObject(Input in) throws IOException, ClassNotFoundException {
|
||||||
|
var least = in.readLong();
|
||||||
|
var most = in.readLong();
|
||||||
|
return new UUID(most, least);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ServiceProvider(service = Persistance.class)
|
||||||
|
public static final class PersistScalaOption extends Persistance<Option> {
|
||||||
|
public PersistScalaOption() {
|
||||||
|
super(Option.class, true, 4431);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void writeObject(Option obj, Output out) throws IOException {
|
||||||
|
out.writeObject(obj.isEmpty() ? null : obj.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Option readObject(Input in) throws IOException, ClassNotFoundException {
|
||||||
|
var obj = in.readObject();
|
||||||
|
return Option.apply(obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ServiceProvider(service = Persistance.class)
|
||||||
|
public static final class PersistString extends Persistance<String> {
|
||||||
|
public PersistString() {
|
||||||
|
super(String.class, true, 4437);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void writeObject(String obj, Output out) throws IOException {
|
||||||
|
out.writeUTF(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String readObject(Input in) throws IOException, ClassNotFoundException {
|
||||||
|
var obj = in.readUTF();
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ServiceProvider(service = Persistance.class)
|
||||||
|
public static final class PersistLong extends Persistance<Long> {
|
||||||
|
public PersistLong() {
|
||||||
|
super(Long.class, true, 4438);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void writeObject(Long obj, Output out) throws IOException {
|
||||||
|
out.writeLong(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Long readObject(Input in) throws IOException, ClassNotFoundException {
|
||||||
|
var obj = in.readLong();
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ServiceProvider(service = Persistance.class)
|
||||||
|
public static final class PersistDouble extends Persistance<Double> {
|
||||||
|
public PersistDouble() {
|
||||||
|
super(Double.class, true, 4439);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void writeObject(Double obj, Output out) throws IOException {
|
||||||
|
out.writeDouble(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Double readObject(Input in) throws IOException, ClassNotFoundException {
|
||||||
|
var obj = in.readDouble();
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ServiceProvider(service = Persistance.class)
|
||||||
|
public static final class PersistScalaList extends Persistance<List> {
|
||||||
|
public PersistScalaList() {
|
||||||
|
super(List.class, true, 4432);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void writeObject(List list, Output out) throws IOException {
|
||||||
|
var size = list.size();
|
||||||
|
out.writeInt(size);
|
||||||
|
var l = list.reverse();
|
||||||
|
for (var i = 0; i < size; i++) {
|
||||||
|
out.writeObject(l.head());
|
||||||
|
l = (List) l.tail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
protected List readObject(Input in) throws IOException, ClassNotFoundException {
|
||||||
|
var size = in.readInt();
|
||||||
|
List list = scala.collection.immutable.Nil$.MODULE$;
|
||||||
|
for (var i = 0; i < size; i++) {
|
||||||
|
var elem = in.readObject();
|
||||||
|
list = scala.collection.immutable.$colon$colon$.MODULE$.apply(elem, list);
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ServiceProvider(service = Persistance.class)
|
||||||
|
public static final class PersistScalaMap extends Persistance<scala.collection.immutable.Map> {
|
||||||
|
public PersistScalaMap() {
|
||||||
|
super(scala.collection.immutable.Map.class, true, 4444);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
protected void writeObject(scala.collection.immutable.Map map, Output out) throws IOException {
|
||||||
|
var size = map.size();
|
||||||
|
out.writeInt(size);
|
||||||
|
var it = map.iterator();
|
||||||
|
while (size-- > 0) {
|
||||||
|
var tuple = (Tuple2) it.next();
|
||||||
|
out.writeObject(tuple._1());
|
||||||
|
out.writeObject(tuple._2());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
protected scala.collection.immutable.Map readObject(Input in)
|
||||||
|
throws IOException, ClassNotFoundException {
|
||||||
|
var size = in.readInt();
|
||||||
|
var map = scala.collection.immutable.Map$.MODULE$.empty();
|
||||||
|
for (var i = 0; i < size; i++) {
|
||||||
|
var key = in.readObject();
|
||||||
|
var value = in.readObject();
|
||||||
|
map = map.$plus(new Tuple2(key, value));
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ServiceProvider(service = Persistance.class)
|
||||||
|
public static final class PersistScalaMutableMap
|
||||||
|
extends Persistance<scala.collection.mutable.Map> {
|
||||||
|
public PersistScalaMutableMap() {
|
||||||
|
super(scala.collection.mutable.Map.class, true, 4949);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
protected void writeObject(scala.collection.mutable.Map map, Output out) throws IOException {
|
||||||
|
var size = map.size();
|
||||||
|
out.writeInt(size);
|
||||||
|
var it = map.iterator();
|
||||||
|
while (it.hasNext()) {
|
||||||
|
var tuple = (Tuple2) it.next();
|
||||||
|
out.writeObject(tuple._1());
|
||||||
|
out.writeObject(tuple._2());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
protected scala.collection.mutable.Map readObject(Input in)
|
||||||
|
throws IOException, ClassNotFoundException {
|
||||||
|
var size = in.readInt();
|
||||||
|
var map = scala.collection.mutable.Map$.MODULE$.empty();
|
||||||
|
for (var i = 0; i < size; i++) {
|
||||||
|
var key = in.readObject();
|
||||||
|
var value = in.readObject();
|
||||||
|
map.put(key, value);
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ServiceProvider(service = Persistance.class)
|
||||||
|
public static final class PersistScalaSet extends Persistance<scala.collection.immutable.Set> {
|
||||||
|
public PersistScalaSet() {
|
||||||
|
super(scala.collection.immutable.Set.class, true, 4445);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
protected void writeObject(scala.collection.immutable.Set set, Output out) throws IOException {
|
||||||
|
var size = set.size();
|
||||||
|
out.writeInt(size);
|
||||||
|
var it = set.iterator();
|
||||||
|
while (it.hasNext()) {
|
||||||
|
var obj = it.next();
|
||||||
|
out.writeObject(obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
protected scala.collection.immutable.Set readObject(Input in)
|
||||||
|
throws IOException, ClassNotFoundException {
|
||||||
|
var size = in.readInt();
|
||||||
|
var map = scala.collection.immutable.Set$.MODULE$.empty();
|
||||||
|
for (var i = 0; i < size; i++) {
|
||||||
|
var elem = in.readObject();
|
||||||
|
map = map.$plus(elem);
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ServiceProvider(service = Persistance.class)
|
||||||
|
public static final class PersistMap extends Persistance<HashMap> {
|
||||||
|
public PersistMap() {
|
||||||
|
super(HashMap.class, true, 4440);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void writeObject(HashMap m, Output out) throws IOException {
|
||||||
|
var size = m.size();
|
||||||
|
out.writeInt(size);
|
||||||
|
var it = m.entrySet().iterator();
|
||||||
|
while (it.hasNext()) {
|
||||||
|
var entry = (Map.Entry) it.next();
|
||||||
|
out.writeObject(entry.getKey());
|
||||||
|
out.writeObject(entry.getValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
protected HashMap readObject(Input in) throws IOException, ClassNotFoundException {
|
||||||
|
var size = in.readInt();
|
||||||
|
var map = new HashMap<Object, Object>();
|
||||||
|
for (var i = 0; i < size; i++) {
|
||||||
|
var key = in.readObject();
|
||||||
|
var value = in.readObject();
|
||||||
|
map.put(key, value);
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ServiceProvider(service = Persistance.class)
|
||||||
|
public static final class PersistScalaSeq extends Persistance<Seq> {
|
||||||
|
public PersistScalaSeq() {
|
||||||
|
super(Seq.class, true, 4433);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void writeObject(Seq list, Output out) throws IOException {
|
||||||
|
var size = list.size();
|
||||||
|
out.writeInt(size);
|
||||||
|
for (var i = 0; i < size; i++) {
|
||||||
|
out.writeObject(list.apply(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
protected Seq readObject(Input in) throws IOException, ClassNotFoundException {
|
||||||
|
var size = in.readInt();
|
||||||
|
Reference<?>[] arr = new Reference<?>[size];
|
||||||
|
for (var i = 0; i < size; i++) {
|
||||||
|
arr[i] = in.readReference(Object.class);
|
||||||
|
}
|
||||||
|
return new IrLazySeq(arr, size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ServiceProvider(service = Persistance.class)
|
||||||
|
public static final class PersistMetadataStorage extends Persistance<MetadataStorage> {
|
||||||
|
public PersistMetadataStorage() {
|
||||||
|
super(MetadataStorage.class, false, 301);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
protected void writeObject(MetadataStorage obj, Output out) throws IOException {
|
||||||
|
var map =
|
||||||
|
obj.map(
|
||||||
|
(processingPass, data) -> {
|
||||||
|
var t = new Tuple2<>(processingPass.getClass().getName(), data);
|
||||||
|
return t;
|
||||||
|
});
|
||||||
|
out.writeInline(scala.collection.immutable.Map.class, map);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
protected MetadataStorage readObject(Input in) throws IOException, ClassNotFoundException {
|
||||||
|
var storage = new MetadataStorage(nil());
|
||||||
|
var map = in.readInline(scala.collection.immutable.Map.class);
|
||||||
|
var it = map.iterator();
|
||||||
|
while (it.hasNext()) {
|
||||||
|
var obj = (Tuple2<String, ProcessingPass.Metadata>) it.next();
|
||||||
|
try {
|
||||||
|
var pass = (ProcessingPass) Class.forName(obj._1()).getField("MODULE$").get(null);
|
||||||
|
var data = obj._2();
|
||||||
|
storage.update(pass, data);
|
||||||
|
} catch (ReflectiveOperationException ex) {
|
||||||
|
throw new IOException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return storage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ServiceProvider(service = Persistance.class)
|
||||||
|
public static final class PersistDiagnosticStorage extends Persistance<DiagnosticStorage> {
|
||||||
|
public PersistDiagnosticStorage() {
|
||||||
|
super(DiagnosticStorage.class, false, 302);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void writeObject(DiagnosticStorage obj, Output out) throws IOException {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
protected DiagnosticStorage readObject(Input in) throws IOException, ClassNotFoundException {
|
||||||
|
return new DiagnosticStorage(
|
||||||
|
(scala.collection.immutable.List) scala.collection.immutable.Nil$.MODULE$);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private static <T> scala.collection.immutable.List<T> nil() {
|
||||||
|
return (scala.collection.immutable.List<T>) scala.collection.immutable.Nil$.MODULE$;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T> scala.collection.immutable.List<T> join(
|
||||||
|
T head, scala.collection.immutable.List<T> tail) {
|
||||||
|
return scala.collection.immutable.$colon$colon$.MODULE$.apply(head, tail);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private static <E extends Throwable> E raise(Class<E> clazz, Throwable t) throws E {
|
||||||
|
throw (E) t;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
package org.enso.compiler.core.ir;
|
||||||
|
|
||||||
|
import org.enso.persist.Persistable;
|
||||||
|
|
||||||
|
@Persistable(clazz=Location.class, id=1)
|
||||||
|
public record Location(int start, int end) {
|
||||||
|
public int length() {
|
||||||
|
return end - start;
|
||||||
|
}
|
||||||
|
}
|
@ -4,7 +4,7 @@ package org.enso.compiler.core.ir
|
|||||||
*
|
*
|
||||||
* @param initDiagnostics the initial diagnostics
|
* @param initDiagnostics the initial diagnostics
|
||||||
*/
|
*/
|
||||||
sealed class DiagnosticStorage(initDiagnostics: Seq[Diagnostic] = Seq())
|
final class DiagnosticStorage(initDiagnostics: Seq[Diagnostic] = Seq())
|
||||||
extends Serializable {
|
extends Serializable {
|
||||||
private var diagnostics: List[Diagnostic] = initDiagnostics.toList
|
private var diagnostics: List[Diagnostic] = initDiagnostics.toList
|
||||||
|
|
||||||
|
@ -45,14 +45,25 @@ object Function {
|
|||||||
*/
|
*/
|
||||||
sealed case class Lambda(
|
sealed case class Lambda(
|
||||||
override val arguments: List[DefinitionArgument],
|
override val arguments: List[DefinitionArgument],
|
||||||
override val body: Expression,
|
bodySeq: Seq[Expression],
|
||||||
location: Option[IdentifiedLocation],
|
location: Option[IdentifiedLocation],
|
||||||
override val canBeTCO: Boolean = true,
|
override val canBeTCO: Boolean,
|
||||||
passData: MetadataStorage = MetadataStorage(),
|
passData: MetadataStorage,
|
||||||
diagnostics: DiagnosticStorage = DiagnosticStorage()
|
diagnostics: DiagnosticStorage
|
||||||
) extends Function
|
) extends Function
|
||||||
with IRKind.Primitive {
|
with IRKind.Primitive {
|
||||||
|
def this(
|
||||||
|
arguments: List[DefinitionArgument],
|
||||||
|
body: Expression,
|
||||||
|
location: Option[IdentifiedLocation],
|
||||||
|
canBeTCO: Boolean = true,
|
||||||
|
passData: MetadataStorage = new MetadataStorage(),
|
||||||
|
diagnostics: DiagnosticStorage = new DiagnosticStorage()
|
||||||
|
) = {
|
||||||
|
this(arguments, Seq(body), location, canBeTCO, passData, diagnostics)
|
||||||
|
}
|
||||||
var id: UUID @Identifier = randomId
|
var id: UUID @Identifier = randomId
|
||||||
|
override lazy val body = bodySeq.head
|
||||||
|
|
||||||
/** Creates a copy of `this`.
|
/** Creates a copy of `this`.
|
||||||
*
|
*
|
||||||
@ -75,7 +86,7 @@ object Function {
|
|||||||
id: UUID @Identifier = id
|
id: UUID @Identifier = id
|
||||||
): Lambda = {
|
): Lambda = {
|
||||||
val res =
|
val res =
|
||||||
Lambda(arguments, body, location, canBeTCO, passData, diagnostics)
|
Lambda(arguments, Seq(body), location, canBeTCO, passData, diagnostics)
|
||||||
res.id = id
|
res.id = id
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
@ -150,6 +161,29 @@ object Function {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
object Lambda {
|
||||||
|
def unapply(l: Lambda): Some[
|
||||||
|
(
|
||||||
|
List[DefinitionArgument],
|
||||||
|
Expression,
|
||||||
|
Option[IdentifiedLocation],
|
||||||
|
Boolean,
|
||||||
|
MetadataStorage,
|
||||||
|
DiagnosticStorage
|
||||||
|
)
|
||||||
|
] =
|
||||||
|
Some(
|
||||||
|
(
|
||||||
|
l.arguments,
|
||||||
|
l.body,
|
||||||
|
l.location,
|
||||||
|
l.canBeTCO,
|
||||||
|
l.passData,
|
||||||
|
l.diagnostics
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/** A representation of the syntactic sugar for defining functions.
|
/** A representation of the syntactic sugar for defining functions.
|
||||||
*
|
*
|
||||||
* @param name the name of the function
|
* @param name the name of the function
|
||||||
|
@ -1,36 +0,0 @@
|
|||||||
package org.enso.compiler.core.ir
|
|
||||||
|
|
||||||
import org.enso.syntax.text.Location
|
|
||||||
|
|
||||||
import java.util.UUID
|
|
||||||
|
|
||||||
/** Couples a location with a possible source identifier.
|
|
||||||
*
|
|
||||||
* @param location the code location.
|
|
||||||
* @param id the identifier for the location.
|
|
||||||
*/
|
|
||||||
case class IdentifiedLocation(location: Location, id: Option[UUID]) {
|
|
||||||
|
|
||||||
/** @return the character index of the start of this source location.
|
|
||||||
*/
|
|
||||||
def start: Int = location.start
|
|
||||||
|
|
||||||
/** @return the character index of the end of this source location.
|
|
||||||
*/
|
|
||||||
def end: Int = location.end
|
|
||||||
|
|
||||||
/** @return the length in characters of this location.
|
|
||||||
*/
|
|
||||||
def length: Int = location.length
|
|
||||||
}
|
|
||||||
|
|
||||||
object IdentifiedLocation {
|
|
||||||
|
|
||||||
/** Utility constructor, building a location without an ID.
|
|
||||||
*
|
|
||||||
* @param location the code location.
|
|
||||||
* @return an [[IdentifiedLocation]] corresponding to the input location.
|
|
||||||
*/
|
|
||||||
def apply(location: Location): IdentifiedLocation =
|
|
||||||
IdentifiedLocation(location, None)
|
|
||||||
}
|
|
@ -11,7 +11,7 @@ import org.enso.compiler.core.CompilerStub
|
|||||||
* storage with
|
* storage with
|
||||||
*/
|
*/
|
||||||
//noinspection DuplicatedCode
|
//noinspection DuplicatedCode
|
||||||
class MetadataStorage(
|
final class MetadataStorage(
|
||||||
startingMeta: Seq[MetadataPair[_]] = Seq()
|
startingMeta: Seq[MetadataPair[_]] = Seq()
|
||||||
) extends Serializable {
|
) extends Serializable {
|
||||||
private var metadata: Map[ProcessingPass, Any] = Map(
|
private var metadata: Map[ProcessingPass, Any] = Map(
|
||||||
|
@ -23,7 +23,7 @@ import java.util.UUID
|
|||||||
@SerialVersionUID(
|
@SerialVersionUID(
|
||||||
8160L // Use BindingsMap
|
8160L // Use BindingsMap
|
||||||
) // prevents reading broken caches, see PR-3692 for details
|
) // prevents reading broken caches, see PR-3692 for details
|
||||||
sealed case class Module(
|
final case class Module(
|
||||||
imports: List[Import],
|
imports: List[Import],
|
||||||
exports: List[Export],
|
exports: List[Export],
|
||||||
bindings: List[Definition],
|
bindings: List[Definition],
|
||||||
|
@ -3,7 +3,6 @@ package org.enso.compiler.core.ir
|
|||||||
import org.enso.compiler.core.{ConstantsNames, IR, Identifier}
|
import org.enso.compiler.core.{ConstantsNames, IR, Identifier}
|
||||||
import org.enso.compiler.core.IR.randomId
|
import org.enso.compiler.core.IR.randomId
|
||||||
import org.enso.compiler.core.Implicits.{ShowPassData, ToStringHelper}
|
import org.enso.compiler.core.Implicits.{ShowPassData, ToStringHelper}
|
||||||
import org.enso.syntax.text.Location
|
|
||||||
|
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
@ -45,7 +44,7 @@ object Name {
|
|||||||
* @param passData the pass metadata associated with this node
|
* @param passData the pass metadata associated with this node
|
||||||
* @param diagnostics compiler diagnostics for this node
|
* @param diagnostics compiler diagnostics for this node
|
||||||
*/
|
*/
|
||||||
sealed case class MethodReference(
|
final case class MethodReference(
|
||||||
typePointer: Option[Name],
|
typePointer: Option[Name],
|
||||||
methodName: Name,
|
methodName: Name,
|
||||||
location: Option[IdentifiedLocation],
|
location: Option[IdentifiedLocation],
|
||||||
@ -183,8 +182,8 @@ object Name {
|
|||||||
(identLoc, segment) => {
|
(identLoc, segment) => {
|
||||||
identLoc.flatMap(loc => {
|
identLoc.flatMap(loc => {
|
||||||
Some(
|
Some(
|
||||||
IdentifiedLocation(
|
new IdentifiedLocation(
|
||||||
Location(
|
new Location(
|
||||||
loc.location.start,
|
loc.location.start,
|
||||||
segment.location
|
segment.location
|
||||||
.flatMap(l => Some(l.location.end))
|
.flatMap(l => Some(l.location.end))
|
||||||
@ -206,7 +205,7 @@ object Name {
|
|||||||
* @param diagnostics compiler diagnostics for this node
|
* @param diagnostics compiler diagnostics for this node
|
||||||
* @return a copy of `this`, updated with the specified values
|
* @return a copy of `this`, updated with the specified values
|
||||||
*/
|
*/
|
||||||
sealed case class Qualified(
|
final case class Qualified(
|
||||||
parts: List[Name],
|
parts: List[Name],
|
||||||
location: Option[IdentifiedLocation],
|
location: Option[IdentifiedLocation],
|
||||||
passData: MetadataStorage = MetadataStorage(),
|
passData: MetadataStorage = MetadataStorage(),
|
||||||
|
@ -49,13 +49,34 @@ object Method {
|
|||||||
*/
|
*/
|
||||||
sealed case class Explicit(
|
sealed case class Explicit(
|
||||||
override val methodReference: Name.MethodReference,
|
override val methodReference: Name.MethodReference,
|
||||||
override val body: Expression,
|
val bodySeq: Seq[Expression],
|
||||||
|
val isStatic: Boolean,
|
||||||
|
val isStaticWrapperForInstanceMethod: Boolean,
|
||||||
override val location: Option[IdentifiedLocation],
|
override val location: Option[IdentifiedLocation],
|
||||||
override val passData: MetadataStorage = MetadataStorage(),
|
override val passData: MetadataStorage,
|
||||||
override val diagnostics: DiagnosticStorage = DiagnosticStorage()
|
override val diagnostics: DiagnosticStorage
|
||||||
) extends Method
|
) extends Method
|
||||||
with IRKind.Primitive {
|
with IRKind.Primitive {
|
||||||
|
def this(
|
||||||
|
methodReference: Name.MethodReference,
|
||||||
|
body: Expression,
|
||||||
|
location: Option[IdentifiedLocation],
|
||||||
|
passData: MetadataStorage = MetadataStorage(),
|
||||||
|
diagnostics: DiagnosticStorage = DiagnosticStorage()
|
||||||
|
) = {
|
||||||
|
this(
|
||||||
|
methodReference,
|
||||||
|
Seq(body),
|
||||||
|
Explicit.computeIsStatic(body),
|
||||||
|
Explicit.computeIsStaticWrapperForInstanceMethod(body),
|
||||||
|
location,
|
||||||
|
passData,
|
||||||
|
diagnostics
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
var id: UUID @Identifier = randomId
|
var id: UUID @Identifier = randomId
|
||||||
|
lazy val body = bodySeq.head
|
||||||
|
|
||||||
/** Creates a copy of `this`.
|
/** Creates a copy of `this`.
|
||||||
*
|
*
|
||||||
@ -70,14 +91,19 @@ object Method {
|
|||||||
def copy(
|
def copy(
|
||||||
methodReference: Name.MethodReference = methodReference,
|
methodReference: Name.MethodReference = methodReference,
|
||||||
body: Expression = body,
|
body: Expression = body,
|
||||||
location: Option[IdentifiedLocation] = location,
|
isStatic: Boolean = Explicit.computeIsStatic(body),
|
||||||
passData: MetadataStorage = passData,
|
isStaticWrapperForInstanceMethod: Boolean =
|
||||||
diagnostics: DiagnosticStorage = diagnostics,
|
Explicit.computeIsStaticWrapperForInstanceMethod(body),
|
||||||
id: UUID @Identifier = id
|
location: Option[IdentifiedLocation] = location,
|
||||||
|
passData: MetadataStorage = passData,
|
||||||
|
diagnostics: DiagnosticStorage = diagnostics,
|
||||||
|
id: UUID @Identifier = id
|
||||||
): Explicit = {
|
): Explicit = {
|
||||||
val res = Explicit(
|
val res = Explicit(
|
||||||
methodReference,
|
methodReference,
|
||||||
body,
|
List(body),
|
||||||
|
isStatic,
|
||||||
|
isStaticWrapperForInstanceMethod,
|
||||||
location,
|
location,
|
||||||
passData,
|
passData,
|
||||||
diagnostics
|
diagnostics
|
||||||
@ -156,8 +182,21 @@ object Method {
|
|||||||
|
|
||||||
s"${methodReference.showCode(indent)} = $exprStr"
|
s"${methodReference.showCode(indent)} = $exprStr"
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
def isStatic: Boolean = body match {
|
object Explicit {
|
||||||
|
def unapply(m: Explicit): Option[
|
||||||
|
(
|
||||||
|
Name.MethodReference,
|
||||||
|
Expression,
|
||||||
|
Option[IdentifiedLocation],
|
||||||
|
MetadataStorage,
|
||||||
|
DiagnosticStorage
|
||||||
|
)
|
||||||
|
] = {
|
||||||
|
Some((m.methodReference, m.body, m.location, m.passData, m.diagnostics))
|
||||||
|
}
|
||||||
|
private def computeIsStatic(body: IR): Boolean = body match {
|
||||||
case function: Function.Lambda =>
|
case function: Function.Lambda =>
|
||||||
function.arguments.headOption.map(_.name) match {
|
function.arguments.headOption.map(_.name) match {
|
||||||
case Some(Name.Self(_, true, _, _)) => true
|
case Some(Name.Self(_, true, _, _)) => true
|
||||||
@ -167,21 +206,21 @@ object Method {
|
|||||||
true // if it's not a function, it has no arguments, therefore no `self`
|
true // if it's not a function, it has no arguments, therefore no `self`
|
||||||
}
|
}
|
||||||
|
|
||||||
def isStaticWrapperForInstanceMethod: Boolean = body match {
|
private def computeIsStaticWrapperForInstanceMethod(body: IR): Boolean =
|
||||||
case function: Function.Lambda =>
|
body match {
|
||||||
function.arguments.map(_.name) match {
|
case function: Function.Lambda =>
|
||||||
case Name.Self(_, true, _, _) :: Name.Self(
|
function.arguments.map(_.name) match {
|
||||||
_,
|
case Name.Self(_, true, _, _) :: Name.Self(
|
||||||
false,
|
_,
|
||||||
_,
|
false,
|
||||||
_
|
_,
|
||||||
) :: _ =>
|
_
|
||||||
true
|
) :: _ =>
|
||||||
case _ => false
|
true
|
||||||
}
|
case _ => false
|
||||||
case _ => false
|
}
|
||||||
}
|
case _ => false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** The definition of a method for a given constructor using sugared
|
/** The definition of a method for a given constructor using sugared
|
||||||
|
@ -21,7 +21,11 @@ public class EnsoParserTest {
|
|||||||
|
|
||||||
@BeforeClass
|
@BeforeClass
|
||||||
public static void initEnsoParser() {
|
public static void initEnsoParser() {
|
||||||
ensoCompiler = new EnsoParser();
|
try {
|
||||||
|
ensoCompiler = new EnsoParser();
|
||||||
|
} catch (LinkageError e) {
|
||||||
|
throw new AssertionError(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterClass
|
@AfterClass
|
||||||
@ -1252,7 +1256,7 @@ public class EnsoParserTest {
|
|||||||
""",
|
""",
|
||||||
"""
|
"""
|
||||||
# Comment with empty line
|
# Comment with empty line
|
||||||
|
|
||||||
private
|
private
|
||||||
"""
|
"""
|
||||||
);
|
);
|
||||||
@ -1345,29 +1349,13 @@ public class EnsoParserTest {
|
|||||||
* @return string representation of the IR
|
* @return string representation of the IR
|
||||||
*/
|
*/
|
||||||
private static String simplifyIR(IR ir, boolean noIds, boolean noLocations, boolean lessDocs) {
|
private static String simplifyIR(IR ir, boolean noIds, boolean noLocations, boolean lessDocs) {
|
||||||
|
if (noLocations) {
|
||||||
|
ir = ir.duplicate(false, true, true, true);
|
||||||
|
}
|
||||||
String txt = ir.pretty();
|
String txt = ir.pretty();
|
||||||
if (noIds) {
|
if (noIds) {
|
||||||
txt = txt.replaceAll("[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]\\-[0-9a-f][0-9a-f][0-9a-f][0-9a-f]\\-[0-9a-f][0-9a-f][0-9a-f][0-9a-f]\\-[0-9a-f][0-9a-f][0-9a-f][0-9a-f]\\-[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]", "_");
|
txt = txt.replaceAll("[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]\\-[0-9a-f][0-9a-f][0-9a-f][0-9a-f]\\-[0-9a-f][0-9a-f][0-9a-f][0-9a-f]\\-[0-9a-f][0-9a-f][0-9a-f][0-9a-f]\\-[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]", "_");
|
||||||
}
|
}
|
||||||
if (noLocations) {
|
|
||||||
for (;;) {
|
|
||||||
final String pref = " Location(";
|
|
||||||
int at = txt.indexOf(pref);
|
|
||||||
if (at == -1) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
int to = at + pref.length();
|
|
||||||
int depth = 1;
|
|
||||||
while (depth > 0) {
|
|
||||||
switch (txt.charAt(to)) {
|
|
||||||
case '(' -> depth++;
|
|
||||||
case ')' -> depth--;
|
|
||||||
}
|
|
||||||
to++;
|
|
||||||
}
|
|
||||||
txt = txt.substring(0, at) + "Location[_]" + txt.substring(to);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (lessDocs) {
|
if (lessDocs) {
|
||||||
for (;;) {
|
for (;;) {
|
||||||
final String pref = "Comment.Documentation(";
|
final String pref = "Comment.Documentation(";
|
||||||
|
@ -0,0 +1,377 @@
|
|||||||
|
package org.enso.compiler.core;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import org.enso.compiler.core.ir.DiagnosticStorage;
|
||||||
|
import org.enso.compiler.core.ir.IdentifiedLocation;
|
||||||
|
import org.enso.compiler.core.ir.Location;
|
||||||
|
import org.enso.compiler.core.ir.MetadataStorage;
|
||||||
|
import org.enso.compiler.core.ir.Module;
|
||||||
|
import org.enso.persist.Persistable;
|
||||||
|
import org.enso.persist.Persistance;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.openide.util.lookup.ServiceProvider;
|
||||||
|
|
||||||
|
import scala.Option;
|
||||||
|
import scala.Tuple2;
|
||||||
|
import scala.collection.immutable.List;
|
||||||
|
import scala.collection.immutable.Seq;
|
||||||
|
|
||||||
|
public class IrPersistanceTest {
|
||||||
|
@Test
|
||||||
|
public void locationTest() throws Exception {
|
||||||
|
var l = new Location(12, 33);
|
||||||
|
var n = serde(Location.class, l, 8);
|
||||||
|
|
||||||
|
assertEquals(12, n.start());
|
||||||
|
assertEquals(33, n.end());
|
||||||
|
assertEquals(l.length(), n.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void identifiedLocation() throws Exception {
|
||||||
|
var il = new IdentifiedLocation(new Location(5, 19), null);
|
||||||
|
var in = serde(IdentifiedLocation.class, il, 12);
|
||||||
|
assertEquals(il, in);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void identifiedLocationNoUUID() throws Exception {
|
||||||
|
var il = new IdentifiedLocation(new Location(5, 19), UUID.randomUUID());
|
||||||
|
var in = serde(IdentifiedLocation.class, il, 32);
|
||||||
|
assertEquals("UUIDs are serialized at the moment", il, in);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public void scalaMap() throws Exception {
|
||||||
|
var idLoc1 = new IdentifiedLocation(new Location(1, 5));
|
||||||
|
var in = scala.collection.immutable.Map$.MODULE$.empty().$plus(new Tuple2("Hi", idLoc1));
|
||||||
|
|
||||||
|
var out = serde(scala.collection.immutable.Map.class, in, 36);
|
||||||
|
|
||||||
|
assertEquals("One element", 1, out.size());
|
||||||
|
assertEquals(in, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public void scalaHashMap() throws Exception {
|
||||||
|
var idLoc1 = new IdentifiedLocation(new Location(1, 5));
|
||||||
|
var immutable = join(new Tuple2("Hi", idLoc1), nil());
|
||||||
|
var in =
|
||||||
|
(scala.collection.mutable.HashMap)
|
||||||
|
scala.collection.mutable.HashMap$.MODULE$.apply(immutable);
|
||||||
|
|
||||||
|
var out = serde(scala.collection.mutable.Map.class, in, 36);
|
||||||
|
|
||||||
|
assertEquals("One element", 1, out.size());
|
||||||
|
assertEquals(in, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public void scalaSet() throws Exception {
|
||||||
|
var idLoc1 = new IdentifiedLocation(new Location(1, 5));
|
||||||
|
var in = scala.collection.immutable.Set$.MODULE$.empty().$plus(idLoc1);
|
||||||
|
|
||||||
|
var out = serde(scala.collection.immutable.Set.class, in, 24);
|
||||||
|
|
||||||
|
assertEquals("One element", 1, out.size());
|
||||||
|
assertEquals(in, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void scalaList() throws Exception {
|
||||||
|
var idLoc1 = new IdentifiedLocation(new Location(1, 5));
|
||||||
|
var idLoc2 = new IdentifiedLocation(new Location(2, 4), UUID.randomUUID());
|
||||||
|
var in = join(idLoc2, join(idLoc1, nil()));
|
||||||
|
|
||||||
|
var out = serde(List.class, in, 64);
|
||||||
|
|
||||||
|
assertEquals("Two elements", 2, out.size());
|
||||||
|
assertEquals("UUIDs are serialized at the moment", idLoc2, out.head());
|
||||||
|
assertEquals("Tail is the same", idLoc1, out.last());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void scalaListSharedRef() throws Exception {
|
||||||
|
var idLoc1 = new IdentifiedLocation(new Location(1, 5));
|
||||||
|
var in = join(idLoc1, join(idLoc1, nil()));
|
||||||
|
|
||||||
|
var out = serde(List.class, in, 32);
|
||||||
|
|
||||||
|
assertEquals("Two elements", 2, out.size());
|
||||||
|
assertEquals("Head is equal to original", idLoc1, out.head());
|
||||||
|
assertEquals("Tail is equal to original", idLoc1, out.last());
|
||||||
|
assertSame("Head and tail are the same", out.head(), out.last());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public void scalaListSharedRefAtDepth() throws Exception {
|
||||||
|
var idLoc1 = Singleton.INSTANCE;
|
||||||
|
var in = join(Option.apply(idLoc1), join(Option.apply(idLoc1), nil()));
|
||||||
|
|
||||||
|
var out = serde(List.class, in, -1);
|
||||||
|
|
||||||
|
assertEquals("Two elements", 2, out.size());
|
||||||
|
var readHead = (Option<Singleton>) out.head();
|
||||||
|
var readTail = (Option<Singleton>) out.last();
|
||||||
|
|
||||||
|
assertEquals("Head is equal to original", idLoc1, readHead.get());
|
||||||
|
assertEquals("Tail is equal to original", idLoc1, readTail.get());
|
||||||
|
|
||||||
|
assertNotSame("Head and tail are different", readHead, readTail);
|
||||||
|
assertSame("Head and tail are the same", readHead.get(), readTail.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void lazyScalaSequence() throws Exception {
|
||||||
|
var s1 = new LazySeq("Hello");
|
||||||
|
var s2 = new LazySeq("World");
|
||||||
|
|
||||||
|
var second = new boolean[1];
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
var in =
|
||||||
|
(Seq<String>)
|
||||||
|
Seq.fill(
|
||||||
|
2,
|
||||||
|
() -> {
|
||||||
|
if (second[0]) {
|
||||||
|
return s2;
|
||||||
|
}
|
||||||
|
second[0] = true;
|
||||||
|
return s1;
|
||||||
|
});
|
||||||
|
assertEquals("Seq with two elements created", 2, in.length());
|
||||||
|
|
||||||
|
LazySeq.forbidden = true;
|
||||||
|
var out = serde(Seq.class, in, -1);
|
||||||
|
|
||||||
|
assertEquals("Two elements", 2, out.size());
|
||||||
|
|
||||||
|
LazySeq.forbidden = false;
|
||||||
|
|
||||||
|
assertEquals("Lazily deserialized s2", s2, out.head());
|
||||||
|
assertNotSame("Lazily deserialized s2", s2, out.head());
|
||||||
|
assertEquals("Lazily deserialized s1", s1, out.last());
|
||||||
|
assertNotSame("Lazily deserialized s1", s1, out.head());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void serializeModule() throws Exception {
|
||||||
|
var meta = new MetadataStorage(nil());
|
||||||
|
var diag = new DiagnosticStorage(nil());
|
||||||
|
var m = new Module(nil(), nil(), nil(), true, Option.empty(), meta, diag);
|
||||||
|
|
||||||
|
var out = serde(Module.class, m, -1);
|
||||||
|
|
||||||
|
assertEquals("Same", m, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void hashMap() throws Exception {
|
||||||
|
var map = new HashMap<String, String>();
|
||||||
|
map.put("one", "uno");
|
||||||
|
map.put("two", "duo");
|
||||||
|
map.put("ten", "tre");
|
||||||
|
|
||||||
|
var out = serde(HashMap.class, map, -1);
|
||||||
|
|
||||||
|
assertEquals("Same", map, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void readResolve() throws Exception {
|
||||||
|
var in = new Service(5);
|
||||||
|
var arr = Persistance.write(in, (Function<Object, Object>) null);
|
||||||
|
|
||||||
|
var plain = Persistance.read(arr, (Function<Object, Object>) null);
|
||||||
|
assertEquals("Remains five", 5, plain.get(Service.class).value());
|
||||||
|
|
||||||
|
var multiOnRead = Persistance.read(arr, (obj) -> obj instanceof Service s ? new Service(s.value() * 3) : obj);
|
||||||
|
assertEquals("Multiplied on read", 15, multiOnRead.get(Service.class).value());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void writeReplace() throws Exception {
|
||||||
|
var in = new Service(5);
|
||||||
|
var arr = Persistance.write(in, (obj) -> obj instanceof Service s ? new Service(s.value() * 3) : obj);
|
||||||
|
|
||||||
|
var plain = Persistance.read(arr, (Function<Object, Object>) null);
|
||||||
|
assertEquals("Multiplied on write", 15, plain.get(Service.class).value());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void readResolveInline() throws Exception {
|
||||||
|
var in = new ServiceSupply(new Service(5));
|
||||||
|
var arr = Persistance.write(in, (Function<Object, Object>) null);
|
||||||
|
|
||||||
|
var plain = Persistance.read(arr, (Function<Object, Object>) null);
|
||||||
|
assertEquals("Remains five", 5, plain.get(ServiceSupply.class).supply().value());
|
||||||
|
|
||||||
|
var multiOnRead = Persistance.read(arr, (obj) -> obj instanceof Service s ? new Service(s.value() * 3) : obj);
|
||||||
|
assertEquals("Multiplied on read", 15, multiOnRead.get(ServiceSupply.class).supply().value());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void writeReplaceInline() throws Exception {
|
||||||
|
var in = new ServiceSupply(new Service(5));
|
||||||
|
var arr = Persistance.write(in, (obj) -> obj instanceof Service s ? new Service(s.value() * 3) : obj);
|
||||||
|
|
||||||
|
var plain = Persistance.read(arr, (Function<Object, Object>) null);
|
||||||
|
assertEquals("Multiplied on write", 15, plain.get(ServiceSupply.class).supply().value());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void readResolveReference() throws Exception {
|
||||||
|
var in = new IntegerSupply(new Service(5));
|
||||||
|
var arr = Persistance.write(in, (Function<Object, Object>) null);
|
||||||
|
|
||||||
|
var plain = Persistance.read(arr, (Function<Object, Object>) null);
|
||||||
|
assertEquals("Remains five", 5, (int) plain.get(IntegerSupply.class).supply().get());
|
||||||
|
assertEquals("Remains five 2", 5, (int) plain.get(IntegerSupply.class).supply().get());
|
||||||
|
|
||||||
|
var multiOnRead = Persistance.read(arr, (obj) -> obj instanceof Service s ? new Service(s.value() * 3) : obj);
|
||||||
|
assertEquals("Multiplied on read", 15, (int) multiOnRead.get(IntegerSupply.class).supply().get());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void writeReplaceReference() throws Exception {
|
||||||
|
var in = new IntegerSupply(new Service(5));
|
||||||
|
var arr = Persistance.write(in, (obj) -> obj instanceof Service s ? new Service(s.value() * 3) : obj);
|
||||||
|
|
||||||
|
var plain = Persistance.read(arr, (Function<Object, Object>) null);
|
||||||
|
assertEquals("Multiplied on write", 15, (int) plain.get(IntegerSupply.class).supply().get());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T> T serde(Class<T> clazz, T l, int expectedSize) throws IOException {
|
||||||
|
var arr = Persistance.write(l, (Function<Object, Object>) null);
|
||||||
|
if (expectedSize >= 0) {
|
||||||
|
assertEquals(expectedSize, arr.length - 12);
|
||||||
|
}
|
||||||
|
var ref = Persistance.read(arr, (Function<Object, Object>) null);
|
||||||
|
return ref.get(clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private static final <T> scala.collection.immutable.List<T> nil() {
|
||||||
|
return (scala.collection.immutable.List<T>) scala.collection.immutable.Nil$.MODULE$;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final <T> scala.collection.immutable.List<T> join(
|
||||||
|
T head, scala.collection.immutable.List<T> tail) {
|
||||||
|
return scala.collection.immutable.$colon$colon$.MODULE$.apply(head, tail);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class LazySeq implements CharSequence {
|
||||||
|
|
||||||
|
private static boolean forbidden;
|
||||||
|
|
||||||
|
private final String value;
|
||||||
|
|
||||||
|
public LazySeq(String value) {
|
||||||
|
if (forbidden) {
|
||||||
|
throw new IllegalStateException("Cannot create LazySeq right now!");
|
||||||
|
}
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public char charAt(int index) {
|
||||||
|
return value.charAt(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int length() {
|
||||||
|
return value.length();
|
||||||
|
}
|
||||||
|
|
||||||
|
public CharSequence subSequence(int beginIndex, int endIndex) {
|
||||||
|
return value.subSequence(beginIndex, endIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int hash = 5;
|
||||||
|
hash = 53 * hash + Objects.hashCode(this.value);
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (getClass() != obj.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
final LazySeq other = (LazySeq) obj;
|
||||||
|
return Objects.equals(this.value, other.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ServiceProvider(service = Persistance.class)
|
||||||
|
public static final class PersistLazySeq extends Persistance<LazySeq> {
|
||||||
|
|
||||||
|
public PersistLazySeq() {
|
||||||
|
super(LazySeq.class, false, 432432);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void writeObject(LazySeq obj, Output out) throws IOException {
|
||||||
|
out.writeUTF(obj.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected LazySeq readObject(Input in) throws IOException, ClassNotFoundException {
|
||||||
|
var s = in.readUTF();
|
||||||
|
return new LazySeq(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class Singleton {
|
||||||
|
public static final Singleton INSTANCE = new Singleton();
|
||||||
|
|
||||||
|
private Singleton() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ServiceProvider(service = Persistance.class)
|
||||||
|
public static final class PersistSingleton extends Persistance<Singleton> {
|
||||||
|
|
||||||
|
public PersistSingleton() {
|
||||||
|
super(Singleton.class, false, 432433);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void writeObject(Singleton obj, Output out) throws IOException {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Singleton readObject(Input in) throws IOException, ClassNotFoundException {
|
||||||
|
return Singleton.INSTANCE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Persistable(clazz=Service.class, id=432434)
|
||||||
|
public record Service(int value) implements Supplier<Integer> {
|
||||||
|
@Override
|
||||||
|
public Integer get() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Persistable(clazz=IntegerSupply.class, id=432435)
|
||||||
|
public record IntegerSupply(Supplier<Integer> supply) {}
|
||||||
|
|
||||||
|
@Persistable(clazz=ServiceSupply.class, id=432436)
|
||||||
|
public record ServiceSupply(Service supply) {}
|
||||||
|
}
|
@ -217,7 +217,7 @@ public abstract class Cache<T, M extends Cache.Metadata> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
logger.log(logLevel, "Unable to load a cache [" + logName + "]");
|
logger.log(logLevel, "Unable to load a cache [" + logName + "]");
|
||||||
} catch (IOException e) {
|
} catch (Exception e) {
|
||||||
logger.log(
|
logger.log(
|
||||||
Level.WARNING,
|
Level.WARNING,
|
||||||
"Unable to load a cache [" + logName + "]: " + e.getMessage(),
|
"Unable to load a cache [" + logName + "]: " + e.getMessage(),
|
||||||
@ -271,14 +271,13 @@ public abstract class Cache<T, M extends Cache.Metadata> {
|
|||||||
invalidateCache(cacheRoot, logger);
|
invalidateCache(cacheRoot, logger);
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
} catch (IOException ioe) {
|
} catch (ClassNotFoundException | IOException ex) {
|
||||||
logger.log(
|
logger.log(
|
||||||
logLevel, "`" + logName + "` failed to load (caused by: " + ioe.getMessage() + ").");
|
Level.WARNING,
|
||||||
|
"`" + logName + "` in " + dataPath + " failed to load: " + ex.getMessage());
|
||||||
|
logger.log(logLevel, "`" + logName + "` failed to load.", ex);
|
||||||
invalidateCache(cacheRoot, logger);
|
invalidateCache(cacheRoot, logger);
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
} catch (ClassNotFoundException e) {
|
|
||||||
logger.log(Level.WARNING, logName + " appears to be corrupted", e);
|
|
||||||
return Optional.empty();
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
logger.log(logLevel, "One or more digests did not match for the cache for [{0}].", logName);
|
logger.log(logLevel, "One or more digests did not match for the cache for [{0}].", logName);
|
||||||
|
@ -1,21 +1,21 @@
|
|||||||
package org.enso.interpreter.caches;
|
package org.enso.interpreter.caches;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.ObjectInputStream;
|
|
||||||
import java.io.ObjectOutputStream;
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.enso.persist.Persistance;
|
||||||
|
import org.enso.persist.Persistable;
|
||||||
import org.enso.compiler.data.BindingsMap;
|
import org.enso.compiler.data.BindingsMap;
|
||||||
|
import org.enso.compiler.data.BindingsMap.DefinedEntity;
|
||||||
|
import org.enso.compiler.data.BindingsMap.ModuleReference;
|
||||||
import org.enso.editions.LibraryName;
|
import org.enso.editions.LibraryName;
|
||||||
import org.enso.interpreter.runtime.EnsoContext;
|
import org.enso.interpreter.runtime.EnsoContext;
|
||||||
import org.enso.pkg.QualifiedName;
|
import org.enso.pkg.QualifiedName;
|
||||||
import org.enso.pkg.SourceFile;
|
import org.enso.pkg.SourceFile;
|
||||||
|
import org.openide.util.lookup.ServiceProvider;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
@ -24,8 +24,11 @@ import com.oracle.truffle.api.TruffleFile;
|
|||||||
import com.oracle.truffle.api.TruffleLogger;
|
import com.oracle.truffle.api.TruffleLogger;
|
||||||
|
|
||||||
import buildinfo.Info;
|
import buildinfo.Info;
|
||||||
|
import scala.Option;
|
||||||
|
import scala.Tuple2;
|
||||||
import scala.collection.immutable.Map;
|
import scala.collection.immutable.Map;
|
||||||
|
|
||||||
|
@Persistable(clazz = QualifiedName.class, id = 30300)
|
||||||
public final class ImportExportCache extends Cache<ImportExportCache.CachedBindings, ImportExportCache.Metadata> {
|
public final class ImportExportCache extends Cache<ImportExportCache.CachedBindings, ImportExportCache.Metadata> {
|
||||||
|
|
||||||
private final LibraryName libraryName;
|
private final LibraryName libraryName;
|
||||||
@ -49,13 +52,9 @@ public final class ImportExportCache extends Cache<ImportExportCache.CachedBindi
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected CachedBindings deserialize(EnsoContext context, byte[] data, Metadata meta, TruffleLogger logger) throws ClassNotFoundException, IOException, ClassNotFoundException {
|
protected CachedBindings deserialize(EnsoContext context, byte[] data, Metadata meta, TruffleLogger logger) throws ClassNotFoundException, IOException, ClassNotFoundException {
|
||||||
try (var stream = new ObjectInputStream(new ByteArrayInputStream(data))) {
|
var ref = Persistance.read(data, null);
|
||||||
if (stream.readObject() instanceof MapToBindings bindings) {
|
var bindings = ref.get(MapToBindings.class);
|
||||||
return new CachedBindings(libraryName, bindings, Optional.empty());
|
return new CachedBindings(libraryName, bindings, Optional.empty());
|
||||||
} else {
|
|
||||||
throw new ClassNotFoundException("Expected ImportExportCache.FileToBindings, got " + data.getClass());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -107,25 +106,58 @@ public final class ImportExportCache extends Cache<ImportExportCache.CachedBindi
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected byte[] serialize(EnsoContext context, CachedBindings entry) throws IOException {
|
protected byte[] serialize(EnsoContext context, CachedBindings entry) throws IOException {
|
||||||
var byteStream = new ByteArrayOutputStream();
|
var arr = Persistance.write(entry.bindings(), null);
|
||||||
try (ObjectOutputStream stream = new ObjectOutputStream(byteStream)) {
|
return arr;
|
||||||
stream.writeObject(entry.bindings());
|
|
||||||
}
|
|
||||||
return byteStream.toByteArray();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final class MapToBindings implements Serializable {
|
public static final class MapToBindings {
|
||||||
private final Map<QualifiedName, BindingsMap> _entries;
|
private final Map<QualifiedName, Persistance.Reference<BindingsMap>> entries;
|
||||||
|
|
||||||
public MapToBindings(Map<QualifiedName, BindingsMap> entries) {
|
public MapToBindings(Map<QualifiedName, Persistance.Reference<BindingsMap>> entries) {
|
||||||
this._entries = entries;
|
this.entries = entries;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<QualifiedName, BindingsMap> entries() {
|
public Option<BindingsMap> findForModule(QualifiedName moduleName) {
|
||||||
return _entries;
|
var ref = entries.get(moduleName);
|
||||||
|
if (ref.isEmpty()) {
|
||||||
|
return Option.empty();
|
||||||
|
}
|
||||||
|
return Option.apply(ref.get().get(BindingsMap.class));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ServiceProvider(service = Persistance.class)
|
||||||
|
public static final class PersistMapToBindings extends Persistance<MapToBindings> {
|
||||||
|
public PersistMapToBindings() {
|
||||||
|
super(MapToBindings.class, false, 364);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void writeObject(MapToBindings obj, Output out) throws IOException {
|
||||||
|
out.writeInt(obj.entries.size());
|
||||||
|
var it = obj.entries.iterator();
|
||||||
|
while (it.hasNext()) {
|
||||||
|
var e = it.next();
|
||||||
|
out.writeInline(QualifiedName.class, e._1());
|
||||||
|
out.writeObject(e._2().get(BindingsMap.class));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
protected MapToBindings readObject(Input in) throws IOException, ClassNotFoundException {
|
||||||
|
var size = in.readInt();
|
||||||
|
var b = Map.newBuilder();
|
||||||
|
b.sizeHint(size);
|
||||||
|
while (size-- > 0) {
|
||||||
|
var name = in.readInline(QualifiedName.class);
|
||||||
|
var value = in.readReference(BindingsMap.class);
|
||||||
|
b.addOne(Tuple2.apply(name, value));
|
||||||
|
}
|
||||||
|
return new MapToBindings((Map) b.result());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static record CachedBindings(LibraryName libraryName, MapToBindings bindings, Optional<List<SourceFile<TruffleFile>>> sources) {
|
public static record CachedBindings(LibraryName libraryName, MapToBindings bindings, Optional<List<SourceFile<TruffleFile>>> sources) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,4 +171,57 @@ public final class ImportExportCache extends Cache<ImportExportCache.CachedBindi
|
|||||||
|
|
||||||
private final static ObjectMapper objectMapper = new ObjectMapper();
|
private final static ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
|
||||||
|
|
||||||
|
@Persistable(clazz=BindingsMap.PolyglotSymbol.class, id=33006)
|
||||||
|
@Persistable(clazz=org.enso.compiler.data.BindingsMap$ModuleReference$Abstract.class, id=33007)
|
||||||
|
@Persistable(clazz=BindingsMap.ModuleMethod.class, id=33008)
|
||||||
|
@Persistable(clazz=BindingsMap.Type.class, id=33009)
|
||||||
|
@Persistable(clazz=BindingsMap.ResolvedImport.class, id=33010)
|
||||||
|
@Persistable(clazz=BindingsMap.Cons.class, id=33011)
|
||||||
|
@Persistable(clazz=BindingsMap.ResolvedModule.class, id=33012)
|
||||||
|
@Persistable(clazz=BindingsMap.ResolvedType.class, id=33013)
|
||||||
|
@Persistable(clazz=BindingsMap.ResolvedMethod.class, id=33014)
|
||||||
|
@Persistable(clazz=BindingsMap.ExportedModule.class, id=33015)
|
||||||
|
@Persistable(clazz=org.enso.compiler.data.BindingsMap$SymbolRestriction$Only.class, id=33016)
|
||||||
|
@Persistable(clazz=org.enso.compiler.data.BindingsMap$SymbolRestriction$Union.class, id=33017)
|
||||||
|
@Persistable(clazz=org.enso.compiler.data.BindingsMap$SymbolRestriction$Intersect.class, id=33018)
|
||||||
|
@Persistable(clazz=org.enso.compiler.data.BindingsMap$SymbolRestriction$AllowedResolution.class, id=33019)
|
||||||
|
@Persistable(clazz=org.enso.compiler.data.BindingsMap$SymbolRestriction$All$.class, id=33020)
|
||||||
|
@Persistable(clazz=org.enso.compiler.data.BindingsMap$SymbolRestriction$Hiding.class, id=33021)
|
||||||
|
@Persistable(clazz=BindingsMap.Resolution.class, id=33029)
|
||||||
|
@Persistable(clazz=BindingsMap.ResolvedConstructor.class, id=33030)
|
||||||
|
@Persistable(clazz=BindingsMap.ResolvedPolyglotSymbol.class, id=33031)
|
||||||
|
@Persistable(clazz=BindingsMap.ResolvedPolyglotField.class, id=33032)
|
||||||
|
@ServiceProvider(service = Persistance.class)
|
||||||
|
public static final class PersistBindingsMap extends Persistance<BindingsMap> {
|
||||||
|
public PersistBindingsMap() {
|
||||||
|
super(BindingsMap.class, false, 33005);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void writeObject(BindingsMap obj, Output out) throws IOException {
|
||||||
|
out.writeObject(obj.definedEntities());
|
||||||
|
out.writeObject(obj.currentModule());
|
||||||
|
out.writeInline(scala.collection.immutable.List.class, obj.resolvedImports());
|
||||||
|
out.writeInline(scala.collection.immutable.List.class, obj.resolvedExports());
|
||||||
|
out.writeInline(scala.collection.immutable.Map.class, obj.exportedSymbols());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
protected BindingsMap readObject(Input in) throws IOException, ClassNotFoundException {
|
||||||
|
var de = (scala.collection.immutable.List<DefinedEntity>) in.readObject();
|
||||||
|
var cm = (ModuleReference) in.readObject();
|
||||||
|
var imp = in.readInline(scala.collection.immutable.List.class);
|
||||||
|
var exp = in.readInline(scala.collection.immutable.List.class);
|
||||||
|
var sym = in.readInline(scala.collection.immutable.Map.class);
|
||||||
|
var map = new BindingsMap(de, cm);
|
||||||
|
map.resolvedImports_$eq(imp);
|
||||||
|
map.resolvedExports_$eq(exp);
|
||||||
|
map.exportedSymbols_$eq(sym);
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,28 +1,28 @@
|
|||||||
package org.enso.interpreter.caches;
|
package org.enso.interpreter.caches;
|
||||||
|
|
||||||
import buildinfo.Info;
|
import buildinfo.Info;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.oracle.truffle.api.TruffleLogger;
|
import com.oracle.truffle.api.TruffleLogger;
|
||||||
import com.oracle.truffle.api.source.Source;
|
import com.oracle.truffle.api.source.Source;
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.enso.compiler.core.ir.Module;
|
import org.enso.compiler.core.ir.Module;
|
||||||
|
import org.enso.compiler.core.ir.ProcessingPass;
|
||||||
import org.enso.interpreter.runtime.EnsoContext;
|
import org.enso.interpreter.runtime.EnsoContext;
|
||||||
import org.enso.interpreter.runtime.builtin.Builtins;
|
import org.enso.interpreter.runtime.builtin.Builtins;
|
||||||
import org.enso.polyglot.CompilationStage;
|
import org.enso.polyglot.CompilationStage;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.ObjectInputStream;
|
|
||||||
import java.io.ObjectOutputStream;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
|
||||||
|
import org.enso.persist.Persistance;
|
||||||
|
|
||||||
public final class ModuleCache extends Cache<ModuleCache.CachedModule, ModuleCache.Metadata> {
|
public final class ModuleCache extends Cache<ModuleCache.CachedModule, ModuleCache.Metadata> {
|
||||||
|
|
||||||
private final org.enso.interpreter.runtime.Module module;
|
private final org.enso.interpreter.runtime.Module module;
|
||||||
@ -46,17 +46,19 @@ public final class ModuleCache extends Cache<ModuleCache.CachedModule, ModuleCac
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected CachedModule deserialize(EnsoContext context, byte[] data, Metadata meta, TruffleLogger logger) throws ClassNotFoundException, IOException, ClassNotFoundException {
|
protected CachedModule deserialize(EnsoContext context, byte[] data, Metadata meta, TruffleLogger logger) throws ClassNotFoundException, IOException, ClassNotFoundException {
|
||||||
try (var stream = new ObjectInputStream(new ByteArrayInputStream(data))) {
|
var ref = Persistance.read(data, (obj) -> switch (obj) {
|
||||||
if (stream.readObject() instanceof Module ir) {
|
case ProcessingPass.Metadata metadata -> {
|
||||||
try {
|
var option = metadata.restoreFromSerialization(context.getCompiler().context());
|
||||||
return new CachedModule(ir,CompilationStage.valueOf(meta.compilationStage()), module.getSource());
|
if (option.nonEmpty()) {
|
||||||
} catch (IOException ioe) {
|
yield option.get();
|
||||||
throw new ClassNotFoundException(ioe.getMessage());
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
throw new ClassNotFoundException("Expected Module, got " + data.getClass());
|
throw raise(RuntimeException.class, new IOException("Cannot convert " + metadata));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
default -> obj;
|
||||||
|
});
|
||||||
|
var mod = ref.get(Module.class);
|
||||||
|
return new CachedModule(mod, CompilationStage.valueOf(meta.compilationStage()), module.getSource());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -142,37 +144,11 @@ public final class ModuleCache extends Cache<ModuleCache.CachedModule, ModuleCac
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected byte[] serialize(EnsoContext context, CachedModule entry) throws IOException {
|
protected byte[] serialize(EnsoContext context, CachedModule entry) throws IOException {
|
||||||
var byteStream = new ByteArrayOutputStream();
|
var arr = Persistance.write(entry.moduleIR(), (obj) -> switch (obj) {
|
||||||
boolean noUUIDs = false;
|
case ProcessingPass.Metadata metadata -> metadata.prepareForSerialization(context.getCompiler().context());
|
||||||
for (var p : context.getPackageRepository().getLoadedPackagesJava()) {
|
default -> obj;
|
||||||
if ("Standard".equals(p.namespace())) {
|
});
|
||||||
for (var s : p.listSourcesJava()) {
|
return arr;
|
||||||
if (s.file().getPath().equals(entry.source().getPath())) {
|
|
||||||
noUUIDs = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
try (var stream = new ObjectOutputStream(byteStream) {
|
|
||||||
void filterUUIDs() {
|
|
||||||
enableReplaceObject(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Object replaceObject(Object obj) throws IOException {
|
|
||||||
if (obj instanceof UUID) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
}) {
|
|
||||||
if (noUUIDs) {
|
|
||||||
stream.filterUUIDs();
|
|
||||||
}
|
|
||||||
stream.writeObject(entry.moduleIR());
|
|
||||||
}
|
|
||||||
return byteStream.toByteArray();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public record CachedModule(Module moduleIR, CompilationStage compilationStage, Source source) {
|
public record CachedModule(Module moduleIR, CompilationStage compilationStage, Source source) {
|
||||||
@ -189,4 +165,8 @@ public final class ModuleCache extends Cache<ModuleCache.CachedModule, ModuleCac
|
|||||||
|
|
||||||
private final static ObjectMapper objectMapper = new ObjectMapper();
|
private final static ObjectMapper objectMapper = new ObjectMapper();
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private static <T extends Exception> T raise(Class<T> cls, Exception e) throws T {
|
||||||
|
throw (T)e;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -62,7 +62,6 @@ public final class Module implements EnsoObject {
|
|||||||
private QualifiedName name;
|
private QualifiedName name;
|
||||||
private final ModuleCache cache;
|
private final ModuleCache cache;
|
||||||
private boolean wasLoadedFromCache;
|
private boolean wasLoadedFromCache;
|
||||||
private boolean hasCrossModuleLinks;
|
|
||||||
private final boolean synthetic;
|
private final boolean synthetic;
|
||||||
/**
|
/**
|
||||||
* This list is filled in case there is a directory with the same name as this module. The
|
* This list is filled in case there is a directory with the same name as this module. The
|
||||||
@ -88,7 +87,6 @@ public final class Module implements EnsoObject {
|
|||||||
this.name = name;
|
this.name = name;
|
||||||
this.cache = new ModuleCache(this);
|
this.cache = new ModuleCache(this);
|
||||||
this.wasLoadedFromCache = false;
|
this.wasLoadedFromCache = false;
|
||||||
this.hasCrossModuleLinks = false;
|
|
||||||
this.synthetic = false;
|
this.synthetic = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,7 +104,6 @@ public final class Module implements EnsoObject {
|
|||||||
this.name = name;
|
this.name = name;
|
||||||
this.cache = new ModuleCache(this);
|
this.cache = new ModuleCache(this);
|
||||||
this.wasLoadedFromCache = false;
|
this.wasLoadedFromCache = false;
|
||||||
this.hasCrossModuleLinks = false;
|
|
||||||
this.patchedValues = new PatchedModuleValues(this);
|
this.patchedValues = new PatchedModuleValues(this);
|
||||||
this.synthetic = false;
|
this.synthetic = false;
|
||||||
}
|
}
|
||||||
@ -125,7 +122,6 @@ public final class Module implements EnsoObject {
|
|||||||
this.name = name;
|
this.name = name;
|
||||||
this.cache = new ModuleCache(this);
|
this.cache = new ModuleCache(this);
|
||||||
this.wasLoadedFromCache = false;
|
this.wasLoadedFromCache = false;
|
||||||
this.hasCrossModuleLinks = false;
|
|
||||||
this.patchedValues = new PatchedModuleValues(this);
|
this.patchedValues = new PatchedModuleValues(this);
|
||||||
this.synthetic = false;
|
this.synthetic = false;
|
||||||
}
|
}
|
||||||
@ -147,7 +143,6 @@ public final class Module implements EnsoObject {
|
|||||||
this.compilationStage = synthetic ? CompilationStage.INITIAL : CompilationStage.AFTER_CODEGEN;
|
this.compilationStage = synthetic ? CompilationStage.INITIAL : CompilationStage.AFTER_CODEGEN;
|
||||||
this.cache = new ModuleCache(this);
|
this.cache = new ModuleCache(this);
|
||||||
this.wasLoadedFromCache = false;
|
this.wasLoadedFromCache = false;
|
||||||
this.hasCrossModuleLinks = false;
|
|
||||||
this.synthetic = synthetic;
|
this.synthetic = synthetic;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -516,19 +511,6 @@ public final class Module implements EnsoObject {
|
|||||||
this.wasLoadedFromCache = wasLoadedFromCache;
|
this.wasLoadedFromCache = wasLoadedFromCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return {@code true} if the module has had its cross-module links restored, otherwise {@code
|
|
||||||
* false}
|
|
||||||
*/
|
|
||||||
public boolean hasCrossModuleLinks() {
|
|
||||||
return hasCrossModuleLinks;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @param hasCrossModuleLinks whether or not the module has cross-module links restored */
|
|
||||||
void setHasCrossModuleLinks(boolean hasCrossModuleLinks) {
|
|
||||||
this.hasCrossModuleLinks = hasCrossModuleLinks;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Turns this module into appropriate {@link CompilerContext} wrapper.
|
* Turns this module into appropriate {@link CompilerContext} wrapper.
|
||||||
*
|
*
|
||||||
|
@ -150,11 +150,6 @@ final class TruffleCompilerContext implements CompilerContext {
|
|||||||
return ((Module)module).unsafeModule().wasLoadedFromCache();
|
return ((Module)module).unsafeModule().wasLoadedFromCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasCrossModuleLinks(CompilerContext.Module module) {
|
|
||||||
return ((Module)module).unsafeModule().hasCrossModuleLinks();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public org.enso.compiler.core.ir.Module getIr(CompilerContext.Module module) {
|
public org.enso.compiler.core.ir.Module getIr(CompilerContext.Module module) {
|
||||||
return module.getIr();
|
return module.getIr();
|
||||||
@ -225,17 +220,9 @@ final class TruffleCompilerContext implements CompilerContext {
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
builtins.initializeBuiltinsIr(this, freshNameSupply, passes);
|
builtins.initializeBuiltinsIr(this, freshNameSupply, passes);
|
||||||
updateModule(
|
|
||||||
builtinsModule,
|
|
||||||
u -> u.hasCrossModuleLinks(true)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
builtins.initializeBuiltinsIr(this, freshNameSupply, passes);
|
builtins.initializeBuiltinsIr(this, freshNameSupply, passes);
|
||||||
updateModule(
|
|
||||||
builtinsModule,
|
|
||||||
u -> u.hasCrossModuleLinks(true)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (irCachingEnabled && !wasLoadedFromCache(builtinsModule)) {
|
if (irCachingEnabled && !wasLoadedFromCache(builtinsModule)) {
|
||||||
@ -288,7 +275,6 @@ final class TruffleCompilerContext implements CompilerContext {
|
|||||||
private org.enso.compiler.core.ir.Module ir;
|
private org.enso.compiler.core.ir.Module ir;
|
||||||
private CompilationStage stage;
|
private CompilationStage stage;
|
||||||
private Boolean loadedFromCache;
|
private Boolean loadedFromCache;
|
||||||
private Boolean hasCrossModuleLinks;
|
|
||||||
private boolean resetScope;
|
private boolean resetScope;
|
||||||
private boolean invalidateCache;
|
private boolean invalidateCache;
|
||||||
|
|
||||||
@ -316,11 +302,6 @@ final class TruffleCompilerContext implements CompilerContext {
|
|||||||
this.loadedFromCache = b;
|
this.loadedFromCache = b;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void hasCrossModuleLinks(boolean b) {
|
|
||||||
this.hasCrossModuleLinks = b;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void resetScope() {
|
public void resetScope() {
|
||||||
this.resetScope = true;
|
this.resetScope = true;
|
||||||
@ -348,9 +329,6 @@ final class TruffleCompilerContext implements CompilerContext {
|
|||||||
if (loadedFromCache != null) {
|
if (loadedFromCache != null) {
|
||||||
module.module.setLoadedFromCache(loadedFromCache);
|
module.module.setLoadedFromCache(loadedFromCache);
|
||||||
}
|
}
|
||||||
if (hasCrossModuleLinks != null) {
|
|
||||||
module.module.setHasCrossModuleLinks(hasCrossModuleLinks);
|
|
||||||
}
|
|
||||||
if (resetScope) {
|
if (resetScope) {
|
||||||
module.module.ensureScopeExists();
|
module.module.ensureScopeExists();
|
||||||
module.module.getScope().reset();
|
module.module.getScope().reset();
|
||||||
@ -429,11 +407,6 @@ final class TruffleCompilerContext implements CompilerContext {
|
|||||||
return module.isSynthetic();
|
return module.isSynthetic();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasCrossModuleLinks() {
|
|
||||||
return module.hasCrossModuleLinks();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public org.enso.compiler.core.ir.Module getIr() {
|
public org.enso.compiler.core.ir.Module getIr() {
|
||||||
return module.getIr();
|
return module.getIr();
|
||||||
|
@ -10,6 +10,7 @@ import java.util.Objects;
|
|||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.function.Supplier;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import org.enso.interpreter.runtime.Module;
|
import org.enso.interpreter.runtime.Module;
|
||||||
import org.enso.interpreter.runtime.callable.function.Function;
|
import org.enso.interpreter.runtime.callable.function.Function;
|
||||||
@ -24,7 +25,7 @@ public final class ModuleScope implements EnsoObject {
|
|||||||
private final Module module;
|
private final Module module;
|
||||||
private Map<String, Object> polyglotSymbols;
|
private Map<String, Object> polyglotSymbols;
|
||||||
private Map<String, Type> types;
|
private Map<String, Type> types;
|
||||||
private Map<Type, Map<String, Function>> methods;
|
private Map<Type, Map<String, Supplier<Function>>> methods;
|
||||||
private Map<Type, Map<Type, Function>> conversions;
|
private Map<Type, Map<Type, Function>> conversions;
|
||||||
private Set<ModuleScope> imports;
|
private Set<ModuleScope> imports;
|
||||||
private Set<ModuleScope> exports;
|
private Set<ModuleScope> exports;
|
||||||
@ -56,7 +57,7 @@ public final class ModuleScope implements EnsoObject {
|
|||||||
Type associatedType,
|
Type associatedType,
|
||||||
Map<String, Object> polyglotSymbols,
|
Map<String, Object> polyglotSymbols,
|
||||||
Map<String, Type> types,
|
Map<String, Type> types,
|
||||||
Map<Type, Map<String, Function>> methods,
|
Map<Type, Map<String, Supplier<Function>>> methods,
|
||||||
Map<Type, Map<Type, Function>> conversions,
|
Map<Type, Map<Type, Function>> conversions,
|
||||||
Set<ModuleScope> imports,
|
Set<ModuleScope> imports,
|
||||||
Set<ModuleScope> exports) {
|
Set<ModuleScope> exports) {
|
||||||
@ -95,14 +96,14 @@ public final class ModuleScope implements EnsoObject {
|
|||||||
* @param type the type for which method map is requested
|
* @param type the type for which method map is requested
|
||||||
* @return a map containing all the defined methods by name
|
* @return a map containing all the defined methods by name
|
||||||
*/
|
*/
|
||||||
private Map<String, Function> ensureMethodMapFor(Type type) {
|
private Map<String, Supplier<Function>> ensureMethodMapFor(Type type) {
|
||||||
Type tpeKey = type == null ? noTypeKey : type;
|
Type tpeKey = type == null ? noTypeKey : type;
|
||||||
return methods.computeIfAbsent(tpeKey, k -> new HashMap<>());
|
return methods.computeIfAbsent(tpeKey, k -> new HashMap<>());
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<String, Function> getMethodMapFor(Type type) {
|
private Map<String, Supplier<Function>> getMethodMapFor(Type type) {
|
||||||
Type tpeKey = type == null ? noTypeKey : type;
|
Type tpeKey = type == null ? noTypeKey : type;
|
||||||
Map<String, Function> result = methods.get(type);
|
Map<String, Supplier<Function>> result = methods.get(type);
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
return new HashMap<>();
|
return new HashMap<>();
|
||||||
}
|
}
|
||||||
@ -117,14 +118,55 @@ public final class ModuleScope implements EnsoObject {
|
|||||||
* @param function the {@link Function} associated with this definition
|
* @param function the {@link Function} associated with this definition
|
||||||
*/
|
*/
|
||||||
public void registerMethod(Type type, String method, Function function) {
|
public void registerMethod(Type type, String method, Function function) {
|
||||||
Map<String, Function> methodMap = ensureMethodMapFor(type);
|
Map<String, Supplier<Function>> methodMap = ensureMethodMapFor(type);
|
||||||
|
|
||||||
// Builtin types will have double definition because of
|
// Builtin types will have double definition because of
|
||||||
// BuiltinMethod and that's OK
|
// BuiltinMethod and that's OK
|
||||||
if (methodMap.containsKey(method) && !type.isBuiltin()) {
|
if (methodMap.containsKey(method) && !type.isBuiltin()) {
|
||||||
throw new RedefinedMethodException(type.getName(), method);
|
throw new RedefinedMethodException(type.getName(), method);
|
||||||
} else {
|
} else {
|
||||||
methodMap.put(method, function);
|
methodMap.put(method, new CachingSupplier<>(function));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a lazily constructed method defined for a given type.
|
||||||
|
*
|
||||||
|
* @param type the type the method was defined for
|
||||||
|
* @param method method name
|
||||||
|
* @param supply provider of the {@link Function} associated with this definition
|
||||||
|
*/
|
||||||
|
public void registerMethod(Type type, String method, Supplier<Function> supply) {
|
||||||
|
Map<String, Supplier<Function>> methodMap = ensureMethodMapFor(type);
|
||||||
|
|
||||||
|
// Builtin types will have double definition because of
|
||||||
|
// BuiltinMethod and that's OK
|
||||||
|
if (methodMap.containsKey(method) && !type.isBuiltin()) {
|
||||||
|
throw new RedefinedMethodException(type.getName(), method);
|
||||||
|
} else {
|
||||||
|
methodMap.put(method, new CachingSupplier<>(supply));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class CachingSupplier<T> implements Supplier<T> {
|
||||||
|
private final Supplier<T> supply;
|
||||||
|
private T memo;
|
||||||
|
|
||||||
|
CachingSupplier(Supplier<T> supply) {
|
||||||
|
this.supply = supply;
|
||||||
|
}
|
||||||
|
|
||||||
|
CachingSupplier(T memo) {
|
||||||
|
this.supply = null;
|
||||||
|
this.memo = memo;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T get() {
|
||||||
|
if (memo == null) {
|
||||||
|
memo = supply.get();
|
||||||
|
}
|
||||||
|
return memo;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -185,14 +227,14 @@ public final class ModuleScope implements EnsoObject {
|
|||||||
*/
|
*/
|
||||||
@TruffleBoundary
|
@TruffleBoundary
|
||||||
public Function lookupMethodDefinition(Type type, String name) {
|
public Function lookupMethodDefinition(Type type, String name) {
|
||||||
Function definedWithAtom = type.getDefinitionScope().getMethodMapFor(type).get(name);
|
var definedWithAtom = type.getDefinitionScope().getMethodMapFor(type).get(name);
|
||||||
if (definedWithAtom != null) {
|
if (definedWithAtom != null) {
|
||||||
return definedWithAtom;
|
return definedWithAtom.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
Function definedHere = getMethodMapFor(type).get(name);
|
var definedHere = getMethodMapFor(type).get(name);
|
||||||
if (definedHere != null) {
|
if (definedHere != null) {
|
||||||
return definedHere;
|
return definedHere.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
return imports.stream()
|
return imports.stream()
|
||||||
@ -226,13 +268,14 @@ public final class ModuleScope implements EnsoObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Function getExportedMethod(Type type, String name) {
|
private Function getExportedMethod(Type type, String name) {
|
||||||
Function here = getMethodMapFor(type).get(name);
|
var here = getMethodMapFor(type).get(name);
|
||||||
if (here != null) {
|
if (here != null) {
|
||||||
return here;
|
return here.get();
|
||||||
}
|
}
|
||||||
return exports.stream()
|
return exports.stream()
|
||||||
.map(scope -> scope.getMethodMapFor(type).get(name))
|
.map(scope -> scope.getMethodMapFor(type).get(name))
|
||||||
.filter(Objects::nonNull)
|
.filter(Objects::nonNull)
|
||||||
|
.map(s -> s.get())
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.orElse(null);
|
.orElse(null);
|
||||||
}
|
}
|
||||||
@ -281,8 +324,12 @@ public final class ModuleScope implements EnsoObject {
|
|||||||
/** @return a method for the given type */
|
/** @return a method for the given type */
|
||||||
public Function getMethodForType(Type tpe, String name) {
|
public Function getMethodForType(Type tpe, String name) {
|
||||||
Type tpeKey = tpe == null ? noTypeKey : tpe;
|
Type tpeKey = tpe == null ? noTypeKey : tpe;
|
||||||
Map<String, Function> allTpeMethods = methods.get(tpeKey);
|
var allTpeMethods = methods.get(tpeKey);
|
||||||
return allTpeMethods == null ? null : allTpeMethods.get(name);
|
if (allTpeMethods == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var supply = allTpeMethods.get(name);
|
||||||
|
return supply == null ? null : supply.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -293,7 +340,7 @@ public final class ModuleScope implements EnsoObject {
|
|||||||
*/
|
*/
|
||||||
public Set<String> getMethodNamesForType(Type tpe) {
|
public Set<String> getMethodNamesForType(Type tpe) {
|
||||||
Type tpeKey = tpe == null ? noTypeKey : tpe;
|
Type tpeKey = tpe == null ? noTypeKey : tpe;
|
||||||
Map<String, Function> allTpeMethods = methods.get(tpeKey);
|
var allTpeMethods = methods.get(tpeKey);
|
||||||
return allTpeMethods == null ? null : allTpeMethods.keySet();
|
return allTpeMethods == null ? null : allTpeMethods.keySet();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -305,7 +352,7 @@ public final class ModuleScope implements EnsoObject {
|
|||||||
*/
|
*/
|
||||||
public void registerAllMethodsOfTypeToScope(Type tpe, ModuleScope scope) {
|
public void registerAllMethodsOfTypeToScope(Type tpe, ModuleScope scope) {
|
||||||
Type tpeKey = tpe == null ? noTypeKey : tpe;
|
Type tpeKey = tpe == null ? noTypeKey : tpe;
|
||||||
Map<String, Function> allTypeMethods = methods.get(tpeKey);
|
var allTypeMethods = methods.get(tpeKey);
|
||||||
if (allTypeMethods != null) {
|
if (allTypeMethods != null) {
|
||||||
allTypeMethods.forEach((name, fun) -> scope.registerMethod(tpeKey, name, fun));
|
allTypeMethods.forEach((name, fun) -> scope.registerMethod(tpeKey, name, fun));
|
||||||
}
|
}
|
||||||
@ -313,7 +360,10 @@ public final class ModuleScope implements EnsoObject {
|
|||||||
|
|
||||||
/** @return methods for all registered types */
|
/** @return methods for all registered types */
|
||||||
public List<Function> getAllMethods() {
|
public List<Function> getAllMethods() {
|
||||||
return methods.values().stream().flatMap(e -> e.values().stream()).collect(Collectors.toList());
|
return methods.values().stream()
|
||||||
|
.flatMap(e -> e.values().stream())
|
||||||
|
.map(s -> s.get())
|
||||||
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @return the raw conversions held by this module */
|
/** @return the raw conversions held by this module */
|
||||||
@ -346,7 +396,7 @@ public final class ModuleScope implements EnsoObject {
|
|||||||
public ModuleScope withTypes(List<String> typeNames) {
|
public ModuleScope withTypes(List<String> typeNames) {
|
||||||
Map<String, Object> polyglotSymbols = new HashMap<>(this.polyglotSymbols);
|
Map<String, Object> polyglotSymbols = new HashMap<>(this.polyglotSymbols);
|
||||||
Map<String, Type> requestedTypes = new HashMap<>(this.types);
|
Map<String, Type> requestedTypes = new HashMap<>(this.types);
|
||||||
Map<Type, Map<String, Function>> methods = new ConcurrentHashMap<>();
|
Map<Type, Map<String, Supplier<Function>>> methods = new ConcurrentHashMap<>();
|
||||||
Map<Type, Map<Type, Function>> conversions = new ConcurrentHashMap<>();
|
Map<Type, Map<Type, Function>> conversions = new ConcurrentHashMap<>();
|
||||||
Set<ModuleScope> imports = new HashSet<>(this.imports);
|
Set<ModuleScope> imports = new HashSet<>(this.imports);
|
||||||
Set<ModuleScope> exports = new HashSet<>(this.exports);
|
Set<ModuleScope> exports = new HashSet<>(this.exports);
|
||||||
|
@ -586,7 +586,7 @@ private class DefaultPackageRepository(
|
|||||||
}
|
}
|
||||||
loadedLibraryBindings.get(libraryName)
|
loadedLibraryBindings.get(libraryName)
|
||||||
}
|
}
|
||||||
cache.flatMap(_.bindings.entries().get(moduleName))
|
cache.flatMap(_.bindings.findForModule(moduleName))
|
||||||
}
|
}
|
||||||
|
|
||||||
private def loadDependencies(pkg: Package[TruffleFile]): Unit = {
|
private def loadDependencies(pkg: Package[TruffleFile]): Unit = {
|
||||||
|
@ -438,191 +438,203 @@ class IrToTruffle(
|
|||||||
dataflowInfo
|
dataflowInfo
|
||||||
)
|
)
|
||||||
|
|
||||||
val function = methodDef.body match {
|
moduleScope.registerMethod(
|
||||||
case fn: Function if isBuiltinMethod(fn.body) =>
|
cons,
|
||||||
// For builtin types that own the builtin method we only check that
|
methodDef.methodName.name,
|
||||||
// the method has been registered during the initialization of builtins
|
() => {
|
||||||
// and not attempt to register it in the scope (can't redefined methods).
|
val function = methodDef.body match {
|
||||||
// For non-builtin types (or modules) that own the builtin method
|
case fn: Function if isBuiltinMethod(fn.body) =>
|
||||||
// we have to look up the function and register it in the scope.
|
// For builtin types that own the builtin method we only check that
|
||||||
// Static wrappers for instance methods have to be registered always.
|
// the method has been registered during the initialization of builtins
|
||||||
val fullMethodName = methodDef.body
|
// and not attempt to register it in the scope (can't redefined methods).
|
||||||
.asInstanceOf[Function.Lambda]
|
// For non-builtin types (or modules) that own the builtin method
|
||||||
.body
|
// we have to look up the function and register it in the scope.
|
||||||
.asInstanceOf[Literal.Text]
|
// Static wrappers for instance methods have to be registered always.
|
||||||
|
val fullMethodName = methodDef.body
|
||||||
|
.asInstanceOf[Function.Lambda]
|
||||||
|
.body
|
||||||
|
.asInstanceOf[Literal.Text]
|
||||||
|
|
||||||
val builtinNameElements = fullMethodName.text.split('.')
|
val builtinNameElements = fullMethodName.text.split('.')
|
||||||
if (builtinNameElements.length != 2) {
|
if (builtinNameElements.length != 2) {
|
||||||
throw new CompilerError(
|
throw new CompilerError(
|
||||||
s"Unknown builtin method ${fullMethodName.text}, probably should be '$fullMethodDefName?'"
|
s"Unknown builtin method ${fullMethodName.text}, probably should be '$fullMethodDefName?'"
|
||||||
)
|
)
|
||||||
}
|
|
||||||
val methodName = builtinNameElements(1)
|
|
||||||
val methodOwnerName = builtinNameElements(0)
|
|
||||||
|
|
||||||
val staticWrapper = methodDef.isStaticWrapperForInstanceMethod
|
|
||||||
|
|
||||||
val builtinFunction = context.getBuiltins
|
|
||||||
.getBuiltinFunction(
|
|
||||||
methodOwnerName,
|
|
||||||
methodName,
|
|
||||||
language,
|
|
||||||
staticWrapper
|
|
||||||
)
|
|
||||||
builtinFunction.toScala
|
|
||||||
.map(Some(_))
|
|
||||||
.toRight(
|
|
||||||
new CompilerError(
|
|
||||||
s"Unable to find Truffle Node for method ${cons.getName}.${methodDef.methodName.name}"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.left
|
|
||||||
.flatMap { l =>
|
|
||||||
// Builtin Types Number and Integer have methods only for documentation purposes
|
|
||||||
val number = context.getBuiltins.number()
|
|
||||||
val ok =
|
|
||||||
staticWrapper && (cons == number.getNumber.getEigentype || cons == number.getInteger.getEigentype) ||
|
|
||||||
!staticWrapper && (cons == number.getNumber || cons == number.getInteger)
|
|
||||||
if (ok) Right(None)
|
|
||||||
else Left(l)
|
|
||||||
}
|
|
||||||
.map(fOpt =>
|
|
||||||
fOpt.map { m =>
|
|
||||||
if (m.isAutoRegister) {
|
|
||||||
val irFunctionArgumentsCount = fn.arguments.length
|
|
||||||
val builtinArgumentsCount =
|
|
||||||
m.getFunction.getSchema.getArgumentsCount
|
|
||||||
if (irFunctionArgumentsCount != builtinArgumentsCount) {
|
|
||||||
val irFunctionArguments =
|
|
||||||
fn.arguments.map(_.name.name).mkString(",")
|
|
||||||
val builtinArguments =
|
|
||||||
m.getFunction.getSchema.getArgumentInfos
|
|
||||||
.map(_.getName)
|
|
||||||
.mkString(",")
|
|
||||||
throw new CompilerError(
|
|
||||||
s"Wrong number of arguments provided in the definition of builtin function ${cons.getName}.${methodDef.methodName.name}. " +
|
|
||||||
s"[$irFunctionArguments] vs [$builtinArguments]"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
val bodyBuilder =
|
|
||||||
new expressionProcessor.BuildFunctionBody(
|
|
||||||
fn.arguments,
|
|
||||||
fn.body,
|
|
||||||
effectContext,
|
|
||||||
true
|
|
||||||
)
|
|
||||||
val builtinRootNode =
|
|
||||||
m.getFunction.getCallTarget.getRootNode
|
|
||||||
.asInstanceOf[BuiltinRootNode]
|
|
||||||
builtinRootNode.setModuleName(moduleScope.getModule.getName)
|
|
||||||
builtinRootNode.setTypeName(cons.getQualifiedName)
|
|
||||||
new RuntimeFunction(
|
|
||||||
m.getFunction.getCallTarget,
|
|
||||||
null,
|
|
||||||
new FunctionSchema(
|
|
||||||
new Array[RuntimeAnnotation](0),
|
|
||||||
bodyBuilder.args(): _*
|
|
||||||
)
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
m.getFunction
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
)
|
val methodName = builtinNameElements(1)
|
||||||
case fn: Function =>
|
val methodOwnerName = builtinNameElements(0)
|
||||||
val bodyBuilder =
|
|
||||||
new expressionProcessor.BuildFunctionBody(
|
|
||||||
fn.arguments,
|
|
||||||
fn.body,
|
|
||||||
effectContext,
|
|
||||||
true
|
|
||||||
)
|
|
||||||
val rootNode = MethodRootNode.build(
|
|
||||||
language,
|
|
||||||
expressionProcessor.scope,
|
|
||||||
moduleScope,
|
|
||||||
() => bodyBuilder.bodyNode(),
|
|
||||||
makeSection(moduleScope, methodDef.location),
|
|
||||||
cons,
|
|
||||||
methodDef.methodName.name
|
|
||||||
)
|
|
||||||
val callTarget = rootNode.getCallTarget
|
|
||||||
val arguments = bodyBuilder.args()
|
|
||||||
// build annotations
|
|
||||||
val annotations =
|
|
||||||
methodDef.getMetadata(GenericAnnotations).toVector.flatMap {
|
|
||||||
meta =>
|
|
||||||
meta.annotations
|
|
||||||
.collect { case annotation: Name.GenericAnnotation =>
|
|
||||||
val scopeElements = Seq(
|
|
||||||
cons.getName,
|
|
||||||
methodDef.methodName.name,
|
|
||||||
annotation.name
|
|
||||||
)
|
|
||||||
val scopeName =
|
|
||||||
scopeElements.mkString(Constants.SCOPE_SEPARATOR)
|
|
||||||
val scopeInfo = annotation
|
|
||||||
.unsafeGetMetadata(
|
|
||||||
AliasAnalysis,
|
|
||||||
s"Missing scope information for annotation " +
|
|
||||||
s"${annotation.name} of method " +
|
|
||||||
scopeElements.init.mkString(Constants.SCOPE_SEPARATOR)
|
|
||||||
)
|
|
||||||
.unsafeAs[AliasAnalysis.Info.Scope.Root]
|
|
||||||
val dataflowInfo = annotation.unsafeGetMetadata(
|
|
||||||
DataflowAnalysis,
|
|
||||||
"Missing dataflow information for annotation " +
|
|
||||||
s"${annotation.name} of method " +
|
|
||||||
scopeElements.init.mkString(Constants.SCOPE_SEPARATOR)
|
|
||||||
)
|
|
||||||
val expressionProcessor = new ExpressionProcessor(
|
|
||||||
scopeName,
|
|
||||||
scopeInfo.graph,
|
|
||||||
scopeInfo.graph.rootScope,
|
|
||||||
dataflowInfo
|
|
||||||
)
|
|
||||||
val expressionNode =
|
|
||||||
expressionProcessor.run(annotation.expression, true)
|
|
||||||
val closureName =
|
|
||||||
s"<default::${expressionProcessor.scopeName}>"
|
|
||||||
val closureRootNode = ClosureRootNode.build(
|
|
||||||
language,
|
|
||||||
expressionProcessor.scope,
|
|
||||||
moduleScope,
|
|
||||||
expressionNode,
|
|
||||||
makeSection(moduleScope, annotation.location),
|
|
||||||
closureName,
|
|
||||||
true,
|
|
||||||
false
|
|
||||||
)
|
|
||||||
new RuntimeAnnotation(annotation.name, closureRootNode)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Right(
|
val staticWrapper = methodDef.isStaticWrapperForInstanceMethod
|
||||||
Some(
|
|
||||||
new RuntimeFunction(
|
val builtinFunction = context.getBuiltins
|
||||||
callTarget,
|
.getBuiltinFunction(
|
||||||
null,
|
methodOwnerName,
|
||||||
new FunctionSchema(annotations.toArray, arguments: _*)
|
methodName,
|
||||||
|
language,
|
||||||
|
staticWrapper
|
||||||
|
)
|
||||||
|
builtinFunction.toScala
|
||||||
|
.map(Some(_))
|
||||||
|
.toRight(
|
||||||
|
new CompilerError(
|
||||||
|
s"Unable to find Truffle Node for method ${cons.getName}.${methodDef.methodName.name}"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.left
|
||||||
|
.flatMap { l =>
|
||||||
|
// Builtin Types Number and Integer have methods only for documentation purposes
|
||||||
|
val number = context.getBuiltins.number()
|
||||||
|
val ok =
|
||||||
|
staticWrapper && (cons == number.getNumber.getEigentype || cons == number.getInteger.getEigentype) ||
|
||||||
|
!staticWrapper && (cons == number.getNumber || cons == number.getInteger)
|
||||||
|
if (ok) Right(None)
|
||||||
|
else Left(l)
|
||||||
|
}
|
||||||
|
.map(fOpt =>
|
||||||
|
fOpt.map { m =>
|
||||||
|
if (m.isAutoRegister) {
|
||||||
|
val irFunctionArgumentsCount = fn.arguments.length
|
||||||
|
val builtinArgumentsCount =
|
||||||
|
m.getFunction.getSchema.getArgumentsCount
|
||||||
|
if (irFunctionArgumentsCount != builtinArgumentsCount) {
|
||||||
|
val irFunctionArguments =
|
||||||
|
fn.arguments.map(_.name.name).mkString(",")
|
||||||
|
val builtinArguments =
|
||||||
|
m.getFunction.getSchema.getArgumentInfos
|
||||||
|
.map(_.getName)
|
||||||
|
.mkString(",")
|
||||||
|
throw new CompilerError(
|
||||||
|
s"Wrong number of arguments provided in the definition of builtin function ${cons.getName}.${methodDef.methodName.name}. " +
|
||||||
|
s"[$irFunctionArguments] vs [$builtinArguments]"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val bodyBuilder =
|
||||||
|
new expressionProcessor.BuildFunctionBody(
|
||||||
|
fn.arguments,
|
||||||
|
fn.body,
|
||||||
|
effectContext,
|
||||||
|
true
|
||||||
|
)
|
||||||
|
val builtinRootNode =
|
||||||
|
m.getFunction.getCallTarget.getRootNode
|
||||||
|
.asInstanceOf[BuiltinRootNode]
|
||||||
|
builtinRootNode
|
||||||
|
.setModuleName(moduleScope.getModule.getName)
|
||||||
|
builtinRootNode.setTypeName(cons.getQualifiedName)
|
||||||
|
new RuntimeFunction(
|
||||||
|
m.getFunction.getCallTarget,
|
||||||
|
null,
|
||||||
|
new FunctionSchema(
|
||||||
|
new Array[RuntimeAnnotation](0),
|
||||||
|
bodyBuilder.args(): _*
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
m.getFunction
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
case fn: Function =>
|
||||||
|
val bodyBuilder =
|
||||||
|
new expressionProcessor.BuildFunctionBody(
|
||||||
|
fn.arguments,
|
||||||
|
fn.body,
|
||||||
|
effectContext,
|
||||||
|
true
|
||||||
|
)
|
||||||
|
val rootNode = MethodRootNode.build(
|
||||||
|
language,
|
||||||
|
expressionProcessor.scope,
|
||||||
|
moduleScope,
|
||||||
|
() => bodyBuilder.bodyNode(),
|
||||||
|
makeSection(moduleScope, methodDef.location),
|
||||||
|
cons,
|
||||||
|
methodDef.methodName.name
|
||||||
)
|
)
|
||||||
)
|
val callTarget = rootNode.getCallTarget
|
||||||
)
|
val arguments = bodyBuilder.args()
|
||||||
case _ =>
|
// build annotations
|
||||||
Left(
|
val annotations =
|
||||||
new CompilerError(
|
methodDef.getMetadata(GenericAnnotations).toVector.flatMap {
|
||||||
"Method bodies must be functions at the point of codegen."
|
meta =>
|
||||||
)
|
meta.annotations
|
||||||
)
|
.collect { case annotation: Name.GenericAnnotation =>
|
||||||
}
|
val scopeElements = Seq(
|
||||||
function match {
|
cons.getName,
|
||||||
case Left(failure) =>
|
methodDef.methodName.name,
|
||||||
throw failure
|
annotation.name
|
||||||
case Right(Some(fun)) =>
|
)
|
||||||
moduleScope.registerMethod(cons, methodDef.methodName.name, fun)
|
val scopeName =
|
||||||
case _ =>
|
scopeElements.mkString(Constants.SCOPE_SEPARATOR)
|
||||||
// Don't register dummy function nodes
|
val scopeInfo = annotation
|
||||||
}
|
.unsafeGetMetadata(
|
||||||
|
AliasAnalysis,
|
||||||
|
s"Missing scope information for annotation " +
|
||||||
|
s"${annotation.name} of method " +
|
||||||
|
scopeElements.init
|
||||||
|
.mkString(Constants.SCOPE_SEPARATOR)
|
||||||
|
)
|
||||||
|
.unsafeAs[AliasAnalysis.Info.Scope.Root]
|
||||||
|
val dataflowInfo = annotation.unsafeGetMetadata(
|
||||||
|
DataflowAnalysis,
|
||||||
|
"Missing dataflow information for annotation " +
|
||||||
|
s"${annotation.name} of method " +
|
||||||
|
scopeElements.init
|
||||||
|
.mkString(Constants.SCOPE_SEPARATOR)
|
||||||
|
)
|
||||||
|
val expressionProcessor = new ExpressionProcessor(
|
||||||
|
scopeName,
|
||||||
|
scopeInfo.graph,
|
||||||
|
scopeInfo.graph.rootScope,
|
||||||
|
dataflowInfo
|
||||||
|
)
|
||||||
|
val expressionNode =
|
||||||
|
expressionProcessor.run(annotation.expression, true)
|
||||||
|
val closureName =
|
||||||
|
s"<default::${expressionProcessor.scopeName}>"
|
||||||
|
val closureRootNode = ClosureRootNode.build(
|
||||||
|
language,
|
||||||
|
expressionProcessor.scope,
|
||||||
|
moduleScope,
|
||||||
|
expressionNode,
|
||||||
|
makeSection(moduleScope, annotation.location),
|
||||||
|
closureName,
|
||||||
|
true,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
new RuntimeAnnotation(
|
||||||
|
annotation.name,
|
||||||
|
closureRootNode
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Right(
|
||||||
|
Some(
|
||||||
|
new RuntimeFunction(
|
||||||
|
callTarget,
|
||||||
|
null,
|
||||||
|
new FunctionSchema(annotations.toArray, arguments: _*)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
case _ =>
|
||||||
|
Left(
|
||||||
|
new CompilerError(
|
||||||
|
"Method bodies must be functions at the point of codegen."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
function match {
|
||||||
|
case Left(failure) =>
|
||||||
|
throw failure
|
||||||
|
case Right(Some(fun)) =>
|
||||||
|
fun
|
||||||
|
case x =>
|
||||||
|
throw new IllegalStateException("Wrong state: " + x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -119,10 +119,6 @@ final class SerializationManager(private val context: TruffleCompilerContext) {
|
|||||||
module.getIr,
|
module.getIr,
|
||||||
module.getIr.duplicate(keepIdentifiers = true)
|
module.getIr.duplicate(keepIdentifiers = true)
|
||||||
)
|
)
|
||||||
duplicatedIr.preorder.foreach(
|
|
||||||
_.passData.prepareForSerialization(compiler.context)
|
|
||||||
)
|
|
||||||
|
|
||||||
val task = doSerializeModule(
|
val task = doSerializeModule(
|
||||||
getCache(module),
|
getCache(module),
|
||||||
duplicatedIr,
|
duplicatedIr,
|
||||||
@ -215,7 +211,10 @@ final class SerializationManager(private val context: TruffleCompilerContext) {
|
|||||||
)
|
)
|
||||||
val abstractBindings =
|
val abstractBindings =
|
||||||
bindings.prepareForSerialization(compiler.context)
|
bindings.prepareForSerialization(compiler.context)
|
||||||
(module.getName, abstractBindings)
|
(
|
||||||
|
module.getName,
|
||||||
|
org.enso.persist.Persistance.Reference.of(abstractBindings)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
.toMap
|
.toMap
|
||||||
),
|
),
|
||||||
@ -401,6 +400,7 @@ final class SerializationManager(private val context: TruffleCompilerContext) {
|
|||||||
compiler: Compiler,
|
compiler: Compiler,
|
||||||
module: Module
|
module: Module
|
||||||
): Option[Boolean] = {
|
): Option[Boolean] = {
|
||||||
|
compiler.getClass()
|
||||||
if (isWaitingForSerialization(module)) {
|
if (isWaitingForSerialization(module)) {
|
||||||
abort(module)
|
abort(module)
|
||||||
None
|
None
|
||||||
@ -411,9 +411,6 @@ final class SerializationManager(private val context: TruffleCompilerContext) {
|
|||||||
|
|
||||||
context.loadCache(getCache(module)).toScala match {
|
context.loadCache(getCache(module)).toScala match {
|
||||||
case Some(loadedCache) =>
|
case Some(loadedCache) =>
|
||||||
val relinkedIrChecks =
|
|
||||||
loadedCache.moduleIR.preorder
|
|
||||||
.map(_.passData.restoreFromSerialization(compiler.context))
|
|
||||||
context.updateModule(
|
context.updateModule(
|
||||||
module,
|
module,
|
||||||
{ u =>
|
{ u =>
|
||||||
@ -428,24 +425,7 @@ final class SerializationManager(private val context: TruffleCompilerContext) {
|
|||||||
module.getName,
|
module.getName,
|
||||||
loadedCache.compilationStage
|
loadedCache.compilationStage
|
||||||
)
|
)
|
||||||
|
Some(true)
|
||||||
if (!relinkedIrChecks.contains(false)) {
|
|
||||||
context.updateModule(module, _.hasCrossModuleLinks(true))
|
|
||||||
context.logSerializationManager(
|
|
||||||
debugLogLevel,
|
|
||||||
"Restored links (early phase) in module [{0}].",
|
|
||||||
module.getName
|
|
||||||
)
|
|
||||||
Some(true)
|
|
||||||
} else {
|
|
||||||
context.logSerializationManager(
|
|
||||||
debugLogLevel,
|
|
||||||
"Could not restore links (early phase) in module [{0}].",
|
|
||||||
module.getName
|
|
||||||
)
|
|
||||||
context.updateModule(module, _.hasCrossModuleLinks(false))
|
|
||||||
Some(false)
|
|
||||||
}
|
|
||||||
case None =>
|
case None =>
|
||||||
context.logSerializationManager(
|
context.logSerializationManager(
|
||||||
debugLogLevel,
|
debugLogLevel,
|
||||||
|
@ -38,7 +38,7 @@ public abstract class CompilerTest {
|
|||||||
return ir;
|
return ir;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void assertIR(String msg, Module old, Module now) throws IOException {
|
public static void assertIR(String msg, Module old, Module now) throws IOException {
|
||||||
Function<IR, String> filter = f -> simplifyIR(f, true, true, false);
|
Function<IR, String> filter = f -> simplifyIR(f, true, true, false);
|
||||||
String ir1 = filter.apply(old);
|
String ir1 = filter.apply(old);
|
||||||
String ir2 = filter.apply(now);
|
String ir2 = filter.apply(now);
|
||||||
@ -129,6 +129,15 @@ public abstract class CompilerTest {
|
|||||||
int to = txt.indexOf("reason =", at + pref.length());
|
int to = txt.indexOf("reason =", at + pref.length());
|
||||||
txt = txt.substring(0, at) + "errors.Syntax (" + txt.substring(to);
|
txt = txt.substring(0, at) + "errors.Syntax (" + txt.substring(to);
|
||||||
}
|
}
|
||||||
|
for (;;) {
|
||||||
|
final String pref = "List(";
|
||||||
|
int at = txt.indexOf(pref);
|
||||||
|
if (at == -1) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
int to = at + pref.length();
|
||||||
|
txt = txt.substring(0, at) + "Seq(" + txt.substring(to);
|
||||||
|
}
|
||||||
return txt;
|
return txt;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package org.enso.compiler;
|
package org.enso.compiler;
|
||||||
|
|
||||||
|
import org.enso.compiler.core.ir.Location;
|
||||||
import org.enso.compiler.core.ir.Module;
|
import org.enso.compiler.core.ir.Module;
|
||||||
import org.enso.compiler.core.ir.expression.errors.Syntax;
|
import org.enso.compiler.core.ir.expression.errors.Syntax;
|
||||||
import org.enso.syntax.text.Location;
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
@ -0,0 +1,58 @@
|
|||||||
|
package org.enso.interpreter.caches;
|
||||||
|
|
||||||
|
import org.enso.compiler.CompilerTest;
|
||||||
|
import org.enso.compiler.core.IR;
|
||||||
|
import org.enso.interpreter.Constants;
|
||||||
|
import org.enso.interpreter.runtime.EnsoContext;
|
||||||
|
import org.enso.interpreter.runtime.builtin.Builtins;
|
||||||
|
import org.enso.interpreter.test.TestBase;
|
||||||
|
import org.enso.interpreter.util.ScalaConversions;
|
||||||
|
import org.enso.polyglot.CompilationStage;
|
||||||
|
import org.enso.polyglot.LanguageInfo;
|
||||||
|
import org.enso.polyglot.MethodNames;
|
||||||
|
import org.enso.polyglot.RuntimeOptions;
|
||||||
|
import org.graalvm.polyglot.Context;
|
||||||
|
import org.junit.Test;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
|
|
||||||
|
public class ModuleCacheTest extends TestBase {
|
||||||
|
private static Context ctx;
|
||||||
|
|
||||||
|
public ModuleCacheTest() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
public static void initializeContext() throws Exception {
|
||||||
|
ctx = defaultContextBuilder()
|
||||||
|
.option(RuntimeOptions.DISABLE_IR_CACHES, "true")
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCompareList() throws Exception {
|
||||||
|
var ensoCtx = (EnsoContext) ctx.getBindings(LanguageInfo.ID).invokeMember(MethodNames.TopScope.LEAK_CONTEXT).asHostObject();
|
||||||
|
var name = "Standard.Base.Data.List";
|
||||||
|
|
||||||
|
var v = ctx.eval("enso", """
|
||||||
|
import Standard.Base.Data.List
|
||||||
|
|
||||||
|
empty = List.List.Nil
|
||||||
|
""").invokeMember(MethodNames.Module.EVAL_EXPRESSION, "empty");
|
||||||
|
assertEquals("List", v.getMetaObject().getMetaSimpleName());
|
||||||
|
|
||||||
|
var option = ensoCtx.findModule(name);
|
||||||
|
assertTrue("Module found", option.isPresent());
|
||||||
|
var module = option.get();
|
||||||
|
var ir = module.getIr().duplicate(true, true, true, true);
|
||||||
|
var cm = new ModuleCache.CachedModule(ir, CompilationStage.AFTER_CODEGEN, module.getSource());
|
||||||
|
byte[] arr = module.getCache().serialize(ensoCtx, cm);
|
||||||
|
|
||||||
|
var meta = new ModuleCache.Metadata("hash", "code", CompilationStage.AFTER_CODEGEN.toString());
|
||||||
|
var cachedIr = module.getCache().deserialize(ensoCtx, arr, meta, null);
|
||||||
|
assertNotNull("IR read", cachedIr);
|
||||||
|
CompilerTest.assertIR(name, ir, cachedIr.moduleIR());
|
||||||
|
}
|
||||||
|
}
|
@ -150,7 +150,7 @@ trait CompilerRunner {
|
|||||||
* @return a method containing `ir` as its body
|
* @return a method containing `ir` as its body
|
||||||
*/
|
*/
|
||||||
def asMethod: definition.Method = {
|
def asMethod: definition.Method = {
|
||||||
definition.Method.Explicit(
|
new definition.Method.Explicit(
|
||||||
Name.MethodReference(
|
Name.MethodReference(
|
||||||
Some(
|
Some(
|
||||||
Name.Qualified(
|
Name.Qualified(
|
||||||
|
@ -31,7 +31,7 @@ class GatherDiagnosticsTest extends CompilerTest {
|
|||||||
hasDefaultsSuspended = false,
|
hasDefaultsSuspended = false,
|
||||||
None
|
None
|
||||||
)
|
)
|
||||||
val lam = Function.Lambda(
|
val lam = new Function.Lambda(
|
||||||
List(
|
List(
|
||||||
DefinitionArgument
|
DefinitionArgument
|
||||||
.Specified(
|
.Specified(
|
||||||
@ -94,10 +94,8 @@ class GatherDiagnosticsTest extends CompilerTest {
|
|||||||
List(),
|
List(),
|
||||||
None
|
None
|
||||||
),
|
),
|
||||||
definition.Method
|
new definition.Method.Explicit(method1Ref, lam, None),
|
||||||
.Explicit(method1Ref, lam, None),
|
new definition.Method.Explicit(method2Ref, error3, None)
|
||||||
definition.Method
|
|
||||||
.Explicit(method2Ref, error3, None)
|
|
||||||
),
|
),
|
||||||
false,
|
false,
|
||||||
None
|
None
|
||||||
|
@ -5,12 +5,12 @@ import org.enso.compiler.core.ir.{
|
|||||||
Empty,
|
Empty,
|
||||||
Expression,
|
Expression,
|
||||||
IdentifiedLocation,
|
IdentifiedLocation,
|
||||||
|
Location,
|
||||||
Name
|
Name
|
||||||
}
|
}
|
||||||
import org.enso.compiler.core.ir.expression.{Application, Operator}
|
import org.enso.compiler.core.ir.expression.{Application, Operator}
|
||||||
import org.enso.compiler.pass.desugar.OperatorToFunction
|
import org.enso.compiler.pass.desugar.OperatorToFunction
|
||||||
import org.enso.compiler.test.CompilerTest
|
import org.enso.compiler.test.CompilerTest
|
||||||
import org.enso.syntax.text.Location
|
|
||||||
|
|
||||||
class OperatorToFunctionTest extends CompilerTest {
|
class OperatorToFunctionTest extends CompilerTest {
|
||||||
|
|
||||||
@ -31,7 +31,7 @@ class OperatorToFunctionTest extends CompilerTest {
|
|||||||
left: Expression,
|
left: Expression,
|
||||||
right: Expression
|
right: Expression
|
||||||
): (Operator.Binary, Application.Prefix) = {
|
): (Operator.Binary, Application.Prefix) = {
|
||||||
val loc = IdentifiedLocation(Location(1, 33))
|
val loc = new IdentifiedLocation(new Location(1, 33))
|
||||||
|
|
||||||
val leftArg = CallArgument.Specified(None, left, left.location)
|
val leftArg = CallArgument.Specified(None, left, left.location)
|
||||||
val rightArg = CallArgument.Specified(None, right, right.location)
|
val rightArg = CallArgument.Specified(None, right, right.location)
|
||||||
|
@ -226,42 +226,41 @@ class LambdaConsolidateTest extends CompilerTest {
|
|||||||
"collapse lambdas with multiple parameters" in {
|
"collapse lambdas with multiple parameters" in {
|
||||||
implicit val inlineContext: InlineContext = mkContext
|
implicit val inlineContext: InlineContext = mkContext
|
||||||
|
|
||||||
val ir: Function.Lambda = Function
|
val ir: Function.Lambda = new Function.Lambda(
|
||||||
.Lambda(
|
List(
|
||||||
|
DefinitionArgument
|
||||||
|
.Specified(
|
||||||
|
Name
|
||||||
|
.Literal("a", isMethod = false, None),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
suspended = false,
|
||||||
|
None
|
||||||
|
),
|
||||||
|
DefinitionArgument.Specified(
|
||||||
|
Name.Literal("b", isMethod = false, None),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
suspended = false,
|
||||||
|
None
|
||||||
|
)
|
||||||
|
),
|
||||||
|
new Function.Lambda(
|
||||||
List(
|
List(
|
||||||
DefinitionArgument
|
|
||||||
.Specified(
|
|
||||||
Name
|
|
||||||
.Literal("a", isMethod = false, None),
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
suspended = false,
|
|
||||||
None
|
|
||||||
),
|
|
||||||
DefinitionArgument.Specified(
|
DefinitionArgument.Specified(
|
||||||
Name.Literal("b", isMethod = false, None),
|
Name
|
||||||
|
.Literal("c", isMethod = false, None),
|
||||||
None,
|
None,
|
||||||
None,
|
None,
|
||||||
suspended = false,
|
suspended = false,
|
||||||
None
|
None
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
Function.Lambda(
|
Name.Literal("c", isMethod = false, None),
|
||||||
List(
|
|
||||||
DefinitionArgument.Specified(
|
|
||||||
Name
|
|
||||||
.Literal("c", isMethod = false, None),
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
suspended = false,
|
|
||||||
None
|
|
||||||
)
|
|
||||||
),
|
|
||||||
Name.Literal("c", isMethod = false, None),
|
|
||||||
None
|
|
||||||
),
|
|
||||||
None
|
None
|
||||||
)
|
),
|
||||||
|
None
|
||||||
|
)
|
||||||
.runPasses(passManager, inlineContext)
|
.runPasses(passManager, inlineContext)
|
||||||
.optimise
|
.optimise
|
||||||
.asInstanceOf[Function.Lambda]
|
.asInstanceOf[Function.Lambda]
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
package org.enso.compiler.test.semantic
|
package org.enso.compiler.test.semantic
|
||||||
|
|
||||||
import org.enso.compiler.core.Implicits.AsMetadata
|
import org.enso.compiler.core.Implicits.AsMetadata
|
||||||
import org.enso.compiler.core.ir.{Module, Warning}
|
import org.enso.compiler.core.ir.{Module, ProcessingPass, Warning}
|
||||||
import org.enso.compiler.core.ir.expression.errors
|
import org.enso.compiler.core.ir.expression.errors
|
||||||
import org.enso.compiler.core.ir.module.scope.Import
|
import org.enso.compiler.core.ir.module.scope.Import
|
||||||
import org.enso.compiler.data.BindingsMap
|
import org.enso.compiler.data.BindingsMap
|
||||||
import org.enso.compiler.pass.analyse.BindingAnalysis
|
import org.enso.compiler.pass.analyse.BindingAnalysis
|
||||||
import org.enso.interpreter.runtime
|
import org.enso.interpreter.runtime
|
||||||
import org.enso.interpreter.runtime.EnsoContext
|
import org.enso.interpreter.runtime.EnsoContext
|
||||||
|
import org.enso.persist.Persistance
|
||||||
import org.enso.pkg.QualifiedName
|
import org.enso.pkg.QualifiedName
|
||||||
import org.enso.polyglot.{LanguageInfo, MethodNames, RuntimeOptions}
|
import org.enso.polyglot.{LanguageInfo, MethodNames, RuntimeOptions}
|
||||||
import org.graalvm.polyglot.{Context, Engine}
|
import org.graalvm.polyglot.{Context, Engine}
|
||||||
@ -15,9 +16,10 @@ import org.scalatest.BeforeAndAfter
|
|||||||
import org.scalatest.matchers.should.Matchers
|
import org.scalatest.matchers.should.Matchers
|
||||||
import org.scalatest.wordspec.AnyWordSpecLike
|
import org.scalatest.wordspec.AnyWordSpecLike
|
||||||
|
|
||||||
import java.io.{ByteArrayOutputStream, ObjectOutputStream}
|
import java.io.ByteArrayOutputStream
|
||||||
import java.nio.file.Paths
|
import java.nio.file.Paths
|
||||||
import java.util.logging.Level
|
import java.util.logging.Level
|
||||||
|
import java.io.IOException
|
||||||
|
|
||||||
/** Tests a single package with multiple modules for import/export resolution.
|
/** Tests a single package with multiple modules for import/export resolution.
|
||||||
* Checks whether the exported symbols and defined entities metadata of the modules
|
* Checks whether the exported symbols and defined entities metadata of the modules
|
||||||
@ -908,13 +910,17 @@ class ImportExportTest
|
|||||||
.diagnostics
|
.diagnostics
|
||||||
.collect({ case w: Warning.DuplicatedImport => w })
|
.collect({ case w: Warning.DuplicatedImport => w })
|
||||||
warn.size shouldEqual 1
|
warn.size shouldEqual 1
|
||||||
val baos = new ByteArrayOutputStream()
|
val arr = Persistance.write(
|
||||||
val stream = new ObjectOutputStream(baos)
|
mainIr,
|
||||||
mainIr.preorder.foreach(
|
{
|
||||||
_.passData.prepareForSerialization(langCtx.getCompiler.context)
|
case metadata: ProcessingPass.Metadata =>
|
||||||
)
|
metadata.prepareForSerialization(
|
||||||
stream.writeObject(mainIr)
|
langCtx.getCompiler.context.asInstanceOf[metadata.Compiler]
|
||||||
baos.toByteArray should not be empty
|
);
|
||||||
|
case obj => obj
|
||||||
|
}
|
||||||
|
);
|
||||||
|
arr should not be empty
|
||||||
}
|
}
|
||||||
|
|
||||||
"serialize ambiguous import error" in {
|
"serialize ambiguous import error" in {
|
||||||
@ -940,13 +946,24 @@ class ImportExportTest
|
|||||||
.reason
|
.reason
|
||||||
.asInstanceOf[errors.ImportExport.AmbiguousImport]
|
.asInstanceOf[errors.ImportExport.AmbiguousImport]
|
||||||
ambiguousImport.symbolName shouldEqual "A_Type"
|
ambiguousImport.symbolName shouldEqual "A_Type"
|
||||||
val baos = new ByteArrayOutputStream()
|
try {
|
||||||
val stream = new ObjectOutputStream(baos)
|
val arr = Persistance.write(
|
||||||
mainIr.preorder.foreach(
|
mainIr,
|
||||||
_.passData.prepareForSerialization(langCtx.getCompiler.context)
|
{
|
||||||
)
|
case metadata: ProcessingPass.Metadata =>
|
||||||
stream.writeObject(mainIr)
|
metadata.prepareForSerialization(
|
||||||
baos.toByteArray should not be empty
|
langCtx.getCompiler.context.asInstanceOf[metadata.Compiler]
|
||||||
|
);
|
||||||
|
case obj => obj
|
||||||
|
}
|
||||||
|
);
|
||||||
|
fail("Shouldn't return anything when there is an error" + arr)
|
||||||
|
} catch {
|
||||||
|
case ex: IOException =>
|
||||||
|
ex.getMessage should equal(
|
||||||
|
"No persistance for org.enso.compiler.core.ir.expression.errors.ImportExport"
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,282 @@
|
|||||||
|
package org.enso.persist.impl;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import javax.annotation.processing.AbstractProcessor;
|
||||||
|
import javax.annotation.processing.Processor;
|
||||||
|
import javax.annotation.processing.RoundEnvironment;
|
||||||
|
import javax.annotation.processing.SupportedAnnotationTypes;
|
||||||
|
import javax.lang.model.SourceVersion;
|
||||||
|
import javax.lang.model.element.AnnotationMirror;
|
||||||
|
import javax.lang.model.element.AnnotationValue;
|
||||||
|
import javax.lang.model.element.Element;
|
||||||
|
import javax.lang.model.element.ElementKind;
|
||||||
|
import javax.lang.model.element.ExecutableElement;
|
||||||
|
import javax.lang.model.element.Modifier;
|
||||||
|
import javax.lang.model.element.PackageElement;
|
||||||
|
import javax.lang.model.element.TypeElement;
|
||||||
|
import javax.lang.model.element.VariableElement;
|
||||||
|
import javax.lang.model.type.TypeMirror;
|
||||||
|
import javax.lang.model.util.SimpleAnnotationValueVisitor9;
|
||||||
|
import javax.tools.Diagnostic.Kind;
|
||||||
|
|
||||||
|
import org.openide.util.lookup.ServiceProvider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Processes the {@code Persistable} annotation. See its javadoc for
|
||||||
|
* proper usage. A new subclass of {@code Persistance} class is generated per each
|
||||||
|
* {@code Persistable} annotation.
|
||||||
|
*/
|
||||||
|
@SupportedAnnotationTypes({
|
||||||
|
"org.enso.persist.Persistable",
|
||||||
|
"org.enso.persist.Persistable.Group"
|
||||||
|
})
|
||||||
|
@ServiceProvider(service=Processor.class)
|
||||||
|
public class PersistableProcessor extends AbstractProcessor {
|
||||||
|
@Override
|
||||||
|
public SourceVersion getSupportedSourceVersion() {
|
||||||
|
return SourceVersion.latest();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
|
||||||
|
var ok = true;
|
||||||
|
var eu = processingEnv.getElementUtils();
|
||||||
|
var Persistable = eu.getTypeElement("org.enso.persist.Persistable");
|
||||||
|
var PersistableGroup = eu.getTypeElement("org.enso.persist.Persistable.Group");
|
||||||
|
try {
|
||||||
|
for (var elem : roundEnv.getElementsAnnotatedWith(Persistable)) {
|
||||||
|
var anno = getAnnotation(elem, Persistable);
|
||||||
|
ok &= generatePersistance(elem, anno);
|
||||||
|
}
|
||||||
|
for (var elem : roundEnv.getElementsAnnotatedWith(PersistableGroup)) {
|
||||||
|
var group = getAnnotation(elem, PersistableGroup);
|
||||||
|
for (var anno : readAnnoArray(group, "value")) {
|
||||||
|
ok &= generatePersistance(elem, anno);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
ok = false;
|
||||||
|
processingEnv.getMessager().printMessage(Kind.ERROR, e.getMessage());
|
||||||
|
}
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String findFqn(Element e) {
|
||||||
|
var inPackage = findNameInPackage(e);
|
||||||
|
var pkg = processingEnv.getElementUtils().getPackageOf(e);
|
||||||
|
return pkg.getQualifiedName() + "." + inPackage;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String findNameInPackage(Element e) {
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
while (e != null && !(e instanceof PackageElement)) {
|
||||||
|
if (!sb.isEmpty()) {
|
||||||
|
sb.insert(0, ".");
|
||||||
|
}
|
||||||
|
sb.insert(0, e.getSimpleName());
|
||||||
|
e = e.getEnclosingElement();
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean generatePersistance(Element orig, AnnotationMirror anno) throws IOException {
|
||||||
|
var eu = processingEnv.getElementUtils();
|
||||||
|
var tu = processingEnv.getTypeUtils();
|
||||||
|
String typeElemName = readAnnoValue(anno, "clazz");
|
||||||
|
if (typeElemName == null) {
|
||||||
|
typeElemName = ((TypeElement) orig).getQualifiedName().toString();
|
||||||
|
}
|
||||||
|
var typeElem = eu.getTypeElement(typeElemName);
|
||||||
|
if (typeElem == null) {
|
||||||
|
processingEnv.getMessager().printMessage(Kind.ERROR, "Cannot find type for " + typeElemName);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var richerConstructor = new Comparator<Object>() {
|
||||||
|
@Override
|
||||||
|
public int compare(Object a, Object b) {
|
||||||
|
var ea = (ExecutableElement)a;
|
||||||
|
var eb = (ExecutableElement)b;
|
||||||
|
|
||||||
|
var diff = eb.getParameters().size() - ea.getParameters().size();
|
||||||
|
if (diff == 0) {
|
||||||
|
diff = countSeq(eb.getParameters()) - countSeq(ea.getParameters());
|
||||||
|
}
|
||||||
|
return diff;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var constructors = typeElem.getEnclosedElements().stream()
|
||||||
|
.filter(
|
||||||
|
e -> e.getModifiers().contains(Modifier.PUBLIC) && e.getKind() == ElementKind.CONSTRUCTOR
|
||||||
|
)
|
||||||
|
.sorted(richerConstructor)
|
||||||
|
.toList();
|
||||||
|
if (constructors.isEmpty()) {
|
||||||
|
processingEnv.getMessager().printMessage(Kind.ERROR, "There should be exactly one constructor in " + typeElem, orig);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var cons = (ExecutableElement) constructors.get(0);
|
||||||
|
if (constructors.size() > 1) {
|
||||||
|
var snd = (ExecutableElement) constructors.get(1);
|
||||||
|
if (richerConstructor.compare(cons, snd) == 0) {
|
||||||
|
processingEnv.getMessager().printMessage(Kind.ERROR, "There should be exactly one 'richest' constructor in " + typeElem, orig);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var pkgName = eu.getPackageOf(orig).getQualifiedName().toString();
|
||||||
|
var className = "Persist" + findNameInPackage(typeElem).replace(".", "_");
|
||||||
|
var fo = processingEnv.getFiler().createSourceFile(pkgName + "." + className, orig);
|
||||||
|
try (var w = fo.openWriter()) {
|
||||||
|
w.append("package ").append(pkgName).append(";\n");
|
||||||
|
w.append("import java.io.IOException;\n");
|
||||||
|
w.append("import org.enso.persist.Persistance;\n");
|
||||||
|
w.append("@org.openide.util.lookup.ServiceProvider(service=Persistance.class)\n");
|
||||||
|
w.append("public final class ").append(className).append(" extends Persistance<").append(typeElemName).append("> {\n");
|
||||||
|
w.append(" public ").append(className).append("() {\n");
|
||||||
|
var id = readAnnoValue(anno, "id").toString(); // Integer.toString(anno.id());
|
||||||
|
w.append(" super(").append(typeElemName).append(".class, false, ").append(id).append(");\n");
|
||||||
|
w.append(" }\n");
|
||||||
|
w.append(" @SuppressWarnings(\"unchecked\")\n");
|
||||||
|
w.append(" protected ").append(typeElemName).append(" readObject(Input in) throws IOException {\n");
|
||||||
|
|
||||||
|
for (var v : cons.getParameters()) {
|
||||||
|
if (tu.isSameType(eu.getTypeElement("java.lang.String").asType(), v.asType())) {
|
||||||
|
w.append(" var ").append(v.getSimpleName()).append(" = in.readUTF();\n");
|
||||||
|
} else if (!v.asType().getKind().isPrimitive()) {
|
||||||
|
var type = tu.erasure(v.asType());
|
||||||
|
var elem = (TypeElement) tu.asElement(type);
|
||||||
|
var name = findFqn(elem);
|
||||||
|
if (shouldInline(elem)) {
|
||||||
|
w.append(" var ").append(v.getSimpleName()).append(" = in.readInline(").append(name).append(".class);\n");
|
||||||
|
} else {
|
||||||
|
w.append(" var ").append(v.getSimpleName()).append(" = (").append(name).append(") in.readObject();\n");
|
||||||
|
}
|
||||||
|
} else switch (v.asType().getKind()) {
|
||||||
|
case BOOLEAN ->
|
||||||
|
w.append(" var ").append(v.getSimpleName()).append(" = in.readBoolean();\n");
|
||||||
|
case INT ->
|
||||||
|
w.append(" var ").append(v.getSimpleName()).append(" = in.readInt();\n");
|
||||||
|
default ->
|
||||||
|
processingEnv.getMessager().printMessage(Kind.ERROR, "Unsupported primitive type: " + v.asType().getKind());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
w.append(" return new ").append(typeElemName).append("(\n");
|
||||||
|
w.append(" ");
|
||||||
|
{
|
||||||
|
var sep = "";
|
||||||
|
for (var v : cons.getParameters()) {
|
||||||
|
w.append(sep);
|
||||||
|
w.append(v.getSimpleName());
|
||||||
|
sep = ", ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
w.append("\n");
|
||||||
|
w.append(" );\n");
|
||||||
|
w.append(" }\n");
|
||||||
|
w.append(" @SuppressWarnings(\"unchecked\")\n");
|
||||||
|
w.append(" protected void writeObject(").append(typeElemName).append(" obj, Output out) throws IOException {\n");
|
||||||
|
|
||||||
|
for (var v : cons.getParameters()) {
|
||||||
|
if (tu.isSameType(eu.getTypeElement("java.lang.String").asType(), v.asType())) {
|
||||||
|
w.append(" out.writeUTF(obj.").append(v.getSimpleName()).append("());\n");
|
||||||
|
} else if (!v.asType().getKind().isPrimitive()) {
|
||||||
|
var type = tu.erasure(v.asType());
|
||||||
|
var elem = (TypeElement) tu.asElement(type);
|
||||||
|
var name = findFqn(elem);
|
||||||
|
if (shouldInline(elem)) {
|
||||||
|
w.append(" out.writeInline(").append(name).append(".class, obj.").append(v.getSimpleName()).append("());\n");
|
||||||
|
} else {
|
||||||
|
w.append(" out.writeObject(obj.").append(v.getSimpleName()).append("());\n");
|
||||||
|
}
|
||||||
|
} else switch (v.asType().getKind()) {
|
||||||
|
case BOOLEAN ->
|
||||||
|
w.append(" out.writeBoolean(obj.").append(v.getSimpleName()).append("());\n");
|
||||||
|
case INT ->
|
||||||
|
w.append(" out.writeInt(obj.").append(v.getSimpleName()).append("());\n");
|
||||||
|
default ->
|
||||||
|
processingEnv.getMessager().printMessage(Kind.ERROR, "Unsupported primitive type: " + v.asType().getKind());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
w.append(" }\n");
|
||||||
|
w.append("}\n");
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int countSeq(List<? extends VariableElement> parameters) {
|
||||||
|
var tu = processingEnv.getTypeUtils();
|
||||||
|
var cnt = 0;
|
||||||
|
for (var p : parameters) {
|
||||||
|
var type = tu.asElement(tu.erasure(p.asType()));
|
||||||
|
if (type != null && type.getSimpleName().toString().equals("Seq")) {
|
||||||
|
cnt++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean shouldInline(TypeElement elem) {
|
||||||
|
var inline = switch (findFqn(elem)) {
|
||||||
|
case "scala.collection.immutable.Seq" -> true;
|
||||||
|
default -> false;
|
||||||
|
} || !elem.getKind().isInterface();
|
||||||
|
return inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
private AnnotationMirror getAnnotation(Element elem, TypeElement annoType) {
|
||||||
|
var tu = processingEnv.getTypeUtils();
|
||||||
|
for (var m : elem.getAnnotationMirrors()) {
|
||||||
|
var realType = m.getAnnotationType();
|
||||||
|
if (tu.isSameType(realType, annoType.asType())) {
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String readAnnoValue(AnnotationMirror mirror, String name) {
|
||||||
|
for (var entry : mirror.getElementValues().entrySet()) {
|
||||||
|
if (name.equals(entry.getKey().getSimpleName().toString())) {
|
||||||
|
return entry.getValue().accept(new SimpleAnnotationValueVisitor9<String, Object>() {
|
||||||
|
@Override
|
||||||
|
public String visitInt(int i, Object p) {
|
||||||
|
return Integer.toString(i);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public String visitType(TypeMirror t, Object p) {
|
||||||
|
var e = (TypeElement) processingEnv.getTypeUtils().asElement(t);
|
||||||
|
return e.getQualifiedName().toString();
|
||||||
|
}
|
||||||
|
}, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<AnnotationMirror> readAnnoArray(AnnotationMirror mirror, String name) {
|
||||||
|
for (var entry : mirror.getElementValues().entrySet()) {
|
||||||
|
if (name.equals(entry.getKey().getSimpleName().toString())) {
|
||||||
|
return entry.getValue().accept(new SimpleAnnotationValueVisitor9<List<AnnotationMirror>, List<AnnotationMirror>>() {
|
||||||
|
@Override
|
||||||
|
public List<AnnotationMirror> visitArray(List<? extends AnnotationValue> vals, List<AnnotationMirror> p) {
|
||||||
|
for (var v : vals) {
|
||||||
|
v.accept(this, p);
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<AnnotationMirror> visitAnnotation(AnnotationMirror a, List<AnnotationMirror> p) {
|
||||||
|
p.add(a);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
}, new ArrayList<>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
package org.enso.persist;
|
||||||
|
|
||||||
|
import org.enso.persist.PerInputImpl.InputCache;
|
||||||
|
import org.enso.persist.Persistance.Reference;
|
||||||
|
|
||||||
|
final class PerBufferReference<T> extends Persistance.Reference<T> {
|
||||||
|
private final Persistance<T> p;
|
||||||
|
private final PerInputImpl.InputCache cache;
|
||||||
|
private final int offset;
|
||||||
|
|
||||||
|
private PerBufferReference(Persistance<T> p, PerInputImpl.InputCache buffer, int offset) {
|
||||||
|
this.p = p;
|
||||||
|
this.cache = buffer;
|
||||||
|
this.offset = offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings(value = "unchecked")
|
||||||
|
final <T> T readObject(Class<T> clazz) {
|
||||||
|
if (p != null) {
|
||||||
|
if (clazz.isAssignableFrom(p.clazz)) {
|
||||||
|
clazz = (Class) p.clazz;
|
||||||
|
} else {
|
||||||
|
throw new ClassCastException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
org.enso.persist.PerInputImpl in = new PerInputImpl(cache, offset);
|
||||||
|
T obj = in.readInline(clazz);
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
static <V> Reference<V> from(InputCache buffer, int offset) {
|
||||||
|
return from(null, buffer, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
static <V> Reference<V> from(Persistance<V> p, InputCache buffer, int offset) {
|
||||||
|
return new PerBufferReference<>(p, buffer, offset);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,107 @@
|
|||||||
|
package org.enso.persist;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.IdentityHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
final class PerGenerator {
|
||||||
|
|
||||||
|
static final byte[] HEADER = new byte[] {0x0a, 0x0d, 0x02, 0x0f};
|
||||||
|
private final OutputStream main;
|
||||||
|
private final Map<Object, Integer> knownObjects = new IdentityHashMap<>();
|
||||||
|
final Function<Object, Object> writeReplace;
|
||||||
|
private int position;
|
||||||
|
|
||||||
|
private PerGenerator(OutputStream out, int position, Function<Object, Object> writeReplace) {
|
||||||
|
this.main = out;
|
||||||
|
this.writeReplace = writeReplace == null ? Function.identity() : writeReplace;
|
||||||
|
this.position = position;
|
||||||
|
}
|
||||||
|
|
||||||
|
static byte[] writeObject(Object obj, Function<Object, Object> writeReplace) throws IOException {
|
||||||
|
var out = new ByteArrayOutputStream();
|
||||||
|
var data = new DataOutputStream(out);
|
||||||
|
data.write(PerGenerator.HEADER);
|
||||||
|
data.writeInt(PerMap.DEFAULT.versionStamp);
|
||||||
|
data.write(new byte[4]); // space
|
||||||
|
data.flush();
|
||||||
|
var g = new PerGenerator(out, 12, writeReplace);
|
||||||
|
var at = g.writeObject(obj);
|
||||||
|
var arr = out.toByteArray();
|
||||||
|
arr[8] = (byte) ((at >> 24) & 0xff);
|
||||||
|
arr[9] = (byte) ((at >> 16) & 0xff);
|
||||||
|
arr[10] = (byte) ((at >> 8) & 0xff);
|
||||||
|
arr[11] = (byte) (at & 0xff);
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
final <T> int writeObject(T t) throws IOException {
|
||||||
|
if (t == null) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
java.lang.Object obj = writeReplace.apply(t);
|
||||||
|
java.lang.Integer found = knownObjects.get(obj);
|
||||||
|
if (found == null) {
|
||||||
|
org.enso.persist.Persistance<?> p = PerMap.DEFAULT.forType(obj.getClass());
|
||||||
|
java.io.ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||||
|
p.writeInline(obj, new ReferenceOutput(this, os));
|
||||||
|
found = this.position;
|
||||||
|
byte[] arr = os.toByteArray();
|
||||||
|
main.write(arr);
|
||||||
|
this.position += arr.length;
|
||||||
|
knownObjects.put(obj, found);
|
||||||
|
}
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
final void writeIndirect(Object obj, Persistance.Output out) throws IOException {
|
||||||
|
if (obj == null) {
|
||||||
|
out.writeInt(-1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
obj = writeReplace.apply(obj);
|
||||||
|
org.enso.persist.Persistance<?> p = PerMap.DEFAULT.forType(obj.getClass());
|
||||||
|
java.lang.Integer found = knownObjects.get(obj);
|
||||||
|
if (found == null) {
|
||||||
|
var os = new ByteArrayOutputStream();
|
||||||
|
var osData = new ReferenceOutput(this, os);
|
||||||
|
p.writeInline(obj, osData);
|
||||||
|
found = position;
|
||||||
|
if (os.size() == 0) {
|
||||||
|
os.write(0);
|
||||||
|
}
|
||||||
|
byte[] arr = os.toByteArray();
|
||||||
|
main.write(arr);
|
||||||
|
position += arr.length;
|
||||||
|
knownObjects.put(obj, found);
|
||||||
|
}
|
||||||
|
out.writeInt(found);
|
||||||
|
out.writeInt(p.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class ReferenceOutput extends DataOutputStream
|
||||||
|
implements Persistance.Output {
|
||||||
|
private final PerGenerator generator;
|
||||||
|
|
||||||
|
ReferenceOutput(PerGenerator g, ByteArrayOutputStream out) {
|
||||||
|
super(out);
|
||||||
|
this.generator = g;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> void writeInline(Class<T> clazz, T t) throws IOException {
|
||||||
|
var obj = generator.writeReplace.apply(t);
|
||||||
|
var p = PerMap.DEFAULT.forType(clazz);
|
||||||
|
p.writeInline(obj, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeObject(Object obj) throws IOException {
|
||||||
|
this.generator.writeIndirect(obj, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,230 @@
|
|||||||
|
package org.enso.persist;
|
||||||
|
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import static org.enso.persist.PerUtils.raise;
|
||||||
|
import org.enso.persist.Persistance.Input;
|
||||||
|
import org.enso.persist.Persistance.Reference;
|
||||||
|
|
||||||
|
final class PerInputImpl implements Input {
|
||||||
|
private final InputCache cache;
|
||||||
|
private final ByteBuffer buf;
|
||||||
|
private int at;
|
||||||
|
|
||||||
|
PerInputImpl(InputCache cache, int at) {
|
||||||
|
this.cache = cache;
|
||||||
|
this.buf = cache.buf();
|
||||||
|
this.at = at;
|
||||||
|
}
|
||||||
|
|
||||||
|
static <T> Reference<T> readObject(byte[] arr, Function<Object, Object> readResolve) throws IOException {
|
||||||
|
for (var i = 0; i < PerGenerator.HEADER.length; i++) {
|
||||||
|
if (arr[i] != PerGenerator.HEADER[i]) {
|
||||||
|
throw new IOException("Wrong header");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var buf = ByteBuffer.wrap(arr);
|
||||||
|
var version = buf.getInt(4);
|
||||||
|
if (version != PerMap.DEFAULT.versionStamp) {
|
||||||
|
throw new IOException("Incompatible version " + version + " != " + PerMap.DEFAULT.versionStamp);
|
||||||
|
}
|
||||||
|
var at = buf.getInt(8);
|
||||||
|
var cache = new InputCache(buf, readResolve);
|
||||||
|
|
||||||
|
return PerBufferReference.from(cache, at);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> T readInline(Class<T> clazz) {
|
||||||
|
Persistance<T> p = PerMap.DEFAULT.forType(clazz);
|
||||||
|
T res = p.readWith(this);
|
||||||
|
var resolve = cache.readResolve().apply(res);
|
||||||
|
return clazz.cast(resolve);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object readObject() throws IOException {
|
||||||
|
var obj = readIndirect(cache, PerMap.DEFAULT, this);
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> Persistance.Reference<T> readReference(Class<T> clazz) throws IOException {
|
||||||
|
var obj = readIndirectAsReference(cache, PerMap.DEFAULT, this, clazz);
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int read() throws IOException {
|
||||||
|
return buf.get(at++);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int read(byte[] b) throws IOException {
|
||||||
|
buf.get(at, b);
|
||||||
|
at += b.length;
|
||||||
|
return b.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int read(byte[] b, int off, int len) throws IOException {
|
||||||
|
buf.get(at, b, off, len);
|
||||||
|
at += len;
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long skip(long n) throws IOException {
|
||||||
|
at += Math.toIntExact(n);
|
||||||
|
return at;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int available() throws IOException {
|
||||||
|
return buf.limit() - at;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void close() throws IOException {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void readFully(byte[] b) throws IOException {
|
||||||
|
read(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void readFully(byte[] b, int off, int len) throws IOException {
|
||||||
|
read(b, off, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int skipBytes(int n) throws IOException {
|
||||||
|
at += n;
|
||||||
|
return at;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean readBoolean() throws IOException {
|
||||||
|
int b = read();
|
||||||
|
return b != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte readByte() throws IOException {
|
||||||
|
return buf.get(at++);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int readUnsignedByte() throws IOException {
|
||||||
|
byte b = readByte();
|
||||||
|
return b >= 0 ? b : b + 256;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public short readShort() throws IOException {
|
||||||
|
short s = buf.getShort(at);
|
||||||
|
at += 2;
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int readUnsignedShort() throws IOException {
|
||||||
|
short s = readShort();
|
||||||
|
return s >= 0 ? s : s + 256 * 256;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public char readChar() throws IOException {
|
||||||
|
return (char) readShort();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int readInt() throws IOException {
|
||||||
|
int i = buf.getInt(at);
|
||||||
|
at += 4;
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long readLong() throws IOException {
|
||||||
|
long l = buf.getLong(at);
|
||||||
|
at += 8;
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float readFloat() throws IOException {
|
||||||
|
float f = buf.getFloat(at);
|
||||||
|
at += 4;
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double readDouble() throws IOException {
|
||||||
|
double d = buf.getDouble(at);
|
||||||
|
at += 8;
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String readLine() throws IOException {
|
||||||
|
throw new IOException("No readLine in buffer");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String readUTF() throws IOException {
|
||||||
|
return DataInputStream.readUTF(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Input[at=" + at + " of " + this.buf.limit() + "]";
|
||||||
|
}
|
||||||
|
|
||||||
|
static Object readIndirect(InputCache cache, PerMap map, Input in) throws IOException {
|
||||||
|
var at = in.readInt();
|
||||||
|
if (at < 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var id = in.readInt();
|
||||||
|
var p = map.forId(id);
|
||||||
|
|
||||||
|
if (cache.cache().get(at) instanceof Object res) {
|
||||||
|
return p.clazz.cast(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
var inData = new PerInputImpl(cache, at);
|
||||||
|
var res = p.readWith(inData);
|
||||||
|
res = cache.readResolve().apply(res);
|
||||||
|
var prev = cache.cache().put(at, res);
|
||||||
|
if (prev != null) {
|
||||||
|
throw raise(RuntimeException.class, new IOException("Adding at " + at + " object: " + res.getClass().getName() + " but there already is " + prev.getClass().getName()));
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
static <T> Reference<T> readIndirectAsReference(InputCache buffer, PerMap map, Input in, Class<T> clazz) throws IOException {
|
||||||
|
var at = in.readInt();
|
||||||
|
if (at < 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var id = in.readInt();
|
||||||
|
var p = map.forId(id);
|
||||||
|
if (clazz.isAssignableFrom(p.clazz)) {
|
||||||
|
return PerBufferReference.from((Persistance<T>) p, buffer, at);
|
||||||
|
} else {
|
||||||
|
throw new IOException("Expecting " + clazz.getName() + " but found " + p.clazz.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static final record InputCache(
|
||||||
|
ByteBuffer buf,
|
||||||
|
Function<Object, Object> readResolve,
|
||||||
|
Map<Integer, Object> cache
|
||||||
|
) {
|
||||||
|
InputCache(ByteBuffer buf, Function<Object, Object> readResolve) {
|
||||||
|
this(buf, readResolve == null ? Function.identity() : readResolve, new HashMap<>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,85 @@
|
|||||||
|
package org.enso.persist;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import org.openide.util.lookup.Lookups;
|
||||||
|
|
||||||
|
final class PerMap {
|
||||||
|
|
||||||
|
static final PerMap DEFAULT = new PerMap();
|
||||||
|
private final Map<Integer, Persistance<?>> ids = new HashMap<>();
|
||||||
|
private final Map<Class<?>, Persistance<?>> types = new HashMap<>();
|
||||||
|
final int versionStamp;
|
||||||
|
|
||||||
|
private PerMap() {
|
||||||
|
int hash = 0;
|
||||||
|
var loader = getClass().getClassLoader();
|
||||||
|
for (var p : Lookups.metaInfServices(loader).lookupAll(Persistance.class)) {
|
||||||
|
org.enso.persist.Persistance<?> prevId = ids.put(p.id, p);
|
||||||
|
if (prevId != null) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Multiple registrations for ID " + p.id + " " + prevId + " != " + p);
|
||||||
|
}
|
||||||
|
hash += p.id;
|
||||||
|
org.enso.persist.Persistance<?> prevType = types.put(p.clazz, p);
|
||||||
|
if (prevType != null) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Multiple registrations for " + p.clazz.getName() + " " + prevId + " != " + p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
versionStamp = hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings(value = "unchecked")
|
||||||
|
private synchronized <T> Persistance<T> searchSupertype(String name, Class<T> type) {
|
||||||
|
// synchronized as it mutes the types map
|
||||||
|
// however over time the types map gets saturated and
|
||||||
|
// the synchronization will get less frequent
|
||||||
|
// please note that Persistance as well as Class (as a key) have all fields final =>
|
||||||
|
// as soon as they become visible from other threads, they have to look consistent
|
||||||
|
NOT_FOUND:
|
||||||
|
if (type != null) {
|
||||||
|
org.enso.persist.Persistance<?> p = types.get(type);
|
||||||
|
if (p != null) {
|
||||||
|
if (!p.includingSubclasses) {
|
||||||
|
break NOT_FOUND;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (java.lang.Class<?> in : type.getInterfaces()) {
|
||||||
|
p = searchSupertype(name, in);
|
||||||
|
if (p != null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (p == null && !type.isInterface()) {
|
||||||
|
p = searchSupertype(name, type.getSuperclass());
|
||||||
|
}
|
||||||
|
types.put(type, p);
|
||||||
|
}
|
||||||
|
return (Persistance<T>) p;
|
||||||
|
}
|
||||||
|
if (type == null) {
|
||||||
|
throw PerUtils.raise(RuntimeException.class, new IOException("No persistance for " + name));
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings(value = "unchecked")
|
||||||
|
final <T> Persistance<T> forType(Class<T> type) {
|
||||||
|
org.enso.persist.Persistance<?> p = types.get(type);
|
||||||
|
if (p == null) {
|
||||||
|
p = searchSupertype(type.getName(), type);
|
||||||
|
}
|
||||||
|
return (Persistance<T>) p;
|
||||||
|
}
|
||||||
|
|
||||||
|
final Persistance<?> forId(int id) {
|
||||||
|
org.enso.persist.Persistance<?> p = ids.get(id);
|
||||||
|
if (p == null) {
|
||||||
|
throw PerUtils.raise(RuntimeException.class, new IOException("No persistance for " + id));
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
package org.enso.persist;
|
||||||
|
|
||||||
|
final class PerMemoryReference<T> extends Persistance.Reference<T> {
|
||||||
|
static final Persistance.Reference<?> NULL = new PerMemoryReference<>(null);
|
||||||
|
private final T value;
|
||||||
|
|
||||||
|
PerMemoryReference(T obj) {
|
||||||
|
this.value = obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
final T value() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
package org.enso.persist;
|
||||||
|
|
||||||
|
final class PerUtils {
|
||||||
|
private PerUtils() {}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
static <E extends Throwable> E raise(Class<E> clazz, Throwable t) throws E {
|
||||||
|
throw (E) t;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,51 @@
|
|||||||
|
package org.enso.persist;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Repeatable;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Annotation for an automatic persistance of a class. Use to generate implementation and
|
||||||
|
* registration of {@link Persistance} subclass to read and write simple records and case classes:
|
||||||
|
*
|
||||||
|
* <p>{@snippet file="org/enso/persist/PersistanceTest.java" region="annotation"}
|
||||||
|
*/
|
||||||
|
@Target(ElementType.TYPE)
|
||||||
|
@Repeatable(Persistable.Group.class)
|
||||||
|
public @interface Persistable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The class to generate {@link Persistance} for. If the value is omitted then the code is
|
||||||
|
* generated for the class that is annotated by this annotation. Example of multiple
|
||||||
|
* {@code @Persistable} annotations on a single element.
|
||||||
|
*
|
||||||
|
* <p>{@snippet file="org/enso/persist/PersistanceTest.java" region="annotation"}
|
||||||
|
*
|
||||||
|
* <p>Example of self annotated class:
|
||||||
|
*
|
||||||
|
* <p>{@snippet file="org/enso/persist/PersistanceTest.java" region="self-annotation"}
|
||||||
|
*
|
||||||
|
* @return the class to generate read/write persistance code for
|
||||||
|
*/
|
||||||
|
Class<?> clazz() default Object.class;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ID of the class. Each registered {@link Persistance} sub class must have a unique ID. This
|
||||||
|
* attribute specifies it for the generated code. When serialization format changes (for example
|
||||||
|
* by changing the number or type of constructor arguments), change also the ID.
|
||||||
|
*
|
||||||
|
* @return unique ID for the persisted {@link #clazz()}
|
||||||
|
*/
|
||||||
|
int id();
|
||||||
|
|
||||||
|
/** Multiple {@link Persistable} annotations. */
|
||||||
|
@Target(ElementType.TYPE)
|
||||||
|
public @interface Group {
|
||||||
|
/**
|
||||||
|
* The array of {@link Persistable} annotations.
|
||||||
|
*
|
||||||
|
* @return the array of annotations.
|
||||||
|
*/
|
||||||
|
Persistable[] value();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,220 @@
|
|||||||
|
package org.enso.persist;
|
||||||
|
|
||||||
|
import java.io.DataInput;
|
||||||
|
import java.io.DataOutput;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import static org.enso.persist.PerUtils.raise;
|
||||||
|
|
||||||
|
/** Central persistance class. Use static {@link
|
||||||
|
* Persistance#write write} method to turn a graph of JVM objects into a {@code byte[]}.
|
||||||
|
*
|
||||||
|
* <p>{@snippet file="org/enso/persist/PersistanceTest.java" region="write"}
|
||||||
|
*
|
||||||
|
* <p>Use sibling static {@link Persistance#read readO} method to read the byte buffer back into
|
||||||
|
* their memory representation.
|
||||||
|
*
|
||||||
|
* <p>{@snippet file="org/enso/persist/PersistanceTest.java" region="read"}
|
||||||
|
*
|
||||||
|
* <h2>Manual Persistance</h2>
|
||||||
|
*
|
||||||
|
* Unlike typical Java serialization (which tries to make things automatic), this framework requires
|
||||||
|
* one to implement the persistance manually. For each class that one wants to support, one has to
|
||||||
|
* implement subclass {@link Persistance} and implement its {@link Persistance#writeObject} and
|
||||||
|
* {@link Persistance#readObject} method.
|
||||||
|
*
|
||||||
|
* <p>{@snippet file="org/enso/persist/PersistanceTest.java" region="manual"}
|
||||||
|
*
|
||||||
|
* There is a semi-automatic way to generate such subclasses of {@link Persistance} via
|
||||||
|
* the {@link Persistable @Persistable} annotation.
|
||||||
|
* @param <T>
|
||||||
|
*/
|
||||||
|
public abstract class Persistance<T> {
|
||||||
|
final Class<T> clazz;
|
||||||
|
final boolean includingSubclasses;
|
||||||
|
final int id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for subclasses to register persistance for certain
|
||||||
|
* {@code clazz}. Sample registration:
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* {@snippet file="org/enso/persist/PersistanceTest.java" region="manual"}
|
||||||
|
*
|
||||||
|
* Each persistance requires unique ID. A stream created by
|
||||||
|
* {@link #write(Object, Function<Object, Object>)} and read by
|
||||||
|
* {@link #read(byte[], Function<Object, Object>)} contains a header derived
|
||||||
|
* from the all the IDs present in the system. When versioning the protocol
|
||||||
|
* and implementation:
|
||||||
|
* <ul>
|
||||||
|
* <li>when you change something really core in the Persitance itself -
|
||||||
|
* change the header</li>
|
||||||
|
* <li>when you add or remove a Persistance implementation the version
|
||||||
|
* changes (computed from all the IDs)</li>
|
||||||
|
* <li>when you change format of some Persitance.writeObject method - change
|
||||||
|
* its ID</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @param clazz the class persistance is written for
|
||||||
|
* @param includingSubclasses should the persistance also apply to
|
||||||
|
* subclasses
|
||||||
|
* @param id unique ID
|
||||||
|
*/
|
||||||
|
protected Persistance(Class<T> clazz, boolean includingSubclasses, int id) {
|
||||||
|
this.clazz = clazz;
|
||||||
|
this.includingSubclasses = includingSubclasses;
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void writeObject(T obj, Output out) throws IOException;
|
||||||
|
protected abstract T readObject(Input in) throws IOException, ClassNotFoundException;
|
||||||
|
|
||||||
|
/** Prints the {@code clazz} and {@code id} values.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public final String toString() {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("Persistance{");
|
||||||
|
sb.append("clazz=").append(clazz.getName());
|
||||||
|
sb.append(", id=").append(id);
|
||||||
|
sb.append('}');
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Extended output interface for {@link #writeObject(Object)} method.
|
||||||
|
*/
|
||||||
|
public static interface Output extends DataOutput {
|
||||||
|
/** Writes an object "inline" - as a value.
|
||||||
|
*
|
||||||
|
* @param <T> the type of the class
|
||||||
|
* @param clazz the class to use to locate {@link Persistance} implementation
|
||||||
|
* @param obj the object to write down
|
||||||
|
* @throws IOException when an I/O problem happens
|
||||||
|
*/
|
||||||
|
public abstract <T> void writeInline(Class<T> clazz, T obj) throws IOException;
|
||||||
|
|
||||||
|
/** Writes an object as a "reference". Objects written by by this method
|
||||||
|
* are shared - each {@code obj} is stored only once and referenced from
|
||||||
|
* all the locations it is used.
|
||||||
|
*
|
||||||
|
* @param obj the object to write down
|
||||||
|
* @throws IOException when an I/O problem happens
|
||||||
|
*/
|
||||||
|
public abstract void writeObject(Object obj) throws IOException;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Extended input interface for the {@link #writeObject(T, Output)} method.
|
||||||
|
*/
|
||||||
|
public static interface Input extends DataInput {
|
||||||
|
/** Reads objects written down by {@link Output#writeInline}.
|
||||||
|
*
|
||||||
|
* @param <T> the type to read
|
||||||
|
* @param clazz class that identifies {@link Persistance} to use for reading
|
||||||
|
* @return the read in object
|
||||||
|
* @throws IOException when an I/O problem happens
|
||||||
|
*/
|
||||||
|
public abstract <T> T readInline(Class<T> clazz) throws IOException;
|
||||||
|
|
||||||
|
/** Reads a reference to object written down by {@link Output#writeObject(Object)}.
|
||||||
|
*
|
||||||
|
* @return the read in object
|
||||||
|
* @throws IOException when an I/O problem happens
|
||||||
|
*/
|
||||||
|
public abstract Object readObject() throws IOException;
|
||||||
|
|
||||||
|
/** * Reads a reference to an object written down by {@link Output#writeObject(Object)}
|
||||||
|
* but without reading the object itself. The object can then be obtained
|
||||||
|
* <em>"later"</em> via the {@link Reference#get(Class)} method.
|
||||||
|
*
|
||||||
|
* @param <T> the type to read
|
||||||
|
* @param clazz the expected type of the object to read
|
||||||
|
* @return a reference allowing to read the object later
|
||||||
|
* @throws IOException when an I/O problem happens
|
||||||
|
*/
|
||||||
|
public <T> Reference<T> readReference(Class<T> clazz) throws IOException;
|
||||||
|
}
|
||||||
|
|
||||||
|
final void writeInline(Object obj, Output out) throws IOException {
|
||||||
|
writeObject(clazz.cast(obj), out);
|
||||||
|
}
|
||||||
|
|
||||||
|
final T readWith(Input in) {
|
||||||
|
try {
|
||||||
|
return readObject(in);
|
||||||
|
} catch (IOException | ClassNotFoundException ex) {
|
||||||
|
throw raise(RuntimeException.class, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Read object written down by {@link #write} from an array.
|
||||||
|
*
|
||||||
|
* <p>{@snippet file="org/enso/persist/PersistanceTest.java" region="read"}
|
||||||
|
*
|
||||||
|
* @param <T> expected type of object
|
||||||
|
* @param arr the stored bytes
|
||||||
|
* @param readResolve either {@code null} or function to call for each object being stored to provide a replacement
|
||||||
|
* @return the read object
|
||||||
|
* @throws java.io.IOException when an I/O problem happens
|
||||||
|
*/
|
||||||
|
public static <T> Reference<T> read(byte[] arr, Function<Object, Object> readResolve) throws IOException {
|
||||||
|
return PerInputImpl.readObject(arr, readResolve);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Writes down an object into an array of bytes. Use {@link #read} method to convert the
|
||||||
|
* array back to object.
|
||||||
|
*
|
||||||
|
* @param obj the object to persist
|
||||||
|
* @param writeReplace {@code null} or a function that allows to convert each object before storing it down
|
||||||
|
* @return the array of bytes
|
||||||
|
* @throws IOException when an I/O problem happens
|
||||||
|
*/
|
||||||
|
public static byte[] write(Object obj, Function<Object, Object> writeReplace) throws IOException {
|
||||||
|
return PerGenerator.writeObject(obj, writeReplace);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Reference to an object. Either created directly or obtained from {@link Input#readReference}
|
||||||
|
* method.
|
||||||
|
*
|
||||||
|
* @see Input#readReference
|
||||||
|
*/
|
||||||
|
public static sealed abstract class Reference<T> permits PerBufferReference, PerMemoryReference {
|
||||||
|
Reference() {
|
||||||
|
}
|
||||||
|
/** Reference to {@code null} object.
|
||||||
|
* @param <T> the type of the reference
|
||||||
|
* @return reference to {@code null}
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static final <T> Reference<T> none() {
|
||||||
|
return (Reference<T>) PerMemoryReference.NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Extract object from the reference. Multiple calls to this method
|
||||||
|
* may return the same or another instance of object depending on the
|
||||||
|
* type of the reference and <em>laziness policy</em>.
|
||||||
|
*
|
||||||
|
* @param <V> the type of the object to expect
|
||||||
|
* @param expectedType the expected clazz of the object
|
||||||
|
* @return the referenced object
|
||||||
|
* @throws ClassCastException if the object isn't of the expected type
|
||||||
|
*/
|
||||||
|
public <V> V get(Class<V> expectedType) {
|
||||||
|
var value = switch (this) {
|
||||||
|
case PerMemoryReference m -> m.value();
|
||||||
|
case PerBufferReference<T> b -> b.readObject(expectedType);
|
||||||
|
};
|
||||||
|
return expectedType.cast(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Creates a reference to existing object.
|
||||||
|
*
|
||||||
|
* @param <V> the type of the object
|
||||||
|
* @param obj the object to "reference"
|
||||||
|
* @return reference pointing to the provided object
|
||||||
|
*/
|
||||||
|
public static <V> Reference<V> of(V obj) {
|
||||||
|
return new PerMemoryReference<>(obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
/**
|
||||||
|
* Framework for persisting Java objects and reading them <em>"lazily"</em>. Use static {@link
|
||||||
|
* Persistance#write write} method to turn a graph of JVM objects into a {@code byte[]}.
|
||||||
|
*
|
||||||
|
* <p>{@snippet file="org/enso/persist/PersistanceTest.java" region="write"}
|
||||||
|
*
|
||||||
|
* <p>Use sibling static {@link Persistance#read readO} method to read the byte buffer back into
|
||||||
|
* their memory representation.
|
||||||
|
*
|
||||||
|
* <p>{@snippet file="org/enso/persist/PersistanceTest.java" region="read"}
|
||||||
|
*
|
||||||
|
* <h2>Laziness</h2>
|
||||||
|
*
|
||||||
|
* The major benefit of this framework is the ability to read only the part of the stored graph that
|
||||||
|
* is actually needed. One may {@linkplain Persistance.Input#readReference obtain a reference} to an
|
||||||
|
* object and turn that {@link Persistance.Reference} into actual object <b>later</b>, when needed.
|
||||||
|
*
|
||||||
|
* <h2>Manual Persistance</h2>
|
||||||
|
*
|
||||||
|
* Unlike typical Java serialization (which tries to make things automatic), this framework requires
|
||||||
|
* one to implement the persistance manually. For each class that one wants to support, one has to
|
||||||
|
* implement subclass {@link Persistance} and implement its {@link Persistance#writeObject} and
|
||||||
|
* {@link Persistance#readObject} method.
|
||||||
|
*
|
||||||
|
* <p>{@snippet file="org/enso/persist/PersistanceTest.java" region="manual"}
|
||||||
|
*
|
||||||
|
* <h2>Semi-automatic Persistance</h2>
|
||||||
|
*
|
||||||
|
* Writing serialization and deserialization implementation manually is a boring, tedious and
|
||||||
|
* errorprone process. That's why there is a {@link Persistable} annotation and associated
|
||||||
|
* annotation processor that generates the necessary {@link Persistance} subclass based on the
|
||||||
|
* "richest" constructor in the class to be persisted. This approach seems to work well for Java
|
||||||
|
* records and Scala case classes.
|
||||||
|
*
|
||||||
|
* <p>{@snippet file="org/enso/persist/PersistanceTest.java" region="annotation"}
|
||||||
|
*/
|
||||||
|
package org.enso.persist;
|
@ -0,0 +1,167 @@
|
|||||||
|
package org.enso.compiler.core;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import org.enso.persist.Persistable;
|
||||||
|
import org.enso.persist.Persistance;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.openide.util.lookup.ServiceProvider;
|
||||||
|
|
||||||
|
public class PersistanceTest {
|
||||||
|
@Test
|
||||||
|
public void testUUIDPersistance() throws Exception {
|
||||||
|
// @start region="write"
|
||||||
|
var obj = UUID.randomUUID();
|
||||||
|
var buffer = Persistance.write(obj, null);
|
||||||
|
assertNotNull("Byte array is returned", buffer);
|
||||||
|
assertNotEquals("It has non-zero length", 0, buffer.length);
|
||||||
|
// @end region="write"
|
||||||
|
|
||||||
|
// @start region="read"
|
||||||
|
var ref = Persistance.read(buffer, null);
|
||||||
|
var loaded = ref.get(UUID.class);
|
||||||
|
assertEquals("The same object was recreated", obj, loaded);
|
||||||
|
// @end region="read"
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void readResolve() throws Exception {
|
||||||
|
var in = new Service(5);
|
||||||
|
var arr = Persistance.write(in, (Function<Object, Object>) null);
|
||||||
|
|
||||||
|
var plain = Persistance.read(arr, (Function<Object, Object>) null);
|
||||||
|
assertEquals("Remains five", 5, plain.get(Service.class).value());
|
||||||
|
|
||||||
|
var multiOnRead = Persistance.read(arr, (obj) -> obj instanceof Service s ? new Service(s.value() * 3) : obj);
|
||||||
|
assertEquals("Multiplied on read", 15, multiOnRead.get(Service.class).value());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void writeReplace() throws Exception {
|
||||||
|
var in = new Service(5);
|
||||||
|
var arr = Persistance.write(in, (obj) -> obj instanceof Service s ? new Service(s.value() * 3) : obj);
|
||||||
|
|
||||||
|
var plain = Persistance.read(arr, (Function<Object, Object>) null);
|
||||||
|
assertEquals("Multiplied on write", 15, plain.get(Service.class).value());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void readResolveInline() throws Exception {
|
||||||
|
var in = new ServiceSupply(new Service(5));
|
||||||
|
var arr = Persistance.write(in, (Function<Object, Object>) null);
|
||||||
|
|
||||||
|
var plain = Persistance.read(arr, (Function<Object, Object>) null);
|
||||||
|
assertEquals("Remains five", 5, plain.get(ServiceSupply.class).supply().value());
|
||||||
|
|
||||||
|
var multiOnRead = Persistance.read(arr, (obj) -> obj instanceof Service s ? new Service(s.value() * 3) : obj);
|
||||||
|
assertEquals("Multiplied on read", 15, multiOnRead.get(ServiceSupply.class).supply().value());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void writeReplaceInline() throws Exception {
|
||||||
|
var in = new ServiceSupply(new Service(5));
|
||||||
|
var arr = Persistance.write(in, (obj) -> obj instanceof Service s ? new Service(s.value() * 3) : obj);
|
||||||
|
|
||||||
|
var plain = Persistance.read(arr, (Function<Object, Object>) null);
|
||||||
|
assertEquals("Multiplied on write", 15, plain.get(ServiceSupply.class).supply().value());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void readResolveReference() throws Exception {
|
||||||
|
var in = new IntegerSupply(new Service(5));
|
||||||
|
var arr = Persistance.write(in, (Function<Object, Object>) null);
|
||||||
|
|
||||||
|
var plain = Persistance.read(arr, (Function<Object, Object>) null);
|
||||||
|
assertEquals("Remains five", 5, (int) plain.get(IntegerSupply.class).supply().get());
|
||||||
|
assertEquals("Remains five 2", 5, (int) plain.get(IntegerSupply.class).supply().get());
|
||||||
|
|
||||||
|
var multiOnRead = Persistance.read(arr, (obj) -> obj instanceof Service s ? new Service(s.value() * 3) : obj);
|
||||||
|
assertEquals("Multiplied on read", 15, (int) multiOnRead.get(IntegerSupply.class).supply().get());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void writeReplaceReference() throws Exception {
|
||||||
|
var in = new IntegerSupply(new Service(5));
|
||||||
|
var arr = Persistance.write(in, (obj) -> obj instanceof Service s ? new Service(s.value() * 3) : obj);
|
||||||
|
|
||||||
|
var plain = Persistance.read(arr, (Function<Object, Object>) null);
|
||||||
|
assertEquals("Multiplied on write", 15, (int) plain.get(IntegerSupply.class).supply().get());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T> T serde(Class<T> clazz, T l, int expectedSize) throws IOException {
|
||||||
|
var arr = Persistance.write(l, (Function<Object, Object>) null);
|
||||||
|
if (expectedSize >= 0) {
|
||||||
|
assertEquals(expectedSize, arr.length - 12);
|
||||||
|
}
|
||||||
|
var ref = Persistance.read(arr, (Function<Object, Object>) null);
|
||||||
|
return ref.get(clazz);
|
||||||
|
}
|
||||||
|
|
||||||
|
// @start region="manual"
|
||||||
|
@ServiceProvider(service = Persistance.class)
|
||||||
|
public static final class PersistUUID extends Persistance<UUID> {
|
||||||
|
|
||||||
|
public PersistUUID() {
|
||||||
|
super(UUID.class, false, 328439);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void writeObject(UUID obj, Output out) throws IOException {
|
||||||
|
out.writeLong(obj.getMostSignificantBits());
|
||||||
|
out.writeLong(obj.getLeastSignificantBits());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected UUID readObject(Input in) throws IOException, ClassNotFoundException {
|
||||||
|
var most = in.readLong();
|
||||||
|
var least = in.readLong();
|
||||||
|
return new UUID(most, least);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// @end region="manual"
|
||||||
|
|
||||||
|
|
||||||
|
public static final class Singleton {
|
||||||
|
public static final Singleton INSTANCE = new Singleton();
|
||||||
|
|
||||||
|
private Singleton() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ServiceProvider(service = Persistance.class)
|
||||||
|
public static final class PersistSingleton extends Persistance<Singleton> {
|
||||||
|
|
||||||
|
public PersistSingleton() {
|
||||||
|
super(Singleton.class, false, 432433);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void writeObject(Singleton obj, Output out) throws IOException {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Singleton readObject(Input in) throws IOException, ClassNotFoundException {
|
||||||
|
return Singleton.INSTANCE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// @start region="annotation"
|
||||||
|
@Persistable(clazz=Service.class, id=432434)
|
||||||
|
@Persistable(clazz=IntegerSupply.class, id=432435)
|
||||||
|
public record Service(int value) implements Supplier<Integer> {
|
||||||
|
@Override
|
||||||
|
public Integer get() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public record IntegerSupply(Supplier<Integer> supply) {}
|
||||||
|
// @end region="annotation"
|
||||||
|
|
||||||
|
// @start region="self-annotation"
|
||||||
|
@Persistable(id=432436)
|
||||||
|
public record ServiceSupply(Service supply) {}
|
||||||
|
// @end region="self-annotation"
|
||||||
|
}
|
@ -1,9 +1,9 @@
|
|||||||
package org.enso.refactoring
|
package org.enso.refactoring
|
||||||
|
|
||||||
import org.enso.syntax.text.Location
|
|
||||||
import org.enso.text.editing.model
|
import org.enso.text.editing.model
|
||||||
import org.enso.text.editing.{IndexedSource, TextEditor}
|
import org.enso.text.editing.{IndexedSource, TextEditor}
|
||||||
import org.enso.text.editing.model.TextEdit
|
import org.enso.text.editing.model.TextEdit
|
||||||
|
import org.enso.compiler.core.ir.Location
|
||||||
|
|
||||||
object RenameUtils {
|
object RenameUtils {
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package org.enso.refactoring;
|
package org.enso.refactoring;
|
||||||
|
|
||||||
import org.enso.syntax.text.Location;
|
import org.enso.compiler.core.ir.Location;
|
||||||
import org.enso.text.buffer.Rope;
|
import org.enso.text.buffer.Rope;
|
||||||
import org.enso.text.editing.IndexedSource;
|
import org.enso.text.editing.IndexedSource;
|
||||||
import org.enso.text.editing.IndexedSource$;
|
import org.enso.text.editing.IndexedSource$;
|
||||||
@ -11,6 +11,7 @@ import org.enso.text.editing.TextEditor;
|
|||||||
import org.enso.text.editing.model;
|
import org.enso.text.editing.model;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import scala.collection.immutable.Seq;
|
import scala.collection.immutable.Seq;
|
||||||
import scala.collection.immutable.VectorBuilder;
|
import scala.collection.immutable.VectorBuilder;
|
||||||
import scala.util.Either;
|
import scala.util.Either;
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
package org.enso.syntax.text
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
//// AbsolutePosition //////////////////////////////////////////////////////////
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
/** Represents an expression's absolute positioning in a source file.
|
|
||||||
* @param start the inclusive, 0-indexed position of the beginning
|
|
||||||
* of the expression
|
|
||||||
* @param end the exclusive, 0-indexed position of the end of
|
|
||||||
* the expression
|
|
||||||
*/
|
|
||||||
case class Location(start: Int, end: Int) {
|
|
||||||
def length: Int = end - start
|
|
||||||
}
|
|
@ -1,2 +1,2 @@
|
|||||||
/FasterXML/jackson-dataformats-binary/blob/2.16/LICENSE
|
|
||||||
#license
|
#license
|
||||||
|
/FasterXML/jackson-dataformats-binary/blob/2.17/LICENSE
|
||||||
|
Loading…
Reference in New Issue
Block a user