diff --git a/.travis.yml b/.travis.yml index ef3868165e51..ffc02d1be6d8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,8 +18,8 @@ jdk: env: global: - - JVMCI_VERSION="jvmci-0.54" - - JDK8_UPDATE_VERSION="192" + - JVMCI_VERSION="jvmci-0.55" + - JDK8_UPDATE_VERSION="202" matrix: include: diff --git a/README.md b/README.md index c37d5da56e1a..0eda43e4b056 100644 --- a/README.md +++ b/README.md @@ -34,10 +34,10 @@ images or shared objects. ## Related Repositories GraalVM allows running of following languages which are being developed and tested in related repositories with GraalVM core to run on top of it using Truffle and Graal compiler. These are: -* [Graal.JS](https://github.com/graalvm/graaljs) - JavaScript (ECMAScript 2017 compatible) and node.js 8.11.1 -* [FastR](https://github.com/oracle/fastr) - R Language 3.4.0 +* [Graal.JS](https://github.com/graalvm/graaljs) - JavaScript (ECMAScript 2018 compatible) and node.js 10.15.0 +* [FastR](https://github.com/oracle/fastr) - R Language 3.5.1 * [GraalPython](https://github.com/graalvm/graalpython) - Python 3.7 -* [TruffleRuby](https://github.com/oracle/truffleruby/) - Ruby Programming Language 2.3.7 +* [TruffleRuby](https://github.com/oracle/truffleruby/) - Ruby Programming Language 2.4.4 * [SimpleLanguage](https://github.com/graalvm/simplelanguage) - A simple demonstration language for the GraalVM. diff --git a/ci.hocon b/ci.hocon index 34d0531d372b..ca12ede7f32d 100644 --- a/ci.hocon +++ b/ci.hocon @@ -27,6 +27,8 @@ include "sulong/ci.hocon" #include "examples/ci.hocon" jvm-config.default = "graal-core" +libgraal_env = "libgraal" +vm_subdir = "vm" include "compiler/ci_common/common.hocon" include "compiler/ci_common/gate.hocon" diff --git a/common.hocon b/common.hocon index db9d4d968026..eb77ddbbbcba 100644 --- a/common.hocon +++ b/common.hocon @@ -1,14 +1,14 @@ # overlay version -overlay = a84dd00c92041dfcd04b34c8d3f4d9e8c047d510 +overlay = d0a64ff32d27517b0683ae6ce9cf9614a7fb51d3 # oraclejdk* are released OracleJDK binaries # labsjdk* are JDKs based on OracleJDK binaries # openjdk8 JDKs on Linux are built by Oracle Labs # openjdk8 JDKs on macOS are based on AdoptOpenJDK binaries jdks: { - labsjdk8: {name : labsjdk, version : "8u192-jvmci-0.54", platformspecific: true} - openjdk8: {name : openjdk, version : "8u192-jvmci-0.54", platformspecific: true} - labsjdk8Debug: {name : labsjdk, version : "8u192-jvmci-0.54-fastdebug", platformspecific: true} + labsjdk8: {name : labsjdk, version : "8u202-jvmci-0.55", platformspecific: true} + openjdk8: {name : openjdk, version : "8u202-jvmci-0.55", platformspecific: true} + labsjdk8Debug: {name : labsjdk, version : "8u202-jvmci-0.55-fastdebug", platformspecific: true} oraclejdk11: {name : oraclejdk, version : "11+28", platformspecific: true} openjdk11: {name : openjdk, version : "11+28", platformspecific: true} @@ -91,6 +91,9 @@ linux-aarch64 : ${linux} { capabilities : [linux, aarch64] } +DEFAULT_HEAP_SIZE : "8G" +LARGE_HEAP_SIZE : "31G" + eclipse : { downloads : { ECLIPSE : {name: eclipse, version: "4.5.2", platformspecific: true} diff --git a/compiler/README.md b/compiler/README.md index 884297a4d91b..b60c90b73835 100644 --- a/compiler/README.md +++ b/compiler/README.md @@ -113,7 +113,7 @@ The advantage of this mode is that Graal can be debugged with a Java debugger. However, it has some disadvantages. Firstly, since Graal uses the object heap, it can reduce application object locality and increase GC pause times. Additionally, it can -complicate fine tuning options such as `-Xmx` and `Xms` which now need to take the +complicate fine tuning options such as `-Xmx` and `-Xms` which now need to take the heap usage of Graal needs to be taken into account. Secondly, Graal will initially be executed in the interpreter and only get faster over time as its hot methods are JIT compiled. This is mitigated to some degree by forcing Graal (and JVMCI) @@ -125,25 +125,39 @@ Graal uses memory separate from the HotSpot heap and it runs compiled from the start. That is, it has execution properties similar to other native HotSpot compilers such as C1 and C2. -To build libgraal, you need to use the `native-image` tool in the `substratevm` suite. +To build a GraalVM image with libgraal: ``` -cd graal/substratevm -mx build -mx buildlibgraal +cd graal/vm +mx --env libgraal build ``` - -This will produce a shared library in the current working directory whose name is -compliant with the shared library naming conventions for the platform; -`libjvmcicompiler.dylib` (macOS), `libjvmcicompiler.so` (Linux, Solaris, etc), `graal.dll` (Windows). - -To use this library, copy it to the same directory as `libjava.dylib`/`libjava.so`/`java.dll` -in your JVMCI JDK8 installation and use the options `-XX:+UseJVMCICompiler -XX:+UseJVMCINativeLibrary`. Alternatively, -you can directly specify the path to the library as the value to `-XX:JVMCILibPath=`. -For example: +The newly built GraalVM image is available at: +``` +mx --env libgraal graalvm-home +``` +or following this symlink: ``` -mx vm -XX:JVMCILibPath=/path/to/libjvmcicompiler.dylib -XX:+UseJVMCICompiler -XX:+UseJVMCINativeLibrary ... +./latest_graalvm_home ``` +For more information about building GraalVM images, see the [README file of the vm suite](../vm/README.md). + +Without leaving the `graal/vm` directory, you can now run libgraal as follows: + +1. Use the GraalVM image that you just built: + + ``` + ./latest_graalvm_home/bin/java -XX:+UseJVMCICompiler -XX:+UseJVMCINativeLibrary ... + ``` + +2. Use `mx`: + - On linux: + ``` + mx -p ../compiler vm -XX:JVMCILibPath=latest_graalvm_home/jre/lib/amd64 -XX:+UseJVMCICompiler -XX:+UseJVMCINativeLibrary ... + ``` + - On macOS: + ``` + mx -p ../compiler vm -XX:JVMCILibPath=latest_graalvm_home/jre/lib -XX:+UseJVMCICompiler -XX:+UseJVMCINativeLibrary ... + ``` ## Publications and Presentations diff --git a/compiler/ci_common/bench.hocon b/compiler/ci_common/bench.hocon index e2e3f2e187d7..4f2b45ddaa75 100644 --- a/compiler/ci_common/bench.hocon +++ b/compiler/ci_common/bench.hocon @@ -23,7 +23,7 @@ bench-jvmci : { bench-common : ${compilerCommon} { environment: { - BENCH_RESULTS_FILE_PATH: "bench-results.json" + BENCH_RESULTS_FILE_PATH : "bench-results.json" } setup: ${compilerCommon.setup} [ ["mx", "build"] @@ -33,21 +33,35 @@ bench-common : ${compilerCommon} { ] } -bench-dacapo: ${bench-common} ${dacapo-bench-notifications} { +large-heap: { + environment : { + XMS: ${LARGE_HEAP_SIZE} + XMX: ${LARGE_HEAP_SIZE} + } +} + +default-heap: { + environment : { + XMS: ${DEFAULT_HEAP_SIZE} + XMX: ${DEFAULT_HEAP_SIZE} + } +} + +bench-dacapo: ${bench-common} ${default-heap} ${dacapo-bench-notifications} { targets : [bench, post-merge] run: [ ${bench-jvmci.mx-dacapo} ${bench-arguments} ] } -bench-dacapo-timing: ${bench-common} ${dacapo-bench-notifications} { +bench-dacapo-timing: ${bench-common} ${default-heap} ${dacapo-bench-notifications} { targets: [bench, daily] run: [ ${bench-jvmci.mx-dacapo-timing} ${bench-arguments} ] } -bench-dacapo-move-profiling: ${bench-common} ${dacapo-bench-notifications} { +bench-dacapo-move-profiling: ${bench-common} ${default-heap} ${dacapo-bench-notifications} { targets: [bench, daily] run: [ ${bench-jvmci.mx-dacapo-move-profiling} ${bench-arguments} @@ -56,21 +70,21 @@ bench-dacapo-move-profiling: ${bench-common} ${dacapo-bench-notifications} { # Scala DaCapo-related targets -bench-scala-dacapo: ${bench-common} ${scala-dacapo-bench-notifications} { +bench-scala-dacapo: ${bench-common} ${default-heap} ${scala-dacapo-bench-notifications} { targets: [bench, post-merge] run: [ ${bench-jvmci.mx-scala-dacapo} ${bench-arguments} ] } -bench-scala-dacapo-timing: ${bench-common} ${scala-dacapo-bench-notifications} { +bench-scala-dacapo-timing: ${bench-common} ${default-heap} ${scala-dacapo-bench-notifications} { targets: [bench, daily] run: [ ${bench-jvmci.mx-scala-dacapo-timing} ${bench-arguments} ] } -bench-scala-dacapo-move-profiling: ${bench-common} ${scala-dacapo-bench-notifications} { +bench-scala-dacapo-move-profiling: ${bench-common} ${default-heap} ${scala-dacapo-bench-notifications} { targets: [bench, daily] run: [ ${bench-jvmci.mx-scala-dacapo-move-profiling} ${bench-arguments} @@ -79,7 +93,7 @@ bench-scala-dacapo-move-profiling: ${bench-common} ${scala-dacapo-bench-notifica # SPECjbb2005-related targets -bench-specjbb2005: ${bench-common} ${specjbb-bench-notifications} { +bench-specjbb2005: ${bench-common} ${large-heap} ${specjbb-bench-notifications} { targets: [bench, post-merge] downloads: { SPECJBB2005: { name: specjbb2005, version: "1.07" } @@ -91,7 +105,7 @@ bench-specjbb2005: ${bench-common} ${specjbb-bench-notifications} { } # SPECJvm2008-related targets -bench-specjvm2008: ${bench-common} ${specjvm-bench-notifications} { +bench-specjvm2008: ${bench-common} ${large-heap} ${specjvm-bench-notifications} { downloads: { SPECJVM2008: { name: specjvm2008, version: "1.01" } } @@ -121,7 +135,7 @@ bench-specjvm2008-OneVM: ${bench-specjvm2008} ${specjvm-bench-notifications} { # SPECjbb2015-related targets -bench-specjbb2015: ${bench-common} ${specjbb-bench-notifications} { +bench-specjbb2015: ${bench-common} ${large-heap} ${specjbb-bench-notifications} { targets: [bench, post-merge] run: [ ${bench-jvmci.mx-specjbb2015} ${bench-arguments} @@ -134,7 +148,7 @@ bench-specjbb2015: ${bench-common} ${specjbb-bench-notifications} { # JMH micros graal -bench-micros-graal-whitebox: ${bench-common} ${micros-bench-notifications} { +bench-micros-graal-whitebox: ${bench-common} ${default-heap} ${micros-bench-notifications} { targets: [bench, weekly] run: [ ${bench-jvmci.mx-micros-graal-whitebox} ${bench-arguments} @@ -142,7 +156,7 @@ bench-micros-graal-whitebox: ${bench-common} ${micros-bench-notifications} { timelimit: "3:00:00" } -bench-micros-graal-dist: ${bench-common} ${micros-bench-notifications} { +bench-micros-graal-dist: ${bench-common} ${default-heap} ${micros-bench-notifications} { targets: [bench, weekly] run: [ ${bench-jvmci.mx-micros-graal-dist} ${bench-arguments} @@ -152,7 +166,7 @@ bench-micros-graal-dist: ${bench-common} ${micros-bench-notifications} { # Renaissance-related targets -bench-renaissance: ${bench-common} ${renaissance-bench-notifications} { +bench-renaissance: ${bench-common} ${default-heap} ${renaissance-bench-notifications} { targets: [bench, post-merge] run: [ ${bench-jvmci.mx-renaissance} ${bench-arguments} @@ -165,7 +179,7 @@ bench-renaissance: ${bench-common} ${renaissance-bench-notifications} { # spark-sql-perf-related targets -bench-spark-sql-perf: ${bench-common} { +bench-spark-sql-perf: ${bench-common} ${default-heap} { targets: [bench, post-merge] run: [ ${bench-jvmci.mx-spark-sql-perf} ${bench-arguments} diff --git a/compiler/ci_common/gate.hocon b/compiler/ci_common/gate.hocon index eb578eda259a..47926c0c5fd7 100644 --- a/compiler/ci_common/gate.hocon +++ b/compiler/ci_common/gate.hocon @@ -18,7 +18,8 @@ gateCoverage : { EXTRA_VM_ARGS : "" } run : [ - ${gateCmd} ["build,coverage", --jacoco-zip, report.zip] + ${gateCmd} ["build,coverage", --jacocout, html] + [mx, sonarqube-upload, "-Dsonar.host.url=$SONAR_HOST_URL", "-Dsonar.projectKey=com.oracle.graal.compiler."${jvm-config.default}, "-Dsonar.projectName=GraalVM - Compiler ("${jvm-config.default}")", --exclude-generated] ] } diff --git a/compiler/ci_common/gate_tasks.hocon b/compiler/ci_common/gate_tasks.hocon index e9489207aba1..3c342d3e5ee6 100644 --- a/compiler/ci_common/gate_tasks.hocon +++ b/compiler/ci_common/gate_tasks.hocon @@ -22,7 +22,7 @@ builds += [ ${gateTestBenchmark} ${labsjdk8} ${gateLinuxAMD64} {name: "gate-compiler-benchmarktest-8-linux-amd64"} ${gateTestBenchmark} ${labsjdk8Debug} ${gateLinuxAMD64} {name: "weekly-test-compiler-benchmarktest-8-linux-amd64-fastdebug"} ${graalWeekly} ${gateStyle} ${labsjdk8} ${gateLinuxAMD64} {name: "gate-compiler-style-linux-amd64"} - ${gateCoverage} ${labsjdk8} ${gateLinuxAMD64} {name: "weekly-compiler-coverage-8-linux-amd64", timelimit: "1:50:00"} ${graalWeekly} { logs += [*/report.zip] } + ${gateCoverage} ${labsjdk8} ${gateLinuxAMD64} {name: "weekly-compiler-coverage-8-linux-amd64", timelimit: "1:50:00"} ${graalWeekly} ${gateTestCompileImmediately} ${labsjdk8} ${gateLinuxAMD64} {name: "gate-compiler-test-compile-immediately-8-linux-amd64"} ${gateMathStubsListener} {name: "daily-hotspot-mathstubs-listener"} diff --git a/compiler/ci_common/m7_eighth.hocon b/compiler/ci_common/m7_eighth.hocon index 990cee99b49f..aafb7d1271d2 100644 --- a/compiler/ci_common/m7_eighth.hocon +++ b/compiler/ci_common/m7_eighth.hocon @@ -3,8 +3,6 @@ m7_eighth.default : ${solaris-sparcv9} { capabilities: ${solaris-sparcv9.capabilities} [m7_eighth] environment : { - XMX : "16g" - XMS : "16g" JVM_CONFIG : ${jvm-config.default} JVM : "server" MACHINE_NAME: "m7_eighth" @@ -17,7 +15,6 @@ m7_eighth.default-g1gc : ${m7_eighth.default} { } } - builds += [ ${m7_eighth.default} ${bench-dacapo} ${labsjdk8} { name: "bench-compiler-dacapo-solaris-m7_eighth", timelimit: "1:00:00" } ${m7_eighth.default} ${bench-dacapo-timing} ${labsjdk8} { name: "bench-compiler-dacapo-timing-solaris-m7_eighth", timelimit: "1:00:00" } diff --git a/compiler/ci_common/x52-jfr.hocon b/compiler/ci_common/x52-jfr.hocon index 9325e17ebc58..25790d14b6c6 100644 --- a/compiler/ci_common/x52-jfr.hocon +++ b/compiler/ci_common/x52-jfr.hocon @@ -1,4 +1,4 @@ -bench-dacapo-jfr: ${bench-common} ${jfr-bench-notifications} { +bench-dacapo-jfr: ${bench-common} ${default-heap} ${jfr-bench-notifications} { environment : { BENCH_SUITE : "dacapo" } @@ -9,7 +9,7 @@ bench-dacapo-jfr: ${bench-common} ${jfr-bench-notifications} { teardown: ${JFR.teardown} # NOTE : Exclude branch-common to avoid polluting bench server with slower runs } -bench-scala-dacapo-jfr: ${bench-common} ${jfr-bench-notifications} { +bench-scala-dacapo-jfr: ${bench-common} ${default-heap} ${jfr-bench-notifications} { environment : { BENCH_SUITE : "scala-dacapo" } @@ -20,7 +20,7 @@ bench-scala-dacapo-jfr: ${bench-common} ${jfr-bench-notifications} { teardown: ${JFR.teardown} # NOTE : Exclude branch-common to avoid polluting bench server with slower runs } -bench-specjvm2008-Single-jfr: ${bench-specjvm2008} ${jfr-bench-notifications} { +bench-specjvm2008-Single-jfr: ${bench-specjvm2008} ${large-heap} ${jfr-bench-notifications} { environment : { BENCH_SUITE : "specjvm2008" } @@ -36,7 +36,7 @@ bench-specjvm2008-Single-jfr: ${bench-specjvm2008} ${jfr-bench-notifications} { timelimit: "3:30:00" } -bench-specjbb2015-jfr: ${bench-common} ${jfr-bench-notifications} { +bench-specjbb2015-jfr: ${bench-common} ${large-heap} ${jfr-bench-notifications} { environment : { BENCH_SUITE : "specjbb2015" } @@ -51,7 +51,7 @@ bench-specjbb2015-jfr: ${bench-common} ${jfr-bench-notifications} { timelimit: "3:00:00" } -bench-renaissance-jfr: ${bench-common} ${jfr-bench-notifications} { +bench-renaissance-jfr: ${bench-common} ${default-heap} ${jfr-bench-notifications} { environment : { BENCH_SUITE : "renaissance" } @@ -66,7 +66,7 @@ bench-renaissance-jfr: ${bench-common} ${jfr-bench-notifications} { timelimit: "3:45:00" } -bench-spark-sql-perf-jfr: ${bench-common} ${jfr-bench-notifications} { +bench-spark-sql-perf-jfr: ${bench-common} ${default-heap} ${jfr-bench-notifications} { environment : { BENCH_SUITE : "spark-sql-perf" } @@ -84,8 +84,6 @@ bench-spark-sql-perf-jfr: ${bench-common} ${jfr-bench-notifications} { x52.default-jfr : ${linux-amd64} { capabilities: ${linux-amd64.capabilities} [x52, no_frequency_scaling] environment : { - XMX : "64g" - XMS : "64g" JVM_CONFIG : ${jvm-config.default} JVM : "server" MACHINE_NAME: "x52" diff --git a/compiler/ci_common/x52.hocon b/compiler/ci_common/x52.hocon index c602fe6fee58..095909dc1067 100644 --- a/compiler/ci_common/x52.hocon +++ b/compiler/ci_common/x52.hocon @@ -1,30 +1,52 @@ x52.default : ${linux-amd64} { capabilities: ${linux-amd64.capabilities} [x52, no_frequency_scaling] environment : { - XMX : "64g" - XMS : "64g" JVM_CONFIG : ${jvm-config.default} JVM : "server" MACHINE_NAME: "x52" } } +x52.default-libgraal : ${x52.default} { + environment : { + MX_PRIMARY_SUITE_PATH: "../"${vm_subdir} + MX_ENV_PATH: ${libgraal_env} + JVM_CONFIG : ${jvm-config.default}"-libgraal" + } +} + x52.tmpfs10g : ${x52.default} { capabilities: ${x52.default.capabilities} [tmpfs10g] } +x52.tmpfs10g-libgraal : ${x52.default-libgraal} { + capabilities: ${x52.default.capabilities} [tmpfs10g] +} + x52.default-g1gc : ${x52.default} { environment : { JVM_CONFIG : ${jvm-config.default}"-g1gc" } } +x52.default-libgraal-g1gc : ${x52.default-libgraal} { + environment : { + JVM_CONFIG : ${jvm-config.default}"-libgraal-g1gc" + } +} + x52.tmpfs10g-g1gc: ${x52.tmpfs10g} { environment : { JVM_CONFIG : ${jvm-config.default}"-g1gc" } } +x52.tmpfs10g-libgraal-g1gc: ${x52.tmpfs10g-libgraal} { + environment : { + JVM_CONFIG : ${jvm-config.default}"-libgraal-g1gc" + } +} + builds += [ ${x52.tmpfs10g} ${bench-dacapo-hwloc} ${labsjdk8} { name: "bench-compiler-dacapo-linux-x52" } ${x52.tmpfs10g} ${bench-dacapo-timing-hwloc} ${labsjdk8} { name: "bench-compiler-dacapo-timing-linux-x52" } @@ -49,4 +71,29 @@ builds += [ ${x52.default} ${bench-specjbb2005} ${labsjdk8} { targets : [daily, bench], name: "bench-compiler-specjbb2005-linux-x52"} ${x52.default-g1gc} ${bench-specjbb2005} ${labsjdk8} { targets : [weekly, bench], name: "bench-compiler-specjbb2005-g1gc-linux-x52"} + + # LibGraal + ${x52.tmpfs10g-libgraal} ${bench-dacapo-hwloc} ${labsjdk8} { targets: [daily, bench], name: "bench-compiler-dacapo-libgraal-linux-x52" } + ${x52.tmpfs10g-libgraal} ${bench-dacapo-timing-hwloc} ${labsjdk8} { targets: [daily, bench], name: "bench-compiler-dacapo-timing-libgraal-linux-x52" } + ${x52.tmpfs10g-libgraal} ${bench-dacapo-move-profiling-hwloc} ${labsjdk8} { targets: [daily, bench], name: "bench-compiler-dacapo-move-profiling-libgraal-linux-x52" } + ${x52.tmpfs10g-libgraal} ${bench-scala-dacapo-hwloc} ${labsjdk8} { targets: [daily, bench], name: "bench-compiler-scala-dacapo-libgraal-linux-x52" } + ${x52.tmpfs10g-libgraal} ${bench-scala-dacapo-timing-hwloc} ${labsjdk8} { targets: [daily, bench], name: "bench-compiler-scala-dacapo-timing-libgraal-linux-x52" } + ${x52.tmpfs10g-libgraal} ${bench-scala-dacapo-move-profiling-hwloc} ${labsjdk8} { targets: [daily, bench], name: "bench-compiler-scala-dacapo-move-profiling-libgraal-linux-x52" } + ${x52.default-libgraal} ${bench-specjvm2008-Single} ${labsjdk8} { targets: [daily, bench], name: "bench-compiler-specjvm2008-Single-libgraal-linux-x52" } + ${x52.default-libgraal} ${bench-specjvm2008-OneVM} ${labsjdk8} { targets: [daily, bench], name: "bench-compiler-specjvm2008-OneVM-libgraal-linux-x52" } + ${x52.default-libgraal} ${bench-specjbb2015} ${labsjdk8} { targets: [daily, bench], name: "bench-compiler-specjbb2015-libgraal-linux-x52" } + ${x52.default-libgraal} ${bench-micros-graal-whitebox} ${labsjdk8} { targets: [daily, bench], name: "bench-compiler-jmh-micros-graal-whitebox-libgraal-linux-x52" } + ${x52.default-libgraal} ${bench-micros-graal-dist} ${labsjdk8} { targets: [daily, bench], name: "bench-compiler-jmh-micros-graal-dist-libgraal-linux-x52" } + + ${x52.tmpfs10g-libgraal-g1gc} ${bench-dacapo-hwloc} ${labsjdk8} { targets: [daily, bench], name: "bench-compiler-dacapo-libgraal-g1gc-linux-x52" } + ${x52.tmpfs10g-libgraal-g1gc} ${bench-scala-dacapo-hwloc} ${labsjdk8} { targets: [daily, bench], name: "bench-compiler-scala-dacapo-libgraal-g1gc-linux-x52" } + ${x52.tmpfs10g-libgraal-g1gc} ${bench-specjvm2008-Single} ${labsjdk8} { targets: [daily, bench], name: "bench-compiler-specjvm2008-libgraal-g1gc-Single-linux-x52" } + ${x52.tmpfs10g-libgraal-g1gc} ${bench-specjbb2015} ${labsjdk8} { targets: [daily, bench], name: "bench-compiler-specjbb2015-libgraal-g1gc-linux-x52" } + + ${x52.default-libgraal} ${bench-renaissance-hwloc} ${labsjdk8} { targets: [daily, bench], name: "bench-compiler-renaissance-libgraal-linux-x52" } + ${x52.default-libgraal-g1gc} ${bench-renaissance-hwloc} ${labsjdk8} { targets: [daily, bench], name: "bench-compiler-renaissance-libgraal-g1gc-linux-x52" } + ${x52.default-libgraal} ${bench-spark-sql-perf} ${labsjdk8} { targets: [daily, bench], name: "bench-compiler-spark-sql-perf-libgraal-linux-x52" } + + ${x52.default-libgraal} ${bench-specjbb2005} ${labsjdk8} { targets: [daily, bench], name: "bench-compiler-specjbb2005-libgraal-linux-x52" } + ${x52.default-libgraal-g1gc} ${bench-specjbb2005} ${labsjdk8} { targets: [daily, bench], name: "bench-compiler-specjbb2005-libgraal-g1gc-linux-x52" } ] diff --git a/compiler/ci_includes/m7_eighth-c1.hocon b/compiler/ci_includes/m7_eighth-c1.hocon index bfd93dea2150..5e5806e84c55 100644 --- a/compiler/ci_includes/m7_eighth-c1.hocon +++ b/compiler/ci_includes/m7_eighth-c1.hocon @@ -4,8 +4,6 @@ m7_eighth.c1 : ${labsjdk8} { targets: [weekly] capabilities: [solaris, m7_eighth] environment : { - XMX : "16g" - XMS : "16g" JVM_CONFIG : "default" JVM : "client" MACHINE_NAME : "m7_eighth" diff --git a/compiler/ci_includes/m7_eighth-c2.hocon b/compiler/ci_includes/m7_eighth-c2.hocon index 3082229ef090..5503aae8e447 100644 --- a/compiler/ci_includes/m7_eighth-c2.hocon +++ b/compiler/ci_includes/m7_eighth-c2.hocon @@ -4,8 +4,6 @@ m7_eighth.c2 : ${labsjdk8} { targets: [weekly] capabilities: [solaris, m7_eighth] environment : { - XMX : "16g" - XMS : "16g" JVM_CONFIG : "default" JVM : "server" MACHINE_NAME : "m7_eighth" diff --git a/compiler/ci_includes/x52-c1.hocon b/compiler/ci_includes/x52-c1.hocon index 492fd7d54eee..56eae8607287 100644 --- a/compiler/ci_includes/x52-c1.hocon +++ b/compiler/ci_includes/x52-c1.hocon @@ -2,8 +2,6 @@ x52.c1 : ${labsjdk8} { capabilities: [linux, x52, no_frequency_scaling] environment : { - XMX : "64g" - XMS : "64g" JVM_CONFIG : "default" JVM : "client" MACHINE_NAME: "x52" diff --git a/compiler/ci_includes/x52-c2-jfr.hocon b/compiler/ci_includes/x52-c2-jfr.hocon index eba95958bc63..b16d54a0ed0b 100644 --- a/compiler/ci_includes/x52-c2-jfr.hocon +++ b/compiler/ci_includes/x52-c2-jfr.hocon @@ -2,8 +2,6 @@ x52.c2-jfr : ${labsjdk8} { capabilities: [linux, x52, no_frequency_scaling] environment : { - XMX : "64g" - XMS : "64g" JVM_CONFIG : "default" JVM : "server" MACHINE_NAME: "x52" diff --git a/compiler/ci_includes/x52-c2.hocon b/compiler/ci_includes/x52-c2.hocon index cf2b3dc1c1df..84ce8271436e 100644 --- a/compiler/ci_includes/x52-c2.hocon +++ b/compiler/ci_includes/x52-c2.hocon @@ -1,8 +1,6 @@ x52.c2-perftag : ${labsjdk8} { capabilities: [linux, x52, no_frequency_scaling] environment : { - XMX : "64g" - XMS : "64g" JVM_CONFIG : "default" JVM : "server" MACHINE_NAME : "x52" diff --git a/compiler/mx.compiler/mx_compiler.py b/compiler/mx.compiler/mx_compiler.py index c4d6d5a504c0..8954534ae041 100644 --- a/compiler/mx.compiler/mx_compiler.py +++ b/compiler/mx.compiler/mx_compiler.py @@ -546,9 +546,7 @@ def compiler_gate_runner(suites, unit_test_runs, bootstrap_tests, tasks, extraVM if t: mx.javadoc(['--exclude-packages', 'com.oracle.truffle.dsl.processor.java'], quietForNoPackages=True) -def compiler_gate_benchmark_runner(tasks, extraVMarguments=None, libgraal=False): - prefix = 'libgraal ' if libgraal else '' - +def compiler_gate_benchmark_runner(tasks, extraVMarguments=None, prefix=''): # run selected DaCapo benchmarks # DaCapo benchmarks that can run with system assertions enabled but diff --git a/compiler/mx.compiler/suite.py b/compiler/mx.compiler/suite.py index 04c5b11b65b5..d30886e76343 100644 --- a/compiler/mx.compiler/suite.py +++ b/compiler/mx.compiler/suite.py @@ -1,5 +1,5 @@ suite = { - "mxversion" : "5.195.1", + "mxversion" : "5.210.2", "name" : "compiler", "sourceinprojectwhitelist" : [], @@ -186,7 +186,7 @@ "org.graalvm.compiler.serviceprovider" : { "subDir" : "src", "sourceDirs" : ["src"], - "dependencies" : ["JVMCI_SERVICES"], + "dependencies" : ["JVMCI_SERVICES", "JVMCI_API"], "checkstyle" : "org.graalvm.compiler.graph", "javaCompliance" : "8+", "workingSets" : "API,Graal", @@ -195,7 +195,11 @@ "org.graalvm.compiler.serviceprovider.jdk8" : { "subDir" : "src", "sourceDirs" : ["src"], - "dependencies" : ["JVMCI_SERVICES"], + "dependencies" : [ + "org.graalvm.compiler.serviceprovider", + "JVMCI_SERVICES", + "JVMCI_API" + ], "overlayTarget" : "org.graalvm.compiler.serviceprovider", "checkstyle" : "org.graalvm.compiler.graph", "javaCompliance" : "8", @@ -795,7 +799,7 @@ "checkstyle" : "org.graalvm.compiler.graph", "javaCompliance" : "8+", "workingSets" : "Graal,LIR", - "findbugs" : "false", + "spotbugs" : "false", "testProject" : True, }, @@ -1084,7 +1088,7 @@ "checkstyle" : "org.graalvm.compiler.graph", "javaCompliance" : "8+", "annotationProcessors" : ["mx:JMH_1_21"], - "findbugsIgnoresGenerated" : True, + "spotbugsIgnoresGenerated" : True, "workingSets" : "Graal,Bench", "testProject" : True, }, @@ -1102,7 +1106,7 @@ "javaCompliance" : "8+", "checkPackagePrefix" : "false", "annotationProcessors" : ["mx:JMH_1_21"], - "findbugsIgnoresGenerated" : True, + "spotbugsIgnoresGenerated" : True, "workingSets" : "Graal,Bench", "testProject" : True, }, @@ -1373,7 +1377,7 @@ "javaCompliance" : "8+", "workingSets" : "Graal,Test", "jacoco" : "exclude", - "findbugs" : "false", + "spotbugs" : "false", "testProject" : True, }, @@ -1697,8 +1701,33 @@ ], "checkstyle" : "org.graalvm.compiler.graph", "javaCompliance" : "8+", - "annotationProcessors" : [ + "workingSets" : "Graal,Truffle", + }, + + "org.graalvm.compiler.truffle.runtime.hotspot.jdk8" : { + "subDir" : "src", + "sourceDirs" : ["src"], + "dependencies" : [ + "org.graalvm.compiler.truffle.runtime", + "JVMCI_HOTSPOT", ], + "checkPackagePrefix" : "false", + "checkstyle" : "org.graalvm.compiler.graph", + "javaCompliance" : "8", + "workingSets" : "Graal,Truffle", + }, + + "org.graalvm.compiler.truffle.runtime.hotspot.jdk9" : { + "subDir" : "src", + "sourceDirs" : ["src"], + "dependencies" : [ + "org.graalvm.compiler.truffle.runtime.hotspot", + "JVMCI_HOTSPOT", + ], + "checkPackagePrefix" : "false", + "checkstyle" : "org.graalvm.compiler.graph", + "javaCompliance" : "9+", + "multiReleaseJarVersion" : "9", "workingSets" : "Graal,Truffle", }, @@ -1755,7 +1784,7 @@ "javaCompliance" : "8+", "checkPackagePrefix" : "false", "annotationProcessors" : ["mx:JMH_1_21"], - "findbugsIgnoresGenerated" : True, + "spotbugsIgnoresGenerated" : True, "workingSets" : "Graal,Bench", "testProject" : True, }, @@ -2134,6 +2163,8 @@ "org.graalvm.compiler.truffle.compiler.amd64", "org.graalvm.compiler.truffle.runtime.serviceprovider.jdk8", "org.graalvm.compiler.truffle.runtime.serviceprovider.jdk9", + "org.graalvm.compiler.truffle.runtime.hotspot.jdk8", + "org.graalvm.compiler.truffle.runtime.hotspot.jdk9", "org.graalvm.compiler.truffle.runtime.hotspot", "org.graalvm.compiler.truffle.runtime.hotspot.java", "org.graalvm.compiler.truffle.runtime.hotspot.libgraal", diff --git a/compiler/src/org.graalvm.compiler.code/src/org/graalvm/compiler/code/CompilationResult.java b/compiler/src/org.graalvm.compiler.code/src/org/graalvm/compiler/code/CompilationResult.java index 5b5fc331067d..3fd5597d6e45 100644 --- a/compiler/src/org.graalvm.compiler.code/src/org/graalvm/compiler/code/CompilationResult.java +++ b/compiler/src/org.graalvm.compiler.code/src/org/graalvm/compiler/code/CompilationResult.java @@ -55,6 +55,7 @@ import jdk.vm.ci.meta.InvokeTarget; import jdk.vm.ci.meta.ResolvedJavaField; import jdk.vm.ci.meta.ResolvedJavaMethod; +import jdk.vm.ci.meta.SpeculationLog; /** * Represents the output from compiling a method, including the compiled machine code, associated @@ -220,6 +221,11 @@ public String toString() { */ private ResolvedJavaMethod[] methods; + /** + * The {@link SpeculationLog} log used during compilation. + */ + private SpeculationLog speculationLog; + /** * The list of fields that were accessed from the bytecodes. */ @@ -372,6 +378,21 @@ public ResolvedJavaMethod[] getMethods() { return methods; } + /** + * Sets the {@link SpeculationLog} log used during compilation. + */ + public void setSpeculationLog(SpeculationLog speculationLog) { + checkOpen(); + this.speculationLog = speculationLog; + } + + /** + * Gets the {@link SpeculationLog} log, if any, used during compilation. + */ + public SpeculationLog getSpeculationLog() { + return speculationLog; + } + /** * Sets the fields that were referenced from the bytecodes that were used as input to the * compilation. diff --git a/compiler/src/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/AbstractObjectStamp.java b/compiler/src/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/AbstractObjectStamp.java index 1968dd11af6f..d1c3b0c74cee 100644 --- a/compiler/src/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/AbstractObjectStamp.java +++ b/compiler/src/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/AbstractObjectStamp.java @@ -48,6 +48,13 @@ protected AbstractObjectStamp(ResolvedJavaType type, boolean exactType, boolean this.exactType = exactType; } + @Override + public void accept(Visitor v) { + super.accept(v); + v.visitObject(type); + v.visitBoolean(exactType); + } + protected abstract AbstractObjectStamp copyWith(ResolvedJavaType newType, boolean newExactType, boolean newNonNull, boolean newAlwaysNull); @Override diff --git a/compiler/src/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/AbstractPointerStamp.java b/compiler/src/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/AbstractPointerStamp.java index bd13eab37b08..b021ede4dee6 100644 --- a/compiler/src/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/AbstractPointerStamp.java +++ b/compiler/src/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/AbstractPointerStamp.java @@ -36,6 +36,12 @@ public abstract class AbstractPointerStamp extends Stamp { private final boolean nonNull; private final boolean alwaysNull; + @Override + public void accept(Visitor v) { + v.visitBoolean(nonNull); + v.visitBoolean(alwaysNull); + } + protected AbstractPointerStamp(boolean nonNull, boolean alwaysNull) { this.nonNull = nonNull; this.alwaysNull = alwaysNull; diff --git a/compiler/src/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/IllegalStamp.java b/compiler/src/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/IllegalStamp.java index 0af3f177ffdd..41e263968111 100644 --- a/compiler/src/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/IllegalStamp.java +++ b/compiler/src/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/IllegalStamp.java @@ -45,6 +45,10 @@ public final class IllegalStamp extends Stamp { private IllegalStamp() { } + @Override + public void accept(Visitor v) { + } + @Override public JavaKind getStackKind() { return JavaKind.Illegal; diff --git a/compiler/src/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/PrimitiveStamp.java b/compiler/src/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/PrimitiveStamp.java index 7322fd9f634c..d9a6149b7a60 100644 --- a/compiler/src/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/PrimitiveStamp.java +++ b/compiler/src/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/PrimitiveStamp.java @@ -39,6 +39,11 @@ protected PrimitiveStamp(int bits, ArithmeticOpTable ops) { this.bits = bits; } + @Override + public void accept(Visitor v) { + v.visitInt(bits); + } + /** * The width in bits of the value described by this stamp. */ diff --git a/compiler/src/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/Stamp.java b/compiler/src/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/Stamp.java index 29a9e6772b6e..f1f0dde3e4b1 100644 --- a/compiler/src/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/Stamp.java +++ b/compiler/src/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/Stamp.java @@ -26,6 +26,7 @@ import org.graalvm.compiler.core.common.LIRKind; import org.graalvm.compiler.core.common.spi.LIRKindTool; +import org.graalvm.compiler.serviceprovider.SpeculationReasonGroup.SpeculationContextObject; import jdk.vm.ci.meta.Constant; import jdk.vm.ci.meta.JavaKind; @@ -36,7 +37,7 @@ /** * A stamp is the basis for a type system. */ -public abstract class Stamp { +public abstract class Stamp implements SpeculationContextObject { protected Stamp() { } @@ -193,4 +194,7 @@ public boolean neverDistinct(Stamp other) { public SymbolicJVMCIReference makeSymbolic() { return null; } + + @Override + public abstract String toString(); } diff --git a/compiler/src/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/VoidStamp.java b/compiler/src/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/VoidStamp.java index 85fab2adc2f6..b82d4308e65d 100644 --- a/compiler/src/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/VoidStamp.java +++ b/compiler/src/org.graalvm.compiler.core.common/src/org/graalvm/compiler/core/common/type/VoidStamp.java @@ -42,6 +42,10 @@ public final class VoidStamp extends Stamp { private VoidStamp() { } + @Override + public void accept(Visitor v) { + } + @Override public Stamp unrestricted() { return this; diff --git a/compiler/src/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/VerifyCallerSensitiveMethods.java b/compiler/src/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/VerifyCallerSensitiveMethods.java index 30a6173974a2..ac88ed97f372 100644 --- a/compiler/src/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/VerifyCallerSensitiveMethods.java +++ b/compiler/src/org.graalvm.compiler.core.test/src/org/graalvm/compiler/core/test/VerifyCallerSensitiveMethods.java @@ -24,7 +24,7 @@ */ package org.graalvm.compiler.core.test; -import static org.graalvm.compiler.serviceprovider.GraalServices.Java8OrEarlier; +import static org.graalvm.compiler.serviceprovider.JavaVersionUtil.Java8OrEarlier; import java.lang.annotation.Annotation; diff --git a/compiler/src/org.graalvm.compiler.core/src/org/graalvm/compiler/core/gen/LIRCompilerBackend.java b/compiler/src/org.graalvm.compiler.core/src/org/graalvm/compiler/core/gen/LIRCompilerBackend.java index b9b4330dfb68..c49b0b12fdd8 100644 --- a/compiler/src/org.graalvm.compiler.core/src/org/graalvm/compiler/core/gen/LIRCompilerBackend.java +++ b/compiler/src/org.graalvm.compiler.core/src/org/graalvm/compiler/core/gen/LIRCompilerBackend.java @@ -66,6 +66,7 @@ import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.ResolvedJavaField; import jdk.vm.ci.meta.ResolvedJavaMethod; +import jdk.vm.ci.meta.SpeculationLog; import jdk.vm.ci.meta.VMConstant; public class LIRCompilerBackend { @@ -83,7 +84,17 @@ public static void emitBackEnd(StructuredGraph gra try (DebugContext.Scope s2 = debug.scope("CodeGen", lirGen, lirGen.getLIR())) { int bytecodeSize = graph.method() == null ? 0 : graph.getBytecodeSize(); compilationResult.setHasUnsafeAccess(graph.hasUnsafeAccess()); - emitCode(backend, graph.getAssumptions(), graph.method(), graph.getMethods(), graph.getFields(), bytecodeSize, lirGen, compilationResult, installedCodeOwner, factory); + emitCode(backend, + graph.getAssumptions(), + graph.method(), + graph.getMethods(), + graph.getFields(), + graph.getSpeculationLog(), + bytecodeSize, + lirGen, + compilationResult, + installedCodeOwner, + factory); } catch (Throwable e) { throw debug.handle(e); } @@ -172,8 +183,17 @@ private static LIRGenerationResult emitLowLevel(TargetDescription target, LIRGen } @SuppressWarnings("try") - public static void emitCode(Backend backend, Assumptions assumptions, ResolvedJavaMethod rootMethod, Collection inlinedMethods, EconomicSet accessedFields, - int bytecodeSize, LIRGenerationResult lirGenRes, CompilationResult compilationResult, ResolvedJavaMethod installedCodeOwner, CompilationResultBuilderFactory factory) { + public static void emitCode(Backend backend, + Assumptions assumptions, + ResolvedJavaMethod rootMethod, + Collection inlinedMethods, + EconomicSet accessedFields, + SpeculationLog speculationLog, + int bytecodeSize, + LIRGenerationResult lirGenRes, + CompilationResult compilationResult, + ResolvedJavaMethod installedCodeOwner, + CompilationResultBuilderFactory factory) { DebugContext debug = lirGenRes.getLIR().getDebug(); try (DebugCloseable a = EmitCode.start(debug)) { LIRGenerationProvider lirBackend = (LIRGenerationProvider) backend; @@ -189,6 +209,9 @@ public static void emitCode(Backend backend, Assumptions assumptions, ResolvedJa compilationResult.setFields(accessedFields); compilationResult.setBytecodeSize(bytecodeSize); } + if (speculationLog != null) { + compilationResult.setSpeculationLog(speculationLog); + } crb.finish(); if (debug.isCountEnabled()) { List ldp = compilationResult.getDataPatches(); diff --git a/compiler/src/org.graalvm.compiler.core/src/org/graalvm/compiler/core/target/Backend.java b/compiler/src/org.graalvm.compiler.core/src/org/graalvm/compiler/core/target/Backend.java index 33253ed45aaf..3d398832fdb8 100644 --- a/compiler/src/org.graalvm.compiler.core/src/org/graalvm/compiler/core/target/Backend.java +++ b/compiler/src/org.graalvm.compiler.core/src/org/graalvm/compiler/core/target/Backend.java @@ -54,7 +54,6 @@ import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.MetaAccessProvider; import jdk.vm.ci.meta.ResolvedJavaMethod; -import jdk.vm.ci.meta.SpeculationLog; /** * Represents a compiler backend for Graal. @@ -124,21 +123,28 @@ public LIRKind getValueKind(JavaKind javaKind) { /** * @see #createInstalledCode(DebugContext, ResolvedJavaMethod, CompilationRequest, - * CompilationResult, SpeculationLog, InstalledCode, boolean, Object[]) + * CompilationResult, InstalledCode, boolean, Object[]) */ - public InstalledCode createInstalledCode(DebugContext debug, ResolvedJavaMethod method, CompilationResult compilationResult, - SpeculationLog speculationLog, InstalledCode predefinedInstalledCode, boolean isDefault) { - return createInstalledCode(debug, method, null, compilationResult, speculationLog, predefinedInstalledCode, isDefault, null); + public InstalledCode createInstalledCode(DebugContext debug, + ResolvedJavaMethod method, + CompilationResult compilationResult, + InstalledCode predefinedInstalledCode, + boolean isDefault) { + return createInstalledCode(debug, method, null, compilationResult, predefinedInstalledCode, isDefault, null); } /** * @see #createInstalledCode(DebugContext, ResolvedJavaMethod, CompilationRequest, - * CompilationResult, SpeculationLog, InstalledCode, boolean, Object[]) + * CompilationResult, InstalledCode, boolean, Object[]) */ @SuppressWarnings("try") - public InstalledCode createInstalledCode(DebugContext debug, ResolvedJavaMethod method, CompilationRequest compilationRequest, CompilationResult compilationResult, - SpeculationLog speculationLog, InstalledCode predefinedInstalledCode, boolean isDefault) { - return createInstalledCode(debug, method, compilationRequest, compilationResult, speculationLog, predefinedInstalledCode, isDefault, null); + public InstalledCode createInstalledCode(DebugContext debug, + ResolvedJavaMethod method, + CompilationRequest compilationRequest, + CompilationResult compilationResult, + InstalledCode predefinedInstalledCode, + boolean isDefault) { + return createInstalledCode(debug, method, compilationRequest, compilationResult, predefinedInstalledCode, isDefault, null); } /** @@ -151,7 +157,6 @@ public InstalledCode createInstalledCode(DebugContext debug, ResolvedJavaMethod * @param predefinedInstalledCode a pre-allocated {@link InstalledCode} object to use as a * reference to the installed code. If {@code null}, a new {@link InstalledCode} * object will be created. - * @param speculationLog the speculation log to be used * @param isDefault specifies if the installed code should be made the default implementation of * {@code compRequest.getMethod()}. The default implementation for a method is the * code executed for standard calls to the method. This argument is ignored if @@ -164,8 +169,13 @@ public InstalledCode createInstalledCode(DebugContext debug, ResolvedJavaMethod * {@link InstalledCode} object */ @SuppressWarnings("try") - public InstalledCode createInstalledCode(DebugContext debug, ResolvedJavaMethod method, CompilationRequest compilationRequest, CompilationResult compilationResult, - SpeculationLog speculationLog, InstalledCode predefinedInstalledCode, boolean isDefault, Object[] context) { + public InstalledCode createInstalledCode(DebugContext debug, + ResolvedJavaMethod method, + CompilationRequest compilationRequest, + CompilationResult compilationResult, + InstalledCode predefinedInstalledCode, + boolean isDefault, + Object[] context) { Object[] debugContext = context != null ? context : new Object[]{getProviders().getCodeCache(), method, compilationResult}; CodeInstallationTask[] tasks; synchronized (this) { @@ -181,7 +191,7 @@ public InstalledCode createInstalledCode(DebugContext debug, ResolvedJavaMethod try { preCodeInstallationTasks(tasks, compilationResult); CompiledCode compiledCode = createCompiledCode(method, compilationRequest, compilationResult, isDefault, debug.getOptions()); - installedCode = getProviders().getCodeCache().installCode(method, compiledCode, predefinedInstalledCode, speculationLog, isDefault); + installedCode = getProviders().getCodeCache().installCode(method, compiledCode, predefinedInstalledCode, compilationResult.getSpeculationLog(), isDefault); assert predefinedInstalledCode == null || installedCode == predefinedInstalledCode; } catch (Throwable t) { failCodeInstallationTasks(tasks, t); @@ -225,12 +235,15 @@ private static void postCodeInstallationTasks(CodeInstallationTask[] tasks, Comp * @param method the method compiled to produce {@code compiledCode} or {@code null} if the * input to {@code compResult} was not a {@link ResolvedJavaMethod} * @param compilationRequest the request or {@code null} - * @param compilationResult the code to be compiled + * @param compilationResult the compiled code * @return a reference to the compiled and ready-to-run installed code * @throws BailoutException if the code installation failed */ - public InstalledCode addInstalledCode(DebugContext debug, ResolvedJavaMethod method, CompilationRequest compilationRequest, CompilationResult compilationResult) { - return createInstalledCode(debug, method, compilationRequest, compilationResult, null, null, false); + public InstalledCode addInstalledCode(DebugContext debug, + ResolvedJavaMethod method, + CompilationRequest compilationRequest, + CompilationResult compilationResult) { + return createInstalledCode(debug, method, compilationRequest, compilationResult, null, false); } /** @@ -239,12 +252,13 @@ public InstalledCode addInstalledCode(DebugContext debug, ResolvedJavaMethod met * * @param method the method compiled to produce {@code compiledCode} or {@code null} if the * input to {@code compResult} was not a {@link ResolvedJavaMethod} - * @param compilationResult the code to be compiled + * @param compilationResult the compiled code * @return a reference to the compiled and ready-to-run installed code * @throws BailoutException if the code installation failed */ public InstalledCode createDefaultInstalledCode(DebugContext debug, ResolvedJavaMethod method, CompilationResult compilationResult) { - return createInstalledCode(debug, method, compilationResult, null, null, true); + System.out.println(compilationResult.getSpeculationLog()); + return createInstalledCode(debug, method, compilationResult, null, true); } /** @@ -257,7 +271,11 @@ public CompilationIdentifier getCompilationIdentifier(ResolvedJavaMethod resolve return CompilationIdentifier.INVALID_COMPILATION_ID; } - public void emitBackEnd(StructuredGraph graph, Object stub, ResolvedJavaMethod installedCodeOwner, CompilationResult compilationResult, CompilationResultBuilderFactory factory, + public void emitBackEnd(StructuredGraph graph, + Object stub, + ResolvedJavaMethod installedCodeOwner, + CompilationResult compilationResult, + CompilationResultBuilderFactory factory, RegisterConfig config, LIRSuites lirSuites) { LIRCompilerBackend.emitBackEnd(graph, stub, installedCodeOwner, this, compilationResult, factory, config, lirSuites); } diff --git a/compiler/src/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugContext.java b/compiler/src/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugContext.java index 730f6a41d970..2585f673aafc 100644 --- a/compiler/src/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugContext.java +++ b/compiler/src/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/DebugContext.java @@ -450,11 +450,11 @@ private DebugContext(Description description, GlobalMetrics globalMetrics, Print } } - public Path getDumpPath(String extension, boolean directory) { + public Path getDumpPath(String extension, boolean createMissingDirectory) { try { String id = description == null ? null : description.identifier; String label = description == null ? null : description.getLabel(); - Path result = PathUtilities.createUnique(immutable.options, DumpPath, id, label, extension, directory); + Path result = PathUtilities.createUnique(immutable.options, DumpPath, id, label, extension, createMissingDirectory); if (ShowDumpFiles.getValue(immutable.options)) { TTY.println("Dumping debug output to %s", result.toAbsolutePath().toString()); } @@ -807,8 +807,9 @@ public boolean inNestedScope() { return true; } return !currentScope.isTopLevel(); + } else { + return false; } - return immutable.scopesEnabled && currentScope == null; } class DisabledScope implements DebugContext.Scope { diff --git a/compiler/src/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/IgvDumpChannel.java b/compiler/src/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/IgvDumpChannel.java index 60df95b3ae3a..537e61e5c1d0 100644 --- a/compiler/src/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/IgvDumpChannel.java +++ b/compiler/src/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/IgvDumpChannel.java @@ -27,6 +27,7 @@ import static org.graalvm.compiler.debug.DebugOptions.PrintGraphHost; import static org.graalvm.compiler.debug.DebugOptions.PrintGraphPort; +import java.io.File; import java.io.IOException; import java.io.InterruptedIOException; import java.net.InetSocketAddress; @@ -42,6 +43,8 @@ import org.graalvm.compiler.debug.DebugOptions.PrintGraphTarget; import org.graalvm.compiler.options.OptionValues; +import jdk.vm.ci.common.NativeImageReinitialize; + final class IgvDumpChannel implements WritableByteChannel { private final Supplier pathProvider; private final OptionValues options; @@ -83,7 +86,7 @@ WritableByteChannel channel() throws IOException { if (sharedChannel == null) { PrintGraphTarget target = DebugOptions.PrintGraph.getValue(options); if (target == PrintGraphTarget.File) { - sharedChannel = createFileChannel(pathProvider); + sharedChannel = createFileChannel(pathProvider, null); } else if (target == PrintGraphTarget.Network) { sharedChannel = createNetworkChannel(pathProvider, options); } else { @@ -98,7 +101,8 @@ private static WritableByteChannel createNetworkChannel(Supplier pathProvi int port = PrintGraphPort.getValue(options); try { WritableByteChannel channel = SocketChannel.open(new InetSocketAddress(host, port)); - TTY.println("Connected to the IGV on %s:%d", host, port); + String targetAnnouncement = String.format("Connected to the IGV on %s:%d", host, port); + maybeAnnounceTarget(targetAnnouncement); return channel; } catch (ClosedByInterruptException | InterruptedIOException e) { /* @@ -108,18 +112,39 @@ private static WritableByteChannel createNetworkChannel(Supplier pathProvi */ return null; } catch (IOException e) { + String networkFailure = String.format("Could not connect to the IGV on %s:%d", host, port); if (pathProvider != null) { - return createFileChannel(pathProvider); + return createFileChannel(pathProvider, networkFailure); } else { - throw new IOException(String.format("Could not connect to the IGV on %s:%d", host, port), e); + throw new IOException(networkFailure, e); } } } - private static WritableByteChannel createFileChannel(Supplier pathProvider) throws IOException { + @NativeImageReinitialize private static String lastTargetAnnouncement; + + private static void maybeAnnounceTarget(String targetAnnouncement) { + if (!targetAnnouncement.equals(lastTargetAnnouncement)) { + // Ignore races - an extra announcement is ok + lastTargetAnnouncement = targetAnnouncement; + TTY.println(targetAnnouncement); + } + } + + private static WritableByteChannel createFileChannel(Supplier pathProvider, String networkFailure) throws IOException { Path path = pathProvider.get(); try { - return FileChannel.open(path, StandardOpenOption.WRITE, StandardOpenOption.CREATE); + FileChannel channel = FileChannel.open(path, StandardOpenOption.WRITE, StandardOpenOption.CREATE); + File dir = path.toFile(); + if (!dir.isDirectory()) { + dir = dir.getParentFile(); + } + if (networkFailure == null) { + maybeAnnounceTarget("Dumping IGV graphs in " + dir); + } else { + maybeAnnounceTarget(networkFailure + ". Dumping IGV graphs in " + dir); + } + return channel; } catch (IOException e) { throw new IOException(String.format("Failed to open %s to dump IGV graphs", path), e); } diff --git a/compiler/src/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/PathUtilities.java b/compiler/src/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/PathUtilities.java index 2c693f5c73ee..d0699eb6b7a1 100644 --- a/compiler/src/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/PathUtilities.java +++ b/compiler/src/org.graalvm.compiler.debug/src/org/graalvm/compiler/debug/PathUtilities.java @@ -84,7 +84,7 @@ public static String sanitizeFileName(String name) { private static final String ELLIPSIS = "..."; - static Path createUnique(OptionValues options, OptionKey baseNameOption, String id, String label, String ext, boolean createDirectory) throws IOException { + static Path createUnique(OptionValues options, OptionKey baseNameOption, String id, String label, String ext, boolean createMissingDirectory) throws IOException { String uniqueTag = ""; int dumpCounter = 1; String prefix; @@ -118,7 +118,7 @@ static Path createUnique(OptionValues options, OptionKey baseNameOption, Path dumpDir = DebugOptions.getDumpDirectory(options); Path result = Paths.get(dumpDir.toString(), fileName); try { - if (createDirectory) { + if (createMissingDirectory) { return Files.createDirectory(result); } else { try { diff --git a/compiler/src/org.graalvm.compiler.hotspot.aarch64/src/org/graalvm/compiler/hotspot/aarch64/AArch64HotSpotJumpToExceptionHandlerInCallerOp.java b/compiler/src/org.graalvm.compiler.hotspot.aarch64/src/org/graalvm/compiler/hotspot/aarch64/AArch64HotSpotJumpToExceptionHandlerInCallerOp.java index e492a1e8918a..c4fdb742c62f 100644 --- a/compiler/src/org.graalvm.compiler.hotspot.aarch64/src/org/graalvm/compiler/hotspot/aarch64/AArch64HotSpotJumpToExceptionHandlerInCallerOp.java +++ b/compiler/src/org.graalvm.compiler.hotspot.aarch64/src/org/graalvm/compiler/hotspot/aarch64/AArch64HotSpotJumpToExceptionHandlerInCallerOp.java @@ -37,7 +37,7 @@ import org.graalvm.compiler.lir.LIRInstructionClass; import org.graalvm.compiler.lir.Opcode; import org.graalvm.compiler.lir.asm.CompilationResultBuilder; -import org.graalvm.compiler.serviceprovider.GraalServices; +import org.graalvm.compiler.serviceprovider.JavaVersionUtil; import jdk.vm.ci.code.Register; import jdk.vm.ci.meta.AllocatableValue; @@ -71,7 +71,7 @@ public AArch64HotSpotJumpToExceptionHandlerInCallerOp(AllocatableValue handlerIn public void emitCode(CompilationResultBuilder crb, AArch64MacroAssembler masm) { leaveFrame(crb, masm, /* emitSafepoint */false, false); - if (GraalServices.JAVA_SPECIFICATION_VERSION < 8) { + if (JavaVersionUtil.JAVA_SPECIFICATION_VERSION < 8) { // Restore sp from fp if the exception PC is a method handle call site. try (ScratchRegister sc = masm.getScratchRegister()) { Register scratch = sc.getRegister(); diff --git a/compiler/src/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotJumpToExceptionHandlerInCallerOp.java b/compiler/src/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotJumpToExceptionHandlerInCallerOp.java index 4755526c3f01..a237daf3b3cd 100644 --- a/compiler/src/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotJumpToExceptionHandlerInCallerOp.java +++ b/compiler/src/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotJumpToExceptionHandlerInCallerOp.java @@ -24,10 +24,10 @@ */ package org.graalvm.compiler.hotspot.amd64; -import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.REG; import static jdk.vm.ci.amd64.AMD64.rbp; import static jdk.vm.ci.amd64.AMD64.rsp; import static jdk.vm.ci.code.ValueUtil.asRegister; +import static org.graalvm.compiler.lir.LIRInstruction.OperandFlag.REG; import org.graalvm.compiler.asm.amd64.AMD64Address; import org.graalvm.compiler.asm.amd64.AMD64Assembler.ConditionFlag; @@ -35,7 +35,7 @@ import org.graalvm.compiler.lir.LIRInstructionClass; import org.graalvm.compiler.lir.Opcode; import org.graalvm.compiler.lir.asm.CompilationResultBuilder; -import org.graalvm.compiler.serviceprovider.GraalServices; +import org.graalvm.compiler.serviceprovider.JavaVersionUtil; import jdk.vm.ci.code.Register; import jdk.vm.ci.meta.AllocatableValue; @@ -71,7 +71,7 @@ public void emitCode(CompilationResultBuilder crb, AMD64MacroAssembler masm) { // Discard the return address, thus completing restoration of caller frame masm.incrementq(rsp, 8); - if (GraalServices.JAVA_SPECIFICATION_VERSION < 8) { + if (JavaVersionUtil.JAVA_SPECIFICATION_VERSION < 8) { // Restore rsp from rbp if the exception PC is a method handle call site. AMD64Address dst = new AMD64Address(thread, isMethodHandleReturnOffset); masm.cmpl(dst, 0); diff --git a/compiler/src/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotLoweringProvider.java b/compiler/src/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotLoweringProvider.java index 34aaf9c163de..94b34d5e4551 100644 --- a/compiler/src/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotLoweringProvider.java +++ b/compiler/src/org.graalvm.compiler.hotspot.amd64/src/org/graalvm/compiler/hotspot/amd64/AMD64HotSpotLoweringProvider.java @@ -45,7 +45,7 @@ import org.graalvm.compiler.replacements.amd64.AMD64ConvertSnippets; import org.graalvm.compiler.replacements.nodes.UnaryMathIntrinsicNode; import org.graalvm.compiler.replacements.nodes.UnaryMathIntrinsicNode.UnaryOperation; -import org.graalvm.compiler.serviceprovider.GraalServices; +import org.graalvm.compiler.serviceprovider.JavaVersionUtil; import jdk.vm.ci.code.TargetDescription; import jdk.vm.ci.hotspot.HotSpotConstantReflectionProvider; @@ -66,7 +66,7 @@ public AMD64HotSpotLoweringProvider(HotSpotGraalRuntimeProvider runtime, MetaAcc @Override public void initialize(OptionValues options, Iterable factories, HotSpotProviders providers, GraalHotSpotVMConfig config) { convertSnippets = new AMD64ConvertSnippets.Templates(options, factories, providers, providers.getSnippetReflection(), providers.getCodeCache().getTarget()); - profileSnippets = ProfileNode.Options.ProbabilisticProfiling.getValue(options) && !GraalServices.Java8OrEarlier + profileSnippets = ProfileNode.Options.ProbabilisticProfiling.getValue(options) && !JavaVersionUtil.Java8OrEarlier ? new ProbabilisticProfileSnippets.Templates(options, factories, providers, providers.getCodeCache().getTarget()) : null; mathSnippets = new AMD64X87MathSnippets.Templates(options, factories, providers, providers.getSnippetReflection(), providers.getCodeCache().getTarget()); diff --git a/compiler/src/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/CheckGraalIntrinsics.java b/compiler/src/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/CheckGraalIntrinsics.java index 873256ef2f08..b349a2d503fe 100644 --- a/compiler/src/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/CheckGraalIntrinsics.java +++ b/compiler/src/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/CheckGraalIntrinsics.java @@ -47,7 +47,7 @@ import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins; import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins.Binding; import org.graalvm.compiler.runtime.RuntimeProvider; -import org.graalvm.compiler.serviceprovider.GraalServices; +import org.graalvm.compiler.serviceprovider.JavaVersionUtil; import org.graalvm.compiler.test.GraalTest; import org.junit.Test; @@ -555,23 +555,23 @@ public CheckGraalIntrinsics() { } private static boolean isJDK9OrHigher() { - return GraalServices.JAVA_SPECIFICATION_VERSION >= 9; + return JavaVersionUtil.JAVA_SPECIFICATION_VERSION >= 9; } private static boolean isJDK10OrHigher() { - return GraalServices.JAVA_SPECIFICATION_VERSION >= 10; + return JavaVersionUtil.JAVA_SPECIFICATION_VERSION >= 10; } private static boolean isJDK11OrHigher() { - return GraalServices.JAVA_SPECIFICATION_VERSION >= 11; + return JavaVersionUtil.JAVA_SPECIFICATION_VERSION >= 11; } private static boolean isJDK12OrHigher() { - return GraalServices.JAVA_SPECIFICATION_VERSION >= 12; + return JavaVersionUtil.JAVA_SPECIFICATION_VERSION >= 12; } private static boolean isJDK13OrHigher() { - return GraalServices.JAVA_SPECIFICATION_VERSION >= 13; + return JavaVersionUtil.JAVA_SPECIFICATION_VERSION >= 13; } public interface Refiner { diff --git a/compiler/src/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/CompileTheWorld.java b/compiler/src/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/CompileTheWorld.java index b528708c6d25..fb1ad3d3d239 100644 --- a/compiler/src/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/CompileTheWorld.java +++ b/compiler/src/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/CompileTheWorld.java @@ -31,7 +31,7 @@ import static org.graalvm.compiler.core.test.ReflectionOptionDescriptors.extractEntries; import static org.graalvm.compiler.debug.MemUseTrackerKey.getCurrentThreadAllocatedBytes; import static org.graalvm.compiler.hotspot.test.CompileTheWorld.Options.DESCRIPTORS; -import static org.graalvm.compiler.serviceprovider.GraalServices.Java8OrEarlier; +import static org.graalvm.compiler.serviceprovider.JavaVersionUtil.Java8OrEarlier; import java.io.Closeable; import java.io.File; @@ -90,7 +90,7 @@ import org.graalvm.compiler.options.OptionKey; import org.graalvm.compiler.options.OptionValues; import org.graalvm.compiler.options.OptionsParser; -import org.graalvm.compiler.serviceprovider.GraalServices; +import org.graalvm.compiler.serviceprovider.JavaVersionUtil; import jdk.vm.ci.hotspot.HotSpotCodeCacheProvider; import jdk.vm.ci.hotspot.HotSpotCompilationRequest; @@ -110,7 +110,7 @@ public final class CompileTheWorld { /** * Magic token to denote that JDK classes are to be compiled. If - * {@link GraalServices#Java8OrEarlier}, then the classes in {@code rt.jar} are compiled. + * {@link JavaVersionUtil#Java8OrEarlier}, then the classes in {@code rt.jar} are compiled. * Otherwise the classes in the Java runtime image are compiled. */ public static final String SUN_BOOT_CLASS_PATH = "sun.boot.class.path"; diff --git a/compiler/src/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/ReservedStackAccessTest.java b/compiler/src/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/ReservedStackAccessTest.java index d245ed00330b..a8df9335c0c9 100644 --- a/compiler/src/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/ReservedStackAccessTest.java +++ b/compiler/src/org.graalvm.compiler.hotspot.test/src/org/graalvm/compiler/hotspot/test/ReservedStackAccessTest.java @@ -73,6 +73,10 @@ public void run() throws IOException, InterruptedException { vmArgs.add("-XX:+UseJVMCICompiler"); vmArgs.add("-Dgraal.Inline=false"); vmArgs.add("-XX:CompileCommand=exclude,java/util/concurrent/locks/AbstractOwnableSynchronizer.setExclusiveOwnerThread"); + + // Avoid SOE in HotSpotJVMCIRuntime.adjustCompilationLevel + vmArgs.add("-Dgraal.CompileGraalWithC1Only=false"); + Subprocess proc = SubprocessUtil.java(vmArgs, ReservedStackAccessTest.class.getName()); boolean passed = false; for (String line : proc.output) { @@ -81,9 +85,7 @@ public void run() throws IOException, InterruptedException { } } if (!passed) { - for (String line : proc.output) { - System.err.println("" + line); - } + System.err.println(proc); } assertTrue(passed); } diff --git a/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/CompilationTask.java b/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/CompilationTask.java index b011c8c4580e..a6a70673e48e 100644 --- a/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/CompilationTask.java +++ b/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/CompilationTask.java @@ -368,8 +368,14 @@ private void installMethod(DebugContext debug, final CompilationResult compResul installedCode = null; Object[] context = {new DebugDumpScope(getIdString(), true), codeCache, getMethod(), compResult}; try (DebugContext.Scope s = debug.scope("CodeInstall", context)) { - installedCode = (HotSpotInstalledCode) backend.createInstalledCode(debug, getRequest().getMethod(), getRequest(), compResult, - getRequest().getMethod().getSpeculationLog(), null, installAsDefault, context); + HotSpotCompilationRequest request = getRequest(); + installedCode = (HotSpotInstalledCode) backend.createInstalledCode(debug, + request.getMethod(), + request, + compResult, + null, + installAsDefault, + context); } catch (Throwable e) { throw debug.handle(e); } diff --git a/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/GraalHotSpotVMConfig.java b/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/GraalHotSpotVMConfig.java index e637f48dad0d..e88d7e43e780 100644 --- a/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/GraalHotSpotVMConfig.java +++ b/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/GraalHotSpotVMConfig.java @@ -389,12 +389,29 @@ public boolean requiresReservedStackCheck(List methods) { public final int pendingExceptionOffset = getFieldOffset("ThreadShadow::_pending_exception", Integer.class, "oop"); public final int pendingDeoptimizationOffset = getFieldOffset("JavaThread::_pending_deoptimization", Integer.class, "int"); - public final int pendingFailedSpeculationOffset = getFieldOffset("JavaThread::_pending_failed_speculation", Integer.class, "long"); public final int pendingTransferToInterpreterOffset = getFieldOffset("JavaThread::_pending_transfer_to_interpreter", Integer.class, "bool"); private final int javaFrameAnchorLastJavaSpOffset = getFieldOffset("JavaFrameAnchor::_last_Java_sp", Integer.class, "intptr_t*"); private final int javaFrameAnchorLastJavaPcOffset = getFieldOffset("JavaFrameAnchor::_last_Java_pc", Integer.class, "address"); + public final int pendingFailedSpeculationOffset; + { + String name = "JavaThread::_pending_failed_speculation"; + int offset = -1; + try { + offset = getFieldOffset(name, Integer.class, "jlong"); + } catch (JVMCIError e) { + try { + offset = getFieldOffset(name, Integer.class, "long"); + } catch (JVMCIError e2) { + } + } + if (offset == -1) { + throw new JVMCIError("cannot get offset of field " + name + " with type long or jlong"); + } + pendingFailedSpeculationOffset = offset; + } + public int threadLastJavaSpOffset() { return javaThreadAnchorOffset + javaFrameAnchorLastJavaSpOffset; } diff --git a/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotBackend.java b/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotBackend.java index 716cfce524d0..e5c36b59a814 100644 --- a/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotBackend.java +++ b/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotBackend.java @@ -71,7 +71,7 @@ import org.graalvm.compiler.options.OptionType; import org.graalvm.compiler.options.OptionValues; import org.graalvm.compiler.phases.tiers.SuitesProvider; -import org.graalvm.compiler.serviceprovider.GraalServices; +import org.graalvm.compiler.serviceprovider.JavaVersionUtil; import org.graalvm.compiler.word.Word; import org.graalvm.word.Pointer; @@ -96,7 +96,7 @@ public abstract class HotSpotBackend extends Backend implements FrameMap.Referen public static class Options { // @formatter:off @Option(help = "Use Graal arithmetic stubs instead of HotSpot stubs where possible") - public static final OptionKey GraalArithmeticStubs = new OptionKey<>(GraalServices.JAVA_SPECIFICATION_VERSION >= 9); + public static final OptionKey GraalArithmeticStubs = new OptionKey<>(JavaVersionUtil.JAVA_SPECIFICATION_VERSION >= 9); @Option(help = "Enables instruction profiling on assembler level. Valid values are a comma separated list of supported instructions." + " Compare with subclasses of Assembler.InstructionCounter.", type = OptionType.Debug) public static final OptionKey ASMInstructionProfiling = new OptionKey<>(null); diff --git a/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotGraalRuntime.java b/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotGraalRuntime.java index 9685ba5cabaa..803eaed225e2 100644 --- a/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotGraalRuntime.java +++ b/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/HotSpotGraalRuntime.java @@ -95,6 +95,8 @@ */ public final class HotSpotGraalRuntime implements HotSpotGraalRuntimeProvider { + private static final boolean IS_AOT = Boolean.getBoolean("com.oracle.graalvm.isaot"); + private static boolean checkArrayIndexScaleInvariants(MetaAccessProvider metaAccess) { assert metaAccess.getArrayIndexScale(JavaKind.Byte) == 1; assert metaAccess.getArrayIndexScale(JavaKind.Boolean) == 1; @@ -159,9 +161,13 @@ private static boolean checkArrayIndexScaleInvariants(MetaAccessProvider metaAcc compilerConfigurationName = compilerConfigurationFactory.getName(); compiler = new HotSpotGraalCompiler(jvmciRuntime, this, options); - management = GraalServices.loadSingle(HotSpotGraalManagementRegistration.class, false); - if (management != null) { - management.initialize(this); + if (IS_AOT) { + management = null; + } else { + management = GraalServices.loadSingle(HotSpotGraalManagementRegistration.class, false); + if (management != null) { + management.initialize(this); + } } BackendMap backendMap = compilerConfigurationFactory.createBackendMap(); diff --git a/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/JVMCIVersionCheck.java b/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/JVMCIVersionCheck.java index 14a94618562d..9619293353ac 100644 --- a/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/JVMCIVersionCheck.java +++ b/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/JVMCIVersionCheck.java @@ -38,8 +38,9 @@ */ class JVMCIVersionCheck { + // 0.55 introduces new HotSpotSpeculationLog API private static final int JVMCI8_MIN_MAJOR_VERSION = 0; - private static final int JVMCI8_MIN_MINOR_VERSION = 54; + private static final int JVMCI8_MIN_MINOR_VERSION = 55; private static void failVersionCheck(boolean exit, String reason, Object... args) { Formatter errorMessage = new Formatter().format(reason, args); diff --git a/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/SymbolicSnippetEncoder.java b/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/SymbolicSnippetEncoder.java index 53c75d7118dc..f8a44b7dc93c 100644 --- a/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/SymbolicSnippetEncoder.java +++ b/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/SymbolicSnippetEncoder.java @@ -918,7 +918,7 @@ static String getCanonicalGraphString(StructuredGraph graph, boolean excludeVirt if (!excludeVirtual || !(node instanceof VirtualObjectNode || node instanceof ProxyNode || node instanceof FullInfopointNode || node instanceof ParameterNode)) { if (node instanceof ConstantNode) { if (checkConstants) { - String name = checkConstants ? node.toString(Verbosity.Name) : node.getClass().getSimpleName(); + String name = node.toString(Verbosity.Name); if (excludeVirtual) { constantsLines.add(name); } else { diff --git a/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/DefaultHotSpotLoweringProvider.java b/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/DefaultHotSpotLoweringProvider.java index 9ffa4292f36b..ddffa772d281 100644 --- a/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/DefaultHotSpotLoweringProvider.java +++ b/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/DefaultHotSpotLoweringProvider.java @@ -163,7 +163,7 @@ import org.graalvm.compiler.replacements.arraycopy.ArrayCopySnippets; import org.graalvm.compiler.replacements.arraycopy.ArrayCopyWithSlowPathNode; import org.graalvm.compiler.replacements.nodes.AssertionNode; -import org.graalvm.compiler.serviceprovider.GraalServices; +import org.graalvm.compiler.serviceprovider.JavaVersionUtil; import org.graalvm.word.LocationIdentity; import jdk.vm.ci.code.TargetDescription; @@ -227,7 +227,7 @@ public void initialize(OptionValues options, Iterable fact stringToBytesSnippets = new StringToBytesSnippets.Templates(options, factories, providers, target); hashCodeSnippets = new HashCodeSnippets.Templates(options, factories, providers, target); resolveConstantSnippets = new ResolveConstantSnippets.Templates(options, factories, providers, target); - if (!GraalServices.Java8OrEarlier) { + if (!JavaVersionUtil.Java8OrEarlier) { profileSnippets = new ProfileSnippets.Templates(options, factories, providers, target); } objectCloneSnippets = new ObjectCloneSnippets.Templates(options, factories, providers, target); diff --git a/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotGraphBuilderPlugins.java b/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotGraphBuilderPlugins.java index 95bc443a6698..bbfdf3b6a409 100644 --- a/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotGraphBuilderPlugins.java +++ b/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotGraphBuilderPlugins.java @@ -29,7 +29,7 @@ import static org.graalvm.compiler.hotspot.meta.HotSpotAOTProfilingPlugin.Options.TieredAOT; import static org.graalvm.compiler.hotspot.replacements.HotSpotReplacementsUtil.JAVA_THREAD_THREAD_OBJECT_LOCATION; import static org.graalvm.compiler.java.BytecodeParserOptions.InlineDuringParsing; -import static org.graalvm.compiler.serviceprovider.GraalServices.Java8OrEarlier; +import static org.graalvm.compiler.serviceprovider.JavaVersionUtil.Java8OrEarlier; import java.lang.invoke.ConstantCallSite; import java.lang.invoke.MutableCallSite; diff --git a/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotNodePlugin.java b/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotNodePlugin.java index 095318a63422..279ee8235f43 100644 --- a/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotNodePlugin.java +++ b/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotNodePlugin.java @@ -243,9 +243,9 @@ public FixedWithNextNode instrumentExceptionDispatch(StructuredGraph graph, Fixe } private static final LocationIdentity JAVA_THREAD_SHOULD_POST_ON_EXCEPTIONS_FLAG_LOCATION = NamedLocationIdentity.mutable("JavaThread::_should_post_on_exceptions_flag"); - private static final Unsafe UNSAFE = initUnsafe(); + static final Unsafe UNSAFE = initUnsafe(); - private static Unsafe initUnsafe() { + static Unsafe initUnsafe() { try { // Fast path when we are trusted. return Unsafe.getUnsafe(); diff --git a/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotUnsafeSubstitutions.java b/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotUnsafeSubstitutions.java index eb078cf8cdd2..b82c575885d0 100644 --- a/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotUnsafeSubstitutions.java +++ b/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/meta/HotSpotUnsafeSubstitutions.java @@ -24,7 +24,7 @@ */ package org.graalvm.compiler.hotspot.meta; -import static org.graalvm.compiler.serviceprovider.GraalServices.Java8OrEarlier; +import static org.graalvm.compiler.serviceprovider.JavaVersionUtil.Java8OrEarlier; import org.graalvm.compiler.api.replacements.ClassSubstitution; import org.graalvm.compiler.api.replacements.MethodSubstitution; diff --git a/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/type/KlassPointerStamp.java b/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/type/KlassPointerStamp.java index e2d8369ff218..88b8b5897b63 100644 --- a/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/type/KlassPointerStamp.java +++ b/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/nodes/type/KlassPointerStamp.java @@ -71,6 +71,13 @@ private KlassPointerStamp(boolean nonNull, boolean alwaysNull, CompressEncoding this.encoding = encoding; } + @Override + public void accept(Visitor v) { + super.accept(v); + v.visitLong(encoding.getBase()); + v.visitInt(encoding.getShift()); + } + @Override protected AbstractPointerStamp copyWith(boolean newNonNull, boolean newAlwaysNull) { return new KlassPointerStamp(newNonNull, newAlwaysNull, encoding); diff --git a/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/phases/OnStackReplacementPhase.java b/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/phases/OnStackReplacementPhase.java index a1b6d957b01e..075b6d18f298 100644 --- a/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/phases/OnStackReplacementPhase.java +++ b/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/phases/OnStackReplacementPhase.java @@ -72,6 +72,7 @@ import org.graalvm.compiler.options.OptionValues; import org.graalvm.compiler.phases.Phase; import org.graalvm.compiler.phases.common.DeadCodeEliminationPhase; +import org.graalvm.compiler.serviceprovider.SpeculationReasonGroup; import jdk.vm.ci.meta.DeoptimizationAction; import jdk.vm.ci.meta.DeoptimizationReason; @@ -100,6 +101,8 @@ private static boolean supportOSRWithLocks(OptionValues options) { return Options.SupportOSRWithLocks.getValue(options); } + private static final SpeculationReasonGroup OSR_LOCAL_SPECULATIONS = new SpeculationReasonGroup("OSRLocal", int.class, Stamp.class, int.class); + @Override @SuppressWarnings("try") protected void run(StructuredGraph graph) { @@ -204,7 +207,7 @@ protected void run(StructuredGraph graph) { osrLocal = graph.addOrUnique(new OSRLocalNode(i, unrestrictedStamp)); } // Speculate on the OSRLocal stamps that could be more precise. - OSRLocalSpeculationReason reason = new OSRLocalSpeculationReason(osrState.bci, narrowedStamp, i); + SpeculationReason reason = OSR_LOCAL_SPECULATIONS.createSpeculationReason(osrState.bci, narrowedStamp, i); if (graph.getSpeculationLog().maySpeculate(reason) && osrLocal instanceof OSRLocalNode && value.getStackKind().equals(JavaKind.Object) && !narrowedStamp.isUnrestricted()) { // Add guard. LogicNode check = graph.addOrUniqueWithInputs(InstanceOfNode.createHelper((ObjectStamp) narrowedStamp, osrLocal, null, null)); @@ -308,30 +311,4 @@ private static boolean osrWithLocks(EntryMarkerNode osr) { public float codeSizeIncrease() { return 5.0f; } - - private static class OSRLocalSpeculationReason implements SpeculationReason { - private int bci; - private Stamp speculatedStamp; - private int localIndex; - - OSRLocalSpeculationReason(int bci, Stamp speculatedStamp, int localIndex) { - this.bci = bci; - this.speculatedStamp = speculatedStamp; - this.localIndex = localIndex; - } - - @Override - public boolean equals(Object obj) { - if (obj instanceof OSRLocalSpeculationReason) { - OSRLocalSpeculationReason that = (OSRLocalSpeculationReason) obj; - return this.bci == that.bci && this.speculatedStamp.equals(that.speculatedStamp) && this.localIndex == that.localIndex; - } - return false; - } - - @Override - public int hashCode() { - return (bci << 16) ^ speculatedStamp.hashCode() ^ localIndex; - } - } } diff --git a/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/SHA2Substitutions.java b/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/SHA2Substitutions.java index 6aa6991524a6..bfb4c3deaa21 100644 --- a/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/SHA2Substitutions.java +++ b/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/SHA2Substitutions.java @@ -26,7 +26,7 @@ import static org.graalvm.compiler.hotspot.GraalHotSpotVMConfig.INJECTED_METAACCESS; import static org.graalvm.compiler.hotspot.GraalHotSpotVMConfigBase.INJECTED_INTRINSIC_CONTEXT; -import static org.graalvm.compiler.serviceprovider.GraalServices.Java8OrEarlier; +import static org.graalvm.compiler.serviceprovider.JavaVersionUtil.Java8OrEarlier; import org.graalvm.compiler.api.replacements.ClassSubstitution; import org.graalvm.compiler.api.replacements.Fold; diff --git a/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/SHA5Substitutions.java b/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/SHA5Substitutions.java index cdf0f2d2ae32..e0837d2c940d 100644 --- a/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/SHA5Substitutions.java +++ b/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/SHA5Substitutions.java @@ -26,7 +26,7 @@ import static org.graalvm.compiler.hotspot.GraalHotSpotVMConfigBase.INJECTED_INTRINSIC_CONTEXT; import static org.graalvm.compiler.hotspot.GraalHotSpotVMConfigBase.INJECTED_METAACCESS; -import static org.graalvm.compiler.serviceprovider.GraalServices.Java8OrEarlier; +import static org.graalvm.compiler.serviceprovider.JavaVersionUtil.Java8OrEarlier; import org.graalvm.compiler.api.replacements.ClassSubstitution; import org.graalvm.compiler.api.replacements.Fold; diff --git a/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/SHASubstitutions.java b/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/SHASubstitutions.java index ca578e8d923a..0995d0eed988 100644 --- a/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/SHASubstitutions.java +++ b/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/replacements/SHASubstitutions.java @@ -26,7 +26,7 @@ import static org.graalvm.compiler.hotspot.GraalHotSpotVMConfigBase.INJECTED_INTRINSIC_CONTEXT; import static org.graalvm.compiler.hotspot.GraalHotSpotVMConfigBase.INJECTED_METAACCESS; -import static org.graalvm.compiler.serviceprovider.GraalServices.Java8OrEarlier; +import static org.graalvm.compiler.serviceprovider.JavaVersionUtil.Java8OrEarlier; import org.graalvm.compiler.api.replacements.ClassSubstitution; import org.graalvm.compiler.api.replacements.Fold; diff --git a/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/stubs/OutOfBoundsExceptionStub.java b/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/stubs/OutOfBoundsExceptionStub.java index 7d7c952569fd..2cf7670cc426 100644 --- a/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/stubs/OutOfBoundsExceptionStub.java +++ b/compiler/src/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/stubs/OutOfBoundsExceptionStub.java @@ -34,7 +34,7 @@ import org.graalvm.compiler.hotspot.meta.HotSpotProviders; import org.graalvm.compiler.hotspot.nodes.AllocaNode; import org.graalvm.compiler.options.OptionValues; -import org.graalvm.compiler.serviceprovider.GraalServices; +import org.graalvm.compiler.serviceprovider.JavaVersionUtil; import org.graalvm.compiler.word.Word; import jdk.vm.ci.code.Register; @@ -48,7 +48,7 @@ public OutOfBoundsExceptionStub(OptionValues options, HotSpotProviders providers } // JDK-8201593: Print array length in ArrayIndexOutOfBoundsException. - private static final boolean PRINT_LENGTH_IN_EXCEPTION = GraalServices.JAVA_SPECIFICATION_VERSION >= 11; + private static final boolean PRINT_LENGTH_IN_EXCEPTION = JavaVersionUtil.JAVA_SPECIFICATION_VERSION >= 11; private static final int MAX_INT_STRING_SIZE = Integer.toString(Integer.MIN_VALUE).length(); private static final String STR_INDEX = "Index "; private static final String STR_OUTOFBOUNDSFORLENGTH = " out of bounds for length "; diff --git a/compiler/src/org.graalvm.compiler.java/src/org/graalvm/compiler/java/BytecodeParser.java b/compiler/src/org.graalvm.compiler.java/src/org/graalvm/compiler/java/BytecodeParser.java index fb8345dc844e..f4beefc36ba6 100644 --- a/compiler/src/org.graalvm.compiler.java/src/org/graalvm/compiler/java/BytecodeParser.java +++ b/compiler/src/org.graalvm.compiler.java/src/org/graalvm/compiler/java/BytecodeParser.java @@ -426,7 +426,7 @@ import org.graalvm.compiler.options.OptionValues; import org.graalvm.compiler.phases.OptimisticOptimizations; import org.graalvm.compiler.phases.util.ValueMergeUtil; -import org.graalvm.compiler.serviceprovider.GraalServices; +import org.graalvm.compiler.serviceprovider.JavaVersionUtil; import org.graalvm.word.LocationIdentity; import jdk.vm.ci.code.BailoutException; @@ -3996,7 +3996,7 @@ protected JavaType lookupType(int cpi, int bytecode) { private String unresolvedMethodAssertionMessage(JavaMethod result) { String message = result.format("%H.%n(%P)%R"); - if (GraalServices.Java8OrEarlier) { + if (JavaVersionUtil.Java8OrEarlier) { JavaType declaringClass = result.getDeclaringClass(); String className = declaringClass.getName(); switch (className) { diff --git a/compiler/src/org.graalvm.compiler.microbenchmarks/src/org/graalvm/compiler/microbenchmarks/lir/GraalCompilerState.java b/compiler/src/org.graalvm.compiler.microbenchmarks/src/org/graalvm/compiler/microbenchmarks/lir/GraalCompilerState.java index e242e9e64680..a849e8040c2d 100644 --- a/compiler/src/org.graalvm.compiler.microbenchmarks/src/org/graalvm/compiler/microbenchmarks/lir/GraalCompilerState.java +++ b/compiler/src/org.graalvm.compiler.microbenchmarks/src/org/graalvm/compiler/microbenchmarks/lir/GraalCompilerState.java @@ -46,11 +46,11 @@ import org.graalvm.compiler.core.common.CompilationIdentifier; import org.graalvm.compiler.core.common.alloc.ComputeBlockOrder; import org.graalvm.compiler.core.common.cfg.AbstractBlockBase; -import org.graalvm.compiler.core.gen.LIRGenerationProvider; import org.graalvm.compiler.core.gen.LIRCompilerBackend; +import org.graalvm.compiler.core.gen.LIRGenerationProvider; import org.graalvm.compiler.core.target.Backend; -import org.graalvm.compiler.debug.DebugHandlersFactory; import org.graalvm.compiler.debug.DebugContext; +import org.graalvm.compiler.debug.DebugHandlersFactory; import org.graalvm.compiler.lir.LIR; import org.graalvm.compiler.lir.asm.CompilationResultBuilderFactory; import org.graalvm.compiler.lir.gen.LIRGenerationResult; @@ -89,6 +89,7 @@ import jdk.vm.ci.meta.ConstantReflectionProvider; import jdk.vm.ci.meta.MetaAccessProvider; import jdk.vm.ci.meta.ResolvedJavaMethod; +import jdk.vm.ci.meta.SpeculationLog; /** * State providing a new copy of a graph for each invocation of a benchmark. Subclasses of this @@ -461,8 +462,10 @@ protected PostAllocationOptimizationContext createPostAllocationOptimizationCont */ protected final void emitCode() { int bytecodeSize = request.graph.method() == null ? 0 : request.graph.getBytecodeSize(); + SpeculationLog speculationLog = null; request.compilationResult.setHasUnsafeAccess(request.graph.hasUnsafeAccess()); - LIRCompilerBackend.emitCode(request.backend, request.graph.getAssumptions(), request.graph.method(), request.graph.getMethods(), request.graph.getFields(), bytecodeSize, lirGenRes, + LIRCompilerBackend.emitCode(request.backend, request.graph.getAssumptions(), request.graph.method(), request.graph.getMethods(), request.graph.getFields(), + speculationLog, bytecodeSize, lirGenRes, request.compilationResult, request.installedCodeOwner, request.factory); } diff --git a/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/BinaryArithmeticNode.java b/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/BinaryArithmeticNode.java index 114aaaa7b8cd..549f7d3d24c1 100644 --- a/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/BinaryArithmeticNode.java +++ b/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/BinaryArithmeticNode.java @@ -90,6 +90,26 @@ public ValueNode canonical(CanonicalizerTool tool, ValueNode forX, ValueNode for if (result != null) { return result; } + if (forX instanceof ConditionalNode && forY.isConstant() && forX.hasExactlyOneUsage()) { + ConditionalNode conditionalNode = (ConditionalNode) forX; + BinaryOp arithmeticOp = getArithmeticOp(); + ConstantNode trueConstant = tryConstantFold(arithmeticOp, conditionalNode.trueValue(), forY, this.stamp(view), view); + if (trueConstant != null) { + ConstantNode falseConstant = tryConstantFold(arithmeticOp, conditionalNode.falseValue(), forY, this.stamp(view), view); + if (falseConstant != null) { + // @formatter:off + /* The arithmetic is folded into a constant on both sides of the conditional. + * Example: + * (cond ? -5 : 5) + 100 + * canonicalizes to: + * (cond ? 95 : 105) + */ + // @formatter:on + return ConditionalNode.create(conditionalNode.condition, trueConstant, + falseConstant, view); + } + } + } return this; } diff --git a/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerEqualsNode.java b/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerEqualsNode.java index 24957e672aea..27fbc7a5932c 100644 --- a/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerEqualsNode.java +++ b/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerEqualsNode.java @@ -165,15 +165,19 @@ public LogicNode canonical(ConstantReflectionProvider constantReflection, MetaAc ValueNode v1 = null; ValueNode v2 = null; if (addX.getX() == addY.getX()) { + // (x + y) == (x + z) => y == z v1 = addX.getY(); v2 = addY.getY(); } else if (addX.getX() == addY.getY()) { + // (x + y) == (z + x) => y == z v1 = addX.getY(); v2 = addY.getX(); } else if (addX.getY() == addY.getX()) { + // (y + x) == (x + z) => y == z v1 = addX.getX(); v2 = addY.getY(); } else if (addX.getY() == addY.getY()) { + // (y + x) == (z + x) => y == z v1 = addX.getX(); v2 = addY.getX(); } @@ -183,6 +187,64 @@ public LogicNode canonical(ConstantReflectionProvider constantReflection, MetaAc } } + if (forX instanceof SubNode && forY instanceof SubNode) { + SubNode subX = (SubNode) forX; + SubNode subY = (SubNode) forY; + ValueNode v1 = null; + ValueNode v2 = null; + if (subX.getX() == subY.getX()) { + // (x - y) == (x - z) => y == z + v1 = subX.getY(); + v2 = subY.getY(); + } else if (subX.getY() == subY.getY()) { + // (y - x) == (z - x) => y == z + v1 = subX.getX(); + v2 = subY.getX(); + } + if (v1 != null) { + assert v2 != null; + return create(v1, v2, view); + } + } + + if (forX instanceof AddNode) { + AddNode addNode = (AddNode) forX; + if (addNode.getX() == forY) { + // (x + y) == x => y == 0 + return create(addNode.getY(), ConstantNode.forIntegerStamp(view.stamp(addNode), 0), view); + } else if (addNode.getY() == forY) { + // (x + y) == y => x == 0 + return create(addNode.getX(), ConstantNode.forIntegerStamp(view.stamp(addNode), 0), view); + } + } + + if (forY instanceof AddNode) { + AddNode addNode = (AddNode) forY; + if (addNode.getX() == forX) { + // x == (x + y) => y == 0 + return create(addNode.getY(), ConstantNode.forIntegerStamp(view.stamp(addNode), 0), view); + } else if (addNode.getY() == forX) { + // y == (x + y) => x == 0 + return create(addNode.getX(), ConstantNode.forIntegerStamp(view.stamp(addNode), 0), view); + } + } + + if (forX instanceof SubNode) { + SubNode subNode = (SubNode) forX; + if (subNode.getX() == forY) { + // (x - y) == x => y == 0 + return create(subNode.getY(), ConstantNode.forIntegerStamp(view.stamp(subNode), 0), view); + } + } + + if (forY instanceof SubNode) { + SubNode subNode = (SubNode) forY; + if (forX == subNode.getX()) { + // x == (x - y) => y == 0 + return create(subNode.getY(), ConstantNode.forIntegerStamp(view.stamp(subNode), 0), view); + } + } + return super.canonical(constantReflection, metaAccess, options, smallestCompareWidth, condition, unorderedIsTrue, forX, forY, view); } diff --git a/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerLessThanNode.java b/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerLessThanNode.java index 61a34a024e04..166b7de2481b 100644 --- a/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerLessThanNode.java +++ b/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/IntegerLessThanNode.java @@ -226,7 +226,6 @@ protected LogicNode findSynonym(ValueNode forX, ValueNode forY, NodeView view) { } } } - } } diff --git a/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/RightShiftNode.java b/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/RightShiftNode.java index 94eb83d56e7a..9aee20e94431 100644 --- a/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/RightShiftNode.java +++ b/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/RightShiftNode.java @@ -83,6 +83,15 @@ private static ValueNode canonical(RightShiftNode rightShiftNode, ArithmeticOpTa return new UnsignedRightShiftNode(forX, forY); } + Stamp xStampGeneric = forX.stamp(view); + if (xStampGeneric instanceof IntegerStamp) { + IntegerStamp xStamp = (IntegerStamp) xStampGeneric; + if (xStamp.lowerBound() >= -1 && xStamp.upperBound() <= 0) { + // Right shift by any amount does not change any bit. + return forX; + } + } + if (forY.isConstant()) { int amount = forY.asJavaConstant().asInt(); int originalAmout = amount; @@ -91,6 +100,16 @@ private static ValueNode canonical(RightShiftNode rightShiftNode, ArithmeticOpTa if (amount == 0) { return forX; } + + if (xStampGeneric instanceof IntegerStamp) { + IntegerStamp xStamp = (IntegerStamp) xStampGeneric; + + if (xStamp.lowerBound() >> amount == xStamp.upperBound() >> amount) { + // Right shift turns the result of the expression into a constant. + return ConstantNode.forIntegerKind(stamp.getStackKind(), xStamp.lowerBound() >> amount); + } + } + if (forX instanceof ShiftNode) { ShiftNode other = (ShiftNode) forX; if (other.getY().isConstant()) { diff --git a/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/UnsignedRightShiftNode.java b/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/UnsignedRightShiftNode.java index 8161c609c680..e341bfee6539 100644 --- a/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/UnsignedRightShiftNode.java +++ b/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/calc/UnsignedRightShiftNode.java @@ -80,6 +80,22 @@ private static ValueNode canonical(UnsignedRightShiftNode node, ArithmeticOpTabl if (amount == 0) { return forX; } + + Stamp xStampGeneric = forX.stamp(view); + if (xStampGeneric instanceof IntegerStamp) { + IntegerStamp xStamp = (IntegerStamp) xStampGeneric; + + if (xStamp.lowerBound() >>> amount == xStamp.upperBound() >>> amount) { + // The result of the shift is constant. + return ConstantNode.forIntegerKind(stamp.getStackKind(), xStamp.lowerBound() >>> amount); + } + + if (amount == xStamp.getBits() - 1 && xStamp.lowerBound() == -1 && xStamp.upperBound() == 0) { + // Shift is equivalent to a negation, i.e., turns -1 into 1 and keeps 0 at 0. + return NegateNode.create(forX, view); + } + } + if (forX instanceof ShiftNode) { ShiftNode other = (ShiftNode) forX; if (other.getY().isConstant()) { diff --git a/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/type/NarrowOopStamp.java b/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/type/NarrowOopStamp.java index 7bab455a2c04..1b2f56693863 100644 --- a/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/type/NarrowOopStamp.java +++ b/compiler/src/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/type/NarrowOopStamp.java @@ -44,6 +44,13 @@ protected NarrowOopStamp(ResolvedJavaType type, boolean exactType, boolean nonNu this.encoding = encoding; } + @Override + public void accept(Visitor v) { + super.accept(v); + v.visitLong(encoding.getBase()); + v.visitInt(encoding.getShift()); + } + @Override protected abstract AbstractObjectStamp copyWith(ResolvedJavaType type, boolean exactType, boolean nonNull, boolean alwaysNull); diff --git a/compiler/src/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/FixReadsPhase.java b/compiler/src/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/FixReadsPhase.java index 9b487d21e3eb..6c2e3fa666c5 100644 --- a/compiler/src/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/FixReadsPhase.java +++ b/compiler/src/org.graalvm.compiler.phases.common/src/org/graalvm/compiler/phases/common/FixReadsPhase.java @@ -28,6 +28,7 @@ import org.graalvm.collections.MapCursor; import org.graalvm.compiler.core.common.GraalOptions; import org.graalvm.compiler.core.common.cfg.BlockMap; +import org.graalvm.compiler.core.common.spi.ConstantFieldProvider; import org.graalvm.compiler.core.common.type.FloatStamp; import org.graalvm.compiler.core.common.type.Stamp; import org.graalvm.compiler.core.common.type.StampFactory; @@ -37,6 +38,7 @@ import org.graalvm.compiler.graph.NodeMap; import org.graalvm.compiler.graph.NodeStack; import org.graalvm.compiler.graph.Position; +import org.graalvm.compiler.graph.spi.CanonicalizerTool; import org.graalvm.compiler.nodeinfo.InputType; import org.graalvm.compiler.nodes.AbstractBeginNode; import org.graalvm.compiler.nodes.AbstractMergeNode; @@ -68,6 +70,7 @@ import org.graalvm.compiler.nodes.memory.MemoryAccess; import org.graalvm.compiler.nodes.memory.MemoryPhiNode; import org.graalvm.compiler.nodes.util.GraphUtil; +import org.graalvm.compiler.options.OptionValues; import org.graalvm.compiler.phases.BasePhase; import org.graalvm.compiler.phases.Phase; import org.graalvm.compiler.phases.graph.ScheduledNodeIterator; @@ -76,7 +79,9 @@ import org.graalvm.compiler.phases.tiers.LowTierContext; import org.graalvm.compiler.phases.tiers.PhaseContext; +import jdk.vm.ci.meta.Assumptions; import jdk.vm.ci.meta.Constant; +import jdk.vm.ci.meta.ConstantReflectionProvider; import jdk.vm.ci.meta.MetaAccessProvider; import jdk.vm.ci.meta.TriState; @@ -136,7 +141,7 @@ protected void processNode(Node node) { } - protected static class RawConditionalEliminationVisitor implements RecursiveVisitor { + public static class RawConditionalEliminationVisitor implements RecursiveVisitor { protected final NodeMap stampMap; protected final NodeStack undoOperations; @@ -147,8 +152,58 @@ protected static class RawConditionalEliminationVisitor implements RecursiveVisi private final BlockMap blockActionStart; private final EconomicMap> endMaps; private final DebugContext debug; + private final RawCanonicalizerTool rawCanonicalizerTool = new RawCanonicalizerTool(); - protected RawConditionalEliminationVisitor(StructuredGraph graph, ScheduleResult schedule, MetaAccessProvider metaAccess, boolean replaceInputsWithConstants) { + private class RawCanonicalizerTool implements NodeView, CanonicalizerTool { + + @Override + public Assumptions getAssumptions() { + return graph.getAssumptions(); + } + + @Override + public MetaAccessProvider getMetaAccess() { + return metaAccess; + } + + @Override + public ConstantReflectionProvider getConstantReflection() { + return null; + } + + @Override + public ConstantFieldProvider getConstantFieldProvider() { + return null; + } + + @Override + public boolean canonicalizeReads() { + return false; + } + + @Override + public boolean allUsagesAvailable() { + return true; + } + + @Override + public Integer smallestCompareWidth() { + return null; + } + + @Override + public OptionValues getOptions() { + return graph.getOptions(); + } + + @Override + public Stamp stamp(ValueNode node) { + return getBestStamp(node); + } + + } + + public RawConditionalEliminationVisitor(StructuredGraph graph, ScheduleResult schedule, MetaAccessProvider metaAccess, boolean replaceInputsWithConstants) { this.graph = graph; this.debug = graph.getDebug(); this.schedule = schedule; @@ -326,8 +381,22 @@ private static Block getBlock(ValueNode node, NodeMap blockToNodeMap) { } protected void processUnary(UnaryNode node) { - Stamp newStamp = node.foldStamp(getBestStamp(node.getValue())); + ValueNode value = node.getValue(); + Stamp bestStamp = getBestStamp(value); + Stamp newStamp = node.foldStamp(bestStamp); if (!checkReplaceWithConstant(newStamp, node)) { + if (!bestStamp.equals(value.stamp(NodeView.DEFAULT))) { + ValueNode newNode = node.canonical(rawCanonicalizerTool); + if (newNode != node) { + // Canonicalization successfully triggered. + if (newNode != null && !newNode.isAlive()) { + newNode = graph.addWithoutUniqueWithInputs(newNode); + } + node.replaceAndDelete(newNode); + GraphUtil.tryKillUnused(value); + return; + } + } registerNewValueStamp(node, newStamp); } } @@ -346,10 +415,31 @@ protected boolean checkReplaceWithConstant(Stamp newStamp, ValueNode node) { } protected void processBinary(BinaryNode node) { - Stamp xStamp = getBestStamp(node.getX()); - Stamp yStamp = getBestStamp(node.getY()); + + ValueNode x = node.getX(); + ValueNode y = node.getY(); + + Stamp xStamp = getBestStamp(x); + Stamp yStamp = getBestStamp(y); Stamp newStamp = node.foldStamp(xStamp, yStamp); if (!checkReplaceWithConstant(newStamp, node)) { + + if (!xStamp.equals(x.stamp(NodeView.DEFAULT)) || !yStamp.equals(y.stamp(NodeView.DEFAULT))) { + // At least one of the inputs has an improved stamp => attempt to canonicalize + // based on that improvement. + ValueNode newNode = node.canonical(rawCanonicalizerTool); + if (newNode != node) { + // Canonicalization successfully triggered. + if (newNode != null && !newNode.isAlive()) { + newNode = graph.addWithoutUniqueWithInputs(newNode); + } + node.replaceAndDelete(newNode); + GraphUtil.tryKillUnused(x); + GraphUtil.tryKillUnused(y); + return; + } + } + registerNewValueStamp(node, newStamp); } } @@ -469,6 +559,10 @@ protected void registerNewStamp(ValueNode value, Stamp newStamp) { protected Stamp getBestStamp(ValueNode value) { ValueNode originalNode = value; + if (!value.isAlive()) { + return value.stamp(NodeView.DEFAULT); + } + StampElement currentStamp = stampMap.getAndGrow(originalNode); if (currentStamp == null) { return value.stamp(NodeView.DEFAULT); diff --git a/compiler/src/org.graalvm.compiler.replacements.aarch64/src/org/graalvm/compiler/replacements/aarch64/AArch64GraphBuilderPlugins.java b/compiler/src/org.graalvm.compiler.replacements.aarch64/src/org/graalvm/compiler/replacements/aarch64/AArch64GraphBuilderPlugins.java index 754e496006fc..a16d2913a306 100644 --- a/compiler/src/org.graalvm.compiler.replacements.aarch64/src/org/graalvm/compiler/replacements/aarch64/AArch64GraphBuilderPlugins.java +++ b/compiler/src/org.graalvm.compiler.replacements.aarch64/src/org/graalvm/compiler/replacements/aarch64/AArch64GraphBuilderPlugins.java @@ -31,9 +31,9 @@ import static org.graalvm.compiler.replacements.nodes.UnaryMathIntrinsicNode.UnaryOperation.LOG10; import static org.graalvm.compiler.replacements.nodes.UnaryMathIntrinsicNode.UnaryOperation.SIN; import static org.graalvm.compiler.replacements.nodes.UnaryMathIntrinsicNode.UnaryOperation.TAN; -import static org.graalvm.compiler.serviceprovider.GraalServices.JAVA_SPECIFICATION_VERSION; -import static org.graalvm.compiler.serviceprovider.GraalServices.Java11OrEarlier; -import static org.graalvm.compiler.serviceprovider.GraalServices.Java8OrEarlier; +import static org.graalvm.compiler.serviceprovider.JavaVersionUtil.JAVA_SPECIFICATION_VERSION; +import static org.graalvm.compiler.serviceprovider.JavaVersionUtil.Java11OrEarlier; +import static org.graalvm.compiler.serviceprovider.JavaVersionUtil.Java8OrEarlier; import org.graalvm.compiler.bytecode.BytecodeProvider; import org.graalvm.compiler.lir.aarch64.AArch64ArithmeticLIRGeneratorTool.RoundingMode; diff --git a/compiler/src/org.graalvm.compiler.replacements.amd64/src/org/graalvm/compiler/replacements/amd64/AMD64GraphBuilderPlugins.java b/compiler/src/org.graalvm.compiler.replacements.amd64/src/org/graalvm/compiler/replacements/amd64/AMD64GraphBuilderPlugins.java index d4f55b6e124b..7a98c8d49699 100644 --- a/compiler/src/org.graalvm.compiler.replacements.amd64/src/org/graalvm/compiler/replacements/amd64/AMD64GraphBuilderPlugins.java +++ b/compiler/src/org.graalvm.compiler.replacements.amd64/src/org/graalvm/compiler/replacements/amd64/AMD64GraphBuilderPlugins.java @@ -32,9 +32,9 @@ import static org.graalvm.compiler.replacements.nodes.UnaryMathIntrinsicNode.UnaryOperation.LOG10; import static org.graalvm.compiler.replacements.nodes.UnaryMathIntrinsicNode.UnaryOperation.SIN; import static org.graalvm.compiler.replacements.nodes.UnaryMathIntrinsicNode.UnaryOperation.TAN; -import static org.graalvm.compiler.serviceprovider.GraalServices.JAVA_SPECIFICATION_VERSION; -import static org.graalvm.compiler.serviceprovider.GraalServices.Java11OrEarlier; -import static org.graalvm.compiler.serviceprovider.GraalServices.Java8OrEarlier; +import static org.graalvm.compiler.serviceprovider.JavaVersionUtil.JAVA_SPECIFICATION_VERSION; +import static org.graalvm.compiler.serviceprovider.JavaVersionUtil.Java11OrEarlier; +import static org.graalvm.compiler.serviceprovider.JavaVersionUtil.Java8OrEarlier; import java.util.Arrays; diff --git a/compiler/src/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/DeoptimizeOnIntegerExactTest.java b/compiler/src/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/DeoptimizeOnIntegerExactTest.java index a2f11a9033e1..6c844db0bd84 100644 --- a/compiler/src/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/DeoptimizeOnIntegerExactTest.java +++ b/compiler/src/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/DeoptimizeOnIntegerExactTest.java @@ -134,6 +134,7 @@ protected SpeculationLog getSpeculationLog() { @Override protected InstalledCode addMethod(DebugContext debug, final ResolvedJavaMethod method, final CompilationResult compilationResult) { - return getBackend().createInstalledCode(debug, method, compilationResult, speculationLog, null, false); + assert speculationLog == compilationResult.getSpeculationLog(); + return getBackend().createInstalledCode(debug, method, compilationResult, null, false); } } diff --git a/compiler/src/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/StringCompareToTest.java b/compiler/src/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/StringCompareToTest.java index 0323504425ba..0d934c6f39f6 100644 --- a/compiler/src/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/StringCompareToTest.java +++ b/compiler/src/org.graalvm.compiler.replacements.test/src/org/graalvm/compiler/replacements/test/StringCompareToTest.java @@ -24,21 +24,23 @@ */ package org.graalvm.compiler.replacements.test; -import jdk.vm.ci.aarch64.AArch64; -import jdk.vm.ci.amd64.AMD64; -import jdk.vm.ci.meta.ResolvedJavaMethod; +import static org.graalvm.compiler.core.common.GraalOptions.RemoveNeverExecutedCode; + +import java.util.List; + import org.graalvm.compiler.graph.Node; import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.options.OptionValues; import org.graalvm.compiler.replacements.nodes.ArrayCompareToNode; import org.graalvm.compiler.serviceprovider.GraalServices; +import org.graalvm.compiler.serviceprovider.JavaVersionUtil; import org.junit.Assert; import org.junit.Assume; import org.junit.Test; -import java.util.List; - -import static org.graalvm.compiler.core.common.GraalOptions.RemoveNeverExecutedCode; +import jdk.vm.ci.aarch64.AArch64; +import jdk.vm.ci.amd64.AMD64; +import jdk.vm.ci.meta.ResolvedJavaMethod; /** * Tests compareTo method intrinsic. @@ -89,7 +91,7 @@ protected void initSubstitution(ResolvedJavaMethod theRealMethod, OptionValues options; boolean needCheckNode = true; - if (GraalServices.Java8OrEarlier) { + if (JavaVersionUtil.Java8OrEarlier) { needCheckNode = false; } else { List vmArgs = GraalServices.getInputArguments(); diff --git a/compiler/src/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/StandardGraphBuilderPlugins.java b/compiler/src/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/StandardGraphBuilderPlugins.java index 25a51897956c..470ed444752b 100644 --- a/compiler/src/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/StandardGraphBuilderPlugins.java +++ b/compiler/src/org.graalvm.compiler.replacements/src/org/graalvm/compiler/replacements/StandardGraphBuilderPlugins.java @@ -33,8 +33,8 @@ import static jdk.vm.ci.code.MemoryBarriers.STORE_LOAD; import static jdk.vm.ci.code.MemoryBarriers.STORE_STORE; import static org.graalvm.compiler.nodes.NamedLocationIdentity.OFF_HEAP_LOCATION; -import static org.graalvm.compiler.serviceprovider.GraalServices.Java11OrEarlier; -import static org.graalvm.compiler.serviceprovider.GraalServices.Java8OrEarlier; +import static org.graalvm.compiler.serviceprovider.JavaVersionUtil.Java11OrEarlier; +import static org.graalvm.compiler.serviceprovider.JavaVersionUtil.Java8OrEarlier; import java.lang.reflect.Array; import java.lang.reflect.Field; @@ -54,8 +54,6 @@ import org.graalvm.compiler.graph.Edges; import org.graalvm.compiler.graph.Node; import org.graalvm.compiler.graph.NodeList; -import org.graalvm.compiler.java.IntegerExactOpSpeculation; -import org.graalvm.compiler.java.IntegerExactOpSpeculation.IntegerExactOp; import org.graalvm.compiler.nodes.AbstractBeginNode; import org.graalvm.compiler.nodes.BeginNode; import org.graalvm.compiler.nodes.ConstantNode; @@ -132,6 +130,7 @@ import org.graalvm.compiler.replacements.nodes.arithmetic.IntegerMulExactSplitNode; import org.graalvm.compiler.replacements.nodes.arithmetic.IntegerSubExactNode; import org.graalvm.compiler.replacements.nodes.arithmetic.IntegerSubExactSplitNode; +import org.graalvm.compiler.serviceprovider.SpeculationReasonGroup; import org.graalvm.word.LocationIdentity; import jdk.vm.ci.code.BytecodePosition; @@ -556,6 +555,14 @@ public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Rec }); } + public enum IntegerExactOp { + INTEGER_ADD_EXACT, + INTEGER_INCREMENT_EXACT, + INTEGER_SUBTRACT_EXACT, + INTEGER_DECREMENT_EXACT, + INTEGER_MULTIPLY_EXACT + } + private static ValueNode createIntegerExactArithmeticNode(ValueNode x, ValueNode y, SpeculationReason speculation, IntegerExactOp op) { switch (op) { case INTEGER_ADD_EXACT: @@ -586,6 +593,8 @@ private static IntegerExactArithmeticSplitNode createIntegerExactSplit(ValueNode } } + private static final SpeculationReasonGroup INTEGER_EXACT_OP_SPECULATIONS = new SpeculationReasonGroup("IntegerExactOp", ResolvedJavaMethod.class, IntegerExactOp.class); + private static void createIntegerExactOperation(GraphBuilderContext b, JavaKind kind, ValueNode x, ValueNode y, IntegerExactOp op) { if (b.needsExplicitException()) { BytecodeExceptionKind exceptionKind = kind == JavaKind.Int ? BytecodeExceptionKind.INTEGER_EXACT_OVERFLOW : BytecodeExceptionKind.LONG_EXACT_OVERFLOW; @@ -597,9 +606,9 @@ private static void createIntegerExactOperation(GraphBuilderContext b, JavaKind if (log == null || (x.isConstant() && y.isConstant())) { b.addPush(kind, createIntegerExactArithmeticNode(x, y, null, op)); } else { - SpeculationReason speculation = new IntegerExactOpSpeculation(b.getMethod(), op); - if (log.maySpeculate(speculation)) { - b.addPush(kind, createIntegerExactArithmeticNode(x, y, speculation, op)); + SpeculationReason reason = INTEGER_EXACT_OP_SPECULATIONS.createSpeculationReason(b.getMethod(), op); + if (log.maySpeculate(reason)) { + b.addPush(kind, createIntegerExactArithmeticNode(x, y, reason, op)); } else { BeginNode begin = b.add(new BeginNode()); IntegerExactArithmeticNode node = (IntegerExactArithmeticNode) b.addPush(kind, createIntegerExactArithmeticNode(x, y, null, op)); @@ -1144,23 +1153,7 @@ public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Rec } } - private static final class DirectiveSpeculationReason implements SpeculationLog.SpeculationReason { - private final BytecodePosition pos; - - private DirectiveSpeculationReason(BytecodePosition pos) { - this.pos = pos; - } - - @Override - public int hashCode() { - return pos.hashCode(); - } - - @Override - public boolean equals(Object obj) { - return obj instanceof DirectiveSpeculationReason && ((DirectiveSpeculationReason) obj).pos.equals(this.pos); - } - } + private static final SpeculationReasonGroup DIRECTIVE_SPECULATIONS = new SpeculationReasonGroup("GraalDirective", BytecodePosition.class); private static void registerGraalDirectivesPlugins(InvocationPlugins plugins) { Registration r = new Registration(plugins, GraalDirectives.class); @@ -1183,9 +1176,9 @@ public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Rec r.register0("deoptimizeAndInvalidateWithSpeculation", new InvocationPlugin() { @Override public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) { - GraalError.guarantee(b.getGraph().getSpeculationLog() != null, "A speculation log is need to use `deoptimizeAndInvalidateWithSpeculation`"); + GraalError.guarantee(b.getGraph().getSpeculationLog() != null, "A speculation log is needed to use `deoptimizeAndInvalidateWithSpeculation`"); BytecodePosition pos = new BytecodePosition(null, b.getMethod(), b.bci()); - DirectiveSpeculationReason reason = new DirectiveSpeculationReason(pos); + SpeculationReason reason = DIRECTIVE_SPECULATIONS.createSpeculationReason(pos); Speculation speculation; if (b.getGraph().getSpeculationLog().maySpeculate(reason)) { speculation = b.getGraph().getSpeculationLog().speculate(reason); diff --git a/compiler/src/org.graalvm.compiler.serviceprovider.jdk8/src/org/graalvm/compiler/serviceprovider/GraalServices.java b/compiler/src/org.graalvm.compiler.serviceprovider.jdk8/src/org/graalvm/compiler/serviceprovider/GraalServices.java index 4a49e6a7710e..9c76fc96eb55 100644 --- a/compiler/src/org.graalvm.compiler.serviceprovider.jdk8/src/org/graalvm/compiler/serviceprovider/GraalServices.java +++ b/compiler/src/org.graalvm.compiler.serviceprovider.jdk8/src/org/graalvm/compiler/serviceprovider/GraalServices.java @@ -28,11 +28,21 @@ import java.io.IOException; import java.io.InputStream; +import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.ServiceConfigurationError; import java.util.concurrent.atomic.AtomicLong; +import java.util.function.Supplier; +import org.graalvm.compiler.serviceprovider.SpeculationReasonGroup.SpeculationContextObject; + +import jdk.vm.ci.code.BytecodePosition; +import jdk.vm.ci.meta.ResolvedJavaField; +import jdk.vm.ci.meta.ResolvedJavaMethod; +import jdk.vm.ci.meta.ResolvedJavaType; +import jdk.vm.ci.meta.SpeculationLog.SpeculationReason; +import jdk.vm.ci.meta.SpeculationLog.SpeculationReasonEncoding; import jdk.vm.ci.services.JVMCIPermission; import jdk.vm.ci.services.Services; @@ -41,30 +51,6 @@ */ public final class GraalServices { - private static int getJavaSpecificationVersion() { - String value = System.getProperty("java.specification.version"); - if (value.startsWith("1.")) { - value = value.substring(2); - } - return Integer.parseInt(value); - } - - /** - * The integer value corresponding to the value of the {@code java.specification.version} system - * property after any leading {@code "1."} has been stripped. - */ - public static final int JAVA_SPECIFICATION_VERSION = getJavaSpecificationVersion(); - - /** - * Determines if the Java runtime is version 8 or earlier. - */ - public static final boolean Java8OrEarlier = JAVA_SPECIFICATION_VERSION <= 8; - - /** - * Determines if the Java runtime is version 11 or earlier. - */ - public static final boolean Java11OrEarlier = JAVA_SPECIFICATION_VERSION <= 11; - private GraalServices() { } @@ -140,6 +126,162 @@ public static boolean isToStringTrusted(Class c) { return cl == null || cl == JVMCI_LOADER || cl == JVMCI_PARENT_LOADER; } + /** + * An implementation of {@link SpeculationReason} based on direct, unencoded values. + */ + static final class DirectSpeculationReason implements SpeculationReason { + final int groupId; + final String groupName; + final Object[] context; + private SpeculationReasonEncoding encoding; + + DirectSpeculationReason(int groupId, String groupName, Object[] context) { + this.groupId = groupId; + this.groupName = groupName; + this.context = context; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof DirectSpeculationReason) { + DirectSpeculationReason that = (DirectSpeculationReason) obj; + return this.groupId == that.groupId && Arrays.equals(this.context, that.context); + } + return false; + } + + @Override + public int hashCode() { + return groupId + Arrays.hashCode(this.context); + } + + @Override + public String toString() { + return String.format("%s@%d%s", groupName, groupId, Arrays.toString(context)); + } + + @Override + public SpeculationReasonEncoding encode(Supplier encodingSupplier) { + if (encoding == null) { + encoding = encodingSupplier.get(); + encoding.addInt(groupId); + for (Object o : context) { + if (o == null) { + encoding.addInt(0); + } else { + addNonNullObject(encoding, o); + } + } + } + return encoding; + } + + static void addNonNullObject(SpeculationReasonEncoding encoding, Object o) { + Class c = o.getClass(); + if (c == String.class) { + encoding.addString((String) o); + } else if (c == Byte.class) { + encoding.addByte((Byte) o); + } else if (c == Short.class) { + encoding.addShort((Short) o); + } else if (c == Character.class) { + encoding.addShort((Character) o); + } else if (c == Integer.class) { + encoding.addInt((Integer) o); + } else if (c == Long.class) { + encoding.addLong((Long) o); + } else if (c == Float.class) { + encoding.addInt(Float.floatToRawIntBits((Float) o)); + } else if (c == Double.class) { + encoding.addLong(Double.doubleToRawLongBits((Double) o)); + } else if (o instanceof Enum) { + encoding.addInt(((Enum) o).ordinal()); + } else if (o instanceof ResolvedJavaMethod) { + encoding.addMethod((ResolvedJavaMethod) o); + } else if (o instanceof ResolvedJavaType) { + encoding.addType((ResolvedJavaType) o); + } else if (o instanceof ResolvedJavaField) { + encoding.addField((ResolvedJavaField) o); + } else if (o instanceof SpeculationContextObject) { + SpeculationContextObject sco = (SpeculationContextObject) o; + // These are compiler objects which all have the same class + // loader so the class name uniquely identifies the class. + encoding.addString(o.getClass().getName()); + sco.accept(new EncodingAdapter(encoding)); + } else if (o.getClass() == BytecodePosition.class) { + BytecodePosition p = (BytecodePosition) o; + while (p != null) { + encoding.addInt(p.getBCI()); + encoding.addMethod(p.getMethod()); + p = p.getCaller(); + } + } else { + throw new IllegalArgumentException("Unsupported type for encoding: " + c.getName()); + } + } + } + + static class EncodingAdapter implements SpeculationContextObject.Visitor { + private final SpeculationReasonEncoding encoding; + + EncodingAdapter(SpeculationReasonEncoding encoding) { + this.encoding = encoding; + } + + @Override + public void visitBoolean(boolean v) { + encoding.addByte(v ? 1 : 0); + } + + @Override + public void visitByte(byte v) { + encoding.addByte(v); + } + + @Override + public void visitChar(char v) { + encoding.addShort(v); + } + + @Override + public void visitShort(short v) { + encoding.addInt(v); + } + + @Override + public void visitInt(int v) { + encoding.addInt(v); + } + + @Override + public void visitLong(long v) { + encoding.addLong(v); + } + + @Override + public void visitFloat(float v) { + encoding.addInt(Float.floatToRawIntBits(v)); + } + + @Override + public void visitDouble(double v) { + encoding.addLong(Double.doubleToRawLongBits(v)); + } + + @Override + public void visitObject(Object v) { + if (v == null) { + encoding.addInt(0); + } else { + DirectSpeculationReason.addNonNullObject(encoding, v); + } + } + } + + static SpeculationReason createSpeculationReason(int groupId, String groupName, Object... context) { + return new DirectSpeculationReason(groupId, groupName, context); + } + /** * Gets a unique identifier for this execution such as a process ID or a * {@linkplain #getGlobalTimeStamp() fixed timestamp}. diff --git a/compiler/src/org.graalvm.compiler.serviceprovider.jdk9/src/org/graalvm/compiler/serviceprovider/GraalServices.java b/compiler/src/org.graalvm.compiler.serviceprovider.jdk9/src/org/graalvm/compiler/serviceprovider/GraalServices.java index aa72235bbee2..4bd789d9afcd 100644 --- a/compiler/src/org.graalvm.compiler.serviceprovider.jdk9/src/org/graalvm/compiler/serviceprovider/GraalServices.java +++ b/compiler/src/org.graalvm.compiler.serviceprovider.jdk9/src/org/graalvm/compiler/serviceprovider/GraalServices.java @@ -28,12 +28,14 @@ import java.io.IOException; import java.io.InputStream; +import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.ServiceConfigurationError; import java.util.ServiceLoader; import java.util.concurrent.atomic.AtomicLong; +import jdk.vm.ci.meta.SpeculationLog.SpeculationReason; import jdk.vm.ci.runtime.JVMCI; import jdk.vm.ci.services.JVMCIPermission; import jdk.vm.ci.services.Services; @@ -43,30 +45,6 @@ */ public final class GraalServices { - private static int getJavaSpecificationVersion() { - String value = System.getProperty("java.specification.version"); - if (value.startsWith("1.")) { - value = value.substring(2); - } - return Integer.parseInt(value); - } - - /** - * The integer value corresponding to the value of the {@code java.specification.version} system - * property after any leading {@code "1."} has been stripped. - */ - public static final int JAVA_SPECIFICATION_VERSION = getJavaSpecificationVersion(); - - /** - * Determines if the Java runtime is version 8 or earlier. - */ - public static final boolean Java8OrEarlier = JAVA_SPECIFICATION_VERSION <= 8; - - /** - * Determines if the Java runtime is version 11 or earlier. - */ - public static final boolean Java11OrEarlier = JAVA_SPECIFICATION_VERSION <= 11; - private GraalServices() { } @@ -194,6 +172,44 @@ public static boolean isToStringTrusted(Class c) { return false; } + /** + * An implementation of {@link SpeculationReason} based on direct, unencoded values. + */ + static final class DirectSpeculationReason implements SpeculationReason { + final int groupId; + final String groupName; + final Object[] context; + + DirectSpeculationReason(int groupId, String groupName, Object[] context) { + this.groupId = groupId; + this.groupName = groupName; + this.context = context; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof DirectSpeculationReason) { + DirectSpeculationReason that = (DirectSpeculationReason) obj; + return this.groupId == that.groupId && Arrays.equals(this.context, that.context); + } + return false; + } + + @Override + public int hashCode() { + return groupId + Arrays.hashCode(this.context); + } + + @Override + public String toString() { + return String.format("%s@%d%s", groupName, groupId, Arrays.toString(context)); + } + } + + static SpeculationReason createSpeculationReason(int groupId, String groupName, Object... context) { + return new DirectSpeculationReason(groupId, groupName, context); + } + /** * Gets a unique identifier for this execution such as a process ID or a * {@linkplain #getGlobalTimeStamp() fixed timestamp}. diff --git a/compiler/src/org.graalvm.compiler.serviceprovider/src/org/graalvm/compiler/serviceprovider/GraalServices.java b/compiler/src/org.graalvm.compiler.serviceprovider/src/org/graalvm/compiler/serviceprovider/GraalServices.java index c032a7297acd..75dd4b89d61b 100644 --- a/compiler/src/org.graalvm.compiler.serviceprovider/src/org/graalvm/compiler/serviceprovider/GraalServices.java +++ b/compiler/src/org.graalvm.compiler.serviceprovider/src/org/graalvm/compiler/serviceprovider/GraalServices.java @@ -28,6 +28,7 @@ import java.io.InputStream; import java.util.List; +import jdk.vm.ci.meta.SpeculationLog.SpeculationReason; import jdk.vm.ci.services.JVMCIPermission; /** @@ -35,26 +36,6 @@ */ public final class GraalServices { - private static int getJavaSpecificationVersion() { - throw shouldNotReachHere(); - } - - /** - * The integer value corresponding to the value of the {@code java.specification.version} system - * property after any leading {@code "1."} has been stripped. - */ - public static final int JAVA_SPECIFICATION_VERSION = getJavaSpecificationVersion(); - - /** - * Determines if the Java runtime is version 8 or earlier. - */ - public static final boolean Java8OrEarlier = JAVA_SPECIFICATION_VERSION <= 8; - - /** - * Determines if the Java runtime is version 11 or earlier. - */ - public static final boolean Java11OrEarlier = JAVA_SPECIFICATION_VERSION <= 11; - private GraalServices() { } @@ -109,6 +90,17 @@ public static boolean isToStringTrusted(Class c) { throw shouldNotReachHere(); } + /** + * Creates an encoding of the context objects representing a speculation reason. + * + * @param groupId + * @param groupName + * @param context the objects forming a key for the speculation + */ + static SpeculationReason createSpeculationReason(int groupId, String groupName, Object... context) { + throw shouldNotReachHere(); + } + /** * Gets a unique identifier for this execution such as a process ID or a * {@linkplain #getGlobalTimeStamp() fixed time stamp}. diff --git a/compiler/src/org.graalvm.compiler.serviceprovider/src/org/graalvm/compiler/serviceprovider/JavaVersionUtil.java b/compiler/src/org.graalvm.compiler.serviceprovider/src/org/graalvm/compiler/serviceprovider/JavaVersionUtil.java new file mode 100644 index 000000000000..e85d689374ee --- /dev/null +++ b/compiler/src/org.graalvm.compiler.serviceprovider/src/org/graalvm/compiler/serviceprovider/JavaVersionUtil.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.graalvm.compiler.serviceprovider; + +/** + * Interface to query which JDK version Graal is running on. + */ +public final class JavaVersionUtil { + + private static int getJavaSpecificationVersion() { + String value = System.getProperty("java.specification.version"); + if (value.startsWith("1.")) { + value = value.substring(2); + } + return Integer.parseInt(value); + } + + /** + * The integer value corresponding to the value of the {@code java.specification.version} system + * property after any leading {@code "1."} has been stripped. + */ + public static final int JAVA_SPECIFICATION_VERSION = getJavaSpecificationVersion(); + + /** + * Determines if the Java runtime is version 8 or earlier. + */ + public static final boolean Java8OrEarlier = JAVA_SPECIFICATION_VERSION <= 8; + + /** + * Determines if the Java runtime is version 11 or earlier. + */ + public static final boolean Java11OrEarlier = JAVA_SPECIFICATION_VERSION <= 11; + + private JavaVersionUtil() { + } +} diff --git a/compiler/src/org.graalvm.compiler.serviceprovider/src/org/graalvm/compiler/serviceprovider/SpeculationReasonGroup.java b/compiler/src/org.graalvm.compiler.serviceprovider/src/org/graalvm/compiler/serviceprovider/SpeculationReasonGroup.java new file mode 100644 index 000000000000..60715974ac60 --- /dev/null +++ b/compiler/src/org.graalvm.compiler.serviceprovider/src/org/graalvm/compiler/serviceprovider/SpeculationReasonGroup.java @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.graalvm.compiler.serviceprovider; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; + +import jdk.vm.ci.code.BytecodePosition; +import jdk.vm.ci.meta.ResolvedJavaMethod; +import jdk.vm.ci.meta.ResolvedJavaType; +import jdk.vm.ci.meta.SpeculationLog.SpeculationReason; + +/** + * Facility for creating speculation reasons partitioned in groups. + */ +public final class SpeculationReasonGroup { + + private final int id; + private final String name; + private final Class[] signature; + + private static final AtomicInteger nextId = new AtomicInteger(1); + + /** + * Creates speculation group whose context will always match {@code signature}. + */ + public SpeculationReasonGroup(String name, Class... signature) { + this.id = nextId.get(); + this.name = name; + this.signature = signature; + for (Class c : signature) { + if (!isOfSupportedType(c)) { + throw new IllegalArgumentException("Unsupported speculation context type: " + c.getName()); + } + } + } + + @Override + public String toString() { + return String.format("%s{id:%d, sig=%s}", name, id, Arrays.toString(signature)); + } + + /** + * Creates a speculation reason described by this group. + * + * @param context the components of the reason instance being created + */ + public SpeculationReason createSpeculationReason(Object... context) { + assert checkSignature(context); + return GraalServices.createSpeculationReason(id, name, context); + } + + private static final Set> SUPPORTED_EXACT_TYPES = new HashSet<>(Arrays.asList( + String.class, + int.class, + long.class, + float.class, + double.class, + BytecodePosition.class)); + + private static boolean isOfSupportedType(Class c) { + if (SUPPORTED_EXACT_TYPES.contains(c)) { + return true; + } + if (Enum.class.isAssignableFrom(c)) { + // Trust the ordinal of an Enum to be unique + return true; + } + if (SpeculationContextObject.class.isAssignableFrom(c)) { + return true; + } + if (ResolvedJavaMethod.class.isAssignableFrom(c) || ResolvedJavaType.class.isAssignableFrom(c)) { + // Only the JVMCI implementation specific concrete subclasses + // of these types will be accepted but we cannot test for that + // here since we are in JVMCI implementation agnostic code. + return true; + } + return false; + } + + static Class toBox(Class c) { + if (c == int.class) { + return Integer.class; + } + if (c == long.class) { + return Long.class; + } + if (c == float.class) { + return Float.class; + } + if (c == double.class) { + return Double.class; + } + return c; + } + + private boolean checkSignature(Object[] context) { + assert signature.length == context.length : name + ": Incorrect number of context arguments. Expected " + signature.length + ", got " + context.length; + for (int i = 0; i < context.length; i++) { + Object o = context[i]; + Class c = signature[i]; + if (o != null) { + if (c == ResolvedJavaMethod.class || c == ResolvedJavaType.class || SpeculationContextObject.class.isAssignableFrom(c)) { + c.cast(o); + } else { + Class oClass = o.getClass(); + assert toBox(c) == oClass : name + ": Context argument " + i + " is not a " + c.getName() + " but a " + oClass.getName(); + } + } else { + if (c.isPrimitive() || Enum.class.isAssignableFrom(c)) { + throw new AssertionError(name + ": Cannot pass null for argument " + i); + } + } + } + return true; + } + + /** + * Denotes part of a {@linkplain SpeculationReasonGroup#createSpeculationReason(Object...) + * reason} that can have its attributes {@linkplain #accept(Visitor) visited}. + */ + public interface SpeculationContextObject { + void accept(Visitor v); + + public interface Visitor { + void visitBoolean(boolean v); + + void visitByte(byte v); + + void visitChar(char v); + + void visitShort(short v); + + void visitInt(int v); + + void visitLong(long v); + + void visitFloat(float v); + + void visitDouble(double v); + + void visitObject(Object v); + } + } +} diff --git a/compiler/src/org.graalvm.compiler.test/src/org/graalvm/compiler/test/GraalTest.java b/compiler/src/org.graalvm.compiler.test/src/org/graalvm/compiler/test/GraalTest.java index a4502c393d63..61dba0a85a36 100644 --- a/compiler/src/org.graalvm.compiler.test/src/org/graalvm/compiler/test/GraalTest.java +++ b/compiler/src/org.graalvm.compiler.test/src/org/graalvm/compiler/test/GraalTest.java @@ -44,6 +44,7 @@ import org.graalvm.compiler.debug.GlobalMetrics; import org.graalvm.compiler.options.OptionValues; import org.graalvm.compiler.serviceprovider.GraalServices; +import org.graalvm.compiler.serviceprovider.JavaVersionUtil; import org.junit.After; import org.junit.Assert; import org.junit.AssumptionViolatedException; @@ -72,8 +73,8 @@ public class GraalTest { } } - public static final boolean Java8OrEarlier = GraalServices.Java8OrEarlier; - public static final boolean Java11OrEarlier = GraalServices.Java11OrEarlier; + public static final boolean Java8OrEarlier = JavaVersionUtil.Java8OrEarlier; + public static final boolean Java11OrEarlier = JavaVersionUtil.Java11OrEarlier; protected Method getMethod(String methodName) { return getMethod(getClass(), methodName); diff --git a/compiler/src/org.graalvm.compiler.test/src/org/graalvm/compiler/test/SubprocessUtil.java b/compiler/src/org.graalvm.compiler.test/src/org/graalvm/compiler/test/SubprocessUtil.java index e525b305f56a..1511c9e48d8a 100644 --- a/compiler/src/org.graalvm.compiler.test/src/org/graalvm/compiler/test/SubprocessUtil.java +++ b/compiler/src/org.graalvm.compiler.test/src/org/graalvm/compiler/test/SubprocessUtil.java @@ -37,7 +37,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -import org.graalvm.compiler.serviceprovider.GraalServices; +import org.graalvm.compiler.serviceprovider.JavaVersionUtil; import org.graalvm.util.CollectionsUtil; import org.junit.Assume; @@ -249,7 +249,7 @@ private static Subprocess javaHelper(List vmArgs, Map en return new Subprocess(command, process.waitFor(), output); } - private static final boolean isJava8OrEarlier = GraalServices.Java8OrEarlier; + private static final boolean isJava8OrEarlier = JavaVersionUtil.Java8OrEarlier; private static boolean hasArg(String optionName) { if (optionName.equals("-cp") || optionName.equals("-classpath")) { diff --git a/compiler/src/org.graalvm.compiler.truffle.common.hotspot.libgraal/src/org/graalvm/compiler/truffle/common/hotspot/libgraal/HotSpotToSVM.java b/compiler/src/org.graalvm.compiler.truffle.common.hotspot.libgraal/src/org/graalvm/compiler/truffle/common/hotspot/libgraal/HotSpotToSVM.java index a2651275b13c..f6ffb72d5472 100644 --- a/compiler/src/org.graalvm.compiler.truffle.common.hotspot.libgraal/src/org/graalvm/compiler/truffle/common/hotspot/libgraal/HotSpotToSVM.java +++ b/compiler/src/org.graalvm.compiler.truffle.common.hotspot.libgraal/src/org/graalvm/compiler/truffle/common/hotspot/libgraal/HotSpotToSVM.java @@ -51,7 +51,6 @@ enum Id { CloseCompilation, CloseDebugContext, CloseDebugContextScope, - CreateSpeculationLog, DoCompile, DumpChannelClose, DumpChannelWrite, diff --git a/compiler/src/org.graalvm.compiler.truffle.common.hotspot.libgraal/src/org/graalvm/compiler/truffle/common/hotspot/libgraal/OptionsEncoder.java b/compiler/src/org.graalvm.compiler.truffle.common.hotspot.libgraal/src/org/graalvm/compiler/truffle/common/hotspot/libgraal/OptionsEncoder.java index d41bdde4f034..5a98f6b41972 100644 --- a/compiler/src/org.graalvm.compiler.truffle.common.hotspot.libgraal/src/org/graalvm/compiler/truffle/common/hotspot/libgraal/OptionsEncoder.java +++ b/compiler/src/org.graalvm.compiler.truffle.common.hotspot.libgraal/src/org/graalvm/compiler/truffle/common/hotspot/libgraal/OptionsEncoder.java @@ -45,7 +45,7 @@ public static boolean isValueSupported(Object value) { } Class valueClass = value.getClass(); return valueClass == Boolean.class || valueClass == Byte.class || valueClass == Short.class || valueClass == Character.class || valueClass == Integer.class || valueClass == Long.class || - valueClass == Float.class || valueClass == Double.class || valueClass == String.class; + valueClass == Float.class || valueClass == Double.class || valueClass == String.class || value.getClass().isEnum(); } public static byte[] encode(final Map options) { @@ -84,6 +84,9 @@ public static byte[] encode(final Map options) { } else if (valueClz == String.class) { out.writeByte('U'); out.writeUTF((String) value); + } else if (valueClz.isEnum()) { + out.writeByte('U'); + out.writeUTF(((Enum) value).name()); } else { throw new IllegalArgumentException(String.format("Key: %s, Value: %s, Value type: %s", key, value, valueClz)); } diff --git a/compiler/src/org.graalvm.compiler.truffle.common.hotspot.libgraal/src/org/graalvm/compiler/truffle/common/hotspot/libgraal/SVMToHotSpot.java b/compiler/src/org.graalvm.compiler.truffle.common.hotspot.libgraal/src/org/graalvm/compiler/truffle/common/hotspot/libgraal/SVMToHotSpot.java index a19160f8f21f..bd21b2366d63 100644 --- a/compiler/src/org.graalvm.compiler.truffle.common.hotspot.libgraal/src/org/graalvm/compiler/truffle/common/hotspot/libgraal/SVMToHotSpot.java +++ b/compiler/src/org.graalvm.compiler.truffle.common.hotspot.libgraal/src/org/graalvm/compiler/truffle/common/hotspot/libgraal/SVMToHotSpot.java @@ -77,6 +77,7 @@ enum Id { GetCompilableName(String.class, CompilableTruffleAST.class), GetConstantFieldInfo(int.class, HotSpotTruffleCompilerRuntime.class, long.class, boolean.class, int.class), GetDescription(String.class, TruffleSourceLanguagePosition.class), + GetFailedSpeculationsAddress(long.class, CompilableTruffleAST.class), GetFrameSlotKindTagForJavaKind(int.class, HotSpotTruffleCompilerRuntime.class, int.class), GetFrameSlotKindTagsCount(int.class, HotSpotTruffleCompilerRuntime.class), GetInlineKind(int.class, HotSpotTruffleCompilerRuntime.class, long.class, boolean.class), @@ -88,7 +89,6 @@ enum Id { GetOffsetEnd(int.class, TruffleSourceLanguagePosition.class), GetOffsetStart(int.class, TruffleSourceLanguagePosition.class), GetPosition(TruffleSourceLanguagePosition.class, TruffleInliningPlan.class, long.class), - GetSpeculationLog(long.class, CompilableTruffleAST.class), GetStackTrace(StackTraceElement[].class, Throwable.class), GetStackTraceElementClassName(String.class, StackTraceElement.class), GetStackTraceElementFileName(String.class, StackTraceElement.class), diff --git a/compiler/src/org.graalvm.compiler.truffle.common.processor/src/org/graalvm/compiler/truffle/common/processor/Option.java b/compiler/src/org.graalvm.compiler.truffle.common.processor/src/org/graalvm/compiler/truffle/common/processor/Option.java index aa1454a70746..038492460bb4 100644 --- a/compiler/src/org.graalvm.compiler.truffle.common.processor/src/org/graalvm/compiler/truffle/common/processor/Option.java +++ b/compiler/src/org.graalvm.compiler.truffle.common.processor/src/org/graalvm/compiler/truffle/common/processor/Option.java @@ -36,19 +36,19 @@ public class Option { static final Option[] options = { option("TruffleCompileOnly") .type("String") - .category("DEBUG") + .category("INTERNAL") .def("null") .help("Restrict compilation to comma-separated list of includes (or excludes prefixed with tilde).", "EBNF format of argument value: CompileOnly = Element, { ',', Element } ;"), option("TruffleCompilation") .type("Boolean") - .category("DEBUG") + .category("INTERNAL") .def("true") .help("Enable or disable truffle compilation."), option("TruffleCompileImmediately") .type("Boolean") - .category("DEBUG") + .category("INTERNAL") .def("false") .help("Compile immediately to test truffle compiler"), @@ -99,7 +99,7 @@ public class Option { option("TruffleFunctionInlining") .type("Boolean") - .category("DEBUG") + .category("INTERNAL") .def("true") .help("Enable automatic inlining of call targets"), @@ -123,37 +123,37 @@ public class Option { option("TruffleOSR") .type("Boolean") - .category("DEBUG") + .category("INTERNAL") .def("true") .help("Enable on stack replacement for Truffle loops."), option("TruffleOSRCompilationThreshold") .type("Integer") - .category("DEBUG") + .category("INTERNAL") .def("100000") .help("Number of loop iterations until on-stack-replacement compilation is triggered."), option("TruffleSplittingMaxCalleeSize") .type("Integer") - .category("DEBUG") + .category("INTERNAL") .def("100") .help("Disable call target splitting if tree size exceeds this limit"), option("TruffleSplittingGrowthLimit") .type("Double") - .category("DEBUG") + .category("INTERNAL") .def("1.5") .help("Disable call target splitting if the number of nodes created by splitting exceeds this factor times node count"), option("TruffleSplittingMaxNumberOfSplitNodes") .type("Integer") - .category("DEBUG") + .category("INTERNAL") .def("500_000") .help("Disable call target splitting if number of nodes created by splitting exceeds this limit"), option("TruffleSplittingMaxPropagationDepth") .type("Integer") - .category("DEBUG") + .category("INTERNAL") .def("5") .help("Propagate info about a polymorphic specialize through maximum this many call targets"), @@ -201,124 +201,124 @@ public class Option { option("TruffleReturnTypeSpeculation") .type("Boolean") - .category("DEBUG") + .category("INTERNAL") .def("true"), option("TruffleArgumentTypeSpeculation") .type("Boolean") - .category("DEBUG") + .category("INTERNAL") .def("true"), option("TruffleUseFrameWithoutBoxing") .type("Boolean") - .category("DEBUG") + .category("INTERNAL") .def("true"), option("TraceTruffleCompilation") .type("Boolean") - .category("DEBUG") + .category("INTERNAL") .def("false") .help("Print information for compilation results"), option("TraceTruffleCompilationDetails") .type("Boolean") - .category("DEBUG") + .category("INTERNAL") .def("false") .help("Print information for compilation queuing"), option("TraceTruffleCompilationPolymorphism") .type("Boolean") - .category("DEBUG") + .category("INTERNAL") .def("false") .help("Print all polymorphic and generic nodes after each compilation"), option("TraceTruffleCompilationAST") .type("Boolean") - .category("DEBUG") + .category("INTERNAL") .def("false") .help("Print the entire AST after each compilation"), option("TraceTruffleCompilationCallTree") .type("Boolean") - .category("DEBUG") + .category("INTERNAL") .def("false") .help("Print the inlined call tree for each compiled method"), option("TraceTruffleExpansionSource") .type("Boolean") - .category("DEBUG") + .category("INTERNAL") .def("false") .help("Print source sections for printed expansion trees"), option("TruffleCompilationExceptionsAreFatal") .type("Boolean") - .category("DEBUG") + .category("INTERNAL") .def("false") .help("Treat compilation exceptions as fatal exceptions that will exit the application"), option("TrufflePerformanceWarningsAreFatal") .type("Boolean") - .category("DEBUG") + .category("INTERNAL") .def("false") .help("Treat performance warnings as fatal occurrences that will exit the applications"), option("TruffleCompilationExceptionsArePrinted") .type("Boolean") - .category("DEBUG") + .category("INTERNAL") .def("true") .help("Prints the exception stack trace for compilation exceptions"), option("TruffleCompilationExceptionsAreThrown") .type("Boolean") - .category("DEBUG") + .category("INTERNAL") .def("false") .help("Treat compilation exceptions as thrown runtime exceptions"), option("TraceTruffleInlining") .type("Boolean") - .category("DEBUG") + .category("INTERNAL") .def("false") .help("Print information for inlining for each compilation."), option("TraceTruffleSplitting") .type("Boolean") - .category("DEBUG") + .category("INTERNAL") .def("false") .help("Print information for each splitted call site."), option("TraceTruffleAssumptions") .type("Boolean") - .category("DEBUG") + .category("INTERNAL") .def("false") .help("Print stack trace on assumption invalidation"), option("TraceTruffleStackTraceLimit") .type("Integer") - .category("DEBUG") + .category("INTERNAL") .def("20") .help("Number of stack trace elements printed by TraceTruffleTransferToInterpreter and TraceTruffleAssumptions"), option("TruffleCompilationStatistics") .type("Boolean") - .category("DEBUG") + .category("INTERNAL") .def("false") .help("Print Truffle compilation statistics at the end of a run."), option("TruffleCompilationStatisticDetails") .type("Boolean") - .category("DEBUG") + .category("INTERNAL") .def("false") .help("Print additional more verbose Truffle compilation statistics at the end of a run."), option("TruffleProfilingEnabled") .type("Boolean") - .category("DEBUG") + .category("INTERNAL") .def("true") .help("Enable/disable builtin profiles in com.oracle.truffle.api.profiles."), option("TraceTruffleTransferToInterpreter") .type("Boolean") - .category("DEBUG") + .category("INTERNAL") .def("false") .help("Print stack trace on transfer to interpreter."), diff --git a/compiler/src/org.graalvm.compiler.truffle.common.processor/src/org/graalvm/compiler/truffle/common/processor/SharedTruffleOptionsProcessor.java b/compiler/src/org.graalvm.compiler.truffle.common.processor/src/org/graalvm/compiler/truffle/common/processor/SharedTruffleOptionsProcessor.java index ba57e1f565b1..436aca84283a 100644 --- a/compiler/src/org.graalvm.compiler.truffle.common.processor/src/org/graalvm/compiler/truffle/common/processor/SharedTruffleOptionsProcessor.java +++ b/compiler/src/org.graalvm.compiler.truffle.common.processor/src/org/graalvm/compiler/truffle/common/processor/SharedTruffleOptionsProcessor.java @@ -141,7 +141,12 @@ public boolean doProcess(Set annotations, RoundEnvironmen } out.printf(" @Option(help = \"%s\", category = OptionCategory.%s)\n", help, option.category); } else { - String optionType = option.category.charAt(0) + option.category.substring(1).toLowerCase(); + String optionType; + if (option.category.equals("INTERNAL")) { + optionType = "Debug"; + } else { + optionType = option.category.charAt(0) + option.category.substring(1).toLowerCase(); + } out.printf(" /**\n"); for (int i = 0; i < option.help.length; i++) { String line = option.help[i]; diff --git a/compiler/src/org.graalvm.compiler.truffle.common/src/org/graalvm/compiler/truffle/common/CompilableTruffleAST.java b/compiler/src/org.graalvm.compiler.truffle.common/src/org/graalvm/compiler/truffle/common/CompilableTruffleAST.java index 54f571b8e347..a4814f574a56 100644 --- a/compiler/src/org.graalvm.compiler.truffle.common/src/org/graalvm/compiler/truffle/common/CompilableTruffleAST.java +++ b/compiler/src/org.graalvm.compiler.truffle.common/src/org/graalvm/compiler/truffle/common/CompilableTruffleAST.java @@ -38,7 +38,12 @@ public interface CompilableTruffleAST { */ JavaConstant asJavaConstant(); - SpeculationLog getSpeculationLog(); + /** + * Gets a speculation log to be used for a single Truffle compilation. The returned speculation + * log provides access to all relevant failed speculations as well as support for making + * speculation during a single compilation. + */ + SpeculationLog getCompilationSpeculationLog(); /** * Notifies this object that a compilation of the AST it represents failed. diff --git a/compiler/src/org.graalvm.compiler.truffle.compiler.amd64/src/org/graalvm/compiler/truffle/compiler/amd64/substitutions/AMD64ArrayUtilsSubstitutions.java b/compiler/src/org.graalvm.compiler.truffle.compiler.amd64/src/org/graalvm/compiler/truffle/compiler/amd64/substitutions/AMD64ArrayUtilsSubstitutions.java index 152fec0b67bc..c5259a891aa9 100644 --- a/compiler/src/org.graalvm.compiler.truffle.compiler.amd64/src/org/graalvm/compiler/truffle/compiler/amd64/substitutions/AMD64ArrayUtilsSubstitutions.java +++ b/compiler/src/org.graalvm.compiler.truffle.compiler.amd64/src/org/graalvm/compiler/truffle/compiler/amd64/substitutions/AMD64ArrayUtilsSubstitutions.java @@ -24,8 +24,9 @@ */ package org.graalvm.compiler.truffle.compiler.amd64.substitutions; -import jdk.vm.ci.meta.JavaKind; -import jdk.vm.ci.meta.MetaAccessProvider; +import static org.graalvm.compiler.api.replacements.Fold.InjectedParameter; +import static org.graalvm.compiler.serviceprovider.JavaVersionUtil.Java8OrEarlier; + import org.graalvm.compiler.api.replacements.ClassSubstitution; import org.graalvm.compiler.api.replacements.Fold; import org.graalvm.compiler.api.replacements.MethodSubstitution; @@ -35,8 +36,8 @@ import org.graalvm.compiler.word.Word; import org.graalvm.word.Pointer; -import static org.graalvm.compiler.api.replacements.Fold.InjectedParameter; -import static org.graalvm.compiler.serviceprovider.GraalServices.Java8OrEarlier; +import jdk.vm.ci.meta.JavaKind; +import jdk.vm.ci.meta.MetaAccessProvider; @ClassSubstitution(className = "com.oracle.truffle.api.ArrayUtils", optional = true) public class AMD64ArrayUtilsSubstitutions { diff --git a/compiler/src/org.graalvm.compiler.truffle.compiler.hotspot.libgraal/src/org/graalvm/compiler/truffle/compiler/hotspot/libgraal/HSCompilableTruffleAST.java b/compiler/src/org.graalvm.compiler.truffle.compiler.hotspot.libgraal/src/org/graalvm/compiler/truffle/compiler/hotspot/libgraal/HSCompilableTruffleAST.java index dd6380f27fdc..17545098abef 100644 --- a/compiler/src/org.graalvm.compiler.truffle.compiler.hotspot.libgraal/src/org/graalvm/compiler/truffle/compiler/hotspot/libgraal/HSCompilableTruffleAST.java +++ b/compiler/src/org.graalvm.compiler.truffle.compiler.hotspot.libgraal/src/org/graalvm/compiler/truffle/compiler/hotspot/libgraal/HSCompilableTruffleAST.java @@ -29,13 +29,13 @@ import static org.graalvm.compiler.truffle.common.hotspot.libgraal.SVMToHotSpot.Id.CompilableToString; import static org.graalvm.compiler.truffle.common.hotspot.libgraal.SVMToHotSpot.Id.CreateStringSupplier; import static org.graalvm.compiler.truffle.common.hotspot.libgraal.SVMToHotSpot.Id.GetCompilableName; -import static org.graalvm.compiler.truffle.common.hotspot.libgraal.SVMToHotSpot.Id.GetSpeculationLog; +import static org.graalvm.compiler.truffle.common.hotspot.libgraal.SVMToHotSpot.Id.GetFailedSpeculationsAddress; import static org.graalvm.compiler.truffle.common.hotspot.libgraal.SVMToHotSpot.Id.OnCompilationFailed; import static org.graalvm.compiler.truffle.compiler.hotspot.libgraal.HSCompilableTruffleASTGen.callAsJavaConstant; import static org.graalvm.compiler.truffle.compiler.hotspot.libgraal.HSCompilableTruffleASTGen.callCompilableToString; import static org.graalvm.compiler.truffle.compiler.hotspot.libgraal.HSCompilableTruffleASTGen.callCreateStringSupplier; import static org.graalvm.compiler.truffle.compiler.hotspot.libgraal.HSCompilableTruffleASTGen.callGetCompilableName; -import static org.graalvm.compiler.truffle.compiler.hotspot.libgraal.HSCompilableTruffleASTGen.callGetSpeculationLog; +import static org.graalvm.compiler.truffle.compiler.hotspot.libgraal.HSCompilableTruffleASTGen.callGetFailedSpeculationsAddress; import static org.graalvm.compiler.truffle.compiler.hotspot.libgraal.HSCompilableTruffleASTGen.callOnCompilationFailed; import static org.graalvm.compiler.truffle.compiler.hotspot.libgraal.JNIUtil.createString; import static org.graalvm.compiler.truffle.compiler.hotspot.libgraal.HotSpotToSVMScope.env; @@ -49,6 +49,7 @@ import org.graalvm.compiler.truffle.compiler.hotspot.libgraal.JNI.JObject; import org.graalvm.compiler.truffle.compiler.hotspot.libgraal.JNI.JString; +import jdk.vm.ci.hotspot.HotSpotSpeculationLog; import jdk.vm.ci.meta.JavaConstant; import jdk.vm.ci.meta.SpeculationLog; @@ -62,7 +63,7 @@ final class HSCompilableTruffleAST extends HSObject implements CompilableTruffle /** * Handle to {@code speculationLog} field of the {@code OptimizedCallTarget}. */ - private Long cachedSpeculationLogHandle; + private Long cachedFailedSpeculationsAddress; /** * Creates a new {@link HSCompilableTruffleAST} holding the JNI {@code JObject} by a global @@ -86,15 +87,15 @@ final class HSCompilableTruffleAST extends HSObject implements CompilableTruffle super(scope, handle); } - @SVMToHotSpot(GetSpeculationLog) + @SVMToHotSpot(GetFailedSpeculationsAddress) @Override - public SpeculationLog getSpeculationLog() { - Long res = cachedSpeculationLogHandle; + public SpeculationLog getCompilationSpeculationLog() { + Long res = cachedFailedSpeculationsAddress; if (res == null) { - res = callGetSpeculationLog(env(), getHandle()); - cachedSpeculationLogHandle = res; + res = callGetFailedSpeculationsAddress(env(), getHandle()); + cachedFailedSpeculationsAddress = res; } - return SVMObjectHandles.resolve(res, SpeculationLog.class); + return new HotSpotSpeculationLog(cachedFailedSpeculationsAddress); } @SVMToHotSpot(AsJavaConstant) diff --git a/compiler/src/org.graalvm.compiler.truffle.compiler.hotspot.libgraal/src/org/graalvm/compiler/truffle/compiler/hotspot/libgraal/HotSpotToSVMEntryPoints.java b/compiler/src/org.graalvm.compiler.truffle.compiler.hotspot.libgraal/src/org/graalvm/compiler/truffle/compiler/hotspot/libgraal/HotSpotToSVMEntryPoints.java index 92e77db0053b..f46809796a46 100644 --- a/compiler/src/org.graalvm.compiler.truffle.compiler.hotspot.libgraal/src/org/graalvm/compiler/truffle/compiler/hotspot/libgraal/HotSpotToSVMEntryPoints.java +++ b/compiler/src/org.graalvm.compiler.truffle.compiler.hotspot.libgraal/src/org/graalvm/compiler/truffle/compiler/hotspot/libgraal/HotSpotToSVMEntryPoints.java @@ -30,7 +30,6 @@ import static org.graalvm.compiler.truffle.common.hotspot.libgraal.HotSpotToSVM.Id.CloseCompilation; import static org.graalvm.compiler.truffle.common.hotspot.libgraal.HotSpotToSVM.Id.CloseDebugContext; import static org.graalvm.compiler.truffle.common.hotspot.libgraal.HotSpotToSVM.Id.CloseDebugContextScope; -import static org.graalvm.compiler.truffle.common.hotspot.libgraal.HotSpotToSVM.Id.CreateSpeculationLog; import static org.graalvm.compiler.truffle.common.hotspot.libgraal.HotSpotToSVM.Id.DoCompile; import static org.graalvm.compiler.truffle.common.hotspot.libgraal.HotSpotToSVM.Id.DumpChannelClose; import static org.graalvm.compiler.truffle.common.hotspot.libgraal.HotSpotToSVM.Id.DumpChannelWrite; @@ -63,7 +62,6 @@ import static org.graalvm.compiler.truffle.common.hotspot.libgraal.HotSpotToSVM.Id.PendingTransferToInterpreterOffset; import static org.graalvm.compiler.truffle.common.hotspot.libgraal.HotSpotToSVM.Id.ReleaseHandle; import static org.graalvm.compiler.truffle.common.hotspot.libgraal.HotSpotToSVM.Id.Shutdown; -import static org.graalvm.compiler.truffle.compiler.hotspot.libgraal.SVMToHotSpotUtil.getJNIClass; import static org.graalvm.compiler.truffle.compiler.hotspot.libgraal.JNIUtil.GetArrayLength; import static org.graalvm.compiler.truffle.compiler.hotspot.libgraal.JNIUtil.GetByteArrayElements; import static org.graalvm.compiler.truffle.compiler.hotspot.libgraal.JNIUtil.NewByteArray; @@ -72,6 +70,7 @@ import static org.graalvm.compiler.truffle.compiler.hotspot.libgraal.JNIUtil.SetObjectArrayElement; import static org.graalvm.compiler.truffle.compiler.hotspot.libgraal.JNIUtil.createHSString; import static org.graalvm.compiler.truffle.compiler.hotspot.libgraal.JNIUtil.createString; +import static org.graalvm.compiler.truffle.compiler.hotspot.libgraal.SVMToHotSpotUtil.getJNIClass; import java.io.IOException; import java.io.InputStream; @@ -89,6 +88,8 @@ import org.graalvm.collections.EconomicMap; import org.graalvm.collections.UnmodifiableMapCursor; +import org.graalvm.compiler.core.common.CompilationIdentifier; +import org.graalvm.compiler.debug.DebugContext; import org.graalvm.compiler.debug.DebugOptions; import org.graalvm.compiler.debug.TTY; import org.graalvm.compiler.hotspot.CompilerConfigurationFactory; @@ -98,15 +99,21 @@ import org.graalvm.compiler.options.OptionValues; import org.graalvm.compiler.options.OptionsParser; import org.graalvm.compiler.truffle.common.CompilableTruffleAST; +import org.graalvm.compiler.truffle.common.TruffleCompilation; import org.graalvm.compiler.truffle.common.TruffleCompilationTask; import org.graalvm.compiler.truffle.common.TruffleCompilerListener; import org.graalvm.compiler.truffle.common.TruffleCompilerListener.CompilationResultInfo; import org.graalvm.compiler.truffle.common.TruffleCompilerListener.GraphInfo; import org.graalvm.compiler.truffle.common.TruffleCompilerRuntimeInstance; +import org.graalvm.compiler.truffle.common.TruffleDebugContext; +import org.graalvm.compiler.truffle.common.TruffleDebugJavaMethod; import org.graalvm.compiler.truffle.common.TruffleInliningPlan; -import org.graalvm.compiler.truffle.common.hotspot.libgraal.OptionsEncoder; +import org.graalvm.compiler.truffle.common.VoidGraphStructure; import org.graalvm.compiler.truffle.common.hotspot.libgraal.HotSpotToSVM; +import org.graalvm.compiler.truffle.common.hotspot.libgraal.OptionsEncoder; +import org.graalvm.compiler.truffle.compiler.TruffleCompilationIdentifier; import org.graalvm.compiler.truffle.compiler.TruffleCompilerOptions; +import org.graalvm.compiler.truffle.compiler.TruffleDebugContextImpl; import org.graalvm.compiler.truffle.compiler.hotspot.HotSpotTruffleCompilerImpl; import org.graalvm.compiler.truffle.compiler.hotspot.HotSpotTruffleCompilerImpl.Options; import org.graalvm.compiler.truffle.compiler.hotspot.libgraal.JNI.JArray; @@ -116,28 +123,18 @@ import org.graalvm.compiler.truffle.compiler.hotspot.libgraal.JNI.JObject; import org.graalvm.compiler.truffle.compiler.hotspot.libgraal.JNI.JObjectArray; import org.graalvm.compiler.truffle.compiler.hotspot.libgraal.JNI.JString; +import org.graalvm.graphio.GraphOutput; import org.graalvm.nativeimage.Isolate; import org.graalvm.nativeimage.IsolateThread; import org.graalvm.nativeimage.Platform.HOSTED_ONLY; import org.graalvm.nativeimage.Platforms; import org.graalvm.nativeimage.c.function.CEntryPoint; import org.graalvm.nativeimage.c.type.CCharPointer; +import org.graalvm.nativeimage.c.type.CTypeConversion; +import org.graalvm.nativeimage.c.type.VoidPointer; import org.graalvm.word.WordFactory; -import jdk.vm.ci.hotspot.HotSpotSpeculationLog; import jdk.vm.ci.meta.ResolvedJavaType; -import jdk.vm.ci.meta.SpeculationLog; -import org.graalvm.compiler.core.common.CompilationIdentifier; -import org.graalvm.compiler.debug.DebugContext; -import org.graalvm.compiler.truffle.common.TruffleCompilation; -import org.graalvm.compiler.truffle.common.TruffleDebugContext; -import org.graalvm.compiler.truffle.common.TruffleDebugJavaMethod; -import org.graalvm.compiler.truffle.common.VoidGraphStructure; -import org.graalvm.compiler.truffle.compiler.TruffleCompilationIdentifier; -import org.graalvm.compiler.truffle.compiler.TruffleDebugContextImpl; -import org.graalvm.graphio.GraphOutput; -import org.graalvm.nativeimage.c.type.CTypeConversion; -import org.graalvm.nativeimage.c.type.VoidPointer; /** * Entry points in SVM for {@linkplain HotSpotToSVM calls} from HotSpot. @@ -406,19 +403,6 @@ public static JByteArray getInitialOptions(JNIEnv env, JClass hsClazz, @CEntryPo return scope.getObjectResult(); } - @HotSpotToSVM(CreateSpeculationLog) - @SuppressWarnings({"unused", "try"}) - @CEntryPoint(name = "Java_org_graalvm_compiler_truffle_runtime_hotspot_libgraal_HotSpotToSVMCalls_createSpeculationLog") - public static long createSpeculationLog(JNIEnv env, JClass hsClazz, @CEntryPoint.IsolateThreadContext long isolateThreadId) { - try (HotSpotToSVMScope s = new HotSpotToSVMScope(CreateSpeculationLog, env)) { - SpeculationLog specLog = new HotSpotSpeculationLog(); - return SVMObjectHandles.create(specLog); - } catch (Throwable t) { - JNIExceptionWrapper.throwInHotSpot(env, t); - return 0; - } - } - @HotSpotToSVM(CleanReferences) @SuppressWarnings({"unused", "try"}) @CEntryPoint(name = "Java_org_graalvm_compiler_truffle_runtime_hotspot_libgraal_HotSpotToSVMCalls_cleanReferences") diff --git a/compiler/src/org.graalvm.compiler.truffle.compiler.hotspot.libgraal/src/org/graalvm/compiler/truffle/compiler/hotspot/libgraal/JNI.java b/compiler/src/org.graalvm.compiler.truffle.compiler.hotspot.libgraal/src/org/graalvm/compiler/truffle/compiler/hotspot/libgraal/JNI.java index e4567fee649f..0ad52fd3a66e 100644 --- a/compiler/src/org.graalvm.compiler.truffle.compiler.hotspot.libgraal/src/org/graalvm/compiler/truffle/compiler/hotspot/libgraal/JNI.java +++ b/compiler/src/org.graalvm.compiler.truffle.compiler.hotspot.libgraal/src/org/graalvm/compiler/truffle/compiler/hotspot/libgraal/JNI.java @@ -473,7 +473,10 @@ private static Path[] findJNIHeaders() { Path javaHome = Paths.get(System.getProperty("java.home")); Path includeFolder = javaHome.resolve("include"); if (!Files.exists(includeFolder)) { - javaHome = javaHome.getParent(); + Path parent = javaHome.getParent(); + if (parent != null) { + javaHome = parent; + } } includeFolder = javaHome.resolve("include"); if (!Files.exists(includeFolder)) { diff --git a/compiler/src/org.graalvm.compiler.truffle.compiler/src/org/graalvm/compiler/truffle/compiler/TruffleCompilationResultBuilderFactory.java b/compiler/src/org.graalvm.compiler.truffle.compiler/src/org/graalvm/compiler/truffle/compiler/TruffleCompilationResultBuilderFactory.java index 86a9ba783a53..1e2ae7762ad4 100644 --- a/compiler/src/org.graalvm.compiler.truffle.compiler/src/org/graalvm/compiler/truffle/compiler/TruffleCompilationResultBuilderFactory.java +++ b/compiler/src/org.graalvm.compiler.truffle.compiler/src/org/graalvm/compiler/truffle/compiler/TruffleCompilationResultBuilderFactory.java @@ -74,6 +74,7 @@ public CompilationResultBuilder createBuilder(CodeCacheProvider codeCache, Forei protected void closeCompilationResult() { CompilationResult result = this.compilationResult; result.setMethods(graph.method(), graph.getMethods()); + result.setSpeculationLog(graph.getSpeculationLog()); result.setBytecodeSize(graph.getBytecodeSize()); Set newAssumptions = new HashSet<>(); diff --git a/compiler/src/org.graalvm.compiler.truffle.compiler/src/org/graalvm/compiler/truffle/compiler/TruffleCompilerImpl.java b/compiler/src/org.graalvm.compiler.truffle.compiler/src/org/graalvm/compiler/truffle/compiler/TruffleCompilerImpl.java index 673402c84db6..f7ac0c5f9bc5 100644 --- a/compiler/src/org.graalvm.compiler.truffle.compiler/src/org/graalvm/compiler/truffle/compiler/TruffleCompilerImpl.java +++ b/compiler/src/org.graalvm.compiler.truffle.compiler/src/org/graalvm/compiler/truffle/compiler/TruffleCompilerImpl.java @@ -428,12 +428,7 @@ public void compileAST(DebugContext debug, final CompilableTruffleAST compilable try (CompilationAlarm alarm = CompilationAlarm.trackCompilationPeriod(TruffleCompilerOptions.getOptions())) { PhaseSuite graphBuilderSuite = createGraphBuilderSuite(); - // Failed speculations must be collected before any compilation or - // partial evaluation is performed. - SpeculationLog speculationLog = compilable.getSpeculationLog(); - if (speculationLog != null) { - speculationLog.collectFailedSpeculations(); - } + SpeculationLog speculationLog = compilable.getCompilationSpeculationLog(); try (DebugCloseable a = PartialEvaluationTime.start(debug); DebugCloseable c = PartialEvaluationMemUse.start(debug)) { graph = partialEvaluator.createGraph(debug, compilable, inliningPlan, AllowAssumptions.YES, compilationId, speculationLog, task); @@ -523,8 +518,8 @@ public CompilationResult compilePEGraph(StructuredGraph graph, String name, Phas try (DebugCloseable a = CodeInstallationTime.start(debug); DebugCloseable c = CodeInstallationMemUse.start(debug)) { InstalledCode installedCode = createInstalledCode(compilable); - - backend.createInstalledCode(debug, graph.method(), compilationRequest, result, graph.getSpeculationLog(), installedCode, false); + assert graph.getSpeculationLog() == result.getSpeculationLog(); + backend.createInstalledCode(debug, graph.method(), compilationRequest, result, installedCode, false); } catch (Throwable e) { throw debug.handle(e); } diff --git a/compiler/src/org.graalvm.compiler.truffle.compiler/src/org/graalvm/compiler/truffle/compiler/nodes/frame/NewFrameNode.java b/compiler/src/org.graalvm.compiler.truffle.compiler/src/org/graalvm/compiler/truffle/compiler/nodes/frame/NewFrameNode.java index 3d9719f470c8..6b859cb75bf7 100644 --- a/compiler/src/org.graalvm.compiler.truffle.compiler/src/org/graalvm/compiler/truffle/compiler/nodes/frame/NewFrameNode.java +++ b/compiler/src/org.graalvm.compiler.truffle.compiler/src/org/graalvm/compiler/truffle/compiler/nodes/frame/NewFrameNode.java @@ -52,6 +52,7 @@ import org.graalvm.compiler.nodes.virtual.VirtualArrayNode; import org.graalvm.compiler.nodes.virtual.VirtualInstanceNode; import org.graalvm.compiler.nodes.virtual.VirtualObjectNode; +import org.graalvm.compiler.serviceprovider.SpeculationReasonGroup; import org.graalvm.compiler.truffle.common.TruffleCompilerRuntime; import org.graalvm.compiler.truffle.compiler.nodes.TruffleAssumption; import org.graalvm.compiler.truffle.compiler.substitutions.KnownTruffleTypes; @@ -88,24 +89,6 @@ public final class NewFrameNode extends FixedWithNextNode implements IterableNod private final SpeculationReason intrinsifyAccessorsSpeculation; - static final class IntrinsifyFrameAccessorsSpeculationReason implements SpeculationReason { - private final JavaConstant frameDescriptor; - - IntrinsifyFrameAccessorsSpeculationReason(JavaConstant frameDescriptor) { - this.frameDescriptor = frameDescriptor; - } - - @Override - public boolean equals(Object obj) { - return obj instanceof IntrinsifyFrameAccessorsSpeculationReason && ((IntrinsifyFrameAccessorsSpeculationReason) obj).frameDescriptor.equals(this.frameDescriptor); - } - - @Override - public int hashCode() { - return frameDescriptor.hashCode(); - } - } - private static JavaKind asJavaKind(JavaConstant frameSlotTag) { int tagValue = frameSlotTag.asInt(); JavaKind rawKind = TruffleCompilerRuntime.getRuntime().getJavaKindForFrameSlotKind(tagValue); @@ -125,6 +108,8 @@ private static JavaKind asJavaKind(JavaConstant frameSlotTag) { throw new IllegalStateException("Unexpected frame slot kind tag: " + tagValue); } + private static final SpeculationReasonGroup INTRINSIFY_FRAME_ACCESSORS_SPECULATIONS = new SpeculationReasonGroup("IntrinsifyFrameAccessor"); + public NewFrameNode(GraphBuilderContext b, ValueNode frameDescriptorNode, ValueNode arguments, KnownTruffleTypes types) { super(TYPE, StampFactory.objectNonNull(TypeReference.createExactTrusted(types.classFrameClass))); @@ -152,8 +137,12 @@ public NewFrameNode(GraphBuilderContext b, ValueNode frameDescriptorNode, ValueN * data arrays, which means that set-methods need a FrameState. Most of the benefit of * accessor method intrinsification is avoiding the FrameState creation during partial * evaluation. + * + * The frame descriptor of a call target does not change and since a SpeculationLog is + * already associated with a specific call target we only need a single speculation object + * representing a speculation on a NewFrameNode. */ - this.intrinsifyAccessorsSpeculation = new IntrinsifyFrameAccessorsSpeculationReason(frameDescriptor); + this.intrinsifyAccessorsSpeculation = INTRINSIFY_FRAME_ACCESSORS_SPECULATIONS.createSpeculationReason(); boolean intrinsify = false; if (!constantReflection.readFieldValue(types.fieldFrameDescriptorMaterializeCalled, frameDescriptor).asBoolean()) { diff --git a/compiler/src/org.graalvm.compiler.truffle.runtime.hotspot.jdk8/src/org/graalvm/compiler/truffle/runtime/hotspot/HotSpotTruffleRuntimeServices.java b/compiler/src/org.graalvm.compiler.truffle.runtime.hotspot.jdk8/src/org/graalvm/compiler/truffle/runtime/hotspot/HotSpotTruffleRuntimeServices.java new file mode 100644 index 000000000000..a9cec44b3e90 --- /dev/null +++ b/compiler/src/org.graalvm.compiler.truffle.runtime.hotspot.jdk8/src/org/graalvm/compiler/truffle/runtime/hotspot/HotSpotTruffleRuntimeServices.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.graalvm.compiler.truffle.runtime.hotspot; + +import org.graalvm.compiler.truffle.runtime.OptimizedCallTarget; + +import jdk.vm.ci.hotspot.HotSpotSpeculationLog; +import jdk.vm.ci.meta.SpeculationLog; + +/** + * JDK 8 version of {@link HotSpotTruffleRuntimeServices}. + */ +class HotSpotTruffleRuntimeServices { + + /** + * A wrapper that holds a strong reference to a "master" speculation log that + * {@linkplain HotSpotSpeculationLog#managesFailedSpeculations() manages} the failed + * speculations list. + */ + static class CompilationSpeculationLog extends HotSpotSpeculationLog { + private final HotSpotSpeculationLog masterLog; + + CompilationSpeculationLog(HotSpotSpeculationLog masterLog) { + super(masterLog.getFailedSpeculationsAddress()); + this.masterLog = masterLog; + } + + @Override + public String toString() { + return masterLog.toString(); + } + } + + /** + * Gets a speculation log to be used for compiling {@code callTarget}. + */ + public static SpeculationLog getCompilationSpeculationLog(OptimizedCallTarget callTarget) { + HotSpotSpeculationLog masterLog = (HotSpotSpeculationLog) callTarget.getSpeculationLog(); + return new CompilationSpeculationLog(masterLog); + } +} diff --git a/compiler/src/org.graalvm.compiler.truffle.runtime.hotspot.jdk9/src/org/graalvm/compiler/truffle/runtime/hotspot/HotSpotTruffleRuntimeServices.java b/compiler/src/org.graalvm.compiler.truffle.runtime.hotspot.jdk9/src/org/graalvm/compiler/truffle/runtime/hotspot/HotSpotTruffleRuntimeServices.java new file mode 100644 index 000000000000..5179ecba6522 --- /dev/null +++ b/compiler/src/org.graalvm.compiler.truffle.runtime.hotspot.jdk9/src/org/graalvm/compiler/truffle/runtime/hotspot/HotSpotTruffleRuntimeServices.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.graalvm.compiler.truffle.runtime.hotspot; + +import org.graalvm.compiler.truffle.runtime.OptimizedCallTarget; + +import jdk.vm.ci.meta.SpeculationLog; + +/** + * JDK 9+ version of {@link HotSpotTruffleRuntimeServices}. + */ +class HotSpotTruffleRuntimeServices { + + /** + * Gets a speculation log to be used for compiling {@code callTarget}. + */ + public static SpeculationLog getCompilationSpeculationLog(OptimizedCallTarget callTarget) { + SpeculationLog log = callTarget.getSpeculationLog(); + if (log != null) { + log.collectFailedSpeculations(); + } + return log; + } +} diff --git a/compiler/src/org.graalvm.compiler.truffle.runtime.hotspot.libgraal/src/org/graalvm/compiler/truffle/runtime/hotspot/libgraal/HotSpotToSVMCalls.java b/compiler/src/org.graalvm.compiler.truffle.runtime.hotspot.libgraal/src/org/graalvm/compiler/truffle/runtime/hotspot/libgraal/HotSpotToSVMCalls.java index a2e460574df8..b79924bdb60e 100644 --- a/compiler/src/org.graalvm.compiler.truffle.runtime.hotspot.libgraal/src/org/graalvm/compiler/truffle/runtime/hotspot/libgraal/HotSpotToSVMCalls.java +++ b/compiler/src/org.graalvm.compiler.truffle.runtime.hotspot.libgraal/src/org/graalvm/compiler/truffle/runtime/hotspot/libgraal/HotSpotToSVMCalls.java @@ -24,13 +24,11 @@ */ package org.graalvm.compiler.truffle.runtime.hotspot.libgraal; -import java.nio.ByteBuffer; import static org.graalvm.compiler.truffle.common.hotspot.libgraal.HotSpotToSVM.Id.AttachThread; import static org.graalvm.compiler.truffle.common.hotspot.libgraal.HotSpotToSVM.Id.CleanReferences; import static org.graalvm.compiler.truffle.common.hotspot.libgraal.HotSpotToSVM.Id.CloseCompilation; import static org.graalvm.compiler.truffle.common.hotspot.libgraal.HotSpotToSVM.Id.CloseDebugContext; import static org.graalvm.compiler.truffle.common.hotspot.libgraal.HotSpotToSVM.Id.CloseDebugContextScope; -import static org.graalvm.compiler.truffle.common.hotspot.libgraal.HotSpotToSVM.Id.CreateSpeculationLog; import static org.graalvm.compiler.truffle.common.hotspot.libgraal.HotSpotToSVM.Id.DoCompile; import static org.graalvm.compiler.truffle.common.hotspot.libgraal.HotSpotToSVM.Id.DumpChannelClose; import static org.graalvm.compiler.truffle.common.hotspot.libgraal.HotSpotToSVM.Id.DumpChannelWrite; @@ -64,6 +62,8 @@ import static org.graalvm.compiler.truffle.common.hotspot.libgraal.HotSpotToSVM.Id.ReleaseHandle; import static org.graalvm.compiler.truffle.common.hotspot.libgraal.HotSpotToSVM.Id.Shutdown; +import java.nio.ByteBuffer; + import org.graalvm.compiler.truffle.common.CompilableTruffleAST; import org.graalvm.compiler.truffle.common.TruffleCompilationTask; import org.graalvm.compiler.truffle.common.TruffleCompilerListener; @@ -125,9 +125,6 @@ static native void doCompile(long isolateThreadId, @HotSpotToSVM(Log) static native void log(long isolateThreadId, String message); - @HotSpotToSVM(CreateSpeculationLog) - static native long createSpeculationLog(long isolateThreadId); - @HotSpotToSVM(GetNodeCount) static native int getNodeCount(long isolateThreadId, long handle); diff --git a/compiler/src/org.graalvm.compiler.truffle.runtime.hotspot.libgraal/src/org/graalvm/compiler/truffle/runtime/hotspot/libgraal/LibGraalTruffleRuntime.java b/compiler/src/org.graalvm.compiler.truffle.runtime.hotspot.libgraal/src/org/graalvm/compiler/truffle/runtime/hotspot/libgraal/LibGraalTruffleRuntime.java index 129062ea4b8b..693007e4bc69 100644 --- a/compiler/src/org.graalvm.compiler.truffle.runtime.hotspot.libgraal/src/org/graalvm/compiler/truffle/runtime/hotspot/libgraal/LibGraalTruffleRuntime.java +++ b/compiler/src/org.graalvm.compiler.truffle.runtime.hotspot.libgraal/src/org/graalvm/compiler/truffle/runtime/hotspot/libgraal/LibGraalTruffleRuntime.java @@ -39,7 +39,6 @@ import jdk.vm.ci.hotspot.HotSpotJVMCIRuntime; import jdk.vm.ci.hotspot.HotSpotResolvedJavaType; import jdk.vm.ci.meta.MetaAccessProvider; -import jdk.vm.ci.meta.SpeculationLog; /** * A {@link TruffleRuntime} that uses libgraal for compilation. @@ -133,11 +132,6 @@ public void log(String message) { HotSpotToSVMCalls.log(getIsolateThreadId(), message); } - @Override - public SpeculationLog createSpeculationLog() { - return new SVMSpeculationLog(HotSpotToSVMCalls.createSpeculationLog(getIsolateThreadId())); - } - /** * Clears JNI GlobalReferences to HotSpot objects held by object on SVM heap. NOTE: This method * is called reflectively by Truffle tests. diff --git a/compiler/src/org.graalvm.compiler.truffle.runtime.hotspot.libgraal/src/org/graalvm/compiler/truffle/runtime/hotspot/libgraal/SVMSpeculationLog.java b/compiler/src/org.graalvm.compiler.truffle.runtime.hotspot.libgraal/src/org/graalvm/compiler/truffle/runtime/hotspot/libgraal/SVMSpeculationLog.java deleted file mode 100644 index 8e8fe594cf05..000000000000 --- a/compiler/src/org.graalvm.compiler.truffle.runtime.hotspot.libgraal/src/org/graalvm/compiler/truffle/runtime/hotspot/libgraal/SVMSpeculationLog.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package org.graalvm.compiler.truffle.runtime.hotspot.libgraal; - -import org.graalvm.compiler.truffle.runtime.OptimizedCallTarget; - -import jdk.vm.ci.meta.JavaConstant; -import jdk.vm.ci.meta.SpeculationLog; - -/** - * Encapsulates a handle to {@link SpeculationLog} object in the SVM heap. This exists only so that - * the SVM object can be collected when its {@link OptimizedCallTarget} owner in the HotSpot Truffle - * runtime dies. Calling any of the methods specified by {@link SpeculationLog} on an instance of - * this class will result in an {@link IllegalArgumentException}. - */ -final class SVMSpeculationLog extends SVMObject implements SpeculationLog { - - SVMSpeculationLog(long handle) { - super(handle); - } - - private static IllegalArgumentException error() { - throw new IllegalArgumentException("Cannot call method on runtime proxy to SVM " + SpeculationLog.class.getSimpleName() + " object"); - } - - @Override - public boolean maySpeculate(SpeculationReason reason) { - throw error(); - } - - @Override - public Speculation speculate(SpeculationReason reason) { - throw error(); - } - - @Override - public void collectFailedSpeculations() { - throw error(); - } - - @Override - public boolean hasSpeculations() { - throw error(); - } - - @Override - public Speculation lookupSpeculation(JavaConstant constant) { - throw error(); - } -} diff --git a/compiler/src/org.graalvm.compiler.truffle.runtime.hotspot.libgraal/src/org/graalvm/compiler/truffle/runtime/hotspot/libgraal/SVMToHotSpotEntryPoints.java b/compiler/src/org.graalvm.compiler.truffle.runtime.hotspot.libgraal/src/org/graalvm/compiler/truffle/runtime/hotspot/libgraal/SVMToHotSpotEntryPoints.java index 989df14b96a1..5170ad13ab9f 100644 --- a/compiler/src/org.graalvm.compiler.truffle.runtime.hotspot.libgraal/src/org/graalvm/compiler/truffle/runtime/hotspot/libgraal/SVMToHotSpotEntryPoints.java +++ b/compiler/src/org.graalvm.compiler.truffle.runtime.hotspot.libgraal/src/org/graalvm/compiler/truffle/runtime/hotspot/libgraal/SVMToHotSpotEntryPoints.java @@ -41,6 +41,7 @@ import static org.graalvm.compiler.truffle.common.hotspot.libgraal.SVMToHotSpot.Id.GetCompilableName; import static org.graalvm.compiler.truffle.common.hotspot.libgraal.SVMToHotSpot.Id.GetConstantFieldInfo; import static org.graalvm.compiler.truffle.common.hotspot.libgraal.SVMToHotSpot.Id.GetDescription; +import static org.graalvm.compiler.truffle.common.hotspot.libgraal.SVMToHotSpot.Id.GetFailedSpeculationsAddress; import static org.graalvm.compiler.truffle.common.hotspot.libgraal.SVMToHotSpot.Id.GetFrameSlotKindTagForJavaKind; import static org.graalvm.compiler.truffle.common.hotspot.libgraal.SVMToHotSpot.Id.GetInlineKind; import static org.graalvm.compiler.truffle.common.hotspot.libgraal.SVMToHotSpot.Id.GetLanguage; @@ -50,7 +51,6 @@ import static org.graalvm.compiler.truffle.common.hotspot.libgraal.SVMToHotSpot.Id.GetOffsetEnd; import static org.graalvm.compiler.truffle.common.hotspot.libgraal.SVMToHotSpot.Id.GetOffsetStart; import static org.graalvm.compiler.truffle.common.hotspot.libgraal.SVMToHotSpot.Id.GetPosition; -import static org.graalvm.compiler.truffle.common.hotspot.libgraal.SVMToHotSpot.Id.GetSpeculationLog; import static org.graalvm.compiler.truffle.common.hotspot.libgraal.SVMToHotSpot.Id.GetStackTrace; import static org.graalvm.compiler.truffle.common.hotspot.libgraal.SVMToHotSpot.Id.GetStackTraceElementClassName; import static org.graalvm.compiler.truffle.common.hotspot.libgraal.SVMToHotSpot.Id.GetStackTraceElementFileName; @@ -104,9 +104,11 @@ import org.graalvm.compiler.truffle.common.hotspot.HotSpotTruffleCompilerRuntime; import org.graalvm.compiler.truffle.common.hotspot.libgraal.SVMToHotSpot; import org.graalvm.compiler.truffle.common.hotspot.libgraal.SVMToHotSpot.Id; +import org.graalvm.compiler.truffle.runtime.OptimizedCallTarget; import jdk.vm.ci.code.InstalledCode; import jdk.vm.ci.hotspot.HotSpotJVMCIRuntime; +import jdk.vm.ci.hotspot.HotSpotSpeculationLog; import jdk.vm.ci.meta.JavaConstant; import jdk.vm.ci.meta.JavaKind; import jdk.vm.ci.meta.ResolvedJavaField; @@ -282,9 +284,11 @@ static void onCodeInstallation(HotSpotTruffleCompilerRuntime truffleRuntime, Com truffleRuntime.onCodeInstallation(compilable, installedCode); } - @SVMToHotSpot(GetSpeculationLog) - static long getSpeculationLog(CompilableTruffleAST compilable) { - return ((SVMSpeculationLog) compilable.getSpeculationLog()).handle; + @SVMToHotSpot(GetFailedSpeculationsAddress) + static long getFailedSpeculationsAddress(CompilableTruffleAST compilable) { + OptimizedCallTarget callTarget = (OptimizedCallTarget) compilable; + HotSpotSpeculationLog log = (HotSpotSpeculationLog) callTarget.getSpeculationLog(); + return log.getFailedSpeculationsAddress(); } /** diff --git a/compiler/src/org.graalvm.compiler.truffle.runtime.hotspot/src/org/graalvm/compiler/truffle/runtime/hotspot/AbstractHotSpotTruffleRuntime.java b/compiler/src/org.graalvm.compiler.truffle.runtime.hotspot/src/org/graalvm/compiler/truffle/runtime/hotspot/AbstractHotSpotTruffleRuntime.java index 5eff12dfc95a..828d8c71610c 100644 --- a/compiler/src/org.graalvm.compiler.truffle.runtime.hotspot/src/org/graalvm/compiler/truffle/runtime/hotspot/AbstractHotSpotTruffleRuntime.java +++ b/compiler/src/org.graalvm.compiler.truffle.runtime.hotspot/src/org/graalvm/compiler/truffle/runtime/hotspot/AbstractHotSpotTruffleRuntime.java @@ -44,6 +44,7 @@ import org.graalvm.compiler.truffle.runtime.BackgroundCompileQueue; import org.graalvm.compiler.truffle.runtime.GraalTruffleRuntime; import org.graalvm.compiler.truffle.runtime.OptimizedCallTarget; +import org.graalvm.compiler.truffle.runtime.OptimizedOSRLoopNode; import org.graalvm.compiler.truffle.runtime.TruffleCallBoundary; import org.graalvm.compiler.truffle.runtime.TruffleRuntimeOptions; @@ -174,6 +175,21 @@ public void onCodeInstallation(CompilableTruffleAST compilable, InstalledCode in callTarget.setInstalledCode(installedCode); } + /** + * Creates a log that {@linkplain HotSpotSpeculationLog#managesFailedSpeculations() manages} a + * native failed speculations list. An important invariant is that an nmethod compiled with this + * log can never be executing once the log object dies. When the log object dies, it frees the + * failed speculations list thus invalidating the + * {@linkplain HotSpotSpeculationLog#getFailedSpeculationsAddress() failed speculations address} + * embedded in the nmethod. If the nmethod were to execute after this point and fail a + * speculation, it would append the failed speculation to the already freed list. + *

+ * Truffle ensures this cannot happen as it only attaches managed speculation logs to + * {@link OptimizedCallTarget}s and {@link OptimizedOSRLoopNode}s. Executions of nmethods + * compiled for an {@link OptimizedCallTarget} or {@link OptimizedOSRLoopNode} object will have + * a strong reference to the object (i.e., as the receiver). This guarantees that such an + * nmethod cannot be executing after the object has died. + */ @Override public SpeculationLog createSpeculationLog() { return new HotSpotSpeculationLog(); diff --git a/compiler/src/org.graalvm.compiler.truffle.runtime.hotspot/src/org/graalvm/compiler/truffle/runtime/hotspot/HotSpotOptimizedCallTarget.java b/compiler/src/org.graalvm.compiler.truffle.runtime.hotspot/src/org/graalvm/compiler/truffle/runtime/hotspot/HotSpotOptimizedCallTarget.java index 870883b47620..464e276cae52 100644 --- a/compiler/src/org.graalvm.compiler.truffle.runtime.hotspot/src/org/graalvm/compiler/truffle/runtime/hotspot/HotSpotOptimizedCallTarget.java +++ b/compiler/src/org.graalvm.compiler.truffle.runtime.hotspot/src/org/graalvm/compiler/truffle/runtime/hotspot/HotSpotOptimizedCallTarget.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,6 +34,7 @@ import jdk.vm.ci.code.InstalledCode; import jdk.vm.ci.hotspot.HotSpotNmethod; +import jdk.vm.ci.meta.SpeculationLog; /** * A HotSpot specific {@link OptimizedCallTarget} whose machine code (if any) is represented by an @@ -116,4 +117,9 @@ public long getCodeAddress() { public void invalidate() { invalidate(null, null); } + + @Override + public SpeculationLog getCompilationSpeculationLog() { + return HotSpotTruffleRuntimeServices.getCompilationSpeculationLog(this); + } } diff --git a/compiler/src/org.graalvm.compiler.truffle.runtime.hotspot/src/org/graalvm/compiler/truffle/runtime/hotspot/HotSpotTruffleRuntimeServices.java b/compiler/src/org.graalvm.compiler.truffle.runtime.hotspot/src/org/graalvm/compiler/truffle/runtime/hotspot/HotSpotTruffleRuntimeServices.java new file mode 100644 index 000000000000..72ae1b9a14ec --- /dev/null +++ b/compiler/src/org.graalvm.compiler.truffle.runtime.hotspot/src/org/graalvm/compiler/truffle/runtime/hotspot/HotSpotTruffleRuntimeServices.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.graalvm.compiler.truffle.runtime.hotspot; + +import org.graalvm.compiler.truffle.runtime.OptimizedCallTarget; + +import jdk.vm.ci.meta.SpeculationLog; + +/** + * JDK versioned functionality used by the HotSpot Truffle runtime. + */ +class HotSpotTruffleRuntimeServices { + + private static InternalError shouldNotReachHere() { + throw new InternalError("JDK specific overlay missing"); + } + + /** + * Gets a speculation log to be used for compiling {@code callTarget}. + * + * @param callTarget + */ + public static SpeculationLog getCompilationSpeculationLog(OptimizedCallTarget callTarget) { + throw shouldNotReachHere(); + } +} diff --git a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/GraalTruffleRuntime.java b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/GraalTruffleRuntime.java index 7469fe621a31..834242f457da 100644 --- a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/GraalTruffleRuntime.java +++ b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/GraalTruffleRuntime.java @@ -27,12 +27,14 @@ import static org.graalvm.compiler.truffle.common.TruffleOutputGroup.GROUP_ID; import static org.graalvm.compiler.truffle.runtime.GraalTruffleRuntime.LazyFrameBoxingQuery.FrameBoxingClass; import static org.graalvm.compiler.truffle.runtime.GraalTruffleRuntime.LazyFrameBoxingQuery.FrameBoxingClassName; -import static org.graalvm.compiler.truffle.runtime.TruffleRuntimeOptions.getValue; import static org.graalvm.compiler.truffle.runtime.SharedTruffleRuntimeOptions.TruffleCompilation; import static org.graalvm.compiler.truffle.runtime.SharedTruffleRuntimeOptions.TruffleCompilationExceptionsAreThrown; import static org.graalvm.compiler.truffle.runtime.SharedTruffleRuntimeOptions.TruffleCompileOnly; import static org.graalvm.compiler.truffle.runtime.SharedTruffleRuntimeOptions.TruffleProfilingEnabled; import static org.graalvm.compiler.truffle.runtime.SharedTruffleRuntimeOptions.TruffleUseFrameWithoutBoxing; +import static org.graalvm.compiler.truffle.runtime.TruffleDebugOptions.PrintGraph; +import static org.graalvm.compiler.truffle.runtime.TruffleDebugOptions.PrintGraphTarget.Disable; +import static org.graalvm.compiler.truffle.runtime.TruffleRuntimeOptions.getValue; import java.io.CharArrayWriter; import java.io.PrintWriter; @@ -54,11 +56,13 @@ import org.graalvm.collections.EconomicMap; import org.graalvm.compiler.truffle.common.CompilableTruffleAST; import org.graalvm.compiler.truffle.common.OptimizedAssumptionDependency; +import org.graalvm.compiler.truffle.common.TruffleCompilation; import org.graalvm.compiler.truffle.common.TruffleCompilationTask; import org.graalvm.compiler.truffle.common.TruffleCompiler; import org.graalvm.compiler.truffle.common.TruffleCompilerRuntime; import org.graalvm.compiler.truffle.common.TruffleDebugContext; import org.graalvm.compiler.truffle.common.TruffleDebugJavaMethod; +import org.graalvm.compiler.truffle.common.TruffleOutputGroup; import org.graalvm.compiler.truffle.runtime.debug.StatisticsListener; import org.graalvm.compiler.truffle.runtime.debug.TraceASTCompilationListener; import org.graalvm.compiler.truffle.runtime.debug.TraceCallTreeListener; @@ -69,6 +73,7 @@ import org.graalvm.compiler.truffle.runtime.serviceprovider.TruffleRuntimeServices; import org.graalvm.options.OptionValues; +import com.oracle.truffle.api.ArrayUtils; import com.oracle.truffle.api.Assumption; import com.oracle.truffle.api.CallTarget; import com.oracle.truffle.api.CompilerAsserts; @@ -77,7 +82,6 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.CompilerOptions; import com.oracle.truffle.api.ExactMath; -import com.oracle.truffle.api.ArrayUtils; import com.oracle.truffle.api.RootCallTarget; import com.oracle.truffle.api.Truffle; import com.oracle.truffle.api.TruffleOptions; @@ -117,8 +121,6 @@ import jdk.vm.ci.meta.ResolvedJavaMethod; import jdk.vm.ci.meta.ResolvedJavaType; import jdk.vm.ci.meta.SpeculationLog; -import org.graalvm.compiler.truffle.common.TruffleCompilation; -import org.graalvm.compiler.truffle.common.TruffleOutputGroup; /** * Implementation of the Truffle runtime when running on top of Graal. @@ -684,7 +686,7 @@ protected void doCompile(TruffleDebugContext debug, TruffleCompilation compilati TruffleInlining inlining = createInliningPlan(callTarget, task); try (AutoCloseable s = debug.scope("Truffle", new TruffleDebugJavaMethod(callTarget))) { // Open the "Truffle::methodName" dump group if dumping is enabled. - try (TruffleOutputGroup o = TruffleOutputGroup.open(debug, callTarget, Collections.singletonMap(GROUP_ID, compilation))) { + try (TruffleOutputGroup o = TruffleDebugOptions.getValue(PrintGraph) == Disable ? null : TruffleOutputGroup.open(debug, callTarget, Collections.singletonMap(GROUP_ID, compilation))) { // Create "AST" and "Call Tree" groups if dumping is enabled. maybeDumpTruffleTree(debug, callTarget, inlining); // Compile the method (puts dumps in "Graal Graphs" group if dumping is enabled). @@ -752,6 +754,13 @@ public CancellableCompileTask submitForCompilation(OptimizedCallTarget optimized return queue.submitCompilationRequest(this, optimizedCallTarget, lastTierCompilation); } + @SuppressWarnings("all") + private static boolean assertionsEnabled() { + boolean enabled = false; + assert enabled = true; + return enabled; + } + public void finishCompilation(OptimizedCallTarget optimizedCallTarget, CancellableCompileTask task, boolean mayBeAsynchronous) { getListener().onCompilationQueued(optimizedCallTarget); @@ -762,7 +771,11 @@ public void finishCompilation(OptimizedCallTarget optimizedCallTarget, Cancellab if (TruffleRuntimeOptions.getValue(TruffleCompilationExceptionsAreThrown) && !(e.getCause() instanceof BailoutException && !((BailoutException) e.getCause()).isPermanent())) { throw new RuntimeException(e.getCause()); } else { - // silently ignored + if (assertionsEnabled()) { + e.printStackTrace(); + } else { + // silently ignored + } } } } diff --git a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/OptimizedCallTarget.java b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/OptimizedCallTarget.java index 1989df236bb9..762a401e5c6a 100644 --- a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/OptimizedCallTarget.java +++ b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/OptimizedCallTarget.java @@ -425,7 +425,11 @@ OptimizedCallTarget cloneUninitialized() { return (OptimizedCallTarget) runtime().createClonedCallTarget(this, clonedRoot); } - @Override + /** + * Gets the speculation log used to collect all failed speculations in the compiled code for + * this call target. Note that this may differ from the speculation log + * {@linkplain CompilableTruffleAST#getCompilationSpeculationLog() used for compilation}. + */ public synchronized SpeculationLog getSpeculationLog() { if (speculationLog == null) { speculationLog = ((GraalTruffleRuntime) Truffle.getRuntime()).createSpeculationLog(); diff --git a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/TruffleDebugOptions.java b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/TruffleDebugOptions.java index 1048cb1d7e69..a1250e49aade 100644 --- a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/TruffleDebugOptions.java +++ b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/TruffleDebugOptions.java @@ -24,6 +24,8 @@ */ package org.graalvm.compiler.truffle.runtime; +import static org.graalvm.compiler.truffle.runtime.TruffleDebugOptions.PrintGraphTarget.File; + import java.util.Map; import org.graalvm.collections.EconomicMap; @@ -32,6 +34,7 @@ import org.graalvm.options.OptionDescriptor; import org.graalvm.options.OptionDescriptors; import org.graalvm.options.OptionKey; +import org.graalvm.options.OptionType; import org.graalvm.options.OptionValues; import com.oracle.truffle.api.Option; @@ -54,6 +57,23 @@ static T getValue(final OptionKey key) { return key.getDefaultValue(); } + /** + * Shadows {@code org.graalvm.compiler.debug.DebugOptions.PrintGraphTarget}. + */ + public enum PrintGraphTarget { + File, + Network, + Disable; + + static PrintGraphTarget translate(Object value) { + return valueOf(String.valueOf(value)); + } + + static OptionType getOptionType() { + return new OptionType<>(PrintGraphTarget.class.getSimpleName(), File, PrintGraphTarget::valueOf); + } + } + static OptionValues getOptions() { OptionValuesImpl result = optionValues; if (result == null) { @@ -63,7 +83,11 @@ static OptionValues getOptions() { final OptionDescriptor descriptor = descriptors.get(e.getKey()); final OptionKey k = descriptor != null ? descriptor.getKey() : null; if (k != null) { - valuesMap.put(k, e.getValue()); + if (e.getKey().equals("PrintGraph")) { + valuesMap.put(k, PrintGraphTarget.translate(e.getValue())); + } else { + valuesMap.put(k, e.getValue()); + } } } result = new OptionValuesImpl(descriptors, valuesMap); @@ -73,6 +97,6 @@ static OptionValues getOptions() { } // Initialized by the options of the same name in org.graalvm.compiler.debug.DebugOptions - @Option(help = "", category = OptionCategory.DEBUG) public static final OptionKey PrintGraph = new OptionKey<>(true); - @Option(help = "", category = OptionCategory.DEBUG) public static final OptionKey PrintTruffleTrees = new OptionKey<>(true); + @Option(help = "", category = OptionCategory.INTERNAL) public static final OptionKey PrintGraph = new OptionKey<>(File, PrintGraphTarget.getOptionType()); + @Option(help = "", category = OptionCategory.INTERNAL) public static final OptionKey PrintTruffleTrees = new OptionKey<>(true); } diff --git a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/TruffleTreeDumper.java b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/TruffleTreeDumper.java index e4d16cf3b8a1..f9b205615702 100644 --- a/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/TruffleTreeDumper.java +++ b/compiler/src/org.graalvm.compiler.truffle.runtime/src/org/graalvm/compiler/truffle/runtime/TruffleTreeDumper.java @@ -24,6 +24,11 @@ */ package org.graalvm.compiler.truffle.runtime; +import static org.graalvm.compiler.truffle.runtime.TruffleDebugOptions.PrintGraph; +import static org.graalvm.compiler.truffle.runtime.TruffleDebugOptions.PrintTruffleTrees; +import static org.graalvm.compiler.truffle.runtime.TruffleDebugOptions.getValue; +import static org.graalvm.compiler.truffle.runtime.TruffleDebugOptions.PrintGraphTarget.Disable; + import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; @@ -60,7 +65,7 @@ private TruffleTreeDumper() { private static final String AFTER_INLINING = "After Inlining"; public static void dump(TruffleDebugContext debug, OptimizedCallTarget callTarget, TruffleInlining inliningDecision) { - if (TruffleDebugOptions.getValue(TruffleDebugOptions.PrintGraph) && TruffleDebugOptions.getValue(TruffleDebugOptions.PrintTruffleTrees)) { + if (getValue(PrintGraph) != Disable && getValue(PrintTruffleTrees)) { try { dumpASTAndCallTrees(debug, callTarget, inliningDecision); } catch (IOException ex) { diff --git a/compiler/src/org.graalvm.compiler.truffle.test/src/org/graalvm/compiler/truffle/test/LazyInitializationTest.java b/compiler/src/org.graalvm.compiler.truffle.test/src/org/graalvm/compiler/truffle/test/LazyInitializationTest.java index 03799fbdd086..82f5967367d3 100644 --- a/compiler/src/org.graalvm.compiler.truffle.test/src/org/graalvm/compiler/truffle/test/LazyInitializationTest.java +++ b/compiler/src/org.graalvm.compiler.truffle.test/src/org/graalvm/compiler/truffle/test/LazyInitializationTest.java @@ -24,7 +24,7 @@ */ package org.graalvm.compiler.truffle.test; -import static org.graalvm.compiler.serviceprovider.GraalServices.Java8OrEarlier; +import static org.graalvm.compiler.serviceprovider.JavaVersionUtil.Java8OrEarlier; import static org.graalvm.compiler.test.SubprocessUtil.getVMCommandLine; import static org.graalvm.compiler.test.SubprocessUtil.withoutDebuggerArguments; diff --git a/compiler/src/org.graalvm.compiler.truffle.test/src/org/graalvm/compiler/truffle/test/PartialEvaluationTest.java b/compiler/src/org.graalvm.compiler.truffle.test/src/org/graalvm/compiler/truffle/test/PartialEvaluationTest.java index 3a39606116d6..419c2a1a58dd 100644 --- a/compiler/src/org.graalvm.compiler.truffle.test/src/org/graalvm/compiler/truffle/test/PartialEvaluationTest.java +++ b/compiler/src/org.graalvm.compiler.truffle.test/src/org/graalvm/compiler/truffle/test/PartialEvaluationTest.java @@ -148,7 +148,7 @@ protected StructuredGraph partialEval(OptimizedCallTarget compilable, Object[] a DebugContext debug = getDebugContext(options); try (DebugContext.Scope s = debug.scope("TruffleCompilation", new TruffleDebugJavaMethod(compilable))) { TruffleInlining inliningDecision = new TruffleInlining(compilable, new DefaultInliningPolicy()); - SpeculationLog speculationLog = compilable.getSpeculationLog(); + SpeculationLog speculationLog = compilable.getCompilationSpeculationLog(); return truffleCompiler.getPartialEvaluator().createGraph(debug, compilable, inliningDecision, allowAssumptions, compilationId, speculationLog, null); } catch (Throwable e) { throw debug.handle(e); diff --git a/compiler/src/org.graalvm.compiler.truffle.test/src/org/graalvm/compiler/truffle/test/TruffleRuntimeTest.java b/compiler/src/org.graalvm.compiler.truffle.test/src/org/graalvm/compiler/truffle/test/TruffleRuntimeTest.java index b94a2dd0568f..6aa423a30a0b 100644 --- a/compiler/src/org.graalvm.compiler.truffle.test/src/org/graalvm/compiler/truffle/test/TruffleRuntimeTest.java +++ b/compiler/src/org.graalvm.compiler.truffle.test/src/org/graalvm/compiler/truffle/test/TruffleRuntimeTest.java @@ -31,7 +31,7 @@ import org.graalvm.compiler.api.test.Graal; import org.graalvm.compiler.runtime.RuntimeProvider; -import org.graalvm.compiler.serviceprovider.GraalServices; +import org.graalvm.compiler.serviceprovider.JavaVersionUtil; import org.junit.Test; import com.oracle.truffle.api.Truffle; @@ -83,7 +83,7 @@ public void testGetLayoutFactory() { LayoutFactory layoutFactory = runtime.getCapability(LayoutFactory.class); assertNotNull("LayoutFactory not found", layoutFactory); - boolean java8OrEarlier = GraalServices.Java8OrEarlier; + boolean java8OrEarlier = JavaVersionUtil.Java8OrEarlier; ClassLoader layoutFactoryCL = layoutFactory.getClass().getClassLoader(); if (java8OrEarlier) { // Bootstrap class loader or JVMCI class loader diff --git a/examples/mx.examples/suite.py b/examples/mx.examples/suite.py index 2e3c038b7ad5..2a18596c4add 100644 --- a/examples/mx.examples/suite.py +++ b/examples/mx.examples/suite.py @@ -1,5 +1,5 @@ suite = { - "mxversion" : "5.90.02", + "mxversion" : "5.210.2", "name" : "examples", "url" : "https://github.com/graalvm/graal", "developer" : { diff --git a/regex/mx.regex/suite.py b/regex/mx.regex/suite.py index 9dfabac2b9c7..5cb5dab8f5dc 100644 --- a/regex/mx.regex/suite.py +++ b/regex/mx.regex/suite.py @@ -1,5 +1,5 @@ suite = { - "mxversion" : "5.176.0", + "mxversion" : "5.210.2", "name" : "regex", @@ -51,6 +51,7 @@ "checkstyleVersion" : "8.8", "javaCompliance" : "8+", "workingSets" : "Truffle,Regex", + "spotbugsIgnoresGenerated" : True, }, "com.oracle.truffle.regex.test" : { diff --git a/sdk/CHANGELOG.md b/sdk/CHANGELOG.md index 4ca7cc510a5f..eb730fead2dc 100644 --- a/sdk/CHANGELOG.md +++ b/sdk/CHANGELOG.md @@ -2,6 +2,11 @@ This changelog summarizes major changes between Graal SDK versions. The main focus is on APIs exported by Graal SDK. +## Version 1.0.0 RC13 +* [OptionCategory.DEBUG](https://www.graalvm.org/truffle/javadoc/org/graalvm/options/OptionCategory.html) has been renamed to `OptionCategory.INTERNAL` for clarity. +* Added `"static"` member to class objects that provides access to the class's static members. +* [OptionStability](https://www.graalvm.org/truffle/javadoc/org/graalvm/options/OptionStability.html) has been added for specifying the stability of an option. + ## Version 1.0 RC11 * Added [SourceSection.hasLines()](http://www.graalvm.org/sdk/javadoc/org/graalvm/polyglot/SourceSection.html#hasLines--), [SourceSection.hasColumns()](http://www.graalvm.org/sdk/javadoc/org/graalvm/polyglot/SourceSection.html#hasColumns--) and [SourceSection.hasCharIndex()](http://www.graalvm.org/sdk/javadoc/org/graalvm/polyglot/SourceSection.html#hasCharIndex--) to distinguish which positions are defined and which are not. * Added [FileSystem.getSeparator()](http://www.graalvm.org/sdk/javadoc/org/graalvm/polyglot/io/FileSystem.html#getSeparator--) to remove a dependency on NIO `FileSystem` for custom `Path` implementations. @@ -28,19 +33,19 @@ This changelog summarizes major changes between Graal SDK versions. The main foc * Byte based sources may be constructed using a `ByteSequence` or from a `File` or `URL`. Whether sources are interpreted as character or byte based sources depends on the specified language. * `Source.hasBytes()` and `Source.hasCharacters()` may be used to find out whether a source is character or byte based. * Byte based sources throw an `UnsupportedOperationException` if methods that access characters, line numbers or column numbers. - * Added `Source.getBytes()` to access the contents of byte based sources. -* Added support for MIME types to sources: + * Added `Source.getBytes()` to access the contents of byte based sources. +* Added support for MIME types to sources: * MIME types can now be assigned using `Source.Builder.mimeType(String)` to sources in addition to the target language. * The MIME type of a source allows languages support different kinds of input. * `Language` instances allow access to the default and supported MIME types using `Language.getMimeTypes()` and `Language.getDefaultMimeType()`. - * MIME types are automatically detected if the source is constructed from a `File` or `URL` if it is not specified explicitly. + * MIME types are automatically detected if the source is constructed from a `File` or `URL` if it is not specified explicitly. * Deprecated `Source.getInputStream()`. Use `Source.getCharacters()` or `Source.getBytes()` instead. * Context methods now consistently throw `IllegalArgumentException` instead of `IllegalStateException` for unsupported sources or missing / inaccessible languages. * Added `Engine.findHome()` to find the GraalVM home folder. ## Version 1.0 RC5 * `PolyglotException.getGuestObject()` now returns `null` to indicate that no exception object is available instead of returning a `Value` instance that returns `true` for `isNull()`. -* Added new [execution listener](http://www.graalvm.org/sdk/javadoc/org/graalvm/polyglot/management/ExecutionListener.html) API that allows for simple, efficient and fine grained introspection of executed code. +* Added new [execution listener](http://www.graalvm.org/sdk/javadoc/org/graalvm/polyglot/management/ExecutionListener.html) API that allows for simple, efficient and fine grained introspection of executed code. ## Version 1.0 RC3 diff --git a/sdk/mx.sdk/mx_sdk.py b/sdk/mx.sdk/mx_sdk.py index ec57d711c0ca..a8a004b8a054 100644 --- a/sdk/mx.sdk/mx_sdk.py +++ b/sdk/mx.sdk/mx_sdk.py @@ -110,6 +110,12 @@ def __init__(self, destination, jar_distributions, build_args, links=None): assert isinstance(self.jar_distributions, list) assert isinstance(self.build_args, list) + def __str__(self): + return self.destination + + def __repr__(self): + return str(self) + class LauncherConfig(AbstractNativeImageConfig): def __init__(self, destination, jar_distributions, main_class, build_args, links=None, is_main_launcher=True): @@ -126,13 +132,18 @@ class LanguageLauncherConfig(LauncherConfig): class LibraryConfig(AbstractNativeImageConfig): - pass + def __init__(self, destination, jar_distributions, build_args, links=None, jvm_library=False): + """ + :type jvm_library: bool + """ + super(LibraryConfig, self).__init__(destination, jar_distributions, build_args, links=links) + self.jvm_library = jvm_library class GraalVmComponent(object): def __init__(self, suite, name, short_name, license_files, third_party_license_files, jar_distributions=None, builder_jar_distributions=None, support_distributions=None, - dir_name=None, launcher_configs=None, provided_executables=None, + dir_name=None, launcher_configs=None, library_configs=None, provided_executables=None, polyglot_lib_build_args=None, polyglot_lib_jar_dependencies=None, polyglot_lib_build_dependencies=None, has_polyglot_lib_entrypoints=False, boot_jars=None, priority=None): @@ -140,7 +151,7 @@ def __init__(self, suite, name, short_name, license_files, third_party_license_f :param suite mx.Suite: the suite this component belongs to :type name: str :param str short_name: a short, unique name for this component - :param str | None dir_name: the directory name in which this component lives. If `None`, the `short_name` is used. + :param str | None | False dir_name: the directory name in which this component lives. If `None`, the `short_name` is used. If `False`, files are copied to the root-dir for the component type. :type license_files: list[str] :type third_party_license_files: list[str] :type provided_executables: list[str] @@ -150,6 +161,7 @@ def __init__(self, suite, name, short_name, license_files, third_party_license_f :type has_polyglot_lib_entrypoints: bool :type boot_jars: list[str] :type launcher_configs: list[LauncherConfig] + :type library_configs: list[LibraryConfig] :type jar_distributions: list[str] :type builder_jar_distributions: list[str] :type support_distributions: list[str] @@ -158,7 +170,7 @@ def __init__(self, suite, name, short_name, license_files, third_party_license_f self.suite = suite self.name = name self.short_name = short_name - self.dir_name = dir_name or short_name + self.dir_name = dir_name if dir_name is not None else short_name self.license_files = license_files self.third_party_license_files = third_party_license_files self.provided_executables = provided_executables or [] @@ -173,6 +185,7 @@ def __init__(self, suite, name, short_name, license_files, third_party_license_f self.priority = priority or 0 """ priority with a higher value means higher priority """ self.launcher_configs = launcher_configs or [] + self.library_configs = library_configs or [] assert isinstance(self.jar_distributions, list) assert isinstance(self.builder_jar_distributions, list) @@ -185,6 +198,7 @@ def __init__(self, suite, name, short_name, license_files, third_party_license_f assert isinstance(self.polyglot_lib_build_dependencies, list) assert isinstance(self.boot_jars, list) assert isinstance(self.launcher_configs, list) + assert isinstance(self.library_configs, list) def __str__(self): return "{} ({})".format(self.name, self.dir_name) @@ -193,20 +207,22 @@ def __str__(self): class GraalVmTruffleComponent(GraalVmComponent): def __init__(self, suite, name, short_name, license_files, third_party_license_files, truffle_jars, builder_jar_distributions=None, support_distributions=None, dir_name=None, launcher_configs=None, - provided_executables=None, polyglot_lib_build_args=None, polyglot_lib_jar_dependencies=None, - polyglot_lib_build_dependencies=None, has_polyglot_lib_entrypoints=False, boot_jars=None, - include_in_polyglot=True, priority=None, post_install_msg=None): + library_configs=None, provided_executables=None, polyglot_lib_build_args=None, + polyglot_lib_jar_dependencies=None, polyglot_lib_build_dependencies=None, + has_polyglot_lib_entrypoints=False, boot_jars=None, include_in_polyglot=True, priority=None, + post_install_msg=None): """ :param truffle_jars list[str]: JAR distributions that should be on the classpath for the language implementation. :param bool include_in_polyglot: whether this component is included in `--language:all` or `--tool:all` and should be part of polyglot images. :param post_install_msg: Post-installation message to be printed :type post_install_msg: str """ - super(GraalVmTruffleComponent, self).__init__(suite, name, short_name, license_files, - third_party_license_files, truffle_jars, builder_jar_distributions, support_distributions, dir_name, launcher_configs, provided_executables, - polyglot_lib_build_args, polyglot_lib_jar_dependencies, polyglot_lib_build_dependencies, - has_polyglot_lib_entrypoints, boot_jars, - priority) + super(GraalVmTruffleComponent, self).__init__(suite, name, short_name, license_files, third_party_license_files, + truffle_jars, builder_jar_distributions, support_distributions, + dir_name, launcher_configs, library_configs, provided_executables, + polyglot_lib_build_args, polyglot_lib_jar_dependencies, + polyglot_lib_build_dependencies, has_polyglot_lib_entrypoints, + boot_jars, priority) self.include_in_polyglot = include_in_polyglot self.post_install_msg = post_install_msg assert isinstance(self.include_in_polyglot, bool) @@ -219,14 +235,22 @@ class GraalVmLanguage(GraalVmTruffleComponent): class GraalVmTool(GraalVmTruffleComponent): def __init__(self, suite, name, short_name, license_files, third_party_license_files, truffle_jars, builder_jar_distributions=None, support_distributions=None, dir_name=None, launcher_configs=None, - provided_executables=None, polyglot_lib_build_args=None, polyglot_lib_jar_dependencies=None, - polyglot_lib_build_dependencies=None, has_polyglot_lib_entrypoints=False, boot_jars=None, - include_in_polyglot=True, include_by_default=False, priority=None): - super(GraalVmTool, self).__init__(suite, name, short_name, license_files, third_party_license_files, + library_configs=None, provided_executables=None, polyglot_lib_build_args=None, + polyglot_lib_jar_dependencies=None, polyglot_lib_build_dependencies=None, + has_polyglot_lib_entrypoints=False, boot_jars=None, include_in_polyglot=True, include_by_default=False, + priority=None): + super(GraalVmTool, self).__init__(suite, + name, + short_name, + license_files, + third_party_license_files, truffle_jars, builder_jar_distributions, - support_distributions, dir_name, - launcher_configs, provided_executables, + support_distributions, + dir_name, + launcher_configs, + library_configs, + provided_executables, polyglot_lib_build_args, polyglot_lib_jar_dependencies, polyglot_lib_build_dependencies, @@ -246,20 +270,27 @@ class GraalVmJreComponent(GraalVmComponent): class GraalVmJvmciComponent(GraalVmJreComponent): - def __init__(self, suite, name, short_name, license_files, third_party_license_files, jvmci_jars, jar_distributions=None, builder_jar_distributions=None, support_distributions=None, - graal_compiler=None, dir_name=None, launcher_configs=None, provided_executables=None, polyglot_lib_build_args=None, polyglot_lib_jar_dependencies=None, - polyglot_lib_build_dependencies=None, has_polyglot_lib_entrypoints=False, boot_jars=None, priority=None): + def __init__(self, suite, name, short_name, license_files, third_party_license_files, jvmci_jars, + jar_distributions=None, builder_jar_distributions=None, support_distributions=None, + graal_compiler=None, dir_name=None, launcher_configs=None, library_configs=None, + provided_executables=None, polyglot_lib_build_args=None, polyglot_lib_jar_dependencies=None, + polyglot_lib_build_dependencies=None, has_polyglot_lib_entrypoints=False, boot_jars=None, + priority=None): """ :type jvmci_jars: list[str] :type graal_compiler: str """ - super(GraalVmJvmciComponent, self).__init__(suite, name, short_name, license_files, + super(GraalVmJvmciComponent, self).__init__(suite, + name, + short_name, + license_files, third_party_license_files, jar_distributions, builder_jar_distributions, support_distributions, dir_name, launcher_configs, + library_configs, provided_executables, polyglot_lib_build_args, polyglot_lib_jar_dependencies, diff --git a/sdk/mx.sdk/suite.py b/sdk/mx.sdk/suite.py index b738b458ed60..36420f5c4ca9 100644 --- a/sdk/mx.sdk/suite.py +++ b/sdk/mx.sdk/suite.py @@ -39,7 +39,7 @@ # SOFTWARE. # suite = { - "mxversion" : "5.183.0", + "mxversion" : "5.210.2", "name" : "sdk", "version" : "1.0.0-rc13", "release" : False, diff --git a/sdk/src/org.graalvm.launcher/src/org/graalvm/launcher/DarwinNativeInterface.java b/sdk/src/org.graalvm.launcher/src/org/graalvm/launcher/DarwinNativeInterface.java index 4591895ab927..46c1fdc897e7 100644 --- a/sdk/src/org.graalvm.launcher/src/org/graalvm/launcher/DarwinNativeInterface.java +++ b/sdk/src/org.graalvm.launcher/src/org/graalvm/launcher/DarwinNativeInterface.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -45,7 +45,7 @@ import org.graalvm.nativeimage.c.function.CFunction; import org.graalvm.nativeimage.c.type.CIntPointer; -@Platforms(Platform.DARWIN.class) +@Platforms(Platform.DARWIN_AND_JNI.class) class DarwinNativeInterface { public static int errno() { diff --git a/sdk/src/org.graalvm.launcher/src/org/graalvm/launcher/Launcher.java b/sdk/src/org.graalvm.launcher/src/org/graalvm/launcher/Launcher.java index 145973051611..9768b40c7c02 100644 --- a/sdk/src/org.graalvm.launcher/src/org/graalvm/launcher/Launcher.java +++ b/sdk/src/org.graalvm.launcher/src/org/graalvm/launcher/Launcher.java @@ -101,7 +101,7 @@ public enum VMType { private final boolean verbose; private boolean help; - private boolean helpDebug; + private boolean helpInternal; private boolean helpExpert; private boolean helpTools; private boolean helpLanguages; @@ -453,7 +453,7 @@ protected boolean isGraalVMAvailable() { @SuppressWarnings("fallthrough") final boolean runPolyglotAction() { - OptionCategory helpCategory = helpDebug ? OptionCategory.DEBUG : (helpExpert ? OptionCategory.EXPERT : OptionCategory.USER); + OptionCategory helpCategory = helpInternal ? OptionCategory.INTERNAL : (helpExpert ? OptionCategory.EXPERT : OptionCategory.USER); switch (versionAction) { case PrintAndContinue: @@ -465,7 +465,7 @@ final boolean runPolyglotAction() { printPolyglotVersions(); return true; } - boolean printDefaultHelp = help || ((helpExpert || helpDebug) && !helpTools && !helpLanguages); + boolean printDefaultHelp = help || ((helpExpert || helpInternal) && !helpTools && !helpLanguages); if (printDefaultHelp) { printHelp(helpCategory); // @formatter:off @@ -484,8 +484,8 @@ final boolean runPolyglotAction() { printOption("--help:languages", "Print options for all installed languages."); printOption("--help:tools", "Print options for all installed tools."); printOption("--help:expert", "Print additional options for experts."); - if (helpExpert || helpDebug) { - printOption("--help:debug", "Print additional options for debugging."); + if (helpExpert || helpInternal) { + printOption("--help:internal", "Print internal options for debugging language implementations and instruments."); } printOption("--version:graalvm", "Print GraalVM version information and exit."); printOption("--show-version:graalvm", "Print GraalVM version information and continue execution."); @@ -497,7 +497,7 @@ final boolean runPolyglotAction() { if (!descriptor.getName().startsWith("engine.") && !descriptor.getName().startsWith("compiler.")) { continue; } - if (!descriptor.isDeprecated() && descriptor.getCategory().ordinal() == helpCategory.ordinal()) { + if (!descriptor.isDeprecated() && sameCategory(descriptor, helpCategory)) { engineOptions.add(asPrintableOption(descriptor)); } } @@ -528,7 +528,7 @@ private static void printInstrumentOptions(Engine engine, OptionCategory optionC for (Instrument instrument : instruments) { List options = new ArrayList<>(); for (OptionDescriptor descriptor : instrument.getOptions()) { - if (!descriptor.isDeprecated() && descriptor.getCategory().ordinal() == optionCategory.ordinal()) { + if (!descriptor.isDeprecated() && sameCategory(descriptor, optionCategory)) { options.add(asPrintableOption(descriptor)); } } @@ -554,7 +554,7 @@ private static void printLanguageOptions(Engine engine, OptionCategory optionCat for (Language language : languages) { List options = new ArrayList<>(); for (OptionDescriptor descriptor : language.getOptions()) { - if (!descriptor.isDeprecated() && descriptor.getCategory().ordinal() == optionCategory.ordinal()) { + if (!descriptor.isDeprecated() && sameCategory(descriptor, optionCategory)) { options.add(asPrintableOption(descriptor)); } } @@ -574,13 +574,24 @@ private static void printLanguageOptions(Engine engine, OptionCategory optionCat } } + @SuppressWarnings("deprecation") + private static boolean sameCategory(OptionDescriptor descriptor, OptionCategory optionCategory) { + return descriptor.getCategory().ordinal() == optionCategory.ordinal() || + (optionCategory.ordinal() == OptionCategory.INTERNAL.ordinal() && + descriptor.getCategory().ordinal() == OptionCategory.DEBUG.ordinal()); + } + boolean parsePolyglotOption(String defaultOptionPrefix, Map options, String arg) { switch (arg) { case "--help": help = true; return true; case "--help:debug": - helpDebug = true; + System.err.println("Warning: --help:debug is deprecated, use --help:internal instead."); + helpInternal = true; + return true; + case "--help:internal": + helpInternal = true; return true; case "--help:expert": helpExpert = true; @@ -710,8 +721,8 @@ private Set collectAllArguments() { options.add("--help:expert"); options.add("--version:graalvm"); options.add("--show-version:graalvm"); - if (helpExpert || helpDebug) { - options.add("--help:debug"); + if (helpExpert || helpInternal) { + options.add("--help:internal"); } addOptions(engine.getOptions(), options); for (Language language : engine.getLanguages().values()) { @@ -781,7 +792,7 @@ static List sortedInstruments(Engine engine) { } static void printOption(OptionCategory optionCategory, OptionDescriptor descriptor) { - if (!descriptor.isDeprecated() && descriptor.getCategory().ordinal() == optionCategory.ordinal()) { + if (!descriptor.isDeprecated() && sameCategory(descriptor, optionCategory)) { printOption(asPrintableOption(descriptor)); } } @@ -1333,7 +1344,11 @@ private void exec(Path executable, List command) { } String[] argv = new String[command.size() + 1]; int i = 0; - argv[i++] = executable.getFileName().toString(); + Path filename = executable.getFileName(); + if (filename == null) { + throw abort(String.format("Cannot determine execute filename from path %s", filename)); + } + argv[i++] = filename.toString(); for (String arg : command) { argv[i++] = arg; } diff --git a/sdk/src/org.graalvm.launcher/src/org/graalvm/launcher/LinuxNativeInterface.java b/sdk/src/org.graalvm.launcher/src/org/graalvm/launcher/LinuxNativeInterface.java index 48878ee88740..fef156483abe 100644 --- a/sdk/src/org.graalvm.launcher/src/org/graalvm/launcher/LinuxNativeInterface.java +++ b/sdk/src/org.graalvm.launcher/src/org/graalvm/launcher/LinuxNativeInterface.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -45,7 +45,7 @@ import org.graalvm.nativeimage.c.function.CFunction; import org.graalvm.nativeimage.c.type.CIntPointer; -@Platforms(Platform.LINUX.class) +@Platforms(Platform.LINUX_AND_JNI.class) class LinuxNativeInterface { public static int errno() { diff --git a/sdk/src/org.graalvm.launcher/src/org/graalvm/launcher/NativeInterface.java b/sdk/src/org.graalvm.launcher/src/org/graalvm/launcher/NativeInterface.java index da8bb40de291..cb90dd34a9e8 100644 --- a/sdk/src/org.graalvm.launcher/src/org/graalvm/launcher/NativeInterface.java +++ b/sdk/src/org.graalvm.launcher/src/org/graalvm/launcher/NativeInterface.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -66,9 +66,9 @@ public List getMacroDefinitions() { } public static int errno() { - if (Platform.includedIn(Platform.LINUX.class)) { + if (Platform.includedIn(Platform.LINUX_AND_JNI.class)) { return LinuxNativeInterface.errno(); - } else if (Platform.includedIn(Platform.DARWIN.class)) { + } else if (Platform.includedIn(Platform.DARWIN_AND_JNI.class)) { return DarwinNativeInterface.errno(); } else { // TODO: this should fail at image generation time diff --git a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/IsolateThread.java b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/IsolateThread.java index 895758c7c8dc..afb984ecad1b 100644 --- a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/IsolateThread.java +++ b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/IsolateThread.java @@ -46,6 +46,9 @@ /** * Pointer to the runtime data structure for a thread. The size and actual layout of the data * structure is unspecified, client code must not make any assumptions about it. + *

+ * The {@link IsolateThread} points to a thread-local data structure. Therefore, the pointer must + * not be shared between threads. * * @since 1.0 */ diff --git a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/Platform.java b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/Platform.java index a961b865a936..16c328f95193 100644 --- a/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/Platform.java +++ b/sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/Platform.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -91,6 +91,40 @@ interface LINUX extends Platform { interface DARWIN extends Platform { } + /** + * Supported operating system: Linux platform that uses JNI based native JDK libraries. + * + * @since 1.0 + */ + interface LINUX_JNI extends Platform { + } + + /** + * Supported operating system: Darwin (MacOS) platform that uses JNI based native JDK libraries. + * + * @since 1.0 + */ + interface DARWIN_JNI extends Platform { + } + + /** + * Temporary platform used to mark classes or methods that are used for LINUX and LINUX_JNI + * platforms. + * + * @since 1.0 + */ + interface LINUX_AND_JNI extends Platform { + } + + /** + * Temporary platform used to mark classes or methods that are used for DARWIN (MacOS) and + * DARWIN_JNI platforms. + * + * @since 1.0 + */ + interface DARWIN_AND_JNI extends Platform { + } + /** * Supported operating system: Windows. * @@ -108,7 +142,7 @@ interface WINDOWS extends Platform { * * @since 1.0 */ - class LINUX_AMD64 implements LINUX, AMD64 { + class LINUX_AMD64 implements LINUX, LINUX_AND_JNI, AMD64 { /** * Instantiates a marker instance of this platform. @@ -124,7 +158,7 @@ public LINUX_AMD64() { * * @since 1.0 */ - class DARWIN_AMD64 implements DARWIN, AMD64 { + class DARWIN_AMD64 implements DARWIN, DARWIN_AND_JNI, AMD64 { /** * Instantiates a marker instance of this platform. @@ -135,6 +169,40 @@ public DARWIN_AMD64() { } } + /** + * Temporary leaf platform that is used to mark classes or methods that are used for LINUX_JNI + * platforms. + * + * @since 1.0 + */ + class LINUX_JNI_AMD64 implements LINUX_JNI, LINUX_AND_JNI, AMD64 { + + /** + * Instantiates a marker instance of this platform. + * + * @since 1.0 + */ + public LINUX_JNI_AMD64() { + } + } + + /** + * Temporary leaf platform that is used to mark classes or methods that are used for DARWIN_JNI + * platforms. + * + * @since 1.0 + */ + class DARWIN_JNI_AMD64 implements DARWIN_JNI, DARWIN_AND_JNI, AMD64 { + + /** + * Instantiates a marker instance of this platform. + * + * @since 1.0 + */ + public DARWIN_JNI_AMD64() { + } + } + /** * Supported leaf platform: Windows on x86 64-bit. * diff --git a/sdk/src/org.graalvm.options/src/org/graalvm/options/OptionCategory.java b/sdk/src/org.graalvm.options/src/org/graalvm/options/OptionCategory.java index 70fe6a1cb6df..47c5350dc400 100644 --- a/sdk/src/org.graalvm.options/src/org/graalvm/options/OptionCategory.java +++ b/sdk/src/org.graalvm.options/src/org/graalvm/options/OptionCategory.java @@ -64,8 +64,16 @@ public enum OptionCategory { /** * An option only relevant when debugging language or instrument implementations. * + * @deprecated Use {@link OptionCategory#INTERNAL} instead. * @since 1.0 */ - DEBUG + @Deprecated DEBUG, + + /** + * An option only relevant when debugging a language implementation or an instrument. + * + * @since 1.0 + */ + INTERNAL } diff --git a/sdk/src/org.graalvm.options/src/org/graalvm/options/OptionDescriptor.java b/sdk/src/org.graalvm.options/src/org/graalvm/options/OptionDescriptor.java index 701369f9a9a1..0d05e345c4e2 100644 --- a/sdk/src/org.graalvm.options/src/org/graalvm/options/OptionDescriptor.java +++ b/sdk/src/org.graalvm.options/src/org/graalvm/options/OptionDescriptor.java @@ -53,13 +53,15 @@ public final class OptionDescriptor { private final String name; private final String help; private final OptionCategory kind; + private final OptionStability stability; private final boolean deprecated; - OptionDescriptor(OptionKey key, String name, String help, OptionCategory kind, boolean deprecated) { + OptionDescriptor(OptionKey key, String name, String help, OptionCategory kind, OptionStability stability, boolean deprecated) { this.key = key; this.name = name; this.help = help; this.kind = kind; + this.stability = stability; this.deprecated = deprecated; } @@ -100,6 +102,15 @@ public OptionCategory getCategory() { return kind; } + /** + * Returns the stability of this option. + * + * @since 1.0 + */ + public OptionStability getStability() { + return stability; + } + /** * Returns a human-readable description on how to use the option. * @@ -170,7 +181,7 @@ public static Builder newBuilder(OptionKey key, String name) { return EMPTY.new Builder(key, name); } - private static final OptionDescriptor EMPTY = new OptionDescriptor(null, null, null, null, false); + private static final OptionDescriptor EMPTY = new OptionDescriptor(null, null, null, null, null, false); /** * Represents an option descriptor builder. @@ -181,9 +192,10 @@ public final class Builder { private final OptionKey key; private final String name; - private boolean deprecated; - private OptionCategory category; - private String help; + private boolean deprecated = false; + private OptionCategory category = OptionCategory.INTERNAL; + private OptionStability stability = OptionStability.EXPERIMENTAL; + private String help = ""; Builder(OptionKey key, String name) { this.key = key; @@ -192,7 +204,7 @@ public final class Builder { /** * Defines the user category for this option. The default value is - * {@link OptionCategory#DEBUG}. + * {@link OptionCategory#INTERNAL}. * * @since 1.0 */ @@ -202,6 +214,18 @@ public Builder category(@SuppressWarnings("hiding") OptionCategory category) { return this; } + /** + * Defines the stability of this option. The default value is + * {@link OptionStability#EXPERIMENTAL}. + * + * @since 1.0 + */ + public Builder stability(@SuppressWarnings("hiding") OptionStability stability) { + Objects.requireNonNull(stability); + this.stability = stability; + return this; + } + /** * Defines if this option is deprecated. The default value for deprecated is * false. This can be used to evolve options between releases. @@ -230,7 +254,7 @@ public Builder help(@SuppressWarnings("hiding") String help) { * @since 1.0 */ public OptionDescriptor build() { - return new OptionDescriptor(key, name, help == null ? "" : help, category == null ? OptionCategory.DEBUG : category, deprecated); + return new OptionDescriptor(key, name, help, category, stability, deprecated); } } diff --git a/sdk/src/org.graalvm.options/src/org/graalvm/options/OptionStability.java b/sdk/src/org.graalvm.options/src/org/graalvm/options/OptionStability.java new file mode 100644 index 000000000000..b4ca2cc0f6f6 --- /dev/null +++ b/sdk/src/org.graalvm.options/src/org/graalvm/options/OptionStability.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package org.graalvm.options; + +/** + * Categorizes options according to their stability. + * + * @since 1.0 + */ +public enum OptionStability { + + /** + * A stable option is expected to remain available for many releases. End users can rely on such + * an option being present. A stable option can still be removed but will go through a clear + * deprecating process before being removed. + * + * @since 1.0 + */ + STABLE, + + /** + * An experimental option has no guarantees of stability and might be removed at any point. + * + * @since 1.0 + */ + EXPERIMENTAL + +} diff --git a/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/Context.java b/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/Context.java index ca609712f6d6..bf79a6006bd2 100644 --- a/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/Context.java +++ b/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/Context.java @@ -478,6 +478,10 @@ public boolean initialize(String languageId) { * provided as {@link Value#getMember(String) members}. Methods and fields are grouped by name, * so only one member is exposed for each name. *

+ * {@link Class} objects have a member named {@code static} referring to the class's companion + * object containing the static methods of the class. Likewise, the companion object has a + * member named {@code class} that points back to the class object. + *

* When an argument value needs to be mapped to match a required Java method parameter type, * then the semantics of {@link Value#as(Class) host value mapping} is used. The result of the * mapping is equivalent of calling {@link Value#as(Class)} with the parameter type. Therefore, diff --git a/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/Engine.java b/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/Engine.java index 3fc4f1a3a4ca..97f19330aeb7 100644 --- a/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/Engine.java +++ b/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/Engine.java @@ -60,7 +60,6 @@ import java.io.InputStream; import java.io.OutputStream; import java.io.Reader; -import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.net.URI; import java.net.URL; @@ -493,6 +492,17 @@ public Engine build() { static class APIAccessImpl extends AbstractPolyglotImpl.APIAccess { + private final boolean useContextClassLoader; + + APIAccessImpl(boolean useContextClassLoader) { + this.useContextClassLoader = useContextClassLoader; + } + + @Override + public boolean useContextClassLoader() { + return useContextClassLoader; + } + @Override public Engine newEngine(AbstractEngineImpl impl) { return new Engine(impl); @@ -581,6 +591,7 @@ private static AbstractPolyglotImpl initEngineImpl() { public AbstractPolyglotImpl run() { AbstractPolyglotImpl engine = null; Class servicesClass = null; + boolean useContextClassLoader = false; if (JDK8_OR_EARLIER) { try { @@ -596,41 +607,33 @@ public AbstractPolyglotImpl run() { throw new InternalError(e); } } - } else { - // As of JDK9, the JVMCI Services class should only be used for service - // types - // defined by JVMCI. Other services types should use ServiceLoader directly. - Iterator providers = ServiceLoader.load(AbstractPolyglotImpl.class).iterator(); - if (providers.hasNext()) { - engine = providers.next(); - if (providers.hasNext()) { - - throw new InternalError(String.format("Multiple %s providers found", AbstractPolyglotImpl.class.getName())); - } - } } if (engine == null) { - try { - Class polyglotClass = Class.forName("com.oracle.truffle.polyglot.PolyglotImpl").asSubclass(AbstractPolyglotImpl.class); - Constructor constructor = polyglotClass.getDeclaredConstructor(); - constructor.setAccessible(true); - engine = constructor.newInstance(); - } catch (ClassNotFoundException e) { - } catch (Exception e1) { - throw new InternalError(e1); - } + engine = searchServiceLoader(); + useContextClassLoader = true; } - if (engine == null) { engine = createInvalidPolyglotImpl(); } if (engine != null) { - engine.setConstructors(new APIAccessImpl()); + engine.setConstructors(new APIAccessImpl(useContextClassLoader)); } return engine; } + + private AbstractPolyglotImpl searchServiceLoader() throws InternalError { + Iterator providers = ServiceLoader.load(AbstractPolyglotImpl.class).iterator(); + if (providers.hasNext()) { + AbstractPolyglotImpl found = providers.next(); + if (providers.hasNext()) { + throw new InternalError(String.format("Multiple %s providers found", AbstractPolyglotImpl.class.getName())); + } + return found; + } + return null; + } }); } diff --git a/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/impl/AbstractPolyglotImpl.java b/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/impl/AbstractPolyglotImpl.java index 0ca851701d2b..081a45edef4e 100644 --- a/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/impl/AbstractPolyglotImpl.java +++ b/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/impl/AbstractPolyglotImpl.java @@ -106,6 +106,8 @@ protected APIAccess() { } } + public abstract boolean useContextClassLoader(); + public abstract Engine newEngine(AbstractEngineImpl impl); public abstract Context newContext(AbstractContextImpl impl); diff --git a/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/io/IOHelper.java b/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/io/IOHelper.java index 2e064cfb6ae1..62becbaa7c75 100644 --- a/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/io/IOHelper.java +++ b/sdk/src/org.graalvm.polyglot/src/org/graalvm/polyglot/io/IOHelper.java @@ -86,7 +86,7 @@ static void copy(final Path source, final Path target, final FileSystem sourceFi } } if (copyOptions.contains(StandardCopyOption.ATOMIC_MOVE)) { - throw new AtomicMoveNotSupportedException(source.getFileName().toString(), target.getFileName().toString(), "Atomic move not supported"); + throw new AtomicMoveNotSupportedException(source.toString(), target.toString(), "Atomic move not supported"); } final Map sourceAttributes = sourceFileSystem.readAttributes( sourceReal, @@ -153,7 +153,7 @@ static void copy(final Path source, final Path target, final FileSystem sourceFi static void move(final Path source, final Path target, final FileSystem fileSystem, CopyOption... options) throws IOException { for (CopyOption option : options) { if (StandardCopyOption.ATOMIC_MOVE.equals(option)) { - throw new AtomicMoveNotSupportedException(source.getFileName().toString(), target.getFileName().toString(), "Atomic move not supported"); + throw new AtomicMoveNotSupportedException(source.toString(), target.toString(), "Atomic move not supported"); } } fileSystem.copy(source, target, options); @@ -163,7 +163,7 @@ static void move(final Path source, final Path target, final FileSystem fileSyst static void move(final Path source, final Path target, final FileSystem sourceFileSystem, final FileSystem targetFileSystem, CopyOption... options) throws IOException { for (CopyOption option : options) { if (StandardCopyOption.ATOMIC_MOVE.equals(option)) { - throw new AtomicMoveNotSupportedException(source.getFileName().toString(), target.getFileName().toString(), "Atomic move not supported"); + throw new AtomicMoveNotSupportedException(source.toString(), target.toString(), "Atomic move not supported"); } } copy(source, target, sourceFileSystem, targetFileSystem, options); diff --git a/substratevm/LIMITATIONS.md b/substratevm/LIMITATIONS.md index cb922c18265c..3c49a2cf8d4f 100644 --- a/substratevm/LIMITATIONS.md +++ b/substratevm/LIMITATIONS.md @@ -214,7 +214,8 @@ Security Manager What: `java.lang.SecurityManager` Since there is no dynamic class loading, there is also no need to sandbox "untrusted" code. -The method `System.getSecurityManager` alsways returns `null`, i.e., there are no runtime security manager checks performed. +The method `System.getSecurityManager` always returns `null`, i.e., there are no runtime security manager checks performed. +Attemps to install a `SecurityManager` using `System.setSecurityManager` are reported as a fatal error. JVMTI, JMX, other native VM interfaces diff --git a/substratevm/ci_includes/gate.hocon b/substratevm/ci_includes/gate.hocon index 568d6b7e58c2..999bd09ef978 100644 --- a/substratevm/ci_includes/gate.hocon +++ b/substratevm/ci_includes/gate.hocon @@ -18,12 +18,6 @@ gate-svm-js: { ] } -gate-svm-truffle-libgraal: { - run: [ - ${svm-cmd-gate} ["build,libgraal"] - ] -} - gate-svm-truffle-tck: { run: [ ${svm-cmd-gate} ["build,truffletck"] @@ -53,14 +47,6 @@ builds += [ ${svm-cmd-gate} ["build,helloworld,maven"] ] } - ${labsjdk8} ${svm-common-linux-gate} ${gate-svm-truffle-libgraal} ${svm-capabilities-base} { - name: "gate-svm-truffle-libgraal" - timelimit: "45:00" - } - ${labsjdk8} ${svm-common-darwin-gate} ${gate-svm-truffle-libgraal} { - name: "gate-svm-darwin-truffle-libgraal" - timelimit: "45:00" - } ${labsjdk8} ${svm-common-linux-gate} ${gate-svm-truffle-tck} ${svm-capabilities-base} { name: "gate-svm-truffle-tck" timelimit: "30:00" diff --git a/substratevm/mx.substratevm/mx_substratevm.py b/substratevm/mx.substratevm/mx_substratevm.py index 435936898129..8277cdce8547 100644 --- a/substratevm/mx.substratevm/mx_substratevm.py +++ b/substratevm/mx.substratevm/mx_substratevm.py @@ -152,6 +152,7 @@ def svm_java80(): IMAGE_ASSERTION_FLAGS = ['-H:+VerifyGraalGraphs', '-H:+VerifyGraalGraphEdges', '-H:+VerifyPhases'] suite = mx.suite('substratevm') svmSuites = [suite] +clibraryDists = ['SVM_HOSTED_NATIVE'] def _host_os_supported(): @@ -172,7 +173,7 @@ def classpath(args): return mx.classpath(args, jdk=mx_compiler.jdk) def clibrary_paths(): - return (join(suite.dir, 'clibraries') for suite in svmSuites) + return (mx._get_dependency_path(d) for d in clibraryDists) def platform_name(): return mx.get_os() + "-" + mx.get_arch() @@ -510,7 +511,6 @@ def __getattr__(self, name): 'build', 'test', 'benchmarktest', - 'libgraal', 'truffletck' ]) @@ -613,67 +613,6 @@ def svm_gate_body(args, tasks): maven_plugin_test([]) -@mx.command(suite.name, 'buildlibgraal') -def build_libgraal_cli(args): - build_libgraal(args) - -def check_libgraal_dependencies(): - if mx.get_os() == 'windows': - return 'libgraal is unsupported on Windows' - - graal_hotspot_library = mx.dependency('substratevm:GRAAL_HOTSPOT_LIBRARY', fatalIfMissing=False) - if not graal_hotspot_library: - return 'libgraal dependency substratevm:GRAAL_HOTSPOT_LIBRARY is missing' - - truffle_compiler_library = mx.dependency('compiler:GRAAL_TRUFFLE_COMPILER_LIBGRAAL', fatalIfMissing=False) - if not truffle_compiler_library: - return 'libgraal dependency compiler:GRAAL_TRUFFLE_COMPILER_LIBGRAAL is missing' - - return None - -def build_libgraal(image_args): - msg = check_libgraal_dependencies() - if msg: - return msg - - graal_hotspot_library = mx.dependency('substratevm:GRAAL_HOTSPOT_LIBRARY') - truffle_compiler_library = mx.dependency('compiler:GRAAL_TRUFFLE_COMPILER_LIBGRAAL') - truffle_api = mx.dependency('truffle:TRUFFLE_API') - - libgraal_args = ['-H:Name=libjvmcicompiler', '--shared', '-cp', os.pathsep.join([graal_hotspot_library.classpath_repr(), truffle_compiler_library.classpath_repr()]), - '--features=com.oracle.svm.graal.hotspot.libgraal.HotSpotGraalLibraryFeature', - '-J-Xbootclasspath/a:' + truffle_api.classpath_repr(), - '-H:-UseServiceLoaderFeature', - '-H:+AllowFoldMethods', - '-Djdk.vm.ci.services.aot=true'] - - native_image_on_jvm(libgraal_args + image_args) - - return None - - -def libgraal_gate_body(args, tasks): - msg = check_libgraal_dependencies() - if msg: - mx.logv('Skipping libgraal because: {}'.format(msg)) - return - - with Task('Build libgraal', tasks, tags=[GraalTags.build, GraalTags.benchmarktest, GraalTags.test, GraalTags.libgraal]) as t: - # Build libgraal with assertions in the image builder and assertions in the image - if t: build_libgraal(['-J-esa', '-ea']) - - extra_vm_argument = ['-XX:+UseJVMCICompiler', '-XX:+UseJVMCINativeLibrary', '-XX:JVMCILibPath=' + os.getcwd()] - if args.extra_vm_argument: - extra_vm_argument += args.extra_vm_argument - - mx_compiler.compiler_gate_benchmark_runner(tasks, extra_vm_argument, libgraal=True) - - with Task('Test libgraal', tasks, tags=[GraalTags.libgraal]) as t: - if t: - mx_unittest.unittest(["--suite", "truffle", "--"] + extra_vm_argument + ["-Dgraal.TruffleCompileImmediately=true", "-Dgraal.TruffleBackgroundCompilation=false"]) - -mx_gate.add_gate_runner(suite, libgraal_gate_body) - def javac_image_command(javac_path): return [join(javac_path, 'javac'), "-proc:none", "-bootclasspath", join(mx_compiler.jdk.home, "jre", "lib", "rt.jar")] @@ -1011,6 +950,36 @@ def deploy_native_image_maven_plugin(svmVersion, repo, gpg, keyid): )) +if os.environ.has_key('LIBGRAAL'): + mx_sdk.register_graalvm_component(mx_sdk.GraalVmJreComponent( + suite=suite, + name='LibGraal', + short_name='lg', + dir_name=False, + license_files=[], + third_party_license_files=[], + jar_distributions=[], + builder_jar_distributions=[], + support_distributions=[], + library_configs=[ + mx_sdk.LibraryConfig( + destination="", + jvm_library=True, + jar_distributions=[ + 'substratevm:GRAAL_HOTSPOT_LIBRARY', + 'compiler:GRAAL_TRUFFLE_COMPILER_LIBGRAAL' + ], + build_args=[ + '--features=com.oracle.svm.graal.hotspot.libgraal.HotSpotGraalLibraryFeature', + '-H:-UseServiceLoaderFeature', + '-H:+AllowFoldMethods', + '-Djdk.vm.ci.services.aot=true' + ], + ), + ], + )) + + @mx.command(suite_name=suite.name, command_name='helloworld', usage_msg='[options]') def helloworld(args): """ diff --git a/substratevm/mx.substratevm/rebuild-images.sh b/substratevm/mx.substratevm/rebuild-images.sh index e1a5771867d2..f1211ab3b606 100755 --- a/substratevm/mx.substratevm/rebuild-images.sh +++ b/substratevm/mx.substratevm/rebuild-images.sh @@ -206,7 +206,7 @@ for binary in "${to_build[@]}"; do language python "bin/graalpython" "com.oracle.graal.python.shell.GraalPythonMain" ;; ruby) - language ruby "bin/ruby" "org.truffleruby.launcher.RubyLauncher" + language ruby "bin/truffleruby" "org.truffleruby.launcher.RubyLauncher" ;; *) echo "shouldNotReachHere()" diff --git a/substratevm/mx.substratevm/suite.py b/substratevm/mx.substratevm/suite.py index 7d3c7fb89b03..acbdb8ff0fe2 100644 --- a/substratevm/mx.substratevm/suite.py +++ b/substratevm/mx.substratevm/suite.py @@ -1,5 +1,5 @@ suite = { - "mxversion": "5.196.3", + "mxversion": "5.210.5", "name": "substratevm", "version" : "1.0.0-rc13", "release" : False, @@ -84,7 +84,7 @@ "workingSets": "SVM", }, - "com.oracle.svm.core.jdk9.posix": { + "com.oracle.svm.core.posix.jdk9": { "subDir": "src", "sourceDirs": ["src"], "dependencies": [ @@ -155,7 +155,9 @@ "com.oracle.svm.core.posix": { "subDir": "src", "sourceDirs": ["src"], - "dependencies": ["com.oracle.svm.core"], + "dependencies": [ + "com.oracle.svm.hosted", + ], "checkstyle": "com.oracle.svm.core", "javaCompliance": "8+", "annotationProcessors": [ @@ -164,7 +166,7 @@ "compiler:GRAAL_OPTIONS_PROCESSOR", ], "workingSets": "SVM", - "findbugs": "false", + "spotbugs": "false", }, "com.oracle.svm.core.windows": { @@ -181,7 +183,7 @@ "compiler:GRAAL_OPTIONS_PROCESSOR", ], "workingSets": "SVM", - "findbugs": "false", + "spotbugs": "false", }, "com.oracle.graal.pointsto": { @@ -268,9 +270,33 @@ }, }, - "com.oracle.svm.native.jvm": { + "com.oracle.svm.native.jvm.posix": { + "subDir": "src", + "native": "static_lib", + "deliverable" : "jvm", + "os_arch" : { + "darwin": { + "amd64" : { + "cflags": ["-g", "-fPIC", "-O2"], + }, + }, + "linux": { + "amd64" : { + "cflags": ["-g", "-fPIC", "-O2"], + }, + }, + "": { + "": { + "ignore": "only darwin and linux are supported", + }, + }, + }, + }, + + "com.oracle.svm.native.jvm.windows": { "subDir": "src", "native": "static_lib", + "deliverable" : "jvm", "os_arch" : { "windows": { "amd64" : { @@ -299,7 +325,7 @@ "compiler:GRAAL_OPTIONS_PROCESSOR", ], "javaCompliance": "8+", - "findbugs": "false", + "spotbugs": "false", }, "com.oracle.svm.driver": { @@ -321,7 +347,7 @@ "compiler:GRAAL_OPTIONS_PROCESSOR", ], "javaCompliance": "8+", - "findbugs": "false", + "spotbugs": "false", }, "com.oracle.svm.junit": { @@ -337,7 +363,7 @@ "compiler:GRAAL_OPTIONS_PROCESSOR", ], "javaCompliance": "8+", - "findbugs": "false", + "spotbugs": "false", }, "com.oracle.svm.test": { @@ -353,7 +379,7 @@ "compiler:GRAAL_OPTIONS_PROCESSOR", ], "javaCompliance": "8+", - "findbugs": "false", + "spotbugs": "false", }, "com.oracle.svm.reflect": { @@ -368,7 +394,7 @@ "compiler:GRAAL_OPTIONS_PROCESSOR", ], "javaCompliance": "8+", - "findbugs": "false", + "spotbugs": "false", }, "com.oracle.svm.tutorial" : { @@ -383,7 +409,7 @@ "compiler:GRAAL_OPTIONS_PROCESSOR", ], "workingSets" : "SVM", - "findbugs" : "false", + "spotbugs" : "false", }, "com.oracle.objectfile" : { @@ -394,7 +420,7 @@ "javaCompliance" : "8+", "annotationProcessors" : ["compiler:GRAAL_OPTIONS_PROCESSOR"], "workingSets" : "SVM", - "findbugs" : "false", + "spotbugs" : "false", }, "com.oracle.svm.graal": { @@ -463,29 +489,6 @@ "workingSets": "SVM", }, - "com.oracle.svm.libffi": { - "subDir": "src", - "native": True, - "vpath": True, - "results": [ - "libffi.a", - "include/ffi.h", - "include/ffitarget.h", - "include/trufflenfi.h", - "include/svm_libffi.h", - ], - "buildEnv": { - "LIBFFI_DIST": "", - "TRUFFLE_NFI": "", - "ARCH": "", - "OS": "" - }, - "buildDependencies": [ - "truffle:LIBFFI_DIST", - "truffle:TRUFFLE_NFI_NATIVE", - ], - }, - "com.oracle.svm.jline": { "subDir": "src", "sourceDirs": ["src"], @@ -501,7 +504,7 @@ "compiler:GRAAL_OPTIONS_PROCESSOR", ], "workingSets": "SVM", - "findbugs": "false", + "spotbugs": "false", }, "com.oracle.svm.polyglot": { @@ -518,7 +521,7 @@ "compiler:GRAAL_OPTIONS_PROCESSOR", ], "workingSets": "SVM", - "findbugs": "false", + "spotbugs": "false", }, "org.graalvm.polyglot.nativeapi" : { @@ -543,7 +546,7 @@ "compiler:GRAAL_OPTIONS_PROCESSOR", ], "workingSets" : "SVM", - "findbugs": "false", + "spotbugs": "false", }, "org.graalvm.polyglot.nativeapi.native" : { @@ -612,7 +615,7 @@ "com.oracle.svm.core.jdk8", "com.oracle.svm.core.jdk9", "com.oracle.svm.core.posix", - "com.oracle.svm.core.jdk9.posix", + "com.oracle.svm.core.posix.jdk9", "com.oracle.svm.core.windows", "com.oracle.svm.core.genscavenge", ], @@ -712,12 +715,6 @@ # Native Projects # "SVM_HOSTED_NATIVE": { - "dependencies": [ - "com.oracle.svm.native.libchelper", - "com.oracle.svm.native.strictmath", - "com.oracle.svm.native.jvm", - "com.oracle.svm.libffi" - ], "native": True, "platformDependent" : True, "platforms" : [ @@ -725,10 +722,20 @@ "darwin-amd64", "windows-amd64", ], + "layout": { + "-/": [ + "dependency:com.oracle.svm.native.libchelper/*", + "dependency:com.oracle.svm.native.strictmath/*", + "dependency:com.oracle.svm.native.jvm.posix/*", + "dependency:com.oracle.svm.native.jvm.windows/*", + "extracted-dependency:truffle:LIBFFI_DIST", + ], + "-/include/": [ + "extracted-dependency:truffle:TRUFFLE_NFI_NATIVE/include/*", + "file:src/com.oracle.svm.libffi/include/svm_libffi.h", + ] + }, "description" : "SubstrateVM image builder native components", - "relpath": True, - "auto_prefix": True, - "output": "clibraries", "maven": True }, diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/CloneTypeFlow.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/CloneTypeFlow.java index df2ab1d89c0e..30332cbc16e4 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/CloneTypeFlow.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/CloneTypeFlow.java @@ -138,10 +138,10 @@ public void update(BigBang bb) { for (AnalysisObject originalObject : inputState.objects(type)) { /* Link all the field flows of the original to the clone. */ for (AnalysisField field : type.getInstanceFields(true)) { - FieldTypeFlow originalObjectFieldFlow = originalObject.getInstanceFieldFlow(bb, field, false); + FieldTypeFlow originalObjectFieldFlow = originalObject.getInstanceFieldFlow(bb, this.method(), field, false); for (AnalysisObject cloneObject : cloneState.objects(type)) { - FieldTypeFlow cloneObjectFieldFlow = cloneObject.getInstanceFieldFlow(bb, field, true); + FieldTypeFlow cloneObjectFieldFlow = cloneObject.getInstanceFieldFlow(bb, this.method(), field, true); originalObjectFieldFlow.addUse(bb, cloneObjectFieldFlow); } } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/FormalParamTypeFlow.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/FormalParamTypeFlow.java index 1a9f9119782b..073b5685aa4d 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/FormalParamTypeFlow.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/FormalParamTypeFlow.java @@ -25,6 +25,7 @@ package com.oracle.graal.pointsto.flow; import org.graalvm.compiler.nodes.ParameterNode; + import com.oracle.graal.pointsto.BigBang; import com.oracle.graal.pointsto.meta.AnalysisMethod; import com.oracle.graal.pointsto.meta.AnalysisType; @@ -53,6 +54,7 @@ public TypeFlow copy(BigBang bb, MethodFlowsGraph methodFlows) { return new FormalParamTypeFlow(this, methodFlows); } + @Override public AnalysisMethod method() { return method; } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/FormalReturnTypeFlow.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/FormalReturnTypeFlow.java index 5d164d067394..eefed94d4156 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/FormalReturnTypeFlow.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/FormalReturnTypeFlow.java @@ -25,6 +25,7 @@ package com.oracle.graal.pointsto.flow; import org.graalvm.compiler.nodes.ValueNode; + import com.oracle.graal.pointsto.BigBang; import com.oracle.graal.pointsto.meta.AnalysisMethod; import com.oracle.graal.pointsto.meta.AnalysisType; @@ -48,6 +49,7 @@ public TypeFlow copy(BigBang bb, MethodFlowsGraph methodFlows) { return new FormalReturnTypeFlow(this, methodFlows); } + @Override public AnalysisMethod method() { return method; } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/InitialParamTypeFlow.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/InitialParamTypeFlow.java index 5fd45f06c776..a9c02f1b4bcf 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/InitialParamTypeFlow.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/InitialParamTypeFlow.java @@ -41,6 +41,7 @@ public InitialParamTypeFlow(AnalysisMethod source, AnalysisType declaredType, in this.position = position; } + @Override public AnalysisMethod method() { return super.getSource(); } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/LoadFieldTypeFlow.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/LoadFieldTypeFlow.java index 540274c95ccf..d0187d3d5e19 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/LoadFieldTypeFlow.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/LoadFieldTypeFlow.java @@ -25,6 +25,7 @@ package com.oracle.graal.pointsto.flow; import org.graalvm.compiler.nodes.java.LoadFieldNode; + import com.oracle.graal.pointsto.BigBang; import com.oracle.graal.pointsto.flow.context.object.AnalysisObject; import com.oracle.graal.pointsto.meta.AnalysisField; @@ -156,7 +157,7 @@ public void onObservedUpdate(BigBang bb) { /* Iterate over the receiver objects. */ for (AnalysisObject object : objectState.objects()) { /* Get the field flow corresponding to the receiver object. */ - FieldTypeFlow fieldFlow = object.getInstanceFieldFlow(bb, field, false); + FieldTypeFlow fieldFlow = object.getInstanceFieldFlow(bb, this.method(), field, false); /* Add the load field flow as a use to the heap sensitive field flow. */ fieldFlow.addUse(bb, this); diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/MethodTypeFlowBuilder.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/MethodTypeFlowBuilder.java index 843e14dc9d98..8714fd00034e 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/MethodTypeFlowBuilder.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/MethodTypeFlowBuilder.java @@ -39,7 +39,6 @@ import org.graalvm.compiler.api.runtime.GraalJVMCICompiler; import org.graalvm.compiler.bytecode.Bytecode; import org.graalvm.compiler.bytecode.ResolvedJavaMethodBytecode; -import org.graalvm.compiler.core.common.GraalOptions; import org.graalvm.compiler.core.common.PermanentBailoutException; import org.graalvm.compiler.core.common.spi.ForeignCallDescriptor; import org.graalvm.compiler.core.common.type.ObjectStamp; @@ -175,7 +174,7 @@ public MethodTypeFlowBuilder(BigBang bb, StructuredGraph graph) { @SuppressWarnings("try") private boolean parse() { - OptionValues options = bb.getOptions(); + OptionValues options = bb.getUniverse().adjustCompilerOptions(bb.getOptions(), method); GraalJVMCICompiler compiler = (GraalJVMCICompiler) JVMCI.getRuntime().getCompiler(); SnippetReflectionProvider snippetReflection = compiler.getGraalRuntime().getRequiredCapability(SnippetReflectionProvider.class); // Use the real SnippetReflectionProvider for dumping @@ -198,17 +197,7 @@ private boolean parse() { if (!method.hasBytecodes()) { return false; } - needParsing = true; - - /* - * The analysis bytecode parsing configuration needs to be as conservative as - * possible and match the compilation bytecode parsing configuration which disables - * liveness analysis to preserve the values of local variables beyond the - * bytecode-liveness. This greatly helps debugging. See - * CompileQueue.defaultParseFunction(). - */ - options = new OptionValues(options, GraalOptions.OptClearNonLiveLocals, false); graph = new StructuredGraph.Builder(options, debug).method(method).build(); } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/OffsetLoadTypeFlow.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/OffsetLoadTypeFlow.java index 41b4315b3917..bc51b8231644 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/OffsetLoadTypeFlow.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/OffsetLoadTypeFlow.java @@ -28,6 +28,7 @@ import org.graalvm.compiler.nodes.extended.JavaReadNode; import org.graalvm.compiler.nodes.extended.RawLoadNode; import org.graalvm.compiler.nodes.java.AtomicReadAndWriteNode; + import com.oracle.graal.pointsto.BigBang; import com.oracle.graal.pointsto.api.UnsafePartitionKind; import com.oracle.graal.pointsto.flow.context.object.AnalysisObject; @@ -187,7 +188,7 @@ public void onObservedUpdate(BigBang bb) { } else { for (AnalysisField field : objectType.unsafeAccessedFields()) { assert field != null; - TypeFlow fieldFlow = object.getInstanceFieldFlow(bb, field, false); + TypeFlow fieldFlow = object.getInstanceFieldFlow(bb, this.method(), field, false); if (fieldFlow.getState().isUnknown()) { bb.getUnsupportedFeatures().addMessage(graphRef.getMethod().format("%H.%n(%p)"), graphRef.getMethod(), "Illegal: Unsafe loading UnknownTypeState from object. Load: " + this.getSource()); @@ -271,7 +272,7 @@ public void onObservedUpdate(BigBang bb) { assert !objectType.isArray(); for (AnalysisField field : objectType.unsafeAccessedFields(partitionKind)) { - TypeFlow fieldFlow = object.getInstanceFieldFlow(bb, field, false); + TypeFlow fieldFlow = object.getInstanceFieldFlow(bb, this.method(), field, false); if (fieldFlow.getState().isUnknown()) { bb.getUnsupportedFeatures().addMessage(graphRef.getMethod().format("%H.%n(%p)"), graphRef.getMethod(), diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/OffsetStoreTypeFlow.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/OffsetStoreTypeFlow.java index b6867e197fd9..ee1eaaeb5f3e 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/OffsetStoreTypeFlow.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/OffsetStoreTypeFlow.java @@ -30,6 +30,8 @@ import org.graalvm.compiler.nodes.extended.JavaWriteNode; import org.graalvm.compiler.nodes.extended.RawStoreNode; import org.graalvm.compiler.nodes.java.AtomicReadAndWriteNode; +import org.graalvm.compiler.nodes.java.UnsafeCompareAndSwapNode; + import com.oracle.graal.pointsto.BigBang; import com.oracle.graal.pointsto.api.UnsafePartitionKind; import com.oracle.graal.pointsto.flow.context.object.AnalysisObject; @@ -38,8 +40,6 @@ import com.oracle.graal.pointsto.nodes.AnalysisUnsafePartitionStoreNode; import com.oracle.graal.pointsto.typestate.TypeState; -import org.graalvm.compiler.nodes.java.UnsafeCompareAndSwapNode; - /** * The abstract class for offset store flows (i.e. indexed stores, unsafe stores at offset, java * writes). @@ -182,14 +182,14 @@ public boolean addState(BigBang bb, TypeState add) { return super.addState(bb, add, true); } - protected void handleUnsafeAccessedFields(BigBang bb, List unsafeAccessedFields, AnalysisObject object) { + void handleUnsafeAccessedFields(BigBang bb, List unsafeAccessedFields, AnalysisObject object) { for (AnalysisField field : unsafeAccessedFields) { /* Write through the field filter flow. */ if (field.hasUnsafeFrozenTypeState()) { - UnsafeWriteSinkTypeFlow unsafeWriteSink = object.getUnsafeWriteSinkFrozenFilterFlow(bb, field); + UnsafeWriteSinkTypeFlow unsafeWriteSink = object.getUnsafeWriteSinkFrozenFilterFlow(bb, this.method(), field); this.addUse(bb, unsafeWriteSink); } else { - FieldFilterTypeFlow fieldFilterFlow = object.getInstanceFieldFilterFlow(bb, field); + FieldFilterTypeFlow fieldFilterFlow = object.getInstanceFieldFilterFlow(bb, this.method(), field); this.addUse(bb, fieldFilterFlow); } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/StoreFieldTypeFlow.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/StoreFieldTypeFlow.java index 30cdd322cc42..84419bfed041 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/StoreFieldTypeFlow.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/StoreFieldTypeFlow.java @@ -25,6 +25,7 @@ package com.oracle.graal.pointsto.flow; import org.graalvm.compiler.nodes.java.StoreFieldNode; + import com.oracle.graal.pointsto.BigBang; import com.oracle.graal.pointsto.flow.context.object.AnalysisObject; import com.oracle.graal.pointsto.meta.AnalysisField; @@ -157,7 +158,7 @@ public void onObservedUpdate(BigBang bb) { /* Iterate over the receiver objects. */ for (AnalysisObject receiver : objectState.objects()) { /* Get the field flow corresponding to the receiver object. */ - FieldTypeFlow fieldFlow = receiver.getInstanceFieldFlow(bb, field, true); + FieldTypeFlow fieldFlow = receiver.getInstanceFieldFlow(bb, this.method(), field, true); /* Register the field flow as a use, if not already registered. */ this.addUse(bb, fieldFlow); } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/TypeFlow.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/TypeFlow.java index b91e365c5d30..9ffe508df976 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/TypeFlow.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/TypeFlow.java @@ -34,6 +34,7 @@ import com.oracle.graal.pointsto.BigBang; import com.oracle.graal.pointsto.api.PointstoOptions; import com.oracle.graal.pointsto.flow.context.AnalysisContext; +import com.oracle.graal.pointsto.meta.AnalysisMethod; import com.oracle.graal.pointsto.meta.AnalysisType; import com.oracle.graal.pointsto.typestate.PointsToStats; import com.oracle.graal.pointsto.typestate.TypeState; @@ -177,6 +178,10 @@ public MethodFlowsGraph graphRef() { return graphRef; } + public AnalysisMethod method() { + return graphRef != null ? graphRef.getMethod() : null; + } + public T getSource() { return source; } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/context/object/AnalysisObject.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/context/object/AnalysisObject.java index 997cf6124192..23409f2c12b1 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/context/object/AnalysisObject.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/context/object/AnalysisObject.java @@ -36,10 +36,12 @@ import com.oracle.graal.pointsto.flow.FieldTypeFlow; import com.oracle.graal.pointsto.flow.UnsafeWriteSinkTypeFlow; import com.oracle.graal.pointsto.meta.AnalysisField; +import com.oracle.graal.pointsto.meta.AnalysisMethod; import com.oracle.graal.pointsto.meta.AnalysisType; import com.oracle.graal.pointsto.meta.AnalysisUniverse; import com.oracle.graal.pointsto.typestore.ArrayElementsTypeStore; import com.oracle.graal.pointsto.typestore.FieldTypeStore; +import com.oracle.graal.pointsto.util.AnalysisError; import jdk.vm.ci.meta.JavaConstant; import jdk.vm.ci.meta.JavaKind; @@ -179,29 +181,33 @@ public ArrayElementsTypeFlow getArrayElementsFlow(BigBang bb, boolean isStore) { } /** Returns the filter field flow corresponding to an unsafe accessed filed. */ - public FieldFilterTypeFlow getInstanceFieldFilterFlow(BigBang bb, AnalysisField field) { + public FieldFilterTypeFlow getInstanceFieldFilterFlow(BigBang bb, AnalysisMethod context, AnalysisField field) { assert !Modifier.isStatic(field.getModifiers()) && field.isUnsafeAccessed(); - FieldTypeStore fieldTypeStore = getInstanceFieldTypeStore(bb, field); + FieldTypeStore fieldTypeStore = getInstanceFieldTypeStore(bb, context, field); return fieldTypeStore.filterFlow(bb); } - public UnsafeWriteSinkTypeFlow getUnsafeWriteSinkFrozenFilterFlow(BigBang bb, AnalysisField field) { + public UnsafeWriteSinkTypeFlow getUnsafeWriteSinkFrozenFilterFlow(BigBang bb, AnalysisMethod context, AnalysisField field) { assert !Modifier.isStatic(field.getModifiers()) && field.hasUnsafeFrozenTypeState(); - FieldTypeStore fieldTypeStore = getInstanceFieldTypeStore(bb, field); + FieldTypeStore fieldTypeStore = getInstanceFieldTypeStore(bb, context, field); return fieldTypeStore.unsafeWriteSinkFlow(bb); } /** Returns the instance field flow corresponding to a filed of the object's type. */ public FieldTypeFlow getInstanceFieldFlow(BigBang bb, AnalysisField field, boolean isStore) { + return getInstanceFieldFlow(bb, null, field, isStore); + } + + public FieldTypeFlow getInstanceFieldFlow(BigBang bb, AnalysisMethod context, AnalysisField field, boolean isStore) { assert !Modifier.isStatic(field.getModifiers()); - FieldTypeStore fieldTypeStore = getInstanceFieldTypeStore(bb, field); + FieldTypeStore fieldTypeStore = getInstanceFieldTypeStore(bb, context, field); return isStore ? fieldTypeStore.writeFlow() : fieldTypeStore.readFlow(); } - protected final FieldTypeStore getInstanceFieldTypeStore(BigBang bb, AnalysisField field) { + final FieldTypeStore getInstanceFieldTypeStore(BigBang bb, AnalysisMethod context, AnalysisField field) { assert !Modifier.isStatic(field.getModifiers()); assert bb != null && !bb.getUniverse().sealed(); @@ -210,6 +216,10 @@ protected final FieldTypeStore getInstanceFieldTypeStore(BigBang bb, AnalysisFie INSTANCE_FIELD_TYPE_STORE_UPDATER.compareAndSet(this, null, new AtomicReferenceArray<>(fields.length)); } + if (field.getPosition() < 0 || field.getPosition() >= instanceFieldsTypeStore.length()) { + throw AnalysisError.fieldNotPresentError(context, field, type); + } + FieldTypeStore fieldStore = instanceFieldsTypeStore.get(field.getPosition()); if (fieldStore == null) { fieldStore = bb.analysisPolicy().createFieldTypeStore(this, field, bb.getUniverse()); diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/context/object/ContextSensitiveAnalysisObject.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/context/object/ContextSensitiveAnalysisObject.java index 126d35d97980..d5047aea098f 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/context/object/ContextSensitiveAnalysisObject.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/flow/context/object/ContextSensitiveAnalysisObject.java @@ -35,6 +35,7 @@ import com.oracle.graal.pointsto.flow.FieldFilterTypeFlow; import com.oracle.graal.pointsto.flow.FieldTypeFlow; import com.oracle.graal.pointsto.meta.AnalysisField; +import com.oracle.graal.pointsto.meta.AnalysisMethod; import com.oracle.graal.pointsto.meta.AnalysisType; import com.oracle.graal.pointsto.meta.AnalysisUniverse; import com.oracle.graal.pointsto.typestore.FieldTypeStore; @@ -123,10 +124,10 @@ public ArrayElementsTypeFlow getArrayElementsFlow(BigBang bb, boolean isStore) { /** Returns the filter field flow corresponding to an unsafe accessed field. */ @Override - public FieldFilterTypeFlow getInstanceFieldFilterFlow(BigBang bb, AnalysisField field) { + public FieldFilterTypeFlow getInstanceFieldFilterFlow(BigBang bb, AnalysisMethod context, AnalysisField field) { assert !Modifier.isStatic(field.getModifiers()) && field.isUnsafeAccessed() && PointstoOptions.AllocationSiteSensitiveHeap.getValue(bb.getOptions()); - FieldTypeStore fieldTypeStore = getInstanceFieldTypeStore(bb, field); + FieldTypeStore fieldTypeStore = getInstanceFieldTypeStore(bb, context, field); if (merged) { /* @@ -143,7 +144,7 @@ public FieldFilterTypeFlow getInstanceFieldFilterFlow(BigBang bb, AnalysisField public FieldTypeFlow getInstanceFieldFlow(BigBang bb, AnalysisField field, boolean isStore) { assert !Modifier.isStatic(field.getModifiers()) && PointstoOptions.AllocationSiteSensitiveHeap.getValue(bb.getOptions()); - FieldTypeStore fieldTypeStore = getInstanceFieldTypeStore(bb, field); + FieldTypeStore fieldTypeStore = getInstanceFieldTypeStore(bb, null, field); if (merged) { /* diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/infrastructure/Universe.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/infrastructure/Universe.java index 962aca79770a..3f19dca63317 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/infrastructure/Universe.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/infrastructure/Universe.java @@ -25,6 +25,7 @@ package com.oracle.graal.pointsto.infrastructure; import org.graalvm.compiler.api.replacements.SnippetReflectionProvider; +import org.graalvm.compiler.options.OptionValues; import com.oracle.graal.pointsto.api.HostVM; @@ -64,5 +65,7 @@ public interface Universe { ResolvedJavaMethod resolveSubstitution(ResolvedJavaMethod method); + OptionValues adjustCompilerOptions(OptionValues optionValues, ResolvedJavaMethod method); + ResolvedJavaType objectType(); } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisMethod.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisMethod.java index 6022776abb54..27816e8080ea 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisMethod.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisMethod.java @@ -325,18 +325,11 @@ public AnalysisType getDeclaringClass() { @Override public int getMaxLocals() { - if (isNative()) { - return getSignature().getParameterCount(!Modifier.isStatic(getModifiers())) * 2; - } return wrapped.getMaxLocals(); } @Override public int getMaxStackSize() { - if (isNative()) { - // At most we have a double-slot return value. - return 2; - } return wrapped.getMaxStackSize(); } diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisUniverse.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisUniverse.java index 02f2ca1561ac..571b2f7469ca 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisUniverse.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/meta/AnalysisUniverse.java @@ -40,7 +40,9 @@ import java.util.function.Function; import org.graalvm.compiler.api.replacements.SnippetReflectionProvider; +import org.graalvm.compiler.core.common.GraalOptions; import org.graalvm.compiler.core.common.SuppressFBWarnings; +import org.graalvm.compiler.options.OptionValues; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; import org.graalvm.util.GuardedAnnotationAccess; @@ -616,6 +618,16 @@ public ResolvedJavaMethod resolveSubstitution(ResolvedJavaMethod method) { return substitutions.resolve(method); } + @Override + public OptionValues adjustCompilerOptions(OptionValues optionValues, ResolvedJavaMethod method) { + /* + * For the AnalysisUniverse we want to always disable the liveness analysis, since we want + * the points-to analysis to be as conservative as possible. We don't want the optimization + * level to affect the execution of the points-to analysis. + */ + return new OptionValues(optionValues, GraalOptions.OptClearNonLiveLocals, false); + } + @Override public AnalysisType objectType() { return objectClass; diff --git a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/util/AnalysisError.java b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/util/AnalysisError.java index c0e31ddf99c4..20fa2f5e3ede 100644 --- a/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/util/AnalysisError.java +++ b/substratevm/src/com.oracle.graal.pointsto/src/com/oracle/graal/pointsto/util/AnalysisError.java @@ -30,7 +30,9 @@ import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; +import com.oracle.graal.pointsto.meta.AnalysisField; import com.oracle.graal.pointsto.meta.AnalysisMethod; +import com.oracle.graal.pointsto.meta.AnalysisType; import jdk.vm.ci.meta.ResolvedJavaType; @@ -96,16 +98,7 @@ public AnalysisMethod getMethod() { private static String message(AnalysisMethod method, Throwable original) { String msg = String.format("Error encountered while parsing %s %n", method.format("%H.%n(%P)")); - msg += String.format("Parsing context:"); - if (method.getTypeFlow().getParsingContext().length > 0) { - for (StackTraceElement e : method.getTypeFlow().getParsingContext()) { - msg += String.format("%n\tparsing %s", e); - } - msg += String.format("%n"); - } else { - msg += String.format(" %n"); - } - + msg += parsingContext(method); StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); original.printStackTrace(pw); @@ -117,6 +110,36 @@ private static String message(AnalysisMethod method, Throwable original) { } + public static class FieldNotPresentError extends AnalysisError { + private static final long serialVersionUID = -7167507945764369928L; + + FieldNotPresentError(AnalysisMethod context, AnalysisField field, AnalysisType type) { + super(message(context, field, type)); + } + + private static String message(AnalysisMethod context, AnalysisField field, AnalysisType type) { + String msg = String.format("Field %s is not present on type %s. ", field.format("%H.%n"), type.toJavaName()); + if (context != null) { + msg += String.format("Error encountered while analysing %s %n", context.format("%H.%n(%P)")); + msg += parsingContext(context); + } + return msg; + } + } + + private static String parsingContext(AnalysisMethod method) { + StringBuilder msg = new StringBuilder("Parsing context:"); + if (method.getTypeFlow().getParsingContext().length > 0) { + for (StackTraceElement e : method.getTypeFlow().getParsingContext()) { + msg.append(String.format("%n\tparsing %s", e)); + } + msg.append(String.format("%n")); + } else { + msg.append(String.format(" %n")); + } + return msg.toString(); + } + public static TypeNotFoundError typeNotFound(ResolvedJavaType type) { throw new TypeNotFoundError(type); } @@ -125,6 +148,10 @@ public static ParsingError parsingError(AnalysisMethod method, Throwable origina throw new ParsingError(method, original); } + public static FieldNotPresentError fieldNotPresentError(AnalysisMethod context, AnalysisField field, AnalysisType type) { + throw new FieldNotPresentError(context, field, type); + } + public static RuntimeException shouldNotReachHere() { throw new AnalysisError("should not reach here"); } diff --git a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GreyToBlackObjRefVisitor.java b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GreyToBlackObjRefVisitor.java index 3b5fee70a94d..daf64d5fbc6b 100644 --- a/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GreyToBlackObjRefVisitor.java +++ b/substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/GreyToBlackObjRefVisitor.java @@ -25,6 +25,7 @@ package com.oracle.svm.core.genscavenge; import org.graalvm.compiler.options.Option; +import org.graalvm.compiler.word.Word; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; import org.graalvm.word.Pointer; @@ -34,6 +35,7 @@ import com.oracle.svm.core.heap.ObjectHeader; import com.oracle.svm.core.heap.ObjectReferenceVisitor; import com.oracle.svm.core.heap.ReferenceAccess; +import com.oracle.svm.core.hub.LayoutEncoding; import com.oracle.svm.core.log.Log; import com.oracle.svm.core.option.HostedOptionKey; @@ -58,7 +60,7 @@ public static GreyToBlackObjRefVisitor factory() { @Override public boolean visitObjectReference(final Pointer objRef, boolean compressed) { - return visitObjectReferenceInline(objRef, compressed); + return visitObjectReferenceInline(objRef, 0, compressed); } /** @@ -67,7 +69,9 @@ public boolean visitObjectReference(final Pointer objRef, boolean compressed) { */ @Override @AlwaysInline("GC performance") - public boolean visitObjectReferenceInline(final Pointer objRef, boolean compressed) { + public boolean visitObjectReferenceInline(final Pointer objRef, final int innerOffset, boolean compressed) { + assert innerOffset >= 0; + getCounters().noteObjRef(); final Log trace = Log.noopLog().string("[GreyToBlackObjRefVisitor.visitObjectReferenceInline:").string(" objRef: ").hex(objRef); if (objRef.isNull()) { @@ -76,7 +80,10 @@ public boolean visitObjectReferenceInline(final Pointer objRef, boolean compress return true; } // Read the referenced Object, carefully. - final Pointer p = ReferenceAccess.singleton().readObjectAsUntrackedPointer(objRef, compressed); + final Pointer offsetP = ReferenceAccess.singleton().readObjectAsUntrackedPointer(objRef, compressed); + assert offsetP.isNonNull() || innerOffset == 0; + final Pointer p = offsetP.subtract(innerOffset); + trace.string(" p: ").hex(p); // It might be null. if (p.isNull()) { @@ -93,7 +100,8 @@ public boolean visitObjectReferenceInline(final Pointer objRef, boolean compress trace.string(" forwards to "); // Update the reference to point to the forwarded Object. final Object obj = ohi.getForwardedObject(p); - ReferenceAccess.singleton().writeObjectAt(objRef, obj, compressed); + final Object offsetObj = (innerOffset == 0) ? obj : Word.objectToUntrackedPointer(obj).add(innerOffset).toObject(); + ReferenceAccess.singleton().writeObjectAt(objRef, offsetObj, compressed); trace.object(obj); if (trace.isEnabled()) { trace.string(" objectHeader: ").string(ohi.toStringFromObject(obj)).string("]").newline(); @@ -102,6 +110,7 @@ public boolean visitObjectReferenceInline(final Pointer objRef, boolean compress } // It might be a real Object. final Object obj = p.toObject(); + assert innerOffset < LayoutEncoding.getSizeFromObject(obj).rawValue(); // If the object is not a heap object there's nothing to do. if (ohi.isNonHeapAllocatedHeader(header)) { getCounters().noteNonHeapReferent(); @@ -127,7 +136,8 @@ public boolean visitObjectReferenceInline(final Pointer objRef, boolean compress if (copy != obj) { getCounters().noteCopiedReferent(); trace.string(" updating objRef: ").hex(objRef).string(" with copy: ").object(copy); - ReferenceAccess.singleton().writeObjectAt(objRef, copy, compressed); + final Object offsetCopy = (innerOffset == 0) ? copy : Word.objectToUntrackedPointer(copy).add(innerOffset).toObject(); + ReferenceAccess.singleton().writeObjectAt(objRef, offsetCopy, compressed); } else { getCounters().noteUnmodifiedReference(); } diff --git a/substratevm/src/com.oracle.svm.core.graal/src/com/oracle/svm/core/graal/snippets/CEntryPointSnippets.java b/substratevm/src/com.oracle.svm.core.graal/src/com/oracle/svm/core/graal/snippets/CEntryPointSnippets.java index 8b4d3dcbad65..66a8db79e1b9 100644 --- a/substratevm/src/com.oracle.svm.core.graal/src/com/oracle/svm/core/graal/snippets/CEntryPointSnippets.java +++ b/substratevm/src/com.oracle.svm.core.graal/src/com/oracle/svm/core/graal/snippets/CEntryPointSnippets.java @@ -64,6 +64,7 @@ import org.graalvm.word.WordFactory; import com.oracle.svm.core.Isolates; +import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.annotate.RestrictHeapAccess; import com.oracle.svm.core.annotate.Uninterruptible; import com.oracle.svm.core.c.function.CEntryPointActions; @@ -87,6 +88,7 @@ import com.oracle.svm.core.thread.JavaThreads; import com.oracle.svm.core.thread.Safepoint; import com.oracle.svm.core.thread.VMThreads; +import com.oracle.svm.core.util.VMError; /** * Snippets for calling from C to Java. See {@link CEntryPointActions} and @@ -107,9 +109,10 @@ public final class CEntryPointSnippets extends SubstrateTemplates implements Sni public static final SubstrateForeignCallDescriptor TEAR_DOWN_ISOLATE = SnippetRuntime.findForeignCall(CEntryPointSnippets.class, "tearDownIsolate", false, LocationIdentity.any()); public static final SubstrateForeignCallDescriptor IS_ATTACHED_MT = SnippetRuntime.findForeignCall(CEntryPointSnippets.class, "isAttachedMT", false, LocationIdentity.any()); public static final SubstrateForeignCallDescriptor FAIL_FATALLY = SnippetRuntime.findForeignCall(CEntryPointSnippets.class, "failFatally", false, LocationIdentity.any()); + public static final SubstrateForeignCallDescriptor VERIFY_ISOLATE_THREAD = SnippetRuntime.findForeignCall(CEntryPointSnippets.class, "verifyIsolateThread", false, LocationIdentity.any()); public static final SubstrateForeignCallDescriptor[] FOREIGN_CALLS = {CREATE_ISOLATE, INITIALIZE_ISOLATE, ATTACH_THREAD, ENTER_ISOLATE_MT, - DETACH_THREAD_MT, REPORT_EXCEPTION, TEAR_DOWN_ISOLATE, IS_ATTACHED_MT, FAIL_FATALLY}; + DETACH_THREAD_MT, REPORT_EXCEPTION, TEAR_DOWN_ISOLATE, IS_ATTACHED_MT, FAIL_FATALLY, VERIFY_ISOLATE_THREAD}; @NodeIntrinsic(value = ForeignCallNode.class) public static native int runtimeCall(@ConstantNodeParameter ForeignCallDescriptor descriptor, CEntryPointCreateIsolateParameters parameters, int vmThreadSize); @@ -253,38 +256,14 @@ public static int detachThreadSnippet() { @SubstrateForeignCallTarget @Uninterruptible(reason = "Thread state going away.") @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not (thread-local) allocate while detaching a thread.") - private static int detachThreadMT(IsolateThread thread) { - int result = CEntryPointErrors.NO_ERROR; - /* - * Make me immune to safepoints (the safepoint mechanism ignores me). We are calling - * functions that are not marked as @Uninterruptible during the detach process. We hold the - * THREAD_MUTEX, so we know that we are not going to be interrupted by a safepoint. But a - * safepoint can already be requested, or our safepoint counter can reach 0 - so it is still - * possible that we enter the safepoint slow path. - */ - VMThreads.StatusSupport.setStatusIgnoreSafepoints(); - - // try-finally because try-with-resources can call interruptible code - VMThreads.THREAD_MUTEX.lockNoTransition(); + private static int detachThreadMT(IsolateThread currentThread) { try { - detachJavaLangThreadMT(thread); - - // clear references to thread to avoid unintended use + VMThreads.detachThread(currentThread); writeCurrentVMThread(VMThreads.nullThread()); - - VMThreads.detachThread(thread); } catch (Throwable t) { - result = CEntryPointErrors.UNCAUGHT_EXCEPTION; - } finally { - VMThreads.THREAD_MUTEX.unlock(); - VMThreads.singleton().freeIsolateThread(thread); + return CEntryPointErrors.UNCAUGHT_EXCEPTION; } - return result; - } - - @Uninterruptible(reason = "For calling interruptible code from uninterruptible code.", callerMustBe = true, mayBeInlined = true, calleeMustBe = false) - private static void detachJavaLangThreadMT(IsolateThread thread) { - JavaThreads.detachThread(thread); + return CEntryPointErrors.NO_ERROR; } @Snippet @@ -369,6 +348,32 @@ public static int enterSnippet(IsolateThread thread) { } if (MultiThreaded.getValue()) { Safepoint.transitionNativeToJava(); + if (runtimeAssertionsEnabled()) { + runtimeCall(VERIFY_ISOLATE_THREAD, thread); + } + } + + return CEntryPointErrors.NO_ERROR; + } + + @Fold + static boolean runtimeAssertionsEnabled() { + return SubstrateOptions.getRuntimeAssertionsForClass(CEntryPointSnippets.class.getName()); + } + + /** + * Verify that the {@link IsolateThread} provided by the user is correct, i.e., the same + * {@link IsolateThread} that would be used by a C entry point providing only the + * {@link Isolate}. This is a slow check and therefore only done when assertions are enabled. + */ + @Uninterruptible(reason = "Thread state not set up yet") + @SubstrateForeignCallTarget + private static int verifyIsolateThread(IsolateThread thread) { + IsolateThread threadFromOS = VMThreads.singleton().findIsolateThreadforCurrentOSThread(); + + if (!thread.equal(threadFromOS)) { + throw VMError.shouldNotReachHere("A call from native code to Java code provided the wrong JNI environment or the wrong IsolateThread. " + + "The JNI environment / IsolateThread is a thread-local data structure and must not be shared between threads."); } return CEntryPointErrors.NO_ERROR; } diff --git a/substratevm/src/com.oracle.svm.core.graal/src/com/oracle/svm/core/graal/snippets/PosixAMD64VaListSnippets.java b/substratevm/src/com.oracle.svm.core.graal/src/com/oracle/svm/core/graal/snippets/PosixAMD64VaListSnippets.java index 15dada9ad5b6..a3c0fa167a78 100644 --- a/substratevm/src/com.oracle.svm.core.graal/src/com/oracle/svm/core/graal/snippets/PosixAMD64VaListSnippets.java +++ b/substratevm/src/com.oracle.svm.core.graal/src/com/oracle/svm/core/graal/snippets/PosixAMD64VaListSnippets.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -40,6 +40,8 @@ import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platform.DARWIN_AMD64; import org.graalvm.nativeimage.Platform.LINUX_AMD64; +import org.graalvm.nativeimage.Platform.DARWIN_JNI_AMD64; +import org.graalvm.nativeimage.Platform.LINUX_JNI_AMD64; import org.graalvm.word.Pointer; import com.oracle.svm.core.annotate.AutomaticFeature; @@ -52,7 +54,8 @@ class PosixAMD64VaListSnippetsFeature implements GraalFeature { @Override public boolean isInConfiguration(IsInConfigurationAccess access) { - return Platform.includedIn(LINUX_AMD64.class) || Platform.includedIn(DARWIN_AMD64.class); + return Platform.includedIn(LINUX_AMD64.class) || Platform.includedIn(DARWIN_AMD64.class) || + Platform.includedIn(LINUX_JNI_AMD64.class) || Platform.includedIn(DARWIN_JNI_AMD64.class); } @Override @@ -71,7 +74,7 @@ public void registerLowerings(RuntimeConfiguration runtimeConfig, OptionValues o * remaining arguments (if any) on the stack. Therefore, a varargs function creating a * {@code va_list} must initially save the contents of the argument registers so that they can be * read from the {@code va_list} in another function. The {@code va_list} structure looks like this: - * + * *

  *   typedef struct {
  *     unsigned int gp_offset;  // offset of the next general-purpose argument in reg_save_area
@@ -80,7 +83,7 @@ public void registerLowerings(RuntimeConfiguration runtimeConfig, OptionValues o
  *     void *reg_save_area;     // start address of the register save area
  *   } va_list[1];
  * 
- * + * * Reading a {@code va_list} requires knowing the types of the arguments. General-purpose values * (integers and pointers) are passed in the six 64-bit registers {@code rdi, rsi, rdx, rcx, r8} and * {@code r9}, which are saved to the start of {@code reg_save_area}. Floating-point values are diff --git a/substratevm/src/com.oracle.svm.core.graal/src/com/oracle/svm/core/graal/snippets/StackOverflowCheckImpl.java b/substratevm/src/com.oracle.svm.core.graal/src/com/oracle/svm/core/graal/snippets/StackOverflowCheckImpl.java index ae11989e37a2..f4a6766b43a9 100644 --- a/substratevm/src/com.oracle.svm.core.graal/src/com/oracle/svm/core/graal/snippets/StackOverflowCheckImpl.java +++ b/substratevm/src/com.oracle.svm.core.graal/src/com/oracle/svm/core/graal/snippets/StackOverflowCheckImpl.java @@ -58,6 +58,7 @@ import org.graalvm.nativeimage.IsolateThread; import org.graalvm.nativeimage.c.function.CFunction; import org.graalvm.word.UnsignedWord; +import org.graalvm.word.WordFactory; import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.annotate.AutomaticFeature; @@ -141,6 +142,19 @@ public void makeYellowZoneAvailable() { stackBoundaryTL.set(stackBoundaryTL.get().subtract(Options.StackYellowZoneSize.getValue())); } yellowZoneStateTL.set(state + 1); + + /* + * Check that after enabling the yellow zone there is actually stack space available again. + * Otherwise we would immediately throw a StackOverflowError when reaching the first + * non-Uninterruptible callee, and then we would recursively end up here again. + */ + UnsignedWord stackBoundary = StackOverflowCheckImpl.stackBoundaryTL.get(); + if (KnownIntrinsics.readStackPointer().belowOrEqual(stackBoundary)) { + throw VMError.shouldNotReachHere("StackOverflowError: Enabling the yellow zone of the stack did not make any stack space available. Possible reasons for that: " + + "1) A call from native code to Java code provided the wrong JNI environment or the wrong IsolateThread; " + + "2) Frames of native code filled the stack, and now there is not even enough stack space left to throw a regular StackOverflowError; " + + "3) An internal VM error occurred."); + } } @Uninterruptible(reason = "Atomically manipulating state of multiple thread local variables.") @@ -168,6 +182,22 @@ public int yellowAndRedZoneSize() { return Options.StackYellowZoneSize.getValue() + Options.StackRedZoneSize.getValue(); } + + @Uninterruptible(reason = "Called by fatal error handling that is uninterruptible.") + @Override + public void disableStackOverflowChecksForFatalError() { + /* + * Setting the boundary to a low value effectively disables the check. We are not using 0 so + * that we can distinguish the value set here from an uninitialized value. + */ + stackBoundaryTL.set(WordFactory.unsigned(1)); + /* + * A random marker value. The actual value does not matter, but having a high value also + * ensures that any future calls to protectYellowZone() do not modify the stack boundary + * again. + */ + yellowZoneStateTL.set(0xfefefefe); + } } @NodeInfo(cycles = NodeCycles.CYCLES_4, size = NodeSize.SIZE_8) diff --git a/substratevm/src/com.oracle.svm.core.jdk9.posix/src/com/oracle/svm/core/jdk9/posix/Target_java_io_FileCleanable.java b/substratevm/src/com.oracle.svm.core.posix.jdk9/src/com/oracle/svm/core/posix/Target_java_io_FileCleanable.java similarity index 96% rename from substratevm/src/com.oracle.svm.core.jdk9.posix/src/com/oracle/svm/core/jdk9/posix/Target_java_io_FileCleanable.java rename to substratevm/src/com.oracle.svm.core.posix.jdk9/src/com/oracle/svm/core/posix/Target_java_io_FileCleanable.java index 1b4a28bf2f75..d7d714189c9a 100644 --- a/substratevm/src/com.oracle.svm.core.jdk9.posix/src/com/oracle/svm/core/jdk9/posix/Target_java_io_FileCleanable.java +++ b/substratevm/src/com.oracle.svm.core.posix.jdk9/src/com/oracle/svm/core/posix/Target_java_io_FileCleanable.java @@ -22,14 +22,13 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package com.oracle.svm.core.jdk9.posix; +package com.oracle.svm.core.posix; import java.io.IOException; import com.oracle.svm.core.annotate.Substitute; import com.oracle.svm.core.annotate.TargetClass; import com.oracle.svm.core.jdk.JDK9OrLater; -import com.oracle.svm.core.posix.PosixUtils; import com.oracle.svm.core.posix.headers.Unistd; @TargetClass(className = "java.io.FileCleanable", onlyWith = JDK9OrLater.class) diff --git a/substratevm/src/com.oracle.svm.core.jdk9.posix/src/com/oracle/svm/core/jdk9/posix/Target_java_lang_ProcessHandleImpl.java b/substratevm/src/com.oracle.svm.core.posix.jdk9/src/com/oracle/svm/core/posix/Target_java_lang_ProcessHandleImpl.java similarity index 95% rename from substratevm/src/com.oracle.svm.core.jdk9.posix/src/com/oracle/svm/core/jdk9/posix/Target_java_lang_ProcessHandleImpl.java rename to substratevm/src/com.oracle.svm.core.posix.jdk9/src/com/oracle/svm/core/posix/Target_java_lang_ProcessHandleImpl.java index f1da9c8620ba..034200ed7de1 100644 --- a/substratevm/src/com.oracle.svm.core.jdk9.posix/src/com/oracle/svm/core/jdk9/posix/Target_java_lang_ProcessHandleImpl.java +++ b/substratevm/src/com.oracle.svm.core.posix.jdk9/src/com/oracle/svm/core/posix/Target_java_lang_ProcessHandleImpl.java @@ -22,14 +22,13 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package com.oracle.svm.core.jdk9.posix; +package com.oracle.svm.core.posix; import org.graalvm.nativeimage.ProcessProperties; import com.oracle.svm.core.annotate.Substitute; import com.oracle.svm.core.annotate.TargetClass; import com.oracle.svm.core.jdk.JDK9OrLater; -import com.oracle.svm.core.posix.Java_lang_Process_Supplement; import com.oracle.svm.core.util.VMError; @TargetClass(className = "java.lang.ProcessHandleImpl", onlyWith = JDK9OrLater.class) diff --git a/substratevm/src/com.oracle.svm.core.jdk9.posix/src/com/oracle/svm/core/jdk9/posix/Target_java_lang_ProcessImpl.java b/substratevm/src/com.oracle.svm.core.posix.jdk9/src/com/oracle/svm/core/posix/Target_java_lang_ProcessImpl.java similarity index 94% rename from substratevm/src/com.oracle.svm.core.jdk9.posix/src/com/oracle/svm/core/jdk9/posix/Target_java_lang_ProcessImpl.java rename to substratevm/src/com.oracle.svm.core.posix.jdk9/src/com/oracle/svm/core/posix/Target_java_lang_ProcessImpl.java index ac2239cc5b68..61de44a991b8 100644 --- a/substratevm/src/com.oracle.svm.core.jdk9.posix/src/com/oracle/svm/core/jdk9/posix/Target_java_lang_ProcessImpl.java +++ b/substratevm/src/com.oracle.svm.core.posix.jdk9/src/com/oracle/svm/core/posix/Target_java_lang_ProcessImpl.java @@ -22,11 +22,12 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package com.oracle.svm.core.jdk9.posix; +package com.oracle.svm.core.posix; import java.io.IOException; import org.graalvm.compiler.serviceprovider.GraalServices; +import org.graalvm.compiler.serviceprovider.JavaVersionUtil; import org.graalvm.nativeimage.Feature; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; @@ -35,9 +36,7 @@ import com.oracle.svm.core.annotate.AutomaticFeature; import com.oracle.svm.core.annotate.Substitute; import com.oracle.svm.core.annotate.TargetClass; -import com.oracle.svm.core.annotate.TargetElement; import com.oracle.svm.core.jdk.JDK9OrLater; -import com.oracle.svm.core.posix.Java_lang_Process_Supplement; import com.oracle.svm.core.util.VMError; @AutomaticFeature @@ -45,7 +44,7 @@ class JavaLangSubstitutionsJDK9OrLaterFeature implements Feature { @Override public boolean isInConfiguration(IsInConfigurationAccess access) { - return !GraalServices.Java8OrEarlier; + return !JavaVersionUtil.Java8OrEarlier; } @Override diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/JavaNetNetUtil.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/JavaNetNetUtil.java index 506eee1bfed9..9ee439b8f513 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/JavaNetNetUtil.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/JavaNetNetUtil.java @@ -34,6 +34,7 @@ import java.nio.channels.DatagramChannel; import org.graalvm.compiler.api.replacements.Fold; +import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.PinnedObject; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; @@ -57,6 +58,7 @@ import com.oracle.svm.core.posix.headers.Poll.pollfd; import com.oracle.svm.core.posix.headers.Socket; import com.oracle.svm.core.posix.headers.Sysctl; +import com.oracle.svm.core.posix.headers.Time; import com.oracle.svm.core.posix.headers.Unistd; import com.oracle.svm.core.posix.headers.darwin.DarwinSysctl; import com.oracle.svm.core.util.VMError; @@ -1326,17 +1328,40 @@ static void NET_SetTrafficClass(Socket.sockaddr him, int trafficClass) { } /* @formatter:on */ - /* Do not re-wrap commented-out code. @formatter:off */ - // 074 #define NET_Timeout JVM_Timeout - static int NET_Timeout(int fd, long timeout) { - return Target_os.timeout(fd, timeout); + /* { Do not re-wrap commented-out code. @formatter:off */ + // 1669 long NET_GetCurrentTime() { + static long NET_GetCurrentTime() { + // 1670 struct timeval time; + Time.timeval time = StackValue.get(Time.timeval.class); + // 1671 gettimeofday(&time, NULL); + Time.gettimeofday(time, WordFactory.nullPointer()); + // 1672 return (time.tv_sec * 1000 + time.tv_usec / 1000); + return (time.tv_sec() * 1000 + time.tv_usec() / 1000); } - /* @formatter:on */ + /* } Do not re-wrap commented-out code. @formatter:on */ + + /* { Do not re-wrap commented-out code. @formatter:off */ + // 1675 int NET_TimeoutWithCurrentTime(int s, long timeout, long currentTime) { + static long NET_TimeoutWithCurrentTime(int s, long timeout, long currentTime) { + // 1676 return NET_Timeout0(s, timeout, currentTime); + return ImageSingletons.lookup(PosixJavaNetClose.class).NET_Timeout0(s, timeout, currentTime); + } + /* } Do not re-wrap commented-out code. @formatter:on */ + + /* { Do not re-wrap commented-out code. @formatter:off */ + // 1679 int NET_Timeout(int s, long timeout) { + static int NET_Timeout(int s, long timeout) { + // 1680 long currentTime = (timeout > 0) ? NET_GetCurrentTime() : 0; + long currentTime = (timeout > 0) ? NET_GetCurrentTime() : 0; + // 1681 return NET_Timeout0(s, timeout, currentTime); + return ImageSingletons.lookup(PosixJavaNetClose.class).NET_Timeout0(s, timeout, currentTime); + } + /* } Do not re-wrap commented-out code. @formatter:on */ /* Do not re-wrap commented-out code. @formatter:off */ // 075 #define NET_Read JVM_Read static int NET_Read(int fd, CCharPointer bufP, int len) { - return (int) Target_os.restartable_read(fd, bufP, len); + return ImageSingletons.lookup(PosixJavaNetClose.class).NET_Read(fd, bufP, len); } /* @formatter:on */ diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/JavaUtilZipSubstitutions.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/JavaUtilZipSubstitutions.java index 81f69ad57dda..ce0cc2fe035e 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/JavaUtilZipSubstitutions.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/JavaUtilZipSubstitutions.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,6 +30,7 @@ import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; import org.graalvm.nativeimage.StackValue; +import org.graalvm.nativeimage.c.function.CLibrary; import org.graalvm.nativeimage.c.struct.SizeOf; import org.graalvm.nativeimage.c.type.CCharPointer; import org.graalvm.nativeimage.c.type.CTypeConversion; @@ -732,5 +733,14 @@ static void doSetDictionary(long addr, CCharPointer bytes, int len) { } /** Dummy class to have a class with the file's name. */ +@Platforms({Platform.LINUX.class, Platform.DARWIN.class}) public final class JavaUtilZipSubstitutions { } + +@Platforms({Platform.LINUX_JNI.class, Platform.DARWIN_JNI.class}) +@CLibrary("zip") +final class JavaUtilZipJNISubstitutions { + + private JavaUtilZipJNISubstitutions() { + } +} diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/Java_lang_Process_Supplement.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/Java_lang_Process_Supplement.java index 898218f764e3..2eb7eabf44ee 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/Java_lang_Process_Supplement.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/Java_lang_Process_Supplement.java @@ -57,7 +57,6 @@ import com.oracle.svm.core.posix.headers.Limits; import com.oracle.svm.core.posix.headers.Unistd; import com.oracle.svm.core.posix.headers.UnistdNoTransitions; -import com.oracle.svm.core.posix.headers.Wait; import com.oracle.svm.core.util.VMError; @Platforms({Platform.LINUX.class, Platform.DARWIN.class}) @@ -424,32 +423,10 @@ public static int forkAndExec( public static int waitForProcessExit0(long pid, boolean reapvalue) { if (reapvalue) { - return waitForProcessExit(Math.toIntExact(pid)); + return PosixUtils.waitForProcessExit(Math.toIntExact(pid)); } else { /* The waitid libc call, currently ... */ throw VMError.unimplemented(); } } - - public static int waitForProcessExit(int ppid) { - CIntPointer statusptr = StackValue.get(CIntPointer.class); - while (Wait.waitpid(ppid, statusptr, 0) < 0) { - if (Errno.errno() == Errno.ECHILD()) { - return 0; - } else if (Errno.errno() == Errno.EINTR()) { - break; - } else { - return -1; - } - } - - int status = statusptr.read(); - if (Wait.WIFEXITED(status)) { - return Wait.WEXITSTATUS(status); - } else if (Wait.WIFSIGNALED(status)) { - // Exited because of signal: return 0x80 + signal number like shells do - return 0x80 + Wait.WTERMSIG(status); - } - return status; - } } diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixInterruptSignalHandler.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixInterruptSignalHandler.java new file mode 100644 index 000000000000..caa9e5fecb9e --- /dev/null +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixInterruptSignalHandler.java @@ -0,0 +1,131 @@ +/* + * Copyright (c) 2013, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.svm.core.posix; + +import java.io.IOException; + +import org.graalvm.nativeimage.Platform; +import org.graalvm.nativeimage.Platforms; +import org.graalvm.nativeimage.StackValue; +import org.graalvm.nativeimage.c.function.CEntryPoint; +import org.graalvm.nativeimage.c.function.CEntryPointLiteral; + +import com.oracle.svm.core.annotate.Uninterruptible; +import com.oracle.svm.core.c.function.CEntryPointOptions; +import com.oracle.svm.core.c.function.CEntryPointOptions.NoEpilogue; +import com.oracle.svm.core.c.function.CEntryPointOptions.NoPrologue; +import com.oracle.svm.core.c.function.CEntryPointOptions.Publish; +import com.oracle.svm.core.posix.headers.Signal; +import com.oracle.svm.core.posix.headers.Signal.SignalDispatcher; + +/** + * A null handler for the signal used to interrupt blocking operations. + * + * The null signal handler for the interrupt signal is called from a static initialization block of + * {@link sun.nio.ch.NativeThread}. I need to set up the signal handler at runtime. The null signal + * handler is also set up, at runtime, when libjvm is loaded, from the init() methods + * of /jdk8u-dev/jdk/src/solaris/native/java/net/bsd_close.c, and + * /jdk8u-dev/jdk/src/solaris/native/java/net/linux_close.c. I consolidate all the calls in an + * {@link #ensureInitialized} method that is called at runtime by the various users. + * + * Adapted from /jdk8u-dev/jdk/src/solaris/native/sun/nio/ch/NativeThread.c, but shared by anyone + * who sends C signals to interrupt blocked operations. + */ +@Platforms({Platform.LINUX.class, Platform.DARWIN.class}) +public class PosixInterruptSignalHandler { + + /** + * + * I am not worried about races, as they will all register the same signal handler. + */ + static boolean initialized = false; + + /* { Do not re-format commented code: @formatter:off */ + // 035 #ifdef __linux__ + // 036 #include + // 037 #include + // 038 /* Also defined in net/linux_close.c */ + // 039 #define INTERRUPT_SIGNAL (__SIGRTMAX - 2) + // 040 #elif __solaris__ + // 041 #include + // 042 #include + // 043 #define INTERRUPT_SIGNAL (SIGRTMAX - 2) + // 044 #elif _ALLBSD_SOURCE + // 045 #include + // 046 #include + // 047 /* Also defined in net/bsd_close.c */ + // 048 #define INTERRUPT_SIGNAL SIGIO + // 049 #else + // 050 #error "missing platform-specific definition here" + // 051 #endif + static final Signal.SignalEnum INTERRUPT_SIGNAL = Signal.SignalEnum.SIGIO; + /* } Do not re-format commented code: @formatter:on */ + + public static Signal.SignalEnum getInterruptSignal() { + return INTERRUPT_SIGNAL; + } + + @CEntryPoint + @CEntryPointOptions(prologue = NoPrologue.class, epilogue = NoEpilogue.class, publishAs = Publish.NotPublished, include = CEntryPointOptions.NotIncludedAutomatically.class) + @Uninterruptible(reason = "Can not check for safepoints because I am running on a borrowed thread.") + private static void nullHandler(@SuppressWarnings("unused") int signalNumber) { + } + + /** The address of the null signal handler. */ + private static final CEntryPointLiteral nullDispatcher = CEntryPointLiteral.create(PosixInterruptSignalHandler.class, "nullHandler", int.class); + + /** Set up a null signal handler for the interrupt signal. */ + static void ensureInitialized() throws IOException { + /* I avoid overwriting any previous signal handler by checking for initialization, first. */ + if (!initialized) { + /* { Do not re-format commented code: @formatter:off */ + // 061 /* Install the null handler for INTERRUPT_SIGNAL. This might overwrite the + // 062 * handler previously installed by java/net/linux_close.c, but that's okay + // 063 * since neither handler actually does anything. We install our own + // 064 * handler here simply out of paranoia; ultimately the two mechanisms + // 065 * should somehow be unified, perhaps within the VM. + // 066 */ + // 067 + // 068 sigset_t ss; + // 069 struct sigaction sa, osa; + Signal.sigaction saPointer = StackValue.get(Signal.sigaction.class); + Signal.sigaction osaPointer = StackValue.get(Signal.sigaction.class); + // 070 sa.sa_handler = nullHandler; + saPointer.sa_handler(PosixInterruptSignalHandler.nullDispatcher.getFunctionPointer()); + // 071 sa.sa_flags = 0; + saPointer.sa_flags(0); + // 072 sigemptyset(&sa.sa_mask); + Signal.sigemptyset(saPointer.sa_mask()); + // 073 if (sigaction(INTERRUPT_SIGNAL, &sa, &osa) < 0) + if (Signal.sigaction(INTERRUPT_SIGNAL, saPointer, osaPointer) < 0) { + // 074 JNU_ThrowIOExceptionWithLastError(env, "sigaction"); + throw PosixUtils.newIOExceptionWithLastError("sigaction"); + } + /* } Do not re-format commented code: @formatter:on */ + initialized = true; + } + } +} diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixJavaIOSubstitutions.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixJavaIOSubstitutions.java index 225e47ad7cee..7c6d52346faf 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixJavaIOSubstitutions.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixJavaIOSubstitutions.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -79,17 +79,21 @@ import static com.oracle.svm.core.posix.headers.Unistd.ftruncate; import static com.oracle.svm.core.posix.headers.Unistd.lseek; +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; import java.io.File; import java.io.FileDescriptor; import java.io.FileNotFoundException; +import java.io.FileInputStream; import java.io.FileOutputStream; +import java.io.PrintStream; import java.io.IOException; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; import java.util.concurrent.locks.ReentrantLock; -import org.graalvm.compiler.serviceprovider.GraalServices; +import org.graalvm.compiler.serviceprovider.JavaVersionUtil; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; import org.graalvm.nativeimage.StackValue; @@ -103,6 +107,7 @@ import com.oracle.svm.core.SubstrateUtil; import com.oracle.svm.core.annotate.Alias; +import com.oracle.svm.core.annotate.AutomaticFeature; import com.oracle.svm.core.annotate.RecomputeFieldValue; import com.oracle.svm.core.annotate.Substitute; import com.oracle.svm.core.annotate.TargetClass; @@ -120,7 +125,60 @@ import com.oracle.svm.core.posix.headers.Time.timeval; import com.oracle.svm.core.posix.headers.Unistd; import com.oracle.svm.core.snippets.KnownIntrinsics; +import com.oracle.svm.core.log.Log; import com.oracle.svm.core.util.VMError; +import com.oracle.svm.hosted.jni.JNIRuntimeAccess; +import org.graalvm.nativeimage.RuntimeClassInitialization; +import org.graalvm.nativeimage.c.function.CLibrary; +import org.graalvm.nativeimage.Feature; + +@Platforms({Platform.LINUX_JNI.class, Platform.DARWIN_JNI.class}) +@AutomaticFeature +@CLibrary("java") +class PosixJavaIOSubstituteFeature implements Feature { + + @Override + public void duringSetup(DuringSetupAccess access) { + // Can't re-initialize the classes list below: + // Error: com.oracle.graal.pointsto.constraints.UnsupportedFeatureException: No instances + // are allowed in the image heap for a class + // that is initialized or reinitialized at image runtime: java.io.XXX. + // + // RuntimeClassInitialization.rerunClassInitialization(access.findClassByName("java.io.FileDescriptor")); + // RuntimeClassInitialization.rerunClassInitialization(access.findClassByName("java.io.FileInputStream")); + // RuntimeClassInitialization.rerunClassInitialization(access.findClassByName("java.io.FileOutputStream")); + // RuntimeClassInitialization.rerunClassInitialization(access.findClassByName("java.io.UnixFileSystem")); + + RuntimeClassInitialization.rerunClassInitialization(access.findClassByName("java.io.RandomAccessFile")); + RuntimeClassInitialization.rerunClassInitialization(access.findClassByName("java.util.zip.ZipFile")); + RuntimeClassInitialization.rerunClassInitialization(access.findClassByName("java.util.zip.Inflater")); + RuntimeClassInitialization.rerunClassInitialization(access.findClassByName("java.util.zip.Deflater")); + } + + @Override + public void beforeAnalysis(BeforeAnalysisAccess access) { + try { + JNIRuntimeAccess.register(java.lang.String.class); + JNIRuntimeAccess.register(access.findClassByName("java.lang.String").getDeclaredConstructor(byte[].class, String.class)); + JNIRuntimeAccess.register(access.findClassByName("java.lang.String").getDeclaredMethod("getBytes", String.class)); + JNIRuntimeAccess.register(java.io.File.class); + JNIRuntimeAccess.register(java.io.File.class.getDeclaredField("path")); + JNIRuntimeAccess.register(java.io.FileOutputStream.class); + JNIRuntimeAccess.register(java.io.FileOutputStream.class.getDeclaredField("fd")); + JNIRuntimeAccess.register(java.io.FileInputStream.class); + JNIRuntimeAccess.register(java.io.FileInputStream.class.getDeclaredField("fd")); + JNIRuntimeAccess.register(java.io.FileDescriptor.class); + JNIRuntimeAccess.register(java.io.FileDescriptor.class.getDeclaredField("fd")); + JNIRuntimeAccess.register(java.io.RandomAccessFile.class); + JNIRuntimeAccess.register(java.io.RandomAccessFile.class.getDeclaredField("fd")); + JNIRuntimeAccess.register(java.io.IOException.class); + JNIRuntimeAccess.register(java.io.IOException.class.getDeclaredConstructor(String.class)); + JNIRuntimeAccess.register(access.findClassByName("java.io.UnixFileSystem")); + } catch (NoSuchFieldException | NoSuchMethodException e) { + VMError.shouldNotReachHere("PosixJavaIOSubstitutionFeature: Error registering class or method: ", e); + } + } +} @TargetClass(className = "java.io.ExpiringCache") @Platforms({Platform.LINUX.class, Platform.DARWIN.class}) @@ -570,7 +628,6 @@ private void close0() throws IOException { private void write(int b, boolean append) throws IOException { PosixUtils.writeSingle(SubstrateUtil.getFileDescriptor(KnownIntrinsics.unsafeCast(this, FileOutputStream.class)), b, append); } - } @TargetClass(java.io.RandomAccessFile.class) @@ -723,7 +780,7 @@ static boolean istty() { // 050 jboolean on) { @Substitute static boolean echo(boolean on) throws IOException { - if (GraalServices.Java8OrEarlier) { + if (JavaVersionUtil.Java8OrEarlier) { /* Initialize the echo shut down hook, once. */ Util_java_io_Console_JDK8OrEarlier.addShutdownHook(); } @@ -818,6 +875,85 @@ public void run() { } } -/** Dummy class to have a class with the file's name. */ +@TargetClass(java.io.FileInputStream.class) +@Platforms({Platform.LINUX_JNI.class, Platform.DARWIN_JNI.class}) +final class Target_java_io_FileInputStream_jni { + + @Alias + static native void initIDs(); +} + +@TargetClass(java.io.RandomAccessFile.class) +@Platforms({Platform.LINUX_JNI.class, Platform.DARWIN_JNI.class}) +final class Target_java_io_RandomAccessFile_jni { + + @Alias + static native void initIDs(); +} + +@TargetClass(java.io.FileDescriptor.class) +@Platforms({Platform.LINUX_JNI.class, Platform.DARWIN_JNI.class}) +final class Target_java_io_FileDescriptor_jni { + + @Alias + static native void initIDs(); + + @Alias static FileDescriptor in; + @Alias static FileDescriptor out; + @Alias static FileDescriptor err; + +} + +@TargetClass(java.io.FileOutputStream.class) +@Platforms({Platform.LINUX_JNI.class, Platform.DARWIN_JNI.class}) +final class Target_java_io_FileOutputStream_jni { + + @Alias + static native void initIDs(); +} + +@TargetClass(className = "java.io.UnixFileSystem") +@Platforms({Platform.LINUX_JNI.class, Platform.DARWIN_JNI.class}) +final class Target_java_io_UnixFileSystem_jni { + + @Alias + static native void initIDs(); +} + +@Platforms({Platform.LINUX.class, Platform.LINUX_JNI.class, Platform.DARWIN.class, Platform.DARWIN_JNI.class}) public final class PosixJavaIOSubstitutions { + + /** Private constructor: No instances. */ + private PosixJavaIOSubstitutions() { + } + + @Platforms({Platform.LINUX_JNI.class, Platform.DARWIN_JNI.class}) + public static boolean initIDs() { + try { + /* + * java.dll is normally loaded by the VM. After loading java.dll, the VM then calls + * initializeSystemClasses which loads zip.dll. + * + * We might want to consider calling System.initializeSystemClasses instead of + * explicitly loading the builtin zip library. + */ + + System.loadLibrary("java"); + + Target_java_io_FileDescriptor_jni.initIDs(); + Target_java_io_FileInputStream_jni.initIDs(); + Target_java_io_FileOutputStream_jni.initIDs(); + Target_java_io_UnixFileSystem_jni.initIDs(); + + System.setIn(new BufferedInputStream(new FileInputStream(FileDescriptor.in))); + System.setOut(new PrintStream(new BufferedOutputStream(new FileOutputStream(FileDescriptor.out), 128), true)); + System.setErr(new PrintStream(new BufferedOutputStream(new FileOutputStream(FileDescriptor.err), 128), true)); + + System.loadLibrary("zip"); + return true; + } catch (UnsatisfiedLinkError e) { + Log.log().string("System.loadLibrary failed, " + e).newline(); + return false; + } + } } diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixJavaLangSubstitutions.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixJavaLangSubstitutions.java index 0e57a370ba12..725f303e50bf 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixJavaLangSubstitutions.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixJavaLangSubstitutions.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,8 +24,6 @@ */ package com.oracle.svm.core.posix; -import static com.oracle.svm.core.posix.headers.Time.gettimeofday; - import java.io.Console; import java.io.IOException; import java.io.InputStream; @@ -43,10 +41,10 @@ import org.graalvm.word.WordFactory; import com.oracle.svm.core.LibCHelper; -import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.annotate.Alias; import com.oracle.svm.core.annotate.Delete; import com.oracle.svm.core.annotate.InjectAccessors; +import com.oracle.svm.core.annotate.AutomaticFeature; import com.oracle.svm.core.annotate.RecomputeFieldValue; import com.oracle.svm.core.annotate.Substitute; import com.oracle.svm.core.annotate.TargetClass; @@ -54,11 +52,23 @@ import com.oracle.svm.core.jdk.JDK8OrEarlier; import com.oracle.svm.core.posix.headers.LibC; import com.oracle.svm.core.posix.headers.Signal; +import com.oracle.svm.core.posix.headers.Time; import com.oracle.svm.core.posix.headers.Time.timeval; import com.oracle.svm.core.posix.headers.Time.timezone; -import com.oracle.svm.core.posix.headers.Unistd; import com.oracle.svm.core.snippets.KnownIntrinsics; import com.oracle.svm.core.util.PointerUtils; +import org.graalvm.nativeimage.Feature; +import org.graalvm.nativeimage.RuntimeClassInitialization; + +@Platforms({Platform.LINUX_JNI.class, Platform.DARWIN_JNI.class}) +@AutomaticFeature +class PosixJavaLangSubstituteFeature implements Feature { + + @Override + public void duringSetup(DuringSetupAccess access) { + RuntimeClassInitialization.rerunClassInitialization(access.findClassByName("java.lang.UNIXProcess")); + } +} @TargetClass(className = "java.lang.ProcessEnvironment") @Platforms({Platform.LINUX.class, Platform.DARWIN.class}) @@ -181,12 +191,12 @@ final class Target_java_lang_ProcessEnvironment_Value { } @TargetClass(className = "java.lang.UNIXProcess", onlyWith = JDK8OrEarlier.class) -@Platforms({Platform.LINUX.class, Platform.DARWIN.class}) +@Platforms({Platform.LINUX_AND_JNI.class, Platform.DARWIN_AND_JNI.class}) final class Target_java_lang_UNIXProcess { // The reaper thread pool and thread groups (currently) confuse the analysis, so we launch // reaper threads individually (with the only difference being that threads are not recycled) - @Delete static Executor processReaperExecutor; + @Platforms({Platform.LINUX.class, Platform.DARWIN.class}) @Delete static Executor processReaperExecutor; @Alias int pid; @Alias OutputStream stdin; @@ -203,6 +213,7 @@ final class Target_java_lang_UNIXProcess { */ @Substitute + @Platforms({Platform.LINUX.class, Platform.DARWIN.class}) @SuppressWarnings({"unused", "static-method"}) int forkAndExec(int mode, byte[] helperpath, byte[] file, @@ -216,6 +227,7 @@ int forkAndExec(int mode, byte[] helperpath, } @Substitute + @Platforms({Platform.LINUX.class, Platform.DARWIN.class}) void initStreams(int[] fds) { Object in = Target_java_lang_ProcessBuilder_NullOutputStream.INSTANCE; if (fds[0] != -1) { @@ -265,12 +277,14 @@ public void run() { } @Substitute + @Platforms({Platform.LINUX.class, Platform.DARWIN.class}) @SuppressWarnings({"static-method"}) int waitForProcessExit(int ppid) { - return Java_lang_Process_Supplement.waitForProcessExit(ppid); + return PosixUtils.waitForProcessExit(ppid); } @Substitute + @Platforms({Platform.LINUX.class, Platform.DARWIN.class}) static void destroyProcess(int ppid, boolean force) { int sig = force ? Signal.SignalEnum.SIGKILL.getCValue() : Signal.SignalEnum.SIGTERM.getCValue(); Signal.kill(ppid, sig); @@ -312,7 +326,7 @@ final class Target_java_lang_ProcessBuilder_NullOutputStream { } @TargetClass(java.lang.System.class) -@Platforms({Platform.LINUX.class, Platform.DARWIN.class}) +@Platforms({Platform.LINUX.class, Platform.LINUX_JNI.class, Platform.DARWIN.class, Platform.DARWIN_JNI.class}) final class Target_java_lang_System { @Alias @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Reset)// @@ -323,7 +337,7 @@ final class Target_java_lang_System { public static long currentTimeMillis() { timeval timeval = StackValue.get(timeval.class); timezone timezone = WordFactory.nullPointer(); - gettimeofday(timeval, timezone); + Time.gettimeofday(timeval, timezone); return timeval.tv_sec() * 1_000L + timeval.tv_usec() / 1_000L; } } @@ -338,26 +352,21 @@ static void halt0(int status) { } } -@TargetClass(java.lang.Runtime.class) -@Platforms({Platform.LINUX.class, Platform.DARWIN.class}) -@SuppressWarnings({"static-method"}) -final class Target_java_lang_Runtime { - - @Substitute - private int availableProcessors() { - if (SubstrateOptions.MultiThreaded.getValue()) { - return (int) Unistd.sysconf(Unistd._SC_NPROCESSORS_ONLN()); - } else { - return 1; - } - } -} - /** Dummy class to have a class with the file's name. */ -@Platforms({Platform.LINUX.class, Platform.DARWIN.class}) +@Platforms({Platform.LINUX_AND_JNI.class, Platform.DARWIN_AND_JNI.class}) public final class PosixJavaLangSubstitutions { /** Private constructor: No instances. */ private PosixJavaLangSubstitutions() { } + + @Platforms({Platform.LINUX_JNI.class, Platform.DARWIN_JNI.class}) + public static boolean initIDs() { + // The JDK uses posix_spawn on the Mac to launch executables. + // This requires a separate process "jspawnhelper" which we + // don't want to have to rely on. Force the use of FORK on + // Linux and Mac. + System.setProperty("jdk.lang.Process.launchMechanism", "FORK"); + return true; + } } diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixJavaNIOSubstitutions.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixJavaNIOSubstitutions.java index ef7572325d55..1ede2380ef3e 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixJavaNIOSubstitutions.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixJavaNIOSubstitutions.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -117,13 +117,14 @@ import java.util.function.Predicate; import org.graalvm.compiler.word.ObjectAccess; +import org.graalvm.nativeimage.Feature; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; +import org.graalvm.nativeimage.RuntimeClassInitialization; import org.graalvm.nativeimage.StackValue; -import org.graalvm.nativeimage.c.function.CEntryPoint; -import org.graalvm.nativeimage.c.function.CEntryPointLiteral; import org.graalvm.nativeimage.c.function.CFunctionPointer; import org.graalvm.nativeimage.c.function.InvokeCFunctionPointer; +import org.graalvm.nativeimage.c.function.CLibrary; import org.graalvm.nativeimage.c.struct.SizeOf; import org.graalvm.nativeimage.c.type.CCharPointer; import org.graalvm.nativeimage.c.type.CIntPointer; @@ -141,7 +142,9 @@ import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.SubstrateUtil; +import com.oracle.svm.core.OS; import com.oracle.svm.core.annotate.Alias; +import com.oracle.svm.core.annotate.AutomaticFeature; import com.oracle.svm.core.annotate.Delete; import com.oracle.svm.core.annotate.Inject; import com.oracle.svm.core.annotate.InjectAccessors; @@ -149,11 +152,6 @@ import com.oracle.svm.core.annotate.Substitute; import com.oracle.svm.core.annotate.TargetClass; import com.oracle.svm.core.annotate.TargetElement; -import com.oracle.svm.core.annotate.Uninterruptible; -import com.oracle.svm.core.c.function.CEntryPointOptions; -import com.oracle.svm.core.c.function.CEntryPointOptions.NoEpilogue; -import com.oracle.svm.core.c.function.CEntryPointOptions.NoPrologue; -import com.oracle.svm.core.c.function.CEntryPointOptions.Publish; import com.oracle.svm.core.config.ConfigurationValues; import com.oracle.svm.core.jdk.JDK8OrEarlier; import com.oracle.svm.core.jdk.JDK9OrLater; @@ -177,8 +175,6 @@ import com.oracle.svm.core.posix.headers.Pwd.passwd; import com.oracle.svm.core.posix.headers.Pwd.passwdPointer; import com.oracle.svm.core.posix.headers.Resource.rlimit; -import com.oracle.svm.core.posix.headers.Signal; -import com.oracle.svm.core.posix.headers.Signal.SignalDispatcher; import com.oracle.svm.core.posix.headers.Socket; import com.oracle.svm.core.posix.headers.Socket.sockaddr; import com.oracle.svm.core.posix.headers.Stat; @@ -194,9 +190,89 @@ import com.oracle.svm.core.posix.headers.linux.Mntent; import com.oracle.svm.core.posix.headers.linux.Mntent.mntent; import com.oracle.svm.core.snippets.KnownIntrinsics; +import com.oracle.svm.core.util.VMError; +import com.oracle.svm.hosted.jni.JNIRuntimeAccess; import jdk.vm.ci.meta.JavaKind; +@Platforms({Platform.LINUX_JNI.class, Platform.DARWIN_JNI.class}) +@AutomaticFeature +@CLibrary("nio") +class PosixJavaNIOSubstituteFeature implements Feature { + + @Override + public void duringSetup(DuringSetupAccess access) { + RuntimeClassInitialization.rerunClassInitialization(access.findClassByName("sun.nio.ch.FileKey")); + RuntimeClassInitialization.rerunClassInitialization(access.findClassByName("sun.nio.fs.UnixNativeDispatcher")); + RuntimeClassInitialization.rerunClassInitialization(access.findClassByName("sun.nio.ch.ServerSocketChannelImpl")); + RuntimeClassInitialization.rerunClassInitialization(access.findClassByName("sun.nio.ch.IOUtil")); + RuntimeClassInitialization.rerunClassInitialization(access.findClassByName("sun.nio.ch.FileChannelImpl")); + RuntimeClassInitialization.rerunClassInitialization(access.findClassByName("java.nio.file.FileSystems")); + } + + @Override + public void beforeAnalysis(BeforeAnalysisAccess access) { + try { + if (OS.getCurrent() == OS.DARWIN || OS.getCurrent() == OS.LINUX) { + JNIRuntimeAccess.register(access.findClassByName("sun.nio.fs.UnixFileAttributes")); + JNIRuntimeAccess.register(access.findClassByName("sun.nio.fs.UnixFileAttributes").getDeclaredField("st_mode")); + JNIRuntimeAccess.register(access.findClassByName("sun.nio.fs.UnixFileAttributes").getDeclaredField("st_ino")); + JNIRuntimeAccess.register(access.findClassByName("sun.nio.fs.UnixFileAttributes").getDeclaredField("st_dev")); + JNIRuntimeAccess.register(access.findClassByName("sun.nio.fs.UnixFileAttributes").getDeclaredField("st_rdev")); + JNIRuntimeAccess.register(access.findClassByName("sun.nio.fs.UnixFileAttributes").getDeclaredField("st_nlink")); + JNIRuntimeAccess.register(access.findClassByName("sun.nio.fs.UnixFileAttributes").getDeclaredField("st_uid")); + JNIRuntimeAccess.register(access.findClassByName("sun.nio.fs.UnixFileAttributes").getDeclaredField("st_gid")); + JNIRuntimeAccess.register(access.findClassByName("sun.nio.fs.UnixFileAttributes").getDeclaredField("st_size")); + JNIRuntimeAccess.register(access.findClassByName("sun.nio.fs.UnixFileAttributes").getDeclaredField("st_atime_sec")); + JNIRuntimeAccess.register(access.findClassByName("sun.nio.fs.UnixFileAttributes").getDeclaredField("st_atime_nsec")); + JNIRuntimeAccess.register(access.findClassByName("sun.nio.fs.UnixFileAttributes").getDeclaredField("st_mtime_sec")); + JNIRuntimeAccess.register(access.findClassByName("sun.nio.fs.UnixFileAttributes").getDeclaredField("st_mtime_nsec")); + JNIRuntimeAccess.register(access.findClassByName("sun.nio.fs.UnixFileAttributes").getDeclaredField("st_ctime_sec")); + JNIRuntimeAccess.register(access.findClassByName("sun.nio.fs.UnixFileAttributes").getDeclaredField("st_ctime_nsec")); + + // Only needed ifdef _DARWIN_FEATURE_64_BIT_INODE + JNIRuntimeAccess.register(access.findClassByName("sun.nio.fs.UnixFileAttributes").getDeclaredField("st_birthtime_sec")); + + JNIRuntimeAccess.register(access.findClassByName("sun.nio.fs.UnixFileStoreAttributes")); + JNIRuntimeAccess.register(access.findClassByName("sun.nio.fs.UnixFileStoreAttributes").getDeclaredField("f_frsize")); + JNIRuntimeAccess.register(access.findClassByName("sun.nio.fs.UnixFileStoreAttributes").getDeclaredField("f_blocks")); + JNIRuntimeAccess.register(access.findClassByName("sun.nio.fs.UnixFileStoreAttributes").getDeclaredField("f_bfree")); + JNIRuntimeAccess.register(access.findClassByName("sun.nio.fs.UnixFileStoreAttributes").getDeclaredField("f_bavail")); + + JNIRuntimeAccess.register(access.findClassByName("sun.nio.fs.UnixMountEntry")); + JNIRuntimeAccess.register(access.findClassByName("sun.nio.fs.UnixMountEntry").getDeclaredField("name")); + JNIRuntimeAccess.register(access.findClassByName("sun.nio.fs.UnixMountEntry").getDeclaredField("dir")); + JNIRuntimeAccess.register(access.findClassByName("sun.nio.fs.UnixMountEntry").getDeclaredField("fstype")); + JNIRuntimeAccess.register(access.findClassByName("sun.nio.fs.UnixMountEntry").getDeclaredField("opts")); + JNIRuntimeAccess.register(access.findClassByName("sun.nio.fs.UnixMountEntry").getDeclaredField("dev")); + + JNIRuntimeAccess.register(access.findClassByName("sun.nio.ch.FileKey")); + JNIRuntimeAccess.register(access.findClassByName("sun.nio.ch.FileKey").getDeclaredField("st_dev")); + JNIRuntimeAccess.register(access.findClassByName("sun.nio.ch.FileKey").getDeclaredField("st_ino")); + + JNIRuntimeAccess.register(access.findClassByName("sun.nio.ch.FileChannelImpl")); + JNIRuntimeAccess.register(access.findClassByName("sun.nio.ch.FileChannelImpl").getDeclaredField("fd")); + + JNIRuntimeAccess.register(access.findClassByName("sun.nio.ch.DatagramChannelImpl")); + JNIRuntimeAccess.register(access.findClassByName("sun.nio.ch.DatagramChannelImpl").getDeclaredField("sender")); + JNIRuntimeAccess.register(access.findClassByName("sun.nio.ch.DatagramChannelImpl").getDeclaredField("cachedSenderInetAddress")); + JNIRuntimeAccess.register(access.findClassByName("sun.nio.ch.DatagramChannelImpl").getDeclaredField("cachedSenderPort")); + + JNIRuntimeAccess.register(access.findClassByName("java.lang.Exception")); + JNIRuntimeAccess.register(access.findClassByName("java.lang.Exception").getDeclaredConstructor()); + JNIRuntimeAccess.register(access.findClassByName("sun.nio.fs.UnixException")); + JNIRuntimeAccess.register(access.findClassByName("sun.nio.fs.UnixException").getDeclaredConstructor(int.class)); + JNIRuntimeAccess.register(access.findClassByName("sun.nio.fs.UnixException").getDeclaredConstructor(String.class)); + } + + } catch (NoSuchFieldException | NoSuchMethodException e) { + VMError.shouldNotReachHere("JNIRuntimeAccess.register failed: ", e); + + } + } +} + +@Platforms({Platform.LINUX.class, Platform.DARWIN.class}) public final class PosixJavaNIOSubstitutions { // Checkstyle: stop @@ -348,14 +424,13 @@ static long current() { @Substitute private static void signal(long thread) throws IOException { if (SubstrateOptions.MultiThreaded.getValue()) { - Util_sun_nio_ch_NativeThread.ensureInitialized(); // 090 int ret; int ret; // 091 #ifdef __solaris__ // 092 ret = thr_kill((thread_t)thread, INTERRUPT_SIGNAL); // 093 #else // 094 ret = pthread_kill((pthread_t)thread, INTERRUPT_SIGNAL); - ret = Pthread.pthread_kill(WordFactory.pointer(thread), Util_sun_nio_ch_NativeThread.INTERRUPT_SIGNAL); + ret = Pthread.pthread_kill(WordFactory.pointer(thread), PosixInterruptSignalHandler.INTERRUPT_SIGNAL); // 095 #endif // 096 if (ret != 0) if (ret != 0) { @@ -365,90 +440,31 @@ private static void signal(long thread) throws IOException { } } - /** See {@link Util_sun_nio_ch_NativeThread#ensureInitialized()}. */ @Substitute - private static void init() { - throw new InternalError("init() is only called from static initializers, so not reachable in Substrate VM"); + // 58 JNIEXPORT void JNICALL + // 59 Java_sun_nio_ch_NativeThread_init(JNIEnv *env, jclass cl) + // 60 { + private static /* native */ void init() throws IOException { + // 61 /* Install the null handler for INTERRUPT_SIGNAL. This might overwrite the + // 62 * handler previously installed by java/net/linux_close.c, but that's okay + // 63 * since neither handler actually does anything. We install our own + // 64 * handler here simply out of paranoia; ultimately the two mechanisms + // 65 * should somehow be unified, perhaps within the VM. + // 66 */ + // 67 + // 68 sigset_t ss; + // 69 struct sigaction sa, osa; + // 70 sa.sa_handler = nullHandler; + // 71 sa.sa_flags = 0; + // 72 sigemptyset(&sa.sa_mask); + // 73 if (sigaction(INTERRUPT_SIGNAL, &sa, &osa) < 0) + // 74 JNU_ThrowIOExceptionWithLastError(env, "sigaction"); + PosixInterruptSignalHandler.ensureInitialized(); } /* } Do not re-format commented code: @formatter:on */ } - static final class Util_sun_nio_ch_NativeThread { - - /** - * The initialization of {@link sun.nio.ch.NativeThread} is in a static block that gets run - * during image building. I need to initialize the signal handler at run time. I am not - * worried about races, as they will all register the same signal handler. - */ - static boolean initialized = false; - - /* { Do not re-format commented code: @formatter:off */ - // 035 #ifdef __linux__ - // 036 #include - // 037 #include - // 038 /* Also defined in net/linux_close.c */ - // 039 #define INTERRUPT_SIGNAL (__SIGRTMAX - 2) - // 040 #elif __solaris__ - // 041 #include - // 042 #include - // 043 #define INTERRUPT_SIGNAL (SIGRTMAX - 2) - // 044 #elif _ALLBSD_SOURCE - // 045 #include - // 046 #include - // 047 /* Also defined in net/bsd_close.c */ - // 048 #define INTERRUPT_SIGNAL SIGIO - // 049 #else - // 050 #error "missing platform-specific definition here" - // 051 #endif - static final Signal.SignalEnum INTERRUPT_SIGNAL = Signal.SignalEnum.SIGIO; - /* } Do not re-format commented code: @formatter:on */ - - /* Translated from jdk/src/solaris/native/sun/nio/ch/NativeThread.c?v=Java_1.8.0_40_b10. */ - // 053 static void - // 054 nullHandler(int sig) - // 055 { - // 056 } - @CEntryPoint - @CEntryPointOptions(prologue = NoPrologue.class, epilogue = NoEpilogue.class, publishAs = Publish.NotPublished, include = CEntryPointOptions.NotIncludedAutomatically.class) - @Uninterruptible(reason = "Can not check for safepoints because I am running on a borrowed thread.") - private static void nullHandler(@SuppressWarnings("unused") int signalNumber) { - } - - /** The address of the null signal handler. */ - private static final CEntryPointLiteral nullDispatcher = CEntryPointLiteral.create(Util_sun_nio_ch_NativeThread.class, "nullHandler", int.class); - - static void ensureInitialized() throws IOException { - if (!initialized) { - /* { Do not re-format commented code: @formatter:off */ - // 061 /* Install the null handler for INTERRUPT_SIGNAL. This might overwrite the - // 062 * handler previously installed by java/net/linux_close.c, but that's okay - // 063 * since neither handler actually does anything. We install our own - // 064 * handler here simply out of paranoia; ultimately the two mechanisms - // 065 * should somehow be unified, perhaps within the VM. - // 066 */ - // 067 - // 068 sigset_t ss; - // 069 struct sigaction sa, osa; - Signal.sigaction saPointer = StackValue.get(Signal.sigaction.class); - Signal.sigaction osaPointer = StackValue.get(Signal.sigaction.class); - // 070 sa.sa_handler = nullHandler; - saPointer.sa_handler(Util_sun_nio_ch_NativeThread.nullDispatcher.getFunctionPointer()); - // 071 sa.sa_flags = 0; - saPointer.sa_flags(0); - // 072 sigemptyset(&sa.sa_mask); - Signal.sigemptyset(saPointer.sa_mask()); - // 073 if (sigaction(INTERRUPT_SIGNAL, &sa, &osa) < 0) - if (Signal.sigaction(INTERRUPT_SIGNAL, saPointer, osaPointer) < 0) { - // 074 JNU_ThrowIOExceptionWithLastError(env, "sigaction"); - throw PosixUtils.newIOExceptionWithLastError("sigaction"); - } - /* } Do not re-format commented code: @formatter:on */ - initialized = true; - } - } - } - /* * Converted from JDK 7 update 40 C source file: src/solaris/native/sun/nio/ch/IOUtil.c */ @@ -1552,7 +1568,6 @@ private static void init() { } @Substitute - @TargetElement(onlyWith = JDK9OrLater.class) private static long seek0(FileDescriptor fd, long offset) throws IOException { int f = fdval(fd); long result = 0; @@ -2897,20 +2912,6 @@ private static int unmap0(long address, long len) throws IOException { return handle(munmap(a, WordFactory.unsigned(len)), "Unmap failed"); } - @Substitute - @TargetElement(onlyWith = JDK8OrEarlier.class) - private long position0(FileDescriptor fdo, long offset) throws IOException { - int fd = fdval(fdo); - long result = 0; - - if (offset < 0) { - result = lseek(fd, WordFactory.zero(), SEEK_CUR()).rawValue(); - } else { - result = lseek(fd, WordFactory.signed(offset), SEEK_SET()).rawValue(); - } - return handle(result, "Position failed"); - } - // @Substitute // private static void close0(FileDescriptor fdo) throws IOException { // int fd = fdval(fdo); @@ -4041,3 +4042,16 @@ interface magic_close_func extends CFunctionPointer { /* } Do not format quoted code: @formatter:on */ } } + +/** + * Re-run the class initialization for {@code sun.nio.ch.NativeThread} so that it ensures that the + * interrupt signal handler is initialized at runtime. + */ +@AutomaticFeature +final class SunNioChNativeThreadFeature implements Feature { + + @Override + public void duringSetup(DuringSetupAccess access) { + RuntimeClassInitialization.rerunClassInitialization(access.findClassByName("sun.nio.ch.NativeThread")); + } +} diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixJavaNetClose.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixJavaNetClose.java new file mode 100644 index 000000000000..044e886a9f90 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixJavaNetClose.java @@ -0,0 +1,356 @@ +/* + * Copyright (c) 2019, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.svm.core.posix; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.IntSupplier; + +import org.graalvm.nativeimage.Platform; +import org.graalvm.nativeimage.Platforms; +import org.graalvm.nativeimage.c.type.CIntPointer; +import org.graalvm.word.PointerBase; +import org.graalvm.word.WordFactory; + +import com.oracle.svm.core.posix.headers.Errno; +import com.oracle.svm.core.posix.headers.Poll; +import com.oracle.svm.core.posix.headers.Socket; +import com.oracle.svm.core.posix.headers.Uio; + +/** + * Interrupt blocking operations when a file descriptor is closed. + * + * This is a translation of the mechanism in /jdk8u-dev/jdk/src/solaris/native/java/net/bsd_close.c + * and /jdk8u-dev/jdk/src/solaris/native/java/net/linux_close.c that implements interruption of + * blocking operations. Not done as a direct translation of the methods, which maintain lists of + * pthreads, and file descriptors, etc. Rather the mechanism is implemented in Java using Java + * threads and Thread.interrupt. + */ +@Platforms({Platform.LINUX.class, Platform.DARWIN.class}) +public abstract class PosixJavaNetClose { + + /** + * A ThreadEntry is created for each blocking operation, to record the thread that is blocking, + * and whether the operation has been interrupted. + */ + protected static class ThreadEntry { + + private Thread thread; + private boolean intr; + + /** Fields are initialized in {@link PosixJavaNetClose#startOp(FdEntry, ThreadEntry)}. */ + public ThreadEntry() { + /* Nothing to do. */ + } + + public Thread getThread() { + return thread; + } + + public void setThread(Thread value) { + thread = value; + } + + public boolean getIntr() { + return intr; + } + + public void setIntr(boolean value) { + intr = value; + } + } + + /** + * An FdEntry is created for each file descriptor that is used in a blocking operation, to hold + * a list of threads that are blocked on the file descriptor. Once created, a FdEntry persists + * past the end of the operation, and is reused if the same file descriptor is re-opened. + */ + protected static class FdEntry { + + /** A list of threads blocking on a file descriptor. */ + private final List threadList; + + public FdEntry() { + /* Most file descriptors have one thread blocked on them. */ + threadList = new ArrayList<>(2); + } + + public List getThreadList() { + return threadList; + } + } + + /** + * A map from file descriptors to a list of threads that are blocked on that file descriptor. + */ + private static final Map fdTable = new ConcurrentHashMap<>(); + + /** + * Many threads will be looking up entries in the map. After a while it should be rare that a + * new entry will need to be created, as an entry for a particular file descriptor persists in + * the map, but adding a new entry to the map has to be atomic. + */ + protected static FdEntry getFdEntry(int fd) { + /* `fd` is auto-boxed via Integer.valueOf. */ + return fdTable.computeIfAbsent(fd, unused -> new FdEntry()); + } + + /** + * { @formatter:off + * Start a blocking operation :- + * Insert thread onto thread list for the fd. + * } @formatter:on + */ + protected static void startOp(FdEntry fdEntry, ThreadEntry self) { + self.setThread(Thread.currentThread()); + self.setIntr(false); + /* { Allow synchronization: Checkstyle: stop. */ + synchronized (fdEntry) { + /* } Allow synchronization: Checkstyle: resume. */ + fdEntry.getThreadList().add(self); + } + } + + /** + * { @formatter:off + * End a blocking operation :- + * Remove thread from thread list for the fd. + * If fd has been interrupted then set errno to EBADF. + * } @formatter:on + */ + protected static void endOp(FdEntry fdEntry, ThreadEntry self) { + boolean badErrno = false; + /* { Allow synchronization: Checkstyle: stop. */ + synchronized (fdEntry) { + /* } Allow synchronization: Checkstyle: resume. */ + fdEntry.getThreadList().remove(self); + if (self.getIntr()) { + badErrno = true; + } + } + if (badErrno) { + Errno.set_errno(Errno.EBADF()); + } + } + + /* + * Declarations of methods whose implementation differs by platform. These method are + * implemented in, e.g., DarwinJavaNetCloseSupport and LinuxJavaNetCloseSupport. + */ + + protected abstract int closefd(int fd1, int fd2); + + /* + * Implementations that are identical between bsd_close.c and linux_close.c. Where line numbers + * are given they are from bsd_close.c. + */ + + /* { Allow method names with underscores: Checkstyle: stop */ + + /* { Do not re-wrap commented-out code. @formatter:off */ + // 268 /************** Basic I/O operations here ***************/ + // 269 + // 270 /* + // 271 * Macro to perform a blocking IO operation. Restarts + // 272 * automatically if interrupted by signal (other than + // 273 * our wakeup signal) + // 274 */ + // 275 #define BLOCKING_IO_RETURN_INT(FD, FUNC) { \ + // 276 int ret; \ + // 277 threadEntry_t self; \ + // 278 fdEntry_t *fdEntry = getFdEntry(FD); \ + // 279 if (fdEntry == NULL) { \ + // 280 errno = EBADF; \ + // 281 return -1; \ + // 282 } \ + // 283 do { \ + // 284 startOp(fdEntry, &self); \ + // 285 ret = FUNC; \ + // 286 endOp(fdEntry, &self); \ + // 287 } while (ret == -1 && errno == EINTR); \ + // 288 return ret; \ + // 289 } + /** + * The differences from the above macro: + * (1) Passing an IntSupplier instead of the text of a function application. + * The IntSupplier must capture all the state that it needs. + * (2) Instead of a stack-value threadEntry_t, a new ThreadEntry is allocated + * on the heap. + */ + protected int BLOCKING_IO_RETURN_INT(int FD, IntSupplier FUNC) { + int ret; + final FdEntry fdEntry = getFdEntry(FD); + if (fdEntry == null) { + Errno.set_errno(Errno.EBADF()); + return -1; + } + final ThreadEntry self = new ThreadEntry(); + do { + startOp(fdEntry, self); + ret = FUNC.getAsInt(); + endOp(fdEntry, self); + } while ((ret == -1) && Errno.errno() == Errno.EINTR()); + return ret; + } + /* } Do not re-wrap commented-out code. @formatter:on */ + + /* { Do not re-wrap commented-out code. @formatter:off */ + // 246 /* + // 247 * Wrapper for dup2 - same semantics as dup2 system call except + // 248 * that any threads blocked in an I/O system call on fd2 will be + // 249 * preempted and return -1/EBADF; + // 250 */ + // 251 int NET_Dup2(int fd, int fd2) { + public int NET_Dup2(int fd, int fd2) { + // 252 if (fd < 0) { + if (fd < 0 ) { + // 253 errno = EBADF; + Errno.set_errno(Errno.EBADF()); + // 254 return -1; + return -1; + } + // 256 return closefd(fd, fd2); + return closefd(fd, fd2); + } + /* } Do not re-wrap commented-out code. @formatter:on */ + + /* { Do not re-wrap commented-out code. @formatter:off */ + // 259 /* + // 260 * Wrapper for close - same semantics as close system call + // 261 * except that any threads blocked in an I/O on fd will be + // 262 * preempted and the I/O system call will return -1/EBADF. + // 263 */ + // 264 int NET_SocketClose(int fd) { + public int NET_SocketClose(int fd) { + // 265 return closefd(-1, fd); + return closefd(-1, fd); + } + /* } Do not re-wrap commented-out code. @formatter:on */ + + // 291 int NET_Read(int s, void* buf, size_t len) { + public int NET_Read(int s, PointerBase buf, long len) { + // 292 BLOCKING_IO_RETURN_INT( s, recv(s, buf, len, 0) ); + return BLOCKING_IO_RETURN_INT(s, () -> { + return (int) (Socket.recv(s, buf, WordFactory.unsigned(len), 0).rawValue()); + }); + } + + // 295 int NET_NonBlockingRead(int s, void* buf, size_t len) { + public int NET_NonBlockingRead(int s, PointerBase buf, long len) { + // 296 BLOCKING_IO_RETURN_INT( s, recv(s, buf, len, MSG_DONTWAIT)); + return BLOCKING_IO_RETURN_INT(s, () -> { + return (int) (Socket.recv(s, buf, WordFactory.unsigned(len), Socket.MSG_DONTWAIT()).rawValue()); + }); + } + + // 299 int NET_ReadV(int s, const struct iovec * vector, int count) { + public int NET_ReadV(int s, Uio.iovec vector, int count) { + // 300 BLOCKING_IO_RETURN_INT( s, readv(s, vector, count) ); + return BLOCKING_IO_RETURN_INT(s, () -> { + return (int) (Uio.readv(s, vector, count).rawValue()); + }); + } + + /* { Do not re-wrap commented-out code. @formatter:off */ + // 303 int NET_RecvFrom(int s, void *buf, int len, unsigned int flags, + // 304 struct sockaddr *from, int *fromlen) { + public int NET_RecvFrom(int s, PointerBase buf, int len, int flags, Socket.sockaddr from, CIntPointer fromlen) { + // 305 /* casting int *fromlen -> socklen_t* Both are ints */ + // 306 BLOCKING_IO_RETURN_INT( s, recvfrom(s, buf, len, flags, from, (socklen_t *)fromlen) + // ); + return BLOCKING_IO_RETURN_INT(s, () -> { + return (int) (Socket.recvfrom(s, buf, WordFactory.unsigned(len), flags, from, fromlen).rawValue()); + }); + } + /* } Do not re-wrap commented-out code. @formatter:on */ + + // 309 int NET_Send(int s, void *msg, int len, unsigned int flags) { + public int NET_Send(int s, PointerBase msg, int len, int flags) { + // 310 BLOCKING_IO_RETURN_INT( s, send(s, msg, len, flags) ); + return BLOCKING_IO_RETURN_INT(s, () -> { + return (int) (Socket.send(s, msg, WordFactory.unsigned(len), flags).rawValue()); + }); + } + + // 313 int NET_WriteV(int s, const struct iovec * vector, int count) { + public int NET_WriteV(int s, Uio.iovec vector, int count) { + // 314 BLOCKING_IO_RETURN_INT( s, writev(s, vector, count) ); + return BLOCKING_IO_RETURN_INT(s, () -> { + return (int) (Uio.writev(s, vector, count).rawValue()); + }); + } + + /* { Do not re-wrap commented-out code. @formatter:off */ + // 317 int NET_SendTo(int s, const void *msg, int len, unsigned int + // 318 flags, const struct sockaddr *to, int tolen) { + public int NET_SendTo(int s, PointerBase msg, int len, int flags, Socket.sockaddr to, int tolen) { + // 319 BLOCKING_IO_RETURN_INT( s, sendto(s, msg, len, flags, to, tolen) ); + return BLOCKING_IO_RETURN_INT(s, () -> { + return (int) (Socket.sendto(s, msg, WordFactory.unsigned(len), flags, to, tolen).rawValue()); + }); + } + /* } Do not re-wrap commented-out code. @formatter:on */ + + /* { Do not re-wrap commented-out code. @formatter:off */ + // 330 int NET_Connect(int s, struct sockaddr *addr, int addrlen) { + public int NET_Connect(int s, Socket.sockaddr addr, int addrlen) { + // 331 BLOCKING_IO_RETURN_INT( s, connect(s, addr, addrlen) ); + return BLOCKING_IO_RETURN_INT(s, () -> { + return Socket.connect(s, addr, addrlen); + }); + } + /* } Do not re-wrap commented-out code. @formatter:on */ + + /* { Do not re-wrap commented-out code. @formatter:off */ + // 334 #ifndef USE_SELECT + /* USE_SELECT seems not to be defined on Darwin or Linux. */ + // 335 int NET_Poll(struct pollfd *ufds, unsigned int nfds, int timeout) { + public int NET_Poll(Poll.pollfd ufds, int nfds, int timeout) { + /* ufds is a C array of Poll.pollfd instances. */ + // 336 BLOCKING_IO_RETURN_INT( ufds[0].fd, poll(ufds, nfds, timeout) ); + return BLOCKING_IO_RETURN_INT(ufds.fd(), () -> { + return Poll.poll(ufds, nfds, timeout); + }); + } + // 338 #else + // 339 int NET_Select(int s, fd_set *readfds, fd_set *writefds, + // 340 fd_set *exceptfds, struct timeval *timeout) { + // 341 BLOCKING_IO_RETURN_INT( s-1, + // 342 select(s, readfds, writefds, exceptfds, timeout) ); + // 343 } + // 344 #endif + /* } Do not re-wrap commented-out code. @formatter:on */ + + // 52 extern int NET_Timeout0(int s, long timeout, long currentTime); + public abstract int NET_Timeout0(int s, long timeout, long currentTime); + + // 65 extern int NET_Accept(int s, struct sockaddr *addr, int *addrlen); + public abstract int NET_Accept(int s, Socket.sockaddr addr, CIntPointer addrlen); + + /* } Do not reformat commented out C code: @formatter:on */ + /* } Allow method names with underscores: Checkstyle: resume */ +} diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixJavaNetSubstitutions.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixJavaNetSubstitutions.java index 1ec785155b5d..70fc8ded97fc 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixJavaNetSubstitutions.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixJavaNetSubstitutions.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -48,7 +48,7 @@ import java.util.HashMap; import java.util.LinkedHashMap; -import org.graalvm.compiler.serviceprovider.GraalServices; +import org.graalvm.compiler.serviceprovider.JavaVersionUtil; import org.graalvm.nativeimage.Feature; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.PinnedObject; @@ -90,13 +90,190 @@ import com.oracle.svm.core.snippets.KnownIntrinsics; import com.oracle.svm.core.util.Utf8; import com.oracle.svm.core.util.VMError; +import com.oracle.svm.hosted.jni.JNIRuntimeAccess; +import org.graalvm.nativeimage.RuntimeReflection; +import org.graalvm.nativeimage.c.function.CLibrary; -/** Dummy class to have a class with the file's name. */ +@Platforms({Platform.LINUX_JNI.class, Platform.DARWIN_JNI.class}) +@AutomaticFeature +@CLibrary("net") +class PosixJavaNetSubstitutionsFeature implements Feature { + + @Override + public void duringSetup(DuringSetupAccess access) { + try { + RuntimeClassInitialization.rerunClassInitialization(access.findClassByName("java.net.InetAddress")); + RuntimeClassInitialization.rerunClassInitialization(access.findClassByName("java.net.Inet4AddressImpl")); + RuntimeClassInitialization.rerunClassInitialization(access.findClassByName("java.net.Inet6AddressImpl")); + RuntimeClassInitialization.rerunClassInitialization(access.findClassByName("java.net.SocketInputStream")); + RuntimeClassInitialization.rerunClassInitialization(access.findClassByName("java.net.SocketOutputStream")); + RuntimeClassInitialization.rerunClassInitialization(access.findClassByName("java.net.DatagramPacket")); + RuntimeClassInitialization.rerunClassInitialization(access.findClassByName("java.net.AbstractPlainSocketImpl")); + RuntimeClassInitialization.rerunClassInitialization(access.findClassByName("java.net.AbstractPlainDatagramSocketImpl")); + RuntimeClassInitialization.rerunClassInitialization(access.findClassByName("java.net.PlainSocketImpl")); + RuntimeClassInitialization.rerunClassInitialization(access.findClassByName("java.net.PlainDatagramSocketImpl")); + RuntimeClassInitialization.rerunClassInitialization(access.findClassByName("sun.net.ExtendedOptionsImpl")); + } catch (Exception e) { + VMError.shouldNotReachHere("PosixJavaNetSubstitutionsFeature: Error registering rerunClassInitialization: ", e); + } + } + + @Override + public void beforeAnalysis(BeforeAnalysisAccess access) { + try { + /* Common Networking Classes */ + JNIRuntimeAccess.register(access.findClassByName("java.net.NetworkInterface")); + JNIRuntimeAccess.register(access.findClassByName("java.net.NetworkInterface").getDeclaredField("name")); + JNIRuntimeAccess.register(access.findClassByName("java.net.NetworkInterface").getDeclaredField("displayName")); + JNIRuntimeAccess.register(access.findClassByName("java.net.NetworkInterface").getDeclaredField("index")); + JNIRuntimeAccess.register(access.findClassByName("java.net.NetworkInterface").getDeclaredField("addrs")); + JNIRuntimeAccess.register(access.findClassByName("java.net.NetworkInterface").getDeclaredField("bindings")); + JNIRuntimeAccess.register(access.findClassByName("java.net.NetworkInterface").getDeclaredField("childs")); + JNIRuntimeAccess.register(access.findClassByName("java.net.NetworkInterface").getDeclaredField("virtual")); + JNIRuntimeAccess.register(access.findClassByName("java.net.NetworkInterface").getDeclaredField("parent")); + JNIRuntimeAccess.register(access.findClassByName("java.net.NetworkInterface").getDeclaredField("defaultIndex")); + JNIRuntimeAccess.register(access.findClassByName("java.net.NetworkInterface").getDeclaredConstructor()); + + JNIRuntimeAccess.register(access.findClassByName("java.net.InterfaceAddress")); + JNIRuntimeAccess.register(access.findClassByName("java.net.InterfaceAddress").getDeclaredConstructor()); + JNIRuntimeAccess.register(access.findClassByName("java.net.InterfaceAddress").getDeclaredField("address")); + JNIRuntimeAccess.register(access.findClassByName("java.net.InterfaceAddress").getDeclaredField("broadcast")); + JNIRuntimeAccess.register(access.findClassByName("java.net.InterfaceAddress").getDeclaredField("maskLength")); + + JNIRuntimeAccess.register(access.findClassByName("java.net.InetAddress")); + JNIRuntimeAccess.register(access.findClassByName("java.net.InetAddress").getDeclaredField("holder")); + JNIRuntimeAccess.register(access.findClassByName("java.net.InetAddress").getDeclaredField("preferIPv6Address")); + JNIRuntimeAccess.register(access.findClassByName("java.net.InetAddress").getDeclaredMethod("anyLocalAddress")); + + JNIRuntimeAccess.register(access.findClassByName("java.net.InetAddressContainer")); + JNIRuntimeAccess.register(access.findClassByName("java.net.InetAddressContainer").getDeclaredField("addr")); + + JNIRuntimeAccess.register(access.findClassByName("java.net.InetAddress$InetAddressHolder")); + JNIRuntimeAccess.register(access.findClassByName("java.net.InetAddress$InetAddressHolder").getDeclaredField("address")); + JNIRuntimeAccess.register(access.findClassByName("java.net.InetAddress$InetAddressHolder").getDeclaredField("family")); + JNIRuntimeAccess.register(access.findClassByName("java.net.InetAddress$InetAddressHolder").getDeclaredField("hostName")); + JNIRuntimeAccess.register(access.findClassByName("java.net.InetAddress$InetAddressHolder").getDeclaredField("originalHostName")); + + JNIRuntimeAccess.register(access.findClassByName("java.net.Inet4Address")); + JNIRuntimeAccess.register(access.findClassByName("java.net.Inet4Address").getDeclaredConstructor()); + + JNIRuntimeAccess.register(access.findClassByName("java.net.Inet6Address")); + JNIRuntimeAccess.register(access.findClassByName("java.net.Inet6Address").getDeclaredField("holder6")); + JNIRuntimeAccess.register(access.findClassByName("java.net.Inet6Address").getDeclaredField("cached_scope_id")); + JNIRuntimeAccess.register(access.findClassByName("java.net.Inet6Address").getDeclaredConstructor()); + JNIRuntimeAccess.register(access.findClassByName("java.net.Inet6Address$Inet6AddressHolder")); + JNIRuntimeAccess.register(access.findClassByName("java.net.Inet6Address$Inet6AddressHolder").getDeclaredField("ipaddress")); + JNIRuntimeAccess.register(access.findClassByName("java.net.Inet6Address$Inet6AddressHolder").getDeclaredField("scope_id")); + JNIRuntimeAccess.register(access.findClassByName("java.net.Inet6Address$Inet6AddressHolder").getDeclaredField("scope_id_set")); + JNIRuntimeAccess.register(access.findClassByName("java.net.Inet6Address$Inet6AddressHolder").getDeclaredField("scope_ifname")); + + JNIRuntimeAccess.register(access.findClassByName("java.net.DatagramPacket")); + JNIRuntimeAccess.register(access.findClassByName("java.net.DatagramPacket").getDeclaredField("address")); + JNIRuntimeAccess.register(access.findClassByName("java.net.DatagramPacket").getDeclaredField("port")); + JNIRuntimeAccess.register(access.findClassByName("java.net.DatagramPacket").getDeclaredField("buf")); + JNIRuntimeAccess.register(access.findClassByName("java.net.DatagramPacket").getDeclaredField("offset")); + JNIRuntimeAccess.register(access.findClassByName("java.net.DatagramPacket").getDeclaredField("length")); + JNIRuntimeAccess.register(access.findClassByName("java.net.DatagramPacket").getDeclaredField("bufLength")); + + JNIRuntimeAccess.register(access.findClassByName("java.net.InetSocketAddress")); + JNIRuntimeAccess.register(access.findClassByName("java.net.InetSocketAddress").getDeclaredConstructor(InetAddress.class, int.class)); + + JNIRuntimeAccess.register(access.findClassByName("java.net.SocketException")); + JNIRuntimeAccess.register(access.findClassByName("java.net.SocketException").getDeclaredConstructor(String.class)); + JNIRuntimeAccess.register(access.findClassByName("java.net.ConnectException")); + JNIRuntimeAccess.register(access.findClassByName("java.net.ConnectException").getDeclaredConstructor(String.class)); + + /* Linux/Darwin specific classes */ + JNIRuntimeAccess.register(access.findClassByName("java.net.SocketInputStream")); + JNIRuntimeAccess.register(access.findClassByName("java.net.SocketOutputStream")); + + JNIRuntimeAccess.register(access.findClassByName("java.net.DatagramSocketImpl")); + JNIRuntimeAccess.register(access.findClassByName("java.net.DatagramSocketImpl").getDeclaredField("fd")); + JNIRuntimeAccess.register(access.findClassByName("java.net.DatagramSocketImpl").getDeclaredField("localPort")); + + JNIRuntimeAccess.register(access.findClassByName("java.net.AbstractPlainDatagramSocketImpl")); + JNIRuntimeAccess.register(access.findClassByName("java.net.AbstractPlainDatagramSocketImpl").getDeclaredField("timeout")); + JNIRuntimeAccess.register(access.findClassByName("java.net.AbstractPlainDatagramSocketImpl").getDeclaredField("trafficClass")); + JNIRuntimeAccess.register(access.findClassByName("java.net.AbstractPlainDatagramSocketImpl").getDeclaredField("connected")); + JNIRuntimeAccess.register(access.findClassByName("java.net.AbstractPlainDatagramSocketImpl").getDeclaredField("connectedAddress")); + JNIRuntimeAccess.register(access.findClassByName("java.net.AbstractPlainDatagramSocketImpl").getDeclaredField("connectedPort")); + + JNIRuntimeAccess.register(access.findClassByName("java.net.PlainDatagramSocketImpl")); + + JNIRuntimeAccess.register(access.findClassByName("java.net.SocketImpl")); + JNIRuntimeAccess.register(access.findClassByName("java.net.SocketImpl").getDeclaredField("fd")); + JNIRuntimeAccess.register(access.findClassByName("java.net.SocketImpl").getDeclaredField("localport")); + JNIRuntimeAccess.register(access.findClassByName("java.net.SocketImpl").getDeclaredField("serverSocket")); + JNIRuntimeAccess.register(access.findClassByName("java.net.SocketImpl").getDeclaredField("address")); + JNIRuntimeAccess.register(access.findClassByName("java.net.SocketImpl").getDeclaredField("port")); + + JNIRuntimeAccess.register(access.findClassByName("java.net.AbstractPlainSocketImpl")); + JNIRuntimeAccess.register(access.findClassByName("java.net.AbstractPlainSocketImpl").getDeclaredField("timeout")); + JNIRuntimeAccess.register(access.findClassByName("java.net.AbstractPlainSocketImpl").getDeclaredField("trafficClass")); + JNIRuntimeAccess.register(access.findClassByName("java.net.AbstractPlainSocketImpl").getDeclaredField("fdLock")); + JNIRuntimeAccess.register(access.findClassByName("java.net.AbstractPlainSocketImpl").getDeclaredField("closePending")); + + JNIRuntimeAccess.register(access.findClassByName("java.net.PlainSocketImpl")); + + JNIRuntimeAccess.register(access.findClassByName("jdk.net.SocketFlow")); + JNIRuntimeAccess.register(access.findClassByName("jdk.net.SocketFlow").getDeclaredField("status")); + JNIRuntimeAccess.register(access.findClassByName("jdk.net.SocketFlow").getDeclaredField("priority")); + JNIRuntimeAccess.register(access.findClassByName("jdk.net.SocketFlow").getDeclaredField("bandwidth")); + + JNIRuntimeAccess.register(access.findClassByName("jdk.net.SocketFlow$Status")); + JNIRuntimeAccess.register(access.findClassByName("jdk.net.SocketFlow$Status").getDeclaredField("NO_STATUS")); + JNIRuntimeAccess.register(access.findClassByName("jdk.net.SocketFlow$Status").getDeclaredField("OK")); + JNIRuntimeAccess.register(access.findClassByName("jdk.net.SocketFlow$Status").getDeclaredField("NO_PERMISSION")); + JNIRuntimeAccess.register(access.findClassByName("jdk.net.SocketFlow$Status").getDeclaredField("NOT_CONNECTED")); + JNIRuntimeAccess.register(access.findClassByName("jdk.net.SocketFlow$Status").getDeclaredField("NOT_SUPPORTED")); + JNIRuntimeAccess.register(access.findClassByName("jdk.net.SocketFlow$Status").getDeclaredField("ALREADY_CREATED")); + JNIRuntimeAccess.register(access.findClassByName("jdk.net.SocketFlow$Status").getDeclaredField("IN_PROGRESS")); + JNIRuntimeAccess.register(access.findClassByName("jdk.net.SocketFlow$Status").getDeclaredField("OTHER")); + + JNIRuntimeAccess.register(access.findClassByName("java.lang.Integer").getDeclaredConstructor(int.class)); + JNIRuntimeAccess.register(access.findClassByName("java.lang.Integer").getDeclaredField("value")); + + JNIRuntimeAccess.register(access.findClassByName("java.lang.Boolean")); + JNIRuntimeAccess.register(access.findClassByName("java.lang.Boolean").getDeclaredConstructor(boolean.class)); + JNIRuntimeAccess.register(access.findClassByName("java.lang.Boolean").getDeclaredMethod("getBoolean", String.class)); + + RuntimeReflection.register(access.findClassByName("java.net.InetAddressImpl")); + RuntimeReflection.register(access.findClassByName("java.net.Inet4AddressImpl")); + RuntimeReflection.register(access.findClassByName("java.net.Inet6AddressImpl")); + RuntimeReflection.registerForReflectiveInstantiation(access.findClassByName("java.net.Inet4AddressImpl")); + RuntimeReflection.registerForReflectiveInstantiation(access.findClassByName("java.net.Inet6AddressImpl")); + + } catch (Exception e) { + VMError.shouldNotReachHere("PosixJavaNetSubstitutionsFeature: Error registering class or method: ", e); + } + } +} + +@TargetClass(className = "java.net.NetworkInterface") +@Platforms({Platform.LINUX_JNI.class, Platform.DARWIN_JNI.class}) +final class Target_java_net_NetworkInterface_jni { + + @Alias + static native void init(); +} + +@Platforms({Platform.LINUX_AND_JNI.class, Platform.DARWIN_AND_JNI.class}) public final class PosixJavaNetSubstitutions { /** Private constructor: No instances. */ private PosixJavaNetSubstitutions() { } + + @Platforms({Platform.LINUX_JNI.class, Platform.DARWIN_JNI.class}) + public static boolean initIDs() { + try { + System.loadLibrary("net"); + } catch (UnsatisfiedLinkError e) { + VMError.shouldNotReachHere("System.loadLibrary failed ", e); + } + Target_java_net_NetworkInterface_jni.init(); + return true; + } } /* { Allow names with non-standard names: Checkstyle: stop */ @@ -3341,7 +3518,7 @@ public InetAddress[] lookupAllHostAddr(String host) throws UnknownHostException, ret = new InetAddress[retLen]; - if (GraalServices.Java8OrEarlier) { + if (JavaVersionUtil.Java8OrEarlier) { if (Target_java_net_InetAddress.preferIPv6AddressJDK8OrEarlier) { /* AF_INET addresses will be offset by inet6Count */ inetIndex = inet6Count; @@ -3408,7 +3585,7 @@ public InetAddress[] lookupAllHostAddr(String host) throws UnknownHostException, // 474 inet6Index++; inet6Index++; } - if (!GraalServices.Java8OrEarlier) { + if (!JavaVersionUtil.Java8OrEarlier) { if (Target_java_net_InetAddress.preferIPv6AddressJDK9OrLater == Target_java_net_InetAddress.PREFER_SYSTEM_VALUE) { originalIndex++; inetIndex = 0; @@ -3514,7 +3691,7 @@ static InetAddress[] lookupIfLocalhost(CCharPointer hostname, boolean includeV6) /* Create and fill the Java array. */ int arraySize = addrs4 + addrs6 - (includeLoopback ? 0 : (numV4Loopbacks + numV6Loopbacks)); result = new InetAddress[arraySize]; - if (GraalServices.Java8OrEarlier) { + if (JavaVersionUtil.Java8OrEarlier) { if (Target_java_net_InetAddress.preferIPv6AddressJDK8OrEarlier) { i = includeLoopback ? addrs6 : (addrs6 - numV6Loopbacks); j = 0; diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixLogHandler.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixLogHandler.java index bfd4d2bfb90e..237c9af64814 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixLogHandler.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixLogHandler.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -39,7 +39,7 @@ import com.oracle.svm.core.posix.headers.LibC; @AutomaticFeature -@Platforms({Platform.LINUX.class, Platform.DARWIN.class}) +@Platforms({Platform.LINUX_AND_JNI.class, Platform.DARWIN_AND_JNI.class}) class PosixLogHandlerFeature implements Feature { @Override public void beforeAnalysis(BeforeAnalysisAccess access) { diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixNativeLibraryFeature.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixNativeLibraryFeature.java index a1f1daa9a691..7a2f0a3140ca 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixNativeLibraryFeature.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixNativeLibraryFeature.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,11 +34,12 @@ import org.graalvm.word.WordFactory; import com.oracle.svm.core.annotate.AutomaticFeature; +import com.oracle.svm.core.jdk.Jvm; import com.oracle.svm.core.jdk.PlatformNativeLibrarySupport; import com.oracle.svm.core.posix.headers.Dlfcn; @AutomaticFeature -@Platforms({Platform.LINUX.class, Platform.DARWIN.class}) +@Platforms({Platform.LINUX_AND_JNI.class, Platform.DARWIN_AND_JNI.class}) class PosixNativeLibraryFeature implements Feature { @Override public void afterRegistration(AfterRegistrationAccess access) { @@ -54,6 +55,18 @@ static void initialize() { @Override public boolean initializeBuiltinLibraries() { + if (Platform.includedIn(Platform.LINUX_JNI.class) || + Platform.includedIn(Platform.DARWIN_JNI.class)) { + if (!PosixJavaIOSubstitutions.initIDs()) { + return false; + } + if (!PosixJavaLangSubstitutions.initIDs()) { + return false; + } + if (!PosixJavaNetSubstitutions.initIDs()) { + return false; + } + } return true; } @@ -69,16 +82,6 @@ public PointerBase findBuiltinSymbol(String name) { } } - @Override - public boolean isBuiltinLibrary(String name) { - return false; - } - - @Override - public boolean isBuiltinPkgNative(String name) { - return false; - } - class PosixNativeLibrary implements NativeLibrary { private final String canonicalIdentifier; @@ -86,6 +89,13 @@ class PosixNativeLibrary implements NativeLibrary { private PointerBase dlhandle = WordFactory.nullPointer(); PosixNativeLibrary(String canonicalIdentifier, boolean builtin) { + // Make sure the jvm.lib is available for linking + // Need a better place to put this. + if (Platform.includedIn(Platform.LINUX_JNI.class) || + Platform.includedIn(Platform.DARWIN_JNI.class)) { + Jvm.initialize(); + } + this.canonicalIdentifier = canonicalIdentifier; this.builtin = builtin; } diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixProcessPropertiesSupport.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixProcessPropertiesSupport.java index 9af1161a4b29..ad05fc9bcdc5 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixProcessPropertiesSupport.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixProcessPropertiesSupport.java @@ -70,7 +70,7 @@ public boolean isAlive(long processID) { @Override public int waitForProcessExit(long processID) { - return Java_lang_Process_Supplement.waitForProcessExit(Math.toIntExact(processID)); + return PosixUtils.waitForProcessExit(Math.toIntExact(processID)); } @Override diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixSubstrateOperatingSystemMXBean.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixSubstrateOperatingSystemMXBean.java index ad141009ae7d..db374ecd02f2 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixSubstrateOperatingSystemMXBean.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixSubstrateOperatingSystemMXBean.java @@ -36,7 +36,7 @@ import org.graalvm.nativeimage.Platforms; import org.graalvm.nativeimage.StackValue; -@Platforms({Platform.LINUX.class, Platform.DARWIN.class}) +@Platforms({Platform.LINUX_AND_JNI.class, Platform.DARWIN_AND_JNI.class}) class PosixSubstrateOperatingSystemMXBean extends SubstrateOperatingSystemMXBean { /** @@ -62,7 +62,7 @@ public long getProcessCpuTime() { } -@Platforms({Platform.LINUX.class, Platform.DARWIN.class}) +@Platforms({Platform.LINUX_AND_JNI.class, Platform.DARWIN_AND_JNI.class}) @AutomaticFeature class PosixSubstrateOperatingSystemMXBeanFeature implements Feature { @Override diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixSunNioSubstitutions.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixSunNioSubstitutions.java index ae316f7f1599..7f187f4cf226 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixSunNioSubstitutions.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixSunNioSubstitutions.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -42,6 +42,7 @@ /* Do not reformat commented-out code: @formatter:off */ +@Platforms({Platform.LINUX.class, Platform.DARWIN.class}) public final class PosixSunNioSubstitutions { /** Translations of jdk/src/solaris/native/sun/nio/ch/PollArrayWrapper.c?v=Java_1.8.0_40_b10. */ diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixSystemPropertiesSupport.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixSystemPropertiesSupport.java index 604eeb7b2de2..bb61304cf541 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixSystemPropertiesSupport.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixSystemPropertiesSupport.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -38,7 +38,7 @@ import com.oracle.svm.core.posix.headers.Pwd.passwd; import com.oracle.svm.core.posix.headers.Unistd; -@Platforms({Platform.LINUX.class, Platform.DARWIN.class}) +@Platforms({Platform.LINUX_AND_JNI.class, Platform.DARWIN_AND_JNI.class}) public abstract class PosixSystemPropertiesSupport extends SystemPropertiesSupport { /* diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixUtils.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixUtils.java index 3888df300abf..451e476d5ffe 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixUtils.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixUtils.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -39,11 +39,20 @@ import java.util.ArrayList; import java.util.List; +import com.oracle.svm.core.posix.headers.Dlfcn; +import com.oracle.svm.core.posix.headers.Errno; +import com.oracle.svm.core.posix.headers.Fcntl; +import com.oracle.svm.core.posix.headers.LibC; +import com.oracle.svm.core.posix.headers.Locale; +import com.oracle.svm.core.posix.headers.Unistd; +import com.oracle.svm.core.posix.headers.Wait; import org.graalvm.compiler.core.common.SuppressFBWarnings; import org.graalvm.nativeimage.PinnedObject; import org.graalvm.nativeimage.Platform; +import org.graalvm.nativeimage.Platforms; import org.graalvm.nativeimage.StackValue; import org.graalvm.nativeimage.c.type.CCharPointer; +import org.graalvm.nativeimage.c.type.CIntPointer; import org.graalvm.nativeimage.c.type.CTypeConversion; import org.graalvm.nativeimage.c.type.CTypeConversion.CCharPointerHolder; import org.graalvm.word.PointerBase; @@ -57,15 +66,10 @@ import com.oracle.svm.core.annotate.TargetElement; import com.oracle.svm.core.annotate.Uninterruptible; import com.oracle.svm.core.jdk.JDK9OrLater; -import com.oracle.svm.core.posix.headers.Dlfcn; -import com.oracle.svm.core.posix.headers.Errno; -import com.oracle.svm.core.posix.headers.Fcntl; -import com.oracle.svm.core.posix.headers.LibC; -import com.oracle.svm.core.posix.headers.Locale; -import com.oracle.svm.core.posix.headers.Unistd; import com.oracle.svm.core.snippets.KnownIntrinsics; import com.oracle.svm.core.util.VMError; +@Platforms({Platform.LINUX_AND_JNI.class, Platform.DARWIN_AND_JNI.class}) public class PosixUtils { static String setLocale(String category, String locale) { @@ -268,6 +272,28 @@ public static int getpid(Process process) { return instance.pid; } + public static int waitForProcessExit(int ppid) { + CIntPointer statusptr = StackValue.get(CIntPointer.class); + while (Wait.waitpid(ppid, statusptr, 0) < 0) { + if (Errno.errno() == Errno.ECHILD()) { + return 0; + } else if (Errno.errno() == Errno.EINTR()) { + break; + } else { + return -1; + } + } + + int status = statusptr.read(); + if (Wait.WIFEXITED(status)) { + return Wait.WEXITSTATUS(status); + } else if (Wait.WIFSIGNALED(status)) { + // Exited because of signal: return 0x80 + signal number like shells do + return 0x80 + Wait.WTERMSIG(status); + } + return status; + } + static int readSingle(FileDescriptor fd) throws IOException { CCharPointer retPtr = StackValue.get(CCharPointer.class); int handle = PosixUtils.getFDHandle(fd); diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixVirtualMemoryProvider.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixVirtualMemoryProvider.java index 024b2af2c318..51283ffb57c9 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixVirtualMemoryProvider.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/PosixVirtualMemoryProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -59,7 +59,7 @@ import com.oracle.svm.core.os.VirtualMemoryProvider; @AutomaticFeature -@Platforms({Platform.LINUX.class, Platform.DARWIN.class}) +@Platforms({Platform.LINUX_AND_JNI.class, Platform.DARWIN_AND_JNI.class}) class PosixVirtualMemoryProviderFeature implements Feature { @Override public void beforeAnalysis(BeforeAnalysisAccess access) { diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/SegfaultHandlerFeature.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/SegfaultHandlerFeature.java index c81e7eaa3fb3..595c0002af59 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/SegfaultHandlerFeature.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/SegfaultHandlerFeature.java @@ -26,6 +26,7 @@ import org.graalvm.compiler.options.Option; import org.graalvm.nativeimage.Feature; +import org.graalvm.nativeimage.ImageInfo; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.LogHandler; import org.graalvm.nativeimage.Platform; @@ -229,8 +230,8 @@ public enum AMD64Register { } public static class Options { - @Option(help = "Install segfault handler that prints register contents and full Java stacktrace")// - static final RuntimeOptionKey InstallSegfaultHandler = new RuntimeOptionKey<>(true); + @Option(help = "Install segfault handler that prints register contents and full Java stacktrace. Default: enabled for an executable, disabled for a shared library.")// + static final RuntimeOptionKey InstallSegfaultHandler = new RuntimeOptionKey<>(null); } private static volatile boolean dispatchInProgress = false; @@ -287,7 +288,8 @@ private static void dispatch(int signalNumber, @SuppressWarnings("unused") sigin ucontext_t.class); static void install() { - if (Options.InstallSegfaultHandler.getValue()) { + Boolean optionValue = Options.InstallSegfaultHandler.getValue(); + if (optionValue == Boolean.TRUE || (optionValue == null && ImageInfo.isExecutable())) { int structSigActionSize = SizeOf.get(sigaction.class); sigaction structSigAction = StackValue.get(structSigActionSize); LibC.memset(structSigAction, WordFactory.signed(0), WordFactory.unsigned(structSigActionSize)); diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/SunMiscSubstitutions.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/SunMiscSubstitutions.java index 44f8509b5fc6..f13b93157d50 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/SunMiscSubstitutions.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/SunMiscSubstitutions.java @@ -29,7 +29,7 @@ import java.util.concurrent.locks.ReentrantLock; import java.util.function.Function; -import org.graalvm.compiler.serviceprovider.GraalServices; +import org.graalvm.compiler.serviceprovider.JavaVersionUtil; import org.graalvm.nativeimage.Feature; import org.graalvm.nativeimage.ImageInfo; import org.graalvm.nativeimage.Platform; @@ -58,7 +58,7 @@ class Package_jdk_internal_misc implements Function { @Override public String apply(TargetClass annotation) { - if (GraalServices.Java8OrEarlier) { + if (JavaVersionUtil.Java8OrEarlier) { return "sun.misc." + annotation.className(); } else { return "jdk.internal.misc." + annotation.className(); diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/UnmanagedMemorySupportImpl.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/UnmanagedMemorySupportImpl.java index 366cfecc0ab5..ef511359a3af 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/UnmanagedMemorySupportImpl.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/UnmanagedMemorySupportImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -64,7 +64,7 @@ public void free(PointerBase ptr) { } @AutomaticFeature -@Platforms({Platform.LINUX.class, Platform.DARWIN.class}) +@Platforms({Platform.LINUX_AND_JNI.class, Platform.DARWIN_AND_JNI.class}) class UnmanagedMemoryFeature implements Feature { @Override public void afterRegistration(AfterRegistrationAccess access) { diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/VmPrimsJVM.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/VmPrimsJVM.java index 8714f6294963..faa9cfdd7b15 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/VmPrimsJVM.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/VmPrimsJVM.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,6 +24,8 @@ */ package com.oracle.svm.core.posix; +import org.graalvm.nativeimage.Platform; +import org.graalvm.nativeimage.Platforms; import org.graalvm.nativeimage.StackValue; import org.graalvm.nativeimage.c.type.CCharPointer; import org.graalvm.nativeimage.c.type.CIntPointer; @@ -31,6 +33,7 @@ import com.oracle.svm.core.posix.headers.Socket; /** Native methods (and macros) from src/share/vm/prims/jvm.cpp translated to Java. */ +@Platforms({Platform.LINUX.class, Platform.DARWIN.class}) public final class VmPrimsJVM { /* { Do not re-wrap commented-out code. @formatter:off */ /* { Allow names with underscores: @Checkstyle: stop */ diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/darwin/DarwinCoreFoundationUtils.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/darwin/DarwinCoreFoundationUtils.java index dad40e9d2928..0778bbfdcdec 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/darwin/DarwinCoreFoundationUtils.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/darwin/DarwinCoreFoundationUtils.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -32,7 +32,7 @@ import com.oracle.svm.core.posix.headers.darwin.CoreFoundation; -@Platforms(Platform.DARWIN.class) +@Platforms(Platform.DARWIN_AND_JNI.class) public final class DarwinCoreFoundationUtils { private DarwinCoreFoundationUtils() { diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/darwin/DarwinImageHeapProvider.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/darwin/DarwinImageHeapProvider.java index 0b18a8510f76..9c58fb858eee 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/darwin/DarwinImageHeapProvider.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/darwin/DarwinImageHeapProvider.java @@ -50,11 +50,13 @@ import com.oracle.svm.core.util.UnsignedUtils; @AutomaticFeature -@Platforms(Platform.DARWIN.class) +@Platforms(Platform.DARWIN_AND_JNI.class) class DarwinImageHeapProviderFeature implements Feature { @Override - public void afterRegistration(AfterRegistrationAccess access) { - ImageSingletons.add(ImageHeapProvider.class, new DarwinImageHeapProvider()); + public void duringSetup(DuringSetupAccess access) { + if (!ImageSingletons.contains(ImageHeapProvider.class)) { + ImageSingletons.add(ImageHeapProvider.class, new DarwinImageHeapProvider()); + } } } diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/darwin/DarwinJavaNetCloseImpl.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/darwin/DarwinJavaNetCloseImpl.java new file mode 100644 index 000000000000..d27ab8016d86 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/darwin/DarwinJavaNetCloseImpl.java @@ -0,0 +1,289 @@ +/* + * Copyright (c) 2019, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.svm.core.posix.darwin; + +import org.graalvm.nativeimage.Feature; +import org.graalvm.nativeimage.ImageSingletons; +import org.graalvm.nativeimage.Platform; +import org.graalvm.nativeimage.Platforms; +import org.graalvm.nativeimage.StackValue; +import org.graalvm.nativeimage.c.struct.SizeOf; +import org.graalvm.nativeimage.c.type.CIntPointer; +import org.graalvm.word.WordFactory; + +import com.oracle.svm.core.annotate.AutomaticFeature; +import com.oracle.svm.core.posix.PosixJavaNetClose; +import com.oracle.svm.core.posix.headers.Errno; +import com.oracle.svm.core.posix.headers.LibC; +import com.oracle.svm.core.posix.headers.SysSelect; +import com.oracle.svm.core.posix.headers.Socket; +import com.oracle.svm.core.posix.headers.SysParam; +import com.oracle.svm.core.posix.headers.Time; +import com.oracle.svm.core.posix.headers.Unistd; + +/** + * Translation of the mechanisms in /jdk8u-dev/jdk/src/solaris/native/java/net/bsd_close.c that + * implements interruption of blocking operations. The mechanism is not a direct translation of the + * C functions, which maintain lists of pthreads, and file descriptors, etc. Rather the mechanism is + * implemented in Java using Java threads and Thread.interrupt. + * + * Also in this class are more traditional translations of the C functions that do blocking + * operations. Where the implementations are identical between platforms, the shared code lives in + * {@link PosixJavaNetClose}. + */ +@Platforms({Platform.DARWIN.class}) +public final class DarwinJavaNetCloseImpl extends PosixJavaNetClose { + + protected DarwinJavaNetCloseImpl() { + /* Nothing to do. */ + } + + /** + * { @formatter:off + * 189 * Close or dup2 a file descriptor ensuring that all threads blocked on + * 190 * the file descriptor are notified via a wakeup signal. + * 191 * + * 192 * fd1 < 0 => close(fd2) + * 193 * fd1 >= 0 => dup2(fd1, fd2) + * 194 * + * 195 * Returns -1 with errno set if operation fails. + * } @formatter:on + */ + @Override + protected int closefd(int fd1, int fd2) { + PosixJavaNetClose.FdEntry fdEntry = getFdEntry(fd2); + if (fdEntry == null) { + Errno.set_errno(Errno.EBADF()); + return -1; + } + int rv; + /* Lock the fd to hold-off additional I/O on this fd. */ + /* { Allow synchronization: Checkstyle: stop. */ + synchronized (fdEntry) { + /* } Allow synchronization: Checkstyle: resume. */ + /* Send a wakeup signal to all threads blocked on this file descriptor. */ + fdEntry.getThreadList().forEach((threadEntry) -> { + threadEntry.setIntr(true); + threadEntry.getThread().interrupt(); + }); + /* And close/dup the file descriptor (restart if interrupted by signal) */ + do { + if (fd1 < 0) { + rv = Unistd.close(fd2); + } else { + rv = Unistd.dup2(fd1, fd2); + } + } while ((rv == -1) && (Errno.errno() == Errno.EINTR())); + } + return rv; + } + + /* + * Implementations of the methods that use interruptible I/O. + */ + + /* { Allow names with underscores: Checkstyle: stop */ + + /* { Do not re-wrap commented-out code. @formatter:off */ + // 322 int NET_Accept(int s, struct sockaddr *addr, int *addrlen) { + @Override + public int NET_Accept(int s, Socket.sockaddr addr, CIntPointer addrlen) { + // 323 socklen_t len = *addrlen; + CIntPointer len_Pointer = StackValue.get(CIntPointer.class); + len_Pointer.write(addrlen.read()); + // 324 int error = accept(s, addr, &len); + int error = Socket.accept(s, addr, len_Pointer); + // 325 if (error != -1) + if (error != -1) { + // 326 *addrlen = (int)len; + addrlen.write(len_Pointer.read()); + } + // 327 BLOCKING_IO_RETURN_INT( s, error ); + return BLOCKING_IO_RETURN_INT(s, () -> { + return error; + }); + } + /* } Do not re-wrap commented-out code. @formatter:on */ + + /* { Do not re-wrap commented-out code. @formatter:off */ + // 346 /* + // 347 * Wrapper for select(s, timeout). We are using select() on Mac OS due to Bug 7131399. + // 348 * Auto restarts with adjusted timeout if interrupted by + // 349 * signal other than our wakeup signal. + // 350 */ + // 351 int NET_Timeout0(int s, long timeout, long currentTime) { + @Override + public int NET_Timeout0(int s, long timeoutArg, long currentTime) { + /* Do not modify argument. */ + long timeout = timeoutArg; + // 352 long prevtime = currentTime, newtime; + long prevtime = currentTime; + long newtime; + // 353 struct timeval t, *tp = &t; + Time.timeval t = StackValue.get(Time.timeval.class); + Time.timeval tp; + tp = t; + // 354 fd_set fds; + SysSelect.fd_set fds = StackValue.get(SysSelect.fd_set.class); + // 355 fd_set* fdsp = NULL; + SysSelect.fd_set fdsp = WordFactory.nullPointer(); + // 356 int allocated = 0; + int allocated = 0; + // 357 threadEntry_t self; + ThreadEntry self = new ThreadEntry(); + // 358 fdEntry_t *fdEntry = getFdEntry(s); + FdEntry fdEntry = getFdEntry(s); + // 359 + // 360 /* + // 361 * Check that fd hasn't been closed. + // 362 */ + // 363 if (fdEntry == NULL) { + if (fdEntry == null) { + // 364 errno = EBADF; + Errno.set_errno(Errno.EBADF()); + // 365 return -1; + return -1; + } + // 367 + // 368 /* + // 369 * Pick up current time as may need to adjust timeout + // 370 */ + // 371 if (timeout > 0) { + if (timeout > 0) { + // 372 /* Timed */ + // 373 t.tv_sec = timeout / 1000; + t.set_tv_sec(timeout / 1000); + // 374 t.tv_usec = (timeout % 1000) * 1000; + t.set_tv_usec((timeout % 1000) * 1000); + // 375 } else if (timeout < 0) { + } else if (timeout < 0) { + // 376 /* Blocking */ + // 377 tp = 0; + tp = WordFactory.nullPointer(); + } else { + // 379 /* Poll */ + // 380 t.tv_sec = 0; + t.set_tv_sec(0); + // 381 t.tv_usec = 0; + t.set_tv_usec(0); + } + // 383 + // 384 if (s < FD_SETSIZE) { + if (s < SysSelect.FD_SETSIZE()) { + // 385 fdsp = &fds; + fdsp = fds; + // 386 FD_ZERO(fdsp); + SysSelect.FD_ZERO(fdsp); + } else { + // 388 int length = (howmany(s+1, NFDBITS)) * sizeof(int); + int length = (SysParam.howmany(s+1, SysSelect.NFDBITS())) * SizeOf.get(CIntPointer.class); + // 389 fdsp = (fd_set *) calloc(1, length); + fdsp = (SysSelect.fd_set) LibC.calloc(WordFactory.unsigned(1), WordFactory.unsigned(length)); + // 390 if (fdsp == NULL) { + if (fdsp.isNull()) { + // 391 return -1; // errno will be set to ENOMEM + return -1; + } + // 393 allocated = 1; + allocated = 1; + } + // 395 FD_SET(s, fdsp); + SysSelect.FD_SET(s, fdsp); + // 396 + // 397 for(;;) { + for (; /* return */;) { + // 398 int rv; + int rv; + // 399 + // 400 /* + // 401 * call select on the fd. If interrupted by our wakeup signal + // 402 * errno will be set to EBADF. + // 403 */ + // 404 + // 405 startOp(fdEntry, &self); + startOp(fdEntry, self); + // 406 rv = select(s+1, fdsp, 0, 0, tp); + rv = SysSelect.select(s + 1, fdsp, WordFactory.nullPointer(), WordFactory.nullPointer(), tp); + // 407 endOp(fdEntry, &self); + endOp(fdEntry, self); + // 408 + // 409 /* + // 410 * If interrupted then adjust timeout. If timeout + // 411 * has expired return 0 (indicating timeout expired). + // 412 */ + // 413 if (rv < 0 && errno == EINTR) { + if ((rv < 0) && (Errno.errno() == Errno.EINTR())) { + // 414 if (timeout > 0) { + if (timeout > 0) { + // 415 struct timeval now; + Time.timeval now = StackValue.get(Time.timeval.class); + // 416 gettimeofday(&now, NULL); + Time.gettimeofday(now, WordFactory.nullPointer()); + // 417 newtime = now.tv_sec * 1000 + now.tv_usec / 1000; + newtime = ((now.tv_sec() * 1000) + (now.tv_usec() / 1000)); + // 418 timeout -= newtime - prevtime; + timeout -= newtime - prevtime; + // 419 if (timeout <= 0) { + if (timeout <= 0) { + // 420 if (allocated != 0) + if (allocated != 0) { + // 421 free(fdsp); + LibC.free(fdsp); + } + // 422 return 0; + return 0; + } + // 424 prevtime = newtime; + prevtime = newtime; + // 425 t.tv_sec = timeout / 1000; + t.set_tv_sec(timeout / 1000); + // 426 t.tv_usec = (timeout % 1000) * 1000; + t.set_tv_usec((timeout % 1000) * 1000); + } + } else { + // 429 if (allocated != 0) + if (allocated != 0) { + // 430 free(fdsp); + LibC.free(fdsp); + } + // 431 return rv; + return rv; + } + // 433 + } + } + /* } Do not re-wrap commented-out code. @formatter:on */ + + /* } Allow names with underscores: Checkstyle: resume */ +} + +@Platforms({Platform.DARWIN.class}) +@AutomaticFeature +class DarwinJavaNetCloseFeature implements Feature { + @Override + public void afterRegistration(AfterRegistrationAccess access) { + ImageSingletons.add(PosixJavaNetClose.class, new DarwinJavaNetCloseImpl()); + } +} diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/darwin/DarwinPhysicalMemory.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/darwin/DarwinPhysicalMemory.java index 7a95fea37f2a..8b0006773798 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/darwin/DarwinPhysicalMemory.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/darwin/DarwinPhysicalMemory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -44,7 +44,7 @@ import com.oracle.svm.core.posix.headers.darwin.DarwinSysctl; import com.oracle.svm.core.util.VMError; -@Platforms(Platform.DARWIN.class) +@Platforms(Platform.DARWIN_AND_JNI.class) class DarwinPhysicalMemory extends PhysicalMemory { static class PhysicalMemorySupportImpl implements PhysicalMemorySupport { diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/darwin/DarwinProcessPropertiesSupport.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/darwin/DarwinProcessPropertiesSupport.java index 08dcfa012212..14e74a82ef53 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/darwin/DarwinProcessPropertiesSupport.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/darwin/DarwinProcessPropertiesSupport.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -41,7 +41,7 @@ import com.oracle.svm.core.posix.headers.darwin.DarwinDyld; import com.oracle.svm.core.util.VMError; -@Platforms(Platform.DARWIN.class) +@Platforms(Platform.DARWIN_AND_JNI.class) public class DarwinProcessPropertiesSupport extends PosixProcessPropertiesSupport { @Override diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/darwin/DarwinStackOverflowSupport.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/darwin/DarwinStackOverflowSupport.java index 646990b2a86f..4f1420aa64c1 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/darwin/DarwinStackOverflowSupport.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/darwin/DarwinStackOverflowSupport.java @@ -36,7 +36,7 @@ import com.oracle.svm.core.posix.headers.darwin.DarwinPthread; import com.oracle.svm.core.stack.StackOverflowCheck; -@Platforms({Platform.DARWIN.class}) +@Platforms({Platform.DARWIN_AND_JNI.class}) class DarwinStackOverflowSupport implements StackOverflowCheck.OSSupport { @Uninterruptible(reason = "Called while thread is being attached to the VM, i.e., when the thread state is not yet set up.") @@ -49,7 +49,7 @@ public UnsignedWord lookupStackEnd() { } } -@Platforms({Platform.DARWIN.class}) +@Platforms({Platform.DARWIN_AND_JNI.class}) @AutomaticFeature class DarwinStackOverflowSupportFeature implements Feature { @Override diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/darwin/DarwinSubstitutions.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/darwin/DarwinSubstitutions.java index 08ac52e369aa..d0680a489d46 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/darwin/DarwinSubstitutions.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/darwin/DarwinSubstitutions.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -43,7 +43,7 @@ import com.oracle.svm.core.posix.headers.Time.timezone; import com.oracle.svm.core.posix.headers.darwin.DarwinTime.MachTimebaseInfo; -@Platforms(Platform.DARWIN.class) +@Platforms(Platform.DARWIN_AND_JNI.class) @TargetClass(java.lang.System.class) final class Target_java_lang_System { @@ -86,7 +86,7 @@ public static String mapLibraryName(String libname) { } /** Additional static-like fields for {@link Target_java_lang_System}. */ -@Platforms(Platform.DARWIN.class) +@Platforms(Platform.DARWIN_AND_JNI.class) final class Util_java_lang_System { boolean timeBaseValid = false; boolean fastTime = false; @@ -97,7 +97,7 @@ final class Util_java_lang_System { } } -@Platforms(Platform.DARWIN.class) +@Platforms(Platform.DARWIN_AND_JNI.class) @AutomaticFeature class DarwinSubsitutionsFeature implements Feature { diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/darwin/DarwinSystemPropertiesSupport.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/darwin/DarwinSystemPropertiesSupport.java index 3c83202a8279..f268bf94ed96 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/darwin/DarwinSystemPropertiesSupport.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/darwin/DarwinSystemPropertiesSupport.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -44,7 +44,7 @@ import com.oracle.svm.core.posix.headers.darwin.CoreFoundation; import com.oracle.svm.core.posix.headers.darwin.CoreFoundation.CFStringRef; -@Platforms({Platform.DARWIN.class}) +@Platforms({Platform.DARWIN_AND_JNI.class}) public class DarwinSystemPropertiesSupport extends PosixSystemPropertiesSupport { @Override @@ -99,7 +99,7 @@ protected String osVersionValue() { } } -@Platforms({Platform.DARWIN.class}) +@Platforms({Platform.DARWIN_AND_JNI.class}) @AutomaticFeature class DarwinSystemPropertiesFeature implements Feature { @Override diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/Dlfcn.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/Dlfcn.java index 0bcc4c62a9f4..57dff2a95675 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/Dlfcn.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/Dlfcn.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -32,8 +32,8 @@ import org.graalvm.nativeimage.c.struct.CStruct; import org.graalvm.nativeimage.c.type.CCharPointer; import org.graalvm.nativeimage.Platforms; -import org.graalvm.nativeimage.Platform.DARWIN; -import org.graalvm.nativeimage.Platform.LINUX; +import org.graalvm.nativeimage.Platform.DARWIN_AND_JNI; +import org.graalvm.nativeimage.Platform.LINUX_AND_JNI; import org.graalvm.word.Pointer; import org.graalvm.word.PointerBase; import org.graalvm.word.WordBase; @@ -43,7 +43,7 @@ /** * Definitions manually translated from the C header file dlfcn.h. */ -@Platforms({DARWIN.class, LINUX.class}) +@Platforms({DARWIN_AND_JNI.class, LINUX_AND_JNI.class}) @CContext(PosixDirectives.class) @CLibrary("dl") public class Dlfcn { diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/LibC.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/LibC.java index 3f67013ee2ff..472b3d97b09b 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/LibC.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/LibC.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -40,7 +40,7 @@ /** * Basic functions from the standard C library that we require to be present on all Posix platforms. */ -@Platforms({Platform.LINUX.class, Platform.DARWIN.class}) +@Platforms({Platform.LINUX_AND_JNI.class, Platform.DARWIN_AND_JNI.class}) public class LibC { /** diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/Paths.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/Paths.java index 2767a9e3e95b..c670fd7d00f8 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/Paths.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/Paths.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,7 +34,7 @@ @CContext(PosixDirectives.class) public class Paths { - @Platforms(Platform.LINUX.class) + @Platforms(Platform.LINUX_AND_JNI.class) @CConstant public static native String _PATH_VARTMP(); } diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/PosixDirectives.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/PosixDirectives.java index 7063ab7d8e45..b8989057224a 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/PosixDirectives.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/PosixDirectives.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -61,14 +61,17 @@ public class PosixDirectives implements CContext.Directives { "", "", "", + "", "", "", + "", "", "", "", "", "", "", + "", "", "", "", @@ -99,15 +102,15 @@ public class PosixDirectives implements CContext.Directives { @Override public boolean isInConfiguration() { - return Platform.includedIn(Platform.LINUX.class) || Platform.includedIn(Platform.DARWIN.class); + return Platform.includedIn(Platform.LINUX_AND_JNI.class) || Platform.includedIn(Platform.DARWIN_AND_JNI.class); } @Override public List getHeaderFiles() { List result = new ArrayList<>(Arrays.asList(commonLibs)); - if (Platform.includedIn(Platform.LINUX.class)) { + if (Platform.includedIn(Platform.LINUX_AND_JNI.class)) { result.addAll(Arrays.asList(linuxLibs)); - } else if (Platform.includedIn(Platform.DARWIN.class)) { + } else if (Platform.includedIn(Platform.DARWIN_AND_JNI.class)) { result.addAll(Arrays.asList(darwinLibs)); } else { throw VMError.shouldNotReachHere("Unsupported OS"); diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/Pthread.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/Pthread.java index 5dd2f09de66c..af118640ca90 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/Pthread.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/Pthread.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,8 +35,8 @@ import org.graalvm.nativeimage.c.type.CIntPointer; import org.graalvm.nativeimage.c.type.WordPointer; import org.graalvm.nativeimage.Platforms; -import org.graalvm.nativeimage.Platform.DARWIN; -import org.graalvm.nativeimage.Platform.LINUX; +import org.graalvm.nativeimage.Platform.DARWIN_AND_JNI; +import org.graalvm.nativeimage.Platform.LINUX_AND_JNI; import org.graalvm.word.ComparableWord; import org.graalvm.word.PointerBase; import org.graalvm.word.UnsignedWord; @@ -50,7 +50,7 @@ * Definitions manually translated from the C header file pthread.h. */ @CContext(PosixDirectives.class) -@Platforms({DARWIN.class, LINUX.class}) +@Platforms({DARWIN_AND_JNI.class, LINUX_AND_JNI.class}) @CLibrary("pthread") public class Pthread { diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/Sched.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/Sched.java index c1bfe55dbca8..ec2288746771 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/Sched.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/Sched.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,8 +25,8 @@ package com.oracle.svm.core.posix.headers; import org.graalvm.nativeimage.Platforms; -import org.graalvm.nativeimage.Platform.DARWIN; -import org.graalvm.nativeimage.Platform.LINUX; +import org.graalvm.nativeimage.Platform.DARWIN_AND_JNI; +import org.graalvm.nativeimage.Platform.LINUX_AND_JNI; import org.graalvm.nativeimage.c.CContext; import org.graalvm.nativeimage.c.function.CFunction; @@ -36,7 +36,7 @@ * The definitions I need, manually translated from the C header file . */ -@Platforms({DARWIN.class, LINUX.class}) +@Platforms({DARWIN_AND_JNI.class, LINUX_AND_JNI.class}) @CContext(PosixDirectives.class) public class Sched { diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/SysParam.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/SysParam.java new file mode 100644 index 000000000000..df825a7dffe7 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/SysParam.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2019, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.svm.core.posix.headers; + +import org.graalvm.nativeimage.Platforms; +import org.graalvm.nativeimage.Platform.DARWIN; +import org.graalvm.nativeimage.Platform.LINUX; +import org.graalvm.nativeimage.c.CContext; +import org.graalvm.nativeimage.c.function.CFunction; +import org.graalvm.nativeimage.c.function.CFunction.Transition; + +@Platforms({DARWIN.class, LINUX.class}) +@CContext(PosixDirectives.class) +public class SysParam { + + /* + * Macros from made available a C functions via implementations in + * graal/substratevm/src/com.oracle.svm.native.libchelper/src/macrosAsFunctions.c + */ + + @CFunction(value = "sys_param_howmany", transition = Transition.NO_TRANSITION) + public static native int howmany(int x, int y); +} diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/SysSelect.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/SysSelect.java new file mode 100644 index 000000000000..62b1bf3014c3 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/SysSelect.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2019, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.svm.core.posix.headers; + +import org.graalvm.nativeimage.Platform.DARWIN; +import org.graalvm.nativeimage.Platform.LINUX; +import org.graalvm.nativeimage.Platforms; +import org.graalvm.nativeimage.c.CContext; +import org.graalvm.nativeimage.c.constant.CConstant; +import org.graalvm.nativeimage.c.function.CFunction; +import org.graalvm.nativeimage.c.function.CFunction.Transition; +import org.graalvm.nativeimage.c.struct.CStruct; +import org.graalvm.word.PointerBase; + +@Platforms({DARWIN.class, LINUX.class}) +@CContext(PosixDirectives.class) +public class SysSelect { + /* ( Allow names with underscores: Checkstyle: stop */ + + /* + * An fd_set is an opaque set manipulated by macros. E.g., FD_ZERO, FD_SET, etc., that are + * accessed via C functions in + * graal/substratevm/src/com.oracle.svm.native.libchelper/src/macrosAsFunctions.c. + */ + @CStruct + public interface fd_set extends PointerBase { + } + + @CConstant + public static native int FD_SETSIZE(); + + @CConstant + public static native int NFDBITS(); + + @CFunction + public static native int select(int nfds, fd_set readfds, fd_set writefds, + fd_set exceptfds, Time.timeval timeout); + + /* + * Macros from made available a C functions via implementations in + * graal/substratevm/src/com.oracle.svm.native.libchelper/src/macrosAsFunctions.c + */ + + @CFunction(value = "sys_select_FD_SET", transition = Transition.NO_TRANSITION) + public static native void FD_SET(int fd, PointerBase fdset); + + @CFunction(value = "sys_select_FD_ZERO", transition = Transition.NO_TRANSITION) + public static native void FD_ZERO(PointerBase fdset); + + /* } Allow names with underscores: Checkstyle: resume */ +} diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/Unistd.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/Unistd.java index 7373713c38f8..43e9688cf78b 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/Unistd.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/Unistd.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -659,7 +659,7 @@ public class Unistd { public static native int _SC_NPROCESSORS_ONLN(); @CConstant - @Platforms(Platform.LINUX.class) + @Platforms(Platform.LINUX_AND_JNI.class) public static native int _SC_PHYS_PAGES(); @CConstant @@ -1343,7 +1343,7 @@ public class Unistd { // public static native int _CS_V7_ENV(); @CConstant - @Platforms(Platform.DARWIN.class) + @Platforms(Platform.DARWIN_AND_JNI.class) public static native int _CS_DARWIN_USER_TEMP_DIR(); /** Get file-specific configuration information about PATH. */ diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/ZLib.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/ZLib.java index 5195ef6bf6e1..7868733c8c47 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/ZLib.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/ZLib.java @@ -38,17 +38,19 @@ import org.graalvm.nativeimage.c.type.CLongPointer; import org.graalvm.nativeimage.c.type.WordPointer; import org.graalvm.nativeimage.Platforms; -import org.graalvm.nativeimage.Platform.DARWIN; -import org.graalvm.nativeimage.Platform.LINUX; +import org.graalvm.nativeimage.Platform.DARWIN_AND_JNI; +import org.graalvm.nativeimage.Platform.LINUX_AND_JNI; import org.graalvm.word.PointerBase; import org.graalvm.word.UnsignedWord; //Checkstyle: stop /** - * Definitions manually translated from the C header file dlfcn.h. + * Definitions manually translated from the C header file zlib.h. + * + * We only include this class in the JNI implementation in order to add -lz to the link line. */ -@Platforms({DARWIN.class, LINUX.class}) +@Platforms({DARWIN_AND_JNI.class, LINUX_AND_JNI.class}) @CContext(PosixDirectives.class) @CLibrary("z") public class ZLib { diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/darwin/CoreFoundation.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/darwin/CoreFoundation.java index 55bea1a3c6e1..d4c43be9f929 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/darwin/CoreFoundation.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/darwin/CoreFoundation.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -41,7 +41,7 @@ */ @CContext(PosixDirectives.class) @CLibrary("-framework CoreFoundation") -@Platforms(Platform.DARWIN.class) +@Platforms(Platform.DARWIN_AND_JNI.class) public class CoreFoundation { public interface CFStringRef extends PointerBase { diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/darwin/DarwinDyld.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/darwin/DarwinDyld.java index efe67dd6097c..e88a606bbfa5 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/darwin/DarwinDyld.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/darwin/DarwinDyld.java @@ -36,7 +36,7 @@ // { Allow names with underscores: Checkstyle: stop /** Declarations of method from . */ -@Platforms(Platform.DARWIN.class) +@Platforms(Platform.DARWIN_AND_JNI.class) @CContext(PosixDirectives.class) public class DarwinDyld { diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/darwin/DarwinErrno.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/darwin/DarwinErrno.java index e0c0114c8230..028255d9d14e 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/darwin/DarwinErrno.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/darwin/DarwinErrno.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,7 +35,7 @@ //Checkstyle: stop -@Platforms(Platform.DARWIN.class) +@Platforms(Platform.DARWIN_AND_JNI.class) class DarwinErrno { @TargetClass(com.oracle.svm.core.posix.headers.Errno.class) diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/darwin/DarwinPthread.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/darwin/DarwinPthread.java index a46958a97b0d..652c6a642c1f 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/darwin/DarwinPthread.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/darwin/DarwinPthread.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -38,7 +38,7 @@ import com.oracle.svm.core.posix.headers.Pthread; @CContext(PosixDirectives.class) -@Platforms(Platform.DARWIN.class) +@Platforms(Platform.DARWIN_AND_JNI.class) @CLibrary("pthread") public class DarwinPthread { diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/darwin/DarwinSysctl.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/darwin/DarwinSysctl.java index b4768513bd12..f44b3dcc4ae5 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/darwin/DarwinSysctl.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/darwin/DarwinSysctl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -32,7 +32,7 @@ import com.oracle.svm.core.posix.headers.PosixDirectives; /** Declarations of method from . */ -@Platforms(Platform.DARWIN.class) +@Platforms(Platform.DARWIN_AND_JNI.class) @CContext(PosixDirectives.class) public class DarwinSysctl { // { Allow names with underscores: Checkstyle: stop diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/darwin/DarwinTime.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/darwin/DarwinTime.java index 839f8d0e0bc4..de608be86493 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/darwin/DarwinTime.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/darwin/DarwinTime.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2012, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2012, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -37,7 +37,7 @@ //Checkstyle: stop @CContext(PosixDirectives.class) -@Platforms(Platform.DARWIN.class) +@Platforms(Platform.DARWIN_AND_JNI.class) public class DarwinTime { @CStruct("struct mach_timebase_info") diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/darwin/DarwinVirtualMemory.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/darwin/DarwinVirtualMemory.java index b2a5716a49cb..de6680e50182 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/darwin/DarwinVirtualMemory.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/darwin/DarwinVirtualMemory.java @@ -37,7 +37,7 @@ //Checkstyle: stop @CContext(PosixDirectives.class) -@Platforms(Platform.DARWIN.class) +@Platforms(Platform.DARWIN_AND_JNI.class) public class DarwinVirtualMemory { @CFunction(transition = CFunction.Transition.NO_TRANSITION) diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/linux/LinuxErrno.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/linux/LinuxErrno.java index 5f06ccf695e6..8e685869bcec 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/linux/LinuxErrno.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/linux/LinuxErrno.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,7 +35,7 @@ //Checkstyle: stop -@Platforms(Platform.LINUX.class) +@Platforms(Platform.LINUX_AND_JNI.class) class LinuxErrno { @TargetClass(com.oracle.svm.core.posix.headers.Errno.class) diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/linux/LinuxPthread.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/linux/LinuxPthread.java index 46e5d749df82..7317dc9d758b 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/linux/LinuxPthread.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/linux/LinuxPthread.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,7 +35,7 @@ @CContext(PosixDirectives.class) @CLibrary("pthread") -@Platforms(Platform.LINUX.class) +@Platforms(Platform.LINUX_AND_JNI.class) public class LinuxPthread { /* { Allow names with underscores: Checkstyle: stop */ diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/linux/LinuxTime.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/linux/LinuxTime.java index 4839caaacf6f..784100c6c491 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/linux/LinuxTime.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/headers/linux/LinuxTime.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -46,7 +46,7 @@ * Definitions manually translated from the C header file sys/time.h. */ @CContext(PosixDirectives.class) -@Platforms(Platform.LINUX.class) +@Platforms(Platform.LINUX_AND_JNI.class) public class LinuxTime extends Time { /** Identifier for system-wide realtime clock. */ diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/linux/LinuxImageHeapProvider.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/linux/LinuxImageHeapProvider.java index 3b7a22584aa4..c31581c984aa 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/linux/LinuxImageHeapProvider.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/linux/LinuxImageHeapProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -79,11 +79,13 @@ import jdk.vm.ci.code.MemoryBarriers; @AutomaticFeature -@Platforms(Platform.LINUX.class) +@Platforms(Platform.LINUX_AND_JNI.class) class LinuxImageHeapProviderFeature implements Feature { @Override - public void afterRegistration(AfterRegistrationAccess access) { - ImageSingletons.add(ImageHeapProvider.class, new LinuxImageHeapProvider()); + public void duringSetup(DuringSetupAccess access) { + if (!ImageSingletons.contains(ImageHeapProvider.class)) { + ImageSingletons.add(ImageHeapProvider.class, new LinuxImageHeapProvider()); + } } } diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/linux/LinuxJavaNetCloseImpl.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/linux/LinuxJavaNetCloseImpl.java new file mode 100644 index 000000000000..26626d492c58 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/linux/LinuxJavaNetCloseImpl.java @@ -0,0 +1,232 @@ +/* + * Copyright (c) 2019, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.svm.core.posix.linux; + +import org.graalvm.nativeimage.Feature; +import org.graalvm.nativeimage.ImageSingletons; +import org.graalvm.nativeimage.Platform; +import org.graalvm.nativeimage.Platforms; +import org.graalvm.nativeimage.StackValue; +import org.graalvm.nativeimage.c.type.CIntPointer; +import org.graalvm.word.WordFactory; + +import com.oracle.svm.core.annotate.AutomaticFeature; +import com.oracle.svm.core.posix.PosixJavaNetClose; +import com.oracle.svm.core.posix.headers.Errno; +import com.oracle.svm.core.posix.headers.Poll; +import com.oracle.svm.core.posix.headers.Socket; +import com.oracle.svm.core.posix.headers.Time; +import com.oracle.svm.core.posix.headers.Unistd; + +/** + * Translation of the mechanisms in /jdk8u-dev/jdk/src/solaris/native/java/net/linux_close.c that + * implements interruption of blocking operations. The mechanism is not a direct translation of the + * C functions, which maintain lists of pthreads, and file descriptors, etc. Rather the mechanism is + * implemented in Java using Java threads and Thread.interrupt. + * + * Also in this class are more traditional translations of the C functions that do blocking + * operations. Where the implementations are identical between platforms, the shared code lives in + * {@link PosixJavaNetClose}. + */ +@Platforms({Platform.LINUX.class}) +public class LinuxJavaNetCloseImpl extends PosixJavaNetClose { + + protected LinuxJavaNetCloseImpl() { + /* Nothing to do. */ + } + + /** + * { @formatter:off + * 171 * Close or dup2 a file descriptor ensuring that all threads blocked on + * 172 * the file descriptor are notified via a wakeup signal. + * 173 * + * 174 * fd1 < 0 => close(fd2) + * 175 * fd1 >= 0 => dup2(fd1, fd2) + * 176 * + * 177 * Returns -1 with errno set if operation fails. + * } @formatter:on + */ + @Override + protected int closefd(int fd1, int fd2) { + PosixJavaNetClose.FdEntry fdEntry = getFdEntry(fd2); + if (fdEntry == null) { + Errno.set_errno(Errno.EBADF()); + return -1; + } + int rv; + /* Lock the fd to hold-off additional I/O on this fd. */ + /* { Allow synchronization: Checkstyle: stop. */ + synchronized (fdEntry) { + /* } Allow synchronization: Checkstyle: resume. */ + /* And close/dup the file descriptor (restart if interrupted by signal) */ + do { + if (fd1 < 0) { + rv = Unistd.close(fd2); + } else { + rv = Unistd.dup2(fd1, fd2); + } + } while ((rv == -1) && (Errno.errno() == Errno.EINTR())); + /* Send a wakeup signal to all threads blocked on this file descriptor. */ + fdEntry.getThreadList().forEach((threadEntry) -> { + threadEntry.setIntr(true); + threadEntry.getThread().interrupt(); + }); + } + return rv; + } + + /* + * Implementations of the methods that use interruptible I/O. + */ + + /* { Allow names with underscores: Checkstyle: stop */ + + /* + * The implementation of NET_RecvFrom in linux_close.c has a bug in that it returns before + * storing `socklen` in `fromlen`. The JDK has fixed the issue by using the same implementation + * that bsd_close.c uses, so I am not providing a platform-specific implementation. + */ + + /* + * The implementation of NET_Accept from linux_close.c has a bug in that it returns before + * storing `socklen` into `addrlen`. But the implementation is different from the implementation + * in bsd_close.c, so I am providing a platform-specific implementation that removes the bug. + */ + + /* { Do not re-wrap commented-out code. @formatter:off */ + // 304 int NET_Accept(int s, struct sockaddr *addr, int *addrlen) { + @Override + public int NET_Accept(int s, Socket.sockaddr addr, CIntPointer addrlen) { + // 305 socklen_t socklen = *addrlen; + CIntPointer socklen = StackValue.get(CIntPointer.class); + socklen.write(addrlen.read()); + // 306 BLOCKING_IO_RETURN_INT( s, accept(s, addr, &socklen) ); + int result = BLOCKING_IO_RETURN_INT(s, () -> { + return Socket.accept(s, addr, socklen); + }); + // 307 *addrlen = socklen; + addrlen.write(socklen.read()); + return result; + } + /* } Do not re-wrap commented-out code. @formatter:on */ + + /* { Do not re-wrap commented-out code. @formatter:off */ + // 326 /* + // 327 * Wrapper for poll(s, timeout). + // 328 * Auto restarts with adjusted timeout if interrupted by + // 329 * signal other than our wakeup signal. + // 330 */ + // 331 int NET_Timeout0(int s, long timeout, long currentTime) { + @Override + public int NET_Timeout0(int s, long timeoutArg, long currentTime) { + /* Do not modify argument. */ + long timeout = timeoutArg; + // 332 long prevtime = currentTime, newtime; + long prevtime = currentTime; + long newtime; + // 333 struct timeval t; + Time.timeval t = StackValue.get(Time.timeval.class); + // 334 fdEntry_t *fdEntry = getFdEntry(s); + PosixJavaNetClose.FdEntry fdEntry = getFdEntry(s); + // 335 + // 336 /* + // 337 * Check that fd hasn't been closed. + // 338 */ + // 339 if (fdEntry == NULL) { + if (fdEntry == null) { + // 340 errno = EBADF; + Errno.set_errno(Errno.EBADF()); + // 341 return -1; + return -1; + } + // 343 + // 344 for(;;) { + for (;;) { + // 345 struct pollfd pfd; + Poll.pollfd pfd = StackValue.get(Poll.pollfd.class); + // 346 int rv; + int rv; + // 347 threadEntry_t self; + PosixJavaNetClose.ThreadEntry self = new PosixJavaNetClose.ThreadEntry(); + // 348 + // 349 /* + // 350 * Poll the fd. If interrupted by our wakeup signal + // 351 * errno will be set to EBADF. + // 352 */ + // 353 pfd.fd = s; + pfd.set_fd(s); + // 354 pfd.events = POLLIN | POLLERR; + pfd.set_events(Poll.POLLIN() | Poll.POLLERR()); + // 355 + // 356 startOp(fdEntry, &self); + startOp(fdEntry, self); + // 357 rv = poll(&pfd, 1, timeout); + /* Note the cast from `long timeout` to `int`. */ + rv = Poll.poll(pfd, 1, (int) timeout); + // 358 endOp(fdEntry, &self); + endOp(fdEntry, self); + // 359 + // 360 /* + // 361 * If interrupted then adjust timeout. If timeout + // 362 * has expired return 0 (indicating timeout expired). + // 363 */ + // 364 if (rv < 0 && errno == EINTR) { + if ((rv < 0) && (Errno.errno() == Errno.EINTR())) { + // 365 if (timeout > 0) { + if (timeout > 0) { + // 366 gettimeofday(&t, NULL); + Time.gettimeofday(t, WordFactory.nullPointer()); + // 367 newtime = t.tv_sec * 1000 + t.tv_usec / 1000; + newtime = ((t.tv_sec() * 1000) + (t.tv_usec() / 1000)); + // 368 timeout -= newtime - prevtime; + timeout -= newtime - prevtime; + // 369 if (timeout <= 0) { + if (timeout <= 0) { + // 370 return 0; + return 0; + } + // 372 prevtime = newtime; + prevtime = newtime; + } + } else { + // 375 return rv; + return rv; + } + // 377 + } + } + /* } Do not re-wrap commented-out code. @formatter:on */ + + /* } Allow names with underscores: Checkstyle: resume */ +} + +@Platforms({Platform.LINUX.class}) +@AutomaticFeature +class LinuxJavaNetCloseFeature implements Feature { + @Override + public void afterRegistration(AfterRegistrationAccess access) { + ImageSingletons.add(PosixJavaNetClose.class, new LinuxJavaNetCloseImpl()); + } +} diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/linux/LinuxNIOSubstitutions.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/linux/LinuxNIOSubstitutions.java index 1f27675e00c7..e8ba2c8ce864 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/linux/LinuxNIOSubstitutions.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/linux/LinuxNIOSubstitutions.java @@ -26,7 +26,7 @@ import java.io.IOException; -import org.graalvm.compiler.serviceprovider.GraalServices; +import org.graalvm.compiler.serviceprovider.JavaVersionUtil; import org.graalvm.nativeimage.Feature; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; @@ -429,7 +429,7 @@ static final class EPollArrayWrapperFeature implements Feature { @Override public void duringSetup(DuringSetupAccess access) { - if (GraalServices.Java8OrEarlier) { + if (JavaVersionUtil.Java8OrEarlier) { /* This class only exists on JDK-8 and earlier platforms. */ RuntimeClassInitialization.rerunClassInitialization(access.findClassByName("sun.nio.ch.EPollArrayWrapper")); } diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/linux/LinuxPhysicalMemory.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/linux/LinuxPhysicalMemory.java index d33083ee4be7..11365bf29dee 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/linux/LinuxPhysicalMemory.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/linux/LinuxPhysicalMemory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -42,7 +42,7 @@ import com.oracle.svm.core.posix.headers.Unistd; import com.oracle.svm.core.util.UnsignedUtils; -@Platforms(Platform.LINUX.class) +@Platforms(Platform.LINUX_AND_JNI.class) class LinuxPhysicalMemory extends PhysicalMemory { static class PhysicalMemorySupportImpl implements PhysicalMemorySupport { diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/linux/LinuxProcessPropertiesSupport.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/linux/LinuxProcessPropertiesSupport.java index a0ca2576526d..202ba329bc0b 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/linux/LinuxProcessPropertiesSupport.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/linux/LinuxProcessPropertiesSupport.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -33,7 +33,7 @@ import com.oracle.svm.core.annotate.AutomaticFeature; import com.oracle.svm.core.posix.PosixProcessPropertiesSupport; -@Platforms(Platform.LINUX.class) +@Platforms(Platform.LINUX_AND_JNI.class) public class LinuxProcessPropertiesSupport extends PosixProcessPropertiesSupport { @Override diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/linux/LinuxStackOverflowSupport.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/linux/LinuxStackOverflowSupport.java index b902a6e6bd2a..72c393854ca3 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/linux/LinuxStackOverflowSupport.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/linux/LinuxStackOverflowSupport.java @@ -38,7 +38,7 @@ import com.oracle.svm.core.posix.headers.Pthread; import com.oracle.svm.core.stack.StackOverflowCheck; -@Platforms({Platform.LINUX.class}) +@Platforms({Platform.LINUX_AND_JNI.class}) class LinuxStackOverflowSupport implements StackOverflowCheck.OSSupport { @Uninterruptible(reason = "Called while thread is being attached to the VM, i.e., when the thread state is not yet set up.") @@ -67,7 +67,7 @@ public UnsignedWord lookupStackEnd() { } } -@Platforms({Platform.LINUX.class}) +@Platforms({Platform.LINUX_AND_JNI.class}) @AutomaticFeature class LinuxStackOverflowSupportFeature implements Feature { @Override diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/linux/LinuxSubstitutions.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/linux/LinuxSubstitutions.java index 4f4d8a4f5dba..d6887a62e64f 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/linux/LinuxSubstitutions.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/linux/LinuxSubstitutions.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -40,7 +40,7 @@ import com.oracle.svm.core.posix.headers.Time.timeval; import com.oracle.svm.core.posix.headers.Time.timezone; -@Platforms(Platform.LINUX.class) +@Platforms(Platform.LINUX_AND_JNI.class) @TargetClass(java.lang.System.class) final class Target_java_lang_System { diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/linux/LinuxSystemPropertiesSupport.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/linux/LinuxSystemPropertiesSupport.java index fdc97cceabab..c3e584f330bb 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/linux/LinuxSystemPropertiesSupport.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/linux/LinuxSystemPropertiesSupport.java @@ -37,7 +37,7 @@ import com.oracle.svm.core.posix.headers.Paths; import com.oracle.svm.core.posix.headers.Utsname; -@Platforms({Platform.LINUX.class}) +@Platforms({Platform.LINUX_AND_JNI.class}) public class LinuxSystemPropertiesSupport extends PosixSystemPropertiesSupport { @Override @@ -55,7 +55,7 @@ protected String osVersionValue() { } } -@Platforms({Platform.LINUX.class}) +@Platforms({Platform.LINUX_AND_JNI.class}) @AutomaticFeature class LinuxSystemPropertiesFeature implements Feature { @Override diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/pthread/PthreadConditionUtils.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/pthread/PthreadConditionUtils.java index 4dd29d9c7390..ea824abf23e6 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/pthread/PthreadConditionUtils.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/pthread/PthreadConditionUtils.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -49,7 +49,7 @@ public static int initCondition(Pthread.pthread_cond_t cond) { return status; } - if (Platform.includedIn(Platform.LINUX.class)) { + if (Platform.includedIn(Platform.LINUX_AND_JNI.class)) { /* * On Linux, CLOCK_MONOTONIC is also used in the implementation of System.nanoTime, so * we can safely assume that it is present. @@ -69,7 +69,7 @@ private static void getAbsoluteTimeNanos(timespec result) { * We need the real-time clock to compute absolute deadlines when a conditional wait should * return, but calling System.currentTimeMillis reduces the resolution too much. */ - if (Platform.includedIn(Platform.LINUX.class)) { + if (Platform.includedIn(Platform.LINUX_AND_JNI.class)) { /* * Linux is easy, we can just access the clock that we registered as the attribute when * the condition was created. diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/pthread/PthreadVMLockSupport.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/pthread/PthreadVMLockSupport.java index e70a07c1edb1..2b7fd2670a98 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/pthread/PthreadVMLockSupport.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/pthread/PthreadVMLockSupport.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -58,7 +58,7 @@ * implemented via pthreads. */ @AutomaticFeature -@Platforms({Platform.LINUX.class, Platform.DARWIN.class}) +@Platforms({Platform.LINUX_AND_JNI.class, Platform.DARWIN_AND_JNI.class}) final class PthreadVMLockFeature implements Feature { private final ClassInstanceReplacer mutexReplacer = new ClassInstanceReplacer(VMMutex.class) { diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/thread/PosixJavaThreads.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/thread/PosixJavaThreads.java index 11ce57a2047c..c48ab8a9b237 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/thread/PosixJavaThreads.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/thread/PosixJavaThreads.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -206,7 +206,7 @@ protected void noteThreadStart(Thread thread) { } @TargetClass(Thread.class) -@Platforms({Platform.LINUX.class, Platform.DARWIN.class}) +@Platforms({Platform.LINUX_AND_JNI.class, Platform.DARWIN_AND_JNI.class}) final class Target_java_lang_Thread { @Inject @RecomputeFieldValue(kind = RecomputeFieldValue.Kind.Reset)// boolean hasPthreadIdentifier; @@ -363,7 +363,7 @@ public ParkEvent create() { } @AutomaticFeature -@Platforms({Platform.LINUX.class, Platform.DARWIN.class}) +@Platforms({Platform.LINUX_AND_JNI.class, Platform.DARWIN_AND_JNI.class}) class PosixThreadsFeature implements Feature { @Override public void afterRegistration(AfterRegistrationAccess access) { diff --git a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/thread/PosixVMThreads.java b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/thread/PosixVMThreads.java index 70925ba40c31..669147dfff0e 100644 --- a/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/thread/PosixVMThreads.java +++ b/substratevm/src/com.oracle.svm.core.posix/src/com/oracle/svm/core/posix/thread/PosixVMThreads.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -90,7 +90,7 @@ public void failFatally(int code, CCharPointer message) { } @AutomaticFeature -@Platforms({Platform.LINUX.class, Platform.DARWIN.class}) +@Platforms({Platform.LINUX_AND_JNI.class, Platform.DARWIN_AND_JNI.class}) class PosixVMThreadsFeature implements Feature { @Override public void afterRegistration(AfterRegistrationAccess access) { diff --git a/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsJavaLangSubstitutions.java b/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsJavaLangSubstitutions.java old mode 100644 new mode 100755 index 97766d10224d..5f1eceaa3264 --- a/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsJavaLangSubstitutions.java +++ b/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsJavaLangSubstitutions.java @@ -32,7 +32,6 @@ import com.oracle.svm.core.annotate.Substitute; import com.oracle.svm.core.annotate.TargetClass; import com.oracle.svm.core.annotate.Uninterruptible; -import com.oracle.svm.core.windows.headers.LibC; @TargetClass(java.lang.System.class) @Platforms(Platform.WINDOWS.class) @@ -58,16 +57,6 @@ public static String mapLibraryName(String libname) { } } -@TargetClass(className = "java.lang.Shutdown") -@Platforms(Platform.WINDOWS.class) -final class Target_java_lang_Shutdown { - - @Substitute - static void halt0(int status) { - LibC.exit(status); - } -} - /** Dummy class to have a class with the file's name. */ @Platforms(Platform.WINDOWS.class) public final class WindowsJavaLangSubstitutions { diff --git a/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsNativeLibraryFeature.java b/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsNativeLibraryFeature.java index 1f92648bb2dc..6aedeaf1ec9c 100644 --- a/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsNativeLibraryFeature.java +++ b/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/WindowsNativeLibraryFeature.java @@ -36,8 +36,8 @@ import org.graalvm.word.WordFactory; import com.oracle.svm.core.annotate.AutomaticFeature; +import com.oracle.svm.core.jdk.Jvm; import com.oracle.svm.core.jdk.PlatformNativeLibrarySupport; -import com.oracle.svm.core.windows.headers.Jvm; import com.oracle.svm.core.windows.headers.WinBase; @AutomaticFeature @@ -50,39 +50,6 @@ public void afterRegistration(AfterRegistrationAccess access) { } class WindowsNativeLibrarySupport implements PlatformNativeLibrarySupport { - static final String[] builtInPkgNatives = { - "Java_com_sun_demo_jvmti_hprof", - "Java_com_sun_java_util_jar_pack", - "Java_com_sun_net_ssl", - "Java_com_sun_nio_file", - "Java_com_sun_security_cert_internal_x509", - "Java_java_io", - "Java_java_lang", - "Java_java_math", - "Java_java_net", - "Java_java_nio", - "Java_java_security", - "Java_java_text", - "Java_java_time", - "Java_java_util", - "Java_javax_net", - "Java_javax_script", - "Java_javax_security", - "Java_jdk_internal_org", - "Java_jdk_internal_util", - "Java_jdk_net", - "Java_sun_invoke", - "Java_sun_launcher", - "Java_sun_net", - "Java_sun_nio", - "Java_sun_reflect", - "Java_sun_security", - "Java_sun_text", - "Java_sun_util", - - /* SVM Specific packages */ - "Java_com_oracle_svm_core_jdk" - }; static void initialize() { ImageSingletons.add(PlatformNativeLibrarySupport.class, new WindowsNativeLibrarySupport()); @@ -115,19 +82,6 @@ public PointerBase findBuiltinSymbol(String name) { } } - @Override - public boolean isBuiltinPkgNative(String name) { - // Do a quick check first - if (name.startsWith("Java_")) { - for (String str : builtInPkgNatives) { - if (name.startsWith(str)) { - return true; - } - } - } - return false; - } - class WindowsNativeLibrary implements NativeLibrary { private final String canonicalIdentifier; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/c/function/CEntryPointNativeFunctions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/c/function/CEntryPointNativeFunctions.java index 9e228d113d38..a10999ebf2dd 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/c/function/CEntryPointNativeFunctions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/c/function/CEntryPointNativeFunctions.java @@ -25,6 +25,7 @@ package com.oracle.svm.core.c.function; import java.nio.ByteBuffer; +import java.util.Arrays; import java.util.function.Function; import org.graalvm.nativeimage.CurrentIsolate; @@ -34,11 +35,13 @@ import org.graalvm.nativeimage.c.struct.CPointerTo; import org.graalvm.nativeimage.c.struct.SizeOf; import org.graalvm.nativeimage.c.type.CLongPointer; +import org.graalvm.nativeimage.c.type.WordPointer; import org.graalvm.word.Pointer; import org.graalvm.word.PointerBase; import org.graalvm.word.WordFactory; import com.oracle.svm.core.SubstrateOptions; +import com.oracle.svm.core.annotate.RestrictHeapAccess; import com.oracle.svm.core.annotate.Uninterruptible; import com.oracle.svm.core.c.CGlobalData; import com.oracle.svm.core.c.CGlobalDataFactory; @@ -138,6 +141,11 @@ public static IsolateThread getCurrentThread(Isolate isolate) { "the address of its isolate structure. If an error occurs, returns NULL instead."}) @CEntryPointOptions(prologue = NoPrologue.class, epilogue = NoEpilogue.class, nameTransformation = NameTransformation.class) public static Isolate getIsolate(IsolateThread thread) { + return getIsolateOf(thread); + } + + @Uninterruptible(reason = "Called from uninterruptible code.") + private static Isolate getIsolateOf(IsolateThread thread) { Isolate isolate = WordFactory.nullPointer(); if (thread.isNull()) { // proceed to return null @@ -172,6 +180,66 @@ public static int detachThread(IsolateThread thread) { return result; } + @Uninterruptible(reason = UNINTERRUPTIBLE_REASON, calleeMustBe = false) + @CEntryPoint(name = "detach_threads", documentation = { + "Using the context of the isolate thread from the first argument, detaches the", + "threads in an array pointed to by the second argument, with the length of the", + "array given in the third argument. All of the passed threads must be in the", + "same isolate, including the first argument. None of the threads to detach may", + "execute Java code at the time of the call or later without reattaching first,", + "or their behavior will be entirely undefined. The current thread may be part of", + "the array, however, using detach_thread() should be preferred for detaching only", + "the current thread.", + "Returns 0 on success, or a non-zero value on failure."}) + @CEntryPointOptions(prologue = NoPrologue.class, epilogue = NoEpilogue.class, nameTransformation = NameTransformation.class) + public static int detachThreads(IsolateThread thread, IsolateThread array, int length) { + int result = CEntryPointActions.enter(thread); + if (result != 0) { + CEntryPointActions.leave(); + return result; + } + boolean detachCurrent = false; + if (SubstrateOptions.MultiThreaded.getValue()) { + try { + detachCurrent = detachThreadsInJava(array, length); + } catch (Throwable t) { + result = CEntryPointErrors.UNCAUGHT_EXCEPTION; + } + } + int leaveResult; + if (result == 0 && detachCurrent) { + leaveResult = CEntryPointActions.leaveDetachThread(); + } else { + leaveResult = CEntryPointActions.leave(); + } + return (result != 0) ? result : leaveResult; + } + + @RestrictHeapAccess(access = RestrictHeapAccess.Access.UNRESTRICTED, overridesCallers = true, reason = "Safe context.") + private static boolean detachThreadsInJava(IsolateThread array, int length) { + IsolateThread current = CurrentIsolate.getCurrentThread(); + Isolate currentIsolate = getIsolateOf(current); + boolean containsCurrent = false; + IsolateThread[] jarray = new IsolateThread[length]; + int count = 0; + for (int i = 0; i < length; i++) { + IsolateThread thread = ((WordPointer) array).read(count); + if (thread.equal(current)) { + containsCurrent = true; + } else if (getIsolateOf(thread).notEqual(currentIsolate)) { + throw new IllegalArgumentException("Thread is not attached to this isolate"); + } else { + jarray[count] = thread; + count++; + } + } + if (count > 0) { + jarray = Arrays.copyOf(jarray, count); + VMThreads.detachThreads(jarray); + } + return containsCurrent; + } + @Uninterruptible(reason = UNINTERRUPTIBLE_REASON) @CEntryPoint(name = "tear_down_isolate", documentation = { "Tears down the passed isolate, waiting for any attached threads to detach from", diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoEncoder.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoEncoder.java index cd8f6ab2d62b..8dbb9fd74902 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoEncoder.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/code/CodeInfoEncoder.java @@ -417,6 +417,7 @@ protected void verifyMethod(CompilationResult compilation, int compilationOffset CollectingObjectReferenceVisitor visitor = new CollectingObjectReferenceVisitor(); ReferenceMapDecoder.walkOffsetsFromPointer(WordFactory.zero(), codeInfo.getReferenceMapEncoding(), codeInfo.getReferenceMapIndex(), visitor); ReferenceMapEncoder.Input expected = (ReferenceMapEncoder.Input) infopoint.debugInfo.getReferenceMap(); + visitor.result.verify(); assert expected.equals(visitor.result); if (codeInfo.frameInfo != CodeInfoQueryResult.NO_FRAME_INFO) { @@ -587,9 +588,13 @@ class CollectingObjectReferenceVisitor implements ObjectReferenceVisitor { @Override public boolean visitObjectReference(Pointer objRef, boolean compressed) { - int offset = NumUtil.safeToInt(objRef.rawValue()); - assert !result.isOffsetMarked(offset); - result.markReferenceAtOffset(offset, compressed); + return visitObjectReferenceInline(objRef, 0, compressed); + } + + @Override + public boolean visitObjectReferenceInline(Pointer objRef, int innerOffset, boolean compressed) { + int derivedOffset = NumUtil.safeToInt(objRef.rawValue()); + result.markReferenceAtOffset(derivedOffset, derivedOffset - innerOffset, compressed); return true; } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/handles/ThreadLocalHandles.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/handles/ThreadLocalHandles.java new file mode 100644 index 000000000000..12c367368eb4 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/handles/ThreadLocalHandles.java @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2019, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.svm.core.handles; + +import org.graalvm.nativeimage.ObjectHandle; +import org.graalvm.word.SignedWord; +import org.graalvm.word.WordFactory; + +/** + * Implementation of local object handles, which are bound to a specific thread and can be created + * and destroyed implicitly or explicitly. Local handles can be managed in frames and a frame can be + * discarded in its entirety. + */ +public final class ThreadLocalHandles { + private static final int INITIAL_NUMBER_OF_FRAMES = 4; + + private static final int MIN_VALUE = Math.toIntExact(1 + nullHandle().rawValue()); + private static final int MAX_VALUE = Integer.MAX_VALUE; + + public static U nullHandle() { + return WordFactory.signed(0); + } + + public static boolean isInRange(U handle) { + return handle.rawValue() >= MIN_VALUE && handle.rawValue() <= MAX_VALUE; + } + + private Object[] objects; + private int top = MIN_VALUE; + + private int[] frameStack = new int[INITIAL_NUMBER_OF_FRAMES]; + private int frameCount = 0; + + public ThreadLocalHandles(int initialNumberOfHandles) { + objects = new Object[MIN_VALUE + initialNumberOfHandles]; + } + + private static int toIndex(T handle) { + return (int) handle.rawValue(); + } + + public int getHandleCount() { + return top - MIN_VALUE; + } + + public int pushFrame(int capacity) { + if (frameCount == frameStack.length) { + int[] oldArray = frameStack; + frameStack = new int[oldArray.length * 2]; + System.arraycopy(oldArray, 0, frameStack, 0, oldArray.length); + } + frameStack[frameCount] = top; + frameCount++; + ensureCapacity(capacity); + return frameCount; + } + + @SuppressWarnings("unchecked") + public T create(Object obj) { + if (obj == null) { + return (T) nullHandle(); + } + ensureCapacity(1); + int index = top; + objects[index] = obj; + top++; + return (T) WordFactory.signed(index); + } + + @SuppressWarnings("unchecked") + public U getObject(T handle) { + return (U) objects[toIndex(handle)]; + } + + public boolean delete(T handle) { + int index = toIndex(handle); + Object previous = objects[index]; + objects[index] = null; + return previous != null; + } + + public void popFrame() { + popFramesIncluding(frameCount); + } + + public void popFramesIncluding(int frame) { + assert frame > 0 && frame <= frameCount; + int previousTop = top; + frameCount = frame - 1; + top = frameStack[frameCount]; + for (int i = top; i < previousTop; i++) { + objects[i] = null; // so objects can be garbage collected + } + } + + public void ensureCapacity(int capacity) { + if (top + capacity >= objects.length) { + Object[] oldArray = objects; + int newLength = oldArray.length * 2; + assert newLength >= top + capacity; + objects = new Object[newLength]; + System.arraycopy(oldArray, 0, objects, 0, oldArray.length); + } + } +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/ObjectReferenceVisitor.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/ObjectReferenceVisitor.java index 6bed7d09a0fc..81301637b253 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/ObjectReferenceVisitor.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/ObjectReferenceVisitor.java @@ -61,6 +61,11 @@ default boolean visitObjectReferenceInline(Pointer objRef, boolean compressed) { return visitObjectReference(objRef, compressed); } + @RestrictHeapAccess(access = RestrictHeapAccess.Access.UNRESTRICTED, overridesCallers = true, reason = "Some implementations allocate.") + default boolean visitObjectReferenceInline(Pointer objRef, @SuppressWarnings("unused") int innerOffset, boolean compressed) { + return visitObjectReference(objRef, compressed); + } + /** * Called after all Object references have been visited. If visiting terminates because a * visitor returned false, this method might not be called. diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/ReferenceMapDecoder.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/ReferenceMapDecoder.java index 70d8dcdfb164..59c0567b1523 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/ReferenceMapDecoder.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/ReferenceMapDecoder.java @@ -24,6 +24,7 @@ */ package com.oracle.svm.core.heap; +import org.graalvm.compiler.core.common.NumUtil; import org.graalvm.word.Pointer; import org.graalvm.word.PointerBase; import org.graalvm.word.UnsignedWord; @@ -64,7 +65,7 @@ public static boolean walkOffsetsFromPointer(PointerBase baseAddress, byte[] ref */ int shift; long b; - // Size of gap in bytes + // Size of gap in bytes (negative means the next pointer has derived pointers) long gap = 0; shift = 0; do { @@ -73,6 +74,9 @@ public static boolean walkOffsetsFromPointer(PointerBase baseAddress, byte[] ref shift += 7; idx++; } while ((b & 0x80) != 0); + if ((b & 0x40) != 0 && shift < 64) { + gap |= -1L << shift; + } // Number of pointers (sign distinguishes between compression and uncompression) long count = 0; shift = 0; @@ -90,16 +94,71 @@ public static boolean walkOffsetsFromPointer(PointerBase baseAddress, byte[] ref break; // reached end of table } + boolean derived = false; + if (gap < 0) { + /* Derived pointer run */ + gap = -(gap + 1); + derived = true; + } + objRef = objRef.add(WordFactory.unsigned(gap)); boolean compressed = (count < 0); UnsignedWord refSize = compressed ? compressedSize : uncompressedSize; count = (count < 0) ? -count : count; - for (long c = 0; c < count; c += 1) { - final boolean visitResult = visitor.visitObjectReferenceInline(objRef, compressed); + + if (derived) { + /* + * To correctly relocate a derived pointer, we need to know the value pointed to by + * the base reference and the derived reference before either one is relocated. This + * allows us to compute the inner offset, i.e. how much into the actual object does + * the derived reference point to. + */ + Pointer basePtr = baseAddress.isNull() ? objRef : objRef.readWord(0); + + final boolean visitResult = visitor.visitObjectReferenceInline(objRef, 0, compressed); if (!visitResult) { return false; } + + /* count in this case is the number of derived references for this base pointer */ + for (long d = 0; d < count; d++) { + /* Offset in words from the base reference to the derived reference */ + long refOffset = 0; + shift = 0; + do { + b = ByteArrayReader.getU1(referenceMapEncoding, idx); + refOffset |= (b & 0x7f) << shift; + shift += 7; + idx++; + } while ((b & 0x80) != 0); + if ((b & 0x40) != 0 && shift < 64) { + refOffset |= -1L << shift; + } + + Pointer derivedRef; + if (refOffset >= 0) { + derivedRef = objRef.add(WordFactory.unsigned(refOffset).multiply(refSize)); + } else { + derivedRef = objRef.subtract(WordFactory.unsigned(-refOffset).multiply(refSize)); + } + + Pointer derivedPtr = baseAddress.isNull() ? derivedRef : derivedRef.readWord(0); + int innerOffset = NumUtil.safeToInt(derivedPtr.subtract(basePtr).rawValue()); + + final boolean derivedVisitResult = visitor.visitObjectReferenceInline(derivedRef, innerOffset, compressed); + if (!derivedVisitResult) { + return false; + } + } objRef = objRef.add(refSize); + } else { + for (long c = 0; c < count; c += 1) { + final boolean visitResult = visitor.visitObjectReferenceInline(objRef, 0, compressed); + if (!visitResult) { + return false; + } + objRef = objRef.add(refSize); + } } } return true; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/ReferenceMapEncoder.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/ReferenceMapEncoder.java index 6a22bd5ad302..241392046a69 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/ReferenceMapEncoder.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/ReferenceMapEncoder.java @@ -30,6 +30,7 @@ import java.util.Map; import java.util.NoSuchElementException; import java.util.PrimitiveIterator; +import java.util.Set; import org.graalvm.compiler.core.common.util.TypeConversion; import org.graalvm.compiler.core.common.util.TypeWriter; @@ -56,6 +57,10 @@ public interface OffsetIterator extends PrimitiveIterator.OfInt { * @throws NoSuchElementException */ boolean isNextCompressed(); + + boolean isNextDerived(); + + Set getDerivedOffsets(int baseOffset); } public interface Input { @@ -138,38 +143,52 @@ private long encode(OffsetIterator offsets) { int expectedOffset = 0; while (offsets.hasNext()) { boolean compressed = offsets.isNextCompressed(); + boolean derived = offsets.isNextDerived(); int offset = offsets.nextInt(); - if (offset == expectedOffset && compressed == expectedCompressed) { + if (offset == expectedOffset && compressed == expectedCompressed && !derived) { // An adjacent offset in this run. run += 1; } else { assert offset >= expectedOffset : "values must be strictly increasing"; if (run > 0) { // The end of a run. Encode the *previous* gap and this run of offsets. - encodeRun(gap, run, expectedCompressed); + encodeRun(gap, run, expectedCompressed, false); } // Beginning of the next gap+run pair. gap = offset - expectedOffset; run = 1; } int size = (compressed ? compressedSize : uncompressedSize); + if (derived) { + encodeDerivedRun(gap, offset, offsets.getDerivedOffsets(offset), compressed, size); + run = 0; + gap = 0; + } expectedOffset = offset + size; expectedCompressed = compressed; } if (run > 0) { - encodeRun(gap, run, expectedCompressed); + encodeRun(gap, run, expectedCompressed, false); } encodeEndOfTable(); return startIndex; } - private void encodeRun(int gap, int refsCount, boolean compressed) { + private void encodeRun(int gap, int refsCount, boolean compressed, boolean derived) { assert gap >= 0 && refsCount >= 0; - writeBuffer.putUV(gap); + writeBuffer.putSV(derived ? -gap - 1 : gap); writeBuffer.putSV(compressed ? -refsCount : refsCount); } + private void encodeDerivedRun(int gap, int baseOffset, Set derivedOffsets, boolean compressed, int size) { + encodeRun(gap, derivedOffsets.size(), compressed, true); + for (int derivedOffset : derivedOffsets) { + assert baseOffset % size == 0 && derivedOffset % size == 0 && derivedOffset != baseOffset; + writeBuffer.putSV((derivedOffset - baseOffset) / size); + } + } + private void encodeEndOfTable() { - encodeRun(0, 0, false); + encodeRun(0, 0, false, false); } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/SubstrateReferenceMap.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/SubstrateReferenceMap.java index d83e402870ba..b73bb12ba4f5 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/SubstrateReferenceMap.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/SubstrateReferenceMap.java @@ -26,8 +26,13 @@ import java.util.BitSet; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; import java.util.NoSuchElementException; +import java.util.Set; + +import org.graalvm.collections.EconomicMap; +import org.graalvm.collections.Equivalence; import com.oracle.svm.core.FrameAccess; import com.oracle.svm.core.config.ConfigurationValues; @@ -41,6 +46,9 @@ public class SubstrateReferenceMap extends ReferenceMap implements ReferenceMapE private final BitSet input = new BitSet(); + /* Maps base references with references pointing to the interior of that object */ + private EconomicMap> derived; + private Map debugAllUsedRegisters; private Map debugAllUsedStackSlots; @@ -65,6 +73,32 @@ public void markReferenceAtOffset(int offset, boolean compressed) { } } + public void markReferenceAtOffset(int offset, int baseOffset, boolean compressed) { + if (offset == baseOffset) { + /* We might have already seen the offset as a base to a derived offset */ + if (derived == null || !derived.containsKey(baseOffset)) { + markReferenceAtOffset(baseOffset, compressed); + } + return; + } + + if (!isOffsetMarked(baseOffset)) { + markReferenceAtOffset(baseOffset, compressed); + } + + if (derived == null) { + derived = EconomicMap.create(Equivalence.DEFAULT); + } + Set derivedOffsets = derived.get(baseOffset); + if (derivedOffsets == null) { + derivedOffsets = new HashSet<>(); + derived.put(baseOffset, derivedOffsets); + } + + assert !derivedOffsets.contains(offset); + derivedOffsets.add(offset); + } + private boolean isValidToMark(int offset, boolean isCompressed) { int uncompressedSize = FrameAccess.uncompressedReferenceSize(); int compressedSize = ConfigurationValues.getObjectLayout().getReferenceSize(); @@ -141,12 +175,28 @@ public boolean isNextCompressed() { } return isOffsetCompressed(nextIndex); } + + @Override + public boolean isNextDerived() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + return derived != null && derived.containsKey(nextIndex); + } + + @Override + public Set getDerivedOffsets(int baseOffset) { + if (derived == null || !derived.containsKey(baseOffset)) { + throw new NoSuchElementException(); + } + return derived.get(baseOffset); + } }; } @Override public int hashCode() { - return input.hashCode(); + return input.hashCode() + ((derived == null) ? 0 : derived.hashCode()); } @Override @@ -155,9 +205,65 @@ public boolean equals(Object obj) { return true; } else if (obj instanceof SubstrateReferenceMap) { SubstrateReferenceMap other = (SubstrateReferenceMap) obj; - return input.equals(other.input); + if (!input.equals(other.input)) { + return false; + } + + if (derived == null || other.derived == null) { + return derived == null && other.derived == null; + } + + if (derived.size() != other.derived.size()) { + return false; + } + + for (int base : derived.getKeys()) { + if (!derived.get(base).equals(other.derived.get(base))) { + return false; + } + } + + return true; } else { return false; } } + + public boolean hasNoDerivedOffsets() { + return derived == null || derived.isEmpty(); + } + + public void verify() { + if (derived == null) { + return; + } + + for (int baseOffset : derived.getKeys()) { + for (int derivedOffset : derived.get(baseOffset)) { + assert !derived.containsKey(derivedOffset); + } + } + } + + public void dump(StringBuilder builder) { + if (input.isEmpty()) { + builder.append("[]"); + return; + } + + builder.append('['); + input.stream().forEach(offset -> { + builder.append(offset); + if (derived != null && derived.containsKey(offset)) { + builder.append(" -> {"); + for (int derivedOffset : derived.get(offset)) { + builder.append(derivedOffset); + builder.append(", "); + } + builder.replace(builder.length() - 2, builder.length(), "}"); + } + builder.append(", "); + }); + builder.replace(builder.length() - 2, builder.length(), "]"); + } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java index 07d3176c2a67..4c301a7b3355 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/DynamicHub.java @@ -43,6 +43,7 @@ import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.net.URL; +import java.security.ProtectionDomain; import java.util.ArrayList; import java.util.BitSet; import java.util.IdentityHashMap; @@ -76,6 +77,7 @@ import com.oracle.svm.core.jdk.Target_java_lang_Module; import com.oracle.svm.core.meta.SharedType; import com.oracle.svm.core.snippets.KnownIntrinsics; +import com.oracle.svm.core.util.LazyFinalReference; import com.oracle.svm.core.util.VMError; import jdk.vm.ci.meta.JavaKind; @@ -254,9 +256,15 @@ public final class DynamicHub implements JavaKind.FormatWithToString, AnnotatedE private GenericInfo genericInfo; private AnnotatedSuperInfo annotatedSuperInfo; - private static java.security.ProtectionDomain allPermDomain; + private static final LazyFinalReference allPermDomain = new LazyFinalReference<>(() -> { + java.security.Permissions perms = new java.security.Permissions(); + perms.add(SecurityConstants.ALL_PERMISSION); + return new java.security.ProtectionDomain(null, perms); + }); - private static Target_java_lang_Module singleModule; + private static final LazyFinalReference singleModule = new LazyFinalReference<>(Target_java_lang_Module::new); + + private final LazyFinalReference packageName = new LazyFinalReference<>(this::computePackageName); @Platforms(Platform.HOSTED_ONLY.class) public DynamicHub(String name, boolean isLocalClass, DynamicHub superType, DynamicHub componentHub, String sourceFileName, int modifiers, @@ -1050,7 +1058,23 @@ public Package getPackageJDK9OrLater() { @Substitute // @TargetElement(onlyWith = JDK9OrLater.class) public String getPackageName() { - throw VMError.unsupportedFeature("JDK9OrLater: DynamicHub.getPackageName()"); + return packageName.get(); + } + + private String computePackageName() { + String pn = null; + DynamicHub me = this; + while (me.isArray()) { + me = (DynamicHub) me.getComponentType(); + } + if (me.isPrimitive()) { + pn = "java.lang"; + } else { + String cn = me.getName(); + int dot = cn.lastIndexOf('.'); + pn = (dot != -1) ? cn.substring(0, dot).intern() : ""; + } + return pn; } @Override @@ -1071,13 +1095,8 @@ public Object[] getSigners() { } @Substitute - public Object getProtectionDomain() { - if (allPermDomain == null) { - java.security.Permissions perms = new java.security.Permissions(); - perms.add(SecurityConstants.ALL_PERMISSION); - allPermDomain = new java.security.ProtectionDomain(null, perms); - } - return allPermDomain; + public ProtectionDomain getProtectionDomain() { + return allPermDomain.get(); } @Substitute @@ -1088,10 +1107,7 @@ public boolean desiredAssertionStatus() { @Substitute // @TargetElement(onlyWith = JDK9OrLater.class) public Target_java_lang_Module getModule() { - if (singleModule == null) { - singleModule = new Target_java_lang_Module(); - } - return singleModule; + return singleModule.get(); } @Substitute // diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/SunReflectTypeSubstitutions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/SunReflectTypeSubstitutions.java index 50d969ff39de..2304fb814beb 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/SunReflectTypeSubstitutions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/hub/SunReflectTypeSubstitutions.java @@ -31,7 +31,7 @@ import java.lang.reflect.Type; import org.graalvm.compiler.core.common.SuppressFBWarnings; -import org.graalvm.compiler.serviceprovider.GraalServices; +import org.graalvm.compiler.serviceprovider.JavaVersionUtil; import com.oracle.svm.core.annotate.Alias; import com.oracle.svm.core.annotate.Delete; @@ -89,7 +89,7 @@ private Target_sun_reflect_generics_reflectiveObjects_TypeVariableImpl(GenericDe @Substitute public Type[] getBounds() { /* Variant method bodies from JDK-8 and JDK-9 to use the appropriately typed variables. */ - if (GraalServices.Java8OrEarlier) { + if (JavaVersionUtil.Java8OrEarlier) { return boundsJDK8OrEarlier; } else { Object[] value = boundsJDK9OrLater; @@ -186,7 +186,7 @@ private Target_sun_reflect_generics_reflectiveObjects_WildcardTypeImpl(FieldType @Substitute public Type[] getUpperBounds() { - if (GraalServices.Java8OrEarlier) { + if (JavaVersionUtil.Java8OrEarlier) { return upperBoundsJDK8OrEarlier; } else { Object[] value = upperBoundsJDK9OrLater; @@ -200,7 +200,7 @@ public Type[] getUpperBounds() { @Substitute public Type[] getLowerBounds() { - if (GraalServices.Java8OrEarlier) { + if (JavaVersionUtil.Java8OrEarlier) { return lowerBoundsJDK8OrEarlier; } else { Object[] value = lowerBoundsJDK9OrLater; diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/FileSystemProviderFeature.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/FileSystemProviderFeature.java deleted file mode 100644 index c389a1c73312..000000000000 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/FileSystemProviderFeature.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package com.oracle.svm.core.jdk; - -import java.nio.file.spi.FileSystemProvider; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.graalvm.nativeimage.Feature; -import org.graalvm.nativeimage.ImageSingletons; -import org.graalvm.nativeimage.Platform; -import org.graalvm.nativeimage.Platforms; - -import com.oracle.svm.core.annotate.AutomaticFeature; -import com.oracle.svm.core.annotate.Delete; -import com.oracle.svm.core.annotate.Substitute; -import com.oracle.svm.core.annotate.TargetClass; - -@AutomaticFeature -public final class FileSystemProviderFeature implements Feature { - - @Override - public void afterRegistration(AfterRegistrationAccess access) { - ImageSingletons.add(FileSystemProviderSupport.class, new FileSystemProviderSupport()); - /* - * The first invocation of FileSystemProvider.installedProviders() causes the default - * provider to be initialized (if not already initialized) and loads any other installed - * providers as described by the {@link FileSystems} class. - */ - ArrayList providers = new ArrayList<>(FileSystemProvider.installedProviders()); - /* Currently we do not support access to Java modules (jimage/jrtfs access) in SVM images */ - providers.removeIf(p -> p.getScheme().equals("jrt")); - providers.forEach(p -> addFileSystemProvider(p)); - } - - public static void addFileSystemProvider(FileSystemProvider fileSystemProvider) { - Map fileSystemProviders = ImageSingletons.lookup(FileSystemProviderSupport.class).fileSystemProviders; - fileSystemProviders.put(fileSystemProvider.getScheme(), fileSystemProvider); - } - -} - -final class FileSystemProviderSupport { - final Map fileSystemProviders; - - @Platforms(Platform.HOSTED_ONLY.class) - FileSystemProviderSupport() { - fileSystemProviders = new HashMap<>(); - } -} - -@TargetClass(java.nio.file.spi.FileSystemProvider.class) -final class Target_java_nio_file_spi_FileSystemProvider { - @Substitute - public static List installedProviders() { - return new ArrayList<>(ImageSingletons.lookup(FileSystemProviderSupport.class).fileSystemProviders.values()); - } -} - -@TargetClass(className = "jdk.internal.jrtfs.JrtFileSystemProvider", onlyWith = JDK9OrLater.class) -@Delete -final class Target_jdk_internal_jrtfs_JrtFileSystemProvider { -} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/FileSystemProviderSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/FileSystemProviderSupport.java new file mode 100644 index 000000000000..920f90f51f64 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/FileSystemProviderSupport.java @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.svm.core.jdk; + +import java.nio.file.spi.FileSystemProvider; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.graalvm.compiler.options.Option; +import org.graalvm.nativeimage.Feature; +import org.graalvm.nativeimage.ImageSingletons; +import org.graalvm.nativeimage.Platform; +import org.graalvm.nativeimage.Platforms; + +import com.oracle.svm.core.annotate.AutomaticFeature; +import com.oracle.svm.core.annotate.Delete; +import com.oracle.svm.core.annotate.Substitute; +import com.oracle.svm.core.annotate.TargetClass; +import com.oracle.svm.core.option.HostedOptionKey; + +public final class FileSystemProviderSupport { + + public static class Options { + @Option(help = "Make all supported providers returned by FileSystemProvider.installedProviders() available at run time.")// + public static final HostedOptionKey AddAllFileSystemProviders = new HostedOptionKey<>(true); + } + + final List installedProvidersMutable; + final List installedProvidersImmutable; + + @Platforms(Platform.HOSTED_ONLY.class) + FileSystemProviderSupport(List installedProviders) { + this.installedProvidersMutable = installedProviders; + this.installedProvidersImmutable = Collections.unmodifiableList(installedProviders); + } + + /** + * Registers the provided {@link FileSystemProvider}. If a provider with the same + * {@link FileSystemProvider#getScheme()} is already registered, the old provider is + * overwritten. + */ + @Platforms(Platform.HOSTED_ONLY.class) + public static void register(FileSystemProvider provider) { + List installedProviders = ImageSingletons.lookup(FileSystemProviderSupport.class).installedProvidersMutable; + + String scheme = provider.getScheme(); + for (int i = 0; i < installedProviders.size(); i++) { + /* + * The "equalsIgnoreCase" check corresponds to the way the initial list of + * installedProviders is built in + * java.nio.file.spi.FileSystemProvider.loadInstalledProviders(). + */ + if (installedProviders.get(i).getScheme().equalsIgnoreCase(scheme)) { + installedProviders.set(i, provider); + return; + } + } + installedProviders.add(provider); + } + + /** + * Removes the {@link FileSystemProvider} with the provided + * {@link FileSystemProvider#getScheme() scheme} name. + */ + @Platforms(Platform.HOSTED_ONLY.class) + public static void remove(String scheme) { + List installedProviders = ImageSingletons.lookup(FileSystemProviderSupport.class).installedProvidersMutable; + + for (int i = 0; i < installedProviders.size(); i++) { + /* + * The "equalsIgnoreCase" check corresponds to the way the initial list of + * installedProviders is built in + * java.nio.file.spi.FileSystemProvider.loadInstalledProviders(). + */ + if (installedProviders.get(i).getScheme().equalsIgnoreCase(scheme)) { + installedProviders.remove(i); + return; + } + } + } +} + +@AutomaticFeature +final class FileSystemProviderFeature implements Feature { + + @Override + public void afterRegistration(AfterRegistrationAccess access) { + List installedProviders = new ArrayList<>(); + if (FileSystemProviderSupport.Options.AddAllFileSystemProviders.getValue()) { + /* + * The first invocation of FileSystemProvider.installedProviders() causes the default + * provider to be initialized (if not already initialized) and loads any other installed + * providers as described by the {@link FileSystems} class. + * + * Even though it is not specified, it seems as if the order of elements in the list + * matters. At least the JDK implementation explicitly adds the "file" provider as the + * first element of the list. Therefore, we preserve the order of the providers observed + * during image generation. + */ + installedProviders.addAll(FileSystemProvider.installedProviders()); + } + ImageSingletons.add(FileSystemProviderSupport.class, new FileSystemProviderSupport(installedProviders)); + + /* Currently we do not support access to Java modules (jimage/jrtfs access) in images */ + FileSystemProviderSupport.remove("jrt"); + } +} + +@TargetClass(java.nio.file.spi.FileSystemProvider.class) +final class Target_java_nio_file_spi_FileSystemProvider { + @Substitute + public static List installedProviders() { + return ImageSingletons.lookup(FileSystemProviderSupport.class).installedProvidersImmutable; + } +} + +@TargetClass(className = "jdk.internal.jrtfs.JrtFileSystemProvider", onlyWith = JDK9OrLater.class) +@Delete +final class Target_jdk_internal_jrtfs_JrtFileSystemProvider { +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/FileTypeDetectorSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/FileTypeDetectorSupport.java new file mode 100644 index 000000000000..919416520e37 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/FileTypeDetectorSupport.java @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2019, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.svm.core.jdk; + +/* Checkstyle: allow reflection */ + +import java.io.IOException; +import java.lang.reflect.Field; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.spi.FileTypeDetector; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import org.graalvm.compiler.options.Option; +import org.graalvm.compiler.serviceprovider.JavaVersionUtil; +import org.graalvm.nativeimage.Feature; +import org.graalvm.nativeimage.ImageSingletons; +import org.graalvm.nativeimage.Platform; +import org.graalvm.nativeimage.Platforms; + +import com.oracle.svm.core.annotate.Alias; +import com.oracle.svm.core.annotate.AutomaticFeature; +import com.oracle.svm.core.annotate.RecomputeFieldValue; +import com.oracle.svm.core.annotate.RecomputeFieldValue.Kind; +import com.oracle.svm.core.annotate.TargetClass; +import com.oracle.svm.core.annotate.TargetElement; +import com.oracle.svm.core.option.HostedOptionKey; +import com.oracle.svm.core.util.VMError; + +import jdk.vm.ci.meta.MetaAccessProvider; +import jdk.vm.ci.meta.ResolvedJavaField; + +/** + * This class allows to customize the {@link FileTypeDetector}s used by + * {@link Files#probeContentType(Path)}. + */ +public final class FileTypeDetectorSupport { + + public static class Options { + @Option(help = "Make all supported FileTypeDetector available at run time.")// + public static final HostedOptionKey AddAllFileTypeDetectors = new HostedOptionKey<>(true); + } + + /** + * Prepends the provided {@link FileTypeDetector} at the beginning of the list of detectors. + */ + @Platforms(Platform.HOSTED_ONLY.class) + public static void addFirst(FileTypeDetector detector) { + ImageSingletons.lookup(FileTypeDetectorFeature.class).installedDetectors.add(0, Objects.requireNonNull(detector)); + } + + /** + * Appends the provided {@link FileTypeDetector} at the end of the list of detectors. + */ + @Platforms(Platform.HOSTED_ONLY.class) + public static void addLast(FileTypeDetector detector) { + ImageSingletons.lookup(FileTypeDetectorFeature.class).installedDetectors.add(Objects.requireNonNull(detector)); + } + + /** + * Overwrites the default {@link FileTypeDetector}, i.e., the detector that is used when no + * detector of the list matches. + */ + @Platforms(Platform.HOSTED_ONLY.class) + public static void setDefault(FileTypeDetector detector) { + ImageSingletons.lookup(FileTypeDetectorFeature.class).defaultFileTypeDetector = Objects.requireNonNull(detector); + } + + public static class AlwaysNullFileTypeDetector extends FileTypeDetector { + @Override + public String probeContentType(Path path) throws IOException { + return null; + } + } +} + +@AutomaticFeature +final class FileTypeDetectorFeature implements Feature { + + /* + * These fields are only used during image generation and can therefore be in the hosted-only + * feature class. + */ + List installedDetectors; + FileTypeDetector defaultFileTypeDetector; + + @Override + public void afterRegistration(AfterRegistrationAccess access) { + installedDetectors = new ArrayList<>(); + if (FileTypeDetectorSupport.Options.AddAllFileTypeDetectors.getValue()) { + Class jdkClass = access.findClassByName("java.nio.file.Files$FileTypeDetectors"); + /* Note the typo in the JDK field name on JDK 8. */ + String installedDetectorsFieldName = JavaVersionUtil.Java8OrEarlier ? "installeDetectors" : "installedDetectors"; + installedDetectors.addAll(readStaticField(jdkClass, installedDetectorsFieldName)); + defaultFileTypeDetector = readStaticField(jdkClass, "defaultFileTypeDetector"); + } + if (defaultFileTypeDetector == null) { + /* + * The JDK does not allow a null value for the defaultFileTypeDetector, so we need an + * implementation class that always returns null. + */ + defaultFileTypeDetector = new FileTypeDetectorSupport.AlwaysNullFileTypeDetector(); + } + } + + @SuppressWarnings("unchecked") + private static T readStaticField(Class clazz, String fieldName) { + try { + Field result = clazz.getDeclaredField(fieldName); + result.setAccessible(true); + return (T) result.get(null); + } catch (ReflectiveOperationException ex) { + throw VMError.shouldNotReachHere(ex); + } + } +} + +final class InstalledDetectorsComputer implements RecomputeFieldValue.CustomFieldValueComputer { + @Override + public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { + return ImageSingletons.lookup(FileTypeDetectorFeature.class).installedDetectors; + } +} + +final class DefaultDetectorComputer implements RecomputeFieldValue.CustomFieldValueComputer { + @Override + public Object compute(MetaAccessProvider metaAccess, ResolvedJavaField original, ResolvedJavaField annotated, Object receiver) { + return ImageSingletons.lookup(FileTypeDetectorFeature.class).defaultFileTypeDetector; + } +} + +@TargetClass(value = java.nio.file.Files.class, innerClass = "FileTypeDetectors") +final class Target_java_nio_file_Files_FileTypeDetectors { + + @Alias @TargetElement(onlyWith = JDK8OrEarlier.class) // + @RecomputeFieldValue(kind = Kind.Custom, declClass = InstalledDetectorsComputer.class) // + static List installeDetectors; + @Alias @TargetElement(onlyWith = JDK9OrLater.class) // + @RecomputeFieldValue(kind = Kind.Custom, declClass = InstalledDetectorsComputer.class) // + static List installedDetectors; + + @Alias @RecomputeFieldValue(kind = Kind.Custom, declClass = DefaultDetectorComputer.class) // + static FileTypeDetector defaultFileTypeDetector; +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JDK10OrEarlier.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JDK10OrEarlier.java index 9cf689c1bfdb..3220a8de0c63 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JDK10OrEarlier.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JDK10OrEarlier.java @@ -26,11 +26,11 @@ import java.util.function.BooleanSupplier; -import org.graalvm.compiler.serviceprovider.GraalServices; +import org.graalvm.compiler.serviceprovider.JavaVersionUtil; public class JDK10OrEarlier implements BooleanSupplier { @Override public boolean getAsBoolean() { - return GraalServices.JAVA_SPECIFICATION_VERSION <= 10; + return JavaVersionUtil.JAVA_SPECIFICATION_VERSION <= 10; } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JDK11OrLater.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JDK11OrLater.java index 4b6f1bf46cfe..92cdd10e2f21 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JDK11OrLater.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JDK11OrLater.java @@ -26,11 +26,11 @@ import java.util.function.BooleanSupplier; -import org.graalvm.compiler.serviceprovider.GraalServices; +import org.graalvm.compiler.serviceprovider.JavaVersionUtil; public class JDK11OrLater implements BooleanSupplier { @Override public boolean getAsBoolean() { - return GraalServices.JAVA_SPECIFICATION_VERSION >= 11; + return JavaVersionUtil.JAVA_SPECIFICATION_VERSION >= 11; } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JDK8OrEarlier.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JDK8OrEarlier.java index 835a3cc58cfa..780906d53ced 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JDK8OrEarlier.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JDK8OrEarlier.java @@ -26,11 +26,11 @@ import java.util.function.BooleanSupplier; -import org.graalvm.compiler.serviceprovider.GraalServices; +import org.graalvm.compiler.serviceprovider.JavaVersionUtil; public class JDK8OrEarlier implements BooleanSupplier { @Override public boolean getAsBoolean() { - return GraalServices.Java8OrEarlier; + return JavaVersionUtil.Java8OrEarlier; } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JDK9OrLater.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JDK9OrLater.java index 6ae19706b8a5..1bc90856035a 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JDK9OrLater.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JDK9OrLater.java @@ -26,11 +26,11 @@ import java.util.function.BooleanSupplier; -import org.graalvm.compiler.serviceprovider.GraalServices; +import org.graalvm.compiler.serviceprovider.JavaVersionUtil; public class JDK9OrLater implements BooleanSupplier { @Override public boolean getAsBoolean() { - return !GraalServices.Java8OrEarlier; + return !JavaVersionUtil.Java8OrEarlier; } } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JavaLangSubstitutions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JavaLangSubstitutions.java index ba02813886f4..9a3f20d72242 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JavaLangSubstitutions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JavaLangSubstitutions.java @@ -28,12 +28,7 @@ import static com.oracle.svm.core.snippets.KnownIntrinsics.readHub; import static com.oracle.svm.core.snippets.KnownIntrinsics.unsafeCast; -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; import java.io.File; -import java.io.FileDescriptor; -import java.io.FileInputStream; -import java.io.FileOutputStream; import java.io.InputStream; import java.io.PrintStream; import java.net.URL; @@ -54,7 +49,6 @@ import org.graalvm.compiler.core.common.SuppressFBWarnings; import org.graalvm.compiler.word.ObjectAccess; import org.graalvm.compiler.word.Word; -import org.graalvm.nativeimage.Feature; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; @@ -69,7 +63,6 @@ import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.UnsafeAccess; import com.oracle.svm.core.annotate.Alias; -import com.oracle.svm.core.annotate.AutomaticFeature; import com.oracle.svm.core.annotate.Delete; import com.oracle.svm.core.annotate.KeepOriginal; import com.oracle.svm.core.annotate.NeverInline; @@ -231,6 +224,7 @@ StackTraceElement getStackTraceElement(int index) { } @TargetClass(java.lang.Runtime.class) +@SuppressWarnings({"static-method"}) final class Target_java_lang_Runtime { @Substitute @@ -249,6 +243,16 @@ public void load(String filename) { public void runFinalization() { } + @Substitute + @Platforms({Platform.LINUX_AND_JNI.class, Platform.DARWIN_AND_JNI.class, Platform.WINDOWS.class}) + private int availableProcessors() { + if (SubstrateOptions.MultiThreaded.getValue()) { + return Jvm.JVM_ActiveProcessorCount(); + } else { + return 1; + } + } + // Checkstyle: stop @Alias synchronized native void loadLibrary0(Class fromClass, String libname); @@ -258,43 +262,6 @@ public void runFinalization() { // Checkstyle: resume } -/** - * Provides replacement values for the {@link System#out}, {@link System#err}, and {@link System#in} - * streams at run time. We want a fresh set of objects, so that any buffers filled during image - * generation, as well as any redirection of the streams to new values, do not change the behavior - * at run time. - * - * We use an {@link Feature.DuringSetupAccess#registerObjectReplacer object replacer} because the - * streams can be cached in other instance and static fields in addition to the fields in - * {@link System}. We do not know all these places, so we do now know where to place - * {@link RecomputeFieldValue} annotations. - */ -@AutomaticFeature -class SystemFeature implements Feature { - private static final PrintStream newOut = new PrintStream(new BufferedOutputStream(new FileOutputStream(FileDescriptor.out), 128), true); - private static final PrintStream newErr = new PrintStream(new BufferedOutputStream(new FileOutputStream(FileDescriptor.err), 128), true); - private static final InputStream newIn = new BufferedInputStream(new FileInputStream(FileDescriptor.in)); - - @Override - public void duringSetup(DuringSetupAccess access) { - access.registerObjectReplacer(SystemFeature::replaceStreams); - } - - // Checkstyle: stop - private static Object replaceStreams(Object object) { - if (object == System.out) { - return newOut; - } else if (object == System.err) { - return newErr; - } else if (object == System.in) { - return newIn; - } else { - return object; - } - } - // Checkstyle: resume -} - @TargetClass(java.lang.System.class) final class Target_java_lang_System { @@ -391,6 +358,24 @@ public static void load(String filename) { // Substituted because the original is caller-sensitive, which we don't support Runtime.getRuntime().load(filename); } + + /* + * Note that there is no substitution for getSecurityManager, but instead getSecurityManager it + * is intrinsified in SubstrateGraphBuilderPlugins to always return null. This allows better + * constant folding of SecurityManager code already during static analysis. + */ + @Substitute + private static void setSecurityManager(SecurityManager s) { + if (s != null) { + /* + * We deliberately treat this as a non-recoverable fatal error. We want to prevent bugs + * where an exception is silently ignored by an application and then necessary security + * checks are not in place. + */ + throw VMError.shouldNotReachHere("Installing a SecurityManager is not yet supported"); + } + } + } @TargetClass(java.lang.StrictMath.class) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JavaNIOSubstitutions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JavaNIOSubstitutions.java index 68e3e1820ef0..f71facbac84d 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JavaNIOSubstitutions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JavaNIOSubstitutions.java @@ -24,37 +24,12 @@ */ package com.oracle.svm.core.jdk; -import java.io.IOException; -import java.nio.file.Path; -import java.nio.file.spi.FileTypeDetector; import java.security.SecureRandom; -import org.graalvm.nativeimage.ImageSingletons; - import com.oracle.svm.core.annotate.Alias; import com.oracle.svm.core.annotate.InjectAccessors; -import com.oracle.svm.core.annotate.Substitute; import com.oracle.svm.core.annotate.TargetClass; -@TargetClass(java.nio.file.Files.class) -final class Target_java_nio_file_Files { - - @Substitute - private static String probeContentType(Path path) throws IOException { - FilesSupport fs = ImageSingletons.lookup(FilesSupport.class); - - for (FileTypeDetector detector : fs.installedDetectors) { - String result = detector.probeContentType(path); - if (result != null) { - return result; - } - } - - // fallback to default - return fs.defaultFileTypeDetector.probeContentType(path); - } -} - @TargetClass(className = "java.nio.file.TempFileHelper") final class Target_java_nio_file_TempFileHelper { diff --git a/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/headers/Jvm.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Jvm.java similarity index 74% rename from substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/headers/Jvm.java rename to substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Jvm.java index 135fb7149b33..22552bfae189 100644 --- a/substratevm/src/com.oracle.svm.core.windows/src/com/oracle/svm/core/windows/headers/Jvm.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Jvm.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,11 +22,10 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package com.oracle.svm.core.windows.headers; +package com.oracle.svm.core.jdk; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; -import org.graalvm.nativeimage.c.CContext; import org.graalvm.nativeimage.c.function.CFunction; import org.graalvm.nativeimage.c.function.CLibrary; @@ -35,17 +34,18 @@ /** * Definitions for Hotspot JVM internal functions * - * We only declare an initialize function in order to ensure that the jvm.lib is on the link line. - * This allows and core library dependencies on JVM_ functions to be satisfied by our jvm.lib - * library. + * We declare an initialize function in order to ensure that the jvm lib is on the link line. This + * allows the core library dependencies on JVM_ functions to be satisfied by our jvm library + * (jvm.lib or libjvm.a). * */ -@CContext(WindowsDirectives.class) -@Platforms(Platform.WINDOWS.class) +@Platforms({Platform.LINUX_AND_JNI.class, Platform.DARWIN_AND_JNI.class, Platform.WINDOWS.class}) @CLibrary("jvm") public class Jvm { @CFunction(transition = CFunction.Transition.NO_TRANSITION) public static native void initialize(); + @CFunction(transition = CFunction.Transition.NO_TRANSITION) + public static native int JVM_ActiveProcessorCount(); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Package_jdk_internal_misc.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Package_jdk_internal_misc.java index a307d8fc49bd..dd3512afffb6 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Package_jdk_internal_misc.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Package_jdk_internal_misc.java @@ -26,7 +26,7 @@ import java.util.function.Function; -import org.graalvm.compiler.serviceprovider.GraalServices; +import org.graalvm.compiler.serviceprovider.JavaVersionUtil; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; @@ -37,7 +37,7 @@ public class Package_jdk_internal_misc implements Function @Override public String apply(TargetClass annotation) { - if (GraalServices.Java8OrEarlier) { + if (JavaVersionUtil.Java8OrEarlier) { return "sun.misc." + annotation.className(); } else { return "jdk.internal.misc." + annotation.className(); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Package_jdk_internal_reflect.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Package_jdk_internal_reflect.java index 08434b4c27cb..d462575fa48f 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Package_jdk_internal_reflect.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/Package_jdk_internal_reflect.java @@ -26,7 +26,7 @@ import java.util.function.Function; -import org.graalvm.compiler.serviceprovider.GraalServices; +import org.graalvm.compiler.serviceprovider.JavaVersionUtil; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; @@ -36,7 +36,7 @@ public class Package_jdk_internal_reflect implements Function { @Override public String apply(TargetClass annotation) { - if (GraalServices.Java8OrEarlier) { + if (JavaVersionUtil.Java8OrEarlier) { return "sun.reflect." + annotation.className(); } else { return "jdk.internal.reflect." + annotation.className(); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/PlatformNativeLibrarySupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/PlatformNativeLibrarySupport.java index 7fbc9199e274..8dd7962ff302 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/PlatformNativeLibrarySupport.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/PlatformNativeLibrarySupport.java @@ -29,6 +29,7 @@ import java.util.List; import org.graalvm.nativeimage.ImageSingletons; +import org.graalvm.nativeimage.Platform; import org.graalvm.word.PointerBase; public interface PlatformNativeLibrarySupport { @@ -57,7 +58,56 @@ default boolean isBuiltinLibrary(String name) { return defaultBuiltInLibraries.contains(name); } - boolean isBuiltinPkgNative(String name); + String[] builtInPkgNatives = { + "Java_com_sun_demo_jvmti_hprof", + "Java_com_sun_java_util_jar_pack", + "Java_com_sun_net_ssl", + "Java_com_sun_nio_file", + "Java_com_sun_security_cert_internal_x509", + "Java_java_io", + "Java_java_lang", + "Java_java_math", + "Java_java_net", + "Java_java_nio", + "Java_java_security", + "Java_java_text", + "Java_java_time", + "Java_java_util", + "Java_javax_net", + "Java_javax_script", + "Java_javax_security", + "Java_jdk_internal_org", + "Java_jdk_internal_util", + "Java_jdk_net", + "Java_sun_invoke", + "Java_sun_launcher", + "Java_sun_misc", + "Java_sun_net", + "Java_sun_nio", + "Java_sun_reflect", + "Java_sun_security", + "Java_sun_text", + "Java_sun_util", + + /* SVM Specific packages */ + "Java_com_oracle_svm_core_jdk" + }; + + default boolean isBuiltinPkgNative(String name) { + if (Platform.includedIn(Platform.LINUX_JNI.class) || + Platform.includedIn(Platform.DARWIN_JNI.class) || + Platform.includedIn(Platform.WINDOWS.class)) { + // Do a quick check first + if (name.startsWith("Java_")) { + for (String str : builtInPkgNatives) { + if (name.startsWith(str)) { + return true; + } + } + } + } + return false; + } boolean initializeBuiltinLibraries(); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/RecomputedFields.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/RecomputedFields.java index 6993667f93f4..cf49e27db28a 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/RecomputedFields.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/RecomputedFields.java @@ -51,7 +51,7 @@ import java.util.concurrent.locks.ReentrantLock; import java.util.function.Consumer; -import org.graalvm.compiler.serviceprovider.GraalServices; +import org.graalvm.compiler.serviceprovider.JavaVersionUtil; import org.graalvm.nativeimage.Feature; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.Platform; @@ -433,7 +433,7 @@ public static ForkJoinPool getCommon() { /** Ensure that the common pool variables are initialized. */ protected static void ensureCommonPoolIsInitialized() { if (injectedCommon.get() == null) { - if (GraalServices.Java8OrEarlier) { + if (JavaVersionUtil.Java8OrEarlier) { initializeCommonPool_JDK8OrEarlier(); } else { initializeCommonPool_JDK9OrLater(); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/SunMiscSubstitutions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/SunMiscSubstitutions.java index 0495911e32ae..20d68c309502 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/SunMiscSubstitutions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/SunMiscSubstitutions.java @@ -30,7 +30,7 @@ import java.util.function.Function; import org.graalvm.compiler.nodes.extended.MembarNode; -import org.graalvm.compiler.serviceprovider.GraalServices; +import org.graalvm.compiler.serviceprovider.JavaVersionUtil; import org.graalvm.compiler.word.Word; import org.graalvm.nativeimage.Platform; import org.graalvm.nativeimage.Platforms; @@ -184,7 +184,7 @@ private static void toStdout(String msg) { class Package_jdk_internal_ref implements Function { @Override public String apply(TargetClass annotation) { - if (GraalServices.Java8OrEarlier) { + if (JavaVersionUtil.Java8OrEarlier) { return "sun.misc." + annotation.className(); } else { return "jdk.internal.ref." + annotation.className(); @@ -248,7 +248,7 @@ final class Target_jdk_internal_ref_SoftCleanable { class Package_jdk_internal_perf implements Function { @Override public String apply(TargetClass annotation) { - if (GraalServices.Java8OrEarlier) { + if (JavaVersionUtil.Java8OrEarlier) { return "sun.misc." + annotation.className(); } else { return "jdk.internal.perf." + annotation.className(); @@ -295,7 +295,7 @@ final class Target_jdk_internal_misc_JavaLangAccess { class Package_jdk_internal_loader implements Function { @Override public String apply(TargetClass annotation) { - if (GraalServices.Java8OrEarlier) { + if (JavaVersionUtil.Java8OrEarlier) { return "sun.misc." + annotation.className(); } else { return "jdk.internal.loader." + annotation.className(); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/SystemInOutErrSupport.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/SystemInOutErrSupport.java new file mode 100644 index 000000000000..f6bfc61f98a3 --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/SystemInOutErrSupport.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2019, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.svm.core.jdk; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.FileDescriptor; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.PrintStream; +import java.util.Objects; + +import org.graalvm.nativeimage.Feature; +import org.graalvm.nativeimage.ImageSingletons; + +import com.oracle.svm.core.annotate.AutomaticFeature; +import com.oracle.svm.core.annotate.RecomputeFieldValue; + +/** + * This class provides replacement values for the {@link System#in}, {@link System#out}, and + * {@link System#err} streams at run time. We want a fresh set of objects, so that any buffers + * filled during image generation, as well as any redirection of the streams to new values, do not + * change the behavior at run time. + * + * By default, the streams are replaced to new streams that write to the standard file descriptors. + * This can be customized by calling {@link #setIn}, {@link #setOut}, and {@link #setErr} before the + * static analysis starts, i.e., in a {@link Feature#beforeAnalysis} method. + */ +public final class SystemInOutErrSupport { + private InputStream in = new BufferedInputStream(new FileInputStream(FileDescriptor.in)); + private PrintStream out = new PrintStream(new BufferedOutputStream(new FileOutputStream(FileDescriptor.out), 128), true); + private PrintStream err = new PrintStream(new BufferedOutputStream(new FileOutputStream(FileDescriptor.err), 128), true); + + public static void setIn(InputStream in) { + ImageSingletons.lookup(SystemInOutErrSupport.class).in = Objects.requireNonNull(in); + } + + public static void setOut(PrintStream out) { + ImageSingletons.lookup(SystemInOutErrSupport.class).out = Objects.requireNonNull(out); + } + + public static void setErr(PrintStream err) { + ImageSingletons.lookup(SystemInOutErrSupport.class).err = Objects.requireNonNull(err); + } + + // Checkstyle: stop + Object replaceStreams(Object object) { + if (object == System.in) { + return in; + } else if (object == System.out) { + return out; + } else if (object == System.err) { + return err; + } else { + return object; + } + } + // Checkstyle: resume +} + +/** + * We use an {@link Feature.DuringSetupAccess#registerObjectReplacer object replacer} because the + * streams can be cached in other instance and static fields in addition to the fields in + * {@link System}. We do not know all these places, so we do now know where to place + * {@link RecomputeFieldValue} annotations. + */ +@AutomaticFeature +class SystemInOutErrFeature implements Feature { + @Override + public void duringSetup(DuringSetupAccess access) { + SystemInOutErrSupport support = new SystemInOutErrSupport(); + ImageSingletons.add(SystemInOutErrSupport.class, support); + access.registerObjectReplacer(support::replaceStreams); + } +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/VMErrorSubstitutions.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/VMErrorSubstitutions.java index 438039d00b2b..9144b4fb66ee 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/VMErrorSubstitutions.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/VMErrorSubstitutions.java @@ -35,6 +35,7 @@ import com.oracle.svm.core.annotate.Uninterruptible; import com.oracle.svm.core.log.Log; import com.oracle.svm.core.snippets.KnownIntrinsics; +import com.oracle.svm.core.stack.StackOverflowCheck; import com.oracle.svm.core.stack.ThreadStackPrinter; import com.oracle.svm.core.thread.VMThreads; @@ -49,28 +50,19 @@ final class Target_com_oracle_svm_core_util_VMError { @Uninterruptible(reason = "Allow VMError to be used in uninterruptible code.") @Substitute private static RuntimeException shouldNotReachHere() { - ThreadStackPrinter.printBacktrace(); - VMThreads.StatusSupport.setStatusIgnoreSafepoints(); - VMErrorSubstitutions.shutdown(null, null); - return null; + return shouldNotReachHere(null, null); } @Uninterruptible(reason = "Allow VMError to be used in uninterruptible code.") @Substitute private static RuntimeException shouldNotReachHere(String msg) { - ThreadStackPrinter.printBacktrace(); - VMThreads.StatusSupport.setStatusIgnoreSafepoints(); - VMErrorSubstitutions.shutdown(msg, null); - return null; + return shouldNotReachHere(msg, null); } @Uninterruptible(reason = "Allow VMError to be used in uninterruptible code.") @Substitute private static RuntimeException shouldNotReachHere(Throwable ex) { - ThreadStackPrinter.printBacktrace(); - VMThreads.StatusSupport.setStatusIgnoreSafepoints(); - VMErrorSubstitutions.shutdown(null, ex); - return null; + return shouldNotReachHere(null, ex); } @Uninterruptible(reason = "Allow VMError to be used in uninterruptible code.") @@ -78,6 +70,7 @@ private static RuntimeException shouldNotReachHere(Throwable ex) { private static RuntimeException shouldNotReachHere(String msg, Throwable ex) { ThreadStackPrinter.printBacktrace(); VMThreads.StatusSupport.setStatusIgnoreSafepoints(); + StackOverflowCheck.singleton().disableStackOverflowChecksForFatalError(); VMErrorSubstitutions.shutdown(msg, ex); return null; } @@ -116,40 +109,60 @@ public class VMErrorSubstitutions { @Uninterruptible(reason = "Allow use in uninterruptible code.", calleeMustBe = false) @RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate during printing diagnostics.") static void shutdown(String msg, Throwable ex) { - Log log = Log.log(); - log.autoflush(true); - log.string("VMError.shouldNotReachHere"); - if (msg != null) { - log.string(": ").string(msg); - } - if (ex != null) { + doShutdown(msg, ex); + } + + private static void doShutdown(String msg, Throwable ex) { + try { + Log log = Log.log(); + log.autoflush(true); + /* - * We do not want to call getMessage(), since it can be overridden by subclasses of - * Throwable. So we access the raw detailMessage directly from the field in Throwable. - * That is better than printing nothing. + * Print the error message. If the diagnostic output fails, at least we printed the most + * important bit of information. */ - String detailMessage = JDKUtils.getRawMessage(ex); - StackTraceElement[] stackTrace = JDKUtils.getRawStackTrace(ex); - - log.string(": ").string(ex.getClass().getName()).string(": ").string(detailMessage); - if (stackTrace != null) { - for (StackTraceElement element : stackTrace) { - if (element != null) { - log.newline(); - log.string(" at ").string(element.getClassName()).string(".").string(element.getMethodName()); - log.string("(").string(element.getFileName()).string(":").signed(element.getLineNumber()).string(")"); + log.string("Fatal error"); + if (msg != null) { + log.string(": ").string(msg); + } + if (ex != null) { + /* + * We do not want to call getMessage(), since it can be overridden by subclasses of + * Throwable. So we access the raw detailMessage directly from the field in + * Throwable. That is better than printing nothing. + */ + String detailMessage = JDKUtils.getRawMessage(ex); + StackTraceElement[] stackTrace = JDKUtils.getRawStackTrace(ex); + + log.string(": ").string(ex.getClass().getName()).string(": ").string(detailMessage); + if (stackTrace != null) { + for (StackTraceElement element : stackTrace) { + if (element != null) { + log.newline(); + log.string(" at ").string(element.getClassName()).string(".").string(element.getMethodName()); + log.string("(").string(element.getFileName()).string(":").signed(element.getLineNumber()).string(")"); + } } } } - } - log.newline(); - doShutdown(log); - } + log.newline(); - private static void doShutdown(Log log) { - try { SubstrateUtil.printDiagnostics(log, KnownIntrinsics.readCallerStackPointer(), KnownIntrinsics.readReturnAddress()); - } catch (Throwable ex) { + + /* + * Print the error message again, so that the most important bit of information shows up + * as the last line (which is probably what users look at first). + */ + log.string("Fatal error"); + if (msg != null) { + log.string(": ").string(msg); + } + if (ex != null) { + log.string(": ").string(ex.getClass().getName()).string(": ").string(JDKUtils.getRawMessage(ex)); + } + log.newline(); + + } catch (Throwable ignored) { /* Ignore exceptions reported during error reporting, we are going to exit anyway. */ } ImageSingletons.lookup(LogHandler.class).fatalError(); diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/VarHandleFeature.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/VarHandleFeature.java index 6dda25adcd3c..8b7810f6da4e 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/VarHandleFeature.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/VarHandleFeature.java @@ -36,7 +36,7 @@ import java.util.concurrent.ConcurrentMap; import java.util.function.Consumer; -import org.graalvm.compiler.serviceprovider.GraalServices; +import org.graalvm.compiler.serviceprovider.JavaVersionUtil; import org.graalvm.nativeimage.Feature; import org.graalvm.nativeimage.ImageSingletons; @@ -101,7 +101,7 @@ public class VarHandleFeature implements Feature { @Override public boolean isInConfiguration(IsInConfigurationAccess access) { - return !GraalServices.Java8OrEarlier; + return !JavaVersionUtil.Java8OrEarlier; } @Override diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/SubstrateOptionsParser.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/SubstrateOptionsParser.java index 33aa45d48b12..ce08d736f262 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/SubstrateOptionsParser.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/option/SubstrateOptionsParser.java @@ -147,7 +147,7 @@ static OptionParseResult parseOption(SortedMap options if (eqIndex != -1) { return OptionParseResult.error("Cannot mix +/- with = format: '" + optionPrefix + option + "'"); } - optionName = option.substring(1, eqIndex == -1 ? option.length() : eqIndex); + optionName = option.substring(1, option.length()); if (booleanOptionFormat == BooleanOptionFormat.NAME_VALUE) { return OptionParseResult.error("Option '" + optionName + "' must use = format, not +/- prefix"); } @@ -402,10 +402,12 @@ static void printFlags(Predicate filter, SortedMap currentException = FastThreadLocalFactory.createObject(Throwable.class); @@ -264,7 +267,7 @@ private static void unwindException(Throwable exception, Pointer callerSP, CodeP * exception. So we can start looking for the exception handler immediately in that frame, * without skipping any frames in between. */ - JavaStackWalker.walkCurrentThread(callerSP, callerIP, exceptionStackFrameVisitor); + JavaStackWalker.walkCurrentThread(callerSP, callerIP, ImageSingletons.lookup(ExceptionStackFrameVisitor.class)); /* * The stack walker does not return if an exception handler is found, but instead performs a diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/stack/StackOverflowCheck.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/stack/StackOverflowCheck.java index e539e1b08efc..f0462d40c211 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/stack/StackOverflowCheck.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/stack/StackOverflowCheck.java @@ -120,4 +120,12 @@ static StackOverflowCheck singleton() { * Returns the combined size of the yellow and red zone. */ int yellowAndRedZoneSize(); + + /** + * Disables all stack overflow checks for this thread. This operation is not reversible, i.e., + * it must only be called in the case of a fatal error where the VM is going to exit soon and + * does not execute user code anymore that relies on proper stack overflow checking. + */ + @Uninterruptible(reason = "Called by fatal error handling that is uninterruptible.") + void disableStackOverflowChecksForFatalError(); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/VMThreads.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/VMThreads.java index 8593fa7ecbd0..1cb6094afb8c 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/VMThreads.java +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/VMThreads.java @@ -25,6 +25,7 @@ package com.oracle.svm.core.thread; import org.graalvm.compiler.api.replacements.Fold; +import org.graalvm.nativeimage.CurrentIsolate; import org.graalvm.nativeimage.ImageSingletons; import org.graalvm.nativeimage.Isolate; import org.graalvm.nativeimage.IsolateThread; @@ -211,16 +212,37 @@ public void attachThread(IsolateThread thread) { * method called in every thread. */ @Uninterruptible(reason = "Manipulates the threads list; broadcasts on changes.") - public static void detachThread(IsolateThread vmThread) { - // Manipulating the VMThread list requires the lock for - // changing the status and for notification. - VMThreads.THREAD_MUTEX.guaranteeIsLocked("Must hold the VMThreads mutex."); + public static void detachThread(IsolateThread current) { + assert current.equal(CurrentIsolate.getCurrentThread()) : "Cannot detach different thread with this method"; + + /* + * Make me immune to safepoints (the safepoint mechanism ignores me). We are calling + * functions that are not marked as @Uninterruptible during the detach process. We hold the + * THREAD_MUTEX, so we know that we are not going to be interrupted by a safepoint. But a + * safepoint can already be requested, or our safepoint counter can reach 0 - so it is still + * possible that we enter the safepoint slow path. + */ + StatusSupport.setStatusIgnoreSafepoints(); + + // try-finally because try-with-resources can call interruptible code + THREAD_MUTEX.lockNoTransition(); + try { + detachThreadInSafeContext(current); + } finally { + THREAD_MUTEX.unlock(); + } + } + + @Uninterruptible(reason = "Manipulates the threads list; broadcasts on changes.") + private static void detachThreadInSafeContext(IsolateThread thread) { + detachJavaThread(thread); + // Run down the current list and remove the given VMThread. IsolateThread previous = nullThread(); IsolateThread current = head; while (isNonNullThread(current)) { IsolateThread next = nextTL.get(current); - if (current == vmThread) { + if (current == thread) { // Splice the current element out of the list. if (isNullThread(previous)) { head = next; @@ -234,7 +256,23 @@ public static void detachThread(IsolateThread vmThread) { } } // Signal that the VMThreads list has changed. - VMThreads.THREAD_LIST_CONDITION.broadcast(); + THREAD_LIST_CONDITION.broadcast(); + + singleton().freeIsolateThread(thread); + } + + @Uninterruptible(reason = "For calling interruptible code from uninterruptible code.", calleeMustBe = false) + private static void detachJavaThread(IsolateThread thread) { + JavaThreads.detachThread(thread); + } + + public static void detachThreads(IsolateThread[] threads) { + VMOperation.enqueueBlockingSafepoint("detachThreads", () -> { + for (IsolateThread thread : threads) { + assert !thread.equal(CurrentIsolate.getCurrentThread()) : "Cannot detach current thread with this method"; + detachThreadInSafeContext(thread); + } + }); } @Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true) diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/util/LazyFinalReference.java b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/util/LazyFinalReference.java new file mode 100644 index 000000000000..aa5ac1c6d22e --- /dev/null +++ b/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/util/LazyFinalReference.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2019, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.svm.core.util; + +import java.util.Objects; +import java.util.function.Supplier; + +import com.oracle.svm.core.UnsafeAccess; + +/** + * An object reference that is set lazily to the non-null reference returned by the provided + * {@link Supplier}, in a thread-safe manner: {@link Supplier#get()} might be called more than once + * from different threads, but {@link #get()} will always return the same non-null reference. + */ +public final class LazyFinalReference { + + private static final long VALUE_OFFSET; + + static { + try { + VALUE_OFFSET = UnsafeAccess.UNSAFE.objectFieldOffset(LazyFinalReference.class.getDeclaredField("value")); + } catch (Throwable ex) { + throw VMError.shouldNotReachHere(ex); + } + } + + private final Supplier supplier; + + /** + * Not required to be volatile because the value will be eventually consistent and inconsistency + * is primitive to handle. + */ + private T value; + + public LazyFinalReference(Supplier supplier) { + this.supplier = supplier; + } + + @SuppressWarnings("unchecked") + public T get() { + T v = value; + if (v == null) { + // Try volatile read first in case of memory inconsistency to avoid Supplier call + v = (T) UnsafeAccess.UNSAFE.getObjectVolatile(this, VALUE_OFFSET); + if (v == null) { + T obj = Objects.requireNonNull(supplier.get()); + + if (UnsafeAccess.UNSAFE.compareAndSwapObject(this, VALUE_OFFSET, null, v)) { + v = obj; + } else { + v = (T) UnsafeAccess.UNSAFE.getObjectVolatile(this, VALUE_OFFSET); + } + assert v != null; + } + } + return v; + } +} diff --git a/substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/NativeImageServerHelper.java b/substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/NativeImageServerHelper.java index 4859e9d8b57c..29a48dd1a983 100644 --- a/substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/NativeImageServerHelper.java +++ b/substratevm/src/com.oracle.svm.driver/src/com/oracle/svm/driver/NativeImageServerHelper.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -37,13 +37,13 @@ public class NativeImageServerHelper { @Fold public static boolean isInConfiguration() { - return Platform.includedIn(Platform.LINUX.class) || Platform.includedIn(Platform.DARWIN.class); + return Platform.includedIn(Platform.LINUX_AND_JNI.class) || Platform.includedIn(Platform.DARWIN_AND_JNI.class); } /* * Ensures started server keeps running even after native-image completes. */ - @Platforms({Platform.LINUX.class, Platform.DARWIN.class}) + @Platforms({Platform.LINUX_AND_JNI.class, Platform.DARWIN_AND_JNI.class}) static int daemonize(Runnable runnable) { int pid = Unistd.fork(); switch (pid) { @@ -62,7 +62,7 @@ static int daemonize(Runnable runnable) { } } -@Platforms({Platform.LINUX.class, Platform.DARWIN.class}) +@Platforms({Platform.LINUX_AND_JNI.class, Platform.DARWIN_AND_JNI.class}) class UnistdDirectives implements CContext.Directives { @Override public boolean isInConfiguration() { @@ -80,7 +80,7 @@ public List getMacroDefinitions() { } } -@Platforms({Platform.LINUX.class, Platform.DARWIN.class}) +@Platforms({Platform.LINUX_AND_JNI.class, Platform.DARWIN_AND_JNI.class}) @CContext(UnistdDirectives.class) class Unistd { /** diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ImageClasspathFileTypeDetectorFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ImageClasspathFileTypeDetectorFeature.java new file mode 100644 index 000000000000..58511bfef539 --- /dev/null +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ImageClasspathFileTypeDetectorFeature.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2019, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.svm.hosted; + +import java.lang.reflect.Modifier; +import java.nio.file.spi.FileTypeDetector; + +import org.graalvm.compiler.debug.DebugContext; +import org.graalvm.nativeimage.Feature; + +import com.oracle.svm.core.annotate.AutomaticFeature; +import com.oracle.svm.core.jdk.FileTypeDetectorSupport; + +@AutomaticFeature +public class ImageClasspathFileTypeDetectorFeature implements Feature { + + @Override + public boolean isInConfiguration(IsInConfigurationAccess access) { + return FileTypeDetectorSupport.Options.AddAllFileTypeDetectors.getValue(); + } + + @SuppressWarnings("try") + @Override + public void beforeAnalysis(Feature.BeforeAnalysisAccess access) { + FeatureImpl.BeforeAnalysisAccessImpl config = (FeatureImpl.BeforeAnalysisAccessImpl) access; + DebugContext debugContext = config.getDebugContext(); + try (DebugContext.Scope s = debugContext.scope("registerFileTypeDetectors")) { + for (Class detector : config.findSubclasses(FileTypeDetector.class)) { + if (detector == FileTypeDetectorSupport.AlwaysNullFileTypeDetector.class) { + continue; + } + if (Modifier.isAbstract(detector.getModifiers())) { + continue; + } + try { + FileTypeDetectorSupport.addFirst(detector.getDeclaredConstructor().newInstance()); + debugContext.log("registerFileTypeDetectors: Registered FileTypeDetector " + detector.getName()); + } catch (Exception ex) { + debugContext.log("registerFileTypeDetectors: FileTypeDetector " + detector.getName() + " is not default constructible"); + } + } + } + } +} diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGeneratorRunner.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGeneratorRunner.java index f55e1ef8ca98..23902e3a8eb3 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGeneratorRunner.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGeneratorRunner.java @@ -45,7 +45,7 @@ import org.graalvm.compiler.debug.DebugContext; import org.graalvm.compiler.options.OptionValues; import org.graalvm.compiler.printer.GraalDebugHandlersFactory; -import org.graalvm.compiler.serviceprovider.GraalServices; +import org.graalvm.compiler.serviceprovider.JavaVersionUtil; import org.graalvm.nativeimage.c.function.CEntryPoint; import org.graalvm.nativeimage.c.type.CCharPointerPointer; @@ -161,7 +161,7 @@ private static URL[] verifyClassPathAndConvertToURLs(String[] classpath) { /** Unless the check should be ignored, check that I am running on JDK-8. */ public static boolean isValidJavaVersion() { - return (Boolean.getBoolean("substratevm.IgnoreGraalVersionCheck") || GraalServices.Java8OrEarlier); + return (Boolean.getBoolean("substratevm.IgnoreGraalVersionCheck") || JavaVersionUtil.Java8OrEarlier); } private static void reportToolUserError(String msg) { @@ -282,7 +282,7 @@ private int buildImage(String[] arguments, String[] classpath, ClassLoader class compilationExecutor = Inflation.createExecutor(debug, maxConcurrentThreads); generator = new NativeImageGenerator(imageClassLoader, optionParser); generator.run(entryPoints, mainEntryPoint, javaMainSupport, imageName, imageKind, SubstitutionProcessor.IDENTITY, - analysisExecutor, compilationExecutor, optionParser.getRuntimeOptionNames()); + compilationExecutor, analysisExecutor, optionParser.getRuntimeOptionNames()); } catch (InterruptImageBuilding e) { if (analysisExecutor != null) { analysisExecutor.shutdownNow(); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResourcesFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResourcesFeature.java index 400da216f7de..c480db13ff6b 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResourcesFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ResourcesFeature.java @@ -108,14 +108,19 @@ public void beforeAnalysis(BeforeAnalysisAccess access) { @SuppressWarnings("try") private void scanDirectory(DebugContext debugContext, File f, String relativePath, Pattern... patterns) throws IOException { if (f.isDirectory()) { - for (File ch : f.listFiles()) { - scanDirectory(debugContext, ch, relativePath.isEmpty() ? ch.getName() : relativePath + "/" + ch.getName(), patterns); + File[] files = f.listFiles(); + if (files == null) { + throw UserError.abort("Cannot scan directory " + f); + } else { + for (File ch : files) { + scanDirectory(debugContext, ch, relativePath.isEmpty() ? ch.getName() : relativePath + "/" + ch.getName(), patterns); + } } } else { if (matches(patterns, relativePath)) { try (FileInputStream is = new FileInputStream(f)) { try (DebugContext.Scope s = debugContext.scope("registerResource")) { - debugContext.log("ResourcesFeature: registerResource: " + relativePath.substring(1)); + debugContext.log("ResourcesFeature: registerResource: " + relativePath); } Resources.registerResource(relativePath, is); } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SecurityServicesFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SecurityServicesFeature.java index 66067f997b79..bfce58d53c4e 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SecurityServicesFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/SecurityServicesFeature.java @@ -39,7 +39,7 @@ import java.util.function.Function; import org.graalvm.compiler.options.Option; -import org.graalvm.compiler.serviceprovider.GraalServices; +import org.graalvm.compiler.serviceprovider.JavaVersionUtil; import org.graalvm.nativeimage.Feature; import org.graalvm.nativeimage.RuntimeClassInitialization; import org.graalvm.nativeimage.RuntimeReflection; @@ -58,6 +58,9 @@ public class SecurityServicesFeature implements Feature { static class Options { + @Option(help = "Enable the feature that provides support for security services.")// + public static final HostedOptionKey EnableSecurityServicesFeature = new HostedOptionKey<>(true); + @Option(help = "Enable trace logging for the security services feature.")// static final HostedOptionKey TraceSecurityServices = new HostedOptionKey<>(false); } @@ -80,6 +83,11 @@ static class Options { private static final String CIPHER_SERVICE = "Cipher"; private static final String KEY_AGREEMENT_SERVICE = "KeyAgreement"; + @Override + public boolean isInConfiguration(IsInConfigurationAccess access) { + return Options.EnableSecurityServicesFeature.getValue(); + } + @Override public void duringSetup(DuringSetupAccess access) { /* @@ -96,7 +104,7 @@ public void duringSetup(DuringSetupAccess access) { RuntimeClassInitialization.rerunClassInitialization(access.findClassByName("sun.security.provider.SeedGenerator")); RuntimeClassInitialization.rerunClassInitialization(access.findClassByName("sun.security.provider.SecureRandom$SeederHolder")); - if (!GraalServices.Java8OrEarlier) { + if (!JavaVersionUtil.Java8OrEarlier) { RuntimeClassInitialization.rerunClassInitialization(access.findClassByName("sun.security.provider.FileInputStreamPool")); } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationSupport.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationSupport.java index cce9544d498a..15fe0977aada 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationSupport.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/AnnotationSupport.java @@ -28,14 +28,22 @@ import java.lang.reflect.Modifier; import java.util.Arrays; +import com.oracle.svm.core.graal.nodes.DeadEndNode; +import com.oracle.svm.core.graal.nodes.UnreachableNode; +import jdk.vm.ci.meta.DeoptimizationAction; +import jdk.vm.ci.meta.DeoptimizationReason; import org.graalvm.compiler.api.replacements.SnippetReflectionProvider; import org.graalvm.compiler.core.common.type.StampFactory; import org.graalvm.compiler.core.common.type.StampPair; import org.graalvm.compiler.core.common.type.TypeReference; import org.graalvm.compiler.debug.DebugContext; import org.graalvm.compiler.java.FrameStateBuilder; +import org.graalvm.compiler.nodes.BeginNode; import org.graalvm.compiler.nodes.CallTargetNode.InvokeKind; import org.graalvm.compiler.nodes.ConstantNode; +import org.graalvm.compiler.nodes.EndNode; +import org.graalvm.compiler.nodes.FixedGuardNode; +import org.graalvm.compiler.nodes.LogicConstantNode; import org.graalvm.compiler.nodes.LogicNode; import org.graalvm.compiler.nodes.PiNode; import org.graalvm.compiler.nodes.ReturnNode; @@ -159,11 +167,31 @@ private synchronized AnnotationSubstitutionType getSubstitution(ResolvedJavaType result.addSubstitutionMethod(originalMethod, substitutionMethod); } + for (ResolvedJavaMethod originalMethod : type.getDeclaredConstructors()) { + AnnotationSubstitutionMethod substitutionMethod = new AnnotationConstructorMethod(originalMethod); + result.addSubstitutionMethod(originalMethod, substitutionMethod); + } + typeSubstitutions.put(type, result); } return result; } + static class AnnotationConstructorMethod extends AnnotationSubstitutionMethod { + AnnotationConstructorMethod(ResolvedJavaMethod original) { + super(original); + } + + @Override + public StructuredGraph buildGraph(DebugContext debug, ResolvedJavaMethod method, HostedProviders providers, Purpose purpose) { + HostedGraphKit kit = new HostedGraphKit(debug, providers, method); + StructuredGraph graph = kit.getGraph(); + graph.addAfterFixed(graph.start(), graph.add(new FixedGuardNode(LogicConstantNode.forBoolean(true, graph), DeoptimizationReason.UnreachedCode, DeoptimizationAction.None, true))); + assert graph.verify(); + return graph; + } + } + /* Used to check the type of fields that need special guarding against missing types. */ static boolean isClassType(JavaType type, MetaAccessProvider metaAccess) { return type.getJavaKind() == JavaKind.Object && diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/CustomSubstitutionMethod.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/CustomSubstitutionMethod.java index c600a61d538d..0bc32841c148 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/CustomSubstitutionMethod.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/annotation/CustomSubstitutionMethod.java @@ -81,7 +81,7 @@ public ResolvedJavaType getDeclaringClass() { @Override public int getMaxLocals() { - return getSignature().getParameterCount(true) + 2; + return getSignature().getParameterCount(!isStatic()) * 2; } @Override diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CompileQueue.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CompileQueue.java index 9bc74d628a80..fb470e28551a 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CompileQueue.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/CompileQueue.java @@ -51,7 +51,6 @@ import org.graalvm.compiler.core.GraalCompiler; import org.graalvm.compiler.core.common.CompilationIdentifier; import org.graalvm.compiler.core.common.CompilationIdentifier.Verbosity; -import org.graalvm.compiler.core.common.GraalOptions; import org.graalvm.compiler.core.common.spi.ForeignCallsProvider; import org.graalvm.compiler.debug.DebugContext; import org.graalvm.compiler.debug.DebugContext.Description; @@ -661,7 +660,6 @@ private void defaultParseFunction(DebugContext debug, HostedMethod method, Compi HostedProviders providers = (HostedProviders) config.lookupBackend(method).getProviders(); boolean needParsing = false; - OptionValues options = HostedOptionValues.singleton(); StructuredGraph graph = method.buildGraph(debug, method, providers, Purpose.AOT_COMPILATION); if (graph == null) { InvocationPlugin plugin = providers.getGraphBuilderPlugins().getInvocationPlugins().lookupInvocation(method); @@ -677,16 +675,7 @@ private void defaultParseFunction(DebugContext debug, HostedMethod method, Compi } if (graph == null) { needParsing = true; - if (!method.compilationInfo.isDeoptTarget()) { - /* - * Disabling liveness analysis preserves the values of local variables beyond the - * bytecode-liveness. This greatly helps debugging. When local variable numbers are - * reused by javac, local variables can still get illegal values. Since we cannot - * "restore" such illegal values during deoptimization, we must do liveness analysis - * for deoptimization target methods. - */ - options = new OptionValues(options, GraalOptions.OptClearNonLiveLocals, false); - } + OptionValues options = universe.adjustCompilerOptions(HostedOptionValues.singleton(), method); graph = new StructuredGraph.Builder(options, debug).method(method).build(); } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/InliningUtilities.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/InliningUtilities.java index b1242162f87c..8c54cc373d92 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/InliningUtilities.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/InliningUtilities.java @@ -35,7 +35,7 @@ import org.graalvm.compiler.nodes.StructuredGraph; import org.graalvm.compiler.nodes.java.MethodCallTargetNode; import org.graalvm.compiler.nodes.spi.ValueProxy; -import org.graalvm.compiler.serviceprovider.GraalServices; +import org.graalvm.compiler.serviceprovider.JavaVersionUtil; import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.core.util.VMError; @@ -50,7 +50,7 @@ public class InliningUtilities { @SuppressWarnings("unchecked") private static Class lookupForceInlineAnnotation() { try { - if (GraalServices.Java8OrEarlier) { + if (JavaVersionUtil.Java8OrEarlier) { return (Class) Class.forName("java.lang.invoke.ForceInline"); } else { return (Class) Class.forName("jdk.internal.vm.annotation.ForceInline"); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/SubstrateLIRBackendFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/SubstrateLIRBackendFeature.java index c4c10ca03319..7446f4874539 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/SubstrateLIRBackendFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/SubstrateLIRBackendFeature.java @@ -30,6 +30,7 @@ import org.graalvm.nativeimage.ImageSingletons; import com.oracle.svm.core.annotate.AutomaticFeature; +import com.oracle.svm.core.snippets.SnippetRuntime; import com.oracle.svm.hosted.image.LIRNativeImageCodeCache; import com.oracle.svm.hosted.image.NativeImageCodeCache; import com.oracle.svm.hosted.image.NativeImageCodeCacheFactory; @@ -50,5 +51,6 @@ public NativeImageCodeCache newCodeCache(CompileQueue compileQueue, NativeImageH return new LIRNativeImageCodeCache(compileQueue.getCompilations(), heap); } }); + ImageSingletons.add(SnippetRuntime.ExceptionStackFrameVisitor.class, new SnippetRuntime.ExceptionStackFrameVisitor()); } } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeBootImage.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeBootImage.java index c53659412b12..9738e923b771 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeBootImage.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeBootImage.java @@ -122,7 +122,10 @@ public Section getTextSection() { protected final void write(Path outputFile) { try { - Files.createDirectories(outputFile.normalize().getParent()); + Path outFileParent = outputFile.normalize().getParent(); + if (outFileParent != null) { + Files.createDirectories(outFileParent); + } FileChannel channel = FileChannel.open(outputFile, StandardOpenOption.WRITE, StandardOpenOption.READ, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE); objectFile.write(channel); } catch (Exception ex) { @@ -190,9 +193,13 @@ private void writeHeaderFile(Path outDir, Header header, List meth } writer.appendln("#endif"); - - String fileName = outDir.getFileName().resolve(header.name() + dynamicSuffix).toString(); - writer.writeFile(fileName, false); + Path fileNamePath = outDir.getFileName(); + if (fileNamePath == null) { + throw UserError.abort("Cannot determine header file name for directory " + outDir); + } else { + String fileName = fileNamePath.resolve(header.name() + dynamicSuffix).toString(); + writer.writeFile(fileName, false); + } } /** diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeBootImageViaCC.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeBootImageViaCC.java index 8af27f32ad98..18eeb9d39e4e 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeBootImageViaCC.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/NativeBootImageViaCC.java @@ -114,7 +114,7 @@ protected void setOutputKind(List cmd) { throw UserError.abort(OS.getCurrent().name() + " does not support building static executable images."); case SHARED_LIBRARY: cmd.add("-shared"); - if (Platform.includedIn(Platform.DARWIN.class)) { + if (Platform.includedIn(Platform.DARWIN_AND_JNI.class)) { cmd.add("-undefined"); cmd.add("dynamic_lookup"); } @@ -212,6 +212,7 @@ LinkerInvocation getLinkerInvocation(Path outputDirectory, Path tempDirectory, S for (String libraryPath : nativeLibs.getLibraryPaths()) { inv.addLibPath(libraryPath); } + for (String rPath : OptionUtils.flatten(",", SubstrateOptions.LinkerRPath.getValue())) { inv.addRPath(rPath); } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedUniverse.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedUniverse.java index 719dae1cbe15..f14e4d2fd800 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedUniverse.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/HostedUniverse.java @@ -33,7 +33,9 @@ import java.util.Set; import org.graalvm.compiler.api.replacements.SnippetReflectionProvider; +import org.graalvm.compiler.core.common.GraalOptions; import org.graalvm.compiler.core.common.spi.ConstantFieldProvider; +import org.graalvm.compiler.options.OptionValues; import com.oracle.graal.pointsto.BigBang; import com.oracle.graal.pointsto.constraints.UnsupportedFeatureException; @@ -45,6 +47,7 @@ import com.oracle.graal.pointsto.meta.AnalysisMethod; import com.oracle.graal.pointsto.meta.AnalysisType; import com.oracle.graal.pointsto.util.ConcurrentLightHashSet; +import com.oracle.svm.core.SubstrateOptions; import com.oracle.svm.hosted.SVMHost; import com.oracle.svm.hosted.analysis.Inflation; @@ -255,6 +258,21 @@ public ResolvedJavaMethod resolveSubstitution(ResolvedJavaMethod method) { return method; } + @Override + public OptionValues adjustCompilerOptions(OptionValues optionValues, ResolvedJavaMethod method) { + if (SubstrateOptions.Optimize.getValue() <= 0 && !((HostedMethod) method).isDeoptTarget()) { + /* + * Disabling liveness analysis preserves the values of local variables beyond the + * bytecode-liveness. This greatly helps debugging. When local variable numbers are + * reused by javac, local variables can still get illegal values. Since we cannot + * "restore" such illegal values during deoptimization, we cannot disable liveness + * analysis for deoptimization target methods. + */ + return new OptionValues(optionValues, GraalOptions.OptClearNonLiveLocals, false); + } + return optionValues; + } + @Override public HostedType objectType() { return types.get(bb.getUniverse().objectType()); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/UniverseBuilder.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/UniverseBuilder.java index 05fa33ba5df0..6f4de17d5d19 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/UniverseBuilder.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/meta/UniverseBuilder.java @@ -1142,6 +1142,7 @@ private void buildHubs() { Map referenceMaps = new HashMap<>(); for (HostedType type : hUniverse.orderedTypes) { ReferenceMapEncoder.Input referenceMap = createReferenceMap(type); + assert ((SubstrateReferenceMap) referenceMap).hasNoDerivedOffsets(); referenceMaps.put(type, referenceMap); referenceMapEncoder.add(referenceMap); } @@ -1195,6 +1196,7 @@ private void buildHubs() { // pointer maps in Dynamic Hub ReferenceMapEncoder.Input referenceMap = referenceMaps.get(type); assert referenceMap != null; + assert ((SubstrateReferenceMap) referenceMap).hasNoDerivedOffsets(); long referenceMapIndex = referenceMapEncoder.lookupEncoding(referenceMap); DynamicHub hub = type.getHub(); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/UnsafeAutomaticSubstitutionProcessor.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/UnsafeAutomaticSubstitutionProcessor.java index c61f6e408ff0..48626ffb7b0d 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/UnsafeAutomaticSubstitutionProcessor.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/substitute/UnsafeAutomaticSubstitutionProcessor.java @@ -67,7 +67,7 @@ import org.graalvm.compiler.phases.OptimisticOptimizations; import org.graalvm.compiler.phases.common.CanonicalizerPhase; import org.graalvm.compiler.phases.tiers.HighTierContext; -import org.graalvm.compiler.serviceprovider.GraalServices; +import org.graalvm.compiler.serviceprovider.JavaVersionUtil; import org.graalvm.nativeimage.Feature; import com.oracle.graal.pointsto.infrastructure.SubstitutionProcessor; @@ -163,7 +163,7 @@ public void init(ImageClassLoader loader, MetaAccessProvider originalMetaAccess) Class unsafeClass; try { - if (GraalServices.Java8OrEarlier) { + if (JavaVersionUtil.Java8OrEarlier) { unsafeClass = Class.forName("sun.misc.Unsafe"); } else { unsafeClass = Class.forName("jdk.internal.misc.Unsafe"); @@ -177,7 +177,7 @@ public void init(ImageClassLoader loader, MetaAccessProvider originalMetaAccess) noCheckedExceptionsSet.add(unsafeObjectFieldOffsetFieldMethod); neverInlineSet.add(unsafeObjectFieldOffsetFieldMethod); - if (!GraalServices.Java8OrEarlier) { + if (!JavaVersionUtil.Java8OrEarlier) { /* JDK-9 introduced Unsafe.objectFieldOffset(Class, String). */ Method unsafeObjectClassStringOffset = unsafeClass.getMethod("objectFieldOffset", java.lang.Class.class, String.class); unsafeObjectFieldOffsetClassStringMethod = originalMetaAccess.lookupJavaMethod(unsafeObjectClassStringOffset); diff --git a/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/JNIObjectHandles.java b/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/JNIObjectHandles.java index 67e94a10fede..4d6aca79d608 100644 --- a/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/JNIObjectHandles.java +++ b/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/JNIObjectHandles.java @@ -25,26 +25,26 @@ package com.oracle.svm.jni; import org.graalvm.word.SignedWord; -import org.graalvm.word.WordFactory; +import com.oracle.svm.core.handles.ThreadLocalHandles; import com.oracle.svm.jni.nativeapi.JNIObjectHandle; import com.oracle.svm.jni.nativeapi.JNIObjectRefType; /** - * Manages accesses to both {@link JNIThreadLocalHandles local} and {@link JNIGlobalHandles global} - * JNI object handles. + * Manages accesses to both {@link ThreadLocalHandles local} and {@link JNIGlobalHandles global} JNI + * object handles. */ public final class JNIObjectHandles { public static T nullHandle() { - return WordFactory.signed(0); + return ThreadLocalHandles.nullHandle(); } public static T getObject(JNIObjectHandle handle) { if (handle.equal(nullHandle())) { return null; } - if (JNIThreadLocalHandles.isInRange(handle)) { + if (ThreadLocalHandles.isInRange(handle)) { return JNIThreadLocalHandles.get().getObject(handle); } if (JNIGlobalHandles.singleton().isInRange(handle)) { @@ -54,7 +54,7 @@ public static T getObject(JNIObjectHandle handle) { } public static JNIObjectRefType getHandleType(JNIObjectHandle handle) { - if (JNIThreadLocalHandles.isInRange(handle)) { + if (ThreadLocalHandles.isInRange(handle)) { return JNIObjectRefType.Local; } if (JNIGlobalHandles.singleton().isInRange(handle)) { diff --git a/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/JNIThreadLocalHandles.java b/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/JNIThreadLocalHandles.java index 7b240b189bac..f439e2e00a40 100644 --- a/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/JNIThreadLocalHandles.java +++ b/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/JNIThreadLocalHandles.java @@ -24,115 +24,25 @@ */ package com.oracle.svm.jni; -import org.graalvm.word.WordFactory; - +import com.oracle.svm.core.handles.ThreadLocalHandles; import com.oracle.svm.core.threadlocal.FastThreadLocalFactory; import com.oracle.svm.core.threadlocal.FastThreadLocalObject; import com.oracle.svm.jni.nativeapi.JNIObjectHandle; -/** - * Implementation of JNI local object handles, which are bound to a specific thread and can be - * created and destroyed implicitly or explicitly. Local handles can be managed in frames and a - * frame can be discarded in its entirety. - */ public final class JNIThreadLocalHandles { /** * Minimum available local handles according to specification: "Before it enters a native * method, the VM automatically ensures that at least 16 local references can be created". */ static final int NATIVE_CALL_MINIMUM_HANDLE_CAPACITY = 16; - private static final int INITIAL_NUMBER_OF_HANDLES = NATIVE_CALL_MINIMUM_HANDLE_CAPACITY; - private static final int INITIAL_NUMBER_OF_FRAMES = 4; - - private static final int MIN_VALUE = Math.toIntExact(1 + JNIObjectHandles.nullHandle().rawValue()); - private static final int MAX_VALUE = Integer.MAX_VALUE; - private static final FastThreadLocalObject handles = FastThreadLocalFactory.createObject(JNIThreadLocalHandles.class); + @SuppressWarnings("rawtypes") private static final FastThreadLocalObject handles = FastThreadLocalFactory.createObject(ThreadLocalHandles.class); - public static JNIThreadLocalHandles get() { + @SuppressWarnings("unchecked") + public static ThreadLocalHandles get() { if (handles.get() == null) { - handles.set(new JNIThreadLocalHandles()); + handles.set(new ThreadLocalHandles(NATIVE_CALL_MINIMUM_HANDLE_CAPACITY)); } return handles.get(); } - - public static boolean isInRange(JNIObjectHandle handle) { - return handle.rawValue() >= MIN_VALUE && handle.rawValue() <= MAX_VALUE; - } - - private Object[] objects = new Object[MIN_VALUE + INITIAL_NUMBER_OF_HANDLES]; - private int top = MIN_VALUE; - - private int[] frameStack = new int[INITIAL_NUMBER_OF_FRAMES]; - private int frameCount = 0; - - private JNIThreadLocalHandles() { - } - - private static int toIndex(JNIObjectHandle handle) { - return (int) handle.rawValue(); - } - - public int getHandleCount() { - return top - MIN_VALUE; - } - - public int pushFrame(int capacity) { - if (frameCount == frameStack.length) { - int[] oldArray = frameStack; - frameStack = new int[oldArray.length * 2]; - System.arraycopy(oldArray, 0, frameStack, 0, oldArray.length); - } - frameStack[frameCount] = top; - frameCount++; - ensureCapacity(capacity); - return frameCount; - } - - public JNIObjectHandle create(Object obj) { - if (obj == null) { - return JNIObjectHandles.nullHandle(); - } - ensureCapacity(1); - int index = top; - objects[index] = obj; - top++; - return (JNIObjectHandle) WordFactory.signed(index); - } - - @SuppressWarnings("unchecked") - public T getObject(JNIObjectHandle handle) { - return (T) objects[toIndex(handle)]; - } - - public boolean delete(JNIObjectHandle handle) { - int index = toIndex(handle); - Object previous = objects[index]; - objects[index] = null; - return previous != null; - } - - public void popFrame() { - popFramesIncluding(frameCount); - } - - public void popFramesIncluding(int frame) { - assert frame > 0 && frame <= frameCount; - int previousTop = top; - frameCount = frame - 1; - top = frameStack[frameCount]; - for (int i = top; i < previousTop; i++) { - objects[i] = null; // so objects can be garbage collected - } - } - - public void ensureCapacity(int capacity) { - if (top + capacity >= objects.length) { - Object[] oldArray = objects; - int newLength = oldArray.length * 2; - assert newLength >= top + capacity; - objects = new Object[newLength]; - System.arraycopy(oldArray, 0, objects, 0, oldArray.length); - } - } } diff --git a/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/hosted/JNIJavaCallWrapperMethod.java b/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/hosted/JNIJavaCallWrapperMethod.java index 150979ad70d6..038e330e3d58 100644 --- a/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/hosted/JNIJavaCallWrapperMethod.java +++ b/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/hosted/JNIJavaCallWrapperMethod.java @@ -310,6 +310,7 @@ private List> loadAndUnboxArguments(JNIGraphKi ValueNode created = kit.append(new NewInstanceNode(receiverClass, true)); AbstractMergeNode merge = kit.endIf(); receiver = kit.unique(new ValuePhiNode(StampFactory.object(), merge, new ValueNode[]{created, unboxed})); + merge.setStateAfter(kit.getFrameState().create(kit.bci(), merge)); } else { receiver = unboxed; } diff --git a/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/hosted/JNINativeCallWrapperMethod.java b/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/hosted/JNINativeCallWrapperMethod.java index 1a2ad1b8dbfc..e3edeb9614d3 100644 --- a/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/hosted/JNINativeCallWrapperMethod.java +++ b/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/hosted/JNINativeCallWrapperMethod.java @@ -73,8 +73,6 @@ * handles and for unboxing an object return value. */ class JNINativeCallWrapperMethod extends CustomSubstitutionMethod { - private int maxLocals = -1; - private final JNINativeLinkage linkage; JNINativeCallWrapperMethod(ResolvedJavaMethod method) { @@ -92,25 +90,6 @@ private static JNINativeLinkage createLinkage(ResolvedJavaMethod method) { return JNIAccessFeature.singleton().makeLinkage(className, unwrapped.getName(), descriptor); } - @Override - public int getMaxLocals() { - if (maxLocals == -1) { - maxLocals = 0; - Signature sig = getOriginal().getSignature(); - int count = sig.getParameterCount(false); - if (!getOriginal().isStatic()) { - maxLocals++; - } - for (int i = 0; i < count; i++) { - maxLocals++; - if (sig.getParameterKind(i).needsTwoSlots()) { - maxLocals++; - } - } - } - return maxLocals; - } - @Override public boolean isSynthetic() { return true; diff --git a/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/nativeapi/JNIHeaderDirectives.java b/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/nativeapi/JNIHeaderDirectives.java index 46d577e730f2..8fcfab762a62 100644 --- a/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/nativeapi/JNIHeaderDirectives.java +++ b/substratevm/src/com.oracle.svm.jni/src/com/oracle/svm/jni/nativeapi/JNIHeaderDirectives.java @@ -29,14 +29,14 @@ import java.util.Collections; import java.util.List; -import org.graalvm.compiler.serviceprovider.GraalServices; +import org.graalvm.compiler.serviceprovider.JavaVersionUtil; import org.graalvm.nativeimage.c.CContext; import com.oracle.svm.core.OS; public class JNIHeaderDirectives implements CContext.Directives { - private final Path jdkIncludeDir = GraalServices.Java8OrEarlier + private final Path jdkIncludeDir = JavaVersionUtil.Java8OrEarlier ? Paths.get(System.getProperty("java.home")).getParent().resolve("include") : Paths.get(System.getProperty("java.home")).resolve("include"); diff --git a/substratevm/src/com.oracle.svm.libffi/Makefile b/substratevm/src/com.oracle.svm.libffi/Makefile deleted file mode 100644 index e24b36b246df..000000000000 --- a/substratevm/src/com.oracle.svm.libffi/Makefile +++ /dev/null @@ -1,90 +0,0 @@ -# -# Copyright (c) 2017, 2017, Oracle and/or its affiliates. All rights reserved. -# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. -# -# This code is free software; you can redistribute it and/or modify it -# under the terms of the GNU General Public License version 2 only, as -# published by the Free Software Foundation. Oracle designates this -# particular file as subject to the "Classpath" exception as provided -# by Oracle in the LICENSE file that accompanied this code. -# -# This code is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -# version 2 for more details (a copy is included in the LICENSE file that -# accompanied this code). -# -# You should have received a copy of the GNU General Public License version -# 2 along with this work; if not, write to the Free Software Foundation, -# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. -# -# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA -# or visit www.oracle.com if you need additional information or have any -# questions. -# - -# 'make MX_VERBOSE=y' will report all lines executed. The actual value doesn't -# matter as long as it's not empty. -QUIETLY$(MX_VERBOSE) = @ - -.PHONY: default - -CFLAGS=-g -fPIC -DPIC -ifeq (${OS},solaris) -CFLAGS+=-m64 -endif - -TARGET=${OS}-${ARCH}/.. - -DEPLOYED_HEADERS=ffi.h ffitarget.h svm_libffi.h trufflenfi.h - -HEADERFILES=${DEPLOYED_HEADERS:%.h=${TARGET}/include/%.h} - -ifneq (${OS},windows) - -default: ${TARGET}/libffi.a ${HEADERFILES} - -${TARGET}/libffi.a: ${LIBFFI_DIST}/libffi.a - $(QUIETLY) mkdir -p ${TARGET} - $(QUIETLY) cp $< $@ - $(QUIETLY) touch $@ - -${TARGET}/include/trufflenfi.h: ${TRUFFLE_NFI}/include/trufflenfi.h - $(QUIETLY) mkdir -p ${TARGET}/include - $(QUIETLY) cp $< $@ - $(QUIETLY) touch $@ - -${TARGET}/include/svm_libffi.h: include/svm_libffi.h - $(QUIETLY) mkdir -p ${TARGET}/include - $(QUIETLY) cp $< $@ - $(QUIETLY) touch $@ - -${TARGET}/include/%.h: ${LIBFFI_DIST}/include/%.h - $(QUIETLY) mkdir -p ${TARGET}/include - $(QUIETLY) cp $< $@ - $(QUIETLY) touch $@ - -else - -all: ${TARGET} ${HEADERFILES} - -${TARGET}: - echo "Building libffi on Windows is not currently supported" - $(shell mkdir -p ${TARGET}) - $(shell touch ${TARGET}/libffi.a) - -${TARGET}/include/trufflenfi.h: ${TRUFFLE_NFI}/include/trufflenfi.h - $(QUIETLY) mkdir -p ${TARGET}/include - $(QUIETLY) cp $< $@ - $(QUIETLY) touch $@ - -${TARGET}/include/svm_libffi.h: include/svm_libffi.h - $(QUIETLY) mkdir -p ${TARGET}/include - $(QUIETLY) cp $< $@ - $(QUIETLY) touch $@ - -${TARGET}/include/ffi.h ${TARGET}/include/ffitarget.h: - $(QUIETLY) mkdir -p ${TARGET}/include - $(QUIETLY) touch $@ - -endif diff --git a/substratevm/src/com.oracle.svm.native.jvm.posix/src/JvmFuncs.c b/substratevm/src/com.oracle.svm.native.jvm.posix/src/JvmFuncs.c new file mode 100644 index 000000000000..35a2be38fcdd --- /dev/null +++ b/substratevm/src/com.oracle.svm.native.jvm.posix/src/JvmFuncs.c @@ -0,0 +1,340 @@ +/* + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* JVM_ functions imported from the hotspot sources */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define JNIEXPORT +#define JNIIMPORT +typedef long jlong; + +#define OS_OK 0 +#define OS_ERR -1 + +/* macros for restartable system calls */ + +#define RESTARTABLE(_cmd, _result) do { \ + _result = _cmd; \ + } while(((int)_result == OS_ERR) && (errno == EINTR)) + +#define RESTARTABLE_RETURN_INT(_cmd) do { \ + int _result; \ + RESTARTABLE(_cmd, _result); \ + return _result; \ +} while(0) + +typedef unsigned char u_char; +typedef u_char* address; +typedef uintptr_t address_word; +inline address_word castable_address(address x) { return (address_word)(x) ; } +#define CAST_TO_FN_PTR(func_type, value) ((func_type)(castable_address(value))) +#define CAST_FROM_FN_PTR(new_type, func_ptr) ((new_type)((address_word)(func_ptr))) + +typedef void (*sa_handler_t)(int); +typedef void (*sa_sigaction_t)(int, siginfo_t *, void *); +typedef sa_handler_t (*signal_t)(int, sa_handler_t); +typedef int (*sigaction_t)(int, const struct sigaction *, struct sigaction *); + +JNIEXPORT void initialize() { +} + +/* Only called in java.lang.Runtime native methods. */ +JNIEXPORT void JVM_FreeMemory() { + printf("JVM_FreeMemory called: Unimplemented\n"); +} + +JNIEXPORT jlong JVM_TotalMemory() { + printf("JVM_TotalMemory called: Unimplemented\n"); + return 0L; +} + +JNIEXPORT jlong JVM_MaxMemory() { + printf("JVM_MaxMemory called: Unimplemented\n"); + return 0L; +} + +JNIEXPORT void JVM_GC() { + printf("JVM_GC called: Unimplemented\n"); +} + +JNIEXPORT void JVM_TraceInstructions(int on) { + printf("JVM_TraceInstructions called: Unimplemented\n"); +} + +JNIEXPORT void JVM_TraceMethodCalls(int on) { + printf("JVM_TraceMethods called: Unimplemented\n"); +} + +JNIEXPORT int JVM_ActiveProcessorCount() { + return sysconf(_SC_NPROCESSORS_ONLN); +} + +JNIEXPORT int JVM_Connect(int fd, struct sockaddr* him, socklen_t len) { + RESTARTABLE_RETURN_INT(connect(fd, him, len)); +} + +JNIEXPORT void * JVM_FindLibraryEntry(void* handle, const char* name) { + return dlsym(handle, name); +} + +JNIEXPORT int JVM_GetHostName(char* name, int namelen) { + return gethostname(name, namelen); +} + +JNIEXPORT int JVM_GetSockOpt(int fd, int level, int optname, + char *optval, socklen_t* optlen) { + return getsockopt(fd, level, optname, optval, optlen); +} + +JNIEXPORT int JVM_Socket(int domain, int type, int protocol) { + return socket(domain, type, protocol); +} + +struct siglabel { + const char *name; + int number; +}; + +#define ARRAY_SIZE(array) (sizeof(array)/sizeof((array)[0])) + +#ifdef __linux__ +struct siglabel siglabels[] = { + /* derived from /usr/include/bits/signum.h on RH7.2 */ + "HUP", SIGHUP, /* Hangup (POSIX). */ + "INT", SIGINT, /* Interrupt (ANSI). */ + "QUIT", SIGQUIT, /* Quit (POSIX). */ + "ILL", SIGILL, /* Illegal instruction (ANSI). */ + "TRAP", SIGTRAP, /* Trace trap (POSIX). */ + "ABRT", SIGABRT, /* Abort (ANSI). */ + "IOT", SIGIOT, /* IOT trap (4.2 BSD). */ + "BUS", SIGBUS, /* BUS error (4.2 BSD). */ + "FPE", SIGFPE, /* Floating-point exception (ANSI). */ + "KILL", SIGKILL, /* Kill, unblockable (POSIX). */ + "USR1", SIGUSR1, /* User-defined signal 1 (POSIX). */ + "SEGV", SIGSEGV, /* Segmentation violation (ANSI). */ + "USR2", SIGUSR2, /* User-defined signal 2 (POSIX). */ + "PIPE", SIGPIPE, /* Broken pipe (POSIX). */ + "ALRM", SIGALRM, /* Alarm clock (POSIX). */ + "TERM", SIGTERM, /* Termination (ANSI). */ +#ifdef SIGSTKFLT + "STKFLT", SIGSTKFLT, /* Stack fault. */ +#endif + "CLD", SIGCLD, /* Same as SIGCHLD (System V). */ + "CHLD", SIGCHLD, /* Child status has changed (POSIX). */ + "CONT", SIGCONT, /* Continue (POSIX). */ + "STOP", SIGSTOP, /* Stop, unblockable (POSIX). */ + "TSTP", SIGTSTP, /* Keyboard stop (POSIX). */ + "TTIN", SIGTTIN, /* Background read from tty (POSIX). */ + "TTOU", SIGTTOU, /* Background write to tty (POSIX). */ + "URG", SIGURG, /* Urgent condition on socket (4.2 BSD). */ + "XCPU", SIGXCPU, /* CPU limit exceeded (4.2 BSD). */ + "XFSZ", SIGXFSZ, /* File size limit exceeded (4.2 BSD). */ + "VTALRM", SIGVTALRM, /* Virtual alarm clock (4.2 BSD). */ + "PROF", SIGPROF, /* Profiling alarm clock (4.2 BSD). */ + "WINCH", SIGWINCH, /* Window size change (4.3 BSD, Sun). */ + "POLL", SIGPOLL, /* Pollable event occurred (System V). */ + "IO", SIGIO, /* I/O now possible (4.2 BSD). */ + "PWR", SIGPWR, /* Power failure restart (System V). */ +#ifdef SIGSYS + "SYS", SIGSYS /* Bad system call. Only on some Linuxen! */ +#endif + }; +#endif + +#ifdef __APPLE__ +struct siglabel siglabels[] = { + /* derived from /usr/include/bits/signum.h on RH7.2 */ + "HUP", SIGHUP, /* Hangup (POSIX). */ + "INT", SIGINT, /* Interrupt (ANSI). */ + "QUIT", SIGQUIT, /* Quit (POSIX). */ + "ILL", SIGILL, /* Illegal instruction (ANSI). */ + "TRAP", SIGTRAP, /* Trace trap (POSIX). */ + "ABRT", SIGABRT, /* Abort (ANSI). */ + "EMT", SIGEMT, /* EMT trap */ + "FPE", SIGFPE, /* Floating-point exception (ANSI). */ + "KILL", SIGKILL, /* Kill, unblockable (POSIX). */ + "BUS", SIGBUS, /* BUS error (4.2 BSD). */ + "SEGV", SIGSEGV, /* Segmentation violation (ANSI). */ + "SYS", SIGSYS, /* Bad system call. Only on some Bsden! */ + "PIPE", SIGPIPE, /* Broken pipe (POSIX). */ + "ALRM", SIGALRM, /* Alarm clock (POSIX). */ + "TERM", SIGTERM, /* Termination (ANSI). */ + "URG", SIGURG, /* Urgent condition on socket (4.2 BSD). */ + "STOP", SIGSTOP, /* Stop, unblockable (POSIX). */ + "TSTP", SIGTSTP, /* Keyboard stop (POSIX). */ + "CONT", SIGCONT, /* Continue (POSIX). */ + "CHLD", SIGCHLD, /* Child status has changed (POSIX). */ + "TTIN", SIGTTIN, /* Background read from tty (POSIX). */ + "TTOU", SIGTTOU, /* Background write to tty (POSIX). */ + "IO", SIGIO, /* I/O now possible (4.2 BSD). */ + "XCPU", SIGXCPU, /* CPU limit exceeded (4.2 BSD). */ + "XFSZ", SIGXFSZ, /* File size limit exceeded (4.2 BSD). */ + "VTALRM", SIGVTALRM, /* Virtual alarm clock (4.2 BSD). */ + "PROF", SIGPROF, /* Profiling alarm clock (4.2 BSD). */ + "WINCH", SIGWINCH, /* Window size change (4.3 BSD, Sun). */ + "INFO", SIGINFO, /* Information request. */ + "USR1", SIGUSR1, /* User-defined signal 1 (POSIX). */ + "USR2", SIGUSR2 /* User-defined signal 2 (POSIX). */ + }; +#endif + +JNIEXPORT int JVM_FindSignal(const char *name) { + /* find and return the named signal's number */ + uint i; + + for (i = 0; i < ARRAY_SIZE(siglabels); i++) { + if(!strcmp(name, siglabels[i].name)) { + return siglabels[i].number; + } + } + return -1; +} + +JNIEXPORT int JVM_GetSockName(int fd, struct sockaddr* him, socklen_t* len) { + return getsockname(fd, him, len); +} + +JNIEXPORT int JVM_Listen(int fd, int count) { + return listen(fd, count); +} + +JNIEXPORT int JVM_RaiseSignal(int sig) { + raise(sig); + return 1; +} + +JNIEXPORT void * JVM_RegisterSignal(int sig, void* handler) { + struct sigaction sigAct, oldSigAct; + + sigfillset(&(sigAct.sa_mask)); + sigAct.sa_flags = SA_RESTART|SA_SIGINFO; + sigAct.sa_handler = CAST_TO_FN_PTR(sa_handler_t, handler); + + if (sigaction(sig, &sigAct, &oldSigAct)) { + /* -1 means registration failed */ + return (void *)-1; + } + + return CAST_FROM_FN_PTR(void*, oldSigAct.sa_handler); +} + +JNIEXPORT int JVM_Send(int fd, char* buf, size_t nBytes, uint flags) { + RESTARTABLE_RETURN_INT(send(fd, buf, nBytes, flags)); +} + +JNIEXPORT int JVM_SetSockOpt(int fd, int level, int optname, + const char* optval, socklen_t optlen) { + return setsockopt(fd, level, optname, optval, optlen); +} + +JNIEXPORT int JVM_SocketAvailable(int fd, int *pbytes) { + int ret; + + if (fd < 0) + return OS_OK; + + RESTARTABLE(ioctl(fd, FIONREAD, pbytes), ret); + + return (ret == OS_ERR) ? 0 : 1; +} + +JNIEXPORT int JVM_SocketClose(int fd) { + return close(fd); +} + +JNIEXPORT int JVM_SocketShutdown(int fd, int howto) { + return shutdown(fd, howto); +} + +/* Called directly from several native functions */ +JNIEXPORT int JVM_InitializeSocketLibrary() { + /* A noop, returns 0 in hotspot */ + return 0; +} + +JNIEXPORT jlong Java_java_lang_System_currentTimeMillis(void *env, void * ignored) { + struct timeval time; + int status = gettimeofday(&time, NULL); + return (jlong)(time.tv_sec * 1000) + (jlong)(time.tv_usec / 1000); +} + +JNIEXPORT jlong JVM_CurrentTimeMillis(void *env, void * ignored) { + return Java_java_lang_System_currentTimeMillis(env, ignored); +} + +JNIEXPORT void JVM_Halt(int retcode) { + _exit(retcode); +} + +JNIEXPORT int JVM_GetLastErrorString(char *buf, int len) { + const char *s; + size_t n; + + if (errno == 0) { + return 0; + } + + s = strerror(errno); + n = strlen(s); + if (n >= len) { + n = len - 1; + } + + strncpy(buf, s, n); + buf[n] = '\0'; + return n; +} + +int jio_vfprintf(FILE* f, const char *fmt, va_list args) { + return vfprintf(f, fmt, args); +} + +int jio_vsnprintf(char *str, size_t count, const char *fmt, va_list args) { + int result; + + if ((intptr_t)count <= 0) return -1; + + result = vsnprintf(str, count, fmt, args); + if ((result > 0 && (size_t)result >= count) || result == -1) { + str[count - 1] = '\0'; + result = -1; + } + + return result; +} + diff --git a/substratevm/src/com.oracle.svm.native.jvm.windows/src/JvmFuncs.c b/substratevm/src/com.oracle.svm.native.jvm.windows/src/JvmFuncs.c new file mode 100644 index 000000000000..cbb66b8d34ce --- /dev/null +++ b/substratevm/src/com.oracle.svm.native.jvm.windows/src/JvmFuncs.c @@ -0,0 +1,219 @@ +/* + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* JVM_ functions imported from the hotspot sources */ +#include +#include +#include +#include + +#define JNIEXPORT __declspec(dllexport) +#define JNIIMPORT __declspec(dllimport) + +#define BitsPerByte 8 + +typedef long jlong; +typedef int jint; + +static int _processor_count = 0; +static jlong _performance_frequency = 0L; + +jlong jlong_from(DWORD high, DWORD low) { + return ((((uint64_t)high) << 32) | low); +} + +jlong as_long(LARGE_INTEGER x) { + return jlong_from(x.HighPart, x.LowPart); +} + +JNIEXPORT void initialize() { + LARGE_INTEGER count; + SYSTEM_INFO si; + GetSystemInfo(&si); + _processor_count = si.dwNumberOfProcessors; + + if (QueryPerformanceFrequency(&count)) { + _performance_frequency = as_long(count); + } +} + +/* Only called in java.lang.Runtime native methods. */ +JNIEXPORT void JVM_FreeMemory() { + printf("JVM_FreeMemory called: Unimplemented\n"); +} + +JNIEXPORT jlong JVM_TotalMemory() { + printf("JVM_TotalMemory called: Unimplemented\n"); + return 0L; +} + +JNIEXPORT jlong JVM_MaxMemory() { + printf("JVM_MaxMemory called: Unimplemented\n"); + return 0L; +} + +JNIEXPORT void JVM_GC() { + printf("JVM_GC called: Unimplemented\n"); +} + +JNIEXPORT void JVM_TraceInstructions(int on) { + printf("JVM_TraceInstructions called: Unimplemented\n"); +} + +JNIEXPORT void JVM_TraceMethodCalls(int on) { + printf("JVM_TraceMethods called: Unimplemented\n"); +} + +JNIEXPORT int JVM_ActiveProcessorCount() { + DWORD_PTR lpProcessAffinityMask = 0; + DWORD_PTR lpSystemAffinityMask = 0; + if (_processor_count <= sizeof(UINT_PTR) * BitsPerByte && + GetProcessAffinityMask(GetCurrentProcess(), &lpProcessAffinityMask, &lpSystemAffinityMask)) { + int bitcount = 0; + // Nof active processors is number of bits in process affinity mask + while (lpProcessAffinityMask != 0) { + lpProcessAffinityMask = lpProcessAffinityMask & (lpProcessAffinityMask-1); + bitcount++; + } + return bitcount; + } else { + return _processor_count; + } +} + +HANDLE interrupt_event = NULL; + +JNIEXPORT HANDLE JVM_GetThreadInterruptEvent() { + if (interrupt_event != NULL) { + return interrupt_event; + } + interrupt_event = CreateEvent(NULL, TRUE, FALSE, NULL); + return interrupt_event; +} + +/* Called directly from several native functions */ +JNIEXPORT int JVM_InitializeSocketLibrary() { + /* A noop, returns 0 in hotspot */ + return 0; +} + +static jlong _time_offset = 116444736000000000L; +static jlong NANOSECS_PER_SEC = 1000000000L; +static jint NANOSECS_PER_MILLISEC = 1000000; + +static jlong getCurrentTimeMillis() { + jlong a; + FILETIME wt; + GetSystemTimeAsFileTime(&wt); + a = jlong_from(wt.dwHighDateTime, wt.dwLowDateTime); + return (a - _time_offset) / 10000; +} + +JNIEXPORT jlong Java_java_lang_System_nanoTime(void *env, void * ignored) { + LARGE_INTEGER current_count; + double current, freq; + jlong time; + + if (_performance_frequency == 0L) { + return (getCurrentTimeMillis() * NANOSECS_PER_MILLISEC); + } + + QueryPerformanceCounter(¤t_count); + current = as_long(current_count); + freq = _performance_frequency; + time = (jlong)((current/freq) * NANOSECS_PER_SEC); + return time; +} + +JNIEXPORT jlong JVM_NanoTime(void *env, void * ignored) { + return Java_java_lang_System_nanoTime(env, ignored); +} + +JNIEXPORT jlong Java_java_lang_System_currentTimeMillis(void *env, void * ignored) { + return (Java_java_lang_System_nanoTime(env, ignored) / NANOSECS_PER_MILLISEC); +} + +JNIEXPORT jlong JVM_CurrentTimeMillis(void *env, void * ignored) { + return Java_java_lang_System_currentTimeMillis(env, ignored); +} + +JNIEXPORT void JVM_Halt(int retcode) { + _exit(retcode); +} + +JNIEXPORT int JVM_GetLastErrorString(char *buf, int len) { + DWORD errval; + + if ((errval = GetLastError()) != 0) { + /* DOS error */ + size_t n = (size_t)FormatMessage( + FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + errval, + 0, + buf, + (DWORD)len, + NULL); + if (n > 3) { + /* Drop final '.', CR, LF */ + if (buf[n - 1] == '\n') n--; + if (buf[n - 1] == '\r') n--; + if (buf[n - 1] == '.') n--; + buf[n] = '\0'; + } + return n; + } + + if (errno != 0) { + /* C runtime error that has no corresponding DOS error code */ + const char* s = strerror(errno); + size_t n = strlen(s); + if (n >= len) n = len - 1; + strncpy(buf, s, n); + buf[n] = '\0'; + return n; + } + + return 0; +} + +int jio_vfprintf(FILE* f, const char *fmt, va_list args) { + return vfprintf(f, fmt, args); +} + +int jio_vsnprintf(char *str, size_t count, const char *fmt, va_list args) { + int result; + + if ((intptr_t)count <= 0) return -1; + + result = vsnprintf(str, count, fmt, args); + if ((result > 0 && (size_t)result >= count) || result == -1) { + str[count - 1] = '\0'; + result = -1; + } + + return result; +} + diff --git a/substratevm/src/com.oracle.svm.native.jvm/src/JvmFuncs.c b/substratevm/src/com.oracle.svm.native.jvm/src/JvmFuncs.c deleted file mode 100644 index 482779b6fae5..000000000000 --- a/substratevm/src/com.oracle.svm.native.jvm/src/JvmFuncs.c +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright (c) 2018, 2018, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -/* JvmFuncs is currently only used on Windows */ - -#ifdef _WIN64 - -#include -#include - -#define JNIEXPORT __declspec(dllexport) -#define JNIIMPORT __declspec(dllimport) - -JNIEXPORT initialize() { -} - -/* Only called in java.lang.Runtime native methods. */ -JNIEXPORT JVM_FreeMemory() { - printf("JVM_FreeMemory called: Unimplemented\n"); -} - -JNIEXPORT JVM_TotalMemory() { - printf("JVM_TotalMemory called: Unimplemented\n"); -} - - -JNIEXPORT JVM_MaxMemory() { - printf("JVM_MaxMemory called: Unimplemented\n"); -} - -JNIEXPORT JVM_GC() { - printf("JVM_GC called: Unimplemented\n"); -} - -JNIEXPORT JVM_TraceInstructions() { - printf("JVM_TraceInstructions called: Unimplemented\n"); -} - -JNIEXPORT JVM_TraceMethodCalls() { - printf("JVM_TraceMethods called: Unimplemented\n"); -} - -JNIEXPORT JVM_ActiveProcessorCount() { - printf("JVM_ActiveProcessorCount called: Unimplemented\n"); -} - - -HANDLE interrupt_event = NULL; - -JNIEXPORT HANDLE JVM_GetThreadInterruptEvent() { - if (interrupt_event != NULL) { - return interrupt_event; - } - interrupt_event = CreateEvent(NULL, TRUE, FALSE, NULL); - return interrupt_event; -} - - -/* Called directly from several native functions */ -JNIEXPORT int JVM_InitializeSocketLibrary() { - /* A noop, returns 0 in hotspot */ - return 0; -} - -JNIEXPORT JVM_CurrentTimeMillis() { - printf("JVM_CurrentTimeMillis called: Unimplemented\n"); -} - -JNIEXPORT JVM_GetLastErrorString() { - printf("JVM_GetLastErrorString called: Unimplemented\n"); -} - -int jio_vsnprintf(char *str, size_t count, const char *fmt, va_list args) { - int result; - - if ((intptr_t)count <= 0) return -1; - - result = vsnprintf(str, count, fmt, args); - if ((result > 0 && (size_t)result >= count) || result == -1) { - str[count - 1] = '\0'; - result = -1; - } - - return result; -} - -int jio_snprintf(char *str, size_t count, const char *fmt, ...) { - va_list args; - int len; - va_start(args, fmt); - len = jio_vsnprintf(str, count, fmt, args); - va_end(args); - return len; -} - -int jio_fprintf(FILE* f, const char *fmt, ...) { - int len; - va_list args; - va_start(args, fmt); - len = jio_vfprintf(f, fmt, args); - va_end(args); - return len; -} - -int jio_vfprintf(FILE* f, const char *fmt, va_list args) { - return vfprintf(f, fmt, args); -} - -#endif diff --git a/substratevm/src/com.oracle.svm.native.libchelper/src/macrosAsFunctions.c b/substratevm/src/com.oracle.svm.native.libchelper/src/macrosAsFunctions.c new file mode 100644 index 000000000000..e8943a20b99b --- /dev/null +++ b/substratevm/src/com.oracle.svm.native.libchelper/src/macrosAsFunctions.c @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2019, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#if (defined(__APPLE__) || defined(__linux__)) +/* + * Macros from made available as C functions. + */ + +#include + +void sys_select_FD_SET(int fd, fd_set *set) { + FD_SET(fd, set); +} + +void sys_select_FD_ZERO(fd_set *set) { + FD_ZERO(set); +} +#endif // (defined(__APPLE__) || defined(__linux__)) + +#if (defined(__APPLE__) || defined(__linux__)) +/* + * Macros from made available as C functions. + */ + +#include + +int sys_param_howmany(int x, int y) { + return howmany(x, y); +} +#endif // (defined(__APPLE__) || defined(__linux__)) diff --git a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionSubstitution.java b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionSubstitution.java index 1db96582bdc1..ff4f8ddc298b 100644 --- a/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionSubstitution.java +++ b/substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionSubstitution.java @@ -33,7 +33,7 @@ import java.lang.reflect.Method; import java.util.HashMap; -import org.graalvm.compiler.serviceprovider.GraalServices; +import org.graalvm.compiler.serviceprovider.JavaVersionUtil; import com.oracle.svm.core.SubstrateUtil; import com.oracle.svm.core.jdk.UninterruptibleUtils.AtomicInteger; @@ -99,7 +99,7 @@ private static Class getAccessorInterface(Member member) { /** Track classes in the `reflect` package across JDK versions. */ private static Class packageJdkInternalReflectClassForName(String className) { - final String packageName = (GraalServices.Java8OrEarlier ? "sun.reflect." : "jdk.internal.reflect."); + final String packageName = (JavaVersionUtil.Java8OrEarlier ? "sun.reflect." : "jdk.internal.reflect."); try { /* { Allow reflection in hosted code. Checkstyle: stop. */ return Class.forName(packageName + className); @@ -115,7 +115,7 @@ private static byte[] generateProxyClass(final String name, Class[] interface /* { Allow reflection in hosted code. Checkstyle: stop. */ try { if (generateProxyMethod == null) { - final String packageName = (GraalServices.Java8OrEarlier ? "sun.misc." : "java.lang.reflect."); + final String packageName = (JavaVersionUtil.Java8OrEarlier ? "sun.misc." : "java.lang.reflect."); generateProxyMethod = Class.forName(packageName + "ProxyGenerator").getDeclaredMethod("generateProxyClass", String.class, Class[].class); generateProxyMethod.setAccessible(true); } diff --git a/substratevm/src/com.oracle.svm.truffle.nfi/src/com/oracle/svm/truffle/nfi/TruffleNFISupport.java b/substratevm/src/com.oracle.svm.truffle.nfi/src/com/oracle/svm/truffle/nfi/TruffleNFISupport.java index 9141fc57beb1..5ce237a51de0 100644 --- a/substratevm/src/com.oracle.svm.truffle.nfi/src/com/oracle/svm/truffle/nfi/TruffleNFISupport.java +++ b/substratevm/src/com.oracle.svm.truffle.nfi/src/com/oracle/svm/truffle/nfi/TruffleNFISupport.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,8 +31,8 @@ import org.graalvm.nativeimage.ObjectHandle; import org.graalvm.nativeimage.ObjectHandles; import org.graalvm.nativeimage.Platform; -import org.graalvm.nativeimage.Platform.DARWIN; -import org.graalvm.nativeimage.Platform.LINUX; +import org.graalvm.nativeimage.Platform.DARWIN_AND_JNI; +import org.graalvm.nativeimage.Platform.LINUX_AND_JNI; import org.graalvm.nativeimage.c.type.CCharPointer; import org.graalvm.nativeimage.c.type.CTypeConversion; import org.graalvm.word.SignedWord; @@ -63,9 +63,9 @@ public final class TruffleNFISupport { closureHandles = ObjectHandles.create(); contextHandles = ObjectHandles.create(); - if (Platform.includedIn(LINUX.class)) { + if (Platform.includedIn(LINUX_AND_JNI.class)) { errnoGetterFunctionName = "__errno_location"; - } else if (Platform.includedIn(DARWIN.class)) { + } else if (Platform.includedIn(DARWIN_AND_JNI.class)) { errnoGetterFunctionName = "__error"; } else { throw VMError.unsupportedFeature("unsupported platform for TruffleNFIFeature"); diff --git a/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleFeature.java b/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleFeature.java index 92e0feb185f5..21e550780740 100644 --- a/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleFeature.java +++ b/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleFeature.java @@ -35,7 +35,6 @@ import java.lang.reflect.Modifier; import java.math.BigDecimal; import java.math.BigInteger; -import java.nio.file.spi.FileTypeDetector; import java.util.Arrays; import java.util.Collection; import java.util.Collections; @@ -96,8 +95,6 @@ import com.oracle.svm.core.annotate.TargetClass; import com.oracle.svm.core.deopt.Deoptimizer; import com.oracle.svm.core.heap.Heap; -import com.oracle.svm.core.jdk.FilesFeature; -import com.oracle.svm.core.jdk.FilesSupport; import com.oracle.svm.core.option.HostedOptionKey; import com.oracle.svm.core.option.HostedOptionValues; import com.oracle.svm.core.option.SubstrateOptionsParser; @@ -107,7 +104,6 @@ import com.oracle.svm.graal.hosted.GraalFeature; import com.oracle.svm.graal.hosted.GraalFeature.CallTreeNode; import com.oracle.svm.graal.hosted.GraalFeature.RuntimeBytecodeParser; -import com.oracle.svm.hosted.FeatureImpl.AfterRegistrationAccessImpl; import com.oracle.svm.hosted.FeatureImpl.BeforeAnalysisAccessImpl; import com.oracle.svm.hosted.FeatureImpl.BeforeCompilationAccessImpl; import com.oracle.svm.hosted.code.InliningUtilities; @@ -204,7 +200,7 @@ public static Support getSupport() { @Override public List> getRequiredFeatures() { - return Arrays.asList(GraalFeature.class, NodeClassFeature.class, FilesFeature.class); + return Arrays.asList(GraalFeature.class, NodeClassFeature.class); } private static void initializeTruffleReflectively(ClassLoader imageClassLoader) { @@ -258,22 +254,9 @@ public void afterRegistration(AfterRegistrationAccess a) { truffleRuntime.resetHosted(); } - /* sun.nio.fs.GnomeFileTypeDetector is currently not supported (GR-4863) */ - AfterRegistrationAccessImpl access = (AfterRegistrationAccessImpl) a; - access.findSubclasses(FileTypeDetector.class).stream().filter(detector -> !detector.getClass().getName().equals("sun.nio.fs.GnomeFileTypeDetector")).filter( - detector -> !Modifier.isAbstract(detector.getModifiers())).forEach(this::safeLoadFileDetector); - initializeTruffleReflectively(Thread.currentThread().getContextClassLoader()); } - private void safeLoadFileDetector(Class detector) { - try { - ImageSingletons.lookup(FilesSupport.class).addFileTypeDetector(detector.getDeclaredConstructor().newInstance()); - } catch (Exception ex) { - throw VMError.shouldNotReachHere(ex); - } - } - @Override public void cleanup() { // clean the cached call target nodes to prevent them from keeping application classes alive diff --git a/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/api/SubstrateOptimizedCallTarget.java b/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/api/SubstrateOptimizedCallTarget.java index 7e20cab792c9..fa52e9acfdd3 100644 --- a/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/api/SubstrateOptimizedCallTarget.java +++ b/substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/api/SubstrateOptimizedCallTarget.java @@ -41,6 +41,7 @@ import com.oracle.truffle.api.nodes.RootNode; import jdk.vm.ci.meta.ResolvedJavaMethod; +import jdk.vm.ci.meta.SpeculationLog; public class SubstrateOptimizedCallTarget extends OptimizedCallTarget implements SubstrateInstalledCode, OptimizedAssumptionDependency { @@ -56,6 +57,11 @@ public SubstrateSpeculationLog getSpeculationLog() { return (SubstrateSpeculationLog) super.getSpeculationLog(); } + @Override + public SpeculationLog getCompilationSpeculationLog() { + return getSpeculationLog(); + } + @Override public void invalidate() { invalidate(null, null); diff --git a/substratevm/src/org.graalvm.polyglot.nativeapi.native/src/polyglot-nativeapi/polyglot-nativeapi.c b/substratevm/src/org.graalvm.polyglot.nativeapi.native/src/polyglot-nativeapi/polyglot-nativeapi.c index e9d842bd3c50..39b570879a77 100644 --- a/substratevm/src/org.graalvm.polyglot.nativeapi.native/src/polyglot-nativeapi/polyglot-nativeapi.c +++ b/substratevm/src/org.graalvm.polyglot.nativeapi.native/src/polyglot-nativeapi/polyglot-nativeapi.c @@ -88,6 +88,8 @@ extern graal_isolate_t* graal_get_isolate(graal_isolatethread_t* thread); */ extern int graal_detach_thread(graal_isolatethread_t* thread); +extern int graal_detach_threads(graal_isolatethread_t* thread, graal_isolatethread_t** threads, int length); + /* * Tears down the passed isolate, waiting for any attached threads to detach from * it, then discards the isolate's objects, threads, and any other state or context @@ -130,6 +132,14 @@ poly_status poly_detach_thread(graal_isolatethread_t* thread) { } }; +poly_status poly_detach_threads(graal_isolatethread_t* thread, graal_isolatethread_t** threads, int length) { + if (graal_detach_threads(thread, threads, length)) { + return poly_generic_failure; + } else { + return poly_ok; + } +}; + poly_status poly_tear_down_isolate(graal_isolatethread_t* thread) { if (graal_tear_down_isolate(thread)) { return poly_generic_failure; diff --git a/substratevm/src/org.graalvm.polyglot.nativeapi/resources/polyglot_isolate.h b/substratevm/src/org.graalvm.polyglot.nativeapi/resources/polyglot_isolate.h index 68081361bc76..bbcd6126e1bf 100644 --- a/substratevm/src/org.graalvm.polyglot.nativeapi/resources/polyglot_isolate.h +++ b/substratevm/src/org.graalvm.polyglot.nativeapi/resources/polyglot_isolate.h @@ -37,6 +37,8 @@ extern "C" { * Returns poly_ok on success, or a poly_generic_failure value on failure. * On success, the current thread is attached to the created isolate, and the * address of the isolate structure is written to the passed pointer. + * Every thread starts with a default handle scope. This scope is released when + * the thread is detached. */ poly_status poly_create_isolate(const poly_isolate_params* params, poly_isolate* isolate, poly_thread* thread); @@ -71,6 +73,26 @@ poly_isolate poly_get_isolate(poly_thread thread); */ poly_status poly_detach_thread(poly_thread thread); +/** + * Using the context of the isolate thread from the first argument, detaches the + * threads in an array pointed to by the second argument, with the length of the + * array given in the third argument. All of the passed threads must be in the + * same isolate, including the first argument. None of the threads to detach may + * execute Java code at the time of the call or later without reattaching first, + * or their behavior will be entirely undefined. The current thread may be part of + * the array, however, using detach_thread() should be preferred for detaching only + * the current thread. + * + * @param thread current thread + * @param threads array of threads to detach + * @param length number of threads in the array + * @return poly_ok success, or poly_generic_failure on failure. + * + * @see graal_detach_threads + * @since 1.0 + */ +poly_status poly_detach_threads(poly_thread thread, poly_thread* threads, int length); + /* * Tears down the passed isolate, waiting for any attached threads to detach from * it, then discards the isolate's objects, threads, and any other state or context diff --git a/substratevm/src/org.graalvm.polyglot.nativeapi/resources/polyglot_types.h b/substratevm/src/org.graalvm.polyglot.nativeapi/resources/polyglot_types.h index e4814f02cb70..659c43cfa2cd 100644 --- a/substratevm/src/org.graalvm.polyglot.nativeapi/resources/polyglot_types.h +++ b/substratevm/src/org.graalvm.polyglot.nativeapi/resources/polyglot_types.h @@ -52,6 +52,8 @@ typedef struct { typedef void* poly_handle; +typedef poly_handle poly_reference; + typedef poly_handle poly_value; typedef poly_handle poly_engine; diff --git a/substratevm/src/org.graalvm.polyglot.nativeapi/src/org/graalvm/polyglot/nativeapi/PolyglotNativeAPI.java b/substratevm/src/org.graalvm.polyglot.nativeapi/src/org/graalvm/polyglot/nativeapi/PolyglotNativeAPI.java index 5713af0a7af8..f4242472ac89 100644 --- a/substratevm/src/org.graalvm.polyglot.nativeapi/src/org/graalvm/polyglot/nativeapi/PolyglotNativeAPI.java +++ b/substratevm/src/org.graalvm.polyglot.nativeapi/src/org/graalvm/polyglot/nativeapi/PolyglotNativeAPI.java @@ -42,7 +42,6 @@ import org.graalvm.nativeimage.CurrentIsolate; import org.graalvm.nativeimage.ObjectHandle; -import org.graalvm.nativeimage.ObjectHandles; import org.graalvm.nativeimage.UnmanagedMemory; import org.graalvm.nativeimage.c.function.CEntryPoint; import org.graalvm.nativeimage.c.struct.SizeOf; @@ -67,6 +66,7 @@ import org.graalvm.polyglot.nativeapi.types.CUnsignedBytePointer; import org.graalvm.polyglot.nativeapi.types.CUnsignedIntPointer; import org.graalvm.polyglot.nativeapi.types.CUnsignedShortPointer; +import org.graalvm.polyglot.nativeapi.types.PolyglotNativeAPITypes; import org.graalvm.polyglot.nativeapi.types.PolyglotNativeAPITypes.PolyglotCallback; import org.graalvm.polyglot.nativeapi.types.PolyglotNativeAPITypes.PolyglotCallbackInfo; import org.graalvm.polyglot.nativeapi.types.PolyglotNativeAPITypes.PolyglotContext; @@ -79,7 +79,6 @@ import org.graalvm.polyglot.nativeapi.types.PolyglotNativeAPITypes.PolyglotEnginePointer; import org.graalvm.polyglot.nativeapi.types.PolyglotNativeAPITypes.PolyglotExtendedErrorInfo; import org.graalvm.polyglot.nativeapi.types.PolyglotNativeAPITypes.PolyglotExtendedErrorInfoPointer; -import org.graalvm.polyglot.nativeapi.types.PolyglotNativeAPITypes.PolyglotHandle; import org.graalvm.polyglot.nativeapi.types.PolyglotNativeAPITypes.PolyglotIsolateThread; import org.graalvm.polyglot.nativeapi.types.PolyglotNativeAPITypes.PolyglotLanguage; import org.graalvm.polyglot.nativeapi.types.PolyglotNativeAPITypes.PolyglotLanguagePointer; @@ -96,6 +95,10 @@ import com.oracle.svm.core.c.CConst; import com.oracle.svm.core.c.CHeader; import com.oracle.svm.core.c.CUnsigned; +import com.oracle.svm.core.handles.ObjectHandlesImpl; +import com.oracle.svm.core.handles.ThreadLocalHandles; +import com.oracle.svm.core.threadlocal.FastThreadLocalFactory; +import com.oracle.svm.core.threadlocal.FastThreadLocalObject; @SuppressWarnings("unused") @CHeader(value = PolyglotAPIHeader.class) @@ -107,9 +110,22 @@ public final class PolyglotNativeAPI { private static final int MAX_UNSIGNED_SHORT = (1 << 16) - 1; private static final long MAX_UNSIGNED_INT = (1L << 32) - 1; private static final UnsignedWord POLY_AUTO_LENGTH = WordFactory.unsigned(0xFFFFFFFFFFFFFFFFL); + private static final int DEFAULT_FRAME_CAPACITY = 16; private static ThreadLocal errorInfo = new ThreadLocal<>(); private static ThreadLocal exceptionsTL = new ThreadLocal<>(); + @SuppressWarnings("rawtypes") private static final FastThreadLocalObject handles = FastThreadLocalFactory.createObject(ThreadLocalHandles.class); + + @SuppressWarnings("unchecked") + private static ThreadLocalHandles getHandles() { + if (handles.get() == null) { + handles.set(new ThreadLocalHandles(DEFAULT_FRAME_CAPACITY)); + } + return handles.get(); + } + + private static final ObjectHandlesImpl objectHandles = new ObjectHandlesImpl( + WordFactory.signed(Long.MIN_VALUE), ThreadLocalHandles.nullHandle().subtract(1), ThreadLocalHandles.nullHandle()); private static class ErrorInfoHolder { PolyglotExtendedErrorInfo info; @@ -176,7 +192,7 @@ public static PolyglotStatus poly_engine_builder_build(PolyglotIsolateThread thr }) public static PolyglotStatus poly_create_engine(PolyglotIsolateThread thread, PolyglotEnginePointer result) { return withHandledErrors(() -> { - ObjectHandle handle = createHandle(Engine.create()); + PolyglotNativeAPITypes.PolyglotHandle handle = createHandle(Engine.create()); result.write(handle); }); } @@ -1422,10 +1438,6 @@ public static PolyglotStatus poly_create_function(PolyglotIsolateThread thread, } } finally { PolyglotCallbackInfoInternal info = fetchHandle(cbInfo); - for (ObjectHandle arg : info.arguments) { - destroyHandle(arg); - } - destroyHandle(cbInfo); } }; value.write(createHandle(c.asValue(executable))); @@ -1471,15 +1483,50 @@ public static PolyglotStatus poly_throw_exception(PolyglotIsolateThread thread, return withHandledErrors(() -> exceptionsTL.set(new CallbackException(CTypeConversion.toJavaString(utf8_message)))); } - @CEntryPoint(name = "poly_destroy_handle", documentation = { - "Destroys a poly_handle. After this point, the handle must not be used anymore. ", + @CEntryPoint(name = "poly_delete_reference", documentation = { + "Deletes a poly_reference. After this point, the reference must not be used anymore.", + "", + " @since 1.0", + }) + public static PolyglotStatus poly_delete_reference(PolyglotIsolateThread thread, PolyglotNativeAPITypes.PolyglotReference reference) { + return withHandledErrors(() -> objectHandles.destroy(reference)); + } + + @CEntryPoint(name = "poly_create_reference", documentation = { + "Creates a poly_reference from a poly_handle. After this point, the reference is alive until poly_delete_reference is called. ", "", "Handles are: poly_engine, poly_engine_builder, poly_context, poly_context_builder, poly_language, poly_value, ", "and poly_callback_info.", " @since 1.0", }) - public static PolyglotStatus poly_destroy_handle(PolyglotIsolateThread thread, PolyglotHandle handle) { - return withHandledErrors(() -> destroyHandle(handle)); + public static PolyglotStatus poly_create_reference(PolyglotIsolateThread thread, PolyglotNativeAPITypes.PolyglotHandle handle, PolyglotNativeAPITypes.PolyglotReferencePointer reference) { + + return withHandledErrors(() -> { + ObjectHandle ref = objectHandles.create(getHandles().getObject(handle)); + reference.write((PolyglotNativeAPITypes.PolyglotReference) ref); + }); + } + + @CEntryPoint(name = "poly_open_handle_scope", documentation = { + "Opens a handle scope. Until the scope is closed, all objects will belong to the newly created scope.", + "", + "Handles are: poly_engine, poly_engine_builder, poly_context, poly_context_builder, poly_language, poly_value, ", + "and poly_callback_info.", + " @since 1.0", + }) + public static PolyglotStatus poly_open_handle_scope(PolyglotIsolateThread thread) { + return withHandledErrors(() -> getHandles().pushFrame(DEFAULT_FRAME_CAPACITY)); + } + + @CEntryPoint(name = "poly_close_handle_scope", documentation = { + "Closes a handle scope. After this point, the handles from the current scope must not be used anymore.", + "", + "Handles are: poly_engine, poly_engine_builder, poly_context, poly_context_builder, poly_language, poly_value, ", + "and poly_callback_info.", + " @since 1.0", + }) + public static PolyglotStatus poly_close_handle_scope(PolyglotIsolateThread thread) { + return withHandledErrors(() -> getHandles().popFrame()); } private static class PolyglotCallbackInfoInternal { @@ -1553,16 +1600,25 @@ private static PolyglotStatus withHandledErrors(VoidThunk func) { } } - private static ObjectHandle createHandle(Object result) { - return ObjectHandles.getGlobal().create(result); + private static PolyglotNativeAPITypes.PolyglotHandle createHandle(Object result) { + return getHandles().create(result); } - private static T fetchHandle(ObjectHandle object) { - return ObjectHandles.getGlobal().get(object); - } + @SuppressWarnings("unchecked") + private static T fetchHandle(PolyglotNativeAPITypes.PolyglotHandle object) { + if (object.equal(ThreadLocalHandles.nullHandle())) { + return null; + } + + if (ThreadLocalHandles.isInRange(object)) { + return getHandles().getObject(object); + } + + if (objectHandles.isInRange(object)) { + return objectHandles.get(object); + } - private static void destroyHandle(ObjectHandle handle) { - ObjectHandles.getGlobal().destroy(handle); + throw new RuntimeException("Invalid poly_reference or poly_handle."); } public static class CallbackException extends RuntimeException { diff --git a/substratevm/src/org.graalvm.polyglot.nativeapi/src/org/graalvm/polyglot/nativeapi/PolyglotNativeAPIFeature.java b/substratevm/src/org.graalvm.polyglot.nativeapi/src/org/graalvm/polyglot/nativeapi/PolyglotNativeAPIFeature.java index 7146d3b5e873..cc04b30117d2 100644 --- a/substratevm/src/org.graalvm.polyglot.nativeapi/src/org/graalvm/polyglot/nativeapi/PolyglotNativeAPIFeature.java +++ b/substratevm/src/org.graalvm.polyglot.nativeapi/src/org/graalvm/polyglot/nativeapi/PolyglotNativeAPIFeature.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -64,7 +64,7 @@ public void afterImageWrite(AfterImageWriteAccess access) { throw new RuntimeException(e); } }); - if (Platform.includedIn(Platform.DARWIN.class)) { + if (Platform.includedIn(Platform.DARWIN_AND_JNI.class)) { // on Darwin, change the `id` install name String id = System.getProperty("org.graalvm.polyglot.install_name_id"); if (id == null) { diff --git a/substratevm/src/org.graalvm.polyglot.nativeapi/src/org/graalvm/polyglot/nativeapi/types/PolyglotNativeAPITypes.java b/substratevm/src/org.graalvm.polyglot.nativeapi/src/org/graalvm/polyglot/nativeapi/types/PolyglotNativeAPITypes.java index 4349a0a50b68..eccf99c451f8 100644 --- a/substratevm/src/org.graalvm.polyglot.nativeapi/src/org/graalvm/polyglot/nativeapi/types/PolyglotNativeAPITypes.java +++ b/substratevm/src/org.graalvm.polyglot.nativeapi/src/org/graalvm/polyglot/nativeapi/types/PolyglotNativeAPITypes.java @@ -105,13 +105,13 @@ public interface SizeTPointer extends PointerBase { @CPointerTo(nameOfCType = "poly_engine") @CTypedef(name = "poly_engine") - public interface PolyglotEngine extends PointerBase, ObjectHandle { + public interface PolyglotEngine extends PointerBase, PolyglotHandle { } @CPointerTo(nameOfCType = "poly_engine") - public interface PolyglotEnginePointer extends PointerBase, ObjectHandle { + public interface PolyglotEnginePointer extends PointerBase, PolyglotHandle { - void write(ObjectHandle value); + void write(PolyglotHandle value); } @@ -121,13 +121,24 @@ public interface PolyglotHandle extends PointerBase, ObjectHandle { } + @CPointerTo(nameOfCType = "poly_reference") + @CTypedef(name = "poly_reference") + public interface PolyglotReference extends PointerBase, PolyglotHandle { + + } + + @CPointerTo(nameOfCType = "poly_reference") + public interface PolyglotReferencePointer extends PointerBase, PolyglotHandle { + void write(PolyglotReference value); + } + @CPointerTo(nameOfCType = "poly_context") @CTypedef(name = "poly_context") - public interface PolyglotContext extends PointerBase, ObjectHandle { + public interface PolyglotContext extends PointerBase, PolyglotHandle { } @CPointerTo(nameOfCType = "poly_context") - public interface PolyglotContextPointer extends PointerBase, ObjectHandle { + public interface PolyglotContextPointer extends PointerBase, PolyglotHandle { void write(ObjectHandle value); @@ -135,11 +146,11 @@ public interface PolyglotContextPointer extends PointerBase, ObjectHandle { @CPointerTo(nameOfCType = "poly_context_builder") @CTypedef(name = "poly_context_builder") - public interface PolyglotContextBuilder extends PointerBase, ObjectHandle { + public interface PolyglotContextBuilder extends PointerBase, PolyglotHandle { } @CPointerTo(nameOfCType = "poly_context_builder") - public interface PolyglotContextBuilderPointer extends PointerBase, ObjectHandle { + public interface PolyglotContextBuilderPointer extends PointerBase, PolyglotHandle { void write(ObjectHandle value); @@ -147,11 +158,11 @@ public interface PolyglotContextBuilderPointer extends PointerBase, ObjectHandle @CPointerTo(nameOfCType = "poly_engine_builder") @CTypedef(name = "poly_engine_builder") - public interface PolyglotEngineBuilder extends PointerBase, ObjectHandle { + public interface PolyglotEngineBuilder extends PointerBase, PolyglotHandle { } @CPointerTo(nameOfCType = "poly_engine_builder") - public interface PolyglotEngineBuilderPointer extends PointerBase, ObjectHandle { + public interface PolyglotEngineBuilderPointer extends PointerBase, PolyglotHandle { void write(ObjectHandle value); @@ -159,16 +170,16 @@ public interface PolyglotEngineBuilderPointer extends PointerBase, ObjectHandle @CPointerTo(nameOfCType = "poly_value") @CTypedef(name = "poly_value") - public interface PolyglotValue extends PointerBase, ObjectHandle { + public interface PolyglotValue extends PointerBase, PolyglotHandle { } @CPointerTo(nameOfCType = "poly_language") @CTypedef(name = "poly_language") - public interface PolyglotLanguage extends PointerBase, ObjectHandle { + public interface PolyglotLanguage extends PointerBase, PolyglotHandle { } @CPointerTo(nameOfCType = "poly_language") - public interface PolyglotLanguagePointer extends PointerBase, ObjectHandle { + public interface PolyglotLanguagePointer extends PointerBase, PolyglotHandle { void write(ObjectHandle value); @@ -176,7 +187,7 @@ public interface PolyglotLanguagePointer extends PointerBase, ObjectHandle { } @CPointerTo(nameOfCType = "poly_value") - public interface PolyglotValuePointer extends PointerBase, ObjectHandle { + public interface PolyglotValuePointer extends PointerBase, PolyglotHandle { PolyglotValue read(long index); @@ -187,7 +198,7 @@ public interface PolyglotValuePointer extends PointerBase, ObjectHandle { @CPointerTo(nameOfCType = "poly_callback_info") @CTypedef(name = "poly_callback_info") - public interface PolyglotCallbackInfo extends ObjectHandle, PointerBase { + public interface PolyglotCallbackInfo extends PointerBase, PolyglotHandle { } @CTypedef(name = "poly_callback") diff --git a/sulong/CHANGELOG.md b/sulong/CHANGELOG.md index 97b1d107613d..95003724b99b 100644 --- a/sulong/CHANGELOG.md +++ b/sulong/CHANGELOG.md @@ -7,6 +7,10 @@ New features: as well as bitcode files in an embedded xar archive in the `__bundle` section of executables or dylibs. +Changes: + +* Update libc++/libc++abi imports to 5.0.2 (no actual code changes). + # Version 1.0.0 RC12 Removed: diff --git a/sulong/LICENSE b/sulong/LICENSE index 8c5cbf7a05a2..fc11e4d8ff34 100644 --- a/sulong/LICENSE +++ b/sulong/LICENSE @@ -1,8 +1,8 @@ -Sulong is copyright (c) 2013-2018 Oracle and/or its affiliates, and is licensed for use as follows: +Sulong is copyright (c) 2013-2019 Oracle and/or its affiliates, and is licensed for use as follows: """ -Copyright (c) 2013, 2018, Oracle and/or its affiliates. +Copyright (c) 2013, 2019, Oracle and/or its affiliates. All rights reserved. diff --git a/sulong/ci.hocon b/sulong/ci.hocon index b431b23eb2c3..36656cecfb55 100644 --- a/sulong/ci.hocon +++ b/sulong/ci.hocon @@ -101,7 +101,7 @@ sulong-gate-asm-parser: ${sulong-gateCommon} ${linux-amd64} { sulong-coverage-linux: ${sulong-gateTest38-linux} ${requireGCC} ${sulong-weekly-notifications} { run: [ [mx, --jacoco-whitelist-package, com.oracle.truffle.llvm, --jacoco-exclude-annotation, "@GeneratedBy", gate, --tags, "build,sulongCoverage", --jacocout, html] - [mx, --jacoco-whitelist-package, com.oracle.truffle.llvm, --jacoco-exclude-annotation, "@GeneratedBy", sonarqube-upload, "-Dsonar.host.url=$SONAR_HOST_URL", "-Dsonar.projectKey=com.oracle.graalvm.sulong", --exclude-generated] + [mx, --jacoco-whitelist-package, com.oracle.truffle.llvm, --jacoco-exclude-annotation, "@GeneratedBy", sonarqube-upload, "-Dsonar.host.url=$SONAR_HOST_URL", "-Dsonar.projectKey=com.oracle.graalvm.sulong", "-Dsonar.projectName=GraalVM - Sulong", --exclude-generated] ] targets: [weekly] timelimit: "1:00:00" @@ -122,7 +122,7 @@ builds += [ ${sulong-gateTest60-linux} ${requireGCC} { name: gate-sulong-basic_v60, run: [[mx, gate, --tags, "build,sulongBasic,nwcc,llvm"]] } ${sulong-gateTest40-darwin} { name: gate-sulong-basic_mac, run: [[mx, gate, --tags, "build,sulongBasic,nwcc,llvm"]] } - ${sulong-gateTest38-linux} ${sulong-ruby-downstream-test} { name: gate-sulong-ruby-downstream } + ${sulong-gateTest38-linux} ${sulong-ruby-downstream-test} { targets: [post-merge], name: gate-sulong-ruby-downstream } ${sulong-gateTest38-linux} ${sulong-python-downstream-test} { name: gate-sulong-python-downstream } ${sulong-coverage-linux} { name: weekly-sulong-coverage } diff --git a/sulong/mx.sulong/suite.py b/sulong/mx.sulong/suite.py index 89bfa7ed54e5..a62297fda9fd 100644 --- a/sulong/mx.sulong/suite.py +++ b/sulong/mx.sulong/suite.py @@ -1,5 +1,5 @@ suite = { - "mxversion" : "5.208.0", + "mxversion" : "5.210.2", "name" : "sulong", "versionConflictResolution" : "latest", @@ -71,6 +71,8 @@ }, "workingSets" : "Truffle, LLVM", "license" : "BSD-new", + "testProject" : True, + "jacoco" : "exclude", }, "com.oracle.truffle.llvm.test.native" : { "subDir" : "projects", @@ -98,6 +100,8 @@ "javaCompliance" : "1.8", "workingSets" : "Truffle, LLVM", "license" : "BSD-new", + "testProject" : True, + "jacoco" : "exclude", }, "com.oracle.truffle.llvm.spi" : { @@ -242,6 +246,7 @@ "license" : "BSD-new", "testProject" : True, "defaultBuild" : False, + "jacoco" : "exclude", }, "com.oracle.truffle.llvm.pipe.native" : { @@ -263,6 +268,7 @@ "license" : "BSD-new", "testProject" : True, "defaultBuild" : False, + "jacoco" : "exclude", }, "com.oracle.truffle.llvm.libraries.bitcode" : { "subDir" : "projects", diff --git a/sulong/projects/com.oracle.truffle.llvm.instruments/src/com/oracle/truffle/llvm/instruments/trace/LLVMTracerInstrument.java b/sulong/projects/com.oracle.truffle.llvm.instruments/src/com/oracle/truffle/llvm/instruments/trace/LLVMTracerInstrument.java index 71d8b094c6a9..86c0578f7bba 100644 --- a/sulong/projects/com.oracle.truffle.llvm.instruments/src/com/oracle/truffle/llvm/instruments/trace/LLVMTracerInstrument.java +++ b/sulong/projects/com.oracle.truffle.llvm.instruments/src/com/oracle/truffle/llvm/instruments/trace/LLVMTracerInstrument.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. + * Copyright (c) 2019, Oracle and/or its affiliates. * * All rights reserved. * @@ -52,7 +52,7 @@ public final class LLVMTracerInstrument extends TruffleInstrument { static final String ID = "TraceLLVM"; static final String NAME = "LLVMTracerInstrument"; - @Option(name = "", category = OptionCategory.DEBUG, help = "Enable tracing of executed instructions (defaults to \'stdout\', can be set to \'stderr\').") // + @Option(name = "", category = OptionCategory.INTERNAL, help = "Enable tracing of executed instructions (defaults to \'stdout\', can be set to \'stderr\').") // static final OptionKey TRACELLVM = new OptionKey<>(String.valueOf("stdout")); private PrintStream traceTarget; diff --git a/sulong/projects/com.oracle.truffle.llvm.nodes/src/com/oracle/truffle/llvm/nodes/intrinsics/sulong/LLVMPrintStackTrace.java b/sulong/projects/com.oracle.truffle.llvm.nodes/src/com/oracle/truffle/llvm/nodes/intrinsics/sulong/LLVMPrintStackTrace.java index f1eb18246fd0..3fde15b8cf62 100644 --- a/sulong/projects/com.oracle.truffle.llvm.nodes/src/com/oracle/truffle/llvm/nodes/intrinsics/sulong/LLVMPrintStackTrace.java +++ b/sulong/projects/com.oracle.truffle.llvm.nodes/src/com/oracle/truffle/llvm/nodes/intrinsics/sulong/LLVMPrintStackTrace.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2018, Oracle and/or its affiliates. + * Copyright (c) 2017, 2019, Oracle and/or its affiliates. * * All rights reserved. * @@ -34,6 +34,7 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.CompilerAsserts; import com.oracle.truffle.api.TruffleException; +import com.oracle.truffle.api.TruffleStackTrace; import com.oracle.truffle.api.TruffleStackTraceElement; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.nodes.Node; @@ -68,8 +69,7 @@ public static SulongStackTrace getStackTrace(LLVMNode node) { private static SulongStackTrace getStackTrace(LLVMNode node, String message, boolean filterCurrentLocation) { Throwable t = new CThrowable(node, message); - TruffleStackTraceElement.fillIn(t); - List ctrace = TruffleStackTraceElement.getStackTrace(t); + List ctrace = TruffleStackTrace.getStacktrace(t); SulongStackTrace trace = new SulongStackTrace(message); for (int i = 0; i < ctrace.size(); i++) { diff --git a/sulong/projects/com.oracle.truffle.llvm.nodes/src/com/oracle/truffle/llvm/nodes/memory/rmw/LLVMI1RMWNode.java b/sulong/projects/com.oracle.truffle.llvm.nodes/src/com/oracle/truffle/llvm/nodes/memory/rmw/LLVMI1RMWNode.java index b2638b4e32d5..467eab2bbb1c 100644 --- a/sulong/projects/com.oracle.truffle.llvm.nodes/src/com/oracle/truffle/llvm/nodes/memory/rmw/LLVMI1RMWNode.java +++ b/sulong/projects/com.oracle.truffle.llvm.nodes/src/com/oracle/truffle/llvm/nodes/memory/rmw/LLVMI1RMWNode.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2018, Oracle and/or its affiliates. + * Copyright (c) 2017, 2019, Oracle and/or its affiliates. * * All rights reserved. * diff --git a/sulong/projects/com.oracle.truffle.llvm.runtime/src/com/oracle/truffle/llvm/runtime/ContextExtension.java b/sulong/projects/com.oracle.truffle.llvm.runtime/src/com/oracle/truffle/llvm/runtime/ContextExtension.java index 1ac9acfa97e8..b1cd107b64de 100644 --- a/sulong/projects/com.oracle.truffle.llvm.runtime/src/com/oracle/truffle/llvm/runtime/ContextExtension.java +++ b/sulong/projects/com.oracle.truffle.llvm.runtime/src/com/oracle/truffle/llvm/runtime/ContextExtension.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2018, Oracle and/or its affiliates. + * Copyright (c) 2017, 2019, Oracle and/or its affiliates. * * All rights reserved. * @@ -38,4 +38,7 @@ public interface ContextExtension { default Class extensionClass() { return this.getClass(); } + + default void initialize() { + } } diff --git a/sulong/projects/com.oracle.truffle.llvm.runtime/src/com/oracle/truffle/llvm/runtime/LLVMContext.java b/sulong/projects/com.oracle.truffle.llvm.runtime/src/com/oracle/truffle/llvm/runtime/LLVMContext.java index ff7c7cd380a9..99d077575aa9 100644 --- a/sulong/projects/com.oracle.truffle.llvm.runtime/src/com/oracle/truffle/llvm/runtime/LLVMContext.java +++ b/sulong/projects/com.oracle.truffle.llvm.runtime/src/com/oracle/truffle/llvm/runtime/LLVMContext.java @@ -96,7 +96,7 @@ public final class LLVMContext { private DataLayout dataLayout; private final List runningThreads = new ArrayList<>(); - private final LLVMThreadingStack threadingStack; + @CompilationFinal private LLVMThreadingStack threadingStack; private final Object[] mainArguments; private final Map environment; private final LinkedList caughtExceptionStack = new LinkedList<>(); @@ -175,7 +175,6 @@ public LLVMContext(LLVMLanguage language, Env env, Configuration activeConfigura this.dataLayout = new DataLayout(); this.destructorFunctions = new ArrayList<>(); this.nativeCallStatistics = SulongEngineOption.isTrue(env.getOptions().get(SulongEngineOption.NATIVE_CALL_STATS)) ? new HashMap<>() : null; - this.threadingStack = new LLVMThreadingStack(Thread.currentThread(), env.getOptions().get(SulongEngineOption.STACK_SIZE_KB)); this.sigDfl = LLVMNativePointer.create(0); this.sigIgn = LLVMNativePointer.create(1); this.sigErr = LLVMNativePointer.create(-1); @@ -230,6 +229,18 @@ public void execute(VirtualFrame frame) { } } + public void initialize() { + assert this.threadingStack == null; + this.threadingStack = new LLVMThreadingStack(Thread.currentThread(), env.getOptions().get(SulongEngineOption.STACK_SIZE_KB)); + for (ContextExtension ext : contextExtensions) { + ext.initialize(); + } + } + + public boolean isInitialized() { + return threadingStack != null; + } + public LLVMStatementNode createInitializeContextNode(FrameDescriptor rootFrame) { // we can't do the initialization in the LLVMContext constructor nor in // Sulong.createContext() because Truffle is not properly initialized there. So, we need to @@ -305,29 +316,31 @@ public void dispose(LLVMMemory memory) { } } - threadingStack.freeMainStack(memory); + if (isInitialized()) { + threadingStack.freeMainStack(memory); - // free the space allocated for non-pointer globals - Truffle.getRuntime().createCallTarget(new RootNode(language) { + // free the space allocated for non-pointer globals + Truffle.getRuntime().createCallTarget(new RootNode(language) { - @Child LLVMMemoryOpNode freeRo = nodeFactory.createFreeGlobalsBlock(true); - @Child LLVMMemoryOpNode freeRw = nodeFactory.createFreeGlobalsBlock(false); + @Child LLVMMemoryOpNode freeRo = nodeFactory.createFreeGlobalsBlock(true); + @Child LLVMMemoryOpNode freeRw = nodeFactory.createFreeGlobalsBlock(false); - @Override - public Object execute(VirtualFrame frame) { - for (LLVMPointer store : globalsReadOnlyStore) { - if (store != null) { - freeRo.execute(store); + @Override + public Object execute(VirtualFrame frame) { + for (LLVMPointer store : globalsReadOnlyStore) { + if (store != null) { + freeRo.execute(store); + } } - } - for (LLVMPointer store : globalsNonPointerStore) { - if (store != null) { - freeRw.execute(store); + for (LLVMPointer store : globalsNonPointerStore) { + if (store != null) { + freeRw.execute(store); + } } + return null; } - return null; - } - }).call(); + }).call(); + } // free the space which might have been when putting pointer-type globals into native memory for (LLVMPointer pointer : globalsReverseMap.keySet()) { @@ -600,6 +613,7 @@ public LinkedList getCaughtExceptionStack() { } public LLVMThreadingStack getThreadingStack() { + assert threadingStack != null; return threadingStack; } @@ -778,7 +792,11 @@ public int hashCode() { } private static String extractName(Path path) { - String nameWithExt = path.getFileName().toString(); + Path filename = path.getFileName(); + if (filename == null) { + throw new IllegalArgumentException("Path " + path + " is empty"); + } + String nameWithExt = filename.toString(); int lengthWithoutExt = nameWithExt.lastIndexOf("."); if (lengthWithoutExt > 0) { return nameWithExt.substring(0, lengthWithoutExt); diff --git a/sulong/projects/com.oracle.truffle.llvm.runtime/src/com/oracle/truffle/llvm/runtime/NFIContextExtension.java b/sulong/projects/com.oracle.truffle.llvm.runtime/src/com/oracle/truffle/llvm/runtime/NFIContextExtension.java index ef5bf66cd7e2..360cd418580d 100644 --- a/sulong/projects/com.oracle.truffle.llvm.runtime/src/com/oracle/truffle/llvm/runtime/NFIContextExtension.java +++ b/sulong/projects/com.oracle.truffle.llvm.runtime/src/com/oracle/truffle/llvm/runtime/NFIContextExtension.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, 2018, Oracle and/or its affiliates. + * Copyright (c) 2017, 2019, Oracle and/or its affiliates. * * All rights reserved. * @@ -29,8 +29,10 @@ */ package com.oracle.truffle.llvm.runtime; +import java.nio.file.Path; import java.util.List; +import com.oracle.truffle.api.CompilerDirectives; import org.graalvm.collections.EconomicMap; import org.graalvm.collections.MapCursor; @@ -58,7 +60,7 @@ import com.oracle.truffle.llvm.runtime.types.VoidType; public final class NFIContextExtension implements ContextExtension { - private final TruffleObject defaultLibraryHandle; + @CompilerDirectives.CompilationFinal private TruffleObject defaultLibraryHandle; private final ExternalLibrary defaultLibrary; // we use an EconomicMap because iteration order must match the insertion order private final EconomicMap libraryHandles = EconomicMap.create(); @@ -67,11 +69,20 @@ public final class NFIContextExtension implements ContextExtension { public NFIContextExtension(Env env) { this.env = env; - this.defaultLibraryHandle = loadDefaultLibrary(); this.defaultLibrary = ExternalLibrary.external("NativeDefault", true); this.nativeFunctions = new LLVMNativeFunctions(this); } + @Override + public void initialize() { + assert !isInitialized(); + this.defaultLibraryHandle = loadDefaultLibrary(); + } + + public boolean isInitialized() { + return defaultLibraryHandle != null; + } + public static class UnsupportedNativeTypeException extends Exception { private static final long serialVersionUID = 1L; @@ -158,7 +169,11 @@ public static String getNativeLibrarySuffix() { } private boolean handleSpecialLibraries(ExternalLibrary lib) { - String fileName = lib.getPath().getFileName().toString().trim(); + Path fileNamePath = lib.getPath().getFileName(); + if (fileNamePath == null) { + throw new IllegalArgumentException("Filename path of " + lib.getPath() + " is null"); + } + String fileName = fileNamePath.toString().trim(); if (fileName.startsWith("libc.")) { // nothing to do, since libsulong.so already links against libc.so return true; @@ -298,6 +313,7 @@ public NativeLookupResult getNativeFunctionOrNull(LLVMContext context, String na } TruffleObject symbol = getNativeFunctionOrNull(defaultLibraryHandle, name); if (symbol != null) { + assert isInitialized(); return new NativeLookupResult(defaultLibrary, symbol); } return null; @@ -317,6 +333,7 @@ private NativeLookupResult getNativeDataObjectOrNull(LLVMContext context, String } TruffleObject symbol = getNativeDataObjectOrNull(defaultLibraryHandle, realName); if (symbol != null) { + assert isInitialized(); return new NativeLookupResult(defaultLibrary, symbol); } return null; diff --git a/sulong/projects/com.oracle.truffle.llvm.runtime/src/com/oracle/truffle/llvm/runtime/memory/LLVMMemory.java b/sulong/projects/com.oracle.truffle.llvm.runtime/src/com/oracle/truffle/llvm/runtime/memory/LLVMMemory.java index 640f487010ff..419a146ca1b1 100644 --- a/sulong/projects/com.oracle.truffle.llvm.runtime/src/com/oracle/truffle/llvm/runtime/memory/LLVMMemory.java +++ b/sulong/projects/com.oracle.truffle.llvm.runtime/src/com/oracle/truffle/llvm/runtime/memory/LLVMMemory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. * * All rights reserved. * @@ -29,7 +29,6 @@ */ package com.oracle.truffle.llvm.runtime.memory; -import java.util.function.BinaryOperator; import java.util.function.IntBinaryOperator; import java.util.function.LongBinaryOperator; @@ -208,11 +207,29 @@ public final void putByteArray(LLVMNativePointer addr, byte[] bytes) { public abstract int getAndOpI32(LLVMNativePointer address, int value, IntBinaryOperator f); - public abstract short getAndOpI16(LLVMNativePointer address, short value, BinaryOperator f); + @FunctionalInterface + public interface ShortBinaryOperator { - public abstract byte getAndOpI8(LLVMNativePointer address, byte value, BinaryOperator f); + short apply(short a, short b); + } + + public abstract short getAndOpI16(LLVMNativePointer address, short value, ShortBinaryOperator f); + + @FunctionalInterface + public interface ByteBinaryOperator { + + byte apply(byte a, byte b); + } + + public abstract byte getAndOpI8(LLVMNativePointer address, byte value, ByteBinaryOperator f); + + @FunctionalInterface + public interface BooleanBinaryOperator { + + boolean apply(boolean a, boolean b); + } - public abstract boolean getAndOpI1(LLVMNativePointer address, boolean value, BinaryOperator f); + public abstract boolean getAndOpI1(LLVMNativePointer address, boolean value, BooleanBinaryOperator f); public abstract void fullFence(); diff --git a/sulong/projects/com.oracle.truffle.llvm.runtime/src/com/oracle/truffle/llvm/runtime/memory/LLVMNativeMemory.java b/sulong/projects/com.oracle.truffle.llvm.runtime/src/com/oracle/truffle/llvm/runtime/memory/LLVMNativeMemory.java index 41bd3ddb87e7..5e351f10a433 100644 --- a/sulong/projects/com.oracle.truffle.llvm.runtime/src/com/oracle/truffle/llvm/runtime/memory/LLVMNativeMemory.java +++ b/sulong/projects/com.oracle.truffle.llvm.runtime/src/com/oracle/truffle/llvm/runtime/memory/LLVMNativeMemory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016, 2018, Oracle and/or its affiliates. + * Copyright (c) 2016, 2019, Oracle and/or its affiliates. * * All rights reserved. * @@ -30,7 +30,6 @@ package com.oracle.truffle.llvm.runtime.memory; import java.lang.reflect.Field; -import java.util.function.BinaryOperator; import java.util.function.IntBinaryOperator; import java.util.function.LongBinaryOperator; @@ -491,7 +490,7 @@ public int getAndOpI32(LLVMNativePointer address, int value, IntBinaryOperator f } @Override - public short getAndOpI16(LLVMNativePointer address, short value, BinaryOperator f) { + public short getAndOpI16(LLVMNativePointer address, short value, ShortBinaryOperator f) { short old; short nevv; do { @@ -502,7 +501,7 @@ public short getAndOpI16(LLVMNativePointer address, short value, BinaryOperator< } @Override - public byte getAndOpI8(LLVMNativePointer address, byte value, BinaryOperator f) { + public byte getAndOpI8(LLVMNativePointer address, byte value, ByteBinaryOperator f) { byte old; byte nevv; do { @@ -513,7 +512,7 @@ public byte getAndOpI8(LLVMNativePointer address, byte value, BinaryOperator f) { + public boolean getAndOpI1(LLVMNativePointer address, boolean value, BooleanBinaryOperator f) { byte old; boolean nevv; do { diff --git a/sulong/projects/com.oracle.truffle.llvm.runtime/src/com/oracle/truffle/llvm/runtime/options/SulongEngineOption.java b/sulong/projects/com.oracle.truffle.llvm.runtime/src/com/oracle/truffle/llvm/runtime/options/SulongEngineOption.java index d460c1b6bdbb..1a3f00839502 100644 --- a/sulong/projects/com.oracle.truffle.llvm.runtime/src/com/oracle/truffle/llvm/runtime/options/SulongEngineOption.java +++ b/sulong/projects/com.oracle.truffle.llvm.runtime/src/com/oracle/truffle/llvm/runtime/options/SulongEngineOption.java @@ -32,100 +32,82 @@ import java.io.PrintStream; import java.util.ArrayList; import java.util.Arrays; +import java.util.Iterator; import java.util.List; import org.graalvm.options.OptionCategory; import org.graalvm.options.OptionDescriptor; +import org.graalvm.options.OptionDescriptors; import org.graalvm.options.OptionKey; import com.oracle.truffle.api.CompilerAsserts; +import com.oracle.truffle.api.Option; import com.oracle.truffle.api.TruffleLanguage; public final class SulongEngineOption { public static final String OPTION_ARRAY_SEPARATOR = ":"; - public static final OptionKey STACK_SIZE_KB = new OptionKey<>(81920); - public static final String STACK_SIZE_KB_NAME = "llvm.stackSizeKB"; - public static final String STACK_SIZE_KB_INFO = "The stack size in KB."; + @Option(name = "llvm.stackSizeKB", category = OptionCategory.USER, help = "The stack size in KB.") public static final OptionKey STACK_SIZE_KB = new OptionKey<>(81920); - public static final OptionKey LIBRARY_PATH = new OptionKey<>(""); public static final String LIBRARY_PATH_NAME = "llvm.libraryPath"; - public static final String LIBRARY_PATH_INFO = "A list of paths where Sulong will search for relative libraries. Paths are delimited by " + OPTION_ARRAY_SEPARATOR + " ."; + @Option(name = LIBRARY_PATH_NAME, category = OptionCategory.USER, help = "A list of paths where Sulong will search for relative libraries. Paths are delimited by " + OPTION_ARRAY_SEPARATOR + + " .") public static final OptionKey LIBRARY_PATH = new OptionKey<>(""); + @Option(name = "llvm.sourcePath", category = OptionCategory.USER, help = "This option is deprecated. Use --inspect.SourcePath instead.", deprecated = true) // public static final OptionKey SOURCE_PATH = new OptionKey<>(""); - public static final String SOURCE_PATH_NAME = "llvm.sourcePath"; - public static final String SOURCE_PATH_INFO = "This option is deprecated. Use --inspect.SourcePath instead."; - - public static final OptionKey LIBRARIES = new OptionKey<>(""); - public static final String LIBRARIES_NAME = "llvm.libraries"; - public static final String LIBRARIES_INFO = "List of libraries (precompiled libraires *.dylib/*.so as well as bitcode libraries *.bc). Files with a relative path will be looked up relative to llvm.libraryPath. Libraries are delimited by " + - OPTION_ARRAY_SEPARATOR + " ."; + @Option(name = "llvm.loadC++Libraries", category = OptionCategory.EXPERT, help = "Specifies whether the standard C++ libraries (libc++ and libc++abi) should be loaded. Enabled by default.") // public static final OptionKey LOAD_CXX_LIBRARIES = new OptionKey<>(true); - public static final String LOAD_CXX_LIBRARIES_NAME = "llvm.loadC++Libraries"; - public static final String LOAD_CXX_LIBRARIES_INFO = "Specifies whether the standard C++ libraries (libc++ and libc++abi) should be loaded. Enabled by default."; + @Option(name = "llvm.enableExternalNativeAccess", category = OptionCategory.USER, help = "Enable Sulongs native interface.") // public static final OptionKey ENABLE_NFI = new OptionKey<>(true); - public static final String ENABLE_NFI_NAME = "llvm.enableExternalNativeAccess"; - public static final String ENABLE_NFI_INFO = "Enable Sulongs native interface."; + @Option(name = "llvm.debugSysCalls", category = OptionCategory.USER, help = "Turns syscall debugging on/off. Can be \'true\', \'false\', \'stdout\', \'stderr\' or a filepath.") // public static final OptionKey DEBUG_SYSCALLS = new OptionKey<>(String.valueOf(false)); - public static final String DEBUG_SYSCALLS_NAME = "llvm.debugSysCalls"; - public static final String DEBUG_SYSCALLS_INFO = "Turns syscall debugging on/off. Can be \'true\', \'false\', \'stdout\', \'stderr\' or a filepath."; + @Option(name = "llvm.printNativeCallStats", category = OptionCategory.USER, help = "Outputs stats about native call site frequencies. Can be \'true\', \'false\', \'stdout\', \'stderr\' or a filepath.") // public static final OptionKey NATIVE_CALL_STATS = new OptionKey<>(String.valueOf(false)); - public static final String NATIVE_CALL_STATS_NAME = "llvm.printNativeCallStats"; - public static final String NATIVE_CALL_STATS_INFO = "Outputs stats about native call site frequencies. Can be \'true\', \'false\', \'stdout\', \'stderr\' or a filepath."; + @Option(name = "llvm.printLifetimeAnalysisStats", category = OptionCategory.USER, help = "Prints the results of the lifetime analysis. Can be \'true\', \'false\', \'stdout\', \'stderr\' or a filepath.") // public static final OptionKey PRINT_LIFE_TIME_ANALYSIS_STATS = new OptionKey<>(String.valueOf(false)); - public static final String PRINT_LIFE_TIME_ANALYSIS_STATS_NAME = "llvm.printLifetimeAnalysisStats"; - public static final String PRINT_LIFE_TIME_ANALYSIS_STATS_INFO = "Prints the results of the lifetime analysis. Can be \'true\', \'false\', \'stdout\', \'stderr\' or a filepath."; + @Option(name = "llvm.parseOnly", category = OptionCategory.EXPERT, help = "Only parses a bc file; execution is not possible.") // public static final OptionKey PARSE_ONLY = new OptionKey<>(false); - public static final String PARSE_ONLY_NAME = "llvm.parseOnly"; - public static final String PARSE_ONLY_INFO = "Only parses a bc file; execution is not possible."; + @Option(name = "llvm.enableLVI", category = OptionCategory.EXPERT, help = "Enable source-level inspection of local variables.") // public static final OptionKey ENABLE_LVI = new OptionKey<>(false); - public static final String ENABLE_LVI_NAME = "llvm.enableLVI"; - public static final String ENABLE_LVI_INFO = "Enable source-level inspection of local variables."; + @Option(name = "llvm.lazyParsing", category = OptionCategory.EXPERT, help = "Enable lazy parsing of LLVM bitcode files.") // public static final OptionKey LAZY_PARSING = new OptionKey<>(true); - public static final String LAZY_PARSING_NAME = "llvm.lazyParsing"; - public static final String LAZY_PARSING_INFO = "Enable lazy parsing of LLVM bitcode files."; + @Option(name = "llvm.llDebug", category = OptionCategory.EXPERT, help = "Enable IR-level debugging of LLVM bitcode files.") // public static final OptionKey LL_DEBUG = new OptionKey<>(false); - public static final String LL_DEBUG_NAME = "llvm.llDebug"; - public static final String LL_DEBUG_INFO = "Enable IR-level debugging of LLVM bitcode files."; + @Option(name = "llvm.llDebug.sources", category = OptionCategory.EXPERT, help = "Provide the locations of *.ll files for debugging. The expected format is ={:=}.") // public static final OptionKey LL_DEBUG_SOURCES = new OptionKey<>(""); - public static final String LL_DEBUG_SOURCES_NAME = "llvm.llDebug.sources"; - public static final String LL_DEBUG_SOURCES_INFO = "Provide the locations of *.ll files for debugging. The expected format is ={:=}."; + @Option(name = "llvm.printStackTraceOnAbort", category = OptionCategory.INTERNAL, help = "Prints a C stack trace when abort() is called.") // public static final OptionKey STACKTRACE_ON_ABORT = new OptionKey<>(false); - public static final String STACKTRACE_ON_ABORT_NAME = "llvm.printStackTraceOnAbort"; - public static final String STACKTRACE_ON_ABORT_INFO = "Prints a C stack trace when abort() is called."; + + public static final String LIBRARIES_NAME = "llvm.libraries"; + public static final String LIBRARIES_INFO = "List of libraries (precompiled libraires *.dylib/*.so as well as bitcode libraries *.bc). Files with a relative path will be looked up relative to llvm.libraryPath. Libraries are delimited by " + + OPTION_ARRAY_SEPARATOR + " ."; + @Option(name = LIBRARIES_NAME, category = OptionCategory.USER, help = LIBRARIES_INFO) public static final OptionKey LIBRARIES = new OptionKey<>(""); public static List describeOptions() { ArrayList options = new ArrayList<>(); - options.add(OptionDescriptor.newBuilder(STACK_SIZE_KB, STACK_SIZE_KB_NAME).help(STACK_SIZE_KB_INFO).category(OptionCategory.USER).build()); - options.add(OptionDescriptor.newBuilder(LIBRARIES, LIBRARIES_NAME).help(LIBRARIES_INFO).category(OptionCategory.USER).build()); - options.add(OptionDescriptor.newBuilder(LIBRARY_PATH, LIBRARY_PATH_NAME).help(LIBRARY_PATH_INFO).category(OptionCategory.USER).build()); - options.add(OptionDescriptor.newBuilder(SOURCE_PATH, SOURCE_PATH_NAME).help(SOURCE_PATH_INFO).category(OptionCategory.USER).deprecated(true).build()); - options.add(OptionDescriptor.newBuilder(LOAD_CXX_LIBRARIES, LOAD_CXX_LIBRARIES_NAME).help(LOAD_CXX_LIBRARIES_INFO).category(OptionCategory.EXPERT).build()); - options.add(OptionDescriptor.newBuilder(ENABLE_NFI, ENABLE_NFI_NAME).help(ENABLE_NFI_INFO).category(OptionCategory.USER).build()); - options.add(OptionDescriptor.newBuilder(DEBUG_SYSCALLS, DEBUG_SYSCALLS_NAME).help(DEBUG_SYSCALLS_INFO).category(OptionCategory.USER).build()); - options.add(OptionDescriptor.newBuilder(NATIVE_CALL_STATS, NATIVE_CALL_STATS_NAME).help(NATIVE_CALL_STATS_INFO).category(OptionCategory.USER).build()); - options.add(OptionDescriptor.newBuilder(PRINT_LIFE_TIME_ANALYSIS_STATS, PRINT_LIFE_TIME_ANALYSIS_STATS_NAME).help(PRINT_LIFE_TIME_ANALYSIS_STATS_INFO).category(OptionCategory.USER).build()); - options.add(OptionDescriptor.newBuilder(PARSE_ONLY, PARSE_ONLY_NAME).help(PARSE_ONLY_INFO).category(OptionCategory.EXPERT).build()); - options.add(OptionDescriptor.newBuilder(ENABLE_LVI, ENABLE_LVI_NAME).help(ENABLE_LVI_INFO).category(OptionCategory.DEBUG).build()); - options.add(OptionDescriptor.newBuilder(LAZY_PARSING, LAZY_PARSING_NAME).help(LAZY_PARSING_INFO).category(OptionCategory.EXPERT).build()); - options.add(OptionDescriptor.newBuilder(LL_DEBUG, LL_DEBUG_NAME).help(LL_DEBUG_INFO).category(OptionCategory.DEBUG).build()); - options.add(OptionDescriptor.newBuilder(LL_DEBUG_SOURCES, LL_DEBUG_SOURCES_NAME).help(LL_DEBUG_SOURCES_INFO).category(OptionCategory.DEBUG).build()); - options.add(OptionDescriptor.newBuilder(STACKTRACE_ON_ABORT, STACKTRACE_ON_ABORT_NAME).help(STACKTRACE_ON_ABORT_INFO).category(OptionCategory.DEBUG).build()); + Iterator iterator = SulongEngineOption.createDescriptors().iterator(); + while (iterator.hasNext()) { + options.add(iterator.next()); + } return options; } + public static OptionDescriptors createDescriptors() { + return new SulongEngineOptionOptionDescriptors(); + } + public static PrintStream getStream(String name) { if ("stderr".equals(name)) { return System.err; diff --git a/sulong/projects/com.oracle.truffle.llvm/src/com/oracle/truffle/llvm/Sulong.java b/sulong/projects/com.oracle.truffle.llvm/src/com/oracle/truffle/llvm/Sulong.java index 116594b8aabd..2bf3d530e5c4 100644 --- a/sulong/projects/com.oracle.truffle.llvm/src/com/oracle/truffle/llvm/Sulong.java +++ b/sulong/projects/com.oracle.truffle.llvm/src/com/oracle/truffle/llvm/Sulong.java @@ -121,6 +121,11 @@ protected LLVMContext createContext(com.oracle.truffle.api.TruffleLanguage.Env e return newContext; } + @Override + protected void initializeContext(LLVMContext context) { + context.initialize(); + } + @Override protected void disposeContext(LLVMContext context) { LLVMMemory memory = getCapability(LLVMMemory.class); @@ -205,7 +210,9 @@ protected boolean isThreadAccessAllowed(Thread thread, boolean singleThreaded) { @Override protected void disposeThread(LLVMContext context, Thread thread) { super.disposeThread(context, thread); - context.getThreadingStack().freeStack(getCapability(LLVMMemory.class), thread); + if (context.isInitialized()) { + context.getThreadingStack().freeStack(getCapability(LLVMMemory.class), thread); + } } @Override diff --git a/tools/mx.tools/suite.py b/tools/mx.tools/suite.py index 7528f23da705..2ecb80d80bba 100644 --- a/tools/mx.tools/suite.py +++ b/tools/mx.tools/suite.py @@ -1,5 +1,5 @@ suite = { - "mxversion": "5.195.1", + "mxversion": "5.210.2", "name": "tools", "defaultLicense" : "GPLv2-CPE", diff --git a/tools/src/com.oracle.truffle.tools.chromeinspector/src/com/oracle/truffle/tools/chromeinspector/instrument/InspectorInstrument.java b/tools/src/com.oracle.truffle.tools.chromeinspector/src/com/oracle/truffle/tools/chromeinspector/instrument/InspectorInstrument.java index 1ca9315ccd3d..911b6f9846fa 100644 --- a/tools/src/com.oracle.truffle.tools.chromeinspector/src/com/oracle/truffle/tools/chromeinspector/instrument/InspectorInstrument.java +++ b/tools/src/com.oracle.truffle.tools.chromeinspector/src/com/oracle/truffle/tools/chromeinspector/instrument/InspectorInstrument.java @@ -157,7 +157,7 @@ private static URI createURIFromPath(String path) throws URISyntaxException { @com.oracle.truffle.api.Option(name = "", help = "Start the Chrome inspector on [[host:]port]. (default: :" + DEFAULT_PORT + ")", category = OptionCategory.USER) // static final OptionKey Inspect = new OptionKey<>(DEFAULT_ADDRESS, ADDRESS_OR_BOOLEAN); - @com.oracle.truffle.api.Option(help = "Attach to an existing endpoint instead of creating a new one. (default:false)", category = OptionCategory.DEBUG) // + @com.oracle.truffle.api.Option(help = "Attach to an existing endpoint instead of creating a new one. (default:false)", category = OptionCategory.INTERNAL) // static final OptionKey Attach = new OptionKey<>(false); @com.oracle.truffle.api.Option(help = "Suspend the execution at first executed source line. (default:true)", category = OptionCategory.USER) // @@ -175,10 +175,10 @@ private static URI createURIFromPath(String path) throws URISyntaxException { @com.oracle.truffle.api.Option(help = "Path to the chrome inspect. (default: randomly generated)", category = OptionCategory.EXPERT) // static final OptionKey Path = new OptionKey<>(""); - @com.oracle.truffle.api.Option(help = "Inspect internal sources. (default:false)", category = OptionCategory.DEBUG) // + @com.oracle.truffle.api.Option(help = "Inspect internal sources. (default:false)", category = OptionCategory.INTERNAL) // static final OptionKey Internal = new OptionKey<>(false); - @com.oracle.truffle.api.Option(help = "Inspect language initialization. (default:false)", category = OptionCategory.DEBUG) // + @com.oracle.truffle.api.Option(help = "Inspect language initialization. (default:false)", category = OptionCategory.INTERNAL) // static final OptionKey Initialization = new OptionKey<>(false); @com.oracle.truffle.api.Option(help = "Use TLS/SSL. (default:false)", category = OptionCategory.EXPERT) // diff --git a/tools/src/com.oracle.truffle.tools.chromeinspector/src/com/oracle/truffle/tools/chromeinspector/objects/Console.java b/tools/src/com.oracle.truffle.tools.chromeinspector/src/com/oracle/truffle/tools/chromeinspector/objects/Console.java index f72bf6ccda3b..31883a086062 100644 --- a/tools/src/com.oracle.truffle.tools.chromeinspector/src/com/oracle/truffle/tools/chromeinspector/objects/Console.java +++ b/tools/src/com.oracle.truffle.tools.chromeinspector/src/com/oracle/truffle/tools/chromeinspector/objects/Console.java @@ -68,6 +68,12 @@ class Console extends AbstractInspectorObject { METHOD_CLEAR, METHOD_COUNT, METHOD_COUNT_RESET, METHOD_ASSERT, METHOD_MARK_TIMELINE, METHOD_PROFILE, METHOD_PROFILE_END, METHOD_TIMELINE, METHOD_TIMELINE_END, METHOD_TIME, METHOD_TIME_END, METHOD_TIME_STAMP}; private static final TruffleObject KEYS = new Keys(); + private static final Object UNKNOWN = new Object() { + @Override + public String toString() { + return "unknown"; + } + }; private InspectorServerConnection connection; private final Map time = new ConcurrentHashMap<>(); @@ -137,7 +143,7 @@ protected Object getFieldValueOrNull(String name) { protected Object invokeMethod(String name, Object[] arguments) { Object arg; if (arguments.length < 1) { - arg = null; + arg = UNKNOWN; } else { if (!(arguments[0] instanceof String || arguments[0] instanceof Number)) { throw UnsupportedTypeException.raise(arguments); @@ -198,7 +204,7 @@ protected Object invokeMethod(String name, Object[] arguments) { case METHOD_TIME_END: long t2 = System.nanoTime(); Long t1 = time.remove(arg); - String timer = (arg == null) ? "default" : arg.toString(); + String timer = arg.toString(); if (t1 == null) { arg = "Timer '" + timer + "' does not exist"; type = "warning"; diff --git a/tools/src/com.oracle.truffle.tools.profiler/src/com/oracle/truffle/tools/profiler/impl/CPUSamplerCLI.java b/tools/src/com.oracle.truffle.tools.profiler/src/com/oracle/truffle/tools/profiler/impl/CPUSamplerCLI.java index 3e31d16ca06a..de0f4b198b4c 100644 --- a/tools/src/com.oracle.truffle.tools.profiler/src/com/oracle/truffle/tools/profiler/impl/CPUSamplerCLI.java +++ b/tools/src/com.oracle.truffle.tools.profiler/src/com/oracle/truffle/tools/profiler/impl/CPUSamplerCLI.java @@ -109,7 +109,7 @@ public CPUSampler.Mode apply(String s) { @Option(name = "FilterLanguage", help = "Only profile languages with mime-type. (eg. +, default:no filter).", category = OptionCategory.USER) static final OptionKey FILTER_LANGUAGE = new OptionKey<>( ""); - @Option(name = "SampleInternal", help = "Capture internal elements (default:false).", category = OptionCategory.DEBUG) static final OptionKey SAMPLE_INTERNAL = new OptionKey<>(false); + @Option(name = "SampleInternal", help = "Capture internal elements (default:false).", category = OptionCategory.INTERNAL) static final OptionKey SAMPLE_INTERNAL = new OptionKey<>(false); @Option(name = "SummariseThreads", help = "Print output as a summary of all 'per thread' profiles. (default: false)", category = OptionCategory.USER) static final OptionKey SUMMARISE_THREADS = new OptionKey<>(false); diff --git a/tools/src/com.oracle.truffle.tools.profiler/src/com/oracle/truffle/tools/profiler/impl/CPUTracerCLI.java b/tools/src/com.oracle.truffle.tools.profiler/src/com/oracle/truffle/tools/profiler/impl/CPUTracerCLI.java index 656a9a34117f..1b5c644ae2ad 100644 --- a/tools/src/com.oracle.truffle.tools.profiler/src/com/oracle/truffle/tools/profiler/impl/CPUTracerCLI.java +++ b/tools/src/com.oracle.truffle.tools.profiler/src/com/oracle/truffle/tools/profiler/impl/CPUTracerCLI.java @@ -70,7 +70,7 @@ public Output apply(String s) { @Option(name = "TraceCalls", help = "Capture calls when tracing (default:false).", category = OptionCategory.USER) static final OptionKey TRACE_CALLS = new OptionKey<>(false); - @Option(name = "TraceInternal", help = "Trace internal elements (default:false).", category = OptionCategory.DEBUG) static final OptionKey TRACE_INTERNAL = new OptionKey<>(false); + @Option(name = "TraceInternal", help = "Trace internal elements (default:false).", category = OptionCategory.INTERNAL) static final OptionKey TRACE_INTERNAL = new OptionKey<>(false); @Option(name = "FilterRootName", help = "Wildcard filter for program roots. (eg. Math.*, default:*).", category = OptionCategory.USER) static final OptionKey FILTER_ROOT = new OptionKey<>( new Object[0], WILDCARD_FILTER_TYPE); diff --git a/tools/src/com.oracle.truffle.tools.profiler/src/com/oracle/truffle/tools/profiler/impl/MemoryTracerCLI.java b/tools/src/com.oracle.truffle.tools.profiler/src/com/oracle/truffle/tools/profiler/impl/MemoryTracerCLI.java index 85ea44b2d8d9..8fc9e5200a0c 100644 --- a/tools/src/com.oracle.truffle.tools.profiler/src/com/oracle/truffle/tools/profiler/impl/MemoryTracerCLI.java +++ b/tools/src/com.oracle.truffle.tools.profiler/src/com/oracle/truffle/tools/profiler/impl/MemoryTracerCLI.java @@ -93,7 +93,7 @@ public void accept(Output output) { @Option(name = "TraceCalls", help = "Capture calls when tracing (default:false).", category = OptionCategory.USER) static final OptionKey TRACE_CALLS = new OptionKey<>(false); - @Option(name = "TraceInternal", help = "Capture internal elements (default:false).", category = OptionCategory.DEBUG) static final OptionKey TRACE_INTERNAL = new OptionKey<>(false); + @Option(name = "TraceInternal", help = "Capture internal elements (default:false).", category = OptionCategory.INTERNAL) static final OptionKey TRACE_INTERNAL = new OptionKey<>(false); @Option(name = "FilterRootName", help = "Wildcard filter for program roots. (eg. Math.*, default:*).", category = OptionCategory.USER) static final OptionKey FILTER_ROOT = new OptionKey<>( new Object[0], WILDCARD_FILTER_TYPE); diff --git a/truffle/CHANGELOG.md b/truffle/CHANGELOG.md index 9fe9405ce6cd..b9d94b11b1d6 100644 --- a/truffle/CHANGELOG.md +++ b/truffle/CHANGELOG.md @@ -5,10 +5,13 @@ This changelog summarizes major changes between Truffle versions relevant to lan ## Version 1.0.0 RC13 * Added [Debugger.getSessionCount()](https://www.graalvm.org/truffle/javadoc/com/oracle/truffle/api/debug/Debugger.html#getSessionCount--) to return the number of active debugger sessions. * The [TruffleFile.getName()](https://www.graalvm.org/truffle/javadoc/com/oracle/truffle/api/TruffleFile.html#getName--) returns `null` for root directory. +* `TruffleLanguage` can [register additional services](https://www.graalvm.org/truffle/javadoc/com/oracle/truffle/api/TruffleLanguage.Env.html#registerService-java.lang.Object-). This change also deprecates the automatic registration of the language class as a service. * Enabled the [experimental monomorphization heuristic](https://github.com/oracle/graal/blob/master/truffle/docs/splitting/) as default. Old heuristic still available as legacy, but will be removed soon. * Added [TypeDescriptor.instantiable(instanceType, vararg, parameterTypes)](https://www.graalvm.org/truffle/javadoc/org/graalvm/polyglot/tck/TypeDescriptor.html#instantiable-org.graalvm.polyglot.tck.TypeDescriptor-boolean-org.graalvm.polyglot.tck.TypeDescriptor...-) into TCK to support instantiable types. * The name of an [@Option](http://www.graalvm.org/truffle/javadoc/com/oracle/truffle/api/Option.html) can now start with a lowercase letter. +* Allowed navigation from host class to host symbol (companion object for static members) via the synthetic member `"static"`. +* Moved `getStackTrace` and `fillIn` from [TruffleStackTraceElement](https://www.graalvm.org/truffle/javadoc/com/oracle/truffle/api/TruffleStackTraceElement.html) to [TruffleStackTrace](https://www.graalvm.org/truffle/javadoc/com/oracle/truffle/api/TruffleStackTrace.html). ## Version 1.0.0 RC12 * Fixed: [Env.asHostException()](https://www.graalvm.org/truffle/javadoc/com/oracle/truffle/api/TruffleLanguage.Env.html#asHostException-java.lang.Throwable-) should throw an `IllegalArgumentException` if the provided value is not a host exception. diff --git a/truffle/mx.truffle/mx_truffle.py b/truffle/mx.truffle/mx_truffle.py index 3df96e644d72..3be1f68b6bcf 100644 --- a/truffle/mx.truffle/mx_truffle.py +++ b/truffle/mx.truffle/mx_truffle.py @@ -173,6 +173,8 @@ def _unittest_config_participant(config): # This is required for the call to setAccessible in # TruffleTCK.testValueWithSource to work. vmArgs = vmArgs + ['--add-opens=org.graalvm.truffle/com.oracle.truffle.polyglot=ALL-UNNAMED', '--add-modules=ALL-MODULE-PATH'] + # Tests should fail when instruments fail + vmArgs = vmArgs + ['-Dpolyglot.engine.InstrumentExceptionsAreThrown=true'] return (vmArgs, mainClass, mainClassArgs) mx_unittest.add_config_participant(_unittest_config_participant) diff --git a/truffle/mx.truffle/suite.py b/truffle/mx.truffle/suite.py index 46bd0f93fa90..b40a95817702 100644 --- a/truffle/mx.truffle/suite.py +++ b/truffle/mx.truffle/suite.py @@ -39,7 +39,7 @@ # SOFTWARE. # suite = { - "mxversion" : "5.206.0", + "mxversion" : "5.210.2", "name" : "truffle", "version" : "1.0.0-rc13", "release" : False, @@ -198,7 +198,7 @@ "imports" : ["jdk.internal.loader"], "checkstyle" : "com.oracle.truffle.dsl.processor", "javaCompliance" : "8+", - "findbugsIgnoresGenerated" : True, + "spotbugsIgnoresGenerated" : True, "testProject" : True, "annotationProcessors" : ["mx:JMH_1_21", "TRUFFLE_DSL_PROCESSOR"], "workingSets" : "API,Truffle,Test", diff --git a/truffle/src/com.oracle.truffle.api.debug/src/com/oracle/truffle/api/debug/DebugException.java b/truffle/src/com.oracle.truffle.api.debug/src/com/oracle/truffle/api/debug/DebugException.java index c802df165cdc..dc306c4ce258 100644 --- a/truffle/src/com.oracle.truffle.api.debug/src/com/oracle/truffle/api/debug/DebugException.java +++ b/truffle/src/com.oracle.truffle.api.debug/src/com/oracle/truffle/api/debug/DebugException.java @@ -47,6 +47,7 @@ import java.util.List; import com.oracle.truffle.api.TruffleException; +import com.oracle.truffle.api.TruffleStackTrace; import com.oracle.truffle.api.TruffleStackTraceElement; import com.oracle.truffle.api.frame.FrameInstance; import com.oracle.truffle.api.nodes.LanguageInfo; @@ -169,8 +170,7 @@ public StackTraceElement[] getStackTrace() { public List getDebugStackTrace() { if (debugStackTrace == null) { if (exception != null) { - TruffleStackTraceElement.fillIn(exception); - List stackTrace = TruffleStackTraceElement.getStackTrace(exception); + List stackTrace = TruffleStackTrace.getStacktrace(exception); int n = stackTrace.size(); List debugStack = new ArrayList<>(n); for (int i = 0; i < n; i++) { diff --git a/truffle/src/com.oracle.truffle.api.instrumentation.test/src/com/oracle/truffle/api/instrumentation/test/InstrumentationTest.java b/truffle/src/com.oracle.truffle.api.instrumentation.test/src/com/oracle/truffle/api/instrumentation/test/InstrumentationTest.java index 5318760ec44f..227b03fa8296 100644 --- a/truffle/src/com.oracle.truffle.api.instrumentation.test/src/com/oracle/truffle/api/instrumentation/test/InstrumentationTest.java +++ b/truffle/src/com.oracle.truffle.api.instrumentation.test/src/com/oracle/truffle/api/instrumentation/test/InstrumentationTest.java @@ -426,12 +426,14 @@ public void testInstrumentException2() throws IOException { TestInstrumentException2.returnedExceptional = 0; TestInstrumentException2.returnedValue = 0; assureEnabled(engine.getInstruments().get("testInstrumentException2")); - run("ROOT(EXPRESSION)"); - String errorText = getErr(); - Assert.assertTrue(errorText, errorText.contains("MyLanguageException")); - - Assert.assertEquals(0, TestInstrumentException2.returnedExceptional); - Assert.assertEquals(1, TestInstrumentException2.returnedValue); + try { + run("ROOT(EXPRESSION)"); + Assert.fail("No exception was thrown."); + } catch (PolyglotException ex) { + Assert.assertTrue(ex.getMessage(), ex.getMessage().contains("MyLanguageException")); + } + Assert.assertEquals(1, TestInstrumentException2.returnedExceptional); + Assert.assertEquals(0, TestInstrumentException2.returnedValue); } @Registration(name = "", version = "", id = "testInstrumentException2", services = Object.class) @@ -474,8 +476,12 @@ public void testInstrumentException3() throws IOException { TestInstrumentException3.returnedExceptional = 0; TestInstrumentException3.onEnter = 0; assureEnabled(engine.getInstruments().get("testInstrumentException3")); - run("ROOT(EXPRESSION)"); - Assert.assertTrue(getErr().contains("MyLanguageException")); + try { + run("ROOT(EXPRESSION)"); + Assert.fail("No exception was thrown."); + } catch (PolyglotException ex) { + Assert.assertTrue(ex.getMessage(), ex.getMessage().contains("MyLanguageException")); + } Assert.assertEquals(0, TestInstrumentException3.returnedExceptional); Assert.assertEquals(1, TestInstrumentException3.onEnter); } @@ -982,7 +988,8 @@ public void onLoad(LoadSourceSectionEvent evt) { } InstrumentableNode node = (InstrumentableNode) evt.getNode(); SourceSection ss = evt.getNode().getSourceSection(); - if (ss == null) { + if (ss == null || ss.getCharacters().toString().startsWith("ROOT(DEFINE")) { + // No SourceSection, or the outer function return; } Class tag = tester.getTag(); diff --git a/truffle/src/com.oracle.truffle.api.instrumentation.test/src/com/oracle/truffle/api/instrumentation/test/SourceListenerTest.java b/truffle/src/com.oracle.truffle.api.instrumentation.test/src/com/oracle/truffle/api/instrumentation/test/SourceListenerTest.java index 0d59b043aa70..d9593e0f36ba 100644 --- a/truffle/src/com.oracle.truffle.api.instrumentation.test/src/com/oracle/truffle/api/instrumentation/test/SourceListenerTest.java +++ b/truffle/src/com.oracle.truffle.api.instrumentation.test/src/com/oracle/truffle/api/instrumentation/test/SourceListenerTest.java @@ -47,6 +47,7 @@ import java.util.List; import org.graalvm.polyglot.Instrument; +import org.graalvm.polyglot.PolyglotException; import org.graalvm.polyglot.Source; import org.junit.Assert; import org.junit.Assume; @@ -253,8 +254,12 @@ protected void onCreate(Env env) { @Test public void testLoadSourceException() throws IOException { assureEnabled(engine.getInstruments().get("testLoadSourceException")); - run(""); - Assert.assertTrue(getErr().contains("TestLoadSourceExceptionClass")); + try { + run(""); + Assert.fail("No exception was thrown."); + } catch (PolyglotException ex) { + Assert.assertTrue(ex.getMessage(), ex.getMessage().contains("TestLoadSourceExceptionClass")); + } } private static class TestLoadSourceExceptionClass extends RuntimeException { diff --git a/truffle/src/com.oracle.truffle.api.instrumentation.test/src/com/oracle/truffle/api/instrumentation/test/SourceSectionListenerTest.java b/truffle/src/com.oracle.truffle.api.instrumentation.test/src/com/oracle/truffle/api/instrumentation/test/SourceSectionListenerTest.java index c642323b0195..4b530d4c9193 100644 --- a/truffle/src/com.oracle.truffle.api.instrumentation.test/src/com/oracle/truffle/api/instrumentation/test/SourceSectionListenerTest.java +++ b/truffle/src/com.oracle.truffle.api.instrumentation.test/src/com/oracle/truffle/api/instrumentation/test/SourceSectionListenerTest.java @@ -56,6 +56,7 @@ import com.oracle.truffle.api.instrumentation.TruffleInstrument.Registration; import org.graalvm.polyglot.Instrument; +import org.graalvm.polyglot.PolyglotException; import org.graalvm.polyglot.Source; import org.graalvm.polyglot.SourceSection; @@ -257,8 +258,12 @@ public void onLoad(LoadSourceSectionEvent event) { @Test public void testLoadSourceSectionException() throws IOException { assureEnabled(engine.getInstruments().get("testLoadSourceSectionException")); - run("STATEMENT"); - Assert.assertTrue(getErr().contains("TestLoadSourceSectionExceptionClass")); + try { + run("STATEMENT"); + Assert.fail("No exception was thrown."); + } catch (PolyglotException ex) { + Assert.assertTrue(ex.getMessage(), ex.getMessage().contains("TestLoadSourceSectionExceptionClass")); + } } private static class TestLoadSourceSectionExceptionClass extends RuntimeException { diff --git a/truffle/src/com.oracle.truffle.api.instrumentation.test/src/com/oracle/truffle/api/instrumentation/test/UnwindReenterReturnTest.java b/truffle/src/com.oracle.truffle.api.instrumentation.test/src/com/oracle/truffle/api/instrumentation/test/UnwindReenterReturnTest.java index fe761c2002f2..d302da7d7cc5 100644 --- a/truffle/src/com.oracle.truffle.api.instrumentation.test/src/com/oracle/truffle/api/instrumentation/test/UnwindReenterReturnTest.java +++ b/truffle/src/com.oracle.truffle.api.instrumentation.test/src/com/oracle/truffle/api/instrumentation/test/UnwindReenterReturnTest.java @@ -1038,6 +1038,9 @@ void submitSequential() { bindingExec = env.getInstrumenter().attachExecutionEventListener( SourceSectionFilter.newBuilder().tagIs(StandardTags.StatementTag.class, StandardTags.CallTag.class).build(), new ExecutionEventListener() { + + boolean unwound = false; + @Override public void onEnter(EventContext context, VirtualFrame frame) { onEnter(context); @@ -1063,7 +1066,10 @@ public void onReturnValue(EventContext context, VirtualFrame frame, Object resul @TruffleBoundary private void onReturnValue() { - lock.unlock(); + if (unwound && lock.isHeldByCurrentThread()) { + unwound = false; + lock.unlock(); + } } @Override @@ -1072,6 +1078,7 @@ public void onReturnExceptional(EventContext context, VirtualFrame frame, Throwa @Override public Object onUnwind(EventContext context, VirtualFrame frame, Object info) { + unwound = true; return 1; } }); diff --git a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/RootNodeTest.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/RootNodeTest.java index af2deaf4280c..1b50b8c43b52 100644 --- a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/RootNodeTest.java +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/RootNodeTest.java @@ -50,6 +50,7 @@ import com.oracle.truffle.api.Truffle; import com.oracle.truffle.api.TruffleException; import com.oracle.truffle.api.TruffleRuntime; +import com.oracle.truffle.api.TruffleStackTrace; import com.oracle.truffle.api.TruffleStackTraceElement; import com.oracle.truffle.api.frame.MaterializedFrame; import com.oracle.truffle.api.frame.VirtualFrame; @@ -116,7 +117,7 @@ public void testNotCapturingFrames() { try { Truffle.getRuntime().createCallTarget(rootNode).call(marker); } catch (TestException e) { - List stackTrace = TruffleStackTraceElement.getStackTrace(e); + List stackTrace = TruffleStackTrace.getStacktrace(e); Assert.assertEquals(1, stackTrace.size()); Assert.assertNull(stackTrace.get(0).getLocation()); Assert.assertEquals(rootNode.getCallTarget(), stackTrace.get(0).getTarget()); @@ -132,7 +133,7 @@ public void testCapturingFrames() { Truffle.getRuntime().createCallTarget(rootNode).call(marker); } catch (TestException e) { MaterializedFrame frame = e.frame; - List stackTrace = TruffleStackTraceElement.getStackTrace(e); + List stackTrace = TruffleStackTrace.getStacktrace(e); Assert.assertEquals(1, stackTrace.size()); Assert.assertNull(stackTrace.get(0).getLocation()); Assert.assertEquals(rootNode.getCallTarget(), stackTrace.get(0).getTarget()); diff --git a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/HostInteropTest.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/HostInteropTest.java index d077869b61e6..3185545878f0 100644 --- a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/HostInteropTest.java +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/host/HostInteropTest.java @@ -154,6 +154,21 @@ public void conversionToClass2() { assertEquals("Both class objects are the same", expected, computed); } + @Test + public void classToStatic() { + TruffleObject expected = asTruffleHostSymbol(Class.class); + TruffleObject computed = toJavaSymbol(asTruffleObject(Class.class)); + assertEquals("Both host symbol objects are the same", expected, computed); + } + + private static TruffleObject toJavaSymbol(TruffleObject obj) { + try { + return (TruffleObject) ForeignAccess.sendRead(Message.READ.createNode(), obj, "static"); + } catch (UnknownIdentifierException | UnsupportedMessageException e) { + throw new AssertionError(e); + } + } + @Test public void nullAsJavaObject() { TruffleObject nullObject = asTruffleObject(null); diff --git a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/option/OptionProcessorTest.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/option/OptionProcessorTest.java index 7baa0f29e347..5ceabc0e4c80 100644 --- a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/option/OptionProcessorTest.java +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/option/OptionProcessorTest.java @@ -93,7 +93,7 @@ public void testTestLang() { assertNotNull(descriptor); assertEquals("Help for lowerCaseOption", descriptor.getHelp()); assertTrue(descriptor.isDeprecated()); - assertSame(OptionCategory.DEBUG, descriptor.getCategory()); + assertSame(OptionCategory.INTERNAL, descriptor.getCategory()); assertSame(OptionTestLang1.LOWER_CASE_OPTION, descriptor.getKey()); Iterator iterator = descriptors.iterator(); @@ -224,7 +224,7 @@ public static class OptionTestLang1 extends TruffleLanguage { static final OptionKey StringOption2 = new OptionKey<>("defaultValue"); // The variable name differs from the option name on purpose, to test they can be different - @Option(help = "Help for lowerCaseOption", name = "lowerCaseOption", deprecated = true, category = OptionCategory.DEBUG) // + @Option(help = "Help for lowerCaseOption", name = "lowerCaseOption", deprecated = true, category = OptionCategory.INTERNAL) // static final OptionKey LOWER_CASE_OPTION = new OptionKey<>("defaultValue"); @Override diff --git a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ContextParallelCloseTest.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ContextParallelCloseTest.java new file mode 100644 index 000000000000..490c9e43d9eb --- /dev/null +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ContextParallelCloseTest.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.oracle.truffle.api.test.polyglot; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +import org.graalvm.polyglot.Context; +import org.graalvm.polyglot.Engine; +import org.junit.Test; + +import com.oracle.truffle.api.test.polyglot.PolyglotCachingTest.ReuseLanguage; + +public class ContextParallelCloseTest { + + @Test + public void testCloseDeadlock() throws InterruptedException, ExecutionException { + final int threads = 10; + ExecutorService service = Executors.newFixedThreadPool(threads); + try { + for (int iteration = 0; iteration < 100; iteration++) { + Engine engine; + Context context; + if (iteration % 2 == 0) { + engine = Engine.create(); + context = Context.newBuilder().engine(engine).build(); + } else { + context = Context.newBuilder().build(); + engine = context.getEngine(); + } + context.initialize(ReuseLanguage.ID); + final CountDownLatch latch = new CountDownLatch(threads); + List> futures = new ArrayList<>(); + for (int i = 0; i < threads; i++) { + final int innerIteration = i; + futures.add(service.submit(() -> { + latch.countDown(); + try { + latch.await(); + } catch (InterruptedException e) { + } + if (innerIteration % 3 == 0) { + context.close(); + engine.close(); + } else { + engine.close(); + } + })); + } + for (Future future : futures) { + future.get(); + } + try { + // context must be closed successfully by at least one thread + context.eval(ReuseLanguage.ID, ""); + fail(); + } catch (IllegalStateException e) { + assertEquals("Engine is already closed.", e.getMessage()); + } + } + } finally { + service.shutdown(); + } + } + +} diff --git a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ContextPolicyTest.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ContextPolicyTest.java index 6f49047152ce..9556fd8b6057 100644 --- a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ContextPolicyTest.java +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ContextPolicyTest.java @@ -454,7 +454,7 @@ public SingleContextPolicyLanguage() { languageInstances.add(this); } - @Option(help = "", category = OptionCategory.DEBUG) // + @Option(help = "", category = OptionCategory.INTERNAL) // static final OptionKey Dummy = new OptionKey<>(0); @Override @@ -488,7 +488,7 @@ protected boolean isObjectOfLanguage(Object object) { @Registration(id = SINGLE_REUSE_LANGUAGE, name = SINGLE_REUSE_LANGUAGE, contextPolicy = ContextPolicy.REUSE) public static class SingleReusePolicyLanguage extends SingleContextPolicyLanguage { - @Option(help = "", category = OptionCategory.DEBUG) // + @Option(help = "", category = OptionCategory.INTERNAL) // static final OptionKey Dummy = new OptionKey<>(0); @Override @@ -504,7 +504,7 @@ protected OptionDescriptors getOptionDescriptors() { @Registration(id = MULTIPLE_LANGUAGE, name = MULTIPLE_LANGUAGE, contextPolicy = ContextPolicy.SHARED) public static class MultipleContextPolicyLanguage extends SingleContextPolicyLanguage { - @Option(help = "", category = OptionCategory.DEBUG) // + @Option(help = "", category = OptionCategory.INTERNAL) // static final OptionKey Dummy = new OptionKey<>(0); @Override diff --git a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/EngineAPITestLanguage.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/EngineAPITestLanguage.java index a56685df7f8e..f174e55bf2fe 100644 --- a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/EngineAPITestLanguage.java +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/EngineAPITestLanguage.java @@ -76,7 +76,7 @@ public class EngineAPITestLanguage extends TruffleLanguage { static final String Option3_HELP = "Option2_HELP"; static final boolean Option3_DEPRECATED = true; - static final OptionCategory Option3_CATEGORY = OptionCategory.DEBUG; + static final OptionCategory Option3_CATEGORY = OptionCategory.INTERNAL; static final String Option3_NAME = EngineAPITestLanguage.ID + ".Option3"; static final String Option3_DEFAULT = EngineAPITestLanguage.ID + "Option3_Default"; @@ -86,7 +86,7 @@ public class EngineAPITestLanguage extends TruffleLanguage { @Option(category = OptionCategory.EXPERT, name = "", help = Option2_HELP, deprecated = Option2_DEPRECATED) // static final OptionKey Option2 = new OptionKey<>(Option2_DEFAULT); - @Option(category = OptionCategory.DEBUG, help = Option3_HELP, deprecated = Option3_DEPRECATED) // + @Option(category = OptionCategory.INTERNAL, help = Option3_HELP, deprecated = Option3_DEPRECATED) // static final OptionKey Option3 = new OptionKey<>(Option3_DEFAULT); static class LanguageContext { diff --git a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/LanguageSPITest.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/LanguageSPITest.java index a96fffa995c0..abe35796949e 100644 --- a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/LanguageSPITest.java +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/LanguageSPITest.java @@ -107,6 +107,8 @@ import com.oracle.truffle.api.source.Source; import com.oracle.truffle.api.source.SourceSection; import com.oracle.truffle.api.test.polyglot.LanguageSPITestLanguage.LanguageContext; +import java.lang.reflect.Field; +import java.lang.reflect.Method; public class LanguageSPITest { @@ -657,7 +659,7 @@ public static class MultiContextLanguage extends ProxyLanguage { boolean contextCachingEnabled = false; int executionIndex; - @Option(help = "", category = OptionCategory.DEBUG) static final OptionKey DummyOption = new OptionKey<>(0); + @Option(help = "", category = OptionCategory.INTERNAL) static final OptionKey DummyOption = new OptionKey<>(0); @Override protected OptionDescriptors getOptionDescriptors() { @@ -1798,23 +1800,116 @@ public void testLookup() { protected CallTarget parse(com.oracle.truffle.api.TruffleLanguage.ParsingRequest request) throws Exception { Env env = ProxyLanguage.getCurrentContext().env; LanguageInfo languageInfo = env.getLanguages().get(LanguageSPITestLanguage.ID); - boolean found = env.lookup(languageInfo, LanguageSPITestLanguageService.class) != null; + String className = request.getSource().getCharacters().toString(); + boolean found = env.lookup(languageInfo, Class.forName(className)) != null; return Truffle.getRuntime().createCallTarget(RootNode.createConstantNode(found)); } }); // Not loaded language try (Context context = Context.create(LanguageSPITestLanguage.ID, ProxyLanguage.ID)) { - Value result = context.eval(ProxyLanguage.ID, ""); + Value result = context.eval(ProxyLanguage.ID, LanguageSPITestLanguageService.class.getName()); assertTrue(result.isBoolean()); assertFalse(result.asBoolean()); } // Loaded language try (Context context = Context.create(LanguageSPITestLanguage.ID, ProxyLanguage.ID)) { context.initialize(LanguageSPITestLanguage.ID); - Value result = context.eval(ProxyLanguage.ID, ""); + Value result = context.eval(ProxyLanguage.ID, LanguageSPITestLanguageService.class.getName()); assertTrue(result.isBoolean()); assertTrue(result.asBoolean()); } + // Registered service + langContext = null; + try (Context context = Context.create(LanguageSPITestLanguage.ID, ProxyLanguage.ID)) { + Value result = context.eval(ProxyLanguage.ID, LanguageSPITestLanguageService2.class.getName()); + assertTrue(result.isBoolean()); + assertTrue(result.asBoolean()); + result = context.eval(ProxyLanguage.ID, LanguageSPITestLanguageService3.class.getName()); + assertNotNull(langContext); + assertTrue(result.isBoolean()); + assertTrue(result.asBoolean()); + } + // Non registered service + langContext = null; + resetLoadedLanguage(LanguageSPITestLanguage.ID); + try (Context context = Context.create(LanguageSPITestLanguage.ID, ProxyLanguage.ID)) { + Value result = context.eval(ProxyLanguage.ID, LanguageSPITestLanguageService4.class.getName()); + assertFalse(isLanguageLoaded(LanguageSPITestLanguage.ID)); + assertNull(langContext); + assertTrue(result.isBoolean()); + assertFalse(result.asBoolean()); + } + } + + private static boolean isLanguageLoaded(String languageId) { + try { + Object languageCache = findLanguageCache(languageId); + Field field = languageCache.getClass().getDeclaredField("languageClass"); + field.setAccessible(true); + return field.get(languageCache) != null; + } catch (ReflectiveOperationException e) { + throw new AssertionError("Cannot reflectively read LanguageCache.languageClass field.", e); + } + } + + private static void resetLoadedLanguage(String languageId) { + try { + Object languageCache = findLanguageCache(languageId); + Field field = languageCache.getClass().getDeclaredField("languageClass"); + field.setAccessible(true); + field.set(languageCache, null); + } catch (ReflectiveOperationException e) { + throw new AssertionError("Cannot reflectively read LanguageCache.languageClass field.", e); + } + } + + @SuppressWarnings("unchecked") + private static Object findLanguageCache(String languageId) throws ReflectiveOperationException { + Class clazz = Class.forName("com.oracle.truffle.polyglot.LanguageCache"); + Method m = clazz.getDeclaredMethod("languages", ClassLoader.class); + m.setAccessible(true); + Map map = (Map) m.invoke(null, (Object) null); + return map.get(languageId); + } + + @Test + public void testRegisterService() { + ProxyLanguage registerServiceLanguage = new ProxyLanguage() { + @Override + protected ProxyLanguage.LanguageContext createContext(Env env) { + env.registerService(new LanguageSPITestLanguageService2() { + }); + return super.createContext(env); + } + + @Override + protected void initializeContext(ProxyLanguage.LanguageContext context) throws Exception { + try { + context.env.registerService(new LanguageSPITestLanguageService3() { + }); + fail("Illegal state exception should be thrown when calling Env.registerService outside createContext"); + } catch (IllegalStateException e) { + // expected + } + super.initializeContext(context); + } + + @Override + protected CallTarget parse(TruffleLanguage.ParsingRequest request) throws Exception { + try { + getContextReference().get().env.registerService(new LanguageSPITestLanguageService4() { + }); + fail("Illegal state exception should be thrown when calling Env.registerService outside createContext"); + } catch (IllegalStateException e) { + // expected + } + return Truffle.getRuntime().createCallTarget(RootNode.createConstantNode(true)); + } + }; + ProxyLanguage.setDelegate(registerServiceLanguage); + try (Context context = Context.create(ProxyLanguage.ID)) { + context.eval(ProxyLanguage.ID, ""); + } } static final String INHERITED_VERSION = "SPIInheritedVersionLanguage"; diff --git a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/LanguageSPITestLanguage.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/LanguageSPITestLanguage.java index 290060414c8d..2fbf14a8b93e 100644 --- a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/LanguageSPITestLanguage.java +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/LanguageSPITestLanguage.java @@ -54,7 +54,8 @@ import com.oracle.truffle.api.nodes.RootNode; import com.oracle.truffle.api.test.polyglot.LanguageSPITestLanguage.LanguageContext; -@TruffleLanguage.Registration(id = LanguageSPITestLanguage.ID, name = LanguageSPITestLanguage.ID, version = "1.0", contextPolicy = ContextPolicy.SHARED) +@TruffleLanguage.Registration(id = LanguageSPITestLanguage.ID, name = LanguageSPITestLanguage.ID, version = "1.0", contextPolicy = ContextPolicy.SHARED, services = { + LanguageSPITestLanguageService2.class, LanguageSPITestLanguageService3.class}) public class LanguageSPITestLanguage extends TruffleLanguage implements LanguageSPITestLanguageService { static final String ID = "LanguageSPITest"; @@ -64,7 +65,7 @@ public class LanguageSPITestLanguage extends TruffleLanguage im static final AtomicInteger instanceCount = new AtomicInteger(); static class LanguageContext { - + private volatile boolean initialized; int disposeCalled; Env env; Map config; @@ -111,20 +112,31 @@ protected LanguageContext createContext(Env env) { LanguageSPITest.langContext = new LanguageContext(); LanguageSPITest.langContext.env = env; LanguageSPITest.langContext.config = env.getConfig(); + env.registerService(new LanguageSPITestLanguageService2() { + }); + env.registerService(new LanguageSPITestLanguageService3() { + }); return LanguageSPITest.langContext; } @Override - protected void disposeContext(LanguageContext context) { - assertSame(getContext(), context); - assertSame(context, getContextReference().get()); + protected void initializeContext(LanguageContext context) throws Exception { + context.initialized = true; + } - assertSame(context, new RootNode(this) { - @Override - public Object execute(VirtualFrame frame) { - return null; - } - }.getLanguage(LanguageSPITestLanguage.class).getContextReference().get()); + @Override + protected void disposeContext(LanguageContext context) { + if (context.initialized) { + assertSame(getContext(), context); + assertSame(context, getContextReference().get()); + + assertSame(context, new RootNode(this) { + @Override + public Object execute(VirtualFrame frame) { + return null; + } + }.getLanguage(LanguageSPITestLanguage.class).getContextReference().get()); + } context.disposeCalled++; } @@ -137,3 +149,12 @@ protected boolean isObjectOfLanguage(Object object) { interface LanguageSPITestLanguageService { } + +interface LanguageSPITestLanguageService2 { +} + +interface LanguageSPITestLanguageService3 { +} + +interface LanguageSPITestLanguageService4 { +} diff --git a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/SeparatedClassLoadersTest.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/SeparatedClassLoadersTest.java new file mode 100644 index 000000000000..eb5cdaae8794 --- /dev/null +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/SeparatedClassLoadersTest.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.oracle.truffle.api.test.polyglot; + +import com.oracle.truffle.api.Truffle; +import java.net.URL; +import java.net.URLClassLoader; +import java.security.ProtectionDomain; +import org.graalvm.polyglot.Engine; +import org.junit.After; +import static org.junit.Assert.assertNotNull; +import org.junit.Assume; +import org.junit.Before; +import org.junit.Test; + +public class SeparatedClassLoadersTest { + private ClassLoader loader; + + @Before + public void storeLoader() { + loader = Thread.currentThread().getContextClassLoader(); + } + + @Test + public void sdkAndTruffleAPIInSeparateClassLoaders() throws Exception { + final ProtectionDomain sdkDomain = Engine.class.getProtectionDomain(); + Assume.assumeNotNull(sdkDomain); + Assume.assumeNotNull(sdkDomain.getCodeSource()); + URL sdkURL = sdkDomain.getCodeSource().getLocation(); + Assume.assumeNotNull(sdkURL); + + URL truffleURL = Truffle.class.getProtectionDomain().getCodeSource().getLocation(); + Assume.assumeNotNull(truffleURL); + + ClassLoader parent = Engine.class.getClassLoader().getParent(); + + URLClassLoader sdkLoader = new URLClassLoader(new URL[]{sdkURL}, parent); + URLClassLoader truffleLoader = new URLClassLoader(new URL[]{truffleURL}, sdkLoader); + Thread.currentThread().setContextClassLoader(truffleLoader); + + Class engineClass = sdkLoader.loadClass(Engine.class.getName()); + Object engine = engineClass.getMethod("create").invoke(null); + + assertNotNull("Engine has been created", engine); + } + + @After + public void resetLoader() { + Thread.currentThread().setContextClassLoader(loader); + } +} diff --git a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ValueAssert.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ValueAssert.java index 6e9a313d94cd..8a2952594220 100644 --- a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ValueAssert.java +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ValueAssert.java @@ -180,6 +180,7 @@ private static void assertValueImpl(Value value, int depth, Trait... expectedTyp assertClassMembers(value, (Class) hostObject, true); } else { assertClassMembers(value, Class.class, false); + assertTrue(value.hasMember("static")); } } else { assertClassMembers(value, hostObject.getClass(), false); @@ -197,8 +198,10 @@ private static void assertValueImpl(Value value, int depth, Trait... expectedTyp Map expectedValues = new HashMap<>(); for (String key : value.getMemberKeys()) { Value child = value.getMember(key); - assertValueImpl(child, depth + 1, detectSupportedTypes(child)); - expectedValues.put(key, value.getMember(key).as(Object.class)); + expectedValues.put(key, child.as(Object.class)); + if (!isSameHostObject(value, child)) { + assertValueImpl(child, depth + 1, detectSupportedTypes(child)); + } } if (value.isHostObject() && value.asHostObject() instanceof Map) { @@ -246,6 +249,10 @@ private static void assertContentEquals(Map } } + private static boolean isSameHostObject(Value a, Value b) { + return a.isHostObject() && b.isHostObject() && a.asHostObject() == b.asHostObject(); + } + static void assertUnsupported(Value value, Trait... supported) { Set supportedSet = new HashSet<>(Arrays.asList(supported)); for (Trait unsupportedType : Trait.values()) { diff --git a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ValueHostInteropTest.java b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ValueHostInteropTest.java index e16238b7f920..9f764bfd6a89 100644 --- a/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ValueHostInteropTest.java +++ b/truffle/src/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/polyglot/ValueHostInteropTest.java @@ -411,6 +411,14 @@ public void isNull() { assertFalse(context.asValue(new Object()).isNull()); } + @Test + public void testClassStaticMembers() { + Value stringClass = context.asValue(String.class); + Value stringStatic = stringClass.getMember("static"); + assertEquals("concatenated", stringStatic.getMember("join").execute("cat", "con", "enated").asString()); + assertEquals(String.class, stringStatic.getMember("class").asHostObject()); + } + @FunctionalInterface public interface FunctionalWithDefaults { Object call(Object... args); diff --git a/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleException.java b/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleException.java index 1bc9e0428216..ba9461dc349a 100644 --- a/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleException.java +++ b/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleException.java @@ -66,7 +66,7 @@ * * * @since 0.27 - * @see TruffleStackTraceElement#getStackTrace(Throwable) To access the stack trace of an exception. + * @see TruffleStackTrace TruffleStackTrace to access the stack trace of an exception. */ public interface TruffleException { diff --git a/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java b/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java index 1b1d0477324b..b8f1a5ba2417 100644 --- a/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java +++ b/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleLanguage.java @@ -415,6 +415,21 @@ String[] dependentLanguages() default { * @since 1.0 */ ContextPolicy contextPolicy() default ContextPolicy.EXCLUSIVE; + + /** + * Declarative list of classes this language is known to provide. The language is supposed + * to override its {@link #createContext(com.oracle.truffle.api.TruffleLanguage.Env) + * createContext} method and instantiate and {@link Env#registerService(java.lang.Object) + * register} all here in defined services. + *

+ * Languages automatically get created but not yet initialized when their registered + * {@link Env#lookup(com.oracle.truffle.api.nodes.LanguageInfo, java.lang.Class) service is + * requested}. + * + * @since 1.0 + * @return list of service types that this language can provide + */ + Class[] services() default {}; } /** @@ -471,6 +486,10 @@ protected boolean areOptionsCompatible(OptionValues firstOptions, OptionValues n * context creation. Should there be a need to perform complex initialization, do it by * overriding the {@link #initializeContext(java.lang.Object)} method. *

+ * Additional services provided by the language must be + * {@link Env#registerService(java.lang.Object) registered} by this method otherwise + * {@link IllegalStateException} is thrown. + *

* May return {@code null} if the language does not need any per-{@linkplain Context context} * state. Otherwise it should return a new object instance every time it is called. * @@ -1330,6 +1349,7 @@ public static final class Env { @CompilationFinal private volatile boolean initialized = false; @CompilationFinal private volatile Assumption initializedUnchangedAssumption = Truffle.getRuntime().createAssumption("Language context initialized unchanged"); @CompilationFinal private volatile boolean valid; + private volatile List languageServicesCollector; @SuppressWarnings("unchecked") private Env(Object vmObject, TruffleLanguage language, OutputStream out, OutputStream err, InputStream in, Map config, OptionValues options, String[] applicationArguments, @@ -1871,16 +1891,17 @@ public S lookup(InstrumentInfo instrument, Class type) { } /** - * Returns an additional service provided by the given language, specified by type. If an - * language is not loaded, it will not be automatically loaded by requesting a service. In - * order to ensure a language to be loaded at least one {@link Source} must be - * {@link #parse(Source, String...) parsed} first. + * Returns an additional service provided by the given language, specified by type. For + * services registered by {@link Registration#services()} the service lookup will ensure + * that the language is loaded and services are registered. * * @param the requested type * @param language the language to query * @param type the class of the requested type * @return the registered service or null if none is found * @since 0.26 + * @since 1.0 supports services registered by {@link Env#registerService(java.lang.Object) + * registerService} */ @TruffleBoundary public S lookup(LanguageInfo language, Class type) { @@ -1888,6 +1909,11 @@ public S lookup(LanguageInfo language, Class type) { throw new IllegalArgumentException("Cannot request services from the current language."); } + S result = AccessAPI.engineAccess().lookupService(vmObject, language, this.getSpi().languageInfo, type); + if (result != null) { + return result; + } + // Legacy behaviour - deprecate and remove Env otherEnv = AccessAPI.engineAccess().getLanguageEnv(vmObject, language); return otherEnv == null ? null : otherEnv.getSpi().lookup(type); } @@ -2022,6 +2048,32 @@ public void setCurrentWorkingDirectory(TruffleFile currentWorkingDirectory) { fileSystem.setCurrentWorkingDirectory(currentWorkingDirectory.getSPIPath()); } + /** + * Registers additional services provided by the language. The registered services are made + * available to users via + * {@link #lookup(com.oracle.truffle.api.nodes.LanguageInfo, java.lang.Class)} query method. + *

+ * For each service interface enumerated in {@link Registration#services() language + * registration} the language has to register a single service implementation. + *

+ * This method can be called only during the execution of the + * {@link #createContext(com.oracle.truffle.api.TruffleLanguage.Env) createContext method}, + * then the services are collected and cannot be changed anymore. + * + * @param service a service to be returned from associated + * {@link Env#lookup(com.oracle.truffle.api.nodes.LanguageInfo, java.lang.Class) + * lookup method} + * @throws IllegalStateException if the method is called outside of + * {@link #createContext(com.oracle.truffle.api.TruffleLanguage.Env)} method + * @since 1.0 + */ + public void registerService(Object service) { + if (languageServicesCollector == null) { + throw new IllegalStateException("The registerService method can only be called during the execution of the Env.createContext method."); + } + languageServicesCollector.add(service); + } + @SuppressWarnings("rawtypes") @TruffleBoundary E getLanguage(Class languageClass) { @@ -2384,8 +2436,14 @@ public Env createEnv(Object vmObject, TruffleLanguage language, OutputStream } @Override - public Object createEnvContext(Env env) { - Object context = env.getSpi().createContext(env); + public Object createEnvContext(Env env, List servicesCollector) { + env.languageServicesCollector = servicesCollector; + Object context; + try { + context = env.getSpi().createContext(env); + } finally { + env.languageServicesCollector = null; + } env.context = context; Assumption contextUnchanged = env.contextUnchangedAssumption; env.contextUnchangedAssumption = Truffle.getRuntime().createAssumption("Language context unchanged"); diff --git a/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleStackTrace.java b/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleStackTrace.java index 734f46a59ee8..9349db131405 100644 --- a/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleStackTrace.java +++ b/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleStackTrace.java @@ -56,10 +56,26 @@ import com.oracle.truffle.api.nodes.Node; /** - * @see TruffleStackTraceElement To lookup the stack trace. + * Represents a guest language stack trace. + * + * A TruffleStackTrace is automatically added when a {@link Throwable} passes through a + * {@link CallTarget call target}. {@link ControlFlowException} and {@link PolyglotException} do not + * get a TruffleStackTrace. Other {@link Throwable} are added a TruffleStackTrace, as long as there + * is a {@code null} {@link Throwable#getCause() cause} available to insert the TruffleStackTrace. + *

+ * A guest language stack trace element is automatically added by the Truffle runtime every time the + * {@link Throwable} passes through a {@link CallTarget call target}. This is incremental and + * therefore efficient if the exception is later caught in the same compilation unit. + *

+ * Note that if the Throwable is caught, its stack trace should be filled eagerly with + * {@link #fillIn(Throwable)}, unless it can be guaranteed to be re-thrown in the same + * {@link CallTarget call target}, or that the stack trace will not be used. + * + * @see #getStackTrace() getStackTrace() to retrieve the stacktrace from a {@link Throwable}. + * @since 1.0 */ @SuppressWarnings("serial") -final class TruffleStackTrace extends Exception { +public final class TruffleStackTrace extends Exception { private static final TruffleStackTrace EMPTY = new TruffleStackTrace(Collections.emptyList(), 0); private List frames; @@ -87,6 +103,9 @@ private void materializeHostException() { } } + /** + * @since 1.0 + */ @SuppressWarnings("sync-override") @Override public Throwable fillInStackTrace() { @@ -108,14 +127,28 @@ StackTraceElement[] getInternalStackTrace() { } } + /** + * @since 1.0 + */ @Override public String toString() { return "Attached Guest Language Frames (" + frames.size() + ")"; } + /** + * Returns the guest language frames that are stored in this throwable or null if + * no guest language frames can ever be stored in this throwable. This method fills in the + * stacktrace by calling {@link #fillIn(Throwable)}, so it is not necessary to call + * {@link #fillIn(Throwable)} before. The returned list is not modifiable. The number of stack + * trace elements that are filled in can be customized by implementing + * {@link TruffleException#getStackTraceElementLimit()}. + * + * @param throwable the throwable instance to look for guest language frames + * @since 1.0 + */ @TruffleBoundary - static List find(Throwable t) { - TruffleStackTrace stack = fillIn(t); + public static List getStacktrace(Throwable throwable) { + TruffleStackTrace stack = fillIn(throwable); if (stack != null) { return stack.frames; } @@ -166,15 +199,25 @@ private static void insert(Throwable t, LazyStackTrace trace) { } } + /** + * Fills in the guest language stack frames from the current frames on the stack. If the stack + * was already filled before then this method has no effect. The implementation attaches a + * lightweight exception object to the last location in the {@link Throwable#getCause() cause} + * chain of the exception. The number stack trace elements that are filled in can be customized + * by implementing {@link TruffleException#getStackTraceElementLimit()}. + * + * @param throwable the Throwable to fill + * @since 1.0 + */ @TruffleBoundary - static TruffleStackTrace fillIn(Throwable t) { - if (t instanceof ControlFlowException) { + public static TruffleStackTrace fillIn(Throwable throwable) { + if (throwable instanceof ControlFlowException) { return EMPTY; } - LazyStackTrace lazy = findImpl(t); + LazyStackTrace lazy = findImpl(throwable); if (lazy == null) { - Throwable insertCause = findInsertCause(t); + Throwable insertCause = findInsertCause(throwable); if (insertCause == null) { return null; } @@ -187,8 +230,8 @@ static TruffleStackTrace fillIn(Throwable t) { int stackFrameLimit; Node topCallSite; - if (t instanceof TruffleException) { - TruffleException te = (TruffleException) t; + if (throwable instanceof TruffleException) { + TruffleException te = (TruffleException) throwable; topCallSite = te.getLocation(); stackFrameLimit = te.getStackTraceElementLimit(); } else { diff --git a/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleStackTraceElement.java b/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleStackTraceElement.java index e5769faf913c..0296c4b7f025 100644 --- a/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleStackTraceElement.java +++ b/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/TruffleStackTraceElement.java @@ -103,32 +103,21 @@ public Frame getFrame() { } /** - * Returns the guest language frames that are stored in this throwable or null if - * no guest language frames are available. Guest language frames are automatically added by the - * Truffle runtime the first time the exception is passed through a {@link CallTarget call - * target} and the frames are not yet set. Therefore no guest language frames are available - * immediately after the exception was constructed. The returned list is not modifiable. The - * number stack trace elements that are filled in can be customized by implementing - * {@link TruffleException#getStackTraceElementLimit()} . + * @deprecated Use {@link TruffleStackTrace#getStacktrace(Throwable)} instead. * - * @param throwable the throwable instance to look for guest language frames - * @see #fillIn(Throwable) To force early filling of guest language stack frames. * @since 0.27 */ + @Deprecated public static List getStackTrace(Throwable throwable) { - return TruffleStackTrace.find(throwable); + return TruffleStackTrace.getStacktrace(throwable); } /** - * Fills in the guest language stack frames from the current frames on the stack. If the stack - * was already filled before then this method has no effect. The implementation attaches a - * lightweight exception object to the last location in the {@link Throwable#getCause() cause} - * chain of the exception. The number stack trace elements that are filled in can be customized - * by implementing {@link TruffleException#getStackTraceElementLimit()} . + * @deprecated Use {@link TruffleStackTrace#fillIn(Throwable)} instead. * - * @param throwable the throwable to fill * @since 0.27 */ + @Deprecated public static void fillIn(Throwable throwable) { TruffleStackTrace.fillIn(throwable); } diff --git a/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/frame/FrameDescriptor.java b/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/frame/FrameDescriptor.java index 0ab5bae3308b..f838890e7b22 100644 --- a/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/frame/FrameDescriptor.java +++ b/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/frame/FrameDescriptor.java @@ -50,6 +50,8 @@ import java.util.Set; import java.util.function.Predicate; +import org.graalvm.collections.EconomicMap; + import com.oracle.truffle.api.Assumption; import com.oracle.truffle.api.CompilerAsserts; import com.oracle.truffle.api.CompilerDirectives; @@ -57,7 +59,6 @@ import com.oracle.truffle.api.Truffle; import com.oracle.truffle.api.impl.Accessor; import com.oracle.truffle.api.impl.TVMCI; -import org.graalvm.collections.EconomicMap; /** * Descriptor of the slots of frame objects. Multiple frame instances are associated with one such @@ -604,9 +605,12 @@ protected boolean getMaterializeCalled(FrameDescriptor descriptor) { } } + private static AccessorFrames initialize() { + return new AccessorFrames(); + } + static { // registers into Accessor.FRAMES - @SuppressWarnings("unused") - AccessorFrames unused = new AccessorFrames(); + initialize(); } } diff --git a/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java b/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java index 76804846ace0..0fbf1ca3227f 100644 --- a/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java +++ b/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/Accessor.java @@ -82,6 +82,7 @@ import com.oracle.truffle.api.source.Source.SourceBuilder; import com.oracle.truffle.api.source.SourceSection; import java.nio.file.Path; +import java.util.List; /** * Communication between TruffleLanguage API/SPI, and other services. @@ -332,6 +333,7 @@ public Thread createThread(Object vmObject, Runnable runnable, Object innerConte public abstract boolean isHostSymbol(Object guestObject); + public abstract S lookupService(Object languageContextVMObject, LanguageInfo language, LanguageInfo accessingLanguage, Class type); } public abstract static class LanguageSupport { @@ -343,7 +345,7 @@ public abstract Env createEnv(Object vmObject, TruffleLanguage language, Outp public abstract boolean areOptionsCompatible(TruffleLanguage language, OptionValues firstContextOptions, OptionValues newContextOptions); - public abstract Object createEnvContext(Env localEnv); + public abstract Object createEnvContext(Env localEnv, List servicesCollector); public abstract TruffleContext createTruffleContext(Object impl); diff --git a/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/GraphPrintVisitor.java b/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/GraphPrintVisitor.java index 578fcea19939..19fe99981150 100644 --- a/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/GraphPrintVisitor.java +++ b/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/GraphPrintVisitor.java @@ -495,7 +495,7 @@ final NodeElement getElementByObject(Object obj) { final void createElementForNode(Object node) { boolean exists = nodeMap.containsKey(node); if (!exists) { - int nodeId = !exists ? oldOrNextId(node) : nextId(); + int nodeId = oldOrNextId(node); nodeMap.put(node, new NodeElement(nodeId)); String className = NodeUtil.className(node.getClass()); diff --git a/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/source/SourceImpl.java b/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/source/SourceImpl.java index e9b7165f67f1..c440f2986413 100644 --- a/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/source/SourceImpl.java +++ b/truffle/src/com.oracle.truffle.api/src/com/oracle/truffle/api/source/SourceImpl.java @@ -262,9 +262,7 @@ private boolean compareContent(Key other) { } private static boolean compareBytes(ByteSequence bytes, ByteSequence other) { - if (bytes == null) { - return false; - } else if (bytes.length() != other.length()) { + if (bytes == null || bytes.length() != other.length()) { return false; } else { // trusted class @@ -273,12 +271,9 @@ private static boolean compareBytes(ByteSequence bytes, ByteSequence other) { } private static boolean compareCharacters(CharSequence characters, CharSequence other) { - if (characters == null) { - return false; - } else if (characters.length() != other.length()) { + if (characters == null || characters.length() != other.length()) { return false; } else { - assert other != null; return Objects.equals(characters.toString(), other.toString()); } } diff --git a/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/InstrumentableProcessor.java b/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/InstrumentableProcessor.java index 6d0660dbc1e0..721a2dab7fe7 100644 --- a/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/InstrumentableProcessor.java +++ b/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/InstrumentableProcessor.java @@ -68,7 +68,6 @@ import javax.lang.model.util.Types; import javax.tools.Diagnostic.Kind; -import com.oracle.truffle.api.dsl.GeneratedBy; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.instrumentation.GenerateWrapper; import com.oracle.truffle.api.instrumentation.GenerateWrapper.IncomingConverter; @@ -316,7 +315,7 @@ private static CodeTypeElement generateFactory(ProcessorContext context, Element TypeMirror factoryType = context.reloadType(context.getType(com.oracle.truffle.api.instrumentation.InstrumentableFactory.class)); factory.getImplements().add(new CodeTypeMirror.DeclaredCodeTypeMirror(ElementUtils.fromTypeMirror(factoryType), Arrays.asList(sourceType.asType()))); - addGeneratedBy(context, factory, sourceType); + GeneratorUtils.addGeneratedBy(context, factory, sourceType); TypeMirror returnType = context.getType(com.oracle.truffle.api.instrumentation.InstrumentableFactory.WrapperNode.class); CodeExecutableElement createMethod = new CodeExecutableElement(ElementUtils.modifiers(Modifier.PUBLIC), returnType, "createWrapper"); @@ -444,7 +443,7 @@ private CodeTypeElement generateWrapper(ProcessorContext context, Element e, boo wrapperType.getImplements().add(context.getType(com.oracle.truffle.api.instrumentation.InstrumentableFactory.WrapperNode.class)); } - addGeneratedBy(context, wrapperType, sourceType); + GeneratorUtils.addGeneratedBy(context, wrapperType, sourceType); wrapperType.add(createNodeChild(context, sourceType.asType(), FIELD_DELEGATE)); wrapperType.add(createNodeChild(context, context.getType(ProbeNode.class), FIELD_PROBE)); @@ -812,16 +811,6 @@ private static TypeMirror boxed(TypeMirror type, Types types) { } } - private static void addGeneratedBy(ProcessorContext context, CodeTypeElement generatedType, TypeElement generatedByType) { - DeclaredType generatedBy = (DeclaredType) context.getType(GeneratedBy.class); - // only do this if generatedBy is on the classpath. - if (generatedBy != null) { - CodeAnnotationMirror generatedByAnnotation = new CodeAnnotationMirror(generatedBy); - generatedByAnnotation.setElementValue(generatedByAnnotation.findExecutableElement("value"), new CodeAnnotationValue(generatedByType.asType())); - generatedType.addAnnotationMirror(generatedByAnnotation); - } - } - private static boolean isOverrideableOrUndeclared(TypeElement sourceType, String methodName) { List elements = ElementUtils.getDeclaredMethodsInSuperTypes(sourceType, methodName); return elements.isEmpty() || !elements.iterator().next().getModifiers().contains(Modifier.FINAL); diff --git a/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/LanguageRegistrationProcessor.java b/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/LanguageRegistrationProcessor.java index 2bb740eccaf9..c856320302f8 100644 --- a/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/LanguageRegistrationProcessor.java +++ b/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/LanguageRegistrationProcessor.java @@ -135,6 +135,17 @@ private void generateFile(List languages) { } p.setProperty(prefix + "interactive", Boolean.toString(annotation.interactive())); p.setProperty(prefix + "internal", Boolean.toString(annotation.internal())); + + int serviceCounter = 0; + TypeMirror registrationType = ProcessorContext.getInstance().getType(Registration.class); + for (AnnotationMirror anno : l.getAnnotationMirrors()) { + TypeMirror annoType = anno.getAnnotationType(); + if (ElementUtils.typeEquals(registrationType, annoType)) { + for (TypeMirror serviceTypeMirror : ElementUtils.getAnnotationValueList(TypeMirror.class, anno, "services")) { + p.setProperty(prefix + "service" + serviceCounter++, ElementUtils.getQualifiedName(serviceTypeMirror)); + } + } + } } if (cnt > 0) { try { diff --git a/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/OptionProcessor.java b/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/OptionProcessor.java index fb771d48f9d0..f3c680f6124d 100644 --- a/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/OptionProcessor.java +++ b/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/OptionProcessor.java @@ -82,6 +82,7 @@ import com.oracle.truffle.api.Option; import com.oracle.truffle.api.TruffleLanguage; import com.oracle.truffle.api.instrumentation.TruffleInstrument; +import com.oracle.truffle.dsl.processor.generator.GeneratorUtils; import com.oracle.truffle.dsl.processor.java.ElementUtils; import com.oracle.truffle.dsl.processor.java.model.CodeExecutableElement; import com.oracle.truffle.dsl.processor.java.model.CodeTree; @@ -277,7 +278,7 @@ private boolean processElement(Element element, AnnotationMirror elementAnnotati OptionCategory category = annotation.category(); if (category == null) { - category = OptionCategory.DEBUG; + category = OptionCategory.INTERNAL; } for (String group : groupPrefixStrings) { @@ -343,6 +344,7 @@ private CodeTypeElement generateDescriptors(ProcessorContext context, Element el CodeTypeElement descriptors = new CodeTypeElement(typeModifiers, ElementKind.CLASS, pack, optionsClassName); DeclaredType optionDescriptorsType = context.getDeclaredType(OptionDescriptors.class); descriptors.getImplements().add(optionDescriptorsType); + GeneratorUtils.addGeneratedBy(context, descriptors, (TypeElement) element); ExecutableElement get = ElementUtils.findExecutableElement(optionDescriptorsType, "get"); CodeExecutableElement getMethod = CodeExecutableElement.clone(processingEnv, get); diff --git a/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/generator/GeneratorUtils.java b/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/generator/GeneratorUtils.java index a29f94c07134..c1cbcd1a472e 100644 --- a/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/generator/GeneratorUtils.java +++ b/truffle/src/com.oracle.truffle.dsl.processor/src/com/oracle/truffle/dsl/processor/generator/GeneratorUtils.java @@ -150,6 +150,16 @@ static CodeTypeElement createClass(Template sourceModel, TemplateMethod sourceMe return clazz; } + public static void addGeneratedBy(ProcessorContext context, CodeTypeElement generatedType, TypeElement generatedByType) { + DeclaredType generatedBy = (DeclaredType) context.getType(GeneratedBy.class); + // only do this if generatedBy is on the classpath. + if (generatedBy != null) { + CodeAnnotationMirror generatedByAnnotation = new CodeAnnotationMirror(generatedBy); + generatedByAnnotation.setElementValue(generatedByAnnotation.findExecutableElement("value"), new CodeAnnotationValue(generatedByType.asType())); + generatedType.addAnnotationMirror(generatedByAnnotation); + } + } + static List findUserConstructors(TypeMirror nodeType) { List constructors = new ArrayList<>(); for (ExecutableElement constructor : ElementFilter.constructorsIn(ElementUtils.fromTypeMirror(nodeType).getEnclosedElements())) { diff --git a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/FileSystems.java b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/FileSystems.java index 37292e2901af..8d440ce0008c 100644 --- a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/FileSystems.java +++ b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/FileSystems.java @@ -500,7 +500,7 @@ private Set getLanguageHomes() { res = languageHomes; if (res == null) { res = new HashSet<>(); - for (LanguageCache cache : LanguageCache.languages().values()) { + for (LanguageCache cache : LanguageCache.languages(null).values()) { final String languageHome = cache.getLanguageHome(); if (languageHome != null) { res.add(Paths.get(languageHome)); diff --git a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/HostInteropReflect.java b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/HostInteropReflect.java index 5073269ada5c..bcfea9aec6d1 100644 --- a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/HostInteropReflect.java +++ b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/HostInteropReflect.java @@ -75,6 +75,8 @@ final class HostInteropReflect { static final Object[] EMPTY = {}; + static final String STATIC_TO_CLASS = "class"; + static final String CLASS_TO_STATIC = "static"; private HostInteropReflect() { } @@ -115,19 +117,19 @@ static HostFieldDesc findField(Class clazz, String name, boolean onlyStatic) } @CompilerDirectives.TruffleBoundary - static int findKeyInfo(Class clazz, String name, boolean onlyStatic) { + static int findKeyInfo(Class clazz, String name, boolean isStatic, boolean isClass) { boolean readable = false; boolean writable = false; boolean invocable = false; boolean internal = false; HostClassDesc classDesc = HostClassDesc.forClass(clazz); - HostMethodDesc foundMethod = classDesc.lookupMethod(name, onlyStatic); + HostMethodDesc foundMethod = classDesc.lookupMethod(name, isStatic); if (foundMethod != null) { readable = true; invocable = true; } else if (isJNIName(name)) { - foundMethod = classDesc.lookupMethodByJNIName(name, onlyStatic); + foundMethod = classDesc.lookupMethodByJNIName(name, isStatic); if (foundMethod != null) { readable = true; invocable = true; @@ -136,16 +138,16 @@ static int findKeyInfo(Class clazz, String name, boolean onlyStatic) { } if (!readable) { - HostFieldDesc foundField = classDesc.lookupField(name, onlyStatic); + HostFieldDesc foundField = classDesc.lookupField(name, isStatic); if (foundField != null) { readable = true; writable = true; } } - if (onlyStatic) { + if (isStatic) { if (!readable) { - if ("class".equals(name)) { + if (STATIC_TO_CLASS.equals(name)) { readable = true; } } @@ -155,6 +157,12 @@ static int findKeyInfo(Class clazz, String name, boolean onlyStatic) { readable = true; } } + } else if (isClass) { + if (!readable) { + if (CLASS_TO_STATIC.equals(name)) { + readable = true; + } + } } if (readable) { @@ -231,13 +239,13 @@ static boolean isStaticTypeOrInterface(Class t) { } @CompilerDirectives.TruffleBoundary - static String[] findUniquePublicMemberNames(Class clazz, boolean onlyStatic, boolean includeInternal) throws SecurityException { + static String[] findUniquePublicMemberNames(Class clazz, boolean isStatic, boolean isClass, boolean includeInternal) throws SecurityException { HostClassDesc classDesc = HostClassDesc.forClass(clazz); EconomicSet names = EconomicSet.create(); - names.addAll(classDesc.getFieldNames(onlyStatic)); - names.addAll(classDesc.getMethodNames(onlyStatic, includeInternal)); - if (onlyStatic) { - names.add("class"); + names.addAll(classDesc.getFieldNames(isStatic)); + names.addAll(classDesc.getMethodNames(isStatic, includeInternal)); + if (isStatic) { + names.add(STATIC_TO_CLASS); if (!TruffleOptions.AOT) { // GR-13208: SVM does not support Class.getClasses() yet if (Modifier.isPublic(clazz.getModifiers())) { // no support for non-static member types now @@ -249,6 +257,8 @@ static String[] findUniquePublicMemberNames(Class clazz, boolean onlyStatic, } } } + } else if (isClass) { + names.add(CLASS_TO_STATIC); } return names.toArray(new String[names.size()]); } diff --git a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/HostObject.java b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/HostObject.java index 587c8a3b13ae..01c52a75710f 100644 --- a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/HostObject.java +++ b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/HostObject.java @@ -479,13 +479,15 @@ public Object access(HostObject object, String name) { } if (isStatic) { LookupInnerClassNode lookupInnerClassNode = lookupInnerClass(); - if ("class".equals(name)) { + if (HostInteropReflect.STATIC_TO_CLASS.equals(name)) { return HostObject.forClass(lookupClass, object.languageContext); } Class innerclass = lookupInnerClassNode.execute(lookupClass, name); if (innerclass != null) { return HostObject.forStaticClass(innerclass, object.languageContext); } + } else if (object.isClass() && HostInteropReflect.CLASS_TO_STATIC.equals(name)) { + return HostObject.forStaticClass(object.asClass(), object.languageContext); } throw UnknownIdentifierException.raise(name); } @@ -920,7 +922,7 @@ public Object access(HostObject receiver, boolean includeInternal) { if (receiver.isNull()) { throw UnsupportedMessageException.raise(Message.KEYS); } - String[] fields = HostInteropReflect.findUniquePublicMemberNames(receiver.getLookupClass(), receiver.isStaticClass(), includeInternal); + String[] fields = HostInteropReflect.findUniquePublicMemberNames(receiver.getLookupClass(), receiver.isStaticClass(), receiver.isClass(), includeInternal); return HostObject.forObject(fields, receiver.languageContext); } } @@ -931,22 +933,23 @@ abstract static class KeyInfoCacheNode extends Node { KeyInfoCacheNode() { } - public abstract int execute(Class clazz, String name, boolean onlyStatic); + public abstract int execute(Class clazz, String name, boolean isStatic, boolean isClass); @SuppressWarnings("unused") - @Specialization(guards = {"onlyStatic == cachedStatic", "clazz == cachedClazz", "cachedName.equals(name)"}, limit = "LIMIT") - static int doCached(Class clazz, String name, boolean onlyStatic, - @Cached("onlyStatic") boolean cachedStatic, + @Specialization(guards = {"isStatic == cachedStatic", "isClass == cachedIsClass", "clazz == cachedClazz", "cachedName.equals(name)"}, limit = "LIMIT") + static int doCached(Class clazz, String name, boolean isStatic, boolean isClass, + @Cached("isStatic") boolean cachedStatic, + @Cached("isClass") boolean cachedIsClass, @Cached("clazz") Class cachedClazz, @Cached("name") String cachedName, - @Cached("doUncached(clazz, name, onlyStatic)") int cachedKeyInfo) { - assert cachedKeyInfo == doUncached(clazz, name, onlyStatic); + @Cached("doUncached(clazz, name, isStatic, isClass)") int cachedKeyInfo) { + assert cachedKeyInfo == doUncached(clazz, name, isStatic, isClass); return cachedKeyInfo; } @Specialization(replaces = "doCached") - static int doUncached(Class clazz, String name, boolean onlyStatic) { - return HostInteropReflect.findKeyInfo(clazz, name, onlyStatic); + static int doUncached(Class clazz, String name, boolean isStatic, boolean isClass) { + return HostInteropReflect.findKeyInfo(clazz, name, isStatic, isClass); } } @@ -994,7 +997,7 @@ public int access(HostObject receiver, String name) { if (receiver.isNull()) { throw UnsupportedMessageException.raise(Message.KEY_INFO); } - return keyInfoCache().execute(receiver.getLookupClass(), name, receiver.isStaticClass()); + return keyInfoCache().execute(receiver.getLookupClass(), name, receiver.isStaticClass(), receiver.isClass()); } private KeyInfoCacheNode keyInfoCache() { diff --git a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/InstrumentCache.java b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/InstrumentCache.java index 201682039ff9..10aa34e2be4f 100644 --- a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/InstrumentCache.java +++ b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/InstrumentCache.java @@ -57,6 +57,8 @@ import com.oracle.truffle.api.TruffleOptions; import com.oracle.truffle.api.instrumentation.TruffleInstrument; +import java.util.Map; +import java.util.WeakHashMap; //TODO (chumer): maybe this class should share some code with LanguageCache? final class InstrumentCache { @@ -64,7 +66,7 @@ final class InstrumentCache { private static final boolean JDK8OrEarlier = System.getProperty("java.specification.version").compareTo("1.9") < 0; private static final List nativeImageCache = TruffleOptions.AOT ? new ArrayList<>() : null; - private static List runtimeCache; + private static Map> runtimeCaches = new WeakHashMap<>(); private Class instrumentClass; private final String className; @@ -84,7 +86,7 @@ final class InstrumentCache { */ @SuppressWarnings("unused") private static void initializeNativeImageState(ClassLoader imageClassLoader) { - nativeImageCache.addAll(doLoad(Collections.singletonList(imageClassLoader))); + nativeImageCache.addAll(doLoad(Collections.emptyList(), imageClassLoader)); } /** @@ -95,7 +97,7 @@ private static void initializeNativeImageState(ClassLoader imageClassLoader) { @SuppressWarnings("unused") private static void resetNativeImageState() { nativeImageCache.clear(); - runtimeCache = null; + runtimeCaches.clear(); } private InstrumentCache(String prefix, Properties info, ClassLoader loader) { @@ -134,26 +136,33 @@ public boolean isInternal() { return internal; } - static List load(Collection loaders) { + static List load(Collection loaders, ClassLoader additionalLoader) { if (TruffleOptions.AOT) { return nativeImageCache; } - if (runtimeCache != null) { - return runtimeCache; + synchronized (InstrumentCache.class) { + List cache = runtimeCaches.get(additionalLoader); + if (cache == null) { + cache = doLoad(loaders, additionalLoader); + runtimeCaches.put(additionalLoader, cache); + } + return cache; } - return doLoad(loaders); } - private static List doLoad(Collection loaders) { + private static List doLoad(Collection loaders, ClassLoader additionalLoader) { List list = new ArrayList<>(); Set classNamesUsed = new HashSet<>(); for (ClassLoader loader : loaders) { loadForOne(loader, list, classNamesUsed); } + if (additionalLoader != null) { + loadForOne(additionalLoader, list, classNamesUsed); + } if (!JDK8OrEarlier) { loadForOne(ModuleResourceLocator.createLoader(), list, classNamesUsed); } - return runtimeCache = list; + return list; } private static void loadForOne(ClassLoader loader, List list, Set classNamesUsed) { diff --git a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/LanguageCache.java b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/LanguageCache.java index 80ee54f5650e..b66c4a0cfbae 100644 --- a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/LanguageCache.java +++ b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/LanguageCache.java @@ -65,6 +65,7 @@ import com.oracle.truffle.api.TruffleLanguage.ContextPolicy; import com.oracle.truffle.api.TruffleLanguage.Registration; import com.oracle.truffle.api.TruffleOptions; +import java.util.WeakHashMap; /** * Ahead-of-time initialization. If the JVM is started with {@link TruffleOptions#AOT}, it populates @@ -73,7 +74,7 @@ final class LanguageCache implements Comparable { private static final Map nativeImageCache = TruffleOptions.AOT ? new HashMap<>() : null; private static final Map nativeImageMimes = TruffleOptions.AOT ? new HashMap<>() : null; - private static volatile Map runtimeCache; + private static final Map> runtimeCaches = new WeakHashMap<>(); private static volatile Map runtimeMimes; private final String className; private final Set mimeTypes; @@ -89,6 +90,7 @@ final class LanguageCache implements Comparable { private final boolean internal; private final ClassLoader loader; private final TruffleLanguage globalInstance; + private final Set services; private String languageHome; private volatile ContextPolicy policy; private volatile Class> languageClass; @@ -121,6 +123,18 @@ private LanguageCache(String id, String prefix, Properties info, ClassLoader loa this.interactive = Boolean.valueOf(info.getProperty(prefix + "interactive")); this.internal = Boolean.valueOf(info.getProperty(prefix + "internal")); this.languageHome = url; + + Set servicesClassNames = new TreeSet<>(); + for (int servicesCounter = 0;; servicesCounter++) { + String nth = prefix + "service" + servicesCounter; + String serviceName = info.getProperty(nth); + if (serviceName == null) { + break; + } + servicesClassNames.add(serviceName); + } + this.services = Collections.unmodifiableSet(servicesClassNames); + if (TruffleOptions.AOT) { initializeLanguageClass(); assert languageClass != null; @@ -143,7 +157,7 @@ private static TreeSet parseList(Properties info, String prefix) { @SuppressWarnings("unchecked") LanguageCache(String id, String name, String implementationName, String version, boolean interactive, boolean internal, - TruffleLanguage instance) { + TruffleLanguage instance, String... services) { this.id = id; this.className = instance.getClass().getName(); this.mimeTypes = Collections.emptySet(); @@ -161,6 +175,13 @@ private static TreeSet parseList(Properties info, String prefix) { this.languageHome = null; this.policy = ContextPolicy.SHARED; this.globalInstance = instance; + if (services.length == 0) { + this.services = Collections.emptySet(); + } else { + Set servicesClassNames = new TreeSet<>(); + Collections.addAll(servicesClassNames, services); + this.services = Collections.unmodifiableSet(servicesClassNames); + } } static Map languageMimes() { @@ -181,7 +202,7 @@ static Map languageMimes() { private static Map createMimes() { Map mimes = new LinkedHashMap<>(); - for (LanguageCache cache : languages().values()) { + for (LanguageCache cache : languages(null).values()) { for (String mime : cache.getMimeTypes()) { mimes.put(mime, cache); } @@ -189,20 +210,18 @@ private static Map createMimes() { return mimes; } - static Map languages() { + static Map languages(ClassLoader additionalLoader) { if (TruffleOptions.AOT) { return nativeImageCache; } - Map cache = runtimeCache; - if (cache == null) { - synchronized (LanguageCache.class) { - cache = runtimeCache; - if (cache == null) { - runtimeCache = cache = createLanguages(null); - } + synchronized (LanguageCache.class) { + Map cache = runtimeCaches.get(additionalLoader); + if (cache == null) { + cache = createLanguages(additionalLoader); + runtimeCaches.put(additionalLoader, cache); } + return cache; } - return cache; } private static Map createLanguages(ClassLoader additionalLoader) { @@ -274,16 +293,16 @@ private static void collectLanguages(ClassLoader loader, List lis * The previous implementation used a `URL.getPath()`, but OS Windows is * offended by leading slash and maybe other irrelevant characters. Therefore, * for JDK 1.7+ a preferred way to go is URL -> URI -> Path. - * + * * Also, Paths are more strict than Files and URLs, so we can't create an * invalid Path from a random string like "/C:/". This leads us to the * `URISyntaxException` for URL -> URI conversion and * `java.nio.file.InvalidPathException` for URI -> Path conversion. - * + * * For fixing further bugs at this point, please read * http://tools.ietf.org/html/rfc1738 http://tools.ietf.org/html/rfc2396 * (supersedes rfc1738) http://tools.ietf.org/html/rfc3986 (supersedes rfc2396) - * + * * http://url.spec.whatwg.org/ does not contain URI interpretation. When you * call `URI.toASCIIString()` all reserved and non-ASCII characters are * percent-quoted. @@ -291,7 +310,12 @@ private static void collectLanguages(ClassLoader loader, List lis try { Path path; path = Paths.get(((JarURLConnection) connection).getJarFileURL().toURI()); - languageHome = path.getParent().toString(); + Path parent = path.getParent(); + if (parent == null) { + languageHome = null; + } else { + languageHome = parent.toString(); + } } catch (URISyntaxException e) { assert false : "Could not resolve path."; } @@ -404,6 +428,14 @@ ContextPolicy getPolicy() { return policy; } + Collection getServices() { + return services; + } + + boolean supportsService(Class clazz) { + return services.contains(clazz.getName()) || services.contains(clazz.getCanonicalName()); + } + @SuppressWarnings("unchecked") private void initializeLanguageClass() { if (languageClass == null) { @@ -430,11 +462,15 @@ private void initializeLanguageClass() { @Override public String toString() { - return "LanguageCache [id=" + id + ", name=" + name + ", implementationName=" + implementationName + ", version=" + version + ", className=" + className + "]"; + return "LanguageCache [id=" + id + ", name=" + name + ", implementationName=" + implementationName + ", version=" + version + ", className=" + className + ", services=" + services + "]"; } static void resetNativeImageCacheLanguageHomes() { - for (LanguageCache languageCache : languages().values()) { + for (LanguageCache languageCache : languages(null).values()) { + languageCache.languageHome = null; + } + final ClassLoader loader = Thread.currentThread().getContextClassLoader(); + for (LanguageCache languageCache : languages(loader).values()) { languageCache.languageHome = null; } } diff --git a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotContextImpl.java b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotContextImpl.java index 07e9d1521912..20160837c76c 100644 --- a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotContextImpl.java +++ b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotContextImpl.java @@ -58,6 +58,7 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.locks.ReentrantLock; import org.graalvm.polyglot.Context; import org.graalvm.polyglot.Engine; @@ -130,11 +131,13 @@ static boolean isSingleContextAssumptionValid() { * closed state. */ volatile boolean cancelling; - private volatile Thread closingThread; + volatile Thread closingThread; + private final ReentrantLock closingLock = new ReentrantLock(); /* * If the context is closed all operations should fail with IllegalStateException. */ volatile boolean closed; + volatile boolean disposing; final PolyglotEngineImpl engine; @CompilationFinal(dimensions = 1) final PolyglotLanguageContext[] contexts; @@ -903,120 +906,139 @@ synchronized void notifyThreadClosed() { } boolean closeImpl(boolean cancelIfExecuting, boolean waitForPolyglotThreads) { - boolean success = false; - Thread[] remainingThreads = null; - PolyglotContextImpl[] childrenToClose = null; - try { + /* + * As a first step we prepare for close by waiting for other threads to finish closing and + * checking whether other threads are still executing. This block performs the following + * checks: + * + * 1) The close was already performed on another thread -> return true + * + * 2) The close is currently already being performed on this thread -> return true + * + * 3) The close was not yet performed but other threads are still executing -> mark current + * thread as cancelled and return false + * + * 4) The close was not yet performed and no thread is executing -> perform close + */ + boolean waitForClose = false; + while (true) { + if (waitForClose) { + closingLock.lock(); + closingLock.unlock(); + waitForClose = false; + } synchronized (this) { - if (!closed) { - PolyglotThreadInfo threadInfo = getCurrentThreadInfo(); - - // triggers a thread changed event which requires slow path enter - setCachedThreadInfo(PolyglotThreadInfo.NULL); - - if (!threadInfo.explicitContextStack.isEmpty()) { - throw new IllegalStateException("The context is explicitely entered on the current thread. Call leave() before closing the context to resolve this."); + if (closed) { + // already cancelled + return true; + } + Thread localClosingThread = closingThread; + if (localClosingThread != null) { + if (localClosingThread == Thread.currentThread()) { + // currently canceling recursively -> just complete + return true; + } else { + // currently canceling on another thread -> wait for other thread to + // complete closing + waitForClose = true; + continue; } + } + PolyglotThreadInfo threadInfo = getCurrentThreadInfo(); - childrenToClose = childContexts.toArray(new PolyglotContextImpl[childContexts.size()]); + // triggers a thread changed event which requires slow path enter + setCachedThreadInfo(PolyglotThreadInfo.NULL); - if (cancelIfExecuting) { - cancelling = true; - if (threadInfo != PolyglotThreadInfo.NULL) { - threadInfo.cancelled = true; - // clear interrupted status after closingThread - // needed because we interrupt when closingThread from another thread. - Thread.interrupted(); - } - } + if (!threadInfo.explicitContextStack.isEmpty()) { + throw new IllegalStateException("The context is explicitely entered on the current thread. Call leave() before closing the context to resolve this."); + } - if (hasActiveOtherThread(waitForPolyglotThreads)) { - /* - * We are not done executing, cannot close yet. - */ - return false; + if (cancelIfExecuting) { + cancelling = true; + if (threadInfo != PolyglotThreadInfo.NULL) { + threadInfo.cancelled = true; + // clear interrupted status after closingThread + // needed because we interrupt when closingThread from another thread. + Thread.interrupted(); } - closingThread = Thread.currentThread(); } + + if (hasActiveOtherThread(waitForPolyglotThreads)) { + /* + * We are not done executing, cannot close yet. + */ + return false; + } + closingThread = Thread.currentThread(); + closingLock.lock(); + break; } - if (childrenToClose != null) { - Object prev = enter(); - try { - for (PolyglotContextImpl childContext : childrenToClose) { - childContext.closeImpl(cancelIfExecuting, waitForPolyglotThreads); - } + } - // we need to run finalization at least twice in case a finalization run has - // initialized a new contexts - boolean finalizationPerformed; - do { - finalizationPerformed = false; - // inverse context order is already the right order for context - // disposal/finalization - for (int i = contexts.length - 1; i >= 0; i--) { - PolyglotLanguageContext context = contexts[i]; - if (context.isInitialized()) { - try { - finalizationPerformed |= context.finalizeContext(); - } catch (Exception | Error ex) { - throw PolyglotImpl.wrapGuestException(context, ex); - } - } - } - } while (finalizationPerformed); + /* + * If we reach here then we can continue with the close. This means that no other concurrent + * close is running and no other thread is currently executing. Note that only the context + * and closing lock should be acquired in this area to avoid deadlocks. + */ + Thread[] remainingThreads = null; + List disposedContexts = null; + boolean success = false; + try { + assert closingThread == Thread.currentThread(); + assert closingLock.isHeldByCurrentThread() : "lock is acquired"; + assert !closed; + Object prev = enter(); + try { + closeChildContexts(cancelIfExecuting, waitForPolyglotThreads); - // finalization performed commit close -> no actions allowed on dispose - closed = true; + finalizeContext(); - List disposedContexts = new ArrayList<>(contexts.length); - try { - synchronized (this) { - for (int i = contexts.length - 1; i >= 0; i--) { - PolyglotLanguageContext context = contexts[i]; - try { - boolean disposed = context.dispose(); - if (disposed) { - disposedContexts.add(context); - } - } catch (Exception | Error ex) { - throw PolyglotImpl.wrapGuestException(context, ex); - } - } - } - } finally { - for (PolyglotLanguageContext context : disposedContexts) { - context.notifyDisposed(); - } - } - assert childContexts.isEmpty(); - success = true; - } finally { + // finalization performed commit close -> no reinitialization allowed + + disposedContexts = disposeContext(); + + assert childContexts.isEmpty(); + success = true; + } finally { + synchronized (this) { leave(prev); if (success) { - if (parent != null) { - synchronized (parent) { - parent.childContexts.remove(this); - } - } else { - engine.removeContext(this); - } - synchronized (this) { - remainingThreads = threads.keySet().toArray(new Thread[0]); - } + remainingThreads = threads.keySet().toArray(new Thread[0]); } - closed = success; + cancelling = false; if (success) { - if (engine.boundEngine) { - disposeStaticContext(this); - } + closed = true; } - cancelling = false; + // triggers a thread changed event which requires slow path enter + setCachedThreadInfo(PolyglotThreadInfo.NULL); + } + if (success && engine.boundEngine) { + disposeStaticContext(this); } } } finally { closingThread = null; + closingLock.unlock(); + } + + /* + * No longer any lock is held. So we can acquire other locks to cleanup. + */ + if (disposedContexts != null) { + for (PolyglotLanguageContext context : disposedContexts) { + context.notifyDisposed(); + } } + if (success) { + if (parent != null) { + synchronized (parent) { + parent.childContexts.remove(this); + } + } else { + engine.removeContext(this); + } + for (Thread thread : remainingThreads) { VMAccessor.INSTRUMENT.notifyThreadFinished(engine, truffleContext, thread); } @@ -1033,6 +1055,61 @@ boolean closeImpl(boolean cancelIfExecuting, boolean waitForPolyglotThreads) { return true; } + private void closeChildContexts(boolean cancelIfExecuting, boolean waitForPolyglotThreads) { + PolyglotContextImpl[] childrenToClose; + synchronized (this) { + childrenToClose = childContexts.toArray(new PolyglotContextImpl[childContexts.size()]); + } + for (PolyglotContextImpl childContext : childrenToClose) { + childContext.closeImpl(cancelIfExecuting, waitForPolyglotThreads); + } + } + + private List disposeContext() { + assert !this.disposing; + this.disposing = true; + try { + List disposedContexts = new ArrayList<>(contexts.length); + synchronized (this) { + for (int i = contexts.length - 1; i >= 0; i--) { + PolyglotLanguageContext context = contexts[i]; + try { + boolean disposed = context.dispose(); + if (disposed) { + disposedContexts.add(context); + } + } catch (Exception | Error ex) { + throw PolyglotImpl.wrapGuestException(context, ex); + } + } + } + return disposedContexts; + } finally { + this.disposing = false; + } + } + + private void finalizeContext() { + // we need to run finalization at least twice in case a finalization run has + // initialized a new contexts + boolean finalizationPerformed; + do { + finalizationPerformed = false; + // inverse context order is already the right order for context + // disposal/finalization + for (int i = contexts.length - 1; i >= 0; i--) { + PolyglotLanguageContext context = contexts[i]; + if (context.isInitialized()) { + try { + finalizationPerformed |= context.finalizeContext(); + } catch (Exception | Error ex) { + throw PolyglotImpl.wrapGuestException(context, ex); + } + } + } + } while (finalizationPerformed); + } + synchronized void sendInterrupt() { if (!cancelling) { return; diff --git a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotEngineImpl.java b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotEngineImpl.java index 1b2139aa4b36..2615f997969f 100644 --- a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotEngineImpl.java +++ b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotEngineImpl.java @@ -458,7 +458,8 @@ PolyglotLanguage findLanguage(String languageId, String mimeType, boolean failIf private Map initializeInstruments(Map infos) { Map instruments = new LinkedHashMap<>(); - List cachedInstruments = InstrumentCache.load(VMAccessor.allLoaders()); + ClassLoader loader = getAPIAccess().useContextClassLoader() ? Thread.currentThread().getContextClassLoader() : null; + List cachedInstruments = InstrumentCache.load(VMAccessor.allLoaders(), loader); for (InstrumentCache instrumentCache : cachedInstruments) { PolyglotInstrument instrumentImpl = new PolyglotInstrument(this, instrumentCache); instrumentImpl.info = LANGUAGE.createInstrument(instrumentImpl, instrumentCache.getId(), instrumentCache.getName(), instrumentCache.getVersion()); @@ -480,7 +481,7 @@ private Map initializeLanguages(Map polyglotLanguages = new LinkedHashMap<>(); Map cachedLanguages = new HashMap<>(); List sortedLanguages = new ArrayList<>(); - for (LanguageCache lang : LanguageCache.languages().values()) { + for (LanguageCache lang : languages().values()) { String id = lang.getId(); if (!cachedLanguages.containsKey(id)) { sortedLanguages.add(lang); @@ -530,6 +531,12 @@ private Map initializeLanguages(Map languages() { + ClassLoader loader = getAPIAccess().useContextClassLoader() ? Thread.currentThread().getContextClassLoader() : null; + final Map languages = LanguageCache.languages(loader); + return languages; + } + private void visitLanguage(Map initErrors, Map cachedLanguages, LinkedHashSet serializedLanguages, LanguageCache language) { visitLanguageImpl(new HashSet<>(), initErrors, cachedLanguages, serializedLanguages, language); @@ -719,12 +726,17 @@ synchronized void ensureClosed(boolean cancelIfExecuting, boolean ignoreCloseFai * Check ahead of time for open contexts to fail early and avoid closing only some * contexts. */ - if (!cancelIfExecuting && !ignoreCloseFailure) { + boolean stillRunning = false; + if (!cancelIfExecuting) { for (PolyglotContextImpl context : localContexts) { synchronized (context) { - if (context.hasActiveOtherThread(false)) { - throw new IllegalStateException(String.format("One of the context instances is currently executing. " + - "Set cancelIfExecuting to true to stop the execution on this thread.")); + if (context.hasActiveOtherThread(false) && context.closingThread == null) { + if (!ignoreCloseFailure) { + throw new IllegalStateException(String.format("One of the context instances is currently executing. " + + "Set cancelIfExecuting to true to stop the execution on this thread.")); + } else { + stillRunning = true; + } } } } @@ -732,13 +744,19 @@ synchronized void ensureClosed(boolean cancelIfExecuting, boolean ignoreCloseFai for (PolyglotContextImpl context : localContexts) { try { boolean closeCompleted = context.closeImpl(cancelIfExecuting, cancelIfExecuting); - if (!closeCompleted && !cancelIfExecuting && !ignoreCloseFailure) { - throw new IllegalStateException(String.format("One of the context instances is currently executing. " + - "Set cancelIfExecuting to true to stop the execution on this thread.")); + if (!closeCompleted && !cancelIfExecuting) { + if (!ignoreCloseFailure) { + throw new IllegalStateException(String.format("One of the context instances is currently executing. " + + "Set cancelIfExecuting to true to stop the execution on this thread.")); + } else { + stillRunning = true; + } } } catch (Throwable e) { if (!ignoreCloseFailure) { throw e; + } else { + stillRunning = true; } } } @@ -746,13 +764,19 @@ synchronized void ensureClosed(boolean cancelIfExecuting, boolean ignoreCloseFai getCancelHandler().waitForClosing(localContexts); } - if (!boundEngine) { + if (!boundEngine && !stillRunning) { for (PolyglotContextImpl context : localContexts) { PolyglotContextImpl.disposeStaticContext(context); } } - contexts.clear(); + // don't commit changes to contexts if still running + if (!stillRunning) { + contexts.clear(); + } + + // instruments should be shut-down even if they are currently still executed + // we want to see instrument output if the process is quit while executing. for (PolyglotInstrument instrumentImpl : idToInstrument.values()) { try { instrumentImpl.notifyClosing(); @@ -771,11 +795,15 @@ synchronized void ensureClosed(boolean cancelIfExecuting, boolean ignoreCloseFai } } } - if (logHandler != null) { - logHandler.close(); + // don't commit to the close if still running as this might cause races in the executing + // context. + if (!stillRunning) { + if (logHandler != null) { + logHandler.close(); + } + ENGINES.remove(this); + closed = true; } - ENGINES.remove(this); - closed = true; } } @@ -874,7 +902,10 @@ static void resetPreInitializedEngine() { private static final class PolyglotShutDownHook implements Runnable { public void run() { - PolyglotEngineImpl[] engines = ENGINES.keySet().toArray(new PolyglotEngineImpl[0]); + PolyglotEngineImpl[] engines; + synchronized (ENGINES) { + engines = ENGINES.keySet().toArray(new PolyglotEngineImpl[0]); + } for (PolyglotEngineImpl engine : engines) { if (DEBUG_MISSING_CLOSE) { PrintStream out = System.out; @@ -897,7 +928,6 @@ public void run() { engine.ensureClosed(false, true); } } - ENGINES.keySet().removeAll(Arrays.asList(engines)); } } diff --git a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotEngineOptions.java b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotEngineOptions.java index d30b377200d6..fd80a580f715 100644 --- a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotEngineOptions.java +++ b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotEngineOptions.java @@ -57,6 +57,6 @@ final class PolyglotEngineOptions { * When the option is set the exceptions thrown by instruments are propagated rather than logged * into err. */ - @Option(name = INSTRUMENT_EXCEPTIONS_ARE_THROWN_NAME, category = OptionCategory.DEBUG, help = "Propagates exceptions thrown by instruments.") static final OptionKey InstrumentExceptionsAreThrown = new OptionKey<>( + @Option(name = INSTRUMENT_EXCEPTIONS_ARE_THROWN_NAME, category = OptionCategory.INTERNAL, help = "Propagates exceptions thrown by instruments.") static final OptionKey InstrumentExceptionsAreThrown = new OptionKey<>( false); } diff --git a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotExceptionImpl.java b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotExceptionImpl.java index f3a6cb6d0983..cff0fa0e5565 100644 --- a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotExceptionImpl.java +++ b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotExceptionImpl.java @@ -63,6 +63,7 @@ import org.graalvm.polyglot.proxy.Proxy; import com.oracle.truffle.api.TruffleException; +import com.oracle.truffle.api.TruffleStackTrace; import com.oracle.truffle.api.TruffleStackTraceElement; final class PolyglotExceptionImpl extends AbstractExceptionImpl implements com.oracle.truffle.polyglot.PolyglotImpl.VMObject { @@ -107,7 +108,7 @@ private PolyglotExceptionImpl(AbstractPolyglotImpl impl, PolyglotEngineImpl engi this.engine = engine; this.context = (languageContext != null) ? languageContext.context : null; this.exception = original; - this.guestFrames = TruffleStackTraceElement.getStackTrace(original); + this.guestFrames = TruffleStackTrace.getStacktrace(original); if (exception instanceof TruffleException) { TruffleException truffleException = (TruffleException) exception; diff --git a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotIllegalStateException.java b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotIllegalStateException.java index 7bae2516a414..e6c6044fc90f 100644 --- a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotIllegalStateException.java +++ b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotIllegalStateException.java @@ -40,6 +40,9 @@ */ package com.oracle.truffle.polyglot; +/** + * Specialized {@link IllegalStateException} which is thrown directly to embedder. + */ @SuppressWarnings("serial") class PolyglotIllegalStateException extends IllegalStateException { diff --git a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotImpl.java b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotImpl.java index f36b094224cb..a5bf2f90609e 100644 --- a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotImpl.java +++ b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotImpl.java @@ -993,7 +993,7 @@ public Set getValidMimeTypes(String language) { if (language == null) { return LanguageCache.languageMimes().keySet(); } else { - LanguageCache lang = LanguageCache.languages().get(language); + LanguageCache lang = LanguageCache.languages(null).get(language); if (lang != null) { return lang.getMimeTypes(); } else { @@ -1004,7 +1004,7 @@ public Set getValidMimeTypes(String language) { @Override public boolean isCharacterBasedSource(String language, String mimeType) { - LanguageCache cache = LanguageCache.languages().get(language); + LanguageCache cache = LanguageCache.languages(null).get(language); if (cache == null) { return true; } @@ -1040,5 +1040,15 @@ public boolean isHostSymbol(Object obj) { return HostObject.isStaticClass(obj); } + @Override + public S lookupService(Object languageContextVMObject, LanguageInfo language, LanguageInfo accessingLanguage, Class type) { + PolyglotLanguage lang = (PolyglotLanguage) NODES.getEngineObject(language); + if (!lang.cache.supportsService(type)) { + return null; + } + PolyglotLanguageContext context = ((PolyglotLanguageContext) languageContextVMObject).context.getContext(lang); + context.ensureCreated((PolyglotLanguage) NODES.getEngineObject(accessingLanguage)); + return context.lookupService(type); + } } } diff --git a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotLanguageContext.java b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotLanguageContext.java index e2c1f11adae6..686aa7db901d 100644 --- a/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotLanguageContext.java +++ b/truffle/src/com.oracle.truffle.polyglot/src/com/oracle/truffle/polyglot/PolyglotLanguageContext.java @@ -65,7 +65,11 @@ import com.oracle.truffle.api.TruffleLogger; import com.oracle.truffle.api.interop.TruffleObject; import com.oracle.truffle.api.nodes.ExplodeLoop; +import com.oracle.truffle.api.nodes.LanguageInfo; import com.oracle.truffle.api.source.Source; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; final class PolyglotLanguageContext implements PolyglotImpl.VMObject { @@ -104,6 +108,7 @@ final class Lazy { @CompilationFinal private volatile Lazy lazy; @CompilationFinal volatile Env env; // effectively final + @CompilationFinal private volatile List languageServices = Collections.emptyList(); PolyglotLanguageContext(PolyglotContextImpl context, PolyglotLanguage language) { this.context = context; @@ -223,7 +228,6 @@ boolean dispose() { LANGUAGE.disposeThread(localEnv, threadInfo.thread); } LANGUAGE.dispose(localEnv); - language.freeInstance(lazy.languageInstance); return true; } return false; @@ -233,6 +237,7 @@ void notifyDisposed() { if (eventsEnabled) { VMAccessor.INSTRUMENT.notifyLanguageContextDisposed(context.engine, context.truffleContext, language.info); } + language.freeInstance(lazy.languageInstance); } Object enterThread(PolyglotThread thread) { @@ -266,7 +271,7 @@ void leaveThread(Object prev, PolyglotThread thread) { VMAccessor.INSTRUMENT.notifyThreadFinished(context.engine, context.truffleContext, thread); } - private void ensureCreated(PolyglotLanguage accessingLanguage) { + void ensureCreated(PolyglotLanguage accessingLanguage) { if (creating) { throw new PolyglotIllegalStateException(String.format("Cyclic access to language context for language %s. " + "The context is currently being created.", language.getId())); @@ -298,7 +303,13 @@ private void ensureCreated(PolyglotLanguage accessingLanguage) { assert VMAccessor.LANGUAGE.getLanguage(env) != null; try { - LANGUAGE.createEnvContext(localEnv); + List languageServicesCollector = new ArrayList<>(); + LANGUAGE.createEnvContext(localEnv, languageServicesCollector); + String errorMessage = verifyServices(language.info, languageServicesCollector, language.cache.getServices()); + if (errorMessage != null) { + throw new IllegalStateException(errorMessage); + } + this.languageServices = languageServicesCollector; lang.language.profile.notifyContextCreate(this, localEnv); if (eventsEnabled) { VMAccessor.INSTRUMENT.notifyLanguageContextCreated(context.engine, context.truffleContext, language.info); @@ -323,6 +334,40 @@ private void ensureCreated(PolyglotLanguage accessingLanguage) { } } + private static String verifyServices(LanguageInfo info, List registeredServices, Collection expectedServices) { + for (String expectedService : expectedServices) { + boolean found = false; + for (Object registeredService : registeredServices) { + if (isSubType(registeredService.getClass(), expectedService)) { + found = true; + break; + } + } + if (!found) { + return String.format("Language %s declares service %s but doesn't register it", info.getName(), expectedService); + } + } + return null; + } + + private static boolean isSubType(Class clazz, String serviceClass) { + if (clazz == null) { + return false; + } + if (serviceClass.equals(clazz.getName()) || serviceClass.equals(clazz.getCanonicalName())) { + return true; + } + if (isSubType(clazz.getSuperclass(), serviceClass)) { + return true; + } + for (Class implementedInterface : clazz.getInterfaces()) { + if (isSubType(implementedInterface, serviceClass)) { + return true; + } + } + return false; + } + boolean ensureInitialized(PolyglotLanguage accessingLanguage) { ensureCreated(accessingLanguage); boolean wasInitialized = false; @@ -366,7 +411,7 @@ boolean ensureInitialized(PolyglotLanguage accessingLanguage) { void checkAccess(PolyglotLanguage accessingLanguage) { context.engine.checkState(); - if (context.closed) { + if (context.closed || context.disposing) { throw new PolyglotIllegalStateException("The Context is already closed."); } boolean accessPermitted = language.isHost() || language.cache.isInternal() || context.config.allowedPublicLanguages.contains(language.info.getId()) || @@ -416,6 +461,15 @@ boolean patch(PolyglotContextConfig newConfig) { } } + S lookupService(Class type) { + for (Object languageService : languageServices) { + if (type.isInstance(languageService)) { + return type.cast(languageService); + } + } + return null; + } + static final class ToGuestValuesNode { @CompilationFinal(dimensions = 1) private volatile ToGuestValueNode[] toGuestValue; diff --git a/truffle/src/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/SLSeparatedClassLoadersTest.java b/truffle/src/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/SLSeparatedClassLoadersTest.java new file mode 100644 index 000000000000..8a864ea604f9 --- /dev/null +++ b/truffle/src/com.oracle.truffle.sl.test/src/com/oracle/truffle/sl/test/SLSeparatedClassLoadersTest.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The Universal Permissive License (UPL), Version 1.0 + * + * Subject to the condition set forth below, permission is hereby granted to any + * person obtaining a copy of this software, associated documentation and/or + * data (collectively the "Software"), free of charge and under any and all + * copyright rights in the Software, and any and all patent rights owned or + * freely licensable by each licensor hereunder covering either (i) the + * unmodified Software as contributed to or provided by such licensor, or (ii) + * the Larger Works (as defined below), to deal in both + * + * (a) the Software, and + * + * (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if + * one is included with the Software each a "Larger Work" to which the Software + * is contributed by such licensors), + * + * without restriction, including without limitation the rights to copy, create + * derivative works of, display, perform, and distribute the Software and make, + * use, sell, offer for sale, import, export, have made, and have sold the + * Software and the Larger Work(s), and to sublicense the foregoing rights on + * either these or other terms. + * + * This license is subject to the following condition: + * + * The above copyright notice and either this complete permission notice or at a + * minimum a reference to the UPL must be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.oracle.truffle.sl.test; + +import com.oracle.truffle.api.Truffle; +import com.oracle.truffle.sl.SLLanguage; +import java.net.URL; +import java.net.URLClassLoader; +import java.security.ProtectionDomain; +import java.util.Map; +import org.graalvm.polyglot.Engine; +import org.junit.After; +import static org.junit.Assert.assertNotNull; +import org.junit.Assume; +import org.junit.Before; +import org.junit.Test; + +public class SLSeparatedClassLoadersTest { + private ClassLoader loader; + + @Before + public void storeLoader() { + loader = Thread.currentThread().getContextClassLoader(); + } + + @Test + public void sdkAndTruffleLanguageAPIAndSLInSeparateClassLoaders() throws Exception { + final ProtectionDomain sdkDomain = Engine.class.getProtectionDomain(); + Assume.assumeNotNull(sdkDomain); + Assume.assumeNotNull(sdkDomain.getCodeSource()); + URL sdkURL = sdkDomain.getCodeSource().getLocation(); + Assume.assumeNotNull(sdkURL); + + URL truffleURL = Truffle.class.getProtectionDomain().getCodeSource().getLocation(); + Assume.assumeNotNull(truffleURL); + + URL slURL = SLLanguage.class.getProtectionDomain().getCodeSource().getLocation(); + Assume.assumeNotNull(slURL); + + ClassLoader parent = Engine.class.getClassLoader().getParent(); + + URLClassLoader sdkLoader = new URLClassLoader(new URL[]{sdkURL}, parent); + URLClassLoader truffleLoader = new URLClassLoader(new URL[]{truffleURL}, sdkLoader); + URLClassLoader slLoader = new URLClassLoader(new URL[]{slURL}, truffleLoader); + Thread.currentThread().setContextClassLoader(slLoader); + + Class engineClass = sdkLoader.loadClass(Engine.class.getName()); + Object engine = engineClass.getMethod("create").invoke(null); + assertNotNull("Engine has been created", engine); + + Map languages = (Map) engineClass.getMethod("getLanguages").invoke(engine); + Object lang = languages.get("sl"); + assertNotNull("SL language found: " + languages, lang); + } + + @After + public void resetLoader() { + Thread.currentThread().setContextClassLoader(loader); + } +} diff --git a/truffle/src/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLFileDetector.java b/truffle/src/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLFileDetector.java index d4acd0b513be..74b17fd9d2e2 100644 --- a/truffle/src/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLFileDetector.java +++ b/truffle/src/com.oracle.truffle.sl/src/com/oracle/truffle/sl/SLFileDetector.java @@ -47,7 +47,7 @@ public final class SLFileDetector extends FileTypeDetector { @Override public String probeContentType(Path path) throws IOException { - if (path.getFileName().toString().endsWith(".sl")) { + if (path.toString().endsWith(".sl")) { return SLLanguage.MIME_TYPE; } return null; diff --git a/truffle/src/com.oracle.truffle.tck.instrumentation/src/com/oracle/truffle/tck/instrumentation/VerifierInstrument.java b/truffle/src/com.oracle.truffle.tck.instrumentation/src/com/oracle/truffle/tck/instrumentation/VerifierInstrument.java index 2f15455ec1bc..75d580d64e12 100644 --- a/truffle/src/com.oracle.truffle.tck.instrumentation/src/com/oracle/truffle/tck/instrumentation/VerifierInstrument.java +++ b/truffle/src/com.oracle.truffle.tck.instrumentation/src/com/oracle/truffle/tck/instrumentation/VerifierInstrument.java @@ -80,6 +80,8 @@ public class VerifierInstrument extends TruffleInstrument implements InlineVerif private InlineScriptFactory inlineScriptFactory; private EventBinding inlineBinding; + private static final ThreadLocal ENTERED = new ThreadLocal<>(); + @Override protected void onCreate(Env instrumentEnv) { this.env = instrumentEnv; @@ -97,17 +99,36 @@ public void setInlineSnippet(String languageId, InlineSnippet inlineSnippet, Inl SourceSectionFilter.newBuilder().tagIs(StatementTag.class, CallTag.class).build(), inlineScriptFactory); } else if (inlineBinding != null) { + if (!inlineScriptFactory.snippetExecuted) { + Assert.fail("Inline snippet was not executed."); + } inlineBinding.dispose(); inlineBinding = null; inlineScriptFactory = null; } } + @TruffleBoundary + private static void leave() { + ENTERED.set(Boolean.FALSE); + } + + @TruffleBoundary + private static void enter() { + ENTERED.set(Boolean.TRUE); + } + + @TruffleBoundary + private static boolean isEntered() { + return Boolean.TRUE == ENTERED.get(); + } + private class InlineScriptFactory implements ExecutionEventNodeFactory { private final Source snippet; private final Predicate predicate; private final InlineVerifier.ResultVerifier resultVerifier; + volatile boolean snippetExecuted = false; InlineScriptFactory(String languageId, InlineSnippet inlineSnippet, InlineVerifier.ResultVerifier verifier) { CharSequence code = inlineSnippet.getCode(); @@ -118,11 +139,12 @@ private class InlineScriptFactory implements ExecutionEventNodeFactory { @Override public ExecutionEventNode create(EventContext context) { - if (predicate == null || canRunAt(context.getInstrumentedSourceSection())) { - return new InlineScriptNode(context); - } else { - return null; + if (!isEntered()) { + if (predicate == null || canRunAt(context.getInstrumentedSourceSection())) { + return new InlineScriptNode(context); + } } + return null; } private boolean canRunAt(com.oracle.truffle.api.source.SourceSection ss) { @@ -166,7 +188,9 @@ private void executeSnippet(VirtualFrame frame) { throw t; } insert(inlineNode); + snippetExecuted = true; } + enter(); try { Object ret = inlineNode.execute(frame); if (resultVerifier != null) { @@ -178,6 +202,8 @@ private void executeSnippet(VirtualFrame frame) { CompilerDirectives.transferToInterpreter(); verify(t); throw t; + } finally { + leave(); } } diff --git a/vm/ci_common/common.hocon b/vm/ci_common/common.hocon index f7ae9c009fa7..35f4b777bde9 100644 --- a/vm/ci_common/common.hocon +++ b/vm/ci_common/common.hocon @@ -23,6 +23,11 @@ common_vm_darwin: ${common_vm} ${darwin} { LANG: en_US.UTF-8 MACOSX_DEPLOYMENT_TARGET: "10.11" } + setup: ${common_vm.setup} [ + [set-export, PKG_INCLUDE_FLAGS_OVERRIDE, "-I/cm/shared/apps/pcre/8.38/include -I/cm/shared/apps/bzip2/1.0.6/include -I/cm/shared/apps/xz/5.2.2/include -I/cm/shared/apps/curl/7.50.1/include"] + [set-export, GCC_LIB_PATH, [echo, "${LD_LIBRARY_PATH}", |, tr, "\:", "\\n", |, grep, lib/gcc, |, tail, "-1"]] + [set-export, PKG_LDFLAGS_OVERRIDE, "-L/cm/shared/apps/bzip2/1.0.6/lib -L/cm/shared/apps/xz/5.2.2/lib -L/cm/shared/apps/pcre/8.38/lib -L/cm/shared/apps/curl/7.50.1/lib -L${GCC_LIB_PATH} -L/usr/lib"] + ] } # SULONG @@ -84,14 +89,13 @@ fastr_linux: ${fastr} { fastr_darwin: ${fastr} { packages: { "pcre" : "==8.38" + "homebrew/gcc" : "==4.9" } environment: { # TODO: check these env vars PATH : "/usr/local/bin:$JAVA_HOME/bin:$PATH" F77: "/usr/local/bin/gfortran-4.9" TZDIR: "/usr/share/zoneinfo" - PKG_INCLUDE_FLAGS_OVERRIDE : """-I/cm/shared/apps/pcre/8.38/include -I/cm/shared/apps/bzip2/1.0.6/include -I/cm/shared/apps/xz/5.2.2/include -I/cm/shared/apps/curl/7.50.1/include""" - PKG_LDFLAGS_OVERRIDE : """ -L/cm/shared/apps/bzip2/1.0.6/lib -L/cm/shared/apps/xz/5.2.2/lib -L/cm/shared/apps/pcre/8.38/lib -L/cm/shared/apps/curl/7.50.1/lib -L/cm/shared/apps/gcc/4.9.1/lib64 -L/usr/local/Cellar/gcc@4.9/4.9.4/lib/gcc/4.9/ -L/usr/lib""" } } @@ -146,12 +150,12 @@ deploy_daily_vm_darwin: ${vm_darwin} { targets: [deploy, daily] } -deploy_vm_linux: ${vm_linux} { - targets: [deploy, post-merge] +daily_vm_linux: ${vm_linux} { + targets: [daily] } daily_vm_darwin: ${vm_darwin} { - targets: [deploy, daily] + targets: [daily] } mx_vm_common: [mx, --strip-jars, --no-sources, --env, ${vm_env}] @@ -182,7 +186,7 @@ builds += [ timelimit: "1:30:00" name: deploy-vm-linux-amd64 }, - ${full_vm_build_darwin} ${darwin-deploy} ${daily_vm_darwin} { + ${full_vm_build_darwin} ${darwin-deploy} ${deploy_daily_vm_darwin} { run: [ ${mx_vm_installables} [--dynamicimports, ${vm_extra_suites}, build] ${mx_vm_installables} [--dynamicimports, ${vm_extra_suites}] ${maven_deploy_vm} @@ -191,7 +195,7 @@ builds += [ timelimit: "1:30:00" name: deploy-vm-installable-darwin-amd64 }, - ${full_vm_build_darwin} ${darwin-deploy} ${daily_vm_darwin} { + ${full_vm_build_darwin} ${darwin-deploy} ${deploy_daily_vm_darwin} { run: [ ${mx_vm_common} [build] ${mx_vm_common} ${maven_deploy_vm} @@ -200,6 +204,15 @@ builds += [ timelimit: "1:30:00" name: deploy-vm-base-darwin-amd64 }, + ${svm-common-linux-amd64} ${custom_vm_linux} ${vm_linux} { + run: [ + [mx, --env, ${libgraal_env}, "--extra-image-builder-argument=-ea", "--extra-image-builder-argument=-J-esa", build] + [mx, --env, ${libgraal_env}, gate, --task, LibGraal] + ] + timelimit: "35:00" + targets: [post-merge] + name: postmerge-vm-libgraal + }, ${svm-common-linux-amd64} ${custom_vm_linux} ${gate_vm_linux} { run: [ [export, "SVM_SUITE="${svm_suite}] diff --git a/vm/ci_includes/vm.hocon b/vm/ci_includes/vm.hocon index 0f497b7a9b9c..75aa58948af8 100644 --- a/vm/ci_includes/vm.hocon +++ b/vm/ci_includes/vm.hocon @@ -1,7 +1,6 @@ -vm_subdir: "vm" -vm_env: "ce" +vm_env: ce vm_java: ${openjdk8} -svm_suite: "/substratevm" +svm_suite: /substratevm vm_extra_suites: "truffleruby,graalpython,fastr" custom_vm_linux: {} custom_vm_darwin: {} diff --git a/vm/mx.vm/gu b/vm/mx.vm/gu index 7714b23ca5d4..9de7651809c1 100755 --- a/vm/mx.vm/gu +++ b/vm/mx.vm/gu @@ -11,7 +11,8 @@ while [ -h "$source" ] ; do fi done bin_dir="$( cd -P "$( dirname "$source" )" && pwd )" -installer_jar="${bin_dir}/../installer.jar" +lib_dir=${bin_dir}/.. +installer_jar="${lib_dir}/installer.jar" root_dir="${bin_dir}/../../../.." java_exe="${root_dir}/bin/java" @@ -28,5 +29,13 @@ do esac done - -exec "${java_exe}" "${JAVA_ARGS[@]}" "-DGRAAL_HOME=${root_dir}" -jar "${installer_jar}" "${PROGRAM_ARGS[@]}" +libs="" +# Add possible drop-in extensions +for x in ${lib_dir}/*.jar ; do + if [ -z "$libs" ]; then + libs="$x" + else + libs="${libs}:$x" + fi +done +exec "${java_exe}" "${JAVA_ARGS[@]}" "-DGRAAL_HOME=${root_dir}" -cp "${libs}" org.graalvm.component.installer.ComponentInstaller "${PROGRAM_ARGS[@]}" diff --git a/vm/mx.vm/libgraal b/vm/mx.vm/libgraal new file mode 100644 index 000000000000..2338cc1911d8 --- /dev/null +++ b/vm/mx.vm/libgraal @@ -0,0 +1,4 @@ +DYNAMIC_IMPORTS=/substratevm +DISABLE_LIBPOLYGLOT=true +FORCE_BASH_LAUNCHERS=true +LIBGRAAL=true diff --git a/vm/mx.vm/mx_vm.py b/vm/mx.vm/mx_vm.py index 5cf87c96b40b..7e50a80ff57e 100644 --- a/vm/mx.vm/mx_vm.py +++ b/vm/mx.vm/mx_vm.py @@ -78,7 +78,8 @@ anyjdk_version_regex = re.compile(r'(openjdk|java) version \"(?P[0-9a-z_\-.]+)\".*\n(OpenJDK|Java\(TM\) SE) Runtime Environment [ 0-9.]*\(build [0-9a-z_\-.+]+\)') openjdk_version_regex = re.compile(r'openjdk version \"(?P[0-9a-z_\-.]+)\".*\nOpenJDK Runtime Environment [ 0-9.]*\(build [0-9a-z_\-.+]+\)') -graalvm_version_regex = re.compile(r'.*\n.*\nGraalVM (?P[0-9a-z_\-.+]+) \(build [0-9a-z\-.+]+, mixed mode\)') +graalvm_version_regex = re.compile(r'.*\n.*\n[a-zA-Z() ]+GraalVM[a-zA-Z ]+(?P[0-9a-z_\-.+]+) \(build [0-9a-z\-.+]+, mixed mode\)') + class BaseGraalVmLayoutDistribution(mx.LayoutDistribution): __metaclass__ = ABCMeta @@ -214,13 +215,14 @@ def _patch_darwin_jdk(): # Add vm.properties # Add TRUFFLE_NFI_NATIVE (TODO: should be part of an other component?) + vm_name = graalvm_vm_name(self, join(_jdk_dir, _src_jdk_base)) if mx.get_os() == 'darwin': # on macOS the directory is not used _add(layout, "/jre/lib/", "extracted-dependency:truffle:TRUFFLE_NFI_NATIVE/bin/") - _add(layout, "/jre/lib/server/vm.properties", "string:name=GraalVM ") + _add(layout, "/jre/lib/server/vm.properties", "string:name=" + vm_name) else: _add(layout, "/jre/lib//", "extracted-dependency:truffle:TRUFFLE_NFI_NATIVE/bin/") - _add(layout, "/jre/lib//server/vm.properties", "string:name=GraalVM ") + _add(layout, "/jre/lib//server/vm.properties", "string:name=" + vm_name) # Add Polyglot launcher if with_polyglot_launcher: @@ -290,6 +292,10 @@ def _add_link(_dest, _target): _launcher_dest = _component_base + _launcher_config.destination # add `LauncherConfig.destination` to the layout _add(layout, _launcher_dest, 'dependency:' + GraalVmLauncher.launcher_project_name(_launcher_config, stage1), _component) + if _debug_images() and GraalVmLauncher.is_launcher_native(_launcher_config, stage1) and GraalVmNativeImage.is_svm_debug_supported(): + _add(layout, dirname(_launcher_dest) + '/', 'dependency:' + GraalVmLauncher.launcher_project_name(_launcher_config, stage1) + '/*.debug', _component) + if _include_sources(): + _add(layout, dirname(_launcher_dest) + '/', 'dependency:' + GraalVmLauncher.launcher_project_name(_launcher_config, stage1) + '/sources', _component) # add links from jre/bin to launcher _add_link(_jdk_jre_bin, _launcher_dest) _jre_bin_names.append(basename(_launcher_dest)) @@ -300,7 +306,17 @@ def _add_link(_dest, _target): # add links from jre/bin to component link _add_link(_jdk_jre_bin, _link_dest) _jre_bin_names.append(basename(_link_dest)) - + for _library_config in _get_library_configs(_component): + _add(layout, '/jre/lib/graalvm/', ['dependency:' + d for d in _library_config.jar_distributions], _component, with_sources=True) + if not stage1: + if _library_config.jvm_library: + assert isinstance(_component, (mx_sdk.GraalVmJdkComponent, mx_sdk.GraalVmJreComponent)) + _library_dest = _component_base if mx.get_os() == 'darwin' else (_component_base + mx.get_arch() + '/') + else: + _library_dest = _component_base + _library_dest += _library_config.destination + # add `LibraryConfig.destination` to the layout + _add(layout, _library_dest, 'dependency:' + GraalVmNativeImage.project_name(_library_config), _component) for _provided_executable in _component.provided_executables: if _component.short_name is 'vvm': _add(layout, _jdk_jre_bin, 'extracted-dependency:tools:VISUALVM_PLATFORM_SPECIFIC/./' + _provided_executable) @@ -744,10 +760,35 @@ def __init__(self, suite, name, deps, workingSets, native_image_config, theLicen def getArchivableResults(self, use_relpath=True, single=False): yield self.output_file(), self.native_image_name + if not single: + debug = self.debug_file() + if debug: + yield debug, basename(debug) + src_dir = self.image_sources_dir() + if exists(src_dir): + logical_root = dirname(src_dir) + for root, _, files in os.walk(src_dir): + for name in files: + yield join(root, name), join(relpath(root, logical_root), name) + + def debug_file(self): + if not self.is_native(): + return None + if GraalVmNativeImage.is_svm_debug_supported(): + return join(self.get_output_base(), self.name, self.native_image_name + '.debug') + return None + + @staticmethod + def is_svm_debug_supported(): + svm_support = _get_svm_support() + return svm_support.is_supported() and svm_support.is_debug_supported() def output_file(self): return join(self.get_output_base(), self.name, self.native_image_name) + def image_sources_dir(self): + return join(self.get_output_base(), self.name, "sources") + def build_args(self): return [mx_subst.string_substitutions.substitute(arg) for arg in self.native_image_config.build_args] @@ -780,7 +821,7 @@ def getBuildTask(self, args): return GraalVmBashLauncherBuildTask(self, args) def is_native(self): - return _get_svm_support().is_supported() and not _force_bash_launchers(self.native_image_config, self.stage1 or None) + return GraalVmLauncher.is_launcher_native(self.native_image_config, self.stage1) def output_file(self): return join(self.get_output_base(), self.name, self.native_image_name) @@ -793,9 +834,13 @@ def get_containing_graalvm(self): @staticmethod def launcher_project_name(native_image_config, stage1=False): - is_bash = not _get_svm_support().is_supported() or _force_bash_launchers(native_image_config, stage1 or None) + is_bash = not GraalVmLauncher.is_launcher_native(native_image_config, stage1) return GraalVmNativeImage.project_name(native_image_config) + ("-bash" if is_bash else "") + ("-stage1" if stage1 else "") + @staticmethod + def is_launcher_native(native_image_config, stage1=False): + return _get_svm_support().is_supported() and not _force_bash_launchers(native_image_config, stage1 or None) + class GraalVmPolyglotLauncher(GraalVmLauncher): def __init__(self, suite, deps, workingSets, launcherConfig, **kw_args): @@ -849,6 +894,7 @@ def getArchivableResults(self, use_relpath=True, single=False): yield absolute_path, e + class GraalVmMiscLauncher(GraalVmLauncher): def __init__(self, native_image_config, stage1=False, **kw_args): super(GraalVmMiscLauncher, self).__init__(_suite, GraalVmLauncher.launcher_project_name(native_image_config, stage1=stage1), [], None, native_image_config, stage1=stage1, **kw_args) @@ -1329,7 +1375,7 @@ def get_stage1_graalvm_distribution(): """:rtype: GraalVmLayoutDistribution""" global _stage1_graalvm_distribution if _stage1_graalvm_distribution == 'uninitialized': - _stage1_graalvm_distribution = GraalVmLayoutDistribution("GraalVM", _base_graalvm_layout, stage1=True) + _stage1_graalvm_distribution = GraalVmLayoutDistribution('GraalVM', _base_graalvm_layout, stage1=True) _stage1_graalvm_distribution.description = "GraalVM distribution (stage1)" _stage1_graalvm_distribution.maven = False return _stage1_graalvm_distribution @@ -1339,7 +1385,7 @@ def get_final_graalvm_distribution(): """:rtype: GraalVmLayoutDistribution""" global _final_graalvm_distribution if _final_graalvm_distribution == 'uninitialized': - _final_graalvm_distribution = GraalVmLayoutDistribution("GraalVM", _base_graalvm_layout) + _final_graalvm_distribution = GraalVmLayoutDistribution('GraalVM', _base_graalvm_layout) _final_graalvm_distribution.description = "GraalVM distribution" _final_graalvm_distribution.maven = True return _final_graalvm_distribution @@ -1436,28 +1482,37 @@ def register_vm_config(config_name, components): _vm_configs[config_name] = components -_launcher_configs = None +_native_image_configs = {} def _get_launcher_configs(component): """ :rtype : list[mx_sdk.LauncherConfig]""" - global _launcher_configs - if _launcher_configs is None: - launchers = {} + return _get_native_image_configs(component, 'launcher_configs') + + +def _get_library_configs(component): + """ :rtype : list[mx_sdk.LibraryConfig]""" + return _get_native_image_configs(component, 'library_configs') + + +def _get_native_image_configs(component, config_type): + if _native_image_configs.get(config_type) is None: + new_configs = {} for component_ in mx_sdk.graalvm_components(): - for launcher_config in component_.launcher_configs: - launcher_name = launcher_config.destination - if launcher_name in launchers: - _, prev_component = launchers[launcher_name] + for config in getattr(component_, config_type): + config_name = config.destination + if config_name in new_configs: + _, prev_component = new_configs[config_name] if prev_component.priority > component_.priority: continue if prev_component.priority == component_.priority: - raise mx.abort("Conflicting launchers: {} and {} both declare a launcher called {}".format(component_.name, prev_component.name, launcher_name)) - launchers[launcher_name] = launcher_config, component_ - _launcher_configs = {} - for launcher_config, component_ in launchers.values(): - _launcher_configs.setdefault(component_.name, []).append(launcher_config) - return _launcher_configs.get(component.name, []) + raise mx.abort("Conflicting native-image configs: {} and {} both declare a config called {}".format(component_.name, prev_component.name, config_name)) + new_configs[config_name] = config, component_ + configs = {} + for config, component_ in new_configs.values(): + configs.setdefault(component_.name, []).append(config) + _native_image_configs[config_type] = configs + return _native_image_configs.get(config_type).get(component.name, []) def mx_register_dynamic_suite_constituents(register_project, register_distribution): @@ -1496,6 +1551,9 @@ def mx_register_dynamic_suite_constituents(register_project, register_distributi register_project(launcher_project) if launcher_project.is_native(): needs_stage1 = True + for library_config in _get_library_configs(component): + register_project(GraalVmLibrary(_suite, GraalVmNativeImage.project_name(library_config), [], None, library_config)) + needs_stage1 = True # The JS components have issues ATM since they share the same directory if isinstance(component, mx_sdk.GraalVmLanguage) and not (_disable_installable(component) or component.dir_name == 'js'): installable_component = GraalVmInstallableComponent(component) @@ -1561,6 +1619,15 @@ def has_svm_polyglot_lib(): return _get_svm_support().is_supported() and _with_polyglot_lib_project() +def get_native_image_locations(name, image_name): + libgraal_libs = [l for l in _get_library_configs(get_component(name)) if image_name in basename(l.destination)] + if libgraal_libs: + assert len(libgraal_libs) == 1, "Ambiguous image name '{}' matches '{}'".format(image_name, libgraal_libs) + p = mx.project(GraalVmLibrary.project_name(libgraal_libs[0])) + return p.output_file() + return None + + def get_component(name, fatalIfMissing=False): """ :type name: str @@ -1750,13 +1817,35 @@ def check_versions(jdk_dir, jdk_version_regex, graalvm_version_regex, expect_gra match = graalvm_version_regex.match(out) if expect_graalvm and match is None: - mx.abort("'{}' is not a GraalVM. Its version string:\n{}\ndoes not match:\n{}").format(jdk_dir, out, graalvm_version_regex.pattern) + mx.abort("'{}' is not a GraalVM. Its version string:\n{}\ndoes not match:\n{}".format(jdk_dir, out, graalvm_version_regex.pattern)) elif expect_graalvm and match.group('graalvm_version') != _suite.release_version(): mx.abort("'{}' has a wrong GraalVM version:\n{}\nexpected:\n{}".format(match.group('graalvm_version'), _suite.release_version())) elif not expect_graalvm and match: mx.abort("GraalVM cannot be built using a GraalVM as base-JDK ('{}').\n{}.".format(jdk_dir, check_env)) +def log_graalvm_vm_name(args): + """Print the VM name of GraalVM""" + parser = ArgumentParser(prog='mx graalvm-vm-name', description='Print the VM name of GraalVM') + _ = parser.parse_args(args) + jdk_base, jdk_dir = _get_jdk_dir() + mx.log(graalvm_vm_name(get_final_graalvm_distribution(), join(jdk_dir, jdk_base))) + + +def graalvm_vm_name(graalvm_dist, jdk_home): + """ + :type jdk_home: str + :rtype str: + """ + java = join(jdk_home, 'bin', 'java') + out = subprocess.check_output([java, '-version'], stderr=subprocess.STDOUT).rstrip() + match = re.search(r'^(?P[a-zA-Z() ]+)64-Bit Server VM', out.split('\n')[-1]) + vm_name = match.group('base_vm_name') if match else '' + vm_name += '{} {}'.format(graalvm_dist.base_name, graalvm_dist.vm_config_name.upper()) if graalvm_dist.vm_config_name else graalvm_dist.base_name + vm_name += ' {}'.format(graalvm_version()) + return vm_name + + mx_gate.add_gate_runner(_suite, mx_vm_gate.gate_body) mx.add_argument('--disable-libpolyglot', action='store_true', help='Disable the \'polyglot\' library project.') mx.add_argument('--disable-polyglot', action='store_true', help='Disable the \'polyglot\' launcher project.') @@ -1771,6 +1860,7 @@ def check_versions(jdk_dir, jdk_version_regex, graalvm_version_regex, expect_gra register_vm_config('ce', ['cmp', 'gu', 'gvm', 'ins', 'js', 'njs', 'polynative', 'pro', 'rgx', 'slg', 'svm', 'tfl', 'libpoly', 'poly', 'vvm']) register_vm_config('ce-no_native', ['bjs', 'blli', 'bnative-image', 'bpolyglot', 'cmp', 'gu', 'gvm', 'ins', 'js', 'njs', 'polynative', 'pro', 'rgx', 'slg', 'svm', 'tfl', 'poly', 'vvm']) +register_vm_config('libgraal', ['cmp', 'gu', 'gvm', 'lg', 'poly', 'polynative', 'rgx', 'svm', 'tfl', 'bnative-image', 'bpolyglot']) def _debug_images(): @@ -1843,5 +1933,6 @@ def mx_post_parse_cmd_line(args): 'graalvm-version': [log_graalvm_version, ''], 'graalvm-home': [log_graalvm_home, ''], 'graalvm-show': [graalvm_show, ''], + 'graalvm-vm-name': [log_graalvm_vm_name, ''], 'standalone-home': [log_standalone_home, 'comp-dir-name'], }) diff --git a/vm/mx.vm/mx_vm_benchmark.py b/vm/mx.vm/mx_vm_benchmark.py index 80cee65e1356..9a392086038e 100644 --- a/vm/mx.vm/mx_vm_benchmark.py +++ b/vm/mx.vm/mx_vm_benchmark.py @@ -30,6 +30,8 @@ import mx_sdk, mx_vm import os +from os.path import dirname + _suite = mx.suite('vm') _native_image_vm_registry = mx_benchmark.VmRegistry('NativeImage', 'ni-vm') @@ -157,3 +159,11 @@ def register_graalvm_vms(): if mx_vm.has_component('svm', fatalIfMissing=False): _native_image_vm_registry.add_vm(NativeImageVm(graalvm_hostvm_name, 'default', [], []), _suite, 10) _gu_vm_registry.add_vm(GuVm(graalvm_hostvm_name, 'default', [], []), _suite, 10) + # Add VMs for libgraal + if mx_vm.has_component('LibGraal', fatalIfMissing=False): + libgraal_location = mx_vm.get_native_image_locations('LibGraal', 'jvmcicompiler') + if libgraal_location is not None: + import mx_graal_benchmark + mx_graal_benchmark.build_jvmci_vm_variants('server', 'graal-core-libgraal', + ['-server', '-XX:+EnableJVMCI', '-Dgraal.CompilerConfiguration=community', '-Djvmci.Compiler=graal', '-XX:+UseJVMCINativeLibrary', '-XX:JVMCILibPath=' + dirname(libgraal_location)], + mx_graal_benchmark._graal_variants, suite=_suite, priority=15, hosted=False) diff --git a/vm/mx.vm/mx_vm_gate.py b/vm/mx.vm/mx_vm_gate.py index 1087391c8bcd..2f9fbd3494db 100644 --- a/vm/mx.vm/mx_vm_gate.py +++ b/vm/mx.vm/mx_vm_gate.py @@ -28,11 +28,12 @@ import mx import mx_vm import mx_subst +import mx_unittest import functools from mx_gate import Task -from os.path import join, exists +from os.path import join, exists, dirname from contextlib import contextmanager _suite = mx.suite('vm') @@ -51,6 +52,7 @@ class VmGateTasks: graalpython = 'graalpython' integration = 'integration' tools = 'tools' + libgraal = 'libgraal' def gate_body(args, tasks): @@ -86,6 +88,23 @@ def gate_body(args, tasks): if t and mx_vm.has_component('Graal.Python', fatalIfMissing=True): pass + if mx_vm.has_component('LibGraal'): + libgraal_location = mx_vm.get_native_image_locations('LibGraal', 'jvmcicompiler') + if libgraal_location is None: + mx.warn("Skipping libgraal tests: no library enabled in the LibGraal component") + else: + extra_vm_argument = ['-XX:+UseJVMCICompiler', '-XX:+UseJVMCINativeLibrary', '-XX:JVMCILibPath=' + dirname(libgraal_location)] + if args.extra_vm_argument: + extra_vm_argument += args.extra_vm_argument + import mx_compiler + mx_compiler.compiler_gate_benchmark_runner(tasks, extra_vm_argument, prefix='LibGraal: ') + + with Task('Test LibGraal', tasks, tags=[VmGateTasks.libgraal]) as t: + if t: + mx_unittest.unittest(["--suite", "truffle", "--"] + extra_vm_argument + ["-Dgraal.TruffleCompileImmediately=true", "-Dgraal.TruffleBackgroundCompilation=false"]) + else: + mx.warn("Skipping libgraal tests: component not enabled") + gate_substratevm(tasks) gate_sulong(tasks) gate_ruby(tasks) diff --git a/vm/mx.vm/suite.py b/vm/mx.vm/suite.py index a3ae1077cfc5..09dea5daa7ff 100644 --- a/vm/mx.vm/suite.py +++ b/vm/mx.vm/suite.py @@ -3,7 +3,7 @@ "version" : "1.0.0-rc13", "release" : False, "groupId" : "org.graalvm", - "mxversion": "5.197.0", + "mxversion": "5.210.2", "defaultLicense" : "GPLv2-CPE", "imports": { "suites": [ @@ -19,7 +19,7 @@ "name": "graal-nodejs", "subdir": True, "dynamic": True, - "version": "dbb75935234fe8b87993effa05237f402e940746", + "version": "e86eac6062ea74a9b57f6a0a5c25abb1bd3e5711", "urls" : [ {"url" : "https://github.com/graalvm/graaljs.git", "kind" : "git"}, {"url": "https://curio.ssw.jku.at/nexus/content/repositories/snapshots", "kind": "binary"}, @@ -29,7 +29,7 @@ "name": "graal-js", "subdir": True, "dynamic": True, - "version": "dbb75935234fe8b87993effa05237f402e940746", + "version": "e86eac6062ea74a9b57f6a0a5c25abb1bd3e5711", "urls": [ {"url": "https://github.com/graalvm/graaljs.git", "kind" : "git"}, {"url": "https://curio.ssw.jku.at/nexus/content/repositories/snapshots", "kind": "binary"}, @@ -37,7 +37,7 @@ }, { "name": "truffleruby", - "version": "76b12742a34e9e13f77bff3f61027f1cfd33922d", + "version": "dd198f9c34e4e7bb5e1ed599b5d88eafb999b332", "dynamic": True, "urls": [ {"url": "https://github.com/oracle/truffleruby.git", "kind": "git"}, @@ -61,7 +61,7 @@ }, { "name": "fastr", - "version": "5299c62556339ff3a703819214edb48f0977756c", + "version": "6af23b2db13ba09242a46a7a7114a289a048abf5", "dynamic": True, "urls": [ {"url": "https://github.com/oracle/fastr.git", "kind": "git"}, @@ -70,7 +70,7 @@ }, { "name": "graalpython", - "version": "97929f2bc45e6cd3f186d22076bd260ad4a61f38", + "version": "563a41456483b85bd0841498a5c7be77aeff4831", "dynamic": True, "urls": [ {"url": "https://github.com/graalvm/graalpython.git", "kind": "git"}, @@ -80,8 +80,6 @@ ] }, - "libraries": {}, - "projects": { "com.oracle.graalvm.locator": { "subDir": "src", @@ -123,7 +121,10 @@ "INSTALLER": { "subDir": "src", "mainClass": "org.graalvm.component.installer.ComponentInstaller", - "dependencies": ["org.graalvm.component.installer"], + "dependencies": [ + "org.graalvm.component.installer", + ], + "maven" : False, }, "INSTALLER_TESTS": { "subDir": "src", @@ -142,7 +143,9 @@ "platformDependent": True, "description": "GraalVM Installer support distribution for the GraalVM", "layout": { - "./": "dependency:vm:INSTALLER", + "./": [ + "dependency:vm:INSTALLER", + ], "bin/": "file:mx.vm/gu", "components/polyglot/.registry" : "string:", }, diff --git a/vm/src/com.oracle.graalvm.locator/src/com/oracle/graalvm/locator/DefaultHomeFinder.java b/vm/src/com.oracle.graalvm.locator/src/com/oracle/graalvm/locator/DefaultHomeFinder.java index aee793f981f7..5647e67c6363 100644 --- a/vm/src/com.oracle.graalvm.locator/src/com/oracle/graalvm/locator/DefaultHomeFinder.java +++ b/vm/src/com.oracle.graalvm.locator/src/com/oracle/graalvm/locator/DefaultHomeFinder.java @@ -264,11 +264,19 @@ private static Map collectHomes(Path folder) { try (DirectoryStream dirContent = Files.newDirectoryStream(folder, new DirectoryStream.Filter() { @Override public boolean accept(Path entry) throws IOException { - return !entry.getFileName().toString().startsWith("."); + Path fileName = entry.getFileName(); + if (fileName == null) { + return false; + } else { + return !fileName.toString().startsWith("."); + } } })) { for (Path home : dirContent) { - res.put(home.getFileName().toString(), home); + Path filename = home.getFileName(); + if (filename != null) { + res.put(filename.toString(), home); + } } } catch (IOException ioe) { throw new RuntimeException(ioe); @@ -367,7 +375,10 @@ private static Path getLanguageHome(Path executableOrObjFile) { private static Path getGraalVMHomeFromLanguageHome(Path languageHome) { // jre// Path languagesOrTools = languageHome.getParent(); - String languagesOrToolsString = languagesOrTools != null ? getFileName(languagesOrTools) : null; + if (languagesOrTools == null) { + return null; + } + String languagesOrToolsString = getFileName(languagesOrTools); if (!"languages".equals(languagesOrToolsString) && !"tools".equals(languagesOrToolsString)) { return null; } @@ -381,7 +392,7 @@ private static Path getGraalVMHomeFromLanguageHome(Path languageHome) { } else { home = jreOrJdk; } - return isJreHome(home) || isJdkHome(home) ? home : null; + return home != null && (isJreHome(home) || isJdkHome(home)) ? home : null; } /** @@ -392,7 +403,7 @@ private static Path getGraalVMHomeFromLanguageHome(Path languageHome) { */ private static Path getGraalVmHomeFallBack(Path executable) { Path bin = executable.getParent(); - if (!"bin".equals(getFileName(bin))) { + if (bin == null || !"bin".equals(getFileName(bin))) { return null; } Path jreOrJdk = bin.getParent(); @@ -470,7 +481,8 @@ private static Path trimAbsolutePath(Path absolute, Path expectedRelative) { if (result == null) { return null; } - if (!result.getFileName().equals(p.getFileName())) { + Path filename = result.getFileName(); + if (filename == null || !filename.equals(p.getFileName())) { return null; } result = result.getParent(); diff --git a/vm/src/com.oracle.graalvm.locator/src/com/oracle/graalvm/locator/GraalVMLocator.java b/vm/src/com.oracle.graalvm.locator/src/com/oracle/graalvm/locator/GraalVMLocator.java index a27463dbb7e8..9451ca46003c 100644 --- a/vm/src/com.oracle.graalvm.locator/src/com/oracle/graalvm/locator/GraalVMLocator.java +++ b/vm/src/com.oracle.graalvm.locator/src/com/oracle/graalvm/locator/GraalVMLocator.java @@ -51,12 +51,7 @@ public final class GraalVMLocator extends TruffleLocator public GraalVMLocator() { } - private static List collectClassPath() { - - HomeFinder homeFinder = HomeFinder.getInstance(); - if (homeFinder == null) { - throw new IllegalStateException("No HomeFinder instance."); - } + private static void setGraalVMProperties(HomeFinder homeFinder) { Path homePath = homeFinder.getHomeFolder(); if (homePath != null) { String home = homePath.toString(); @@ -69,11 +64,27 @@ private static List collectClassPath() { System.setProperty("org.graalvm.home", home); } } - String version = homeFinder.getVersion(); System.setProperty("graalvm.version", version); System.setProperty("org.graalvm.version", version); + for (Map.Entry languageHome : homeFinder.getLanguageHomes().entrySet()) { + setLanguageHomeProperty(languageHome.getKey(), languageHome.getValue()); + } + for (Map.Entry toolHome : homeFinder.getToolHomes().entrySet()) { + setLanguageHomeProperty(toolHome.getKey(), toolHome.getValue()); + } + } + private static void setLanguageHomeProperty(String languageId, Path languageLocation) { + if (Files.isDirectory(languageLocation)) { + final String homeFolderKey = languageId + ".home"; + if (System.getProperty(homeFolderKey) == null) { + System.setProperty(homeFolderKey, languageLocation.toString()); + } + } + } + + private static List collectClassPath(HomeFinder homeFinder) { List classPath = new ArrayList<>(); collectLanguageJars(homeFinder.getLanguageHomes(), classPath); collectLanguageJars(homeFinder.getToolHomes(), classPath); @@ -96,8 +107,15 @@ private static List collectClassPath() { public static ClassLoader getLanguagesLoader() { if (loader == null) { - final List classPath = collectClassPath(); - loader = TruffleOptions.AOT ? null : new GuestLangToolsLoader(classPath.toArray(new URL[0]), GraalVMLocator.class.getClassLoader()); + HomeFinder homeFinder = HomeFinder.getInstance(); + if (homeFinder == null) { + throw new IllegalStateException("No HomeFinder instance."); + } + setGraalVMProperties(homeFinder); + if (!TruffleOptions.AOT) { + final List classPath = collectClassPath(homeFinder); + loader = new GuestLangToolsLoader(classPath.toArray(new URL[0]), GraalVMLocator.class.getClassLoader()); + } } return loader; } @@ -112,13 +130,8 @@ private static class GuestLangToolsLoader extends URLClassLoader { private static void collectLanguageJars(Map homes, List classPath) { for (Map.Entry languageHome : homes.entrySet()) { - final String languageId = languageHome.getKey(); final Path languageLocation = languageHome.getValue(); if (Files.isDirectory(languageLocation)) { - final String homeFolderKey = languageId + ".home"; - if (System.getProperty(homeFolderKey) == null) { - System.setProperty(homeFolderKey, languageLocation.toString()); - } try (DirectoryStream dirStream = Files.newDirectoryStream(languageLocation)) { for (Path file : dirStream) { addJar(classPath, file); @@ -145,7 +158,8 @@ private static void addJarOrDir(List classPath, Path file) { } private static void addJar(List classPath, Path jar) { - if (jar.getFileName().toString().endsWith(".jar") && Files.exists(jar)) { + Path filename = jar.getFileName(); + if (filename != null && filename.toString().endsWith(".jar") && Files.exists(jar)) { try { classPath.add(jar.toUri().toURL()); } catch (MalformedURLException ex) { diff --git a/vm/src/org.graalvm.component.installer.test/src/META-INF/services/org.graalvm.component.installer.SoftwareChannel b/vm/src/org.graalvm.component.installer.test/src/META-INF/services/org.graalvm.component.installer.SoftwareChannel new file mode 100644 index 000000000000..def672b60bcd --- /dev/null +++ b/vm/src/org.graalvm.component.installer.test/src/META-INF/services/org.graalvm.component.installer.SoftwareChannel @@ -0,0 +1 @@ +org.graalvm.component.installer.persist.test.TestCatalog diff --git a/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/Bundle.properties b/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/Bundle.properties new file mode 100644 index 000000000000..7be08d982159 --- /dev/null +++ b/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/Bundle.properties @@ -0,0 +1,130 @@ +# To change this license header, choose License Headers in Project Properties. +# To change this template file, choose Tools | Templates +# and open the template in the editor. + +Installer_BuiltingCatalogURL=https://www.graalvm.org/component-catalog/graal-updater-component-catalog.properties + +INFO_InstallerVersion=GraalVM Component Updater v{0} +INFO_Usage=\n\ + Usage: \n\ + \tgu info [-clLprstuv] prints info about specific component (from file, URL or catalog)\n\ + \tgu available [-lv] lists components available in catalog\n\ + \tgu install [-0cfiLnorvyxY] installs a component package\n\ + \tgu list [-clv] lists installed components, or components from catalog\n\ + \tgu uninstall [-0fxv] uninstalls a component\n\ + \tgu rebuild-images rebuilds native images. Use -h for detailed usage\n\ +\n\ + Common options:\n\ + \t--auto-yes\n\ + \t-A say YES or ACCEPT to all questions\n\ + \t--catalog\n\ + \t-c treat parameters as component IDs from catalog of GraalVM components. This is the default.\n\ + \t--local-file\n\ + \t-L treat parameters as local filenames of packaged components.\n\ + \t--debug\n\ + \t-d debugging. Prints stacktraces, ...\n\ + \t--help\n\ + \t-h print help.\n\ + \t--no-progress\n\ + \t-n do not display download progress.\n\ + \t--url\n\ + \t-u interpret parameters as URLs of packaged components.\n\ + \t--verbose\n\ + \t-v be verbose. Prints versions and dependency info.\n\ +{0}\n\ + Use \n\ + \tgu -h\n\ + to get specific help. + +INFO_UsageExtensions=\n\ + Additonal options:\n\{0}\n + + +ERROR_MissingCommand=No command given. +ERROR_UnknownCommand=Unknown command: {0} +# {0} - option, including the leading - +# {1} - command +ERROR_UnsupportedOption=Command {1} does not support option -{0} +ERROR_CommandWithNoOptions=Command {1} does not support any options +ERROR_UnsupportedGlobalOption=Unknown option: -{0} +ERROR_OptionNeedsParameter=Missing required parameter for option {0} +ERROR_MissingCommand=Missing command. +ERROR_CannotFindPath=File not found: {0} +ERROR_InternalError=Internal error: {0} +ERROR_NoGraalVMDirectory=Could not find GraalVM installation directory. +ERROR_MissingParameter=Missing parameter for command {0} +ERROR_ReadingComponentRegistry=Error loading component registry. +ERROR_MissingFile=Missing file: {0} +ERROR_CorruptedRelease=Release file corrupted in GraalVM installation at: {0} +ERROR_ReleaseSourceRevisions=Could not parse source revisions in: {0} +ERROR_ReadingRealeaseFile=Error accessing release file {0}: {1} +ERROR_AmbiguousCommand=Command {0} is ambiguous. Could be {1} or {2} +ERROR_InvalidGraalVMDirectory=The GraalVM directory {0} is invalid. +ERROR_MultipleSourcesUnsupported=The Updater cannot download from catalog and from specific URL at the same time. \ + Please run the Updater separately for components from a catalog and components from custom URLs. +INSTALLER_Error=Error: {0} +# INSTALLER_InvalidCatalogURL=Catalog URL is invalid: {0} +# {0} - filename +ERROR_UnknownFileFormat=Unknown format. + +STORAGE_CorruptedComponentStorage=Component metadata storage is corrupted. +STORAGE_InvalidReleaseFile=Invalid GraalVM release file. + + +INSTALLER_IOException=I/O error occured: {0} +INSTALLER_FailError={0} +INSTALLER_FileDoesNotExist=File does not exist: {0} +INSTALLER_FileExists=File already exists: {0} +INSTALLER_DirectoryNotEmpty=Directory is not empty: {0} +INSTALLER_AccessDenied=Permission denied: {0} +INSTALLER_InvalidMetadata=Invalid component metadata or corrupted component package +INSTALLER_InternalError=Internal error occured: {0} + +REGISTRY_ReadingComponentList=Error reading component list: {0} +REGISTRY_ReadingComponentMetadata=Error reading metadata of component {0}: {1} + +INSTALL_Capability_graalvm_version=Graal Version +INSTALL_Capability_os_arch=Architecture +INSTALL_Capability_os_name=Operating System + + +VERIFY_VerboseCheckRequirements=Checking requirements of component {1} ({0}), version {2} +VERIFY_VerboseCapability=\tRequires {0} = {1}, GraalVM provides: {2} +VERIFY_VerboseCapabilityNone=None +VERIFY_ComponentExists=The same component {0} ({1}) is already installed in version {2} +VERIFY_Dependency_Failed=Component {0} could not be installed. It requires {1} {2}, but the GraalVM provides {3} +VERIFY_CapabilityMissing=None + +REMOTE_CannotDeleteLocalFile=Could not delete temporary file {0}: {1} +REMOTE_UnknownComponentId=Unknown component: {0} +REMOTE_UnknownComponentMaybeFile=Unknown component: {0}.\n\ + Note: a file {0} exists; to use component stored locally, use -L option \n\ + \tgu -L {0} + +REMOTE_InvalidDownloadURL=Invalid component URL {0}: {1} +REMOTE_InvalidHash=Invalid hash for component {0}: {1} +REMOTE_CatalogLabel=Component catalog +REMOTE_ErrorDownloadCatalogNotFound=The component catalog was not found at {0}. +REMOTE_ErrorDownloadCatalog=Error downloading component catalog from {0}: {1}. \n\ + Please check your connection and proxy settings. \ + If your machine is behind a proxy, environment variables (http_proxy, https_proxy, ...) must be set appropriately. +REMOTE_ErrorDownloadCatalogProxy=Component catalog is unreachable. \n\ + Please check your connection and proxy settings. \ + If your machine is behind a proxy, environment variables (http_proxy, https_proxy, ...) must be set appropriately. +REMOTE_CorruptedCatalogFile=Catalog file {0} is corrupted. +REMOTE_ComponentFileLabel=Component {0} +REMOTE_UnsupportedGraalVersion=Unsupported Graal VM version: {0}, platform {1}/{2} +REMOTE_ErrorDownloadingComponent=Error downloading component {0} from {1}: {2} +REMOTE_ErrorDownloadingNotExist=Package for component {0} is not accessible at {1} + +URL_InvalidDownloadURL=Invalid download URL {0}: {1} +URL_ErrorDownloadingNotExist=Component is not accessible at URL {0} +URL_ErrorDownloadingComponent=Error downloading component from {0}: {1} + +COMPONENT_Ambiguous=Component id {0} is ambiguous. Please use qualfied IDs. +COMPONENT_AmbiguousIdFound=Component id {0} is ambiguous. It may match {0} or {1}. Please use qualified IDs. + +# {0} - error message +ERROR_RecordLicenseAccepted=WARNING: Could not write license acceptance: {0} +ERROR_UserInput=User input error: {0} +ERROR_Aborted=Aborted. diff --git a/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/CatalogIterableTest.java b/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/CatalogIterableTest.java index 969078da472a..7e161d5d935c 100644 --- a/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/CatalogIterableTest.java +++ b/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/CatalogIterableTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,24 +24,27 @@ */ package org.graalvm.component.installer; +import org.graalvm.component.installer.remote.CatalogIterable; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.net.URLConnection; +import java.nio.file.Path; import java.util.List; import java.util.Map; -import java.util.function.Supplier; import java.util.jar.JarFile; import java.util.logging.Level; import java.util.logging.Logger; -import org.graalvm.component.installer.CatalogIterable.RemoteComponentParam; +import org.graalvm.component.installer.DownloadURLIterable.DownloadURLParam; +import org.graalvm.component.installer.jar.JarArchive; +import org.graalvm.component.installer.jar.JarMetaLoader; import org.graalvm.component.installer.model.ComponentInfo; -import org.graalvm.component.installer.model.ComponentRegistry; +import org.graalvm.component.installer.remote.FileDownloader; import org.graalvm.component.installer.persist.MetadataLoader; import org.graalvm.component.installer.persist.ProxyResource; -import org.graalvm.component.installer.persist.RemoteStorage; +import org.graalvm.component.installer.remote.RemotePropertiesStorage; import org.graalvm.component.installer.persist.test.Handler; import org.junit.After; import static org.junit.Assert.assertEquals; @@ -53,7 +56,7 @@ import org.junit.Rule; import org.junit.Test; -public class CatalogIterableTest extends CommandTestBase implements Supplier { +public class CatalogIterableTest extends CommandTestBase implements SoftwareChannel { @Rule public final ProxyResource proxyResource = new ProxyResource(); @Override @@ -124,7 +127,7 @@ public void testCreateFileLoader() throws Exception { @Test public void testVerifyRemoteJars() throws Exception { initRemoteComponent("persist/data/truffleruby3.jar", "test://graalvm.io/download/truffleruby.zip", "testComponent", "test"); - info.setShaDigest(RemoteStorage.toHashBytes(null, "d3a45ea326b379cc3d543cc56130ee9bd395fd1c1d51a470e8c2c8af1129829c", this)); + info.setShaDigest(RemotePropertiesStorage.toHashBytes(null, "d3a45ea326b379cc3d543cc56130ee9bd395fd1c1d51a470e8c2c8af1129829c", this)); try { exception.expect(IOException.class); @@ -143,11 +146,6 @@ void addRemoteComponent(String relative, String u, boolean addParam) throws IOEx } } - @Override - public ComponentRegistry get() { - return registry; - } - @Test public void testReadComponentMetadataNoNetwork() throws Exception { addRemoteComponent("persist/data/truffleruby3.jar", "test://graalvm.io/download/truffleruby.zip", false); @@ -191,7 +189,7 @@ public void testUnknownComponentButExistingFile() throws Exception { @Test public void testMetaAccessesDirectURL() throws Exception { addRemoteComponent("persist/data/truffleruby3.jar", "test://graalvm.io/download/truffleruby.zip", false); - rparam = new RemoteComponentParam(url, rparam.getDisplayName(), rparam.getSpecification(), this, false); + rparam = new DownloadURLParam(url, rparam.getDisplayName(), rparam.getSpecification(), this, false); components.add(param); URL remoteU = rparam.createMetaLoader().getComponentInfo().getRemoteURL(); @@ -203,7 +201,7 @@ public void testMetaAccessesDirectURL() throws Exception { @Test public void testDirectURLAccessedJustOnce() throws Exception { addRemoteComponent("persist/data/truffleruby3.jar", "test://graalvm.io/download/truffleruby.zip", false); - rparam = new RemoteComponentParam(url, rparam.getDisplayName(), rparam.getSpecification(), this, false); + rparam = new DownloadURLParam(url, rparam.getDisplayName(), rparam.getSpecification(), this, false); components.add(param); URL remoteU = rparam.createMetaLoader().getComponentInfo().getRemoteURL(); @@ -213,7 +211,7 @@ public void testDirectURLAccessedJustOnce() throws Exception { Handler.clearVisited(); - JarFile jf = rparam.getJarFile(); + Archive jf = rparam.getArchive(); assertNotNull(jf); assertFalse(Handler.isVisited(url)); } @@ -221,12 +219,12 @@ public void testDirectURLAccessedJustOnce() throws Exception { @Test public void testDirectURLJarClosedAfterMeta() throws Exception { addRemoteComponent("persist/data/truffleruby3.jar", "test://graalvm.io/download/truffleruby.zip", false); - rparam = new RemoteComponentParam(url, rparam.getDisplayName(), rparam.getSpecification(), this, false); + rparam = new DownloadURLParam(url, rparam.getDisplayName(), rparam.getSpecification(), this, false); components.add(param); URL remoteU = rparam.createMetaLoader().getComponentInfo().getRemoteURL(); assertEquals(url, remoteU); - JarFile jf = rparam.getJarFile(); + JarArchive jf = (JarArchive) rparam.getArchive(); assertNotNull(jf.getEntry("META-INF")); rparam.close(); @@ -238,9 +236,9 @@ public void testDirectURLJarClosedAfterMeta() throws Exception { @Test public void testDirectURLJarClosedAfterJar() throws Exception { addRemoteComponent("persist/data/truffleruby3.jar", "test://graalvm.io/download/truffleruby.zip", false); - rparam = new RemoteComponentParam(url, rparam.getDisplayName(), rparam.getSpecification(), this, false); + rparam = new DownloadURLParam(url, rparam.getDisplayName(), rparam.getSpecification(), this, false); components.add(param); - JarFile jf = rparam.getJarFile(); + JarArchive jf = (JarArchive) rparam.getArchive(); assertNotNull(jf.getEntry("META-INF")); rparam.close(); exception.expect(IllegalStateException.class); @@ -292,4 +290,24 @@ public void connect() throws IOException { rubyComp.createFileLoader().getComponentInfo(); } + + @Override + public boolean setupLocation(String urlString) { + // OK + return true; + } + + @Override + public void init(CommandInput input, Feedback output) { + } + + @Override + public MetadataLoader createLocalFileLoader(Path localFile, boolean verify) throws IOException { + return new JarMetaLoader(new JarFile(localFile.toFile(), verify), this); + } + + @Override + public FileDownloader configureDownloader(FileDownloader dn) { + return dn; + } } diff --git a/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/ChunkedConnection.java b/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/ChunkedConnection.java index 4778cc344e80..ed0e27030dc1 100644 --- a/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/ChunkedConnection.java +++ b/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/ChunkedConnection.java @@ -32,7 +32,7 @@ import java.util.concurrent.Semaphore; import java.util.logging.Level; import java.util.logging.Logger; -import org.graalvm.component.installer.persist.FileDownloaderTest; +import org.graalvm.component.installer.remote.FileDownloaderTest; public class ChunkedConnection extends HttpURLConnection { public InputStream delegate; diff --git a/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/CommandTestBase.java b/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/CommandTestBase.java index 2ab877102552..0e14bd78a960 100644 --- a/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/CommandTestBase.java +++ b/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/CommandTestBase.java @@ -34,17 +34,23 @@ import java.util.List; import java.util.Map; import java.util.jar.JarFile; +import org.graalvm.component.installer.DownloadURLIterable.DownloadURLParam; import org.graalvm.component.installer.commands.MockStorage; +import org.graalvm.component.installer.jar.JarMetaLoader; import org.graalvm.component.installer.model.ComponentInfo; import org.graalvm.component.installer.model.ComponentRegistry; import org.graalvm.component.installer.persist.ComponentPackageLoader; +import org.graalvm.component.installer.remote.FileDownloader; +import org.graalvm.component.installer.persist.MetadataLoader; import org.graalvm.component.installer.persist.test.Handler; +import org.graalvm.component.installer.remote.RemoteComponentParam; +import org.graalvm.component.installer.remote.CatalogIterable.CatalogItemParam; import org.junit.Before; import org.junit.Rule; import org.junit.rules.ExpectedException; import org.junit.rules.TemporaryFolder; -public class CommandTestBase extends TestBase implements CommandInput { +public class CommandTestBase extends TestBase implements CommandInput, SoftwareChannel { @Rule public ExpectedException exception = ExpectedException.none(); protected JarFile componentJarFile; @Rule public TemporaryFolder folder = new TemporaryFolder(); @@ -61,7 +67,7 @@ public class CommandTestBase extends TestBase implements CommandInput { protected Map options = new HashMap<>(); ComponentParam param; - CatalogIterable.RemoteComponentParam rparam; + RemoteComponentParam rparam; URL url; URL clu; ComponentInfo info; @@ -76,12 +82,12 @@ protected void initRemoteComponent(String relativeJar, String u, String disp, St File f = dataFile(relativeJar).toFile(); JarFile jf = new JarFile(f, false); - ComponentPackageLoader cpl = new ComponentPackageLoader(jf, this); + ComponentPackageLoader cpl = new JarMetaLoader(jf, this); info = cpl.getComponentInfo(); // unknown in catalog metadata info.setLicensePath(null); info.setRemoteURL(url); - param = rparam = new CatalogIterable.RemoteComponentParam(info, disp, spec, this, false); + param = rparam = new CatalogItemParam(this, info, disp, spec, this, false); } protected void initURLComponent(String relativeJar, String spec) throws IOException { @@ -91,12 +97,12 @@ protected void initURLComponent(String relativeJar, String spec) throws IOExcept File f = dataFile(relativeJar).toFile(); JarFile jf = new JarFile(f, false); - ComponentPackageLoader cpl = new ComponentPackageLoader(jf, this); + ComponentPackageLoader cpl = new JarMetaLoader(jf, this); info = cpl.getComponentInfo(); // unknown in catalog metadata info.setLicensePath(null); info.setRemoteURL(url); - param = rparam = new CatalogIterable.RemoteComponentParam(url, spec, spec, this, false); + param = rparam = new DownloadURLParam(url, spec, spec, this, false); } protected Iterable paramIterable; @@ -186,4 +192,28 @@ public void setUp() throws Exception { storage = new MockStorage(); localRegistry = registry = new ComponentRegistry(this, storage); } + + @Override + public boolean setupLocation(String urlString) { + return false; + } + + @Override + public void init(CommandInput input, Feedback output) { + } + + @Override + public FileDownloader configureDownloader(FileDownloader dn) { + return dn; + } + + @Override + public MetadataLoader createLocalFileLoader(Path localFile, boolean verify) throws IOException { + return new JarMetaLoader(new JarFile(localFile.toFile(), verify), this); + } + + @Override + public MetadataLoader completeMetadata(MetadataLoader ldr, ComponentInfo ci) { + return ldr; + } } diff --git a/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/ComponentInstallerTest.java b/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/ComponentInstallerTest.java index 6bb559e45729..d439be2e1e3e 100644 --- a/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/ComponentInstallerTest.java +++ b/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/ComponentInstallerTest.java @@ -100,7 +100,9 @@ public void testMainHelpConsistent() { int oE = l.indexOf(']'); int sp = l.indexOf(' ', 4); String cn = l.substring(4, sp); - + if (cn.startsWith("<")) { + continue; + } InstallerCommand c = allCmds.remove(cn); if (c == null) { Assert.fail("Unknown command: " + cn); diff --git a/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/DownloadURLIterableTest.java b/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/DownloadURLIterableTest.java index fa3edc649846..2a5ccd2ef3c0 100644 --- a/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/DownloadURLIterableTest.java +++ b/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/DownloadURLIterableTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,8 +25,7 @@ package org.graalvm.component.installer; import java.util.Iterator; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; +import org.graalvm.component.installer.jar.JarArchive; import org.graalvm.component.installer.model.ComponentInfo; import org.graalvm.component.installer.persist.MetadataLoader; import org.graalvm.component.installer.persist.ProxyResource; @@ -71,8 +70,8 @@ public void testURLParameter() throws Exception { assertEquals("ruby", ci.getId()); assertEquals("0.33-dev", ci.getVersionString()); - JarFile jf = ldr.getJarFile(); - JarEntry je = jf.getJarEntry("META-INF/MANIFEST.MF"); + JarArchive jf = (JarArchive) ldr.getArchive(); + Archive.FileEntry je = jf.getJarEntry("META-INF/MANIFEST.MF"); assertNotNull(je); jf.close(); } diff --git a/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/EnvironmentTest.java b/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/EnvironmentTest.java new file mode 100644 index 000000000000..6f8a8801a7fc --- /dev/null +++ b/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/EnvironmentTest.java @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.graalvm.component.installer; + +import java.io.ByteArrayOutputStream; +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.net.MalformedURLException; +import java.text.MessageFormat; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.ResourceBundle; +import org.junit.AfterClass; +import org.junit.Assert; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import org.junit.BeforeClass; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +/** + * Tests basic functionality of the Environment class. + * + * @author sdedic + */ +public class EnvironmentTest { + @Rule public ExpectedException exception = ExpectedException.none(); + + private static ResourceBundle B1; + private static Locale saveLocale; + private Environment env; + + private Map initOptions = new HashMap<>(); + + @BeforeClass + public static void setUp() { + // establish well-known locale, so we may check I18ned strings + saveLocale = Locale.getDefault(); + Locale.setDefault(Locale.US); + B1 = ResourceBundle.getBundle("org.graalvm.component.installer.Bundle", Locale.US); + } + + @AfterClass + public static void tearDown() { + Locale.setDefault(saveLocale); + } + + class FailInputStream extends FilterInputStream { + FailInputStream() { + super(System.in); + } + + @Override + public int read() throws IOException { + Assert.fail("Unexpected read"); + return super.read(); + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + Assert.fail("Unexpected read"); + return super.read(b, off, len); + } + } + + ByteArrayOutputStream outBuffer = new ByteArrayOutputStream(); + ByteArrayOutputStream errBuffer = new ByteArrayOutputStream(); + + @Test + public void testNonInteractiveLineFails() { + setupEmptyEnv(); + env.setNonInteractive(true); + env.setIn(new FailInputStream()); + exception.expect(NonInteractiveException.class); + assertNull(env.acceptLine(false)); + } + + /** + * Non-interactive mode + auto-yes, and confirmation line. Should succeed. + */ + @Test + public void testNonInteractiveYesLineOK() { + setupEmptyEnv(); + env.setNonInteractive(true); + env.setAutoYesEnabled(true); + env.setIn(new FailInputStream()); + assertSame(Feedback.AUTO_YES, env.acceptLine(true)); + } + + /** + * Non-interactive mode + auto-yes, but regular, not a confirmation line. Should fail. + */ + @Test + public void testNonInteractiveNormalLineFails() { + setupEmptyEnv(); + env.setNonInteractive(true); + env.setAutoYesEnabled(true); + env.setIn(new FailInputStream()); + exception.expect(NonInteractiveException.class); + exception.expectMessage(B1.getString("ERROR_NoninteractiveInput")); + assertSame(Feedback.AUTO_YES, env.acceptLine(false)); + } + + /** + * Regular + auto-yes. Confirmation line should not read the input. + */ + @Test + public void testAutoConfirmSkipsInput() { + setupEmptyEnv(); + env.setAutoYesEnabled(true); + env.setIn(new FailInputStream()); + assertSame(Feedback.AUTO_YES, env.acceptLine(true)); + } + + @Test + public void testNonInteractivePasswordFails() { + setupEmptyEnv(); + env.setNonInteractive(true); + env.setIn(new FailInputStream()); + exception.expect(NonInteractiveException.class); + assertNull(env.acceptPassword()); + } + + void setupEmptyEnv() { + List parameters = Arrays.asList("param"); + env = new Environment("test", "org.graalvm.component.installer", parameters, initOptions); + } + + /** + * Checks that an error will be printed without stacktrace. + */ + @Test + public void testErrorMessagePlain() throws Exception { + setupEmptyEnv(); + env.setErr(new PrintStream(errBuffer)); + env.error("ERROR_UserInput", new ClassCastException(), "Foobar"); + String s = new String(errBuffer.toByteArray(), "UTF-8"); + assertEquals(B1.getString("ERROR_UserInput").replace("{0}", "Foobar") + "\n", s); + } + + /** + * Checks that an error will be printed together with the stacktrace. + */ + @Test + public void testErrorMessageWithException() throws Exception { + initOptions.put(Commands.OPTION_DEBUG, ""); + setupEmptyEnv(); + env.setErr(new PrintStream(errBuffer)); + env.error("ERROR_UserInput", new ClassCastException(), "Foobar"); + String all = new String(errBuffer.toByteArray(), "UTF-8"); + String[] lines = all.split("\n"); + assertEquals(B1.getString("ERROR_UserInput").replace("{0}", "Foobar"), lines[0]); + assertTrue(lines[1].contains("ClassCastException")); + } + + @Test + public void testFailureOperation() throws Exception { + setupEmptyEnv(); + + Throwable t = env.failure("URL_InvalidDownloadURL", new MalformedURLException("Foo"), "Bar", "Baz"); + assertTrue(t instanceof FailedOperationException); + String s = MessageFormat.format(B1.getString("URL_InvalidDownloadURL"), "Bar", "Baz"); + assertEquals(t.getLocalizedMessage(), s); + assertNotSame(t, t.getCause()); + assertEquals("Foo", t.getCause().getLocalizedMessage()); + } +} diff --git a/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/SimpleGetoptTest.java b/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/SimpleGetoptTest.java index 3884f944d7e0..7d12f62d4271 100644 --- a/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/SimpleGetoptTest.java +++ b/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/SimpleGetoptTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -47,9 +47,11 @@ public class SimpleGetoptTest extends TestBase { public void setUp() { Map g = new HashMap<>(ComponentInstaller.globalOptions); g.put("8", "=C"); + g.put("long-user", "U"); + g.put("U", "s"); getopt = new SimpleGetopt(g) { @Override - RuntimeException err(String messageKey, Object... args) { + public RuntimeException err(String messageKey, Object... args) { errorKey = messageKey; errorParams = args; throw new FailedOperationException(messageKey); @@ -326,7 +328,7 @@ public void testLongOptionAppendedParameter() { @Test public void testLongOptionWithParameterBeforeCommand() { - setParams("--user-catalog bubu install"); + setParams("--custom-catalog bubu install"); getopt.process(); @@ -336,7 +338,7 @@ public void testLongOptionWithParameterBeforeCommand() { @Test public void testLongOptionWithParameterAfterCommand() { - setParams("install --user-catalog bubu "); + setParams("install --custom-catalog bubu "); getopt.process(); Map opts = getopt.getOptValues(); @@ -361,11 +363,11 @@ public void testComputeAbbreviations() { @Test public void testLongOptionAbbreviation() { - setParams("install --user bubu"); + setParams("install --long-user bubu"); getopt.process(); Map opts = getopt.getOptValues(); - assertEquals("bubu", opts.get("C")); + assertEquals("bubu", opts.get("U")); } @Test @@ -392,4 +394,17 @@ public void testOptionAliasesParamsGlobal() { Map opts = getopt.getOptValues(); assertEquals("bubu", opts.get("C")); } + + @Test + public void testIgnoreUnknownCommands() { + setParams("-v bubak 1"); + getopt.ignoreUnknownCommands(true); + getopt.process(); + Map opts = getopt.getOptValues(); + assertEquals(1, opts.size()); + assertNotNull(opts.get("v")); + + assertEquals(2, getopt.getPositionalParameters().size()); + assertEquals("bubak", getopt.getPositionalParameters().get(0)); + } } diff --git a/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/TestBase.java b/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/TestBase.java index 24c48251f1a3..63e2f67ec214 100644 --- a/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/TestBase.java +++ b/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/TestBase.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -40,23 +40,39 @@ import java.nio.file.attribute.BasicFileAttributes; import java.text.MessageFormat; import java.util.Collections; +import java.util.Enumeration; import java.util.ResourceBundle; import java.util.jar.JarEntry; import java.util.jar.JarFile; import org.junit.AfterClass; import org.junit.ClassRule; +import org.junit.Rule; import org.junit.rules.TemporaryFolder; +import org.junit.rules.TestName; /** * Boilerplate for tests. */ public class TestBase implements Feedback { + private static final ResourceBundle NO_BUNDLE = new ResourceBundle() { + @Override + protected Object handleGetObject(String key) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public Enumeration getKeys() { + throw new UnsupportedOperationException("Not supported yet."); + } + }; protected ResourceBundle defaultBundle = ResourceBundle.getBundle("org.graalvm.component.installer.Bundle"); // NOI18N private Feedback feedbackDelegate; protected boolean verbose; @ClassRule public static TemporaryFolder expandedFolder = new ClassTempFolder(); + @Rule public TemporaryFolder testFolder = new TemporaryFolder(); + @Rule public TestName testName = new TestName(); static class ClassTempFolder extends TemporaryFolder { ThreadLocal root = new ThreadLocal<>(); @@ -197,6 +213,9 @@ public void message(ResourceBundle bundle, String bundleKey, Object... params) { if (bundle != null) { MessageFormat.format(bundle.getString(bundleKey), params); } + if (feedbackDelegate instanceof FeedbackAdapter) { + ((FeedbackAdapter) feedbackDelegate).setBundle(bundle == null ? NO_BUNDLE : bundle); + } if (feedbackDelegate != null) { feedbackDelegate.message(bundleKey, params); } @@ -206,6 +225,9 @@ public void output(ResourceBundle bundle, String bundleKey, Object... params) { if (bundle != null) { MessageFormat.format(bundle.getString(bundleKey), params); } + if (feedbackDelegate instanceof FeedbackAdapter) { + ((FeedbackAdapter) feedbackDelegate).setBundle(bundle == null ? NO_BUNDLE : bundle); + } if (feedbackDelegate != null) { feedbackDelegate.output(bundleKey, params); } @@ -218,16 +240,28 @@ public void outputPart(String bundleKey, Object... params) { @Override public boolean verbatimPart(String msg, boolean beVerbose) { - if (feedbackDelegate != null) { - return feedbackDelegate.verbatimPart(msg, beVerbose); + if (feedbackDelegate instanceof FeedbackAdapter) { + ((FeedbackAdapter) feedbackDelegate).setBundle(NO_BUNDLE); } - return beVerbose; + try { + if (feedbackDelegate != null) { + return feedbackDelegate.verbatimPart(msg, beVerbose); + } + } finally { + if (feedbackDelegate instanceof FeedbackAdapter) { + ((FeedbackAdapter) feedbackDelegate).setBundle(null); + } + } + return verbose; } public void verbosePart(ResourceBundle bundle, String bundleKey, Object... params) { if (bundle != null) { MessageFormat.format(bundle.getString(bundleKey), params); } + if (feedbackDelegate instanceof FeedbackAdapter) { + ((FeedbackAdapter) feedbackDelegate).setBundle(bundle == null ? NO_BUNDLE : bundle); + } if (feedbackDelegate != null) { feedbackDelegate.verbosePart(bundleKey, params); } @@ -237,6 +271,9 @@ public void verboseOutput(ResourceBundle bundle, String bundleKey, Object... par if (bundle != null) { MessageFormat.format(bundle.getString(bundleKey), params); } + if (feedbackDelegate instanceof FeedbackAdapter) { + ((FeedbackAdapter) feedbackDelegate).setBundle(bundle == null ? NO_BUNDLE : bundle); + } if (feedbackDelegate != null) { feedbackDelegate.verboseOutput(bundleKey, params); } @@ -246,6 +283,9 @@ public void error(ResourceBundle bundle, String key, Throwable t, Object... para if (bundle != null) { MessageFormat.format(bundle.getString(key), params); } + if (feedbackDelegate instanceof FeedbackAdapter) { + ((FeedbackAdapter) feedbackDelegate).setBundle(bundle == null ? NO_BUNDLE : bundle); + } if (feedbackDelegate != null) { feedbackDelegate.error(key, t, params); } @@ -256,7 +296,11 @@ public String l10n(ResourceBundle bundle, String key, Object... params) { MessageFormat.format(bundle.getString(key), params); } if (feedbackDelegate != null) { - String s = feedbackDelegate.l10n(key, params); + String s; + if (feedbackDelegate instanceof FeedbackAdapter) { + ((FeedbackAdapter) feedbackDelegate).setBundle(bundle); + } + s = feedbackDelegate.l10n(key, params); if (s != null) { return s; } @@ -271,13 +315,22 @@ public String reallyl10n(ResourceBundle bundle, String key, Object... params) { @Override public boolean verbatimOut(String msg, boolean verboseOutput) { - if (verboseOutput) { - verboseOutput((ResourceBundle) null, msg); - } else { - if (feedbackDelegate != null) { - feedbackDelegate.verbatimOut(msg, verboseOutput); + if (feedbackDelegate instanceof FeedbackAdapter) { + ((FeedbackAdapter) feedbackDelegate).setBundle(NO_BUNDLE); + } + try { + if (verboseOutput) { + verboseOutput((ResourceBundle) null, msg); + } else { + if (feedbackDelegate != null) { + feedbackDelegate.verbatimOut(msg, verboseOutput); + } + output((ResourceBundle) null, msg); + } + } finally { + if (feedbackDelegate instanceof FeedbackAdapter) { + ((FeedbackAdapter) feedbackDelegate).setBundle(null); } - output((ResourceBundle) null, msg); } return verboseOutput; } @@ -346,16 +399,6 @@ public Feedback withBundle(Class clazz) { return new WB(clazz); } - @Override - public String translateFilename(Path f) { - return f.toString(); - } - - @Override - public void bindFilename(Path file, String label) { - - } - class WB implements Feedback { ResourceBundle localBundle; @@ -414,12 +457,12 @@ public Feedback withBundle(Class clazz) { @Override public void outputPart(String bundleKey, Object... params) { - throw new UnsupportedOperationException("Not supported yet."); + TestBase.this.output(localBundle, bundleKey, params); } @Override public boolean verbatimPart(String msg, boolean beVerbose) { - return TestBase.this.verbatimOut(msg, beVerbose); + return TestBase.this.verbatimPart(msg, beVerbose); } @Override @@ -428,17 +471,29 @@ public boolean backspace(int chars, boolean beVerbose) { } @Override - public String translateFilename(Path f) { - return TestBase.this.translateFilename(f); + public String acceptLine(boolean autoYes) { + return TestBase.this.acceptLine(autoYes); } @Override - public void bindFilename(Path file, String label) { - TestBase.this.bindFilename(file, label); + public String acceptPassword() { + return TestBase.this.acceptPassword(); + } + + @Override + public void addLocalFileCache(URL location, Path local) { + TestBase.this.addLocalFileCache(location, local); + } + + @Override + public Path getLocalCache(URL location) { + return TestBase.this.getLocalCache(location); } } public class FeedbackAdapter implements Feedback { + private ResourceBundle currentBundle; + @Override public boolean verbatimOut(String msg, boolean beVerbose) { return verbose; @@ -495,13 +550,42 @@ public boolean backspace(int chars, boolean beVerbose) { return verbose; } + protected String reallyl10n(String k, Object... params) { + return TestBase.this.reallyl10n(getBundle(), k, params); + } + + protected ResourceBundle getBundle() { + if (currentBundle == NO_BUNDLE) { + return null; + } + if (currentBundle != null) { + return currentBundle; + } + return defaultBundle; + } + + void setBundle(ResourceBundle bundle) { + this.currentBundle = bundle; + } + @Override - public String translateFilename(Path f) { - return f.toString(); + public String acceptLine(boolean autoYes) { + return TestBase.this.doAcceptLine(autoYes); } @Override - public void bindFilename(Path file, String label) { + public String acceptPassword() { + return TestBase.this.doAcceptPassword(); + } + + @Override + public void addLocalFileCache(URL location, Path local) { + TestBase.this.addLocalFileCache(location, local); + } + + @Override + public Path getLocalCache(URL location) { + return TestBase.this.getLocalCache(location); } } @@ -509,4 +593,51 @@ public void bindFilename(Path file, String label) { public static boolean isWindows() { return SystemUtils.isWindows(); } + + protected StringBuilder userInput = new StringBuilder(); + protected String password; + protected boolean autoYesEnabled; + + @Override + public String acceptLine(boolean autoYes) { + if (feedbackDelegate != null) { + return feedbackDelegate.acceptLine(autoYes); + } + return doAcceptLine(autoYes); + } + + String doAcceptLine(boolean autoYes) { + if (autoYes && autoYesEnabled) { + return AUTO_YES; + } + int nl = userInput.indexOf("\n"); + if (nl < 0) { + nl = userInput.length(); + } + String r = userInput.substring(0, nl); + userInput.delete(0, nl); + return r; + } + + @Override + public String acceptPassword() { + if (feedbackDelegate != null) { + return feedbackDelegate.acceptPassword(); + } + return password; + } + + String doAcceptPassword() { + return password; + } + + @Override + public void addLocalFileCache(URL location, Path local) { + + } + + @Override + public Path getLocalCache(URL location) { + return null; + } } diff --git a/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/commands/CatalogInstallTest.java b/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/commands/CatalogInstallTest.java index 3efa5a4fca76..716218a318cf 100644 --- a/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/commands/CatalogInstallTest.java +++ b/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/commands/CatalogInstallTest.java @@ -27,13 +27,13 @@ import java.io.IOException; import java.net.URL; import java.net.URLConnection; -import org.graalvm.component.installer.CatalogIterable; +import org.graalvm.component.installer.remote.CatalogIterable; import org.graalvm.component.installer.CommandTestBase; import org.graalvm.component.installer.CommonConstants; import org.graalvm.component.installer.DependencyException; import org.graalvm.component.installer.FailedOperationException; import org.graalvm.component.installer.persist.ProxyResource; -import org.graalvm.component.installer.persist.RemoteCatalogDownloader; +import org.graalvm.component.installer.remote.RemoteCatalogDownloader; import org.graalvm.component.installer.persist.test.Handler; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -78,7 +78,7 @@ private void setupCatalog(String rel) throws Exception { } Handler.bind(TEST_CATALOG_URL, u); - downloader = new RemoteCatalogDownloader(this, this.localRegistry, new URL(TEST_CATALOG_URL)); + downloader = new RemoteCatalogDownloader(this, this, new URL(TEST_CATALOG_URL)); this.registry = downloader.get(); } diff --git a/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/commands/InstallTest.java b/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/commands/InstallTest.java index 7fd6727c818f..282991503e86 100644 --- a/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/commands/InstallTest.java +++ b/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/commands/InstallTest.java @@ -36,7 +36,7 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.logging.Level; import java.util.logging.Logger; -import org.graalvm.component.installer.CatalogIterable; +import org.graalvm.component.installer.remote.CatalogIterable; import org.graalvm.component.installer.Commands; import org.graalvm.component.installer.CommonConstants; import org.graalvm.component.installer.ComponentParam; @@ -45,7 +45,7 @@ import org.graalvm.component.installer.SystemUtils; import org.graalvm.component.installer.model.ComponentInfo; import org.graalvm.component.installer.persist.ProxyResource; -import org.graalvm.component.installer.persist.RemoteCatalogDownloader; +import org.graalvm.component.installer.remote.RemoteCatalogDownloader; import org.graalvm.component.installer.persist.test.Handler; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -240,7 +240,7 @@ public void testFailOnExistingFromCatalog() throws Exception { componentIterable = new CatalogIterable(this, this, new RemoteCatalogDownloader( this, - this.getLocalRegistry(), + this, u)); storage.graalInfo.put(CommonConstants.CAP_GRAALVM_VERSION, "0.33-dev"); textParams.add("ruby"); @@ -271,7 +271,7 @@ public void testSkipExistingFromCatalog() throws Exception { componentIterable = new CatalogIterable(this, this, new RemoteCatalogDownloader( this, - this.getLocalRegistry(), + this, u)); storage.graalInfo.put(CommonConstants.CAP_GRAALVM_VERSION, "0.33-dev"); textParams.add("ruby"); diff --git a/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/commands/InstallerTest.java b/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/commands/InstallerTest.java index 83cbb572a916..c76c59b18113 100644 --- a/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/commands/InstallerTest.java +++ b/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/commands/InstallerTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -39,16 +39,17 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.jar.JarEntry; import java.util.jar.JarFile; -import java.util.zip.ZipEntry; +import org.graalvm.component.installer.Archive; import org.graalvm.component.installer.CommonConstants; import org.graalvm.component.installer.DependencyException; import org.graalvm.component.installer.FailedOperationException; import org.graalvm.component.installer.Feedback; import org.graalvm.component.installer.SystemUtils; import org.graalvm.component.installer.TestBase; +import org.graalvm.component.installer.jar.JarArchive; +import org.graalvm.component.installer.jar.JarMetaLoader; import org.graalvm.component.installer.model.ComponentInfo; import org.graalvm.component.installer.model.ComponentRegistry; import org.graalvm.component.installer.persist.ComponentPackageLoader; @@ -63,7 +64,7 @@ public class InstallerTest extends TestBase { @Rule public ExpectedException exception = ExpectedException.none(); - protected JarFile componentJarFile; + protected JarArchive componentJarFile; @Rule public TemporaryFolder folder = new TemporaryFolder(); private Path targetPath; @@ -76,14 +77,14 @@ public class InstallerTest extends TestBase { private void setupComponentInstall(String relativePath) throws IOException { File f = dataFile(relativePath).toFile(); - componentJarFile = new JarFile(f); + JarFile jf = new JarFile(f); + componentJarFile = new JarArchive(jf); - loader = new ComponentPackageLoader(componentJarFile, this); + loader = new JarMetaLoader(jf, this); componentInfo = loader.createComponentInfo(); loader.loadPaths(); - installer = new Installer(fb(), componentInfo, registry); - installer.setJarFile(componentJarFile); + installer = new Installer(fb(), componentInfo, registry, componentJarFile); installer.setInstallPath(targetPath); installer.setLicenseRelativePath(SystemUtils.fromCommonString(loader.getLicensePath())); } @@ -458,7 +459,7 @@ public void testInstallOneRegularFile() throws Exception { /* * inst.setPermissions(ldr.loadPermissions()); inst.setSymlinks(ldr.loadSymlinks()); */ - JarEntry entry = componentJarFile.getJarEntry("jre/bin/ruby"); + Archive.FileEntry entry = componentJarFile.getJarEntry("jre/bin/ruby"); Path resultPath = installer.installOneFile(installer.translateTargetPath(entry), entry); Path relative = targetPath.relativize(resultPath); assertEquals(entry.getName(), SystemUtils.toCommonPath(relative)); @@ -490,7 +491,7 @@ public void testInstallExistingFileWillNotRevert() throws Exception { Files.createDirectories(existing.getParent()); Files.copy(dataFile("ruby"), existing); - JarEntry entry = componentJarFile.getJarEntry("jre/bin/ruby"); + Archive.FileEntry entry = componentJarFile.getJarEntry("jre/bin/ruby"); Path resultPath = installer.installOneFile(installer.translateTargetPath(entry), entry); Path relative = targetPath.relativize(resultPath); assertEquals(entry.getName(), SystemUtils.toCommonPath(relative)); @@ -522,7 +523,7 @@ public void testInstallOverwrittemFileWillNotRevert() throws Exception { Files.createDirectories(existing.getParent()); Files.copy(dataFile("ruby2"), existing); - JarEntry entry = componentJarFile.getJarEntry("jre/bin/ruby"); + Archive.FileEntry entry = componentJarFile.getJarEntry("jre/bin/ruby"); Path resultPath = installer.installOneFile(installer.translateTargetPath(entry), entry); Path relative = targetPath.relativize(resultPath); assertEquals(entry.getName(), SystemUtils.toCommonPath(relative)); @@ -541,9 +542,18 @@ public void testInstallOverwrittemFileWillNotRevert() throws Exception { @Test public void testInstallOneLicenseFile() throws Exception { + delegateFeedback(new FeedbackAdapter() { + @Override + public String l10n(String key, Object... params) { + if (key.startsWith("LICENSE_")) { + return reallyl10n(key, params); + } + return null; + } + }); setupComponentInstall("truffleruby2.jar"); - JarEntry entry2 = componentJarFile.getJarEntry("LICENSE"); + Archive.FileEntry entry2 = componentJarFile.getJarEntry("LICENSE"); Path resultPath = installer.installOneFile(installer.translateTargetPath(entry2), entry2); Path relative = targetPath.relativize(resultPath); assertNotEquals(entry2.getName(), relative.toString()); @@ -561,7 +571,7 @@ public void testInstallOneLicenseFile() throws Exception { public void testInstallOneDirectory() throws Exception { setupComponentInstall("truffleruby2.jar"); - JarEntry entry = componentJarFile.getJarEntry("jre/bin/"); + Archive.FileEntry entry = componentJarFile.getJarEntry("jre/bin/"); installer.installOneEntry(entry); Path check = targetPath.resolve(SystemUtils.fromCommonString("jre/bin")); @@ -585,7 +595,7 @@ public void testInstallExistingDirectoryWillNotRevert() throws Exception { Path existing = targetPath.resolve(SystemUtils.fromCommonString("jre/bin")); Files.createDirectories(existing); - JarEntry entry = componentJarFile.getJarEntry("jre/bin/"); + Archive.FileEntry entry = componentJarFile.getJarEntry("jre/bin/"); installer.installOneEntry(entry); Path check = targetPath.resolve(SystemUtils.fromCommonString("jre/bin")); @@ -662,7 +672,7 @@ public void testCheckFileReplacementSame() throws Exception { Path existing = dataFile("ruby"); - JarEntry je = componentJarFile.getJarEntry("jre/bin/ruby"); + Archive.FileEntry je = componentJarFile.getJarEntry("jre/bin/ruby"); // should pass: installer.checkFileReplacement(existing, je); @@ -676,7 +686,7 @@ public void testCheckFileReplacementDifferent() throws Exception { setupComponentInstall("truffleruby2.jar"); Path existing = dataFile("ruby2"); - JarEntry je = componentJarFile.getJarEntry("jre/bin/ruby"); + Archive.FileEntry je = componentJarFile.getJarEntry("jre/bin/ruby"); // should fail: exception.expect(FailedOperationException.class); @@ -693,7 +703,7 @@ public void testCheckFileReplacementForced() throws Exception { installer.setReplaceDiferentFiles(true); Path existing = dataFile("ruby2"); - JarEntry je = componentJarFile.getJarEntry("jre/bin/ruby"); + Archive.FileEntry je = componentJarFile.getJarEntry("jre/bin/ruby"); // should succeed: installer.checkFileReplacement(existing, je); @@ -731,7 +741,7 @@ public void testValidateOverwriteDirectoryWithFile() throws IOException { setupComponentInstall("truffleruby2.jar"); Path offending = targetPath.resolve(SystemUtils.fromCommonString("jre/bin/ruby")); Files.createDirectories(offending); - ZipEntry entry = componentJarFile.getEntry("jre/bin/ruby"); + Archive.FileEntry entry = componentJarFile.getJarEntry("jre/bin/ruby"); exception.expect(IOException.class); exception.expectMessage("INSTALL_OverwriteWithFile"); @@ -745,7 +755,7 @@ public void testValidateOverwriteFileWithDirectory() throws IOException { Path offending = targetPath.resolve(SystemUtils.fromCommonString("jre/languages/ruby")); Files.createDirectories(offending.getParent()); Files.createFile(offending); - ZipEntry entry = componentJarFile.getEntry("jre/languages/ruby/"); + Archive.FileEntry entry = componentJarFile.getJarEntry("jre/languages/ruby/"); exception.expect(IOException.class); exception.expectMessage("INSTALL_OverwriteWithDirectory"); diff --git a/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/commands/ListTest.java b/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/commands/ListTest.java index 3ec76acd73ff..abe69eb994e5 100644 --- a/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/commands/ListTest.java +++ b/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/commands/ListTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,10 +31,9 @@ import java.net.URL; import java.util.Objects; import java.util.Properties; -import java.util.ResourceBundle; import org.graalvm.component.installer.CommandTestBase; import org.graalvm.component.installer.model.ComponentRegistry; -import org.graalvm.component.installer.persist.RemoteStorage; +import org.graalvm.component.installer.remote.RemotePropertiesStorage; import static org.junit.Assert.assertEquals; import org.junit.Rule; import org.junit.Test; @@ -47,12 +46,11 @@ public class ListTest extends CommandTestBase { @Rule public TestName name = new TestName(); - private RemoteStorage remoteStorage; + private RemotePropertiesStorage remoteStorage; private Properties catalogContents = new Properties(); - private static final ResourceBundle BUNDLE = ResourceBundle.getBundle("org.graalvm.component.installer.commands.Bundle"); private void initRemoteStorage() throws MalformedURLException { - this.remoteStorage = new RemoteStorage( + this.remoteStorage = new RemotePropertiesStorage( this, localRegistry, catalogContents, "1.0.0-rc3-dev_linux_amd64", new URL("http://go.to/graalvm")); this.registry = new ComponentRegistry(this, remoteStorage); } @@ -73,7 +71,7 @@ public void testAvailablePrintAll() throws Exception { @Override public String l10n(String key, Object... params) { if ("LIST_ComponentShortList".equals(key)) { - return reallyl10n(BUNDLE, key, params); + return reallyl10n(key, params); } return null; } diff --git a/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/commands/MockStorage.java b/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/commands/MockStorage.java index 382c848a83bb..89458e513600 100644 --- a/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/commands/MockStorage.java +++ b/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/commands/MockStorage.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,6 +31,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Collection; +import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.List; @@ -53,6 +54,7 @@ public class MockStorage implements ComponentStorage { DEFAULT_GRAAL_INFO.put(CAP_OS_NAME, "linux"); } + public Map> acceptedLicenses = new HashMap<>(); public List installed = new ArrayList<>(); public Map graalInfo = new HashMap<>(DEFAULT_GRAAL_INFO); public Map> replacedFiles = new HashMap<>(); @@ -105,4 +107,22 @@ public void updateReplacedFiles(Map> newReplacedFiles updatedReplacedFiles = newReplacedFiles; } + @Override + public Date licenseAccepted(ComponentInfo info, String licenseID) { + return acceptedLicenses.computeIfAbsent(info.getId(), (i) -> new HashMap<>()).get(licenseID); + } + + @Override + public void recordLicenseAccepted(ComponentInfo info, String licenseID, String text) throws IOException { + if (info == null) { + acceptedLicenses.clear(); + return; + } + Map acc = acceptedLicenses.computeIfAbsent(info.getId(), (i) -> new HashMap<>()); + if (licenseID != null) { + acc.put(licenseID, new Date()); + } else { + acc.clear(); + } + } } diff --git a/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/model/ComponentRegistryTest.java b/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/model/ComponentRegistryTest.java index 892cbc80dc9a..d4903ac82452 100644 --- a/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/model/ComponentRegistryTest.java +++ b/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/model/ComponentRegistryTest.java @@ -36,6 +36,7 @@ import org.graalvm.component.installer.FailedOperationException; import org.graalvm.component.installer.TestBase; import org.graalvm.component.installer.commands.MockStorage; +import org.graalvm.component.installer.jar.JarMetaLoader; import org.graalvm.component.installer.persist.ComponentPackageLoader; import org.junit.After; import org.junit.AfterClass; @@ -83,7 +84,7 @@ public void setUp() throws Exception { registry = new ComponentRegistry(this, mockStorage); try (JarFile jf = new JarFile(dataFile("truffleruby2.jar").toFile())) { - ComponentPackageLoader ldr = new ComponentPackageLoader(jf, this); + ComponentPackageLoader ldr = new JarMetaLoader(jf, this); rubyInfo = ldr.createComponentInfo(); ldr.loadPaths(); diff --git a/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/model/VerifierTest.java b/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/model/VerifierTest.java index 6337a371c7c0..b11244ffc3b8 100644 --- a/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/model/VerifierTest.java +++ b/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/model/VerifierTest.java @@ -29,6 +29,7 @@ import org.graalvm.component.installer.DependencyException; import org.graalvm.component.installer.TestBase; import org.graalvm.component.installer.commands.MockStorage; +import org.graalvm.component.installer.jar.JarMetaLoader; import org.graalvm.component.installer.persist.ComponentPackageLoader; import org.junit.Before; import org.junit.Rule; @@ -54,7 +55,7 @@ public void setUp() throws Exception { @Test public void testGraalCapabilitiesCaseInsensitive() throws Exception { try (JarFile jf = new JarFile(dataFile("truffleruby2.jar").toFile())) { - ComponentPackageLoader ldr = new ComponentPackageLoader(jf, this); + ComponentPackageLoader ldr = new JarMetaLoader(jf, this); rubyInfo = ldr.createComponentInfo(); ldr.loadPaths(); @@ -69,7 +70,7 @@ public void testGraalCapabilitiesCaseInsensitive() throws Exception { @Test public void testGraalCapabilitiesMismatch() throws Exception { try (JarFile jf = new JarFile(dataFile("truffleruby2.jar").toFile())) { - ComponentPackageLoader ldr = new ComponentPackageLoader(jf, this); + ComponentPackageLoader ldr = new JarMetaLoader(jf, this); rubyInfo = ldr.createComponentInfo(); ldr.loadPaths(); diff --git a/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/persist/ComponentPackageLoaderTest.java b/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/persist/ComponentPackageLoaderTest.java index ebd06942a3c0..85b377951308 100644 --- a/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/persist/ComponentPackageLoaderTest.java +++ b/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/persist/ComponentPackageLoaderTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -46,6 +46,7 @@ import org.graalvm.component.installer.CommonConstants; import org.graalvm.component.installer.FailedOperationException; import org.graalvm.component.installer.TestBase; +import org.graalvm.component.installer.jar.JarMetaLoader; import org.graalvm.component.installer.model.ComponentInfo; import org.junit.After; import org.junit.Assert; @@ -116,7 +117,7 @@ ComponentInfo info() throws IOException { if (jarData == null) { return new ComponentPackageLoader(data::getProperty, this).createComponentInfo(); } else { - return new ComponentPackageLoader(jarData, this).createComponentInfo(); + return new JarMetaLoader(jarData, this).createComponentInfo(); } } @@ -162,7 +163,7 @@ public void testWorkingDirectories() throws Exception { public void testCollectErrors() throws Exception { File f = dataFile("broken1.zip").toFile(); jf = new JarFile(f); - loader = new ComponentPackageLoader(jf, this).infoOnly(true); + loader = new JarMetaLoader(jf, this).infoOnly(true); info = loader.createComponentInfo(); List errs = new ArrayList<>(); @@ -181,7 +182,7 @@ public void testCollectErrors() throws Exception { private void setupLoader() throws IOException { File f = dataFile("data/truffleruby2.jar").toFile(); jf = new JarFile(f); - loader = new ComponentPackageLoader(jf, this); + loader = new JarMetaLoader(jf, this); info = loader.createComponentInfo(); } @@ -220,6 +221,15 @@ public void testComponetInfoFromJar() throws Exception { @Test public void testComponentLicensePath() throws Exception { setupLoader(); + delegateFeedback(new FeedbackAdapter() { + @Override + public String l10n(String key, Object... params) { + if (key.startsWith("LICENSE_")) { + return reallyl10n(key, params); + } + return null; + } + }); // must load file paths loader.loadPaths(); assertNotNull(info.getLicensePath()); diff --git a/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/persist/DirectoryStorageTest.java b/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/persist/DirectoryStorageTest.java index dd7ec574014a..e34fb9235770 100644 --- a/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/persist/DirectoryStorageTest.java +++ b/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/persist/DirectoryStorageTest.java @@ -53,6 +53,7 @@ import org.graalvm.component.installer.model.ComponentInfo; import org.junit.After; import org.junit.AfterClass; +import static org.junit.Assert.assertNotNull; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Rule; @@ -421,4 +422,28 @@ public void saveComponentFiles() throws Exception { assertEquals(golden, lines); } + @Test + public void testAcceptLicense() throws Exception { + copyDir("list1", registryPath); + ComponentInfo info = storage.loadComponentMetadata("fastr"); + + storage.recordLicenseAccepted(info, "cafebabe", "This is a dummy license"); + Path p = registryPath.resolve(SystemUtils.fromCommonString("licenses/cafebabe.accepted/org.graalvm.fastr")); + Path p2 = registryPath.resolve(SystemUtils.fromCommonString("licenses/cafebabe")); + assertTrue(Files.isReadable(p)); + assertEquals(Arrays.asList("This is a dummy license"), Files.readAllLines(p2)); + } + + @Test + public void testLicenseAccepted1() throws Exception { + copyDir("list1", registryPath); + ComponentInfo info = storage.loadComponentMetadata("fastr"); + ComponentInfo info2 = storage.loadComponentMetadata("ruby"); + + Path p = registryPath.resolve(SystemUtils.fromCommonString("licenses/cafebabe.accepted/org.graalvm.fastr")); + Files.createDirectories(p.getParent()); + Files.write(p, Arrays.asList("ahoj")); + assertNotNull(storage.licenseAccepted(info, "cafebabe")); + assertNull(storage.licenseAccepted(info2, "cafebabe")); + } } diff --git a/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/persist/NetworkTestBase.java b/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/persist/NetworkTestBase.java index 4964d8d0ab78..41cc00df89de 100644 --- a/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/persist/NetworkTestBase.java +++ b/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/persist/NetworkTestBase.java @@ -24,10 +24,10 @@ */ package org.graalvm.component.installer.persist; -import org.graalvm.component.installer.TestBase; +import org.graalvm.component.installer.CommandTestBase; import org.junit.Rule; -public class NetworkTestBase extends TestBase { +public class NetworkTestBase extends CommandTestBase { @Rule public ProxyResource proxyResource = new ProxyResource(); diff --git a/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/persist/list1/ruby.component b/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/persist/list1/ruby.component index e69de29bb2d1..de5590ad3753 100644 --- a/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/persist/list1/ruby.component +++ b/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/persist/list1/ruby.component @@ -0,0 +1,4 @@ +Bundle-Symbolic-Name: org.graalvm.rubt +Bundle-Name: Ruby implementation +Bundle-Version: 1.0 +Bundle-RequireCapability-graalvm_version: 0.32 diff --git a/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/persist/test/Handler.java b/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/persist/test/Handler.java index 561a1997a99c..6dfbc1a89fd6 100644 --- a/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/persist/test/Handler.java +++ b/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/persist/test/Handler.java @@ -30,15 +30,19 @@ import java.net.URL; import java.net.URLConnection; import java.net.URLStreamHandler; +import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.Map; import java.util.Set; public class Handler extends URLStreamHandler { private static Map bindings = Collections.synchronizedMap(new HashMap<>()); private static Map connections = Collections.synchronizedMap(new HashMap<>()); + private static Map> multiConnections = Collections.synchronizedMap(new HashMap<>()); private static Set visitedURLs = Collections.synchronizedSet(new HashSet<>()); private static Map httpProxyConnections = Collections.synchronizedMap(new HashMap<>()); @@ -53,6 +57,10 @@ public static void bind(String s, URLConnection con) { connections.put(s, con); } + public static void bindMulti(String s, URLConnection con) { + multiConnections.computeIfAbsent(s, (k) -> new ArrayList<>()).add(con); + } + public static void bindProxy(String s, URLConnection con) { httpProxyConnections.put(s, con); } @@ -60,6 +68,7 @@ public static void bindProxy(String s, URLConnection con) { public static void clear() { bindings.clear(); connections.clear(); + multiConnections.clear(); visitedURLs.clear(); } @@ -89,6 +98,14 @@ protected URLConnection openConnection(URL u, Proxy p) throws IOException { @Override protected URLConnection openConnection(URL u) throws IOException { URLConnection c = connections.get(u.toString()); + if (c == null) { + Collection col = multiConnections.getOrDefault(u.toString(), Collections.emptyList()); + Iterator it = col.iterator(); + if (it.hasNext()) { + c = it.next(); + it.remove(); + } + } return doOpenConnection(u, c); } diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/FilesFeature.java b/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/persist/test/TestCatalog.java similarity index 70% rename from substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/FilesFeature.java rename to vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/persist/test/TestCatalog.java index 815db24dfece..df56306c4ace 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/FilesFeature.java +++ b/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/persist/test/TestCatalog.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,19 +23,23 @@ * questions. */ -package com.oracle.svm.core.jdk; +package org.graalvm.component.installer.persist.test; -import org.graalvm.nativeimage.Feature; -import org.graalvm.nativeimage.ImageSingletons; +import org.graalvm.component.installer.ce.WebCatalog; -import com.oracle.svm.core.annotate.AutomaticFeature; - -@AutomaticFeature -public final class FilesFeature implements Feature { +/** + * Stub that accepts also "test:" URL scheme. + * + * @author sdedic + */ +public class TestCatalog extends WebCatalog { @Override - public void afterRegistration(AfterRegistrationAccess access) { - ImageSingletons.add(FilesSupport.class, new FilesSupport()); + protected boolean acceptURLScheme(String scheme) { + if ("test".equals(scheme)) { + return true; + } + return super.acceptURLScheme(scheme); } } diff --git a/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/persist/FileDownloaderTest.java b/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/remote/FileDownloaderTest.java similarity index 92% rename from vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/persist/FileDownloaderTest.java rename to vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/remote/FileDownloaderTest.java index 291f6e6dc7a0..80c6a3c91ee1 100644 --- a/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/persist/FileDownloaderTest.java +++ b/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/remote/FileDownloaderTest.java @@ -22,7 +22,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package org.graalvm.component.installer.persist; +package org.graalvm.component.installer.remote; import org.graalvm.component.installer.ChunkedConnection; import java.io.File; @@ -33,6 +33,7 @@ import java.net.URLConnection; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; +import org.graalvm.component.installer.persist.NetworkTestBase; import org.graalvm.component.installer.persist.test.Handler; import org.junit.Assert; import static org.junit.Assert.assertArrayEquals; @@ -40,13 +41,10 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import org.junit.Before; -import org.junit.Rule; import org.junit.Test; -import org.junit.rules.ExpectedException; import sun.net.ConnectionResetException; public class FileDownloaderTest extends NetworkTestBase { - @Rule public ExpectedException exception = ExpectedException.none(); class FA extends FeedbackAdapter { @@ -64,7 +62,9 @@ public String l10n(String key, Object... params) { } @Before + @Override public void setUp() throws Exception { + super.setUp(); delegateFeedback(new FA()); } @@ -99,7 +99,7 @@ public void testDownloadComputeDigest() throws Exception { dn.setShaDigest(new byte[0]); dn.download(); - byte[] check = RemoteStorage.toHashBytes(null, "b649fe3b9309d1b3ae4d2dbae70eebd4d2978af32cd1ce7d262ebf7e0f0f53fa", this); + byte[] check = RemotePropertiesStorage.toHashBytes(null, "b649fe3b9309d1b3ae4d2dbae70eebd4d2978af32cd1ce7d262ebf7e0f0f53fa", this); assertArrayEquals(check, dn.getDigest()); } @@ -225,12 +225,15 @@ public void connect() throws IOException { delegateFeedback(check); FileDownloader dn = new FileDownloader("test", u, this); + ProxyConnectionFactory pcf = new ProxyConnectionFactory(this, u); + dn.setConnectionFactory(pcf); + verbose = true; dn.setVerbose(true); dn.setDisplayProgress(true); - dn.envHttpProxy = "http://localhost:11111"; - dn.envHttpsProxy = "http://localhost:11111"; + pcf.envHttpProxy = "http://localhost:11111"; + pcf.envHttpsProxy = "http://localhost:11111"; synchronized (proxyConnect) { proxyConnect.nextChunk = 130 * 1024; @@ -274,8 +277,11 @@ public void connect() throws IOException { dn.setVerbose(true); dn.setDisplayProgress(true); - dn.envHttpProxy = "http://localhost:11111"; - dn.envHttpsProxy = "http://localhost:11111"; + ProxyConnectionFactory pcf = new ProxyConnectionFactory(this, u); + dn.setConnectionFactory(pcf); + + pcf.envHttpProxy = "http://localhost:11111"; + pcf.envHttpsProxy = "http://localhost:11111"; synchronized (directConnect) { directConnect.nextChunk = 130 * 1024; @@ -336,8 +342,11 @@ public void connect() throws IOException { dn.setVerbose(true); dn.setDisplayProgress(true); - dn.envHttpProxy = "http://localhost:11111"; - dn.envHttpsProxy = "http://localhost:11111"; + ProxyConnectionFactory pcf = new ProxyConnectionFactory(this, u); + dn.setConnectionFactory(pcf); + + pcf.envHttpProxy = "http://localhost:11111"; + pcf.envHttpsProxy = "http://localhost:11111"; dn.download(); } diff --git a/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/persist/RemoteCatalogDownloaderTest.java b/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/remote/RemoteCatalogDownloaderTest.java similarity index 83% rename from vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/persist/RemoteCatalogDownloaderTest.java rename to vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/remote/RemoteCatalogDownloaderTest.java index 2c7a3b3aa8be..e2e26a0636dc 100644 --- a/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/persist/RemoteCatalogDownloaderTest.java +++ b/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/remote/RemoteCatalogDownloaderTest.java @@ -22,42 +22,20 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package org.graalvm.component.installer.persist; +package org.graalvm.component.installer.remote; import java.net.ConnectException; import java.net.URL; -import java.nio.file.Path; import org.graalvm.component.installer.CommonConstants; import org.graalvm.component.installer.FailedOperationException; import org.graalvm.component.installer.MockURLConnection; -import org.graalvm.component.installer.commands.MockStorage; -import org.graalvm.component.installer.model.ComponentRegistry; +import org.graalvm.component.installer.persist.NetworkTestBase; import org.graalvm.component.installer.persist.test.Handler; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; -import org.junit.Before; -import org.junit.Rule; import org.junit.Test; -import org.junit.rules.ExpectedException; -import org.junit.rules.TemporaryFolder; public class RemoteCatalogDownloaderTest extends NetworkTestBase { - @Rule public ExpectedException exception = ExpectedException.none(); - - @Rule public TemporaryFolder folder = new TemporaryFolder(); - Path targetPath; - ComponentRegistry localRegistry; - MockStorage storage; - - ComponentRegistry registry; - - @Before - public void setUp() throws Exception { - targetPath = folder.newFolder("inst").toPath(); - storage = new MockStorage(); - localRegistry = new ComponentRegistry(this, storage); - } - @Test public void testDownloadCatalogBadGraalVersion() throws Exception { URL clu = getClass().getResource("catalog"); @@ -65,7 +43,7 @@ public void testDownloadCatalogBadGraalVersion() throws Exception { Handler.bind(u.toString(), clu); - RemoteCatalogDownloader d = new RemoteCatalogDownloader(this, localRegistry, u); + RemoteCatalogDownloader d = new RemoteCatalogDownloader(this, this, u); exception.expect(FailedOperationException.class); exception.expectMessage("REMOTE_UnsupportedGraalVersion"); d.openCatalog(); @@ -78,7 +56,7 @@ public void testDownloadCatalogCorrupted() throws Exception { Handler.bind(u.toString(), clu); - RemoteCatalogDownloader d = new RemoteCatalogDownloader(this, localRegistry, u); + RemoteCatalogDownloader d = new RemoteCatalogDownloader(this, this, u); exception.expect(FailedOperationException.class); exception.expectMessage("REMOTE_CorruptedCatalogFile"); d.openCatalog(); @@ -90,7 +68,7 @@ private void loadRegistry() throws Exception { Handler.bind(u.toString(), clu); storage.graalInfo.put(CommonConstants.CAP_GRAALVM_VERSION, "0.33-dev"); - RemoteCatalogDownloader d = new RemoteCatalogDownloader(this, localRegistry, u); + RemoteCatalogDownloader d = new RemoteCatalogDownloader(this, this, u); registry = d.openCatalog(); } @@ -116,7 +94,7 @@ public void testDownloadCorruptedCatalog() throws Exception { Handler.bind(u.toString(), clu); - RemoteCatalogDownloader d = new RemoteCatalogDownloader(this, localRegistry, u); + RemoteCatalogDownloader d = new RemoteCatalogDownloader(this, this, u); exception.expect(FailedOperationException.class); exception.expectMessage("REMOTE_CorruptedCatalogFile"); d.openCatalog(); @@ -129,7 +107,7 @@ public void testCannotConnectCatalog() throws Exception { Handler.bind(u.toString(), new MockURLConnection(clu.openConnection(), u, new ConnectException())); - RemoteCatalogDownloader d = new RemoteCatalogDownloader(this, localRegistry, u); + RemoteCatalogDownloader d = new RemoteCatalogDownloader(this, this, u); exception.expect(FailedOperationException.class); exception.expectMessage("REMOTE_ErrorDownloadCatalogProxy"); d.openCatalog(); diff --git a/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/persist/RemoteStorageTest.java b/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/remote/RemoteStorageTest.java similarity index 93% rename from vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/persist/RemoteStorageTest.java rename to vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/remote/RemoteStorageTest.java index 038eab3bcfc9..4d141aeb7091 100644 --- a/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/persist/RemoteStorageTest.java +++ b/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/remote/RemoteStorageTest.java @@ -22,7 +22,7 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package org.graalvm.component.installer.persist; +package org.graalvm.component.installer.remote; import java.io.IOException; import java.io.InputStream; @@ -50,7 +50,7 @@ public class RemoteStorageTest extends TestBase { private static final String TEST_GRAAL_VERSION = "0.33-dev_linux_amd64"; private static final String TEST_BASE_URL_DIR = "https://graalvm.io/"; private static final String TEST_BASE_URL = TEST_BASE_URL_DIR + "download/catalog"; - private RemoteStorage remStorage; + private RemotePropertiesStorage remStorage; private MockStorage storage; private ComponentRegistry localRegistry; private Properties catalogProps = new Properties(); @@ -61,7 +61,7 @@ public class RemoteStorageTest extends TestBase { public void setUp() throws Exception { storage = new MockStorage(); localRegistry = new ComponentRegistry(this, storage); - remStorage = new RemoteStorage(this, localRegistry, catalogProps, TEST_GRAAL_VERSION, + remStorage = new RemotePropertiesStorage(this, localRegistry, catalogProps, TEST_GRAAL_VERSION, new URL(TEST_BASE_URL)); try (InputStream is = getClass().getResourceAsStream("catalog")) { catalogProps.load(is); @@ -163,13 +163,13 @@ public void testLoadMetadataMalformed() throws Exception { @Test public void testHashString() throws Exception { - byte[] bytes = RemoteStorage.toHashBytes("test", truffleruby2HashString, this); + byte[] bytes = RemotePropertiesStorage.toHashBytes("test", truffleruby2HashString, this); assertArrayEquals(truffleruby2Hash, bytes); } @Test public void testHashStringDivided() throws Exception { - byte[] bytes = RemoteStorage.toHashBytes("test", truffleruby2HashString2, this); + byte[] bytes = RemotePropertiesStorage.toHashBytes("test", truffleruby2HashString2, this); assertArrayEquals(truffleruby2Hash, bytes); } diff --git a/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/persist/catalog b/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/remote/catalog similarity index 100% rename from vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/persist/catalog rename to vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/remote/catalog diff --git a/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/persist/catalog.bad1 b/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/remote/catalog.bad1 similarity index 100% rename from vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/persist/catalog.bad1 rename to vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/remote/catalog.bad1 diff --git a/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/persist/catalog.bad2 b/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/remote/catalog.bad2 similarity index 100% rename from vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/persist/catalog.bad2 rename to vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/remote/catalog.bad2 diff --git a/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/persist/catalogCorrupted b/vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/remote/catalogCorrupted similarity index 100% rename from vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/persist/catalogCorrupted rename to vm/src/org.graalvm.component.installer.test/src/org/graalvm/component/installer/remote/catalogCorrupted diff --git a/vm/src/org.graalvm.component.installer/src/META-INF/services/org.graalvm.component.installer.ComponentArchiveReader b/vm/src/org.graalvm.component.installer/src/META-INF/services/org.graalvm.component.installer.ComponentArchiveReader new file mode 100644 index 000000000000..2b94a769fb65 --- /dev/null +++ b/vm/src/org.graalvm.component.installer/src/META-INF/services/org.graalvm.component.installer.ComponentArchiveReader @@ -0,0 +1 @@ +org.graalvm.component.installer.ce.JarPackageProvider diff --git a/vm/src/org.graalvm.component.installer/src/META-INF/services/org.graalvm.component.installer.SoftwareChannel b/vm/src/org.graalvm.component.installer/src/META-INF/services/org.graalvm.component.installer.SoftwareChannel new file mode 100644 index 000000000000..4c5017c60402 --- /dev/null +++ b/vm/src/org.graalvm.component.installer/src/META-INF/services/org.graalvm.component.installer.SoftwareChannel @@ -0,0 +1 @@ +org.graalvm.component.installer.ce.WebCatalog diff --git a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/Archive.java b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/Archive.java new file mode 100644 index 000000000000..f6c635ce6f06 --- /dev/null +++ b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/Archive.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.graalvm.component.installer; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.channels.ReadableByteChannel; + +/** + * Simple abstraction over an archive, so that both JAR and RPMs (or other packaging) can be read. + * + * @author sdedic + */ +public interface Archive extends Iterable, AutoCloseable { + /** + * Opens an input stream for the entry. + * + * @param e file entry + * @return the input stream + * @throws IOException on I/O error + */ + InputStream getInputStream(FileEntry e) throws IOException; + + /** + * Checks that contents of the entry matches the given file. If the archive stores checksums, + * the method may not need do byte comparison but can only compute checksum/hash on the supplied + * content and compare to archive's info. + * + * @param bc the existing content + * @param entry archive entry + * @return true if the content is the same + * @throws IOException on I/O error + */ + boolean checkContentsMatches(ReadableByteChannel bc, FileEntry entry) throws IOException; + + /** + * Verifies integrity of the archive. + * + * @param input options for verificaion + * @return true, if the archive has been verified + * @throws IOException + */ + boolean verifyIntegrity(CommandInput input) throws IOException; + + @Override + void close() throws IOException; + + /** + * Represents a single entry in the archive. + */ + interface FileEntry { + /** + * Returns name of the entry. Directory names should end with "/". + * + * @return entry name + */ + String getName(); + + /** + * @return true, if the entry represents a directory + */ + boolean isDirectory(); + + /** + * True, if the entry is a symbolic link. + * + * @return True, if the entry represents a symbolic link + */ + boolean isSymbolicLink(); + + /** + * Link target for symbolic links. + * + * @return target path + * @throws java.io.IOException if the link's target could not be read + * @throws IllegalStateException if the entry is not {@link #isSymbolicLink()}. + */ + String getLinkTarget() throws IOException; + + /** + * @return size of the content, only valid for regular files. + */ + long getSize(); + } +} diff --git a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/Bundle.properties b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/Bundle.properties index 3ed33cb11cf0..acabab63f872 100644 --- a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/Bundle.properties +++ b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/Bundle.properties @@ -15,6 +15,8 @@ INFO_Usage=\n\ \tgu rebuild-images rebuilds native images. Use -h for detailed usage\n\ \n\ Common options:\n\ + \t--auto-yes\n\ + \t-A say YES or ACCEPT to all questions\n\ \t--catalog\n\ \t-c treat parameters as component IDs from catalog of GraalVM components. This is the default.\n\ \t--local-file\n\ @@ -25,15 +27,21 @@ INFO_Usage=\n\ \t-h print help.\n\ \t--no-progress\n\ \t-n do not display download progress.\n\ + \t--non-interactive\n\ + \t-N noninteractive mode. Fail when input is required.\n\ \t--url\n\ \t-u interpret parameters as URLs of packaged components.\n\ \t--verbose\n\ \t-v be verbose. Prints versions and dependency info.\n\ -\n\ +{0}\n\ Use \n\ \tgu -h\n\ to get specific help. +INFO_UsageExtensions=\n\ + Additonal options:\n\{0}\n + + ERROR_MissingCommand=No command given. ERROR_UnknownCommand=Unknown command: {0} # {0} - option, including the leading - @@ -57,7 +65,9 @@ ERROR_InvalidGraalVMDirectory=The GraalVM directory {0} is invalid. ERROR_MultipleSourcesUnsupported=The Updater cannot download from catalog and from specific URL at the same time. \ Please run the Updater separately for components from a catalog and components from custom URLs. INSTALLER_Error=Error: {0} -INSTALLER_InvalidCatalogURL=Catalog URL is invalid: {0} +# INSTALLER_InvalidCatalogURL=Catalog URL is invalid: {0} +# {0} - filename +ERROR_UnknownFileFormat=Unknown format. STORAGE_CorruptedComponentStorage=Component metadata storage is corrupted. STORAGE_InvalidReleaseFile=Invalid GraalVM release file. @@ -87,20 +97,7 @@ VERIFY_ComponentExists=The same component {0} ({1}) is already installed in vers VERIFY_Dependency_Failed=Component {0} could not be installed. It requires {1} {2}, but the GraalVM provides {3} VERIFY_CapabilityMissing=None -MSG_Downloading=Downloading: {0} -MSG_DownloadingVerbose=Downloading: {0} (source: {1}) -MSG_DownloadingFrom=Downloading from: {0} -# There's an important trailing space in the next key -MSG_DownloadReceivingBytes=Receiving {0} kB: -# The progress bar should have a frame and exactly 20 space characters -MSG_DownloadProgress=[ ] -MSG_DownloadProgressSignChar=# -MSG_DownloadingDone=Done. -# {0} - original error message -ERR_ComputeDigest=Error computing digest: {0} -# {0} - expected fingerprint, {1} computed fingerprint -ERR_FileDigestError=Corrupted file, digest does not match. Expected {0}, got {1} - +REMOTE_CannotDeleteLocalFile=Could not delete temporary file {0}: {1} REMOTE_UnknownComponentId=Unknown component: {0} REMOTE_UnknownComponentMaybeFile=Unknown component: {0}.\n\ Note: a file {0} exists; to use component stored locally, use -L option \n\ @@ -128,3 +125,10 @@ URL_ErrorDownloadingComponent=Error downloading component from {0}: {1} COMPONENT_Ambiguous=Component id {0} is ambiguous. Please use qualfied IDs. COMPONENT_AmbiguousIdFound=Component id {0} is ambiguous. It may match {0} or {1}. Please use qualified IDs. + +# {0} - error message +ERROR_RecordLicenseAccepted=WARNING: Could not write license acceptance: {0} +ERROR_UserInput=User input error: {0} +ERROR_Aborted=Aborted. +ERROR_SoftwareChannelBroken=Could not initialize software channel: {0} +ERROR_NoninteractiveInput=Installation in non-interactive mode required user input and was aborted. \ No newline at end of file diff --git a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/BundleConstants.java b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/BundleConstants.java index dd736dcd14e3..f101c840da4d 100644 --- a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/BundleConstants.java +++ b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/BundleConstants.java @@ -34,6 +34,8 @@ public class BundleConstants { public static final String BUNDLE_REQUIRED = "Bundle-RequireCapability"; // NOI18N public static final String GRAALVM_CAPABILITY = "org.graalvm"; // NOI18N public static final String BUNDLE_POLYGLOT_PART = "x-GraalVM-Polyglot-Part"; // NOI18N + public static final String BUNDLE_LICENSE_TYPE = "x-GraalVM-License-Type"; // NOI18N + public static final String BUNDLE_LICENSE_PATH = "x-GraalVM-License-Path"; // NOI18N /** * Extended optional attribute; marks directories, which should be removed completely without @@ -53,4 +55,9 @@ public class BundleConstants { * Post-install message. In the future more x-GraalVM-Message might appear */ public static final String BUNDLE_MESSAGE_POSTINST = "x-GraalVM-Message-PostInst"; // NOI18N + + /** + * Version key in the release file. + */ + public static final String GRAAL_VERSION = "graalvm_version"; // NOI18N } diff --git a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/CatalogIterable.java b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/CatalogIterable.java deleted file mode 100644 index 2c2d4b943b57..000000000000 --- a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/CatalogIterable.java +++ /dev/null @@ -1,294 +0,0 @@ -/* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package org.graalvm.component.installer; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.net.URL; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.function.Supplier; -import java.util.jar.JarFile; -import org.graalvm.component.installer.model.ComponentInfo; -import org.graalvm.component.installer.model.ComponentRegistry; -import org.graalvm.component.installer.persist.ComponentPackageLoader; -import org.graalvm.component.installer.persist.FileDownloader; -import org.graalvm.component.installer.persist.MetadataLoader; - -public class CatalogIterable implements ComponentIterable { - private final CommandInput input; - private final Feedback feedback; - private final Supplier registrySupplier; - private ComponentRegistry remoteRegistry; - private boolean verifyJars; - - public CatalogIterable(CommandInput input, Feedback feedback, Supplier remote) { - this.input = input; - this.feedback = feedback; - this.registrySupplier = remote; - } - - public boolean isVerifyJars() { - return verifyJars; - } - - @Override - public void setVerifyJars(boolean verifyJars) { - this.verifyJars = verifyJars; - } - - @Override - public Iterator iterator() { - return new It(); - } - - ComponentRegistry getRegistry() { - if (remoteRegistry == null) { - remoteRegistry = registrySupplier.get(); - } - return remoteRegistry; - } - - private class It implements Iterator { - private void thrownUnknown(String fname, boolean throwUnknown) { - File f = new File(fname); - if (f.exists() && f.isFile()) { - throw feedback.failure("REMOTE_UnknownComponentMaybeFile", null, fname); - } else if (throwUnknown) { - throw feedback.failure("REMOTE_UnknownComponentId", null, fname); - } - } - - @Override - public boolean hasNext() { - return input.hasParameter(); - } - - @Override - public ComponentParam next() { - String s = input.nextParameter(); - ComponentInfo info; - try { - if (getRegistry().findComponent(s.toLowerCase()) == null) { - thrownUnknown(s, true); - } - - info = getRegistry().loadSingleComponent(s.toLowerCase(), false); - if (info == null) { - thrownUnknown(s, true); - } - } catch (FailedOperationException ex) { - thrownUnknown(s, false); - throw ex; - } - boolean progress = input.optValue(Commands.OPTION_NO_DOWNLOAD_PROGRESS) == null; - RemoteComponentParam param = new RemoteComponentParam( - info, - feedback.l10n("REMOTE_ComponentFileLabel", s), - s, - feedback, progress); - param.setVerifyJars(verifyJars); - return param; - } - } - - static class RemoteComponentParam implements ComponentParam, MetadataLoader { - private final URL remoteURL; - private final String dispName; - private final String spec; - private final Feedback feedback; - private final ComponentInfo catalogInfo; - private ComponentInfo fileInfo; - private final boolean progress; - - private boolean verifyJars; - private JarFile file; - private MetadataLoader fileLoader; - private boolean complete; - - RemoteComponentParam(ComponentInfo catalogInfo, String dispName, String spec, Feedback feedback, boolean progress) { - this.catalogInfo = catalogInfo; - this.dispName = dispName; - this.spec = spec; - this.feedback = feedback; - this.progress = progress; - this.remoteURL = catalogInfo.getRemoteURL(); - } - - RemoteComponentParam(URL remoteURL, String dispName, String spec, Feedback feedback, boolean progress) { - this.catalogInfo = null; - this.dispName = dispName; - this.spec = spec; - this.feedback = feedback; - this.remoteURL = remoteURL; - this.progress = progress; - } - - @Override - public String getSpecification() { - return spec; - } - - @Override - public String getDisplayName() { - return dispName; - } - - @Override - public MetadataLoader createMetaLoader() throws IOException { - if (catalogInfo != null) { - return this; - } else { - return createFileLoader(); - } - } - - public void setVerifyJars(boolean verifyJars) { - this.verifyJars = verifyJars; - } - - @Override - public MetadataLoader createFileLoader() throws IOException { - if (fileLoader != null) { - return fileLoader; - } - - JarFile f = getFile(); - fileLoader = new ComponentPackageLoader(f, feedback) { - @Override - public ComponentInfo createComponentInfo() { - ComponentInfo i = super.createComponentInfo(); - i.setRemoteURL(remoteURL); - complete = true; - fileInfo = i; - return i; - } - - }; - this.file = f; - return fileLoader; - } - - @Override - public JarFile getFile() throws IOException { - if (file != null) { - return file; - } - FileDownloader dn = new FileDownloader( - feedback.l10n("REMOTE_ComponentFileLabel", spec), - remoteURL, feedback); - if (catalogInfo != null) { - dn.setShaDigest(catalogInfo.getShaDigest()); - } - dn.setDisplayProgress(progress); - try { - dn.download(); - file = new JarFile(dn.getLocalFile(), verifyJars); - } catch (FileNotFoundException ex) { - throw feedback.failure("REMOTE_ErrorDownloadingNotExist", ex, spec, remoteURL); - } - return file; - } - - @Override - public boolean isComplete() { - return complete; - } - - @Override - public void close() throws IOException { - if (fileLoader != null) { - fileLoader.close(); - } else if (file != null) { - file.close(); - } - } - - @Override - public ComponentInfo getComponentInfo() { - return fileInfo != null ? fileInfo : catalogInfo; - } - - @Override - public List getErrors() { - return Collections.emptyList(); - } - - @Override - public JarFile getJarFile() { - try { - return getFile(); - } catch (IOException ex) { - throw feedback.failure("REMOTE_ErrorDownloadingComponent", ex, spec, remoteURL, ex.getLocalizedMessage()); - } - } - - @Override - public String getLicensePath() { - return null; - } - - @Override - public MetadataLoader infoOnly(boolean only) { - return this; - } - - @Override - public boolean isNoVerifySymlinks() { - return true; - } - - @Override - public void loadPaths() { - } - - @Override - public Map loadPermissions() throws IOException { - return null; - } - - @Override - public Map loadSymlinks() throws IOException { - return null; - } - - @Override - public void setNoVerifySymlinks(boolean noVerifySymlinks) { - } - - @Override - public String getFullPath() { - return remoteURL.toString(); - } - - @Override - public String getShortName() { - return remoteURL.getFile(); - } - } -} diff --git a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/CommandInput.java b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/CommandInput.java index 0d250c1b2055..bd36ff867f6e 100644 --- a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/CommandInput.java +++ b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/CommandInput.java @@ -85,4 +85,9 @@ public interface CommandInput { * @return option value; {@code null}, if the option is not present */ String optValue(String option); + + default String optValue(String option, String defV) { + String s = optValue(option); + return s == null ? defV : s; + } } diff --git a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/Commands.java b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/Commands.java index ee7426fcda11..6cddab32c5e0 100644 --- a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/Commands.java +++ b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/Commands.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -143,7 +143,7 @@ public interface Commands { * Interpret parameters as remote component IDs, uses user-defined catalog URL. */ String OPTION_FOREIGN_CATALOG = "C"; - String LONG_OPTION_FOREIGN_CATALOG = "user-catalog"; + String LONG_OPTION_FOREIGN_CATALOG = "custom-catalog"; /** * Interpret parameters as URLs. @@ -163,4 +163,15 @@ public interface Commands { String OPTION_FAIL_EXISTING = "i"; // NOI18N String LONG_OPTION_FAIL_EXISTING = "fail-existing"; // NOI18N + /** + * Automatic YES to all questions. + */ + String OPTION_AUTO_YES = "A"; + String LONG_OPTION_AUTO_YES = "auto-yes"; + + /** + * Abort on all prompts except YES/NO. + */ + String OPTION_NON_INTERACTIVE = "N"; + String LONG_OPTION_NON_INTERACTIVE = "non-interactive"; } diff --git a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/CommonConstants.java b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/CommonConstants.java index c00f99d0b01f..fba46d91df3d 100644 --- a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/CommonConstants.java +++ b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/CommonConstants.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -77,4 +77,11 @@ public class CommonConstants { * Key in release file with catalog URL. */ public static final String RELEASE_CATALOG_KEY = "component_catalog"; // NOI18N + + /** + * Default installation dir encoded in RPM packages. The installer will strip this prefix to + * relocate the package contents. + */ + public static final String BUILTIN_INSTALLATION_DIR = "/usr/lib/graalvm"; // NOI18N + } diff --git a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/ComponentArchiveReader.java b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/ComponentArchiveReader.java new file mode 100644 index 000000000000..3a31a842d232 --- /dev/null +++ b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/ComponentArchiveReader.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.graalvm.component.installer; + +import java.io.IOException; +import java.nio.file.Path; +import org.graalvm.component.installer.persist.MetadataLoader; + +/** + * Recognizes file format and creates and loads metadata for it. + * + * @author sdedic + */ +public interface ComponentArchiveReader { + /** + * Creates a MetadataLoader for the file. The method returns {@code null}, if it does not + * support the file format. + * + * @param p path to the file + * @param fileStart bytes at the start of the file; min 8 bytes. + * @param feedback output interface + * @param verify verify archive integrity + * @return Metadata loader instance or {@code null} if unsupported. + * @throws java.io.IOException if the loader creation or file open fails + */ + MetadataLoader createLoader(Path p, byte[] fileStart, Feedback feedback, boolean verify) throws IOException; +} diff --git a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/ComponentInstaller.java b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/ComponentInstaller.java index 91250ee801b8..d451c1f9a266 100644 --- a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/ComponentInstaller.java +++ b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/ComponentInstaller.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,11 +24,11 @@ */ package org.graalvm.component.installer; +import org.graalvm.component.installer.remote.CatalogIterable; import java.io.File; import java.io.IOError; import org.graalvm.component.installer.model.ComponentRegistry; import java.io.IOException; -import java.net.MalformedURLException; import java.net.URISyntaxException; import java.net.URL; import java.nio.file.AccessDeniedException; @@ -41,10 +41,14 @@ import java.util.Arrays; import java.util.Collections; import java.util.HashMap; +import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.ResourceBundle; +import java.util.ServiceConfigurationError; +import java.util.ServiceLoader; +import java.util.function.Consumer; import java.util.logging.Level; import java.util.logging.Logger; import static org.graalvm.component.installer.CommonConstants.PATH_COMPONENT_STORAGE; @@ -55,13 +59,15 @@ import org.graalvm.component.installer.commands.RebuildImageCommand; import org.graalvm.component.installer.commands.UninstallCommand; import org.graalvm.component.installer.persist.DirectoryStorage; -import org.graalvm.component.installer.persist.RemoteCatalogDownloader; +import org.graalvm.component.installer.remote.RemoteCatalogDownloader; /** * The launcher. */ public final class ComponentInstaller { - private static final String GRAAL_DEFAULT_RELATIVE_PATH = "../../.."; + private static final Logger LOG = Logger.getLogger(ComponentInstaller.class.getName()); + + public static final String GRAAL_DEFAULT_RELATIVE_PATH = "../../.."; private String[] mainArguments; private String command; @@ -77,6 +83,10 @@ public final class ComponentInstaller { @SuppressWarnings("deprecation") static void initCommands() { + // not necessary except for tests to cleanup extra items + commands.clear(); + globalOptions.clear(); + commands.put("install", new InstallCommand()); // NOI18N commands.put("uninstall", new UninstallCommand()); // NOI18N commands.put("list", new ListInstalledCommand()); // NOI18N @@ -103,15 +113,39 @@ static void initCommands() { globalOptions.put(Commands.LONG_OPTION_FOREIGN_CATALOG, Commands.OPTION_FOREIGN_CATALOG); globalOptions.put(Commands.LONG_OPTION_URLS, Commands.OPTION_URLS); globalOptions.put(Commands.LONG_OPTION_NO_DOWNLOAD_PROGRESS, Commands.OPTION_NO_DOWNLOAD_PROGRESS); - } - static { - initCommands(); + globalOptions.put(Commands.OPTION_AUTO_YES, ""); + globalOptions.put(Commands.LONG_OPTION_AUTO_YES, Commands.OPTION_AUTO_YES); + + globalOptions.put(Commands.OPTION_NON_INTERACTIVE, ""); + globalOptions.put(Commands.LONG_OPTION_NON_INTERACTIVE, Commands.OPTION_NON_INTERACTIVE); } private static final ResourceBundle BUNDLE = ResourceBundle.getBundle( "org.graalvm.component.installer.Bundle"); // NOI18N + private static void forSoftwareChannels(boolean report, Consumer callback) { + ServiceLoader channels = ServiceLoader.load(SoftwareChannel.class); + for (Iterator it = channels.iterator(); it.hasNext();) { + try { + SoftwareChannel ch = it.next(); + callback.accept(ch); + } catch (ServiceConfigurationError | Exception ex) { + if (report) { + LOG.log(Level.SEVERE, + MessageFormat.format(BUNDLE.getString("ERROR_SoftwareChannelBroken"), ex.getLocalizedMessage())); + } + } + } + } + + static { + initCommands(); + forSoftwareChannels(true, (ch) -> { + globalOptions.putAll(ch.globalOptions()); + }); + } + private ComponentInstaller(String[] args) { this.mainArguments = args; } @@ -122,7 +156,28 @@ private static void printUsage() { } private static void printHelp() { - System.err.println(BUNDLE.getString("INFO_Usage")); // NOI18N + StringBuilder extra = new StringBuilder(); + Environment[] env = new Environment[1]; + + forSoftwareChannels(false, (ch) -> { + if (env[0] == null) { + env[0] = new Environment("help", Collections.emptyList(), Collections.emptyMap()); + } + ch.init(env[0], env[0]); + String s = ch.globalOptionsHelp(); + if (s != null) { + extra.append(s); + } + }); + String extraS; + + if (extra.length() != 0) { + extraS = MessageFormat.format(BUNDLE.getString("INFO_UsageExtensions"), extra.toString()); + } else { + extraS = ""; // NOI18N + } + + System.err.println(MessageFormat.format(BUNDLE.getString("INFO_Usage"), extraS)); // NOI18N } static void printErr(String messageKey, Object... args) { @@ -164,7 +219,7 @@ private int processCommand() { parameters = go.getPositionalParameters(); try { - env = new Environment(command, null, parameters, optValues); + env = new Environment(command, parameters, optValues); finddGraalHome(); env.setGraalHome(graalHomePath); env.setLocalRegistry(new ComponentRegistry(env, new DirectoryStorage( @@ -187,6 +242,12 @@ private int processCommand() { err("ERROR_MultipleSourcesUnsupported"); } + if (env.hasOption(Commands.OPTION_AUTO_YES)) { + env.setAutoYesEnabled(true); + } + if (env.hasOption(Commands.OPTION_NON_INTERACTIVE)) { + env.setNonInteractive(true); + } if (optValues.containsKey(Commands.OPTION_FILES)) { env.setFileIterable(new FileIterable(env, env)); } else if (optValues.containsKey(Commands.OPTION_URLS)) { @@ -195,9 +256,9 @@ private int processCommand() { catalogURL = optValues.get(Commands.OPTION_FOREIGN_CATALOG); RemoteCatalogDownloader downloader = new RemoteCatalogDownloader( env, - env.getLocalRegistry(), + env, getCatalogURL(env)); - env.setComponentRegistry(downloader); + env.setComponentRegistry(downloader::openCatalog); env.setFileIterable(new CatalogIterable(env, env, downloader)); } cmdHandler.init(env, env.withBundle(cmdHandler.getClass())); @@ -220,6 +281,9 @@ private int processCommand() { } catch (MetadataException ex) { env.error("INSTALLER_InvalidMetadata", ex, ex.getMessage()); // NOI18N return 3; + } catch (UserAbortException ex) { + env.error("ERROR_Aborted", ex, ex.getMessage()); // NOI18N + return 4; } catch (InstallerStopException ex) { env.error("INSTALLER_Error", ex, ex.getMessage()); // NOI18N return 3; @@ -276,6 +340,9 @@ public void run() { cmdlineParams = new LinkedList<>(Arrays.asList(mainArguments)); System.exit(processCommand()); + } catch (UserAbortException ex) { + System.err.println(MessageFormat.format( + BUNDLE.getString("ERROR_Aborted"), ex.getMessage())); // NOI18N } catch (Exception ex) { System.err.println(MessageFormat.format( BUNDLE.getString("ERROR_InternalError"), ex.getMessage())); // NOI18N @@ -285,7 +352,7 @@ public void run() { } - private URL getCatalogURL(Feedback f) { + private String getCatalogURL(Feedback f) { String def; if (catalogURL != null) { def = catalogURL; @@ -303,11 +370,7 @@ private URL getCatalogURL(Feedback f) { } } String s = System.getProperty(CommonConstants.SYSPROP_CATALOG_URL, def); - try { - return new URL(s); - } catch (MalformedURLException ex) { - throw f.failure("INSTALLER_InvalidCatalogURL", ex, s); - } + return s; } /** diff --git a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/ComponentParam.java b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/ComponentParam.java index 8b9d0a736cb5..1119f33ffb49 100644 --- a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/ComponentParam.java +++ b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/ComponentParam.java @@ -26,7 +26,7 @@ import java.io.Closeable; import java.io.IOException; -import java.util.jar.JarFile; +import org.graalvm.component.installer.model.ComponentInfo; import org.graalvm.component.installer.persist.MetadataLoader; public interface ComponentParam extends Closeable { @@ -38,8 +38,6 @@ public interface ComponentParam extends Closeable { MetadataLoader createFileLoader() throws IOException; - JarFile getFile() throws IOException; - boolean isComplete(); @Override @@ -48,4 +46,14 @@ public interface ComponentParam extends Closeable { String getFullPath(); String getShortName(); + + /** + * Completes the metadata. The implementation should ensure that {@link #createMetaLoader} will + * populate the {@link ComponentInfo} with complete metadata. If {@code fileList} is true, the + * list of files must be also initialized. File contents do not need to be available locally. + * + * @throws IOException if the metadata load fails. + * @return MetadataLoader capable of loading the ComponentInfo + */ + MetadataLoader completeMetadata() throws IOException; } diff --git a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/DownloadURLIterable.java b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/DownloadURLIterable.java index 765e075bc911..cc8168462bd3 100644 --- a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/DownloadURLIterable.java +++ b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/DownloadURLIterable.java @@ -24,10 +24,14 @@ */ package org.graalvm.component.installer; +import org.graalvm.component.installer.remote.RemoteComponentParam; +import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; +import java.nio.file.Path; import java.util.Iterator; -import org.graalvm.component.installer.CatalogIterable.RemoteComponentParam; +import org.graalvm.component.installer.FileIterable.FileComponent; +import org.graalvm.component.installer.persist.MetadataLoader; public class DownloadURLIterable implements ComponentIterable { private final Feedback feedback; @@ -67,10 +71,29 @@ public ComponentParam next() { throw feedback.failure("URL_InvalidDownloadURL", ex, s, ex.getLocalizedMessage()); } boolean progress = input.optValue(Commands.OPTION_NO_DOWNLOAD_PROGRESS) == null; - RemoteComponentParam p = new RemoteComponentParam(u, s, s, feedback, progress); + RemoteComponentParam p = new DownloadURLParam(u, s, s, feedback, progress); p.setVerifyJars(verifyJars); return p; } } + static class DownloadURLParam extends RemoteComponentParam { + + DownloadURLParam(URL remoteURL, String dispName, String spec, Feedback feedback, boolean progress) { + super(remoteURL, dispName, spec, feedback, progress); + } + + @Override + protected MetadataLoader metadataFromLocal(Path localFile) throws IOException { + // cowardly use autodetection developed for local files. + FileComponent fc = new FileComponent(localFile.toFile(), isVerifyJars(), getFeedback()); + return fc.createFileLoader(); + } + + @Override + public MetadataLoader completeMetadata() throws IOException { + return createFileLoader(); + } + } + } diff --git a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/Environment.java b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/Environment.java index 5f3e651568f9..8cd4a3afab04 100644 --- a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/Environment.java +++ b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/Environment.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,8 +24,13 @@ */ package org.graalvm.component.installer; +import java.io.Console; +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; import org.graalvm.component.installer.model.ComponentRegistry; import java.io.PrintStream; +import java.net.URL; import java.nio.file.Path; import java.text.MessageFormat; import java.util.HashMap; @@ -38,7 +43,7 @@ /** * Implementation of feedback and input for commands. */ -final class Environment implements Feedback, CommandInput { +public final class Environment implements Feedback, CommandInput { private static final ResourceBundle BUNDLE = ResourceBundle.getBundle( "org.graalvm.component.installer.Bundle"); @@ -47,27 +52,48 @@ final class Environment implements Feedback, CommandInput { private final Map options; private final boolean verbose; private final ResourceBundle bundle; + private InputStream in = System.in; private PrintStream err = System.err; private PrintStream out = System.out; private Supplier registrySupplier; private ComponentRegistry localRegistry; private boolean stacktraces; private Iterable fileIterable; - private Map fileMap = new HashMap<>(); + private Map fileMap = new HashMap<>(); private boolean allOutputToErr; - + private boolean autoYesEnabled; + private boolean nonInteractive; private Path graalHome; + Environment(String commandName, List parameters, Map options) { + this(commandName, (String) null, parameters, options); + } + Environment(String commandName, InstallerCommand cmdInstance, List parameters, Map options) { + this(commandName, makeBundle(cmdInstance), parameters, options); + } + + public void setIn(InputStream input) { + this.in = input; + } + + private static String makeBundle(InstallerCommand cmdInstance) { + if (cmdInstance == null) { + return null; + } + String s = cmdInstance.getClass().getName(); + s = s.substring(0, s.lastIndexOf('.')); + return s; + } + + public Environment(String commandName, String bundlePackage, List parameters, Map options) { this.commandName = commandName; this.parameters = new LinkedList<>(parameters); this.options = options; this.verbose = options.containsKey(Commands.OPTION_VERBOSE); this.stacktraces = options.containsKey(Commands.OPTION_DEBUG); - if (cmdInstance != null) { - String s = cmdInstance.getClass().getName(); - s = s.substring(0, s.lastIndexOf('.')); - bundle = ResourceBundle.getBundle(s + ".Bundle"); // NOI18N + if (bundlePackage != null) { + bundle = ResourceBundle.getBundle(bundlePackage + ".Bundle"); // NOI18N } else { bundle = BUNDLE; } @@ -75,6 +101,22 @@ final class Environment implements Feedback, CommandInput { this.fileIterable = new FileIterable(this, this); } + public boolean isAutoYesEnabled() { + return autoYesEnabled; + } + + public void setAutoYesEnabled(boolean autoYesEnabled) { + this.autoYesEnabled = autoYesEnabled; + } + + public boolean isNonInteractive() { + return nonInteractive; + } + + public void setNonInteractive(boolean nonInteractive) { + this.nonInteractive = nonInteractive; + } + public boolean isAllOutputToErr() { return allOutputToErr; } @@ -287,14 +329,25 @@ public boolean backspace(int chars, boolean beVerbose) { } @Override - public String translateFilename(Path f) { - return Environment.this.translateFilename(f); + public String acceptLine(boolean autoYes) { + return Environment.this.acceptLine(autoYes); + } + + @Override + public String acceptPassword() { + return Environment.this.acceptPassword(); } @Override - public void bindFilename(Path file, String label) { - Environment.this.bindFilename(file, label); + public void addLocalFileCache(URL location, Path local) { + Environment.this.addLocalFileCache(location, local); } + + @Override + public Path getLocalCache(URL location) { + return Environment.this.getLocalCache(location); + } + }; } @@ -374,13 +427,66 @@ public String optValue(String optName) { return options.get(optName); } + public boolean hasOption(String optName) { + return optValue(optName) != null; + } + + public char acceptCharacter() { + try { + int input = in.read(); + if (input == -1) { + throw new UserAbortException(); + } + return (char) input; + } catch (EOFException ex) { + throw new UserAbortException(ex); + } catch (IOException ex) { + throw withBundle(Environment.class).failure("ERROR_UserInput", ex, ex.getMessage()); + } + } + @Override - public String translateFilename(Path f) { - return fileMap.getOrDefault(f, f.toString()); + public String acceptLine(boolean autoYes) { + if (autoYes && isAutoYesEnabled()) { + return AUTO_YES; + } + if (isNonInteractive()) { + throw new NonInteractiveException(withBundle(Environment.class).l10n("ERROR_NoninteractiveInput")); + } + StringBuilder sb = new StringBuilder(); + char c; + while ((c = acceptCharacter()) != '\n') { + if (c == 0x08) { + sb.delete(sb.length() - 1, sb.length()); + } else { + sb.append(c); + } + } + return sb.toString(); } @Override - public void bindFilename(Path file, String label) { - fileMap.put(file, label); + public String acceptPassword() { + if (isNonInteractive()) { + throw new NonInteractiveException(withBundle(Environment.class).l10n("ERROR_NoninteractiveInput")); + } + Console console = System.console(); + if (console != null) { + console.flush(); + return String.copyValueOf(console.readPassword()); + } else { + return acceptLine(false); + } } + + @Override + public void addLocalFileCache(URL location, Path local) { + fileMap.put(location, local); + } + + @Override + public Path getLocalCache(URL location) { + return fileMap.get(location); + } + } diff --git a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/Feedback.java b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/Feedback.java index 5ec953ab76da..06f152f31aee 100644 --- a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/Feedback.java +++ b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/Feedback.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,6 +24,7 @@ */ package org.graalvm.component.installer; +import java.net.URL; import java.nio.file.Path; /** @@ -31,6 +32,11 @@ * of exceptions etc. Allows to work with different Bundles. */ public interface Feedback { + /** + * Returned from {@link #acceptLine} for an automatic accept. + */ + String AUTO_YES = ""; + /** * Formats a message on stderr. * @@ -129,7 +135,36 @@ public interface Feedback { boolean backspace(int chars, boolean beVerbose); - String translateFilename(Path f); + /** + * Waits for user input confirmed by ENTER. + * + * @param autoYes returns the {@link #AUTO_YES} if yes-to-all was specified on commandline. + * @return accepted line. + */ + String acceptLine(boolean autoYes); - void bindFilename(Path file, String label); + /** + * Allows to enter password using console services. + * + * @return password + */ + String acceptPassword(); + + /** + * Provides a cache for remote files. The URL contents should be downloaded and stored to the + * `local` file. The file should be marked with {@link java.io.File#deleteOnExit()}. + * + * @param location remote location + * @param local locally cached content + */ + void addLocalFileCache(URL location, Path local); + + /** + * Returns a local cache for the location. Returns {@code null}, if the content is not locally + * available. + * + * @param location the remote location + * @return locally stored content or {@code null}. + */ + Path getLocalCache(URL location); } diff --git a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/FileIterable.java b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/FileIterable.java index 8d9c91a07ba1..0ecd8811fe69 100644 --- a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/FileIterable.java +++ b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/FileIterable.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,9 +26,12 @@ import java.io.File; import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; +import java.nio.channels.ReadableByteChannel; +import java.nio.file.StandardOpenOption; import java.util.Iterator; -import java.util.jar.JarFile; -import org.graalvm.component.installer.persist.ComponentPackageLoader; +import java.util.ServiceLoader; import org.graalvm.component.installer.persist.MetadataLoader; public class FileIterable implements ComponentIterable { @@ -75,8 +78,7 @@ public ComponentParam next() { public static class FileComponent implements ComponentParam { private final File localFile; - private ComponentPackageLoader loader; - private JarFile jf; + private MetadataLoader loader; private final boolean verifyJars; private final Feedback feedback; @@ -88,21 +90,36 @@ public FileComponent(File localFile, boolean verifyJars, Feedback feedback) { @Override public MetadataLoader createMetaLoader() throws IOException { - if (loader == null) { - if (jf == null) { - jf = new JarFile(localFile, verifyJars); + if (loader != null) { + return loader; + } + byte[] fileStart = null; + + try (ReadableByteChannel ch = FileChannel.open(localFile.toPath(), StandardOpenOption.READ)) { + ByteBuffer bb = ByteBuffer.allocate(8); + ch.read(bb); + fileStart = bb.array(); + } + + for (ComponentArchiveReader provider : ServiceLoader.load(ComponentArchiveReader.class)) { + MetadataLoader ldr = provider.createLoader(localFile.toPath(), fileStart, feedback, verifyJars); + if (ldr != null) { + loader = ldr; + return ldr; } - loader = new ComponentPackageLoader(jf, feedback); } - return loader; + throw feedback.failure("ERROR_UnknownFileFormat", null, localFile.toString()); + } + + @Override + public MetadataLoader completeMetadata() throws IOException { + return createMetaLoader(); } @Override public void close() throws IOException { if (loader != null) { loader.close(); - } else if (jf != null) { - jf.close(); } } @@ -111,16 +128,6 @@ public MetadataLoader createFileLoader() throws IOException { return createMetaLoader(); } - @Override - public JarFile getFile() throws IOException { - if (loader != null) { - return loader.getJarFile(); - } else { - jf = new JarFile(localFile, verifyJars); - return jf; - } - } - @Override public boolean isComplete() { return true; @@ -145,6 +152,5 @@ public String getFullPath() { public String getShortName() { return localFile.getName(); } - } } diff --git a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/GenerateCatalog.java b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/GenerateCatalog.java index a25a896de727..ff953442706a 100644 --- a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/GenerateCatalog.java +++ b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/GenerateCatalog.java @@ -49,9 +49,10 @@ import java.util.jar.JarFile; import static org.graalvm.component.installer.BundleConstants.GRAALVM_CAPABILITY; import static org.graalvm.component.installer.CommonConstants.CAP_GRAALVM_VERSION; +import org.graalvm.component.installer.jar.JarMetaLoader; import org.graalvm.component.installer.model.ComponentInfo; import org.graalvm.component.installer.persist.ComponentPackageLoader; -import org.graalvm.component.installer.persist.FileDownloader; +import org.graalvm.component.installer.remote.FileDownloader; /** * @@ -173,7 +174,7 @@ public void run() throws IOException { private void readCommandLine() throws IOException { SimpleGetopt getopt = new SimpleGetopt(OPTIONS) { @Override - RuntimeException err(String messageKey, Object... args) { + public RuntimeException err(String messageKey, Object... args) { ComponentInstaller.printErr(messageKey, args); System.exit(1); return null; @@ -181,7 +182,7 @@ RuntimeException err(String messageKey, Object... args) { }.ignoreUnknownCommands(true); getopt.setParameters(new LinkedList<>(params)); getopt.process(); - this.env = new Environment(null, null, getopt.getPositionalParameters(), getopt.getOptValues()); + this.env = new Environment(null, getopt.getPositionalParameters(), getopt.getOptValues()); this.env.setAllOutputToErr(true); String pb = env.optValue("p"); @@ -310,7 +311,7 @@ private void generateCatalog() throws IOException { File f = spec.f; byte[] hash = computeHash(f); try (JarFile jf = new JarFile(f)) { - ComponentPackageLoader ldr = new ComponentPackageLoader(jf, env); + ComponentPackageLoader ldr = new JarMetaLoader(jf, env); ComponentInfo info = ldr.createComponentInfo(); String prefix = findComponentPrefix(info); if (!graalVMReleases.containsKey(prefix)) { diff --git a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/NonInteractiveException.java b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/NonInteractiveException.java new file mode 100644 index 000000000000..2a81971222f1 --- /dev/null +++ b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/NonInteractiveException.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.graalvm.component.installer; + +/** + * Represents a failure when a non-interactive mode required user input. + * + * @author sdedic + */ +public class NonInteractiveException extends InstallerStopException { + private static final long serialVersionUID = 2L; + + public NonInteractiveException(String message) { + super(message); + } +} diff --git a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/SimpleGetopt.java b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/SimpleGetopt.java index f4964d66306a..9e6104f91bef 100644 --- a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/SimpleGetopt.java +++ b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/SimpleGetopt.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -67,7 +67,7 @@ public SimpleGetopt ignoreUnknownCommands(boolean ignore) { } // overridable by tests - RuntimeException err(String messageKey, Object... args) { + public RuntimeException err(String messageKey, Object... args) { throw ComponentInstaller.err(messageKey, args); } @@ -124,10 +124,6 @@ Map computeAbbreviations(Collection optNames) { String fullName = result.get(s); if (fullName == null) { result.put(s, o); - } else if (fullName.length() == 2) { - continue; - } else if (o.length() == 2) { - result.put(o, o); } else { result.put(s, NO_ABBREV); } @@ -312,7 +308,7 @@ public String getCommand() { } public void addCommandOptions(String commandName, Map optSpec) { - commandOptions.put(commandName, optSpec); + commandOptions.put(commandName, new HashMap<>(optSpec)); } // test only diff --git a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/SoftwareChannel.java b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/SoftwareChannel.java new file mode 100644 index 000000000000..006fd5421dfb --- /dev/null +++ b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/SoftwareChannel.java @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.graalvm.component.installer; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.Collections; +import java.util.Map; +import org.graalvm.component.installer.model.ComponentInfo; +import org.graalvm.component.installer.model.ComponentRegistry; +import org.graalvm.component.installer.remote.FileDownloader; +import org.graalvm.component.installer.persist.MetadataLoader; + +/** + * An abstraction of software delivery channel. The channel provides a Registry of available + * components. It can augment or change the download process and it can interpret the downloaded + * files to support different file formats. + *

+ * + * @author sdedic + */ +public interface SoftwareChannel { + /** + * True, if the channel is willing to handle the URL. URL is passed as a String so that custom + * protocols may be used without registering an URLStreamHandlerFactory. + * + * @param urlString url string, including the scheme + * @return true, if the channel is willing to work with the URL + */ + boolean setupLocation(String urlString); + + /** + * Binds the channel implementation to I/O. + * + * @param input input from the commandline + * @param output user i/o + */ + void init(CommandInput input, Feedback output); + + /** + * Loads and provides access to the component registry. + * + * @return registry instance + */ + ComponentRegistry getRegistry(); + + /** + * Creates a MetadataLoader with full metadata. This may require download of the actual + * component + * + * @param info ComponentInfo to complete + * @param ldr metadata loader for the component + * @return MetadataLoader that provides full component info. + */ + MetadataLoader completeMetadata(MetadataLoader ldr, ComponentInfo info) throws IOException; + + /** + * Configures the downloader with specific options. The downloader may be even replaced with a + * different instance. + * + * @param dn the downloader to configure + * @return the downloader instance. + */ + FileDownloader configureDownloader(FileDownloader dn); + + /** + * Creates metadata + archive loader from a downloaded file. + * + * @param localFile the local file. + * @verify if true, verify archives + * @return loader instance + */ + MetadataLoader createLocalFileLoader(Path localFile, boolean verify) throws IOException; + + /** + * Adds options to the set of global options. Global options allow to accept specific options + * from commandline, which would otherwise cause an error (unknown option). + * + * @return global options to add. + */ + default Map globalOptions() { + return Collections.emptyMap(); + } + + /** + * Provides help for the injected global options. + * + * @return String to append to the displayed help, or {@code null} for empty message. + */ + default String globalOptionsHelp() { + return null; + } +} diff --git a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/SystemUtils.java b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/SystemUtils.java index 92539932e663..2aa6a9c66055 100644 --- a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/SystemUtils.java +++ b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/SystemUtils.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it diff --git a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/URLConnectionFactory.java b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/URLConnectionFactory.java new file mode 100644 index 000000000000..6eb6443e09e8 --- /dev/null +++ b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/URLConnectionFactory.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package org.graalvm.component.installer; + +import java.io.IOException; +import java.net.URL; +import java.net.URLConnection; +import java.util.function.Consumer; + +/** + * + * @author sdedic + */ +public interface URLConnectionFactory { + URLConnection createConnection(URL u, Consumer configCallback) throws IOException; +} diff --git a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/FilesSupport.java b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/UserAbortException.java similarity index 68% rename from substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/FilesSupport.java rename to vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/UserAbortException.java index 7e2541d2c44e..5b00a5b81869 100644 --- a/substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/FilesSupport.java +++ b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/UserAbortException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,17 +22,20 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ +package org.graalvm.component.installer; -package com.oracle.svm.core.jdk; - -import java.nio.file.spi.FileTypeDetector; -import java.util.concurrent.CopyOnWriteArrayList; +/** + * + * @author sdedic + */ +public class UserAbortException extends InstallerStopException { + private static final long serialVersionUID = 1753L; -public final class FilesSupport { - CopyOnWriteArrayList installedDetectors = new CopyOnWriteArrayList<>(); - FileTypeDetector defaultFileTypeDetector = sun.nio.fs.DefaultFileTypeDetector.create(); + public UserAbortException() { + super(null); + } - public void addFileTypeDetector(FileTypeDetector ftd) { - installedDetectors.add(ftd); + public UserAbortException(Throwable cause) { + super(null, cause); } } diff --git a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/ce/Bundle.properties b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/ce/Bundle.properties new file mode 100644 index 000000000000..77b45f9c9168 --- /dev/null +++ b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/ce/Bundle.properties @@ -0,0 +1,14 @@ +REMOTE_CatalogLabel=Component catalog +REMOTE_ErrorDownloadCatalogNotFound=The component catalog was not found at {0}. +REMOTE_ErrorDownloadCatalog=Error downloading component catalog from {0}: {1}. \n\ + Please check your connection and proxy settings. \ + If your machine is behind a proxy, environment variables (http_proxy, https_proxy, ...) must be set appropriately. +REMOTE_ErrorDownloadCatalogProxy=Component catalog is unreachable. \n\ + Please check your connection and proxy settings. \ + If your machine is behind a proxy, environment variables (http_proxy, https_proxy, ...) must be set appropriately. +REMOTE_CorruptedCatalogFile=Catalog file {0} is corrupted. +REMOTE_ComponentFileLabel=Component {0} +REMOTE_UnsupportedGraalVersion=Unsupported Graal VM version: {0}, platform {1}/{2} +REMOTE_ErrorDownloadingComponent=Error downloading component {0} from {1}: {2} +REMOTE_ErrorDownloadingNotExist=Package for component {0} is not accessible at {1} +REMOTE_InvalidURL={0} is not a valid catalog URL: {1} diff --git a/compiler/src/org.graalvm.compiler.java/src/org/graalvm/compiler/java/IntegerExactOpSpeculation.java b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/ce/JarPackageProvider.java similarity index 50% rename from compiler/src/org.graalvm.compiler.java/src/org/graalvm/compiler/java/IntegerExactOpSpeculation.java rename to vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/ce/JarPackageProvider.java index 8fe5762cd51e..ccd637c47c1d 100644 --- a/compiler/src/org.graalvm.compiler.java/src/org/graalvm/compiler/java/IntegerExactOpSpeculation.java +++ b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/ce/JarPackageProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,41 +22,29 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package org.graalvm.compiler.java; - -import jdk.vm.ci.meta.ResolvedJavaMethod; -import jdk.vm.ci.meta.SpeculationLog.SpeculationReason; - -public final class IntegerExactOpSpeculation implements SpeculationReason { - - public enum IntegerExactOp { - INTEGER_ADD_EXACT, - INTEGER_INCREMENT_EXACT, - INTEGER_SUBTRACT_EXACT, - INTEGER_DECREMENT_EXACT, - INTEGER_MULTIPLY_EXACT - } - - protected final String methodDescriptor; - protected final IntegerExactOp op; - - public IntegerExactOpSpeculation(ResolvedJavaMethod method, IntegerExactOp op) { - this.methodDescriptor = method.format("%H.%n(%p)%R"); - this.op = op; - } - - @Override - public int hashCode() { - return methodDescriptor.hashCode() * 31 + op.ordinal(); - } - +package org.graalvm.component.installer.ce; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.jar.JarFile; +import org.graalvm.component.installer.Feedback; +import org.graalvm.component.installer.jar.JarMetaLoader; +import org.graalvm.component.installer.persist.MetadataLoader; +import org.graalvm.component.installer.ComponentArchiveReader; + +/** + * Loads JAR files. + * + * @author sdedic + */ +public final class JarPackageProvider implements ComponentArchiveReader { @Override - public boolean equals(Object obj) { - if (obj instanceof IntegerExactOpSpeculation) { - IntegerExactOpSpeculation other = (IntegerExactOpSpeculation) obj; - return op.equals(other.op) && methodDescriptor.equals(other.methodDescriptor); + public MetadataLoader createLoader(Path p, byte[] magic, Feedback feedback, boolean verify) throws IOException { + if ((magic[0] == 0x50) && (magic[1] == 0x4b) && + (magic[2] == 0x03) && (magic[3] == 0x04)) { + return new JarMetaLoader(new JarFile(p.toFile(), verify), feedback); + } else { + return null; } - return false; } - } diff --git a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/persist/RemoteCatalogDownloader.java b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/ce/WebCatalog.java similarity index 61% rename from vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/persist/RemoteCatalogDownloader.java rename to vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/ce/WebCatalog.java index dc1b8959a1f9..6ca53675827a 100644 --- a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/persist/RemoteCatalogDownloader.java +++ b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/ce/WebCatalog.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,50 +22,90 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package org.graalvm.component.installer.persist; +package org.graalvm.component.installer.ce; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.net.ConnectException; +import java.net.MalformedURLException; import java.net.NoRouteToHostException; import java.net.URL; +import java.nio.file.Path; import java.util.Collections; import java.util.Enumeration; import java.util.Map; import java.util.Properties; -import java.util.function.Supplier; +import java.util.jar.JarFile; import org.graalvm.component.installer.BundleConstants; +import org.graalvm.component.installer.CommandInput; import org.graalvm.component.installer.CommonConstants; import org.graalvm.component.installer.Feedback; +import org.graalvm.component.installer.jar.JarMetaLoader; import org.graalvm.component.installer.model.ComponentRegistry; import org.graalvm.component.installer.model.ComponentStorage; +import org.graalvm.component.installer.remote.FileDownloader; +import org.graalvm.component.installer.persist.MetadataLoader; +import org.graalvm.component.installer.remote.RemotePropertiesStorage; +import org.graalvm.component.installer.SoftwareChannel; +import org.graalvm.component.installer.model.ComponentInfo; -public class RemoteCatalogDownloader implements Supplier { - private final Feedback feedback; - private final URL catalogURL; - private final ComponentRegistry local; - private ComponentRegistry catalog; +/** + * + * @author sdedic + */ +public class WebCatalog implements SoftwareChannel { + private CommandInput input; + private Feedback feedback; + private String urlString; + private URL catalogURL; + private ComponentRegistry local; + + @Override + public boolean setupLocation(String url) { + int schColon = url.indexOf(':'); + if (schColon == -1) { + return false; + } + String scheme = url.toLowerCase().substring(0, schColon); + if (acceptURLScheme(scheme)) { + urlString = url; + return true; + } else { + return false; + } + } - public RemoteCatalogDownloader(Feedback feedback, ComponentRegistry local, URL catalogURL) { - this.feedback = feedback; - this.catalogURL = catalogURL; - this.local = local; + protected boolean acceptURLScheme(String scheme) { + switch (scheme) { + case "http": + case "https": + case "ftp": + case "ftps": + return true; + } + return false; } @Override - public ComponentRegistry get() { - if (catalog == null) { - catalog = openCatalog(); - } - return catalog; + public void init(CommandInput in, Feedback out) { + assert this.input == null; + + this.input = in; + this.feedback = out.withBundle(WebCatalog.class); + this.local = in.getLocalRegistry(); } @SuppressWarnings("unchecked") - public ComponentRegistry openCatalog() { - FileDownloader dn = new FileDownloader(feedback.l10n("REMOTE_CatalogLabel"), catalogURL, feedback); + @Override + public ComponentRegistry getRegistry() { + FileDownloader dn; try { + catalogURL = new URL(urlString); + dn = new FileDownloader(feedback.l10n("REMOTE_CatalogLabel"), catalogURL, feedback); dn.download(); + } catch (MalformedURLException ex) { + throw feedback.failure("REMOTE_InvalidURL", ex, catalogURL, ex.getLocalizedMessage()); } catch (NoRouteToHostException | ConnectException ex) { throw feedback.failure("REMOTE_ErrorDownloadCatalogProxy", ex, catalogURL, ex.getLocalizedMessage()); } catch (FileNotFoundException ex) { @@ -108,7 +148,22 @@ public ComponentRegistry openCatalog() { graalCaps.get(CommonConstants.CAP_OS_ARCH)); } } - ComponentStorage storage = new RemoteStorage(feedback, local, props, sb.toString(), catalogURL); + ComponentStorage storage = new RemotePropertiesStorage(feedback, local, props, sb.toString(), catalogURL); return new ComponentRegistry(feedback, storage); } + + @Override + public MetadataLoader createLocalFileLoader(Path localFile, boolean verify) throws IOException { + return new JarMetaLoader(new JarFile(localFile.toFile(), verify), feedback); + } + + @Override + public FileDownloader configureDownloader(FileDownloader dn) { + return dn; + } + + @Override + public MetadataLoader completeMetadata(MetadataLoader ldr, ComponentInfo info) { + return ldr; + } } diff --git a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/commands/AbstractInstaller.java b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/commands/AbstractInstaller.java new file mode 100644 index 000000000000..2fcfe9f12145 --- /dev/null +++ b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/commands/AbstractInstaller.java @@ -0,0 +1,200 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.graalvm.component.installer.commands; + +import java.io.Closeable; +import java.io.IOException; +import java.nio.file.Path; +import java.util.Collections; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import org.graalvm.component.installer.Archive; +import org.graalvm.component.installer.Feedback; +import org.graalvm.component.installer.model.ComponentInfo; +import org.graalvm.component.installer.model.ComponentRegistry; +import org.graalvm.component.installer.model.Verifier; + +/** + * Abstract configurable base for installer. The real installer and test stub inherits from this + * base; are configured by InstallCommand. + * + * @author sdedic + */ +public abstract class AbstractInstaller implements Closeable { + protected final Feedback feedback; + protected final ComponentInfo componentInfo; + protected final ComponentRegistry registry; + protected final Archive archive; + + private Map permissions = Collections.emptyMap(); + private Map symlinks = Collections.emptyMap(); + private Set componentDirectories = Collections.emptySet(); + private final Set trackedPaths = new HashSet<>(); + private Path licenseRelativePath; + private boolean replaceDiferentFiles; + private boolean replaceComponents; + private boolean dryRun; + private boolean ignoreRequirements; + private boolean failOnExisting; + private Path installPath; + + public AbstractInstaller(Feedback fb, ComponentInfo info, + ComponentRegistry reg, Archive a) { + this.feedback = fb; + this.componentInfo = info; + this.registry = reg; + this.archive = a; + } + + public boolean isFailOnExisting() { + return failOnExisting; + } + + public void setFailOnExisting(boolean failOnExisting) { + this.failOnExisting = failOnExisting; + } + + public boolean isReplaceDiferentFiles() { + return replaceDiferentFiles; + } + + public void setReplaceDiferentFiles(boolean replaceDiferentFiles) { + this.replaceDiferentFiles = replaceDiferentFiles; + } + + public boolean isReplaceComponents() { + return replaceComponents; + } + + public void setReplaceComponents(boolean replaceComponents) { + this.replaceComponents = replaceComponents; + } + + public boolean isIgnoreRequirements() { + return ignoreRequirements; + } + + public void setIgnoreRequirements(boolean ignoreRequirements) { + this.ignoreRequirements = ignoreRequirements; + } + + public ComponentInfo getComponentInfo() { + return componentInfo; + } + + public Set getTrackedPaths() { + return trackedPaths; + } + + public Map getPermissions() { + return permissions; + } + + public void setPermissions(Map permissions) { + this.permissions = permissions; + } + + public Map getSymlinks() { + return symlinks; + } + + public void setSymlinks(Map symlinks) { + this.symlinks = symlinks; + } + + public abstract void revertInstall(); + + public Verifier validateRequirements() { + return new Verifier(feedback, registry, componentInfo) + .ignoreRequirements(ignoreRequirements) + .replaceComponents(replaceComponents) + .ignoreExisting(!failOnExisting) + .validateRequirements(); + } + + /** + * Validates requirements, decides whether to install. Returns false if the component should be + * skipped. + * + * @return true, if the component should be installed + * @throws IOException + */ + public abstract boolean validateAll() throws IOException; + + public abstract void validateFiles() throws IOException; + + public abstract void validateSymlinks() throws IOException; + + public abstract void processPermissions() throws IOException; + + public abstract void createSymlinks() throws IOException; + + public Path getInstallPath() { + return installPath; + } + + public void setInstallPath(Path installPath) { + this.installPath = installPath; + } + + public Path getLicenseRelativePath() { + return licenseRelativePath; + } + + public void setLicenseRelativePath(Path licenseRelativePath) { + this.licenseRelativePath = licenseRelativePath; + } + + @Override + public void close() throws IOException { + if (archive != null) { + archive.close(); + } + } + + public boolean isDryRun() { + return dryRun; + } + + public void setDryRun(boolean dryRun) { + this.dryRun = dryRun; + } + + public abstract boolean isRebuildPolyglot(); + + public Set getComponentDirectories() { + return componentDirectories; + } + + public void setComponentDirectories(Set componentDirectories) { + this.componentDirectories = componentDirectories; + } + + protected void addTrackedPath(String path) { + trackedPaths.add(path); + } + +} diff --git a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/commands/Bundle.properties b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/commands/Bundle.properties index 6d53f5c1216a..9f3272bfc2b8 100644 --- a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/commands/Bundle.properties +++ b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/commands/Bundle.properties @@ -42,7 +42,7 @@ To rebuild and refresh the native binaries, use the following command:\ INSTALLER_FailError={0} -UNINSTALL_UnknownComponentId=Unknown component id: {0}. Use "component-install list" to get list of components and their IDs. +UNINSTALL_UnknownComponentId=Unknown component id: {0}. Use "gu list" to get list of components and their IDs. UNINSTALL_IgnoreFailed=Failed uninstallation of {0} ignored. The error was: {1} UNINSTALL_ErrorDuringProcessing=Uninstallation failed: {0} UNINSTALL_UninstallingComponent=Uninstalling: {0} @@ -69,7 +69,7 @@ Options:\n\ \t--no-progress\n\ \t-n do not display download progress.\n\ \t--overwrite\n\ - \t-o overwrite different files.\n\ + \t-o overwrite different files.\n\ \t--replace\n\ \t-r replace existing components.\n\ \t--url\n\ @@ -206,7 +206,7 @@ ComponentId Version Component name\n\ # # The following bundle is actually a String.format format pattern. # The most important is the width and precision which should follow the header widths -INFO_ComponentShortList=%1$-25.25s%2$-20.20%3$s +INFO_ComponentShortList=%1$-25.25s%2$-20.20s%3$s INFO_ComponentBasicInfo=\ Filename: {3}\n\ @@ -227,3 +227,23 @@ INFO_ComponentDependencyIndent=\t- {0} REBUILD_ToolRelativePath=jre/lib/svm/bin/rebuild-images REBUILD_ImageToolInterrupted=The image rebuild has been interrupted. REBUILD_RewriteRebuildToolName=gu rebuild-images + + +INSTALL_LicenseAcceptedAt=License {0} already accepted on {1} for {2} ({3}). +INSTALL_AcceptLicense=The component(s) {0} requires to accept the following license: {1} +INSTALL_AcceptSingleLicense=Enter "Y" to confirm and accept all the license(s). Enter "R" to the see license text.\n\ + Any other input will abort installation: +INSTALL_LicensesToAccept=Component(s) selected for install require(s) to accept license(s): +INSTALL_LicenseComponentStart={0} +INSTALL_LicenseComponentCont={0}, {1} +INSTALL_AcceptLicenseComponents=\t{2}: {0} - {1} +INSTALL_AcceptAllLicensesPrompt=Enter "Y" to confirm and accept all the license(s).\n\ + Enter a number to the see license text. Enter "a" to abort the operation: +INSTALL_AcceptLicensePrompt=Do you accept this license (Y for "yes", any other input for "no") ? : +INSTALL_AcceptPromptResponseYes=Yes|y +INSTALL_AcceptPromptResponseRead=List|Lst|L|Read|R +INSTALL_AcceptPromptResponseAbort=Abort|a|exit|e +INSTALL_LicenseNumberInvalidEntry=The entry is not valid. Enter a number (1-{0}, "Y" or "a": +INSTALL_LicenseNumberOutOfRange=Invalid number, must be 1-{0}: +INSTALL_LicenseNotFound=License not found in archive at {0}. +INSTALL_ErrorHandlingLicenses=Error during processing of license(s): {0} diff --git a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/commands/InfoCommand.java b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/commands/InfoCommand.java index 281dfd271392..cd158c6265ee 100644 --- a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/commands/InfoCommand.java +++ b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/commands/InfoCommand.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -134,16 +134,10 @@ public int execute() throws IOException { // verifyjar set to false, as Verifier is not supported in SVM // components.add(ldr = new ComponentPackageLoader(new JarFile(f, false), // feedback)); - MetadataLoader ldr = cp.createMetaLoader(); - - components.add(cp); - loadComponentDetails(cp, ldr); - // registerFile(f, ldr.getComponentInfo(), ldr); - addComponent(cp, ldr.getComponentInfo()); + processComponentParam(cp); } catch (ZipException ex) { if (ignoreOpenErrors) { feedback.error("INFO_ErrorOpeningBundle", ex, - // feedback.translateFilename(f.toPath()), cp.getDisplayName(), ex.getLocalizedMessage()); } else { @@ -152,7 +146,6 @@ public int execute() throws IOException { } catch (MetadataException ex) { if (ignoreOpenErrors) { feedback.error("INFO_CorruptedBundleMetadata", ex, - // feedback.translateFilename(f.toPath()), cp.getDisplayName(), ex.getOffendingHeader(), ex.getLocalizedMessage()); @@ -162,7 +155,6 @@ public int execute() throws IOException { } catch (IOException ex) { if (ignoreOpenErrors) { feedback.error("INFO_ErrorReadingBundle", ex, - // feedback.translateFilename(f.toPath()), cp.getDisplayName(), ex.getLocalizedMessage()); } else { @@ -188,12 +180,22 @@ public int execute() throws IOException { return 0; } + void processComponentParam(ComponentParam cp) throws IOException { + MetadataLoader ldr = cp.createMetaLoader(); + + components.add(cp); + loadComponentDetails(cp, ldr); + // registerFile(f, ldr.getComponentInfo(), ldr); + addComponent(cp, ldr.getComponentInfo()); + + } + void registerFile(ComponentParam param, ComponentInfo info, MetadataLoader ldr) { files.put(info, param); map.put(info, ldr); } - void loadComponentDetails(ComponentParam param, MetadataLoader ldr) { + void loadComponentDetails(ComponentParam param, MetadataLoader ldr) throws IOException { ldr.infoOnly(true); ComponentInfo info = ldr.getComponentInfo(); registerFile(param, info, ldr); diff --git a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/commands/InstallCommand.java b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/commands/InstallCommand.java index d6dac62544e9..6baae4a73a49 100644 --- a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/commands/InstallCommand.java +++ b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/commands/InstallCommand.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -36,6 +36,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.zip.ZipException; +import org.graalvm.component.installer.Archive; import org.graalvm.component.installer.CommandInput; import org.graalvm.component.installer.Commands; import org.graalvm.component.installer.CommonConstants; @@ -45,6 +46,7 @@ import org.graalvm.component.installer.InstallerCommand; import org.graalvm.component.installer.InstallerStopException; import org.graalvm.component.installer.SystemUtils; +import org.graalvm.component.installer.UserAbortException; import org.graalvm.component.installer.model.ComponentInfo; import org.graalvm.component.installer.persist.MetadataLoader; @@ -119,6 +121,7 @@ public int execute() throws IOException { if (validateBeforeInstall) { return 0; } + executeStep(this::acceptLicenses, false); executeStep(this::completeInstallers, false); executeStep(this::doInstallation, false); // execute the post-install steps for all processed installers @@ -130,19 +133,35 @@ public int execute() throws IOException { return 0; } + private Map> licensesToAccept = new LinkedHashMap<>(); + + void addLicenseToAccept(String id, MetadataLoader ldr) { + licensesToAccept.computeIfAbsent(id, (x) -> new ArrayList<>()).add(ldr); + } + void prepareInstallation() throws IOException { for (ComponentParam p : input.existingFiles()) { - feedback.output("INSTALL_VerboseProcessingArchive", /* feedback.translateFilename( */p.getDisplayName()); + feedback.output("INSTALL_VerboseProcessingArchive", p.getDisplayName()); current = p.getSpecification(); - Installer inst = createInstaller(p, - validateDownload ? p.createFileLoader() : p.createMetaLoader()); + MetadataLoader ldr = validateDownload ? p.createFileLoader() : p.createMetaLoader(); + Installer inst = createInstaller(p, ldr); boolean keep = force || inst.validateRequirements().shouldInstall(); if (!keep) { // component will be skipped, do not bother with validation feedback.output("INSTALL_ComponentAlreadyInstalled", inst.getComponentInfo().getName(), inst.getComponentInfo().getId()); continue; } + + if (ldr.getLicenseType() != null) { + String path = ldr.getLicensePath(); + if (path != null) { + inst.setLicenseRelativePath(SystemUtils.fromCommonString(ldr.getLicensePath())); + } + String licId = ldr.getLicenseID(); + addLicenseToAccept(licId, ldr); + } + installers.add(inst); if (p.isComplete()) { realInstallers.put(p, inst); @@ -184,6 +203,8 @@ void executeStep(Step s, boolean close) throws IOException { } catch (ZipException ex) { feedback.error("INSTALL_InvalidComponentArchive", ex, current); throw ex; + } catch (UserAbortException ex) { + throw ex; } catch (InstallerStopException | IOException ex) { if (ignoreFailures) { if (current == null) { @@ -354,23 +375,30 @@ Installer createInstaller(ComponentParam p, MetadataLoader ldr) throws IOExcepti ComponentInfo partialInfo; partialInfo = ldr.getComponentInfo(); feedback.verboseOutput("INSTALL_PrepareToInstall", - /* feedback.translateFilename( */ p.getDisplayName(), + p.getDisplayName(), partialInfo.getId(), partialInfo.getVersionString(), partialInfo.getName()); ldr.loadPaths(); - Installer inst = new Installer(feedback, partialInfo, input.getLocalRegistry()); - String path = ldr.getLicensePath(); - if (path != null) { - inst.setLicenseRelativePath(SystemUtils.fromCommonString(ldr.getLicensePath())); + Archive a = null; + if (p.isComplete()) { + a = ldr.getArchive(); + a.verifyIntegrity(input); } + Installer inst = new Installer(feedback, partialInfo, input.getLocalRegistry(), a); inst.setPermissions(ldr.loadPermissions()); inst.setSymlinks(ldr.loadSymlinks()); - if (p.isComplete()) { - inst.setJarFile(p.getFile()); - } configureInstaller(inst); return inst; } + + /** + * Forces the user to accept the licenses. + * + * @throws IOException + */ + void acceptLicenses() throws IOException { + new LicensePresenter(feedback, input.getLocalRegistry(), licensesToAccept).run(); + } } diff --git a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/commands/Installer.java b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/commands/Installer.java index 9f0eb2665d67..8d4c37ebafdc 100644 --- a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/commands/Installer.java +++ b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/commands/Installer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,14 +24,13 @@ */ package org.graalvm.component.installer.commands; -import java.io.Closeable; import java.io.IOException; import java.io.InputStream; -import java.nio.ByteBuffer; import java.nio.channels.ByteChannel; import java.nio.file.Files; import java.nio.file.LinkOption; import java.nio.file.Path; +import java.nio.file.Paths; import java.nio.file.StandardCopyOption; import java.nio.file.attribute.PosixFileAttributeView; import java.nio.file.attribute.PosixFilePermission; @@ -43,28 +42,22 @@ import java.util.List; import java.util.Map; import java.util.Set; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; import java.util.logging.Level; import java.util.logging.Logger; -import java.util.zip.CRC32; -import java.util.zip.ZipEntry; +import org.graalvm.component.installer.Archive; import org.graalvm.component.installer.BundleConstants; import org.graalvm.component.installer.CommonConstants; import org.graalvm.component.installer.model.ComponentRegistry; import org.graalvm.component.installer.Feedback; import org.graalvm.component.installer.SystemUtils; import org.graalvm.component.installer.model.ComponentInfo; -import org.graalvm.component.installer.model.Verifier; /** * The working internals of the 'install' command. */ -public class Installer implements Closeable { +public class Installer extends AbstractInstaller { private static final Logger LOG = Logger.getLogger(Installer.class.getName()); - private static final int CHECKSUM_BUFFER_SIZE = 1024 * 1024; - /** * Default permisions for files that should have the permissions changed. */ @@ -73,101 +66,22 @@ public class Installer implements Closeable { PosixFilePermission.GROUP_READ, PosixFilePermission.GROUP_EXECUTE, PosixFilePermission.OTHERS_READ, PosixFilePermission.OTHERS_EXECUTE); - private final Feedback feedback; - private final ComponentInfo componentInfo; - private JarFile jarFile; - private final ComponentRegistry registry; - private final List filesToDelete = new ArrayList<>(); private final List dirsToDelete = new ArrayList<>(); - private Map permissions = Collections.emptyMap(); - private Map symlinks = Collections.emptyMap(); - private Path installPath; - private Path licenseRelativePath; - private Set componentDirectories = Collections.emptySet(); - - private boolean replaceDiferentFiles; - private boolean replaceComponents; - private boolean dryRun; - private boolean ignoreRequirements; private boolean rebuildPolyglot; - private boolean failOnExisting; - /** * Paths tracked by the component system. */ - private Set trackedPaths = new HashSet<>(); - private Set visitedPaths = new HashSet<>(); - - public boolean isFailOnExisting() { - return failOnExisting; - } - - public void setFailOnExisting(boolean failOnExisting) { - this.failOnExisting = failOnExisting; - } - - public boolean isReplaceDiferentFiles() { - return replaceDiferentFiles; - } - - public void setReplaceDiferentFiles(boolean replaceDiferentFiles) { - this.replaceDiferentFiles = replaceDiferentFiles; - } - - public boolean isReplaceComponents() { - return replaceComponents; - } - - public void setReplaceComponents(boolean replaceComponents) { - this.replaceComponents = replaceComponents; - } - - public boolean isIgnoreRequirements() { - return ignoreRequirements; - } - - public void setIgnoreRequirements(boolean ignoreRequirements) { - this.ignoreRequirements = ignoreRequirements; - } - - public Installer(Feedback feedback, ComponentInfo componentInfo, ComponentRegistry registry) { - this.feedback = feedback; - this.componentInfo = componentInfo; - this.registry = registry; - } - - public ComponentInfo getComponentInfo() { - return componentInfo; - } - - public Set getTrackedPaths() { - return trackedPaths; - } - - public Map getPermissions() { - return permissions; - } - - public void setPermissions(Map permissions) { - this.permissions = permissions; - } - - public Map getSymlinks() { - return symlinks; - } - - public void setJarFile(JarFile jarFile) { - this.jarFile = jarFile; - } + private final Set visitedPaths = new HashSet<>(); - public void setSymlinks(Map symlinks) { - this.symlinks = symlinks; + public Installer(Feedback feedback, ComponentInfo componentInfo, ComponentRegistry registry, Archive a) { + super(feedback, componentInfo, registry, a); } + @Override public void revertInstall() { - if (dryRun) { + if (isDryRun()) { return; } LOG.fine("Reverting installation"); @@ -193,7 +107,7 @@ public void revertInstall() { } } - Path translateTargetPath(ZipEntry entry) { + Path translateTargetPath(Archive.FileEntry entry) { return translateTargetPath(entry.getName()); } @@ -201,20 +115,15 @@ Path translateTargetPath(String n) { Path rel; if (BundleConstants.PATH_LICENSE.equals(n)) { rel = getLicenseRelativePath(); + if (rel == null) { + rel = Paths.get(n); + } } else { rel = SystemUtils.fromCommonString(n); } return getInstallPath().resolve(rel); } - public Verifier validateRequirements() { - return new Verifier(feedback, registry, componentInfo) - .ignoreRequirements(ignoreRequirements) - .replaceComponents(replaceComponents) - .ignoreExisting(!failOnExisting) - .validateRequirements(); - } - /** * Validates requirements, decides whether to install. Returns false if the component should be * skipped. @@ -222,6 +131,7 @@ public Verifier validateRequirements() { * @return true, if the component should be installed * @throws IOException */ + @Override public boolean validateAll() throws IOException { validateRequirements(); ComponentInfo existing = registry.findComponent(componentInfo.getId()); @@ -233,11 +143,12 @@ public boolean validateAll() throws IOException { return true; } + @Override public void validateFiles() throws IOException { - if (jarFile == null) { + if (archive == null) { throw new UnsupportedOperationException(); } - for (JarEntry entry : Collections.list(jarFile.entries())) { + for (Archive.FileEntry entry : archive) { if (entry.getName().startsWith("META-INF")) { // NOI18N continue; } @@ -246,19 +157,21 @@ public void validateFiles() throws IOException { } } + @Override public void validateSymlinks() throws IOException { - for (String sl : symlinks.keySet()) { + Map processSymlinks = getSymlinks(); + for (String sl : processSymlinks.keySet()) { Path target = translateTargetPath(sl); if (Files.exists(target, LinkOption.NOFOLLOW_LINKS)) { checkLinkReplacement(target, - translateTargetPath(symlinks.get(sl))); + translateTargetPath(processSymlinks.get(sl))); } } } - boolean validateOneEntry(Path target, ZipEntry entry) throws IOException { + boolean validateOneEntry(Path target, Archive.FileEntry entry) throws IOException { if (entry.isDirectory()) { - Path dirPath = installPath.resolve(SystemUtils.fromCommonString(entry.getName())); + Path dirPath = getInstallPath().resolve(SystemUtils.fromCommonString(entry.getName())); if (Files.exists(dirPath)) { if (!Files.isDirectory(dirPath)) { throw new IOException( @@ -275,13 +188,13 @@ boolean validateOneEntry(Path target, ZipEntry entry) throws IOException { } public void install() throws IOException { - assert jarFile != null : "Must first download / set jar file"; + assert archive != null : "Must first download / set jar file"; installContent(); installFinish(); } void installContent() throws IOException { - if (jarFile == null) { + if (archive == null) { throw new UnsupportedOperationException(); } // unpack files @@ -289,7 +202,7 @@ void installContent() throws IOException { processPermissions(); createSymlinks(); - List ll = new ArrayList<>(trackedPaths); + List ll = new ArrayList<>(getTrackedPaths()); Collections.sort(ll); // replace paths with the really tracked ones componentInfo.setPaths(ll); @@ -301,13 +214,13 @@ void installContent() throws IOException { void installFinish() throws IOException { // installation succeeded, add to component registry - if (!dryRun) { + if (!isDryRun()) { registry.addComponent(getComponentInfo()); } } void unpackFiles() throws IOException { - for (JarEntry entry : Collections.list(jarFile.entries())) { + for (Archive.FileEntry entry : archive) { installOneEntry(entry); } } @@ -316,8 +229,8 @@ void ensurePathExists(Path targetPath) throws IOException { if (!visitedPaths.add(targetPath)) { return; } - Path relative = installPath.relativize(targetPath); - Path parent = installPath; + Path relative = getInstallPath().relativize(targetPath); + Path parent = getInstallPath(); Path relativeSubpath; int count = 0; for (Path n : relative) { @@ -328,13 +241,13 @@ void ensurePathExists(Path targetPath) throws IOException { // Need to track either directories, which do not exist (and will be created) // AND directories created by other components. - if (!Files.exists(dir) || componentDirectories.contains(pathString)) { + if (!Files.exists(dir) || getComponentDirectories().contains(pathString)) { feedback.verboseOutput("INSTALL_CreatingDirectory", dir); dirsToDelete.add(dir); // add the created directory to the installed file list - trackedPaths.add(pathString); + addTrackedPath(pathString); if (!Files.exists(dir)) { - if (!dryRun) { + if (!isDryRun()) { Files.createDirectory(dir); } } @@ -343,7 +256,7 @@ void ensurePathExists(Path targetPath) throws IOException { } } - Path installOneEntry(JarEntry entry) throws IOException { + Path installOneEntry(Archive.FileEntry entry) throws IOException { if (entry.getName().startsWith("META-INF")) { // NOI18N return null; } @@ -362,9 +275,9 @@ Path installOneEntry(JarEntry entry) throws IOException { } } - Path installOneFile(Path target, JarEntry entry) throws IOException { + Path installOneFile(Path target, Archive.FileEntry entry) throws IOException { // copy contents of the file - try (InputStream jarStream = jarFile.getInputStream(entry)) { + try (InputStream jarStream = archive.getInputStream(entry)) { boolean existingFile = Files.exists(target, LinkOption.NOFOLLOW_LINKS); String eName = entry.getName(); if (existingFile) { @@ -379,21 +292,23 @@ Path installOneFile(Path target, JarEntry entry) throws IOException { feedback.verboseOutput("INSTALL_InstallingFile", eName); } ensurePathExists(target.getParent()); - trackedPaths.add(installPath.relativize(target).toString()); - if (!dryRun) { + addTrackedPath(getInstallPath().relativize(target).toString()); + if (!isDryRun()) { Files.copy(jarStream, target, StandardCopyOption.REPLACE_EXISTING); } } return target; } + @Override public void processPermissions() throws IOException { - List paths = new ArrayList<>(permissions.keySet()); + Map setPermissions = getPermissions(); + List paths = new ArrayList<>(setPermissions.keySet()); Collections.sort(paths); for (String s : paths) { - Path p = installPath.resolve(SystemUtils.fromCommonString(s)); + Path p = getInstallPath().resolve(SystemUtils.fromCommonString(s)); if (Files.exists(p)) { - String permissionString = permissions.get(s); + String permissionString = setPermissions.get(s); Set perms; if (permissionString != null && !"".equals(permissionString)) { @@ -408,20 +323,22 @@ public void processPermissions() throws IOException { } } + @Override public void createSymlinks() throws IOException { if (SystemUtils.isWindows()) { return; } + Map makeSymlinks = getSymlinks(); List createdRelativeLinks = new ArrayList<>(); try { - List paths = new ArrayList<>(symlinks.keySet()); + List paths = new ArrayList<>(makeSymlinks.keySet()); Collections.sort(paths); for (String s : paths) { - Path source = installPath.resolve(SystemUtils.fromCommonString(s)); - Path target = SystemUtils.fromCommonString(symlinks.get(s)); + Path source = getInstallPath().resolve(SystemUtils.fromCommonString(s)); + Path target = SystemUtils.fromCommonString(makeSymlinks.get(s)); ensurePathExists(source.getParent()); createdRelativeLinks.add(s); - trackedPaths.add(s); + addTrackedPath(s); // TODO: check if the symlink does not exist and if so, whether // reads the same. Behaviour similar to file CRC check. if (Files.exists(source, LinkOption.NOFOLLOW_LINKS)) { @@ -435,8 +352,8 @@ public void createSymlinks() throws IOException { } } filesToDelete.add(source); - feedback.verboseOutput("INSTALL_CreatingSymlink", s, symlinks.get(s)); - if (!dryRun) { + feedback.verboseOutput("INSTALL_CreatingSymlink", s, makeSymlinks.get(s)); + if (!isDryRun()) { Files.createSymbolicLink(source, target); } } @@ -447,9 +364,10 @@ public void createSymlinks() throws IOException { } boolean checkLinkReplacement(Path existingPath, Path target) throws IOException { + boolean replace = isReplaceDiferentFiles(); if (Files.exists(existingPath, LinkOption.NOFOLLOW_LINKS)) { if (!Files.isSymbolicLink(existingPath)) { - if (Files.isRegularFile(existingPath) && replaceDiferentFiles) { + if (Files.isRegularFile(existingPath) && replace) { return false; } throw new IOException( @@ -458,7 +376,7 @@ boolean checkLinkReplacement(Path existingPath, Path target) throws IOException } Path p = Files.readSymbolicLink(existingPath); if (!target.equals(p)) { - if (replaceDiferentFiles) { + if (replace) { return false; } throw feedback.failure("INSTALL_ReplacedFileDiffers", null, existingPath); @@ -466,75 +384,30 @@ boolean checkLinkReplacement(Path existingPath, Path target) throws IOException return true; } - boolean checkFileReplacement(Path existingPath, ZipEntry entry) throws IOException { + boolean checkFileReplacement(Path existingPath, Archive.FileEntry entry) throws IOException { + boolean replace = isReplaceDiferentFiles(); if (Files.isDirectory(existingPath)) { throw new IOException( feedback.l10n("INSTALL_OverwriteWithFile", existingPath)); } if (!Files.isRegularFile(existingPath) || (Files.size(existingPath) != entry.getSize())) { - if (replaceDiferentFiles) { + if (replace) { return false; } throw feedback.failure("INSTALL_ReplacedFileDiffers", null, existingPath); } - CRC32 crc = new CRC32(); - ByteBuffer bb = null; try (ByteChannel is = Files.newByteChannel(existingPath)) { - bb = ByteBuffer.allocate(CHECKSUM_BUFFER_SIZE); - while (is.read(bb) >= 0) { - bb.flip(); - crc.update(bb); - bb.clear(); - } - } - if (crc.getValue() != entry.getCrc()) { - if (replaceDiferentFiles) { - return false; + if (!archive.checkContentsMatches(is, entry)) { + if (replace) { + return false; + } + throw feedback.failure("INSTALL_ReplacedFileDiffers", null, existingPath); } - throw feedback.failure("INSTALL_ReplacedFileDiffers", null, existingPath); } return true; } - public Path getInstallPath() { - return installPath; - } - - public void setInstallPath(Path installPath) { - this.installPath = installPath; - } - - public Path getLicenseRelativePath() { - return licenseRelativePath; - } - - public void setLicenseRelativePath(Path licenseRelativePath) { - this.licenseRelativePath = licenseRelativePath; - } - @Override - public void close() throws IOException { - if (jarFile != null) { - jarFile.close(); - } - } - - public boolean isDryRun() { - return dryRun; - } - - public void setDryRun(boolean dryRun) { - this.dryRun = dryRun; - } - - public Set getComponentDirectories() { - return componentDirectories; - } - - public void setComponentDirectories(Set componentDirectories) { - this.componentDirectories = componentDirectories; - } - public boolean isRebuildPolyglot() { return rebuildPolyglot; } diff --git a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/commands/LicensePresenter.java b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/commands/LicensePresenter.java new file mode 100644 index 000000000000..1f9117a10189 --- /dev/null +++ b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/commands/LicensePresenter.java @@ -0,0 +1,344 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.graalvm.component.installer.commands; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import org.graalvm.component.installer.Archive; +import org.graalvm.component.installer.Feedback; +import org.graalvm.component.installer.UserAbortException; +import org.graalvm.component.installer.model.ComponentInfo; +import org.graalvm.component.installer.model.ComponentRegistry; +import org.graalvm.component.installer.persist.MetadataLoader; + +/** + * License console "dialog" driver. + * + * @author sdedic + */ +public class LicensePresenter { + private final Feedback feedback; + private final ComponentRegistry localRegistry; + + /** + * Licenses to be processed. + */ + private final Map> licensesToAccept; + + private State state = State.NONE; + + /** + * True, if multiple licenses were present initially. + */ + private boolean multiLicenses; + + /** + * The ID (hash) of the license to be displayed. + */ + private String displayLicenseId; + + enum State { + /** + * List of licenses with choice to display some or accept all. + */ + LIST, + + /** + * Accepts the input. + */ + LISTINPUT, + + /** + * Single license prompt. + */ + SINGLE, + + /** + * Display a license, confirm it. + */ + LICENSE, + + /** + * Abort. + */ + NONE + } + + public LicensePresenter(Feedback feedback, ComponentRegistry localRegistry, Map> licenseIDs) { + this.feedback = feedback.withBundle(LicensePresenter.class); + this.localRegistry = localRegistry; + this.licensesToAccept = licenseIDs; + } + + public void filterAcceptedLicenses() { + for (String licId : new ArrayList<>(licensesToAccept.keySet())) { + Collection loaders = licensesToAccept.get(licId); + for (MetadataLoader ldr : new ArrayList<>(loaders)) { + ComponentInfo ci = ldr.getComponentInfo(); + Date accepted = localRegistry.isLicenseAccepted(ci, licId); + if (accepted != null) { + feedback.verboseOutput("INSTALL_LicenseAcceptedAt", ldr.getLicenseType(), accepted, ci.getId(), ci.getName()); + loaders.remove(ldr); + } + } + if (loaders.isEmpty()) { + licensesToAccept.remove(licId); + } + } + multiLicenses = licensesToAccept.size() > 1; + } + + public State getState() { + return state; + } + + public boolean isMultiLicenses() { + return multiLicenses; + } + + public String getDisplayLicenseId() { + return displayLicenseId; + } + + String formatComponentList(String licId) { + List loaders = licensesToAccept.get(licId); + String list = null; + for (MetadataLoader l : loaders) { + ComponentInfo ci = l.getComponentInfo(); + if (list == null) { + list = feedback.l10n("INSTALL_LicenseComponentStart", ci.getName()); + } else { + list = feedback.l10n("INSTALL_LicenseComponentCont", ci.getName()); + } + } + return list; + } + + void displayLicenseList() { + feedback.output("INSTALL_LicensesToAccept"); + int idx = 1; + for (String licId : licensesToAccept.keySet()) { + List loaders = licensesToAccept.get(licId); + String list = formatComponentList(licId); + feedback.output("INSTALL_AcceptLicenseComponents", loaders.get(0).getLicenseType(), list, idx); + idx++; + } + feedback.outputPart("INSTALL_AcceptAllLicensesPrompt"); + state = State.LISTINPUT; + } + + private static final Pattern ALL_NUMBERS = Pattern.compile("[0-9]+"); + + boolean isFinished() { + return licensesToAccept.isEmpty(); + } + + void acceptAllLicenses() { + for (String s : licensesToAccept.keySet()) { + for (MetadataLoader ldr : licensesToAccept.get(s)) { + ComponentInfo ci = ldr.getComponentInfo(); + try { + localRegistry.acceptLicense(ci, s, loadLicenseText(s)); + } catch (IOException ex) { + throw feedback.failure("INSTALL_ErrorHandlingLicenses", ex, ex.getLocalizedMessage()); + } + } + } + licensesToAccept.clear(); + } + + boolean isYes(String userInput) { + if (userInput == Feedback.AUTO_YES) { + return true; + } + Pattern p = Pattern.compile(feedback.l10n("INSTALL_AcceptPromptResponseYes"), Pattern.CASE_INSENSITIVE); + return p.matcher(userInput).matches(); + } + + boolean isRead(String userInput) { + Pattern p = Pattern.compile(feedback.l10n("INSTALL_AcceptPromptResponseRead"), Pattern.CASE_INSENSITIVE); + return p.matcher(userInput).matches(); + } + + boolean processUserInputForList() { + String userInput = feedback.acceptLine(true); + Pattern p = Pattern.compile(feedback.l10n("INSTALL_AcceptPromptResponseAbort"), Pattern.CASE_INSENSITIVE); + if (p.matcher(userInput).matches()) { + throw new UserAbortException(); + } + if (isYes(userInput)) { + acceptAllLicenses(); + state = State.NONE; + return true; + } + List ids = new ArrayList<>(licensesToAccept.keySet()); + if (!ALL_NUMBERS.matcher(userInput).matches()) { + feedback.output("INSTALL_LicenseNumberInvalidEntry", ids.size()); + return false; + } + int n = Integer.parseInt(userInput); + if (n < 0 || n > ids.size()) { + feedback.output("INSTALL_LicenseNumberOutOfRange", ids.size()); + return false; + } + displayLicenseId = ids.get(n - 1); + state = State.LICENSE; + return true; + } + + void acceptLicense(String licenseId) { + String licText; + try { + licText = loadLicenseText(licenseId); + } catch (IOException ex) { + throw feedback.failure("INSTALL_ErrorHandlingLicenses", ex, ex.getLocalizedMessage()); + } + for (MetadataLoader ldr : licensesToAccept.get(licenseId)) { + localRegistry.acceptLicense(ldr.getComponentInfo(), licenseId, licText); + } + licensesToAccept.remove(licenseId); + } + + void displaySingleLicense() { + String licId = licensesToAccept.keySet().iterator().next(); + MetadataLoader ldr = licensesToAccept.get(licId).get(0); + String type = ldr.getLicenseType(); + String compList = formatComponentList(licId); + feedback.output("INSTALL_AcceptLicense", compList, type); + feedback.outputPart("INSTALL_AcceptSingleLicense"); + String input = feedback.acceptLine(true); + + if (isYes(input)) { + acceptLicense(licId); + return; + } else if (isRead(input)) { + displayLicenseId = licId; + state = State.LICENSE; + } else { + throw new UserAbortException(); + } + } + + void displayLicenseText() throws IOException { + String text = loadLicenseText(displayLicenseId); + feedback.verbatimOut(text, false); + feedback.output("INSTALL_AcceptLicensePrompt"); + String input = feedback.acceptLine(true); + + if (isYes(input)) { + acceptLicense(displayLicenseId); + state = multiLicenses ? State.LIST : State.SINGLE; + } else if (!multiLicenses) { + throw new UserAbortException(); + } else { + state = State.LIST; + } + } + + /** + * Loads license text from the archive. + * + * @param licenseId + * @return text of the license + * @throws IOException + */ + String loadLicenseText(String licenseId) throws IOException { + MetadataLoader ldr = licensesToAccept.get(licenseId).get(0); + // may require a download of the archive + try (Archive a = ldr.getArchive()) { + String licensePath = ldr.getLicensePath(); + Archive.FileEntry licenseEntry = null; + + for (Archive.FileEntry e : a) { + String n = e.getName(); + if (n.startsWith("/")) { + n = n.substring(1); + } else if (n.startsWith("./")) { + n = n.substring(2); + } + if (n.equals(licensePath)) { + licenseEntry = e; + break; + } + } + + if (licenseEntry == null) { + throw new IOException(feedback.l10n("INSTALL_LicenseNotFound", licensePath)); + } + + try (InputStream es = a.getInputStream(licenseEntry); + InputStreamReader esr = new InputStreamReader(es, "UTF-8"); + BufferedReader buf = new BufferedReader(esr)) { + return buf.lines().collect(Collectors.joining("\n")); // NOI18N + } + } + } + + void init() { + filterAcceptedLicenses(); + state = multiLicenses ? State.LIST : State.SINGLE; + } + + void run() { + init(); + try { + while (!isFinished()) { + singleStep(); + } + } catch (IOException ex) { + throw feedback.failure("INSTALL_ErrorHandlingLicenses", ex, ex.getLocalizedMessage()); + } + } + + void singleStep() throws IOException { + switch (state) { + case LISTINPUT: + processUserInputForList(); + break; + case SINGLE: + displaySingleLicense(); + break; + case LICENSE: + displayLicenseText(); + break; + case NONE: + break; + case LIST: + displayLicenseList(); + break; + default: + throw new AssertionError(state.name()); + } + } +} diff --git a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/commands/Uninstaller.java b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/commands/Uninstaller.java index a5f5087d92da..d67937f90b52 100644 --- a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/commands/Uninstaller.java +++ b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/commands/Uninstaller.java @@ -119,6 +119,9 @@ private void deleteOneFile(Path p, Path rootPath) throws IOException { Path d = p.getParent(); // set the parent directory's permissions, but do not // alter permissions outside the to-be-deleted tree: + if (d == null) { + throw new IOException("Cannot determine parent of " + p); + } if (d.startsWith(rootPath) && !d.equals(rootPath)) { restoreDirPermissions = Files.getPosixFilePermissions(d); Files.setPosixFilePermissions(d, ALL_WRITE_PERMS); diff --git a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/jar/Bundle.properties b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/jar/Bundle.properties new file mode 100644 index 000000000000..691d60dec711 --- /dev/null +++ b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/jar/Bundle.properties @@ -0,0 +1,3 @@ +# {0} - component ID +# {1} - component version +LICENSE_Path_translation=LICENSE-{0}-{1} diff --git a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/jar/JarArchive.java b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/jar/JarArchive.java new file mode 100644 index 000000000000..c4d3d84d10a2 --- /dev/null +++ b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/jar/JarArchive.java @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.graalvm.component.installer.jar; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.ByteBuffer; +import java.nio.channels.ReadableByteChannel; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.zip.CRC32; +import org.graalvm.component.installer.Archive; +import org.graalvm.component.installer.CommandInput; + +/** + * + * @author sdedic + */ +public class JarArchive implements Archive { + private static final int CHECKSUM_BUFFER_SIZE = 1024 * 32; + + private final JarFile jarFile; + + public JarArchive(JarFile jarFile) { + this.jarFile = jarFile; + } + + public FileEntry getEntry(String path) { + return getJarEntry(path); + } + + public FileEntry getJarEntry(String path) { + JarEntry e = jarFile.getJarEntry(path); + return e == null ? null : new JarEntryImpl(e); + } + + @Override + public Iterator iterator() { + return new EntryIterator(jarFile.entries()); + } + + @Override + public void close() throws IOException { + jarFile.close(); + } + + @Override + public InputStream getInputStream(FileEntry e) throws IOException { + return jarFile.getInputStream(((JarEntryImpl) e).e); + } + + @Override + public boolean checkContentsMatches(ReadableByteChannel bc, FileEntry entry) throws IOException { + ByteBuffer bb = ByteBuffer.allocate(CHECKSUM_BUFFER_SIZE); + CRC32 crc = new CRC32(); + while (bc.read(bb) >= 0) { + bb.flip(); + crc.update(bb); + bb.clear(); + } + return crc.getValue() == ((JarEntryImpl) entry).e.getCrc(); + } + + /** + * Always returns true. Signer JARs are automatically verified on open + * + * @return true + */ + @Override + public boolean verifyIntegrity(CommandInput input) { + return true; + } + + private static class EntryIterator implements Iterator { + private final Enumeration enEntries; + + EntryIterator(Enumeration enEntries) { + this.enEntries = enEntries; + } + + @Override + public boolean hasNext() { + return enEntries.hasMoreElements(); + } + + @Override + public JarEntryImpl next() { + return new JarEntryImpl(enEntries.nextElement()); + } + } + + private static class JarEntryImpl implements FileEntry { + private final JarEntry e; + + JarEntryImpl(JarEntry e) { + this.e = e; + } + + @Override + public String getName() { + return e.getName(); + } + + @Override + public boolean isDirectory() { + return e.isDirectory(); + } + + @Override + public long getSize() { + return e.getSize(); + } + + @Override + public boolean isSymbolicLink() { + return false; + } + + @Override + public String getLinkTarget() throws IOException { + throw new IllegalStateException("Not a symbolic link"); + } + } +} diff --git a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/jar/JarMetaLoader.java b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/jar/JarMetaLoader.java new file mode 100644 index 000000000000..100c51e0b6bb --- /dev/null +++ b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/jar/JarMetaLoader.java @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.graalvm.component.installer.jar; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.function.Function; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.jar.Manifest; +import org.graalvm.component.installer.Archive; +import static org.graalvm.component.installer.BundleConstants.META_INF_PATH; +import static org.graalvm.component.installer.BundleConstants.META_INF_PERMISSIONS_PATH; +import static org.graalvm.component.installer.BundleConstants.META_INF_SYMLINKS_PATH; +import static org.graalvm.component.installer.BundleConstants.PATH_LICENSE; +import org.graalvm.component.installer.Feedback; +import org.graalvm.component.installer.model.ComponentInfo; +import org.graalvm.component.installer.persist.ComponentPackageLoader; + +/** + * + * @author sdedic + */ +public class JarMetaLoader extends ComponentPackageLoader { + private final JarFile jarFile; + private final Feedback fb; + + public JarMetaLoader(JarFile jarFile, Feedback feedback) throws IOException { + super(new ManifestValues(jarFile), feedback); + this.jarFile = jarFile; + this.fb = feedback.withBundle(JarMetaLoader.class); + } + + private static class ManifestValues implements Function { + private final Manifest mf; + + ManifestValues(JarFile jf) throws IOException { + this.mf = jf.getManifest(); + } + + @Override + public String apply(String t) { + return mf.getMainAttributes().getValue(t); + } + + } + + @Override + public void close() throws IOException { + super.close(); + jarFile.close(); + } + + @Override + public Archive getArchive() { + return new JarArchive(jarFile); + } + + @Override + public Map loadPermissions() throws IOException { + JarEntry permEntry = jarFile.getJarEntry(META_INF_PERMISSIONS_PATH); + if (permEntry == null) { + return Collections.emptyMap(); + } + try (BufferedReader r = new BufferedReader(new InputStreamReader( + jarFile.getInputStream(permEntry), "UTF-8"))) { + Map permissions = parsePermissions(r); + return permissions; + } + } + + @Override + public void loadPaths() { + ComponentInfo cinfo = getComponentInfo(); + Set emptyDirectories = new HashSet<>(); + List files = new ArrayList<>(); + for (JarEntry en : Collections.list(jarFile.entries())) { + String eName = en.getName(); + if (eName.startsWith(META_INF_PATH)) { + continue; + } + int li = eName.lastIndexOf("/", en.isDirectory() ? eName.length() - 2 : eName.length() - 1); + if (li > 0) { + emptyDirectories.remove(eName.substring(0, li + 1)); + } + if (PATH_LICENSE.equals(eName)) { + String lp = fb.l10n("LICENSE_Path_translation", + cinfo.getId(), + cinfo.getVersionString()); + files.add(lp); + super.setLicensePath(lp); + continue; + } + if (en.isDirectory()) { + // directory names always come first + emptyDirectories.add(eName); + } else { + files.add(eName); + } + } + addFiles(new ArrayList<>(emptyDirectories)); + // sort empty directories first + Collections.sort(files); + cinfo.addPaths(files); + addFiles(files); + } + + @Override + public Map loadSymlinks() throws IOException { + JarEntry symEntry = jarFile.getJarEntry(META_INF_SYMLINKS_PATH); + if (symEntry == null) { + return Collections.emptyMap(); + } + Properties links = new Properties(); + try (InputStream istm = jarFile.getInputStream(symEntry)) { + links.load(istm); + } + return parseSymlinks(links); + } + + @Override + public String getLicenseID() { + String licPath = getLicensePath(); + if (licPath == null) { + return null; + } + JarEntry je = jarFile.getJarEntry(licPath); + if (je == null) { + return null; + } + long crc = je.getCrc(); + return Long.toHexString(crc); + } +} diff --git a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/model/ComponentInfo.java b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/model/ComponentInfo.java index 6f905039cbc3..60f29845c492 100644 --- a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/model/ComponentInfo.java +++ b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/model/ComponentInfo.java @@ -63,6 +63,11 @@ public final class ComponentInfo { */ private String licensePath; + /** + * License type. + */ + private String licenseType; + /** * Assertions on graalVM installation. */ @@ -182,4 +187,12 @@ public String getPostinstMessage() { public void setPostinstMessage(String postinstMessage) { this.postinstMessage = postinstMessage; } + + public String getLicenseType() { + return licenseType; + } + + public void setLicenseType(String licenseType) { + this.licenseType = licenseType; + } } diff --git a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/model/ComponentRegistry.java b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/model/ComponentRegistry.java index 234d4ee3c401..31b4092539c8 100644 --- a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/model/ComponentRegistry.java +++ b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/model/ComponentRegistry.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,6 +29,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; @@ -342,4 +343,16 @@ public String shortenComponentId(ComponentInfo info) { } return id; } + + public Date isLicenseAccepted(ComponentInfo info, String id) { + return storage.licenseAccepted(info, id); + } + + public void acceptLicense(ComponentInfo info, String id, String text) { + try { + storage.recordLicenseAccepted(info, id, text); + } catch (IOException ex) { + env.error("ERROR_RecordLicenseAccepted", ex, ex.getLocalizedMessage()); + } + } } diff --git a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/model/ComponentStorage.java b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/model/ComponentStorage.java index 62c4872fe6b4..92e2de47c349 100644 --- a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/model/ComponentStorage.java +++ b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/model/ComponentStorage.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,6 +26,7 @@ import java.io.IOException; import java.util.Collection; +import java.util.Date; import java.util.Map; import java.util.Set; @@ -76,4 +77,22 @@ public interface ComponentStorage { void updateReplacedFiles(Map> replacedFiles) throws IOException; + /** + * Checks that the license was already accepted. + * + * @param info component for which the license should be checked + * @param licenseID the ID to check + * @return time when the license was accepted + */ + Date licenseAccepted(ComponentInfo info, String licenseID); + + /** + * Records that the license has been accepted. If id is {@code} null, then all license records + * are erased. + * + * @param info the component for which the license is accepted + * @param licenseID the ID to accept or {@code null} + * @param licenseText text of the license, will be recoded. + */ + void recordLicenseAccepted(ComponentInfo info, String licenseID, String licenseText) throws IOException; } diff --git a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/model/Verifier.java b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/model/Verifier.java index 1515d9980501..c8fcd66a4c84 100644 --- a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/model/Verifier.java +++ b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/model/Verifier.java @@ -172,7 +172,8 @@ public Verifier validateRequirements() { String reqVal = requiredCaps.get(s); String graalVal = graalCaps.get(s); if ((reqVal != graalVal) && - (reqVal == null || graalVal == null || (reqVal.compareToIgnoreCase(graalVal) != 0))) { + (reqVal == null || graalVal == null || + (reqVal.replace('-', '_').compareToIgnoreCase(graalVal.replace('-', '_')) != 0))) { String val = graalVal != null ? graalVal : feedback.l10n("VERIFY_CapabilityMissing"); addOrThrow(new DependencyException.Mismatch( GRAALVM_CAPABILITY, diff --git a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/persist/RemoteStorage.java b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/persist/AbstractCatalogStorage.java similarity index 58% rename from vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/persist/RemoteStorage.java rename to vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/persist/AbstractCatalogStorage.java index 95cbc17f6637..0786f8732e0a 100644 --- a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/persist/RemoteStorage.java +++ b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/persist/AbstractCatalogStorage.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,70 +22,35 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ + package org.graalvm.component.installer.persist; import java.io.IOException; import java.net.URL; -import java.text.MessageFormat; import java.util.Collection; -import java.util.HashSet; +import java.util.Date; import java.util.Map; -import java.util.Properties; -import java.util.Set; -import java.util.function.Function; import org.graalvm.component.installer.Feedback; import org.graalvm.component.installer.MetadataException; import org.graalvm.component.installer.model.ComponentInfo; import org.graalvm.component.installer.model.ComponentRegistry; import org.graalvm.component.installer.model.ComponentStorage; -public class RemoteStorage implements ComponentStorage { - private final Properties catalogProperties; - private final String flavourPrefix; - private final ComponentRegistry localRegistry; - private final Feedback feedback; - private final URL baseURL; - - private static final String PROPERTY_HASH = "hash"; // NOI18N - private static final String FORMAT_FLAVOUR = "Component.{0}."; // NOI18N - - public RemoteStorage(Feedback fb, ComponentRegistry localReg, Properties catalogProperties, String graalVersion, URL baseURL) { - this.catalogProperties = catalogProperties; - this.localRegistry = localReg; - this.feedback = fb; +/** + * Base for different storage of remote component list. The default implementation uses properties + * resource reachable through the https. + * + * @author sdedic + */ +public abstract class AbstractCatalogStorage implements ComponentStorage { + protected final ComponentRegistry localRegistry; + protected final Feedback feedback; + protected final URL baseURL; + + public AbstractCatalogStorage(ComponentRegistry localRegistry, Feedback feedback, URL baseURL) { + this.localRegistry = localRegistry; + this.feedback = feedback; this.baseURL = baseURL; - flavourPrefix = MessageFormat.format(FORMAT_FLAVOUR, graalVersion); - } - - @Override - public Set listComponentIDs() throws IOException { - Set ret = new HashSet<>(); - for (String s : catalogProperties.stringPropertyNames()) { - if (!s.startsWith(flavourPrefix)) { - continue; - } - String rest = s.substring(flavourPrefix.length()); - if (rest.indexOf('-') == -1) { - // got a component ID - ret.add(rest.toLowerCase()); - } - } - return ret; - } - - @Override - public Map loadGraalVersionInfo() { - return localRegistry.getGraalCapabilities(); - } - - @Override - public ComponentInfo loadComponentFiles(ComponentInfo ci) throws IOException { - // files are not supported, yet - return ci; - } - - private byte[] toHashBytes(String comp, String hashS) { - return toHashBytes(comp, hashS, feedback); } public static byte[] toHashBytes(String comp, String hashS, Feedback fb) { @@ -97,7 +62,6 @@ public static byte[] toHashBytes(String comp, String hashS, Feedback fb) { boolean divided = !((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f')); // NOI18N boolean lenOK; int s; - if (divided) { lenOK = (val.length() + 1) % 3 == 0; s = (val.length() + 1) / 3; @@ -129,43 +93,18 @@ public static byte[] toHashBytes(String comp, String hashS, Feedback fb) { } @Override - public ComponentInfo loadComponentMetadata(String id) throws IOException { - URL downloadURL; - String s = catalogProperties.getProperty(flavourPrefix + id.toLowerCase()); - if (s == null) { - return null; - } - // try { - downloadURL = new URL(baseURL, s); - // } catch (MalformedURLException ex) { - // throw feedback.failure("REMOTE_InvalidDownloadURL", ex, s, ex.getLocalizedMessage()); - // } - String prefix = flavourPrefix + id.toLowerCase() + "-"; // NOI18N - String hashS = catalogProperties.getProperty(prefix + PROPERTY_HASH); - byte[] hash = hashS == null ? null : toHashBytes(id, hashS); - - ComponentPackageLoader ldr = new ComponentPackageLoader( - new PrefixedPropertyReader(prefix, catalogProperties), - feedback); - ComponentInfo info = ldr.createComponentInfo(); - info.setRemoteURL(downloadURL); - info.setShaDigest(hash); - return info; + public Map loadGraalVersionInfo() { + return localRegistry.getGraalCapabilities(); } - static class PrefixedPropertyReader implements Function { - private final String compPrefix; - private final Properties props; - - PrefixedPropertyReader(String compPrefix, Properties props) { - this.compPrefix = compPrefix; - this.props = props; - } + @Override + public ComponentInfo loadComponentFiles(ComponentInfo ci) throws IOException { + // files are not supported, yet + return ci; + } - @Override - public String apply(String t) { - return props.getProperty(compPrefix + t); - } + protected byte[] toHashBytes(String comp, String hashS) { + return toHashBytes(comp, hashS, feedback); } @Override @@ -183,6 +122,15 @@ public void saveComponent(ComponentInfo info) throws IOException { throw new UnsupportedOperationException("Should not be called."); // NOI18N } + @Override + public Date licenseAccepted(ComponentInfo info, String licenseID) { + return null; + } + + @Override + public void recordLicenseAccepted(ComponentInfo info, String licenseID, String licenseText) throws IOException { + } + @Override public void updateReplacedFiles(Map> replacedFiles) throws IOException { throw new UnsupportedOperationException("Should not be called."); // NOI18N diff --git a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/persist/Bundle.properties b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/persist/Bundle.properties index 989aa3d2805a..9e6a801cdcc4 100644 --- a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/persist/Bundle.properties +++ b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/persist/Bundle.properties @@ -1,7 +1,3 @@ -# {0} - component ID -# {1} - component version -LICENSE_Path_translation=LICENSE-{0}-{1} - ERROR_HeaderMissing=Required header is missing: {0} ERROR_HeaderInvalid=Header {0} value is invalid: {1} ERROR_MissingSymbolicName=Missing symbolic name @@ -31,3 +27,4 @@ ERROR_PermissionFormat=Invalid file permissions format ERROR_BrokenSymlink=Symbolic link {0} is broken ERROR_CircularSymlink=Symbolic link {0} forms a circular reference. ERROR_CorruptedPackageMissingMeta=The package is corrupted, internal metadata is missing. + diff --git a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/persist/ComponentPackageLoader.java b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/persist/ComponentPackageLoader.java index dd50349f2b04..06bd8d96b0b4 100644 --- a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/persist/ComponentPackageLoader.java +++ b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/persist/ComponentPackageLoader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,16 +24,13 @@ */ package org.graalvm.component.installer.persist; +import java.io.BufferedReader; import org.graalvm.component.installer.MetadataException; import org.graalvm.component.installer.InstallerStopException; -import java.io.BufferedReader; import java.io.Closeable; import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; import java.nio.file.Path; import java.nio.file.attribute.PosixFilePermissions; -import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -48,14 +45,8 @@ import java.util.ResourceBundle; import java.util.Set; import java.util.function.Function; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; -import java.util.jar.Manifest; +import org.graalvm.component.installer.Archive; import org.graalvm.component.installer.BundleConstants; -import static org.graalvm.component.installer.BundleConstants.PATH_LICENSE; -import static org.graalvm.component.installer.BundleConstants.META_INF_PATH; -import static org.graalvm.component.installer.BundleConstants.META_INF_PERMISSIONS_PATH; -import static org.graalvm.component.installer.BundleConstants.META_INF_SYMLINKS_PATH; import org.graalvm.component.installer.Feedback; import org.graalvm.component.installer.SystemUtils; import org.graalvm.component.installer.model.ComponentInfo; @@ -65,8 +56,7 @@ */ public class ComponentPackageLoader implements Closeable, MetadataLoader { private final Feedback feedback; - private final JarFile jarFile; - private final Manifest manifest; + /** * Default value producer. */ @@ -96,6 +86,11 @@ public class ComponentPackageLoader implements Closeable, MetadataLoader { */ private String licensePath; + /** + * Type / name of the license. + */ + private String licenseType; + /** * The produced component info. */ @@ -110,26 +105,14 @@ public class ComponentPackageLoader implements Closeable, MetadataLoader { static final ResourceBundle BUNDLE = ResourceBundle.getBundle("org.graalvm.component.installer.persist.Bundle"); - public ComponentPackageLoader(JarFile jarFile, Feedback feedback) throws IOException { - this.feedback = feedback.withBundle(ComponentPackageLoader.class); - this.jarFile = jarFile; - manifest = jarFile.getManifest(); - if (manifest == null) { - throw this.feedback.failure("ERROR_CorruptedPackageMissingMeta", null); - } - valueSupplier = (s) -> manifest.getMainAttributes().getValue(s); - } - - ComponentPackageLoader(Function supplier, Feedback feedback) { + public ComponentPackageLoader(Function supplier, Feedback feedback) { this.feedback = feedback.withBundle(ComponentPackageLoader.class); this.valueSupplier = supplier; - this.jarFile = null; - this.manifest = null; } @Override - public JarFile getJarFile() { - return jarFile; + public Archive getArchive() { + return null; } @Override @@ -214,23 +197,38 @@ public ComponentInfo createComponentInfo() { }, () -> info.setPolyglotRebuild(parseHeader(BundleConstants.BUNDLE_POLYGLOT_PART, null).getBoolean(Boolean.FALSE)), () -> loadWorkingDirectories(), - () -> loadMessages() + () -> loadMessages(), + () -> loadLicenseType() ); return info; } + private void loadLicenseType() { + licenseType = parseHeader(BundleConstants.BUNDLE_LICENSE_TYPE, null).getContents(null); + } + @Override public String getLicensePath() { return licensePath; } + @Override + public String getLicenseID() { + return null; + } + + @Override + public String getLicenseType() { + return licenseType; + } + private void throwInvalidPermissions() { throw feedback.failure("ERROR_PermissionFormat", null); } @SuppressWarnings("unchecked") - Map parsePermissions(BufferedReader r) throws IOException { + protected Map parsePermissions(BufferedReader r) throws IOException { Map result = new LinkedHashMap<>(); Properties prop = new Properties(); @@ -255,19 +253,11 @@ Map parsePermissions(BufferedReader r) throws IOException { @Override public Map loadPermissions() throws IOException { - JarEntry permEntry = jarFile.getJarEntry(META_INF_PERMISSIONS_PATH); - if (permEntry == null) { - return Collections.emptyMap(); - } - try (BufferedReader r = new BufferedReader(new InputStreamReader( - jarFile.getInputStream(permEntry), "UTF-8"))) { - Map permissions = parsePermissions(r); - return permissions; - } + return Collections.emptyMap(); } @SuppressWarnings({"rawtypes", "unchecked"}) - Map parseSymlinks(Properties links) { + protected Map parseSymlinks(Properties links) { for (String key : new HashSet<>(links.stringPropertyNames())) { Path p = SystemUtils.fromCommonString(key).normalize(); String prop = (String) links.remove(key); @@ -303,51 +293,12 @@ Map parseSymlinks(Properties links) { @Override public Map loadSymlinks() throws IOException { - assert info != null; - JarEntry symEntry = jarFile.getJarEntry(META_INF_SYMLINKS_PATH); - if (symEntry == null) { - return Collections.emptyMap(); - } - Properties links = new Properties(); - try (InputStream istm = jarFile.getInputStream(symEntry)) { - links.load(istm); - } - return parseSymlinks(links); + return Collections.emptyMap(); } @Override public void loadPaths() { - ComponentInfo cinfo = getComponentInfo(); - Set emptyDirectories = new HashSet<>(); - for (JarEntry en : Collections.list(jarFile.entries())) { - String eName = en.getName(); - if (eName.startsWith(META_INF_PATH)) { - continue; - } - int li = eName.lastIndexOf("/", en.isDirectory() ? eName.length() - 2 : eName.length() - 1); - if (li > 0) { - emptyDirectories.remove(eName.substring(0, li + 1)); - } - if (PATH_LICENSE.equals(eName)) { - this.licensePath = MessageFormat.format( - BUNDLE.getString("LICENSE_Path_translation"), - cinfo.getId(), - cinfo.getVersionString()); - fileList.add(licensePath); - cinfo.setLicensePath(licensePath); - continue; - } - if (en.isDirectory()) { - // directory names always come first - emptyDirectories.add(eName); - } else { - fileList.add(eName); - } - } - fileList.addAll(emptyDirectories); - // sort empty directories first - Collections.sort(fileList); - cinfo.addPaths(fileList); + getComponentInfo(); } @Override @@ -362,9 +313,6 @@ public void setNoVerifySymlinks(boolean noVerifySymlinks) { @Override public void close() throws IOException { - if (jarFile != null) { - jarFile.close(); - } } private void loadMessages() { @@ -374,4 +322,13 @@ private void loadMessages() { info.setPostinstMessage(text); } } + + protected void setLicensePath(String path) { + this.licensePath = path; + getComponentInfo().setLicensePath(licensePath); + } + + protected void addFiles(List files) { + fileList.addAll(files); + } } diff --git a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/persist/DirectoryStorage.java b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/persist/DirectoryStorage.java index b59986b95711..6d5a8e753688 100644 --- a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/persist/DirectoryStorage.java +++ b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/persist/DirectoryStorage.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,12 +30,17 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.io.UncheckedIOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; +import java.text.MessageFormat; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.Comparator; +import java.util.Date; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; @@ -46,6 +51,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; +import java.util.stream.Stream; import org.graalvm.component.installer.BundleConstants; import org.graalvm.component.installer.CommonConstants; import org.graalvm.component.installer.Feedback; @@ -78,6 +84,22 @@ public class DirectoryStorage implements ComponentStorage { */ private static final String PATH_REPLACED_FILES = "replaced-files.properties"; // NOI18N + /** + * + * Template for license accepted records. + */ + private static final String LICENSE_DIR = "licenses"; // NOI18N' + + /** + * Template for license accepted records. + */ + private static final String LICENSE_CONTENTS_NAME = LICENSE_DIR + "/{0}"; // NOI18N' + + /** + * Template for license accepted records. + */ + private static final String LICENSE_FILE_TEMPLATE = LICENSE_DIR + "/{0}.accepted/{1}"; // NOI18N' + /** * */ @@ -160,7 +182,7 @@ private Map load(InputStream istm) throws IOException { /** * Loads list of components. - * + * * @return component IDs * @throws IOException */ @@ -176,13 +198,17 @@ public boolean accept(File child) { return Files.isRegularFile(child.toPath()) && child.getName().endsWith(COMPONENT_FILE_SUFFIX); } }); - Set result = new HashSet<>(); - for (File f : files) { - String s = registryPath.relativize(f.toPath()).toString(); - int e = s.length() - COMPONENT_FILE_SUFFIX.length(); - result.add(s.substring(0, e)); + if (files != null) { + Set result = new HashSet<>(); + for (File f : files) { + String s = registryPath.relativize(f.toPath()).toString(); + int e = s.length() - COMPONENT_FILE_SUFFIX.length(); + result.add(s.substring(0, e)); + } + return result; + } else { + throw new IllegalArgumentException("File listing of " + d + " returned null."); } - return result; } @SuppressWarnings("unchecked") @@ -195,7 +221,7 @@ ComponentInfo loadMetadataFrom(InputStream fileStream) throws IOException { String name = getRequiredProperty(BundleConstants.BUNDLE_NAME); String version = getRequiredProperty(BundleConstants.BUNDLE_VERSION); - String license = loaded.getProperty(META_LICENSE_FILE); + String license = loaded.getProperty(BundleConstants.BUNDLE_LICENSE_PATH); ci = new ComponentInfo(id, name, version); if (license != null) { @@ -218,6 +244,7 @@ ComponentInfo loadMetadataFrom(InputStream fileStream) throws IOException { } } ci.addWorkingDirectories(ll); + ci.setLicenseType(loaded.getProperty(BundleConstants.BUNDLE_LICENSE_TYPE)); return ci; } @@ -236,7 +263,7 @@ public ComponentInfo loadComponentMetadata(String tag) throws IOException { /** * Loads component files into its metadata. - * + * * @param ci the component metadata * @return initialized ComponentInfo * @throws IOException on I/O errors @@ -320,7 +347,7 @@ public void updateReplacedFiles(Map> replacedFiles) t /** * Deletes component's files. - * + * * @param id component id * @throws IOException */ @@ -334,7 +361,7 @@ public void deleteComponent(String id) throws IOException { /** * Will persist component's metadata. - * + * * @param info * @throws IOException on failure */ @@ -355,7 +382,10 @@ Properties metaToProperties(ComponentInfo info) { p.setProperty(BundleConstants.BUNDLE_NAME, info.getName()); p.setProperty(BundleConstants.BUNDLE_VERSION, info.getVersionString()); if (info.getLicensePath() != null) { - p.setProperty(META_LICENSE_FILE, info.getLicensePath()); + p.setProperty(BundleConstants.BUNDLE_LICENSE_PATH, info.getLicensePath()); + } + if (info.getLicenseType() != null) { + p.setProperty(BundleConstants.BUNDLE_LICENSE_TYPE, info.getLicenseType()); } for (String k : info.getRequiredGraalValues().keySet()) { String v = info.getRequiredGraalValues().get(k); @@ -381,4 +411,64 @@ void saveComponentFileList(ComponentInfo info) throws IOException { Files.write(listFile, entries, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); } + + @Override + public Date licenseAccepted(ComponentInfo info, String licenseID) { + try { + String fn = MessageFormat.format(LICENSE_FILE_TEMPLATE, licenseID, info.getId()); + Path listFile = registryPath.resolve(SystemUtils.fromCommonString(fn)); + if (!Files.isReadable(listFile)) { + return null; + } + return new Date(Files.getLastModifiedTime(listFile).toMillis()); + } catch (IOException ex) { + throw feedback.failure("ERR_CannotReadAcceptance", ex, licenseID); + } + } + + @Override + public void recordLicenseAccepted(ComponentInfo info, String licenseID, String licenseText) throws IOException { + if (licenseID == null) { + clearRecordedLicenses(); + return; + } + String fn = MessageFormat.format(LICENSE_FILE_TEMPLATE, licenseID, info.getId()); + Path listFile = registryPath.resolve(SystemUtils.fromCommonString(fn)); + if (listFile == null) { + throw new IllegalArgumentException(licenseID); + } + Path dir = listFile.getParent(); + if (dir == null) { + throw new IllegalArgumentException(licenseID); + } + if (!Files.isDirectory(dir)) { + // create the directory + Files.createDirectories(dir); + Path contentsFile = registryPath.resolve(SystemUtils.fromCommonString( + MessageFormat.format(LICENSE_CONTENTS_NAME, licenseID))); + Files.write(contentsFile, Arrays.asList(licenseText.split("\n"))); + } + Date d = new Date(); + Files.write(listFile, Collections.singletonList(d.toString()), StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); + } + + void clearRecordedLicenses() throws IOException { + Path listFile = registryPath.resolve(LICENSE_DIR); + if (Files.isDirectory(listFile)) { + try (Stream paths = Files.walk(listFile)) { + paths.sorted(Comparator.reverseOrder()).forEach((p) -> { + try { + if (p.equals(listFile)) { + return; + } + Files.delete(p); + } catch (IOException ex) { + throw new UncheckedIOException(ex); + } + }); + } catch (UncheckedIOException ex) { + throw ex.getCause(); + } + } + } } diff --git a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/persist/MetadataLoader.java b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/persist/MetadataLoader.java index 5435273794b2..4ea06a51e191 100644 --- a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/persist/MetadataLoader.java +++ b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/persist/MetadataLoader.java @@ -24,30 +24,55 @@ */ package org.graalvm.component.installer.persist; +import java.io.Closeable; import java.io.IOException; import java.util.List; import java.util.Map; -import java.util.jar.JarFile; +import org.graalvm.component.installer.Archive; import org.graalvm.component.installer.InstallerStopException; import org.graalvm.component.installer.model.ComponentInfo; -public interface MetadataLoader { - - void close() throws IOException; +/** + * Abstraction that loads metadata for a given component. + * + * @author sdedic + */ +public interface MetadataLoader extends Closeable { ComponentInfo getComponentInfo(); List getErrors(); - JarFile getJarFile(); + Archive getArchive() throws IOException; + + /** + * License name/type. Not the actual content, but general name, like "Oracle OTN license", + * "GPLv2" or so. + * + * @return license type or {@code null} + */ + String getLicenseType(); + + /** + * License ID. Usually a digest of the license file contents. + * + * @return license ID + */ + String getLicenseID(); + /** + * Path to the license file. Should be to iterate through {@link #getArchive} to obtain the + * license contents. + * + * @return path to the license or {@code null} + */ String getLicensePath(); MetadataLoader infoOnly(boolean only); boolean isNoVerifySymlinks(); - void loadPaths(); + void loadPaths() throws IOException; Map loadPermissions() throws IOException; diff --git a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/remote/Bundle.properties b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/remote/Bundle.properties new file mode 100644 index 000000000000..cb253af4ee14 --- /dev/null +++ b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/remote/Bundle.properties @@ -0,0 +1,21 @@ +# There's an important trailing space in the next key +MSG_DownloadReceivingBytes=Receiving {0} kB: +# The progress bar should have a frame and exactly 20 space characters +MSG_DownloadProgress=[ ] +MSG_DownloadProgressSignChar=# +MSG_DownloadingDone=Done. +MSG_DownloadingTerminated=Terminated. +MSG_Downloading=Downloading: {0} +MSG_DownloadingVerbose=Downloading: {0} (source: {1}) +MSG_DownloadingFrom=Downloading from: {0} + +# {0} - original error message +ERR_ComputeDigest=Error computing digest: {0} +# {0} - expected fingerprint, {1} computed fingerprint +ERR_FileDigestError=Corrupted file, digest does not match. Expected {0}, got {1} + +EXC_ProxyFailed=Failed to connect through proxy: {0} +EXC_TimeoutConnectTo=Timeout while connecting to {0} +EXC_CannotConnectTo=Cannot connect to {0} + +REMOTE_CannotHandleLocation=Unable to handle catalog at {0} diff --git a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/remote/CatalogIterable.java b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/remote/CatalogIterable.java new file mode 100644 index 000000000000..8a1a8de93466 --- /dev/null +++ b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/remote/CatalogIterable.java @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.graalvm.component.installer.remote; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Path; +import java.util.Iterator; +import org.graalvm.component.installer.CommandInput; +import org.graalvm.component.installer.Commands; +import org.graalvm.component.installer.ComponentIterable; +import org.graalvm.component.installer.ComponentParam; +import org.graalvm.component.installer.FailedOperationException; +import org.graalvm.component.installer.Feedback; +import org.graalvm.component.installer.SoftwareChannel; +import org.graalvm.component.installer.model.ComponentInfo; +import org.graalvm.component.installer.model.ComponentRegistry; +import org.graalvm.component.installer.persist.MetadataLoader; + +/** + * Interprets installer arguments as entries from a catalog. + * + * @author sdedic + */ +public class CatalogIterable implements ComponentIterable { + private final CommandInput input; + private final Feedback feedback; + private final SoftwareChannel factory; + private ComponentRegistry remoteRegistry; + private boolean verifyJars; + + public CatalogIterable(CommandInput input, Feedback feedback, SoftwareChannel fact) { + this.input = input; + this.feedback = feedback; + this.factory = fact; + } + + public boolean isVerifyJars() { + return verifyJars; + } + + @Override + public void setVerifyJars(boolean verifyJars) { + this.verifyJars = verifyJars; + } + + @Override + public Iterator iterator() { + return new It(); + } + + ComponentRegistry getRegistry() { + if (remoteRegistry == null) { + remoteRegistry = factory.getRegistry(); + } + return remoteRegistry; + } + + private class It implements Iterator { + private void thrownUnknown(String fname, boolean throwUnknown) { + File f = new File(fname); + if (f.exists() && f.isFile()) { + throw feedback.failure("REMOTE_UnknownComponentMaybeFile", null, fname); + } else if (throwUnknown) { + throw feedback.failure("REMOTE_UnknownComponentId", null, fname); + } + } + + @Override + public boolean hasNext() { + return input.hasParameter(); + } + + @Override + public ComponentParam next() { + String s = input.nextParameter(); + ComponentInfo info; + try { + if (getRegistry().findComponent(s.toLowerCase()) == null) { + thrownUnknown(s, true); + } + + info = getRegistry().loadSingleComponent(s.toLowerCase(), false); + if (info == null) { + thrownUnknown(s, true); + } + } catch (FailedOperationException ex) { + thrownUnknown(s, false); + throw ex; + } + boolean progress = input.optValue(Commands.OPTION_NO_DOWNLOAD_PROGRESS) == null; + return createComponenParam(s, info, progress); + } + } + + protected ComponentParam createComponenParam(String cmdLineString, ComponentInfo info, boolean progress) { + RemoteComponentParam param = new CatalogItemParam( + factory, + info, + feedback.l10n("REMOTE_ComponentFileLabel", cmdLineString), + cmdLineString, + feedback, progress); + param.setVerifyJars(verifyJars); + return param; + } + + public static class CatalogItemParam extends RemoteComponentParam { + final SoftwareChannel channel; + + public CatalogItemParam(SoftwareChannel channel, ComponentInfo catalogInfo, String dispName, String spec, Feedback feedback, boolean progress) { + super(catalogInfo, dispName, spec, feedback, progress); + this.channel = channel; + } + + @Override + protected FileDownloader createDownloader() { + FileDownloader d = super.createDownloader(); + return channel.configureDownloader(d); + } + + @Override + protected MetadataLoader metadataFromLocal(Path localFile) throws IOException { + return channel.createLocalFileLoader(localFile, isVerifyJars()); + } + + @Override + public MetadataLoader completeMetadata() throws IOException { + MetadataLoader ldr = createMetaLoader(); + return channel.completeMetadata(ldr, ldr.getComponentInfo()); + } + } +} diff --git a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/persist/FileDownloader.java b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/remote/FileDownloader.java similarity index 61% rename from vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/persist/FileDownloader.java rename to vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/remote/FileDownloader.java index 2181ce706e31..fdded6eef245 100644 --- a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/persist/FileDownloader.java +++ b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/remote/FileDownloader.java @@ -22,43 +22,37 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package org.graalvm.component.installer.persist; +package org.graalvm.component.installer.remote; import java.io.File; import java.io.IOException; -import java.io.InputStream; -import java.net.ConnectException; -import java.net.HttpURLConnection; -import java.net.InetSocketAddress; -import java.net.Proxy; -import java.net.URI; -import java.net.URISyntaxException; +import java.io.UncheckedIOException; import java.net.URL; import java.net.URLConnection; import java.nio.ByteBuffer; import java.nio.channels.Channels; import java.nio.channels.ReadableByteChannel; -import java.nio.channels.WritableByteChannel; +import java.nio.channels.SeekableByteChannel; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Arrays; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicReference; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Consumer; import org.graalvm.component.installer.Feedback; +import org.graalvm.component.installer.URLConnectionFactory; -public class FileDownloader { - String envHttpProxy = System.getenv("http_proxy"); // NOI18N - String envHttpsProxy = System.getenv("https_proxy"); // NOI18N - +/** + * Downloads file to local, optionally checks its integrity using digest. + * + * @author sdedic + */ +public final class FileDownloader { private static final int TRANSFER_LENGTH = 2048; private static final long MIN_PROGRESS_THRESHOLD = Long.getLong("org.graalvm.component.installer.minDownloadFeedback", 1024 * 1024); - private static final int DEFAULT_CONNECT_DELAY = Integer.getInteger("org.graalvm.component.installer.connectDelaySec", 5); private final String fileDescription; private final URL sourceURL; private final Feedback feedback; @@ -70,13 +64,20 @@ public class FileDownloader { private static volatile File tempDir; private boolean displayProgress; private byte[] shaDigest; - private int connectDelay = DEFAULT_CONNECT_DELAY; long sizeThreshold = MIN_PROGRESS_THRESHOLD; + private final Map requestHeaders = new HashMap<>(); + private Consumer dataInterceptor; + private URLConnectionFactory connectionFactory; + + /** + * Algorithm to compute file digest. By default SHA-256 is used. + */ + private String digestAlgorithm = "SHA-256"; public FileDownloader(String fileDescription, URL sourceURL, Feedback feedback) { this.fileDescription = fileDescription; this.sourceURL = sourceURL; - this.feedback = feedback; + this.feedback = feedback.withBundle(FileDownloader.class); } public void setShaDigest(byte[] shaDigest) { @@ -95,6 +96,18 @@ public void setDisplayProgress(boolean displayProgress) { this.displayProgress = displayProgress; } + public void addRequestHeader(String header, String val) { + requestHeaders.put(header, val); + } + + public String getDigestAlgorithm() { + return digestAlgorithm; + } + + public void setDigestAlgorithm(String digestAlgorithm) { + this.digestAlgorithm = digestAlgorithm; + } + public static synchronized File createTempDir() throws IOException { if (tempDir == null) { Path p = Files.createTempDirectory("graalvm_install"); // NOI18N @@ -171,11 +184,15 @@ void makeProgress(boolean first, int chunk) { } } - void stopProgress() { + void stopProgress(boolean success) { if (displayProgress) { feedback.verbatimPart(backspaceString, false); } - feedback.verboseOutput("MSG_DownloadingDone"); + if (success) { + feedback.verboseOutput("MSG_DownloadingDone"); + } else { + feedback.output("MSG_DownloadingTerminated"); + } } void updateFileDigest(ByteBuffer input) throws IOException { @@ -184,7 +201,7 @@ void updateFileDigest(ByteBuffer input) throws IOException { } if (fileDigest == null) { try { - fileDigest = MessageDigest.getInstance("SHA-256"); // NOI18N + fileDigest = MessageDigest.getInstance(getDigestAlgorithm()); // NOI18N } catch (NoSuchAlgorithmException ex) { throw new IOException( feedback.l10n("ERR_ComputeDigest", ex.getLocalizedMessage()), @@ -194,7 +211,7 @@ void updateFileDigest(ByteBuffer input) throws IOException { fileDigest.update(input); } - String fingerPrint(byte[] digest) { + static String fingerPrint(byte[] digest) { StringBuilder sb = new StringBuilder(digest.length * 3); for (int i = 0; i < digest.length; i++) { if (i > 0) { @@ -221,102 +238,21 @@ void verifyDigest() throws IOException { fingerPrint(shaDigest), fingerPrint(computed))); } - private URLConnection openConnectionWithProxies(URL url) throws IOException { - final URLConnection[] conn = {null}; - final CountDownLatch connected = new CountDownLatch(1); - ExecutorService connectors = Executors.newFixedThreadPool(3); - AtomicReference ex3 = new AtomicReference<>(); - AtomicReference ex2 = new AtomicReference<>(); - String httpProxy; - String httpsProxy; - - synchronized (this) { - httpProxy = envHttpProxy; - httpsProxy = envHttpsProxy; + void configureHeaders(URLConnection con) { + for (String h : requestHeaders.keySet()) { + con.addRequestProperty(h, requestHeaders.get(h)); } + } - class Connector implements Runnable { - private final String proxySpec; - private final boolean directConnect; - - Connector() { - directConnect = true; - proxySpec = null; - } - - Connector(String proxySpec) { - this.proxySpec = proxySpec; - this.directConnect = false; - } - - @Override - public void run() { - final Proxy proxy; - if (directConnect) { - proxy = null; - } else { - if (proxySpec == null || proxySpec.isEmpty()) { - return; - } - try { - URI uri = new URI(httpsProxy); - InetSocketAddress address = InetSocketAddress.createUnresolved(uri.getHost(), uri.getPort()); - proxy = new Proxy(Proxy.Type.HTTP, address); - } catch (URISyntaxException ex) { - return; - } - } - try { - URLConnection test = directConnect ? url.openConnection() : url.openConnection(proxy); - test.connect(); - if (test instanceof HttpURLConnection) { - HttpURLConnection htest = (HttpURLConnection) test; - int rcode = htest.getResponseCode(); - if (rcode >= 400) { - // force the exception, should fail with IOException - InputStream stm = test.getInputStream(); - try { - stm.close(); - } catch (IOException ex) { - // swallow, we want to report just proxy failed. - } - throw new IOException(feedback.l10n("EXC_ProxyFailed", rcode)); - } - } - conn[0] = test; - connected.countDown(); - } catch (IOException ex) { - if (directConnect) { - ex3.set(ex); - } else { - ex2.set(ex); - } - } - } - - } - connectors.submit(new Connector(httpProxy)); - connectors.submit(new Connector(httpsProxy)); - connectors.submit(new Connector()); - try { - if (!connected.await(connectDelay, TimeUnit.SECONDS)) { - if (ex3.get() != null) { - throw ex3.get(); - } - throw new ConnectException("Timeout while connecting to " + url); - } - if (conn[0] == null) { - if (ex3.get() != null) { - throw ex3.get(); - } else if (ex2.get() != null) { - throw ex2.get(); - } - throw new ConnectException("Cannot connect to " + url); - } - } catch (InterruptedException ex) { - Thread.currentThread().interrupt(); + protected void dataDownloaded(SeekableByteChannel ch) { + if (dataInterceptor != null) { + dataInterceptor.accept(ch); } - return conn[0]; + } + + public FileDownloader setDataInterceptor(Consumer interceptor) { + this.dataInterceptor = interceptor; + return this; } public void download() throws IOException { @@ -327,7 +263,14 @@ public void download() throws IOException { } else { feedback.output("MSG_DownloadingFrom", getSourceURL()); } - URLConnection conn = openConnectionWithProxies(sourceURL); + + Path localCache = feedback.getLocalCache(sourceURL); + if (localCache != null) { + localFile = localCache.toFile(); + return; + } + + URLConnection conn = getConnectionFactory().createConnection(sourceURL, this::configureHeaders); size = conn.getContentLengthLong(); verbose = feedback.verbosePart("MSG_DownloadReceivingBytes", toKB(size)); if (verbose) { @@ -340,13 +283,11 @@ public void download() throws IOException { setupProgress(); ByteBuffer bb = ByteBuffer.allocate(TRANSFER_LENGTH); localFile = deleteOnExit(File.createTempFile("download", "", createTempDir())); // NOI18N - if (fileDescription != null) { - feedback.bindFilename(localFile.toPath(), fileDescription); - } boolean first = displayProgress; + boolean success = false; try ( ReadableByteChannel rbc = Channels.newChannel(conn.getInputStream()); - WritableByteChannel wbc = Files.newByteChannel(localFile.toPath(), + SeekableByteChannel wbc = Files.newByteChannel(localFile.toPath(), StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) { int read; while ((read = rbc.read(bb)) >= 0) { @@ -356,6 +297,9 @@ public void download() throws IOException { bb.flip(); while (bb.hasRemaining()) { wbc.write(bb); + long pos = wbc.position(); + dataDownloaded(wbc); + wbc.position(pos); } bb.flip(); updateFileDigest(bb); @@ -363,11 +307,27 @@ public void download() throws IOException { bb.clear(); first = false; } + success = true; + } catch (UncheckedIOException ex) { + throw ex.getCause(); } catch (IOException ex) { // f.delete(); throw ex; + } finally { + stopProgress(success); } - stopProgress(); verifyDigest(); + feedback.addLocalFileCache(sourceURL, localFile.toPath()); + } + + public void setConnectionFactory(URLConnectionFactory connFactory) { + this.connectionFactory = connFactory; + } + + URLConnectionFactory getConnectionFactory() { + if (connectionFactory == null) { + connectionFactory = new ProxyConnectionFactory(feedback, sourceURL); + } + return connectionFactory; } } diff --git a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/remote/ProxyConnectionFactory.java b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/remote/ProxyConnectionFactory.java new file mode 100644 index 000000000000..076176e2506f --- /dev/null +++ b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/remote/ProxyConnectionFactory.java @@ -0,0 +1,266 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.graalvm.component.installer.remote; + +import java.io.IOException; +import java.io.InputStream; +import java.net.ConnectException; +import java.net.HttpURLConnection; +import java.net.InetSocketAddress; +import java.net.Proxy; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.net.URLConnection; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; +import org.graalvm.component.installer.Feedback; +import org.graalvm.component.installer.URLConnectionFactory; + +/** + * Creates URLConnections to the given destination. Caches the decision about proxy. + * + * @author sdedic + */ +public class ProxyConnectionFactory implements URLConnectionFactory { + enum ProxyType { + DIRECT, + HTTP, + HTTPS + } + + /** + * The max delay to connect to the final destination or open a proxy connection. In seconds. + */ + private static final int DEFAULT_CONNECT_DELAY = Integer.getInteger("org.graalvm.component.installer.connectDelaySec", 5); + + private final Feedback feedback; + private final URL urlBase; + + /** + * Remembered type of proxy / no proxy. + */ + private volatile ProxyType detectedType; + + /** + * HTTP proxy settings. The default is taken from system environment variables. + */ + String envHttpProxy = System.getenv("http_proxy"); // NOI18N + + /** + * HTTPS proxy settings. The default is taken from system environment variables. + */ + String envHttpsProxy = System.getenv("https_proxy"); // NOI18N + + /** + * The configurable delay for this factory. Initialized to {@link #DEFAULT_CONNECT_DELAY}. + */ + private int connectDelay = DEFAULT_CONNECT_DELAY; + + public ProxyConnectionFactory(Feedback feedback, URL urlBase) { + this.feedback = feedback.withBundle(ProxyConnectionFactory.class); + this.urlBase = urlBase; + } + + public ProxyConnectionFactory setProxy(boolean secure, String proxyURI) { + if (secure) { + envHttpsProxy = proxyURI; + } else { + envHttpProxy = proxyURI; + } + return this; + } + + public URLConnection openConnection(URI relative, Consumer configCallback) throws IOException { + if (relative != null) { + try { + return openConnectionWithProxies(urlBase.toURI().resolve(relative).toURL(), configCallback); + } catch (URISyntaxException ex) { + throw new IOException(ex); + } + } else { + return openConnectionWithProxies(urlBase, configCallback); + } + } + + private URLConnection openConnectionWithProxies(URL url, Consumer configCallback) throws IOException { + final URLConnection[] conn = {null}; + final CountDownLatch connected = new CountDownLatch(1); + ExecutorService connectors = Executors.newFixedThreadPool(3); + AtomicReference ex3 = new AtomicReference<>(); + AtomicReference ex2 = new AtomicReference<>(); + String httpProxy; + String httpsProxy; + + synchronized (this) { + httpProxy = envHttpProxy; + httpsProxy = envHttpsProxy; + } + + class Connector implements Runnable { + private final String proxySpec; + private final boolean directConnect; + private final ProxyType type; + + Connector() { + directConnect = true; + proxySpec = null; + this.type = ProxyType.DIRECT; + } + + Connector(String proxySpec, ProxyType pt) { + this.proxySpec = proxySpec; + this.directConnect = false; + this.type = pt; + } + + @Override + public void run() { + final Proxy proxy; + if (directConnect) { + proxy = null; + } else { + if (proxySpec == null || proxySpec.isEmpty()) { + return; + } + try { + URI uri = new URI(httpsProxy); + InetSocketAddress address = InetSocketAddress.createUnresolved(uri.getHost(), uri.getPort()); + proxy = new Proxy(Proxy.Type.HTTP, address); + } catch (URISyntaxException ex) { + return; + } + } + try { + URLConnection test = directConnect ? url.openConnection() : url.openConnection(proxy); + if (configCallback != null) { + configCallback.accept(test); + } + test.connect(); + if (test instanceof HttpURLConnection) { + HttpURLConnection htest = (HttpURLConnection) test; + int rcode = htest.getResponseCode(); + if (rcode >= 400) { + // force the exception, should fail with IOException + InputStream stm = test.getInputStream(); + try { + stm.close(); + } catch (IOException ex) { + // swallow, we want to report just proxy failed. + } + throw new IOException(feedback.l10n("EXC_ProxyFailed", rcode)); + } + } + synchronized (conn) { + if (conn[0] == null) { + conn[0] = test; + detectedType = type; + } + } + connected.countDown(); + } catch (IOException ex) { + if (directConnect) { + ex3.set(ex); + } else { + ex2.set(ex); + } + } + } + + } + if (detectedType != null) { + Connector c; + switch (detectedType) { + case DIRECT: + c = new Connector(); + break; + case HTTP: + c = new Connector(httpProxy, ProxyType.HTTP); + break; + case HTTPS: + c = new Connector(httpsProxy, ProxyType.HTTPS); + break; + default: + // cannot happen. + throw new AssertionError(detectedType.name()); + } + c.run(); + synchronized (conn) { + if (conn[0] == null) { + if (ex3.get() != null) { + throw ex3.get(); + } else if (ex2.get() != null) { + throw ex2.get(); + } + throw new ConnectException(feedback.l10n("EXC_CannotConnectTo", url)); + } else { + return conn[0]; + } + } + } + + URLConnection res = null; + + connectors.submit(new Connector(httpProxy, ProxyType.HTTP)); + connectors.submit(new Connector(httpsProxy, ProxyType.HTTPS)); + connectors.submit(new Connector()); + try { + if (!connected.await(connectDelay, TimeUnit.SECONDS)) { + if (ex3.get() != null) { + throw ex3.get(); + } + throw new ConnectException(feedback.l10n("EXC_TimeoutConnectTo", url)); + } + synchronized (conn[0]) { + res = conn[0]; + } + if (res == null) { + if (ex3.get() != null) { + throw ex3.get(); + } else if (ex2.get() != null) { + throw ex2.get(); + } + throw new ConnectException(feedback.l10n("EXC_CannotConnectTo", url)); + } + } catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + } + return res; + } + + @Override + public URLConnection createConnection(URL u, Consumer configCallback) throws IOException { + try { + return openConnection(u.toURI(), configCallback); + } catch (URISyntaxException ex) { + throw new IOException(ex); + } + } + +} diff --git a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/remote/RemoteCatalogDownloader.java b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/remote/RemoteCatalogDownloader.java new file mode 100644 index 000000000000..525d360faa29 --- /dev/null +++ b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/remote/RemoteCatalogDownloader.java @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.graalvm.component.installer.remote; + +import java.io.IOException; +import java.net.URL; +import java.nio.file.Path; +import java.util.ServiceLoader; +import org.graalvm.component.installer.CommandInput; +import org.graalvm.component.installer.Feedback; +import org.graalvm.component.installer.model.ComponentRegistry; +import org.graalvm.component.installer.persist.MetadataLoader; +import org.graalvm.component.installer.SoftwareChannel; +import org.graalvm.component.installer.model.ComponentInfo; + +public class RemoteCatalogDownloader implements SoftwareChannel { + private final CommandInput input; + private final Feedback feedback; + private final String catalogString; + + private ComponentRegistry catalog; + private Iterable channels; + private SoftwareChannel delegate; + + public RemoteCatalogDownloader(CommandInput in, Feedback out, String catLocation) { + this.input = in; + this.feedback = out.withBundle(RemoteCatalogDownloader.class); + + this.catalogString = catLocation; + channels = ServiceLoader.load(SoftwareChannel.class); + } + + public RemoteCatalogDownloader(CommandInput in, Feedback out, URL catalogURL) { + this(in, out, catalogURL.toString()); + } + + // for testing only + void setChannels(Iterable chan) { + this.channels = chan; + } + + public ComponentRegistry get() { + if (catalog == null) { + catalog = openCatalog(); + } + return catalog; + } + + SoftwareChannel delegate() { + if (delegate != null) { + return delegate; + } + for (SoftwareChannel ch : channels) { + if (ch.setupLocation(catalogString)) { + this.delegate = ch; + } + } + if (delegate == null) { + throw feedback.failure("REMOTE_CannotHandleLocation", null, catalogString); + } + delegate.init(input, feedback); + return delegate; + } + + @SuppressWarnings("unchecked") + public ComponentRegistry openCatalog() { + return delegate().getRegistry(); + } + + @Override + public boolean setupLocation(String urlString) { + // should be never called, this is just a delegating adapter + throw new IllegalStateException(); + } + + @Override + public void init(CommandInput ignoreInput, Feedback ignoreOutput) { + } + + @Override + public ComponentRegistry getRegistry() { + return delegate().getRegistry(); + } + + @Override + public MetadataLoader createLocalFileLoader(Path localFile, boolean verify) throws IOException { + return delegate.createLocalFileLoader(localFile, verify); + } + + @Override + public FileDownloader configureDownloader(FileDownloader dn) { + return delegate.configureDownloader(dn); + } + + @Override + public MetadataLoader completeMetadata(MetadataLoader ldr, ComponentInfo info) throws IOException { + return delegate.completeMetadata(ldr, info); + } +} diff --git a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/remote/RemoteComponentParam.java b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/remote/RemoteComponentParam.java new file mode 100644 index 000000000000..5841d624e79b --- /dev/null +++ b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/remote/RemoteComponentParam.java @@ -0,0 +1,353 @@ +/* + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.graalvm.component.installer.remote; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import org.graalvm.component.installer.Archive; +import org.graalvm.component.installer.ComponentParam; +import org.graalvm.component.installer.Feedback; +import org.graalvm.component.installer.InstallerStopException; +import org.graalvm.component.installer.model.ComponentInfo; +import org.graalvm.component.installer.persist.MetadataLoader; + +/** + * + * @author sdedic + */ +public abstract class RemoteComponentParam implements ComponentParam, MetadataLoader { + private final URL remoteURL; + private final String dispName; + private final String spec; + private final Feedback feedback; + private final ComponentInfo catalogInfo; + private ComponentInfo fileInfo; + private final boolean progress; + private boolean verifyJars; + private MetadataLoader fileLoader; + private boolean complete; + + protected RemoteComponentParam(ComponentInfo catalogInfo, String dispName, String spec, Feedback feedback, boolean progress) { + this.catalogInfo = catalogInfo; + this.dispName = dispName; + this.spec = spec; + this.feedback = feedback; + this.progress = progress; + this.remoteURL = catalogInfo.getRemoteURL(); + } + + protected RemoteComponentParam(URL remoteURL, String dispName, String spec, Feedback feedback, boolean progress) { + this.catalogInfo = null; + this.dispName = dispName; + this.spec = spec; + this.feedback = feedback; + this.remoteURL = remoteURL; + this.progress = progress; + } + + @Override + public String getSpecification() { + return spec; + } + + @Override + public String getDisplayName() { + return dispName; + } + + @Override + public MetadataLoader createMetaLoader() throws IOException { + if (catalogInfo != null) { + return this; + } else { + return createFileLoader(); + } + } + + public void setVerifyJars(boolean verifyJars) { + this.verifyJars = verifyJars; + } + + private Path localPath; + + @Override + public MetadataLoader createFileLoader() throws IOException { + if (fileLoader != null) { + return fileLoader; + } + return fileLoader = new DelegateMetaLoader( + metadataFromLocal(downloadLocalFile())); + } + + /** + * The delegate will ensure the ComponentParam will start to serve LOCAL ComponentInfo just + * after the delegate metaloader creates it. + */ + class DelegateMetaLoader implements MetadataLoader { + private final MetadataLoader delegate; + + DelegateMetaLoader(MetadataLoader delegate) { + this.delegate = delegate; + } + + @Override + public void close() throws IOException { + delegate.close(); + } + + @Override + public ComponentInfo getComponentInfo() { + fileInfo = delegate.getComponentInfo(); + if (remoteURL != null) { + fileInfo.setRemoteURL(remoteURL); + } + complete = true; + return fileInfo; + } + + @Override + public List getErrors() { + return delegate.getErrors(); + } + + @Override + public Archive getArchive() throws IOException { + return delegate.getArchive(); + } + + @Override + public String getLicenseType() { + return delegate.getLicenseType(); + } + + @Override + public String getLicenseID() { + return delegate.getLicenseID(); + } + + @Override + public String getLicensePath() { + return delegate.getLicensePath(); + } + + @Override + public MetadataLoader infoOnly(boolean only) { + return delegate.infoOnly(only); + } + + @Override + public boolean isNoVerifySymlinks() { + return delegate.isNoVerifySymlinks(); + } + + @Override + public void loadPaths() throws IOException { + delegate.loadPaths(); + } + + @Override + public Map loadPermissions() throws IOException { + return delegate.loadPermissions(); + } + + @Override + public Map loadSymlinks() throws IOException { + return delegate.loadSymlinks(); + } + + @Override + public void setNoVerifySymlinks(boolean noVerifySymlinks) { + delegate.setNoVerifySymlinks(noVerifySymlinks); + } + } + + /** + * Creates a metaloader for the local file. + * + * @param localFile the locally stored file + * @return loader reading the local file. + * @throws IOException error during construction of the loader. + */ + protected abstract MetadataLoader metadataFromLocal(Path localFile) throws IOException; + + protected Path downloadLocalFile() throws IOException { + if (localPath != null && Files.isReadable(localPath)) { + return localPath; + } + try { + FileDownloader dn = createDownloader(); + if (catalogInfo != null) { + dn.setShaDigest(catalogInfo.getShaDigest()); + } + dn.setDisplayProgress(progress); + dn.download(); + localPath = dn.getLocalFile().toPath(); + } catch (FileNotFoundException ex) { + throw feedback.failure("REMOTE_ErrorDownloadingNotExist", ex, spec, remoteURL); + } + return localPath; + } + + protected FileDownloader createDownloader() { + FileDownloader dn = new FileDownloader(feedback.l10n("REMOTE_ComponentFileLabel", spec), remoteURL, feedback); + return dn; + } + + @Override + public boolean isComplete() { + return complete; + } + + @Override + public void close() throws IOException { + if (fileLoader != null) { + fileLoader.close(); + } + if (localPath != null) { + try { + Files.deleteIfExists(localPath); + } catch (IOException ex) { + feedback.error("REMOTE_CannotDeleteLocalFile", ex, localPath.toString(), ex.getLocalizedMessage()); + } + } + } + + @Override + public ComponentInfo getComponentInfo() { + return fileInfo != null ? fileInfo : catalogInfo; + } + + @Override + public List getErrors() { + return Collections.emptyList(); + } + + @Override + public Archive getArchive() { + try { + return createFileLoader().getArchive(); + } catch (IOException ex) { + throw feedback.failure("REMOTE_ErrorDownloadingComponent", ex, spec, remoteURL, ex.getLocalizedMessage()); + } + } + + @Override + public String getLicenseType() { + if (catalogInfo != null) { + return catalogInfo.getLicenseType(); + } else { + return null; + } + } + + @Override + public String getLicenseID() { + try { + return createFileLoader().getLicenseID(); + } catch (IOException ex) { + throw feedback.failure("REMOTE_ErrorDownloadingComponent", ex, spec, remoteURL, ex.getLocalizedMessage()); + } + } + + @Override + public String getLicensePath() { + if (catalogInfo != null) { + String lt = catalogInfo.getLicenseType(); + String path = catalogInfo.getLicensePath(); + if (lt == null || path != null) { + return path; + } + } + try { + return createFileLoader().getLicensePath(); + } catch (IOException ex) { + throw feedback.failure("REMOTE_ErrorDownloadingComponent", ex, spec, remoteURL, ex.getLocalizedMessage()); + } + } + + @Override + public MetadataLoader infoOnly(boolean only) { + return this; + } + + @Override + public boolean isNoVerifySymlinks() { + return true; + } + + @Override + public void loadPaths() { + } + + @Override + public Map loadPermissions() throws IOException { + return null; + } + + @Override + public Map loadSymlinks() throws IOException { + return null; + } + + @Override + public void setNoVerifySymlinks(boolean noVerifySymlinks) { + } + + @Override + public String getFullPath() { + return remoteURL.toString(); + } + + @Override + public String getShortName() { + return remoteURL.getFile(); + } + + protected String getSpec() { + return spec; + } + + protected Feedback getFeedback() { + return feedback; + } + + protected boolean isProgress() { + return progress; + } + + protected boolean isVerifyJars() { + return verifyJars; + } + + protected Path getLocalPath() { + return localPath; + } +} diff --git a/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/remote/RemotePropertiesStorage.java b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/remote/RemotePropertiesStorage.java new file mode 100644 index 000000000000..bbe84a12f282 --- /dev/null +++ b/vm/src/org.graalvm.component.installer/src/org/graalvm/component/installer/remote/RemotePropertiesStorage.java @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.graalvm.component.installer.remote; + +import java.io.IOException; +import java.net.URL; +import java.text.MessageFormat; +import java.util.HashSet; +import java.util.Properties; +import java.util.Set; +import java.util.function.Function; +import org.graalvm.component.installer.Feedback; +import org.graalvm.component.installer.model.ComponentInfo; +import org.graalvm.component.installer.model.ComponentRegistry; +import org.graalvm.component.installer.persist.AbstractCatalogStorage; +import org.graalvm.component.installer.persist.ComponentPackageLoader; + +public class RemotePropertiesStorage extends AbstractCatalogStorage { + protected static final String PROPERTY_HASH = "hash"; // NOI18N + private static final String FORMAT_FLAVOUR = "Component.{0}."; // NOI18N + + private final Properties catalogProperties; + private final String flavourPrefix; + + public RemotePropertiesStorage(Feedback fb, ComponentRegistry localReg, Properties catalogProperties, String graalVersion, URL baseURL) { + super(localReg, fb, baseURL); + this.catalogProperties = catalogProperties; + flavourPrefix = MessageFormat.format(FORMAT_FLAVOUR, graalVersion); + } + + @Override + public Set listComponentIDs() throws IOException { + Set ret = new HashSet<>(); + for (String s : catalogProperties.stringPropertyNames()) { + if (!s.startsWith(flavourPrefix)) { + continue; + } + String rest = s.substring(flavourPrefix.length()); + if (rest.indexOf('-') == -1) { + // got a component ID + ret.add(rest.toLowerCase()); + } + } + return ret; + } + + @Override + public ComponentInfo loadComponentMetadata(String id) throws IOException { + URL downloadURL; + String s = catalogProperties.getProperty(flavourPrefix + id.toLowerCase()); + if (s == null) { + return null; + } + // try { + downloadURL = new URL(baseURL, s); + // } catch (MalformedURLException ex) { + // throw feedback.failure("REMOTE_InvalidDownloadURL", ex, s, ex.getLocalizedMessage()); + // } + String prefix = flavourPrefix + id.toLowerCase() + "-"; // NOI18N + String hashS = catalogProperties.getProperty(prefix + PROPERTY_HASH); + byte[] hash = hashS == null ? null : toHashBytes(id, hashS); + + ComponentPackageLoader ldr = new ComponentPackageLoader( + new PrefixedPropertyReader(prefix, catalogProperties), + feedback); + ComponentInfo info = ldr.createComponentInfo(); + info.setRemoteURL(downloadURL); + info.setShaDigest(hash); + return info; + } + + static class PrefixedPropertyReader implements Function { + private final String compPrefix; + private final Properties props; + + PrefixedPropertyReader(String compPrefix, Properties props) { + this.compPrefix = compPrefix; + this.props = props; + } + + @Override + public String apply(String t) { + return props.getProperty(compPrefix + t); + } + } +}