diff --git a/.github/workflows/run-jdk-compliance-tests.yml b/.github/workflows/run-jdk-compliance-tests.yml index 932ad28680..34fae979a4 100644 --- a/.github/workflows/run-jdk-compliance-tests.yml +++ b/.github/workflows/run-jdk-compliance-tests.yml @@ -20,7 +20,7 @@ jobs: fail-fast: false matrix: os: [ubuntu-20.04, macos-11] - scala: [3.2.1] + scala: [3.2.2] java: [11, 17] steps: - uses: actions/checkout@v3 @@ -44,7 +44,7 @@ jobs: strategy: fail-fast: false matrix: - scala: [3.2.1] + scala: [3.2.2] java: [11, 17] steps: # Disable autocrlf setting, otherwise scalalib patches might not be possible to apply diff --git a/.github/workflows/run-tests-linux-multiarch.yml b/.github/workflows/run-tests-linux-multiarch.yml index a9aadaba49..665081da7e 100644 --- a/.github/workflows/run-tests-linux-multiarch.yml +++ b/.github/workflows/run-tests-linux-multiarch.yml @@ -79,7 +79,7 @@ jobs: fail-fast: false matrix: arch: [linux-arm64] - scala: [2.13.10, 3.2.1] + scala: [2.13.10, 3.2.2] build-mode: [debug, release-fast] lto: [none, thin] gc: [boehm, immix, commix] @@ -87,6 +87,8 @@ jobs: # Release without LTO produces 1 big file taking long to compile, especially when emulated - build-mode: release-fast lto: none + - build-mode: release-size + lto: none - build-mode: debug lto: thin # Reduce ammount of builds combinations @@ -104,6 +106,8 @@ jobs: scala: 3.2.0 - gc: boehm build-mode: release-fast + - gc: boehm + build-mode: release-size steps: - uses: actions/checkout@v3 - uses: ./.github/actions/linux-setup-env @@ -120,8 +124,10 @@ jobs: # Following envs CROSS_ are always present in docker container run: | buildMode=${{matrix.build-mode}} - if [[ "$buildMode" == "release-fast" ]]; then + if [[ "$buildMode" == "release-fast" ]]; then buildMode=releaseFast + elif [[ "$buildMode" == "release-size" ]]; then + buildMode=releaseSize fi SetConfigTemplate=$(cat << EOM diff --git a/.github/workflows/run-tests-linux.yml b/.github/workflows/run-tests-linux.yml index bfb925153e..f5357f5b64 100644 --- a/.github/workflows/run-tests-linux.yml +++ b/.github/workflows/run-tests-linux.yml @@ -19,7 +19,7 @@ jobs: strategy: fail-fast: false matrix: - scala: [3.2.1, 2.13.10, 2.12.17, 2.11.12] + scala: [3.2.2, 2.13.10, 2.12.17] steps: - uses: actions/checkout@v3 - uses: ./.github/actions/linux-setup-env @@ -62,7 +62,7 @@ jobs: strategy: fail-fast: false matrix: - scala: [3.2.1, 2.13.10] + scala: [3.2.2, 2.13.10] build-mode: [debug, release-fast] gc: [boehm, immix, commix] # Create holes in grid to lower number of tests. @@ -72,24 +72,24 @@ jobs: build-mode: debug gc: immix include: - - scala: 2.11.12 - build-mode: debug - gc: immix - - scala: 2.11.12 - build-mode: release-fast - gc: commix - scala: 2.12.17 build-mode: debug gc: immix - scala: 2.12.17 build-mode: release-fast gc: commix + - scala: 2.12.17 + build-mode: release-size + gc: commix - scala: 3.1.3 build-mode: debug gc: immix - scala: 3.1.3 build-mode: release-fast gc: commix + - scala: 3.1.3 + build-mode: release-size + gc: commix steps: - uses: actions/checkout@v3 - uses: ./.github/actions/linux-setup-env @@ -112,7 +112,7 @@ jobs: strategy: fail-fast: false matrix: - scala: [3.2.1, 2.13.10] + scala: [3.2.2, 2.13.10] build-mode: [debug] include: - scala: 2.13.10 @@ -137,7 +137,7 @@ jobs: strategy: fail-fast: false matrix: - scala: [3.2.1, 2.13.10] + scala: [3.2.2, 2.13.10] lto: [thin] optimize: [true] include: @@ -145,7 +145,7 @@ jobs: - scala: 2.13.10 lto: full optimize: true - - scala: 3.2.1 + - scala: 3.2.2 lto: full optimize: false diff --git a/.github/workflows/run-tests-macos.yml b/.github/workflows/run-tests-macos.yml index 26100bdd25..35db8d626b 100644 --- a/.github/workflows/run-tests-macos.yml +++ b/.github/workflows/run-tests-macos.yml @@ -17,15 +17,15 @@ jobs: strategy: fail-fast: false matrix: - scala: [3.2.1, 2.13.10, 2.12.17, 2.11.12] + scala: [3.2.2, 2.13.10, 2.12.17] gc: [immix] include: - scala: 2.13.10 gc: commix + - scala: 2.13.10 + gc: none - scala: 2.12.17 gc: boehm - - scala: 2.11.12 - gc: none - scala: 3.1.3 gc: immix @@ -64,5 +64,5 @@ jobs: - name: Test scripted run: | - export LLVM_BIN=$(brew --prefix llvm@14)/bin + export LLVM_BIN=$(brew --prefix llvm@15)/bin sbt "test-scripted ${{matrix.scala}}" diff --git a/.github/workflows/run-tests-windows.yml b/.github/workflows/run-tests-windows.yml index 0a99a0f062..eb3feeaa57 100644 --- a/.github/workflows/run-tests-windows.yml +++ b/.github/workflows/run-tests-windows.yml @@ -17,13 +17,9 @@ jobs: strategy: fail-fast: false matrix: - scala: [3.2.1, 2.13.10] + scala: [3.2.2, 2.13.10] gc: [boehm, immix, commix] include: - - scala: 2.11.12 - gc: immix - - scala: 2.11.12 - gc: commix - scala: 2.12.17 gc: immix - scala: 2.12.17 @@ -98,12 +94,12 @@ jobs: strategy: fail-fast: false matrix: - scala: [3.2.1, 2.13.10] + scala: [3.2.2, 2.13.10] build-mode: [release-fast] lto: [thin] optimize: [true] include: - - scala: 3.2.1 + - scala: 3.2.2 lto: full optimize: true - scala: 2.13.10 diff --git a/README.md b/README.md index 070561e896..bc72434f74 100644 --- a/README.md +++ b/README.md @@ -18,12 +18,12 @@ Getting Started and full documentation can be found at [https://www.scala-native ## Online Scaladoc [![Scaladoc nativelib](https://javadoc.io/badge2/org.scala-native/nativelib_native0.4_2.13/javadoc.svg?label=nativelib)](https://javadoc.io/doc/org.scala-native/nativelib_native0.4_2.13) +[![Scaladoc javalib](https://javadoc.io/badge2/org.scala-native/javalib_native0.4_2.13/javadoc.svg?label=javalib)](https://javadoc.io/doc/org.scala-native/javalib_native0.4_2.13) [![Scaladoc clib](https://javadoc.io/badge2/org.scala-native/clib_native0.4_2.13/javadoc.svg?label=clib)](https://javadoc.io/doc/org.scala-native/clib_native0.4_2.13) [![Scaladoc posixlib](https://javadoc.io/badge2/org.scala-native/posixlib_native0.4_2.13/javadoc.svg?label=posixlib)](https://javadoc.io/doc/org.scala-native/posixlib_native0.4_2.13) [![Scaladoc windowslib](https://javadoc.io/badge2/org.scala-native/windowslib_native0.4_2.13/javadoc.svg?label=windowslib)](https://javadoc.io/doc/org.scala-native/windowslib_native0.4_2.13) - ## License Scala Native is distributed under the Apache License. diff --git a/auxlib/src/main/scala-3/scala/runtime/function/JProcedure.scala b/auxlib/src/main/scala-3/scala/runtime/function/JProcedure.scala index ae889cfd0b..5a0203ed1a 100644 --- a/auxlib/src/main/scala-3/scala/runtime/function/JProcedure.scala +++ b/auxlib/src/main/scala-3/scala/runtime/function/JProcedure.scala @@ -10,12 +10,10 @@ package scala.runtime.function import scala.runtime.BoxedUnit -import scala.scalanative.annotation.JavaDefaultMethod trait JProcedure0 extends scala.Function0[Object] with java.io.Serializable { def applyVoid(): Unit - @JavaDefaultMethod def apply(): Object = { applyVoid() return BoxedUnit.UNIT @@ -28,7 +26,6 @@ trait JProcedure1[T1] with java.io.Serializable { def applyVoid(t1: T1): Unit - @JavaDefaultMethod def apply(t1: T1): Object = { applyVoid(t1) return BoxedUnit.UNIT @@ -40,7 +37,6 @@ trait JProcedure2[T1, T2] with java.io.Serializable { def applyVoid(t1: T1, t2: T2): Unit - @JavaDefaultMethod def apply(t1: T1, t2: T2): Object = { applyVoid(t1, t2) return BoxedUnit.UNIT @@ -52,7 +48,6 @@ trait JProcedure3[T1, T2, T3] with java.io.Serializable { def applyVoid(t1: T1, t2: T2, t3: T3): Unit - @JavaDefaultMethod def apply(t1: T1, t2: T2, t3: T3): Object = { applyVoid(t1, t2, t3) return BoxedUnit.UNIT @@ -64,7 +59,6 @@ trait JProcedure4[T1, T2, T3, T4] with java.io.Serializable { def applyVoid(t1: T1, t2: T2, t3: T3, t4: T4): Unit - @JavaDefaultMethod def apply(t1: T1, t2: T2, t3: T3, t4: T4): Object = { applyVoid(t1, t2, t3, t4) return BoxedUnit.UNIT @@ -76,7 +70,6 @@ trait JProcedure5[T1, T2, T3, T4, T5] with java.io.Serializable { def applyVoid(t1: T1, t2: T2, t3: T3, t4: T4, t5: T5): Unit - @JavaDefaultMethod def apply(t1: T1, t2: T2, t3: T3, t4: T4, t5: T5): Object = { applyVoid(t1, t2, t3, t4, t5) return BoxedUnit.UNIT @@ -88,7 +81,6 @@ trait JProcedure6[T1, T2, T3, T4, T5, T6] with java.io.Serializable { def applyVoid(t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, t6: T6): Unit - @JavaDefaultMethod def apply(t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, t6: T6): Object = { applyVoid(t1, t2, t3, t4, t5, t6) return BoxedUnit.UNIT @@ -100,7 +92,6 @@ trait JProcedure7[T1, T2, T3, T4, T5, T6, T7] with java.io.Serializable { def applyVoid(t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, t6: T6, t7: T7): Unit - @JavaDefaultMethod def apply(t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, t6: T6, t7: T7): Object = { applyVoid(t1, t2, t3, t4, t5, t6, t7) return BoxedUnit.UNIT @@ -113,7 +104,6 @@ trait JProcedure8[T1, T2, T3, T4, T5, T6, T7, T8] def applyVoid(t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, t6: T6, t7: T7, t8: T8) : Unit - @JavaDefaultMethod def apply(t1: T1, t2: T2, t3: T3, t4: T4, t5: T5, t6: T6, t7: T7, t8: T8) : Object = { applyVoid(t1, t2, t3, t4, t5, t6, t7, t8) @@ -136,7 +126,6 @@ trait JProcedure9[T1, T2, T3, T4, T5, T6, T7, T8, T9] t9: T9 ): Unit - @JavaDefaultMethod def apply( t1: T1, t2: T2, @@ -169,7 +158,6 @@ trait JProcedure10[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10] t10: T10 ): Unit - @JavaDefaultMethod def apply( t1: T1, t2: T2, @@ -217,7 +205,6 @@ trait JProcedure11[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11] t11: T11 ): Unit - @JavaDefaultMethod def apply( t1: T1, t2: T2, @@ -268,7 +255,6 @@ trait JProcedure12[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12] t12: T12 ): Unit - @JavaDefaultMethod def apply( t1: T1, t2: T2, @@ -322,7 +308,6 @@ trait JProcedure13[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13] t13: T13 ): Unit - @JavaDefaultMethod def apply( t1: T1, t2: T2, @@ -379,7 +364,6 @@ trait JProcedure14[T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14] t14: T14 ): Unit - @JavaDefaultMethod def apply( t1: T1, t2: T2, @@ -454,7 +438,6 @@ trait JProcedure15[ t15: T15 ): Unit - @JavaDefaultMethod def apply( t1: T1, t2: T2, @@ -533,7 +516,6 @@ trait JProcedure16[ t16: T16 ): Unit - @JavaDefaultMethod def apply( t1: T1, t2: T2, @@ -633,7 +615,6 @@ trait JProcedure17[ t17: T17 ): Unit - @JavaDefaultMethod def apply( t1: T1, t2: T2, @@ -738,7 +719,6 @@ trait JProcedure18[ t18: T18 ): Unit - @JavaDefaultMethod def apply( t1: T1, t2: T2, @@ -848,7 +828,6 @@ trait JProcedure19[ t19: T19 ): Unit - @JavaDefaultMethod def apply( t1: T1, t2: T2, @@ -963,7 +942,6 @@ trait JProcedure20[ t20: T20 ): Unit - @JavaDefaultMethod def apply( t1: T1, t2: T2, @@ -1083,7 +1061,6 @@ trait JProcedure21[ t21: T21 ): Unit - @JavaDefaultMethod def apply( t1: T1, t2: T2, @@ -1208,7 +1185,6 @@ trait JProcedure22[ t22: T22 ): Unit - @JavaDefaultMethod def apply( t1: T1, t2: T2, diff --git a/auxlib/src/main/scala-3/scala/runtime/function/JProcedure.scala.gyb b/auxlib/src/main/scala-3/scala/runtime/function/JProcedure.scala.gyb index a3b69a6383..8c23b0aeed 100644 --- a/auxlib/src/main/scala-3/scala/runtime/function/JProcedure.scala.gyb +++ b/auxlib/src/main/scala-3/scala/runtime/function/JProcedure.scala.gyb @@ -9,13 +9,11 @@ package scala.runtime.function import scala.runtime.BoxedUnit -import scala.scalanative.annotation.JavaDefaultMethod trait JProcedure0 extends scala.Function0[Object] with java.io.Serializable { def applyVoid(): Unit - @JavaDefaultMethod def apply(): Object = { applyVoid() return BoxedUnit.UNIT @@ -32,7 +30,6 @@ trait JProcedure${N}[${TpsDecl}] with java.io.Serializable { def applyVoid(${args}): Unit - @JavaDefaultMethod def apply(${args}): Object = { applyVoid(${argNames}) return BoxedUnit.UNIT diff --git a/clib/src/main/resources/scala-native/locale.c b/clib/src/main/resources/scala-native/locale.c new file mode 100644 index 0000000000..65ff33c4c0 --- /dev/null +++ b/clib/src/main/resources/scala-native/locale.c @@ -0,0 +1,184 @@ +#ifdef _WIN32 +// No Windows support +#else +#if !(defined __STDC_VERSION__) || (__STDC_VERSION__ < 201112L) +#ifndef SCALANATIVE_SUPPRESS_STRUCT_CHECK_WARNING +#warning "Size and order of C structures are not checked when -std < c11." +#endif +#else // POSIX +#include +#include + +/* _Static_assert statements below verify that this layout is valid + * on Linux & mac_OS. Read the documentation but do not believe it too much. + * Trust the _Static_assert statements. + * + * This is the Linux physical layout. macOS swaps/exchanges the + * int_p_sep_by_space & int_n_cs_precedes fields. + * + * macOS "man localeconv" describes this layout. + * + * Linux "man lconv" describes a layout with int_curr_symbol after + * n_sign_position. _Static_assert below refutes that. + * + * POSIX 2018 describes its usual "which shall include at least the + * following members". As is its right, it then gives a list which + * has no correspondence this layout. + */ + +struct scalanative_lconv { + char *decimal_point; + char *thousands_sep; + char *grouping; + char *int_curr_symbol; + char *currency_symbol; + + char *mon_decimal_point; + char *mon_thousands_sep; + char *mon_grouping; + char *positive_sign; + char *negative_sign; + + char int_frac_digits; + char frac_digits; + char p_cs_precedes; + char p_sep_by_space; + char n_cs_precedes; + + char n_sep_by_space; + char p_sign_posn; + char n_sign_posn; + char int_p_cs_precedes; + char int_p_sep_by_space; // Linux, overlays macOS int_n_cs_precedes + + char int_n_cs_precedes; // Linux, overlays macOS int_p_sep_by_space + char int_n_sep_by_space; + char int_p_sign_posn; + char int_n_sign_posn; +}; + +_Static_assert(sizeof(struct scalanative_lconv) <= sizeof(struct lconv), + "Unexpected size: os lconv"); + +_Static_assert(offsetof(struct scalanative_lconv, decimal_point) == + offsetof(struct lconv, decimal_point), + "Unexpected offset: scalanative_lconv.decimal_point"); + +_Static_assert(offsetof(struct scalanative_lconv, thousands_sep) == + offsetof(struct lconv, thousands_sep), + "Unexpected offset: scalanative_lconv.thousands_sep"); + +_Static_assert(offsetof(struct scalanative_lconv, grouping) == + offsetof(struct lconv, grouping), + "Unexpected offset: scalanative_lconv.grouping"); + +_Static_assert(offsetof(struct scalanative_lconv, int_curr_symbol) == + offsetof(struct lconv, int_curr_symbol), + "Unexpected offset: scalanative_lconv.int_curr_symbol"); + +_Static_assert(offsetof(struct scalanative_lconv, currency_symbol) == + offsetof(struct lconv, currency_symbol), + "Unexpected offset: scalanative_lconv.currency_symbol"); + +_Static_assert(offsetof(struct scalanative_lconv, mon_decimal_point) == + offsetof(struct lconv, mon_decimal_point), + "Unexpected offset: scalanative_lconv.mon_decimal_point"); + +_Static_assert(offsetof(struct scalanative_lconv, mon_grouping) == + offsetof(struct lconv, mon_grouping), + "Unexpected offset: scalanative_lconv.mon_grouping"); + +_Static_assert(offsetof(struct scalanative_lconv, mon_thousands_sep) == + offsetof(struct lconv, mon_thousands_sep), + "Unexpected offset: scalanative_lconv.mon_thousands_sep"); + +_Static_assert(offsetof(struct scalanative_lconv, positive_sign) == + offsetof(struct lconv, positive_sign), + "Unexpected offset: scalanative_lconv.positive_sign"); + +_Static_assert(offsetof(struct scalanative_lconv, negative_sign) == + offsetof(struct lconv, negative_sign), + "Unexpected offset: scalanative_lconv.negative_sign"); + +_Static_assert(offsetof(struct scalanative_lconv, int_frac_digits) == + offsetof(struct lconv, int_frac_digits), + "Unexpected offset: scalanative_lconv.int_frac_digits"); + +_Static_assert(offsetof(struct scalanative_lconv, frac_digits) == + offsetof(struct lconv, frac_digits), + "Unexpected offset: scalanative_lconv,frac_digits"); + +_Static_assert(offsetof(struct scalanative_lconv, p_cs_precedes) == + offsetof(struct lconv, p_cs_precedes), + "Unexpected offset: scalanative_lconv.p_cs_precedes."); + +_Static_assert(offsetof(struct scalanative_lconv, p_sep_by_space) == + offsetof(struct lconv, p_sep_by_space), + "Unexpected offset: scalanative_lconv.p_sep_by_space"); + +_Static_assert(offsetof(struct scalanative_lconv, n_cs_precedes) == + offsetof(struct lconv, n_cs_precedes), + "Unexpected offset: scalanative_lconv.n_cs_precedes"); + +_Static_assert(offsetof(struct scalanative_lconv, n_sep_by_space) == + offsetof(struct lconv, n_sep_by_space), + "Unexpected offset: scalanative_lconv.n_sep_by_space"); + +_Static_assert(offsetof(struct scalanative_lconv, p_sign_posn) == + offsetof(struct lconv, p_sign_posn), + "Unexpected offset: scalanative_lconv.p_sign_posn"); + +_Static_assert(offsetof(struct scalanative_lconv, n_sign_posn) == + offsetof(struct lconv, n_sign_posn), + "Unexpected offset: scalanative_lconv.n_sign_posn"); + +_Static_assert(offsetof(struct scalanative_lconv, int_p_cs_precedes) == + offsetof(struct lconv, int_p_cs_precedes), + "Unexpected offset: scalanative_lconv.int_p_cs_precedes"); + +#ifdef __linux__ +_Static_assert(offsetof(struct scalanative_lconv, int_n_cs_precedes) == + offsetof(struct lconv, int_n_cs_precedes), + "Unexpected offset: scalanative_lconv.int_n_cs_precedes"); +_Static_assert(offsetof(struct scalanative_lconv, int_p_sep_by_space) == + offsetof(struct lconv, int_p_sep_by_space), + "Unexpected offset: scalanative_lconv.int_p_sep_by_space"); +#else // __APPLE__, etc. +// Be aware of the trickery with field names being swapped/exchanged. +_Static_assert(offsetof(struct scalanative_lconv, int_n_cs_precedes) == + offsetof(struct lconv, int_p_sep_by_space), + "Unexpected offset: scalanative_lconv.int_p_sep_by_space"); + +_Static_assert(offsetof(struct scalanative_lconv, int_p_sep_by_space) == + offsetof(struct lconv, int_n_cs_precedes), + "Unexpected offset: scalanative_lconv.int_n_cs_precedes"); +#endif // __APPLE__ + +_Static_assert(offsetof(struct scalanative_lconv, int_n_sep_by_space) == + offsetof(struct lconv, int_n_sep_by_space), + "Unexpected offset: scalanative_lconv.int_n_sep_by_space"); + +_Static_assert(offsetof(struct scalanative_lconv, int_p_sign_posn) == + offsetof(struct lconv, int_p_sign_posn), + "Unexpected offset: scalanative_lconv.int_p_sign_posn"); + +_Static_assert(offsetof(struct scalanative_lconv, int_n_sign_posn) == + offsetof(struct lconv, int_n_sign_posn), + "Unexpected offset: scalanative_lconv.int_n_sign_posn"); + +// Symbolic constants + +int scalanative_lc_all() { return LC_ALL; } + +int scalanative_lc_collate() { return LC_COLLATE; } + +int scalanative_lc_ctype() { return LC_CTYPE; } + +int scalanative_lc_monetary() { return LC_MONETARY; } + +int scalanative_lc_numeric() { return LC_NUMERIC; } + +int scalanative_lc_time() { return LC_TIME; } + +#endif // POSIX +#endif // ! _WIN32 diff --git a/clib/src/main/scala/scala/scalanative/libc/locale.scala b/clib/src/main/scala/scala/scalanative/libc/locale.scala new file mode 100644 index 0000000000..588c03dfed --- /dev/null +++ b/clib/src/main/scala/scala/scalanative/libc/locale.scala @@ -0,0 +1,157 @@ +package scala.scalanative +package libc + +import scalanative.unsafe._ +import scalanative.meta.LinktimeInfo.isLinux + +/** ISO/IEC C definitions for locale.h + * + * See https://en.cppreference.com/w/c/numeric/locale + */ +@extern object locale { + // CStruct is limited to 22 fields, lconv wants 24, so group int_* & use Ops + + /* Be careful here! + * This is the Linux layout. localeOps handles the fact that macOS + * swaps/echanges the int_p_sep_by_space & int_n_cs_precedes fields. + */ + + type lconv = CStruct19[ + CString, // decimal_point + CString, // thousands_sep + CString, // grouping + CString, // int_curr_symbol + CString, // currency_symbol + + CString, // mon_decimal_point + CString, // mon_thousands_sep + CString, // mon_grouping + CString, // positive_sign + CString, // negative_sign + + Byte, // int_frac_digits + Byte, // frac_digits + Byte, // p_cs_precedes + Byte, // p_sep_by_space + Byte, // n_cs_precedes + + Byte, // n_sep_by_space + Byte, // p_sign_posn + Byte, // n_sign_posn + + CStruct6[ + Byte, // int_p_cs_precedes + Byte, // Linux int_p_sep_by_space, macOS int_n_cs_precedes + Byte, // Linux int_n_cs_precedes, macOS int_p_sep_by_space + Byte, // int_n_sep_by_space + Byte, // int_p_sign_posn + Byte // int_n_sign_posn + ] + ] + + // Macros + + @name("scalanative_lc_all") + def LC_ALL: CInt = extern + + @name("scalanative_lc_collate") + def LC_COLLATE: CInt = extern + + @name("scalanative_lc_ctype") + def LC_CTYPE: CInt = extern + + @name("scalanative_lc_monetary") + def LC_MONETARY: CInt = extern + + @name("scalanative_lc_numeric") + def LC_NUMERIC: CInt = extern + + @name("scalanative_lc_time") + def LC_TIME: CInt = extern + +// Methods + + def localeconv(): Ptr[lconv] = extern + + def setlocale(category: CInt, locale: CString): CString = extern +} + +object localeOpsImpl { + import locale.lconv + def decimal_point(ptr: Ptr[lconv]): CString = ptr._1 + def thousands_sep(ptr: Ptr[lconv]): CString = ptr._2 + def grouping(ptr: Ptr[lconv]): CString = ptr._3 + def int_curr_symbol(ptr: Ptr[lconv]): CString = ptr._4 + def currency_symbol(ptr: Ptr[lconv]): CString = ptr._5 + + def mon_decimal_point(ptr: Ptr[lconv]): CString = ptr._6 + def mon_thousands_sep(ptr: Ptr[lconv]): CString = ptr._7 + def mon_grouping(ptr: Ptr[lconv]): CString = ptr._8 + def positive_sign(ptr: Ptr[lconv]): CString = ptr._9 + def negative_sign(ptr: Ptr[lconv]): CString = ptr._10 + + def int_frac_digits(ptr: Ptr[lconv]): CChar = ptr._11 + def frac_digits(ptr: Ptr[lconv]): CChar = ptr._12 + def p_cs_precedes(ptr: Ptr[lconv]): CChar = ptr._13 + def p_sep_by_space(ptr: Ptr[lconv]): CChar = ptr._14 + def n_cs_precedes(ptr: Ptr[lconv]): CChar = ptr._15 + + def n_sep_by_space(ptr: Ptr[lconv]): CChar = ptr._16 + def p_sign_posn(ptr: Ptr[lconv]): CChar = ptr._17 + def n_sign_posn(ptr: Ptr[lconv]): CChar = ptr._18 + def int_p_cs_precedes(ptr: Ptr[lconv]): CChar = ptr._19._1 + def int_p_sep_by_space(ptr: Ptr[lconv]): CChar = + if (isLinux) ptr._19._2 + else ptr._19._3 // macOS & probably BSDs + + def int_n_cs_precedes(ptr: Ptr[lconv]): CChar = + if (isLinux) ptr._19._3 + else ptr._19._2 // macOS & probably BSDs + + def int_n_sep_by_space(ptr: Ptr[lconv]): CChar = ptr._19._4 + def int_p_sign_posn(ptr: Ptr[lconv]): CChar = ptr._19._5 + def int_n_sign_posn(ptr: Ptr[lconv]): CChar = ptr._19._6 + + /* Linux 'man localeconv' documents lconv not to be modified, + * so no corresponding 'set' Ops. + */ +} + +object localeOps { + import locale.lconv + + implicit class lconvOps(val ptr: Ptr[lconv]) extends AnyVal { + def decimal_point: CString = localeOpsImpl.decimal_point(ptr) + def thousands_sep: CString = localeOpsImpl.thousands_sep(ptr) + def grouping: CString = localeOpsImpl.grouping(ptr) + def int_curr_symbol: CString = localeOpsImpl.int_curr_symbol(ptr) + def currency_symbol: CString = localeOpsImpl.currency_symbol(ptr) + + def mon_decimal_point: CString = localeOpsImpl.mon_decimal_point(ptr) + def mon_thousands_sep: CString = localeOpsImpl.mon_thousands_sep(ptr) + def mon_grouping: CString = localeOpsImpl.mon_grouping(ptr) + def positive_sign: CString = localeOpsImpl.positive_sign(ptr) + def negative_sign: CString = localeOpsImpl.negative_sign(ptr) + + def int_frac_digits: CChar = localeOpsImpl.int_frac_digits(ptr) + def frac_digits: CChar = localeOpsImpl.frac_digits(ptr) + + def p_cs_precedes: CChar = localeOpsImpl.p_cs_precedes(ptr) + def p_sep_by_space: CChar = localeOpsImpl.p_sep_by_space(ptr) + def n_cs_precedes: CChar = localeOpsImpl.n_cs_precedes(ptr) + def n_sep_by_space: CChar = localeOpsImpl.n_sep_by_space(ptr) + def p_sign_posn: CChar = localeOpsImpl.p_sign_posn(ptr) + def n_sign_posn: CChar = localeOpsImpl.n_sign_posn(ptr) + + def int_p_cs_precedes: CChar = localeOpsImpl.int_p_cs_precedes(ptr) + def int_n_cs_precedes: CChar = localeOpsImpl.int_n_cs_precedes(ptr) + def int_p_sep_by_space: CChar = localeOpsImpl.int_p_sep_by_space(ptr) + def int_n_sep_by_space: CChar = localeOpsImpl.int_n_sep_by_space(ptr) + def int_p_sign_posn: CChar = localeOpsImpl.int_p_sign_posn(ptr) + def int_n_sign_posn: CChar = localeOpsImpl.int_n_sign_posn(ptr) + + /* Linux 'man localeconv' documents lconv not to be modified, + * so no corresponding 'set' Ops. + */ + } +} diff --git a/docs/changelog/0.4.10.md b/docs/changelog/0.4.10.md new file mode 100644 index 0000000000..3afdf30b3e --- /dev/null +++ b/docs/changelog/0.4.10.md @@ -0,0 +1,173 @@ + +# 0.4.10 (2023-01-27) + +We're happy to announce the release of Scala Native. + +Scala Native 0.4.10 adds support for Scala 3.2.2 with its new lazy vals implementation, and drops the support for Scala 2.11 which has been EOL for over 5 years. +The latest release also contains multiple bug fixes and improvements, including a new release mode `scala.scalanative.build.Mode.releaseSize` oriented for the size of the produced binaries. + +Scala standard library used by this release is based on the following versions: + + + + + + + + + + + + + + + + + + + +
Scala binary versionScala release
2.122.12.17
2.132.13.10
33.2.2
+ + + + + + + + + + + + +
Merged PRs52
Contributors7
+ +## Contributors + +Big thanks to everybody who contributed to this release or reported an issue! + +``` +$ git shortlog -sn --no-merges v0.4.9..v0.4.10 + 23 LeeTibbert + 9 Wojciech Mazur + 7 110416 + 6 Arman Bilge + 3 Eric K Richardson + 1 Hossein Naderi + 1 Dong Nguyen +``` + +## Merged PRs + +## [v0.4.10](https://github.com/scala-native/scala-native/tree/) (2023-01-27) + +[Full Changelog](https://github.com/scala-native/scala-native/compare/v0.4.9..v0.4.10) + +**Merged pull requests:** + +## Supported Scala versions +- Drop Scala 2.11 + [\#3028](https://github.com/scala-native/scala-native/pull/3028) + ([ekrich](https://github.com/ekrich)) +- Support Scala 3.2.2 + [\#3094](https://github.com/scala-native/scala-native/pull/3094) + ([WojciechMazur](https://github.com/WojciechMazur)) + +## Java Standard Library +- Better error handling for `Files#createLink` + [\#3012](https://github.com/scala-native/scala-native/pull/3012) + ([armanbilge](https://github.com/armanbilge)) + [\#3015](https://github.com/scala-native/scala-native/pull/3015) +- Fix #2755: j.nio.Files#readAllBytes reports failed Unix file open call + [\#3026](https://github.com/scala-native/scala-native/pull/3026) + ([LeeTibbert](https://github.com/LeeTibbert)) +- Fix #2973: Implement some requested j.io.InputStream Java 9 &11 methods + [\#3031](https://github.com/scala-native/scala-native/pull/3031) + ([LeeTibbert](https://github.com/LeeTibbert)) +- Make initialization of `System.properties` a lazy operation + [\#3061](https://github.com/scala-native/scala-native/pull/3061) + ([WojciechMazur](https://github.com/WojciechMazur)) +- Add `java.util.concurrent.Flow` + [\#3099](https://github.com/scala-native/scala-native/pull/3099) + ([armanbilge](https://github.com/armanbilge)) +- Port `java.util.TreeMap` and friends + [\#3102](https://github.com/scala-native/scala-native/pull/3102) + ([armanbilge](https://github.com/armanbilge)) +- Add `java.nio.file.Path.of` methods + [\#3083](https://github.com/scala-native/scala-native/pull/3083) + ([i10416](https://github.com/i10416)) +- Generate scaladoc for javalib in Scala 2 + [\#3035](https://github.com/scala-native/scala-native/pull/3035) + ([ekrich](https://github.com/ekrich)) + +## POSIX bindings +- Add POSIX sys/un bindings + [\#3025](https://github.com/scala-native/scala-native/pull/3025) + ([LeeTibbert](https://github.com/LeeTibbert)) +- Add POSIX sys/times bindings + [\#3032](https://github.com/scala-native/scala-native/pull/3032) + ([LeeTibbert](https://github.com/LeeTibbert)) +- Add POSIX bindings for glob, fnmatch and libgen + [\#3041](https://github.com/scala-native/scala-native/pull/3041) + ([LeeTibbert](https://github.com/LeeTibbert)) +- Add POSIX wordexp bindings +- [\#3042](https://github.com/scala-native/scala-native/pull/3042) + ([LeeTibbert](https://github.com/LeeTibbert)) +- Add subset of C and POSIX locale related method + [\#3034](https://github.com/scala-native/scala-native/pull/3034) + ([LeeTibbert](https://github.com/LeeTibbert)) +- Add POSIX langinfo.h and nl_types.h bindings + [\#3044](https://github.com/scala-native/scala-native/pull/3044) + ([LeeTibbert](https://github.com/LeeTibbert)) +- Reduce memory usage in posixlib spawn + [\#3040](https://github.com/scala-native/scala-native/pull/3040) + ([LeeTibbert](https://github.com/LeeTibbert)) + +## Compiler plugin +- Improve: report error on extern in val def + [\#3033](https://github.com/scala-native/scala-native/pull/3033) + ([i10416](https://github.com/i10416)) +- Report error on default arguments in extern method + [\#3045](https://github.com/scala-native/scala-native/pull/3045) + ([i10416](https://github.com/i10416)) +- Fix issue with using opaque types in `CFuncPtr` + [\#3096](https://github.com/scala-native/scala-native/pull/3096) + ([WojciechMazur](https://github.com/WojciechMazur)) +- Fix `NullPointerExceptions` when writing NIR to virtual files + [\#3108](https://github.com/scala-native/scala-native/pull/3108) + ([WojciechMazur](https://github.com/WojciechMazur)) +- Relativize paths in NIR using `mapSourceURI` setting in compiler plugin + [\#3109](https://github.com/scala-native/scala-native/pull/3109) + ([WojciechMazur](https://github.com/WojciechMazur)) + +## JUnit runtime +- Allow to use inherited `org.junit.{Aftter,Before}Class` methods + [\#3055](https://github.com/scala-native/scala-native/pull/3055) + ([WojciechMazur](https://github.com/WojciechMazur)) + +## Build toolchain +- Handle whitespaces in the files passed to the clang when linking + [\#3062](https://github.com/scala-native/scala-native/pull/3062) + ([WojciechMazur](https://github.com/WojciechMazur)) +- Fix #3078, allow methods to have more then 10k instructions + [\#3095](https://github.com/scala-native/scala-native/pull/3095) + ([WojciechMazur](https://github.com/WojciechMazur)) +- Introduce `scalanative.build.Mode.ReleaseSize` + [\#3091](https://github.com/scala-native/scala-native/pull/3091) + ([dongnguyenvt](https://github.com/dongnguyenvt)) + +## sbt plugin +- Add `NativeTags.Link` to limit concurrency of `nativeLink` + [\#3064](https://github.com/scala-native/scala-native/pull/3064) + ([armanbilge](https://github.com/armanbilge)) + +## Other bugfixes +- Fix #3065: FreeBSD once again compiles + [\#3077](https://github.com/scala-native/scala-native/pull/3077) + ([LeeTibbert](https://github.com/LeeTibbert)) +- Fix #3069: Commix gc runs once again on FreeBSD + [\#3079](https://github.com/scala-native/scala-native/pull/3079) + ([LeeTibbert](https://github.com/LeeTibbert)) +- Fix #3073: ProcessMonitor semaphore now works on FreeBSD + [\#3080](https://github.com/scala-native/scala-native/pull/3080) + ([LeeTibbert](https://github.com/LeeTibbert)) + diff --git a/docs/changelog/0.4.9.md b/docs/changelog/0.4.9.md index 98247011c0..6cedbc1e8b 100644 --- a/docs/changelog/0.4.9.md +++ b/docs/changelog/0.4.9.md @@ -1,11 +1,11 @@ -# 0.4.9 (2022-12-23) +# 0.4.9 (2022-11-23) -We're happy to announce the release of Scala Native 0.4.9. +We're happy to announce the release of Scala Native 0.4.9. It's a patch release fixing linkage errors when building Scala 2.13 libraries using Scala 3 dependenices. -It does also reverse version policy changes leading to problems in sbt-crossproject. Improved version policy would be restored in Scala Native 0.5.x. +It does also reverse version policy changes leading to problems in sbt-crossproject. Improved version policy would be restored in Scala Native 0.5.x. Scala Native 0.4.9 introduces a new feature - an experimental support for incremental compilation. @@ -67,7 +67,6 @@ nativeConfig ~= { } ``` - ## Contributors Big thanks to everybody who contributed to this release or reported an issue! @@ -82,7 +81,7 @@ $ git shortlog -sn --no-merges v0.4.8..v0.4.9 ## Merged PRs -## [v0.4.9](https://github.com/scala-native/scala-native/tree/v0.4.9) (2022-12-23) +## [v0.4.9](https://github.com/scala-native/scala-native/tree/v0.4.9) (2022-11-23) [Full Changelog](https://github.com/scala-native/scala-native/compare/v0.4.8...v0.4.9) diff --git a/docs/changelog/index.rst b/docs/changelog/index.rst index e8c43e9d2e..d2d67953a3 100644 --- a/docs/changelog/index.rst +++ b/docs/changelog/index.rst @@ -6,6 +6,7 @@ Changelog .. toctree:: :maxdepth: 1 + 0.4.10 0.4.9 0.4.8 0.4.7 diff --git a/docs/conf.py b/docs/conf.py index 1d2cfa0ebe..31ac2ffa8b 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -69,9 +69,9 @@ def generateScalaNativeCurrentYear(): # built documents. # # The short X.Y version. -version = u'0.4.9' +version = u'0.4.10' # The full version, including alpha/beta/rc tags. -release = u'0.4.9' +release = u'0.4.10' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/docs/contrib/build.rst b/docs/contrib/build.rst index ad5a175493..21d2097b68 100644 --- a/docs/contrib/build.rst +++ b/docs/contrib/build.rst @@ -101,7 +101,7 @@ The `nativeMode` setting is controlled via the `SCALANATIVE_MODE` environment variable. The default mode, `debug` is designed to optimize but compile fast whereas the `release` mode performs additional optimizations and takes longer to compile. The `release-fast` mode builds faster, performs less optimizations, -but may perform better than `release`. +but may perform better than `release`. The `release-size` mode optimizes for reduced size. The `optimize` setting is controlled via the `SCALANATIVE_OPTIMIZE` environment variable. Valid values are `true` and `false`. The default value is `true`. diff --git a/docs/lib/javalib.rst b/docs/lib/javalib.rst index 00301c8579..f804049147 100644 --- a/docs/lib/javalib.rst +++ b/docs/lib/javalib.rst @@ -8,528 +8,512 @@ Scala Native supports a subset of the JDK core libraries reimplemented in Scala. Supported classes ----------------- -Here is the list of currently available classes: - -* ``java.io.BufferedInputStream`` -* ``java.io.BufferedOutputStream`` -* ``java.io.BufferedReader`` -* ``java.io.BufferedWriter`` -* ``java.io.ByteArrayInputStream`` -* ``java.io.ByteArrayOutputStream`` -* ``java.io.Closeable`` -* ``java.io.DataInput`` -* ``java.io.DataInputStream`` -* ``java.io.DataOutput`` -* ``java.io.DataOutputStream`` -* ``java.io.EOFException`` -* ``java.io.File`` -* ``java.io.FileDescriptor`` -* ``java.io.FileFilter`` -* ``java.io.FileInputStream`` -* ``java.io.FileNotFoundException`` -* ``java.io.FileOutputStream`` -* ``java.io.FileReader`` -* ``java.io.FileWriter`` -* ``java.io.FilenameFilter`` -* ``java.io.FilterInputStream`` -* ``java.io.FilterOutputStream`` -* ``java.io.FilterReader`` -* ``java.io.Flushable`` -* ``java.io.IOException`` -* ``java.io.InputStream`` -* ``java.io.InputStreamReader`` -* ``java.io.InterruptedIOException`` -* ``java.io.LineNumberReader`` -* ``java.io.NotSerializableException`` -* ``java.io.ObjectStreamException`` -* ``java.io.OutputStream`` -* ``java.io.OutputStreamWriter`` -* ``java.io.PrintStream`` -* ``java.io.PrintWriter`` -* ``java.io.PushbackInputStream`` -* ``java.io.PushbackReader`` -* ``java.io.RandomAccessFile`` -* ``java.io.Reader`` -* ``java.io.Serializable`` -* ``java.io.StringReader`` -* ``java.io.StringWriter`` -* ``java.io.SyncFailedException`` -* ``java.io.UTFDataFormatException`` -* ``java.io.UncheckedIOException`` -* ``java.io.UnsupportedEncodingException`` -* ``java.io.Writer`` -* ``java.lang.AbstractMethodError`` -* ``java.lang.AbstractStringBuilder`` -* ``java.lang.Appendable`` -* ``java.lang.ArithmeticException`` -* ``java.lang.ArrayIndexOutOfBoundsException`` -* ``java.lang.ArrayStoreException`` -* ``java.lang.AssertionError`` -* ``java.lang.AutoCloseable`` -* ``java.lang.Boolean`` -* ``java.lang.BootstrapMethodError`` -* ``java.lang.Byte`` -* ``java.lang.ByteCache`` -* ``java.lang.CharSequence`` -* ``java.lang.Character`` -* ``java.lang.Character$Subset`` -* ``java.lang.Character$UnicodeBlock`` -* ``java.lang.CharacterCache`` -* ``java.lang.ClassCastException`` -* ``java.lang.ClassCircularityError`` -* ``java.lang.ClassFormatError`` -* ``java.lang.ClassLoader`` -* ``java.lang.ClassNotFoundException`` -* ``java.lang.CloneNotSupportedException`` -* ``java.lang.Cloneable`` -* ``java.lang.Comparable`` -* ``java.lang.Double`` -* ``java.lang.Enum`` -* ``java.lang.EnumConstantNotPresentException`` -* ``java.lang.Error`` -* ``java.lang.Exception`` -* ``java.lang.ExceptionInInitializerError`` -* ``java.lang.Float`` -* ``java.lang.IllegalAccessError`` -* ``java.lang.IllegalAccessException`` -* ``java.lang.IllegalArgumentException`` -* ``java.lang.IllegalMonitorStateException`` -* ``java.lang.IllegalStateException`` -* ``java.lang.IllegalThreadStateException`` -* ``java.lang.IncompatibleClassChangeError`` -* ``java.lang.IndexOutOfBoundsException`` -* ``java.lang.InheritableThreadLocal`` -* ``java.lang.InstantiationError`` -* ``java.lang.InstantiationException`` -* ``java.lang.Integer`` -* ``java.lang.IntegerCache`` -* ``java.lang.IntegerDecimalScale`` -* ``java.lang.InternalError`` -* ``java.lang.InterruptedException`` -* ``java.lang.Iterable`` -* ``java.lang.LinkageError`` -* ``java.lang.Long`` -* ``java.lang.LongCache`` -* ``java.lang.Math`` -* ``java.lang.MathRand`` -* ``java.lang.NegativeArraySizeException`` -* ``java.lang.NoClassDefFoundError`` -* ``java.lang.NoSuchFieldError`` -* ``java.lang.NoSuchFieldException`` -* ``java.lang.NoSuchMethodError`` -* ``java.lang.NoSuchMethodException`` -* ``java.lang.NullPointerException`` -* ``java.lang.Number`` -* ``java.lang.NumberFormatException`` -* ``java.lang.OutOfMemoryError`` -* ``java.lang.Process`` -* ``java.lang.ProcessBuilder`` -* ``java.lang.ProcessBuilder$Redirect`` -* ``java.lang.ProcessBuilder$Redirect$Type`` -* ``java.lang.Readable`` -* ``java.lang.ReflectiveOperationException`` -* ``java.lang.RejectedExecutionException`` -* ``java.lang.Runnable`` -* ``java.lang.Runtime`` -* ``java.lang.Runtime$ProcessBuilderOps`` -* ``java.lang.RuntimeException`` -* ``java.lang.SecurityException`` -* ``java.lang.Short`` -* ``java.lang.ShortCache`` -* ``java.lang.StackOverflowError`` -* ``java.lang.StackTrace`` -* ``java.lang.StackTraceElement`` -* ``java.lang.StackTraceElement$Fail`` -* ``java.lang.String`` -* ``java.lang.StringBuffer`` -* ``java.lang.StringBuilder`` -* ``java.lang.StringIndexOutOfBoundsException`` -* ``java.lang.System`` -* ``java.lang.Thread`` -* ``java.lang.Thread$UncaughtExceptionHandler`` -* ``java.lang.ThreadDeath`` -* ``java.lang.ThreadLocal`` -* ``java.lang.Throwable`` -* ``java.lang.TypeNotPresentException`` -* ``java.lang.UnknownError`` -* ``java.lang.UnsatisfiedLinkError`` -* ``java.lang.UnsupportedClassVersionError`` -* ``java.lang.UnsupportedOperationException`` -* ``java.lang.VerifyError`` -* ``java.lang.VirtualMachineError`` -* ``java.lang.Void`` -* ``java.lang.annotation.Annotation`` -* ``java.lang.annotation.Retention`` -* ``java.lang.annotation.RetentionPolicy`` -* ``java.lang.constant.Constable`` -* ``java.lang.constant.ConstantDesc`` -* ``java.lang.ref.PhantomReference`` -* ``java.lang.ref.Reference`` -* ``java.lang.ref.ReferenceQueue`` -* ``java.lang.ref.SoftReference`` -* ``java.lang.ref.WeakReference`` -* ``java.lang.reflect.AccessibleObject`` -* ``java.lang.reflect.Array`` -* ``java.lang.reflect.Constructor`` -* ``java.lang.reflect.Executable`` -* ``java.lang.reflect.Field`` -* ``java.lang.reflect.InvocationTargetException`` -* ``java.lang.reflect.Method`` -* ``java.lang.reflect.UndeclaredThrowableException`` -* ``java.math.BigDecimal`` -* ``java.math.BigInteger`` -* ``java.math.BitLevel`` -* ``java.math.Conversion`` -* ``java.math.Division`` -* ``java.math.Elementary`` -* ``java.math.Logical`` -* ``java.math.MathContext`` -* ``java.math.Multiplication`` -* ``java.math.Primality`` -* ``java.math.RoundingMode`` -* ``java.net.BindException`` -* ``java.net.ConnectException`` -* ``java.net.Inet4Address`` -* ``java.net.Inet6Address`` -* ``java.net.InetAddress`` -* ``java.net.InetAddressBase`` -* ``java.net.InetSocketAddress`` -* ``java.net.MalformedURLException`` -* ``java.net.NoRouteToHostException`` -* ``java.net.PortUnreachableException`` -* ``java.net.ServerSocket`` -* ``java.net.Socket`` -* ``java.net.SocketAddress`` -* ``java.net.SocketException`` -* ``java.net.SocketImpl`` -* ``java.net.SocketInputStream`` -* ``java.net.SocketOption`` -* ``java.net.SocketOptions`` -* ``java.net.SocketOutputStream`` -* ``java.net.SocketTimeoutException`` -* ``java.net.URI`` -* ``java.net.URI$Helper`` -* ``java.net.URIEncoderDecoder`` -* ``java.net.URISyntaxException`` -* ``java.net.URL`` -* ``java.net.URLClassLoader`` -* ``java.net.URLConnection`` -* ``java.net.URLDecoder`` -* ``java.net.URLEncoder`` -* ``java.net.UnknownHostException`` -* ``java.net.UnknownServiceException`` -* ``java.nio.Buffer`` -* ``java.nio.BufferOverflowException`` -* ``java.nio.BufferUnderflowException`` -* ``java.nio.ByteBuffer`` -* ``java.nio.ByteOrder`` -* ``java.nio.CharBuffer`` -* ``java.nio.DoubleBuffer`` -* ``java.nio.FloatBuffer`` -* ``java.nio.IntBuffer`` -* ``java.nio.InvalidMarkException`` -* ``java.nio.LongBuffer`` -* ``java.nio.MappedByteBuffer`` -* ``java.nio.ReadOnlyBufferException`` -* ``java.nio.ShortBuffer`` -* ``java.nio.channels.ByteChannel`` -* ``java.nio.channels.Channel`` -* ``java.nio.channels.Channels`` -* ``java.nio.channels.ClosedChannelException`` -* ``java.nio.channels.FileChannel`` -* ``java.nio.channels.FileChannel$MapMode`` -* ``java.nio.channels.FileLock`` -* ``java.nio.channels.GatheringByteChannel`` -* ``java.nio.channels.InterruptibleChannel`` -* ``java.nio.channels.NonReadableChannelException`` -* ``java.nio.channels.NonWritableChannelException`` -* ``java.nio.channels.OverlappingFileLockException`` -* ``java.nio.channels.ReadableByteChannel`` -* ``java.nio.channels.ScatteringByteChannel`` -* ``java.nio.channels.SeekableByteChannel`` -* ``java.nio.channels.WritableByteChannel`` -* ``java.nio.channels.spi.AbstractInterruptibleChannel`` -* ``java.nio.charset.CharacterCodingException`` -* ``java.nio.charset.Charset`` -* ``java.nio.charset.CharsetDecoder`` -* ``java.nio.charset.CharsetEncoder`` -* ``java.nio.charset.CoderMalfunctionError`` -* ``java.nio.charset.CoderResult`` -* ``java.nio.charset.CodingErrorAction`` -* ``java.nio.charset.IllegalCharsetNameException`` -* ``java.nio.charset.MalformedInputException`` -* ``java.nio.charset.StandardCharsets`` -* ``java.nio.charset.UnmappableCharacterException`` -* ``java.nio.charset.UnsupportedCharsetException`` -* ``java.nio.file.AccessDeniedException`` -* ``java.nio.file.CopyOption`` -* ``java.nio.file.DirectoryIteratorException`` -* ``java.nio.file.DirectoryNotEmptyException`` -* ``java.nio.file.DirectoryStream`` -* ``java.nio.file.DirectoryStream$Filter`` -* ``java.nio.file.DirectoryStreamImpl`` -* ``java.nio.file.FileAlreadyExistsException`` -* ``java.nio.file.FileSystem`` -* ``java.nio.file.FileSystemException`` -* ``java.nio.file.FileSystemLoopException`` -* ``java.nio.file.FileSystemNotFoundException`` -* ``java.nio.file.FileSystems`` -* ``java.nio.file.FileVisitOption`` -* ``java.nio.file.FileVisitResult`` -* ``java.nio.file.FileVisitor`` -* ``java.nio.file.Files`` -* ``java.nio.file.Files$TerminateTraversalException`` -* ``java.nio.file.InvalidPathException`` -* ``java.nio.file.LinkOption`` -* ``java.nio.file.NoSuchFileException`` -* ``java.nio.file.NotDirectoryException`` -* ``java.nio.file.NotLinkException`` -* ``java.nio.file.OpenOption`` -* ``java.nio.file.Path`` -* ``java.nio.file.PathMatcher`` -* ``java.nio.file.Paths`` -* ``java.nio.file.RegexPathMatcher`` -* ``java.nio.file.SimpleFileVisitor`` -* ``java.nio.file.StandardCopyOption`` -* ``java.nio.file.StandardOpenOption`` -* ``java.nio.file.StandardWatchEventKinds`` -* ``java.nio.file.WatchEvent`` -* ``java.nio.file.WatchEvent$Kind`` -* ``java.nio.file.WatchEvent$Modifier`` -* ``java.nio.file.WatchKey`` -* ``java.nio.file.WatchService`` -* ``java.nio.file.Watchable`` -* ``java.nio.file.attribute.AclEntry`` -* ``java.nio.file.attribute.AclFileAttributeView`` -* ``java.nio.file.attribute.AttributeView`` -* ``java.nio.file.attribute.BasicFileAttributeView`` -* ``java.nio.file.attribute.BasicFileAttributes`` -* ``java.nio.file.attribute.DosFileAttributeView`` -* ``java.nio.file.attribute.DosFileAttributes`` -* ``java.nio.file.attribute.FileAttribute`` -* ``java.nio.file.attribute.FileAttributeView`` -* ``java.nio.file.attribute.FileOwnerAttributeView`` -* ``java.nio.file.attribute.FileStoreAttributeView`` -* ``java.nio.file.attribute.FileTime`` -* ``java.nio.file.attribute.GroupPrincipal`` -* ``java.nio.file.attribute.PosixFileAttributeView`` -* ``java.nio.file.attribute.PosixFileAttributes`` -* ``java.nio.file.attribute.PosixFilePermission`` -* ``java.nio.file.attribute.PosixFilePermissions`` -* ``java.nio.file.attribute.UserDefinedFileAttributeView`` -* ``java.nio.file.attribute.UserPrincipal`` -* ``java.nio.file.attribute.UserPrincipalLookupService`` -* ``java.nio.file.attribute.UserPrincipalNotFoundException`` -* ``java.nio.file.spi.FileSystemProvider`` -* ``java.rmi.Remote`` -* ``java.rmi.RemoteException`` -* ``java.security.AccessControlException`` -* ``java.security.CodeSigner`` -* ``java.security.DummyMessageDigest`` -* ``java.security.GeneralSecurityException`` -* ``java.security.MessageDigest`` -* ``java.security.MessageDigestSpi`` -* ``java.security.NoSuchAlgorithmException`` -* ``java.security.Principal`` -* ``java.security.Timestamp`` -* ``java.security.TimestampConstructorHelper`` -* ``java.security.cert.CertPath`` -* ``java.security.cert.Certificate`` -* ``java.security.cert.CertificateEncodingException`` -* ``java.security.cert.CertificateException`` -* ``java.security.cert.CertificateFactory`` -* ``java.security.cert.X509Certificate`` -* ``java.security.cert.X509Extension`` -* ``java.util.AbstractCollection`` -* ``java.util.AbstractList`` -* ``java.util.AbstractListView`` -* ``java.util.AbstractMap`` -* ``java.util.AbstractMap$SimpleEntry`` -* ``java.util.AbstractMap$SimpleImmutableEntry`` -* ``java.util.AbstractQueue`` -* ``java.util.AbstractRandomAccessListIterator`` -* ``java.util.AbstractSequentialList`` -* ``java.util.AbstractSet`` -* ``java.util.ArrayDeque`` -* ``java.util.ArrayList`` -* ``java.util.Arrays`` -* ``java.util.Arrays$AsRef`` -* ``java.util.BackedUpListIterator`` -* ``java.util.Base64`` -* ``java.util.Base64$Decoder`` -* ``java.util.Base64$DecodingInputStream`` -* ``java.util.Base64$Encoder`` -* ``java.util.Base64$EncodingOutputStream`` -* ``java.util.Base64$Wrapper`` -* ``java.util.BitSet`` -* ``java.util.Calendar`` -* ``java.util.Collection`` -* ``java.util.Collections`` -* ``java.util.Collections$CheckedCollection`` -* ``java.util.Collections$CheckedList`` -* ``java.util.Collections$CheckedListIterator`` -* ``java.util.Collections$CheckedMap`` -* ``java.util.Collections$CheckedSet`` -* ``java.util.Collections$CheckedSortedMap`` -* ``java.util.Collections$CheckedSortedSet`` -* ``java.util.Collections$EmptyIterator`` -* ``java.util.Collections$EmptyListIterator`` -* ``java.util.Collections$ImmutableList`` -* ``java.util.Collections$ImmutableMap`` -* ``java.util.Collections$ImmutableSet`` -* ``java.util.Collections$UnmodifiableCollection`` -* ``java.util.Collections$UnmodifiableIterator`` -* ``java.util.Collections$UnmodifiableList`` -* ``java.util.Collections$UnmodifiableListIterator`` -* ``java.util.Collections$UnmodifiableMap`` -* ``java.util.Collections$UnmodifiableSet`` -* ``java.util.Collections$UnmodifiableSortedMap`` -* ``java.util.Collections$UnmodifiableSortedSet`` -* ``java.util.Collections$WrappedCollection`` -* ``java.util.Collections$WrappedEquals`` -* ``java.util.Collections$WrappedIterator`` -* ``java.util.Collections$WrappedList`` -* ``java.util.Collections$WrappedListIterator`` -* ``java.util.Collections$WrappedMap`` -* ``java.util.Collections$WrappedSet`` -* ``java.util.Collections$WrappedSortedMap`` -* ``java.util.Collections$WrappedSortedSet`` -* ``java.util.Comparator`` -* ``java.util.ConcurrentModificationException`` -* ``java.util.Date`` -* ``java.util.Deque`` -* ``java.util.Dictionary`` -* ``java.util.DuplicateFormatFlagsException`` -* ``java.util.EmptyStackException`` -* ``java.util.EnumSet`` -* ``java.util.Enumeration`` -* ``java.util.FormatFlagsConversionMismatchException`` -* ``java.util.Formattable`` -* ``java.util.FormattableFlags`` -* ``java.util.Formatter`` -* ``java.util.Formatter$BigDecimalLayoutForm`` -* ``java.util.FormatterClosedException`` -* ``java.util.GregorianCalendar`` -* ``java.util.HashMap`` -* ``java.util.HashSet`` -* ``java.util.Hashtable`` -* ``java.util.Hashtable$UnboxedEntry$1`` -* ``java.util.IdentityHashMap`` -* ``java.util.IllegalFormatCodePointException`` -* ``java.util.IllegalFormatConversionException`` -* ``java.util.IllegalFormatException`` -* ``java.util.IllegalFormatFlagsException`` -* ``java.util.IllegalFormatPrecisionException`` -* ``java.util.IllegalFormatWidthException`` -* ``java.util.IllformedLocaleException`` -* ``java.util.InputMismatchException`` -* ``java.util.InvalidPropertiesFormatException`` -* ``java.util.Iterator`` -* ``java.util.LinkedHashMap`` -* ``java.util.LinkedHashSet`` -* ``java.util.LinkedList`` -* ``java.util.List`` -* ``java.util.ListIterator`` -* ``java.util.Map`` -* ``java.util.Map$Entry`` -* ``java.util.MissingFormatArgumentException`` -* ``java.util.MissingFormatWidthException`` -* ``java.util.MissingResourceException`` -* ``java.util.NavigableMap`` -* ``java.util.NavigableSet`` -* ``java.util.NoSuchElementException`` -* ``java.util.Objects`` -* ``java.util.Optional`` -* ``java.util.PriorityQueue`` -* ``java.util.Properties`` -* ``java.util.Queue`` -* ``java.util.Random`` -* ``java.util.RandomAccess`` -* ``java.util.RandomAccessListIterator`` -* ``java.util.ServiceConfigurationError`` -* ``java.util.Set`` -* ``java.util.SizeChangeEvent`` -* ``java.util.SortedMap`` -* ``java.util.SortedSet`` -* ``java.util.StringTokenizer`` -* ``java.util.TooManyListenersException`` -* ``java.util.TreeSet`` -* ``java.util.UUID`` -* ``java.util.UnknownFormatConversionException`` -* ``java.util.UnknownFormatFlagsException`` -* ``java.util.WeakHashMap`` -* ``java.util.concurrent.Callable`` -* ``java.util.concurrent.CancellationException`` -* ``java.util.concurrent.ConcurrentHashMap`` -* ``java.util.concurrent.ConcurrentHashMap$KeySetView`` -* ``java.util.concurrent.ConcurrentLinkedQueue`` -* ``java.util.concurrent.ConcurrentMap`` -* ``java.util.concurrent.ConcurrentSkipListSet`` -* ``java.util.concurrent.ExecutionException`` -* ``java.util.concurrent.Executor`` -* ``java.util.concurrent.RejectedExecutionException`` -* ``java.util.concurrent.Semaphore`` -* ``java.util.concurrent.ThreadFactory`` -* ``java.util.concurrent.ThreadLocalRandom`` -* ``java.util.concurrent.TimeUnit`` -* ``java.util.concurrent.TimeoutException`` -* ``java.util.concurrent.atomic.AtomicBoolean`` -* ``java.util.concurrent.atomic.AtomicInteger`` -* ``java.util.concurrent.atomic.AtomicLong`` -* ``java.util.concurrent.atomic.AtomicLongArray`` -* ``java.util.concurrent.atomic.AtomicReference`` -* ``java.util.concurrent.atomic.AtomicReferenceArray`` -* ``java.util.concurrent.atomic.LongAdder`` -* ``java.util.concurrent.locks.AbstractOwnableSynchronizer`` -* ``java.util.concurrent.locks.AbstractQueuedSynchronizer`` -* ``java.util.concurrent.locks.Lock`` -* ``java.util.concurrent.locks.ReentrantLock`` -* ``java.util.function.BiConsumer`` -* ``java.util.function.BiFunction`` -* ``java.util.function.BiPredicate`` -* ``java.util.function.BinaryOperator`` -* ``java.util.function.Consumer`` -* ``java.util.function.Function`` -* ``java.util.function.IntUnaryOperator`` -* ``java.util.function.Predicate`` -* ``java.util.function.Supplier`` -* ``java.util.function.UnaryOperator`` -* ``java.util.jar.Attributes`` -* ``java.util.jar.Attributes$Name`` -* ``java.util.jar.InitManifest`` -* ``java.util.jar.JarEntry`` -* ``java.util.jar.JarFile`` -* ``java.util.jar.JarInputStream`` -* ``java.util.jar.JarOutputStream`` -* ``java.util.jar.Manifest`` -* ``java.util.regex.MatchResult`` -* ``java.util.regex.Matcher`` -* ``java.util.regex.Pattern`` -* ``java.util.regex.PatternSyntaxException`` -* ``java.util.stream.BaseStream`` -* ``java.util.stream.CompositeStream`` -* ``java.util.stream.EmptyIterator`` -* ``java.util.stream.Stream`` -* ``java.util.stream.Stream$Builder`` -* ``java.util.zip.Adler32`` -* ``java.util.zip.CRC32`` -* ``java.util.zip.CheckedInputStream`` -* ``java.util.zip.CheckedOutputStream`` -* ``java.util.zip.Checksum`` -* ``java.util.zip.DataFormatException`` -* ``java.util.zip.Deflater`` -* ``java.util.zip.DeflaterOutputStream`` -* ``java.util.zip.GZIPInputStream`` -* ``java.util.zip.GZIPOutputStream`` -* ``java.util.zip.Inflater`` -* ``java.util.zip.InflaterInputStream`` -* ``java.util.zip.ZipConstants`` -* ``java.util.zip.ZipEntry`` -* ``java.util.zip.ZipException`` -* ``java.util.zip.ZipFile`` -* ``java.util.zip.ZipInputStream`` -* ``java.util.zip.ZipOutputStream`` - +The classes currently available are: + +java.io +""""""" +* ``BufferedInputStream`` +* ``BufferedOutputStream`` +* ``BufferedReader`` +* ``BufferedWriter`` +* ``ByteArrayInputStream`` +* ``ByteArrayOutputStream`` +* ``Closeable`` +* ``DataInput`` +* ``DataInputStream`` +* ``DataOutput`` +* ``DataOutputStream`` +* ``EOFException`` +* ``File`` +* ``FileDescriptor`` +* ``FileFilter`` +* ``FileInputStream`` +* ``FileNotFoundException`` +* ``FileOutputStream`` +* ``FileReader`` +* ``FileWriter`` +* ``FilenameFilter`` +* ``FilterInputStream`` +* ``FilterOutputStream`` +* ``FilterReader`` +* ``Flushable`` +* ``IOException`` +* ``InputStream`` +* ``InputStreamReader`` +* ``InterruptedIOException`` +* ``LineNumberReader`` +* ``NotSerializableException`` +* ``ObjectStreamException`` +* ``OutputStream`` +* ``OutputStreamWriter`` +* ``PrintStream`` +* ``PrintWriter`` +* ``PushbackInputStream`` +* ``PushbackReader`` +* ``RandomAccessFile`` +* ``Reader`` +* ``Serializable`` +* ``StringReader`` +* ``StringWriter`` +* ``SyncFailedException`` +* ``UTFDataFormatException`` +* ``UncheckedIOException`` +* ``UnsupportedEncodingException`` +* ``Writer`` + +java.lang +""""""""" +* ``AbstractMethodError`` +* ``AbstractStringBuilder`` +* ``Appendable`` +* ``ArithmeticException`` +* ``ArrayIndexOutOfBoundsException`` +* ``ArrayStoreException`` +* ``AssertionError`` +* ``AutoCloseable`` +* ``Boolean`` +* ``BootstrapMethodError`` +* ``Byte`` +* ``ByteCache`` +* ``CharSequence`` +* ``Character`` +* ``Character.Subset`` +* ``Character.UnicodeBlock`` +* ``CharacterCache`` +* ``ClassCastException`` +* ``ClassCircularityError`` +* ``ClassFormatError`` +* ``ClassLoader`` +* ``ClassNotFoundException`` +* ``CloneNotSupportedException`` +* ``Cloneable`` +* ``Comparable`` +* ``Double`` +* ``Enum`` +* ``EnumConstantNotPresentException`` +* ``Error`` +* ``Exception`` +* ``ExceptionInInitializerError`` +* ``Float`` +* ``IllegalAccessError`` +* ``IllegalAccessException`` +* ``IllegalArgumentException`` +* ``IllegalMonitorStateException`` +* ``IllegalStateException`` +* ``IllegalThreadStateException`` +* ``IncompatibleClassChangeError`` +* ``IndexOutOfBoundsException`` +* ``InheritableThreadLocal`` +* ``InstantiationError`` +* ``InstantiationException`` +* ``Integer`` +* ``IntegerCache`` +* ``IntegerDecimalScale`` +* ``InternalError`` +* ``InterruptedException`` +* ``Iterable`` +* ``LinkageError`` +* ``Long`` +* ``LongCache`` +* ``Math`` +* ``MathRand`` +* ``NegativeArraySizeException`` +* ``NoClassDefFoundError`` +* ``NoSuchFieldError`` +* ``NoSuchFieldException`` +* ``NoSuchMethodError`` +* ``NoSuchMethodException`` +* ``NullPointerException`` +* ``Number`` +* ``NumberFormatException`` +* ``OutOfMemoryError`` +* ``Process`` +* ``ProcessBuilder`` +* ``ProcessBuilder.Redirect`` +* ``ProcessBuilder.Redirect.Type`` +* ``Readable`` +* ``ReflectiveOperationException`` +* ``RejectedExecutionException`` +* ``Runnable`` +* ``Runtime`` +* ``RuntimeException`` +* ``SecurityException`` +* ``Short`` +* ``StackOverflowError`` +* ``StackTrace`` +* ``StackTraceElement`` +* ``String`` +* ``StringBuffer`` +* ``StringBuilder`` +* ``StringIndexOutOfBoundsException`` +* ``System`` +* ``Thread`` +* ``Thread.UncaughtExceptionHandler`` +* ``ThreadDeath`` +* ``ThreadLocal`` +* ``Throwable`` +* ``TypeNotPresentException`` +* ``UnknownError`` +* ``UnsatisfiedLinkError`` +* ``UnsupportedClassVersionError`` +* ``UnsupportedOperationException`` +* ``VerifyError`` +* ``VirtualMachineError`` +* ``Void`` +* ``annotation.Annotation`` +* ``annotation.Retention`` +* ``annotation.RetentionPolicy`` +* ``constant.Constable`` +* ``constant.ConstantDesc`` +* ``ref.PhantomReference`` +* ``ref.Reference`` +* ``ref.ReferenceQueue`` +* ``ref.SoftReference`` +* ``ref.WeakReference`` +* ``reflect.AccessibleObject`` +* ``reflect.Array`` +* ``reflect.Constructor`` +* ``reflect.Executable`` +* ``reflect.Field`` +* ``reflect.InvocationTargetException`` +* ``reflect.Method`` +* ``reflect.UndeclaredThrowableException`` + +java.math +""""""""" +* ``BigDecimal`` +* ``BigInteger`` +* ``BitLevel`` +* ``Conversion`` +* ``Division`` +* ``Elementary`` +* ``Logical`` +* ``MathContext`` +* ``Multiplication`` +* ``Primality`` +* ``RoundingMode`` + +java.net +"""""""" +* ``BindException`` +* ``ConnectException`` +* ``Inet4Address`` +* ``Inet6Address`` +* ``InetAddress`` +* ``InetAddressBase`` +* ``InetSocketAddress`` +* ``MalformedURLException`` +* ``NoRouteToHostException`` +* ``PortUnreachableException`` +* ``ServerSocket`` +* ``Socket`` +* ``SocketAddress`` +* ``SocketException`` +* ``SocketImpl`` +* ``SocketInputStream`` +* ``SocketOption`` +* ``SocketOptions`` +* ``SocketOutputStream`` +* ``SocketTimeoutException`` +* ``URI`` +* ``URIEncoderDecoder`` +* ``URISyntaxException`` +* ``URL`` +* ``URLClassLoader`` +* ``URLConnection`` +* ``URLDecoder`` +* ``URLEncoder`` +* ``UnknownHostException`` +* ``UnknownServiceException`` + +java.nio +""""""""" +* ``Buffer`` +* ``BufferOverflowException`` +* ``BufferUnderflowException`` +* ``ByteBuffer`` +* ``ByteOrder`` +* ``CharBuffer`` +* ``DoubleBuffer`` +* ``FloatBuffer`` +* ``IntBuffer`` +* ``InvalidMarkException`` +* ``LongBuffer`` +* ``MappedByteBuffer`` +* ``ReadOnlyBufferException`` +* ``ShortBuffer`` +* ``channels.ByteChannel`` +* ``channels.Channel`` +* ``channels.Channels`` +* ``channels.ClosedChannelException`` +* ``channels.FileChannel`` +* ``channels.FileChannel.MapMode`` +* ``channels.FileLock`` +* ``channels.GatheringByteChannel`` +* ``channels.InterruptibleChannel`` +* ``channels.NonReadableChannelException`` +* ``channels.NonWritableChannelException`` +* ``channels.OverlappingFileLockException`` +* ``channels.ReadableByteChannel`` +* ``channels.ScatteringByteChannel`` +* ``channels.SeekableByteChannel`` +* ``channels.WritableByteChannel`` +* ``channels.spi.AbstractInterruptibleChannel`` +* ``charset.CharacterCodingException`` +* ``charset.Charset`` +* ``charset.CharsetDecoder`` +* ``charset.CharsetEncoder`` +* ``charset.CoderMalfunctionError`` +* ``charset.CoderResult`` +* ``charset.CodingErrorAction`` +* ``charset.IllegalCharsetNameException`` +* ``charset.MalformedInputException`` +* ``charset.StandardCharsets`` +* ``charset.UnmappableCharacterException`` +* ``charset.UnsupportedCharsetException`` +* ``file.AccessDeniedException`` +* ``file.CopyOption`` +* ``file.DirectoryIteratorException`` +* ``file.DirectoryNotEmptyException`` +* ``file.DirectoryStream`` +* ``file.DirectoryStream.Filter`` +* ``file.DirectoryStreamImpl`` +* ``file.FileAlreadyExistsException`` +* ``file.FileSystem`` +* ``file.FileSystemException`` +* ``file.FileSystemLoopException`` +* ``file.FileSystemNotFoundException`` +* ``file.FileSystems`` +* ``file.FileVisitOption`` +* ``file.FileVisitResult`` +* ``file.FileVisitor`` +* ``file.Files`` +* ``file.InvalidPathException`` +* ``file.LinkOption`` +* ``file.NoSuchFileException`` +* ``file.NotDirectoryException`` +* ``file.NotLinkException`` +* ``file.OpenOption`` +* ``file.Path`` +* ``file.PathMatcher`` +* ``file.Paths`` +* ``file.RegexPathMatcher`` +* ``file.SimpleFileVisitor`` +* ``file.StandardCopyOption`` +* ``file.StandardOpenOption`` +* ``file.StandardWatchEventKinds`` +* ``file.WatchEvent`` +* ``file.WatchEvent.Kind`` +* ``file.WatchEvent.Modifier`` +* ``file.WatchKey`` +* ``file.WatchService`` +* ``file.Watchable`` +* ``file.attribute.AclEntry`` +* ``file.attribute.AclFileAttributeView`` +* ``file.attribute.AttributeView`` +* ``file.attribute.BasicFileAttributeView`` +* ``file.attribute.BasicFileAttributes`` +* ``file.attribute.DosFileAttributeView`` +* ``file.attribute.DosFileAttributes`` +* ``file.attribute.FileAttribute`` +* ``file.attribute.FileAttributeView`` +* ``file.attribute.FileOwnerAttributeView`` +* ``file.attribute.FileStoreAttributeView`` +* ``file.attribute.FileTime`` +* ``file.attribute.GroupPrincipal`` +* ``file.attribute.PosixFileAttributeView`` +* ``file.attribute.PosixFileAttributes`` +* ``file.attribute.PosixFilePermission`` +* ``file.attribute.PosixFilePermissions`` +* ``file.attribute.UserDefinedFileAttributeView`` +* ``file.attribute.UserPrincipal`` +* ``file.attribute.UserPrincipalLookupService`` +* ``file.attribute.UserPrincipalNotFoundException`` +* ``file.spi.FileSystemProvider`` + +java.rmi +"""""""" +* ``Remote`` +* ``RemoteException`` + +java.security +""""""""""""" +* ``AccessControlException`` +* ``CodeSigner`` +* ``DummyMessageDigest`` +* ``GeneralSecurityException`` +* ``MessageDigest`` +* ``MessageDigestSpi`` +* ``NoSuchAlgorithmException`` +* ``Principal`` +* ``Timestamp`` +* ``TimestampConstructorHelper`` +* ``cert.CertPath`` +* ``cert.Certificate`` +* ``cert.CertificateEncodingException`` +* ``cert.CertificateException`` +* ``cert.CertificateFactory`` +* ``cert.X509Certificate`` +* ``cert.X509Extension`` + + +java.util +""""""""" +* ``AbstractCollection`` +* ``AbstractList`` +* ``AbstractListView`` +* ``AbstractMap`` +* ``AbstractMap.SimpleEntry`` +* ``AbstractMap.SimpleImmutableEntry`` +* ``AbstractQueue`` +* ``AbstractRandomAccessListIterator`` +* ``AbstractSequentialList`` +* ``AbstractSet`` +* ``ArrayDeque`` +* ``ArrayList`` +* ``Arrays`` +* ``BackedUpListIterator`` +* ``Base64`` +* ``Base64.Decoder`` +* ``Base64.Encoder`` +* ``BitSet`` +* ``Calendar`` +* ``Collection`` +* ``Collections`` +* ``Comparator`` +* ``ConcurrentModificationException`` +* ``Date`` +* ``Deque`` +* ``Dictionary`` +* ``DuplicateFormatFlagsException`` +* ``EmptyStackException`` +* ``EnumSet`` +* ``Enumeration`` +* ``FormatFlagsConversionMismatchException`` +* ``Formattable`` +* ``FormattableFlags`` +* ``Formatter`` +* ``Formatter.BigDecimalLayoutForm`` +* ``FormatterClosedException`` +* ``GregorianCalendar`` +* ``HashMap`` +* ``HashSet`` +* ``Hashtable`` +* ``IdentityHashMap`` +* ``IllegalFormatCodePointException`` +* ``IllegalFormatConversionException`` +* ``IllegalFormatException`` +* ``IllegalFormatFlagsException`` +* ``IllegalFormatPrecisionException`` +* ``IllegalFormatWidthException`` +* ``IllformedLocaleException`` +* ``InputMismatchException`` +* ``InvalidPropertiesFormatException`` +* ``Iterator`` +* ``LinkedHashMap`` +* ``LinkedHashSet`` +* ``LinkedList`` +* ``List`` +* ``ListIterator`` +* ``MissingFormatArgumentException`` +* ``MissingFormatWidthException`` +* ``MissingResourceException`` +* ``NavigableMap`` +* ``NavigableSet`` +* ``NoSuchElementException`` +* ``Objects`` +* ``Optional`` +* ``PriorityQueue`` +* ``Properties`` +* ``Queue`` +* ``Random`` +* ``RandomAccess`` +* ``RandomAccessListIterator`` +* ``ServiceConfigurationError`` +* ``Set`` +* ``SizeChangeEvent`` +* ``SortedMap`` +* ``SortedSet`` +* ``StringTokenizer`` +* ``TooManyListenersException`` +* ``TreeSet`` +* ``UUID`` +* ``UnknownFormatConversionException`` +* ``UnknownFormatFlagsException`` +* ``WeakHashMap`` +* ``concurrent.Callable`` +* ``concurrent.CancellationException`` +* ``concurrent.ConcurrentHashMap`` +* ``concurrent.ConcurrentHashMap.KeySetView`` +* ``concurrent.ConcurrentLinkedQueue`` +* ``concurrent.ConcurrentMap`` +* ``concurrent.ConcurrentSkipListSet`` +* ``concurrent.ExecutionException`` +* ``concurrent.Executor`` +* ``concurrent.RejectedExecutionException`` +* ``concurrent.Semaphore`` +* ``concurrent.ThreadFactory`` +* ``concurrent.ThreadLocalRandom`` +* ``concurrent.TimeUnit`` +* ``concurrent.TimeoutException`` +* ``concurrent.atomic.AtomicBoolean`` +* ``concurrent.atomic.AtomicInteger`` +* ``concurrent.atomic.AtomicLong`` +* ``concurrent.atomic.AtomicLongArray`` +* ``concurrent.atomic.AtomicReference`` +* ``concurrent.atomic.AtomicReferenceArray`` +* ``concurrent.atomic.LongAdder`` +* ``concurrent.locks.AbstractOwnableSynchronizer`` +* ``concurrent.locks.AbstractQueuedSynchronizer`` +* ``concurrent.locks.Lock`` +* ``concurrent.locks.ReentrantLock`` +* ``function.BiConsumer`` +* ``function.BiFunction`` +* ``function.BiPredicate`` +* ``function.BinaryOperator`` +* ``function.Consumer`` +* ``function.Function`` +* ``function.IntUnaryOperator`` +* ``function.Predicate`` +* ``function.Supplier`` +* ``function.UnaryOperator`` +* ``jar.Attributes`` +* ``jar.Attributes.Name`` +* ``jar.InitManifest`` +* ``jar.JarEntry`` +* ``jar.JarFile`` +* ``jar.JarInputStream`` +* ``jar.JarOutputStream`` +* ``jar.Manifest`` +* ``regex.MatchResult`` +* ``regex.Matcher`` +* ``regex.Pattern`` +* ``regex.PatternSyntaxException`` +* ``stream.BaseStream`` +* ``stream.CompositeStream`` +* ``stream.EmptyIterator`` +* ``stream.Stream`` +* ``stream.Stream.Builder`` +* ``zip.Adler32`` +* ``zip.CRC32`` +* ``zip.CheckedInputStream`` +* ``zip.CheckedOutputStream`` +* ``zip.Checksum`` +* ``zip.DataFormatException`` +* ``zip.Deflater`` +* ``zip.DeflaterOutputStream`` +* ``zip.GZIPInputStream`` +* ``zip.GZIPOutputStream`` +* ``zip.Inflater`` +* ``zip.InflaterInputStream`` +* ``zip.ZipConstants`` +* ``zip.ZipEntry`` +* ``zip.ZipException`` +* ``zip.ZipFile`` +* ``zip.ZipInputStream`` +* ``zip.ZipOutputStream`` + + **Note:** This is an ongoing effort, some of the classes listed here might be partially implemented. Please consult `javalib sources `_ @@ -652,7 +636,7 @@ and used as a resource: This is to avoid unnecesarily embedding source files. If necessary, please consider using a different file extension for embedding. Files found in the ``resources/scala-native`` directory will not be embedded as well. It is recommended -to add the ".c" nad ".h" files there. +to add the ".c" and ".h" files there. Reasoning for the lack of ``getResource()`` and ``getResources()``: diff --git a/docs/lib/libc.rst b/docs/lib/libc.rst index ee13474e1e..480be7a20c 100644 --- a/docs/lib/libc.rst +++ b/docs/lib/libc.rst @@ -1,9 +1,11 @@ .. _libc: -C Standard Library -================== +ISO/IEC C Standard Library +========================== Scala Native provides bindings for a core subset of the +International Organization for Standardization/International +Electrotechnical Commission (ISO/IEC) `C standard library `_: ============== ================================== @@ -18,7 +20,7 @@ float.h_ scala.scalanative.libc.float_ inttypes.h_ N/A iso646.h_ N/A limits.h_ N/A -locale.h_ N/A +locale.h_ scala.scalanative.libc.locale_ math.h_ scala.scalanative.libc.math_ setjmp.h_ N/A signal.h_ scala.scalanative.libc.signal_ @@ -75,6 +77,7 @@ wctype.h_ N/A .. _scala.scalanative.libc.ctype: https://github.com/scala-native/scala-native/blob/main/clib/src/main/scala/scala/scalanative/libc/ctype.scala .. _scala.scalanative.libc.errno: https://github.com/scala-native/scala-native/blob/main/clib/src/main/scala/scala/scalanative/libc/errno.scala .. _scala.scalanative.libc.float: https://github.com/scala-native/scala-native/blob/main/clib/src/main/scala/scala/scalanative/libc/float.scala +.. _scala.scalanative.libc.locale: https://github.com/scala-native/scala-native/blob/main/clib/src/main/scala/scala/scalanative/libc/locale.scala .. _scala.scalanative.libc.math: https://github.com/scala-native/scala-native/blob/main/clib/src/main/scala/scala/scalanative/libc/math.scala .. _scala.scalanative.libc.stddef: https://github.com/scala-native/scala-native/blob/main/clib/src/main/scala/scala/scalanative/libc/stddef.scala .. _scala.scalanative.libc.stdio: https://github.com/scala-native/scala-native/blob/main/clib/src/main/scala/scala/scalanative/libc/stdio.scala diff --git a/docs/lib/posixlib.rst b/docs/lib/posixlib.rst index 9823175c48..b1861b5374 100644 --- a/docs/lib/posixlib.rst +++ b/docs/lib/posixlib.rst @@ -22,27 +22,27 @@ C Header Scala Native Module `fenv.h`_ N/A `float.h`_ scala.scalanative.libc.float_ `fmtmsg.h`_ N/A -`fnmatch.h`_ N/A +`fnmatch.h`_ scala.scalanative.posix.fnmatch_ `ftw.h`_ N/A `getopt.h`_ scala.scalanative.posix.getopt_ -`glob.h`_ N/A +`glob.h`_ scala.scalanative.posix.glob_ `grp.h`_ scala.scalanative.posix.grp_ `iconv.h`_ N/A `inttypes.h`_ scala.scalanative.posix.inttypes_ `iso646.h`_ N/A -`langinfo.h`_ N/A -`libgen.h`_ N/A +`langinfo.h`_ scala.scalanative.posix.langinfo_ +`libgen.h`_ scala.scalanative.posix.libgen_ `limits.h`_ scala.scalanative.posix.limits_ `locale.h`_ N/A `math.h`_ scala.scalanative.libc.math_ -`monetary.h`_ N/A +`monetary.h`_ scala.scalanative.posix.monetary_ [#monetary_varargs]_ `mqueue.h`_ N/A `ndbm.h`_ N/A `net/if.h`_ scala.scalanative.posix.net.if_ `netdb.h`_ scala.scalanative.posix.netdb_ `netinet/in.h`_ scala.scalanative.posix.netinet.in_ `netinet/tcp.h`_ scala.scalanative.posix.netinet.tcp_ -`nl_types.h`_ N/A +`nl_types.h`_ scala.scalanative.posix.nl_types_ `poll.h`_ scala.scalanative.posix.poll_ `pthread.h`_ scala.scalanative.posix.pthread_ `pwd.h`_ scala.scalanative.posix.pwd_ @@ -73,12 +73,12 @@ C Header Scala Native Module `sys/stat.h`_ scala.scalanative.posix.sys.stat_ `sys/statvfs.h`_ scala.scalanative.posix.sys.statvfs_ `sys/time.h`_ scala.scalanative.posix.sys.time_ -`sys/times.h`_ N/A +`sys/times.h`_ scala.scalanative.posix.sys.times_ `sys/types.h`_ scala.scalanative.posix.sys.types_ `sys/uio.h`_ scala.scalanative.posix.sys.uio_ -`sys/un.h`_ N/A +`sys/un.h`_ scala.scalanative.posix.sys.un_ `sys/utsname.h`_ scala.scalanative.posix.sys.utsname_ -`sys/wait.h`_ N/A +`sys/wait.h`_ scala.scalanative.posix.sys.wait_ `syslog.h`_ scala.scalanative.posix.syslog_ `tar.h`_ N/A `termios.h`_ scala.scalanative.posix.termios_ @@ -91,7 +91,7 @@ C Header Scala Native Module `utmpx.h`_ N/A `wchar.h`_ N/A `wctype.h`_ N/A -`wordexp.h`_ N/A +`wordexp.h`_ scala.scalanative.posix.wordexp_ ================= ================================== .. _aio.h: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/aio.h.html @@ -186,15 +186,21 @@ C Header Scala Native Module .. _scala.scalanative.posix.errno: https://github.com/scala-native/scala-native/blob/main/posixlib/src/main/scala/scala/scalanative/posix/errno.scala .. _scala.scalanative.posix.fcntl: https://github.com/scala-native/scala-native/blob/main/posixlib/src/main/scala/scala/scalanative/posix/fcntl.scala .. _scala.scalanative.libc.float: https://github.com/scala-native/scala-native/blob/main/clib/src/main/scala/scala/scalanative/libc/float.scala -.. _scala.scalanative.posix.getopt: https://github.com/scala-native/scala-native/blob/main/posixlib/src/main/scala/scala/scalanative/posix/getopt.scala +.. _scala.scalanative.posix.fnmatch: https://github.com/scala-native/scala-native/blob/main/clib/src/main/scala/scala/scalanative/libc/fnmatch.scala +.. _scala.scalanative.posix.getopt: https://github.com/scala-native/scala-native/blob/main/posixlib/src/main/scala/scala/scalanative/posix/getopt.scala [#getopt_no_longer_posix_2018]_ +.. _scala.scalanative.posix.glob: https://github.com/scala-native/scala-native/blob/main/posixlib/src/main/scala/scala/scalanative/posix/glob.scala .. _scala.scalanative.posix.grp: https://github.com/scala-native/scala-native/blob/main/posixlib/src/main/scala/scala/scalanative/posix/grp.scala .. _scala.scalanative.posix.inttypes: https://github.com/scala-native/scala-native/blob/main/posixlib/src/main/scala/scala/scalanative/posix/inttypes.scala +.. _scala.scalanative.posix.langinfo: https://github.com/scala-native/scala-native/blob/main/posixlib/src/main/scala/scala/scalanative/posix/langinfo.scala .. _scala.scalanative.posix.limits: https://github.com/scala-native/scala-native/blob/main/posixlib/src/main/scala/scala/scalanative/posix/limits.scala +.. _scala.scalanative.posix.libgen: https://github.com/scala-native/scala-native/blob/main/posixlib/src/main/scala/scala/scalanative/posix/libgen.scala .. _scala.scalanative.libc.math: https://github.com/scala-native/scala-native/blob/main/clib/src/main/scala/scala/scalanative/libc/math.scala +.. _scala.scalanative.posix.monetary: https://github.com/scala-native/scala-native/blob/main/posixlib/src/main/scala/scala/scalanative/posix/monetaryh.scala .. _scala.scalanative.posix.net.if: https://github.com/scala-native/scala-native/blob/main/posixlib/src/main/scala/scala/scalanative/posix/net/if.scala .. _scala.scalanative.posix.netdb: https://github.com/scala-native/scala-native/blob/main/posixlib/src/main/scala/scala/scalanative/posix/netdb.scala .. _scala.scalanative.posix.netinet.in: https://github.com/scala-native/scala-native/blob/main/posixlib/src/main/scala/scala/scalanative/posix/netinet/in.scala .. _scala.scalanative.posix.netinet.tcp: https://github.com/scala-native/scala-native/blob/main/posixlib/src/main/scala/scala/scalanative/posix/netinet/tcp.scala +.. _scala.scalanative.posix.nl_types: https://github.com/scala-native/scala-native/blob/main/posixlib/src/main/scala/scala/scalanative/posix/nl_types.scala .. _scala.scalanative.posix.poll: https://github.com/scala-native/scala-native/blob/main/posixlib/src/main/scala/scala/scalanative/posix/poll.scala .. _scala.scalanative.posix.pthread: https://github.com/scala-native/scala-native/blob/main/posixlib/src/main/scala/scala/scalanative/posix/pthread.scala .. _scala.scalanative.posix.pwd: https://github.com/scala-native/scala-native/blob/main/posixlib/src/main/scala/scala/scalanative/posix/pwd.scala @@ -213,19 +219,27 @@ C Header Scala Native Module .. _scala.scalanative.posix.sys.stat: https://github.com/scala-native/scala-native/blob/main/posixlib/src/main/scala/scala/scalanative/posix/sys/stat.scala .. _scala.scalanative.posix.sys.statvfs: https://github.com/scala-native/scala-native/blob/main/posixlib/src/main/scala/scala/scalanative/posix/sys/statvfs.scala .. _scala.scalanative.posix.sys.time: https://github.com/scala-native/scala-native/blob/main/posixlib/src/main/scala/scala/scalanative/posix/sys/time.scala +.. _scala.scalanative.posix.sys.times: https://github.com/scala-native/scala-native/blob/main/posixlib/src/main/scala/scala/scalanative/posix/sys/times.scala .. _scala.scalanative.posix.sys.types: https://github.com/scala-native/scala-native/blob/main/posixlib/src/main/scala/scala/scalanative/posix/sys/types.scala .. _scala.scalanative.posix.sys.uio: https://github.com/scala-native/scala-native/blob/main/posixlib/src/main/scala/scala/scalanative/posix/sys/uio.scala +.. _scala.scalanative.posix.sys.un: https://github.com/scala-native/scala-native/blob/main/posixlib/src/main/scala/scala/scalanative/posix/sys/un.scala .. _scala.scalanative.posix.sys.utsname: https://github.com/scala-native/scala-native/blob/main/posixlib/src/main/scala/scala/scalanative/posix/sys/utsname.scala +.. _scala.scalanative.posix.sys.wait: https://github.com/scala-native/scala-native/blob/main/posixlib/src/main/scala/scala/scalanative/posix/sys/wait.scala .. _scala.scalanative.posix.syslog: https://github.com/scala-native/scala-native/blob/main/posixlib/src/main/scala/scala/scalanative/posix/syslog.scala .. _scala.scalanative.posix.termios: https://github.com/scala-native/scala-native/blob/main/posixlib/src/main/scala/scala/scalanative/posix/termios.scala .. _scala.scalanative.posix.time: https://github.com/scala-native/scala-native/blob/main/posixlib/src/main/scala/scala/scalanative/posix/time.scala .. _scala.scalanative.posix.unistd: https://github.com/scala-native/scala-native/blob/main/posixlib/src/main/scala/scala/scalanative/posix/unistd.scala .. _scala.scalanative.posix.utime: https://github.com/scala-native/scala-native/blob/main/posixlib/src/main/scala/scala/scalanative/posix/utime.scala +.. _scala.scalanative.posix.wchar: https://github.com/scala-native/scala-native/blob/main/posixlib/src/main/scala/scala/scalanative/posix/wchar.scala +.. _scala.scalanative.posix.wordexp: https://github.com/scala-native/scala-native/blob/main/posixlib/src/main/scala/scala/scalanative/posix/wordexp.scala .. rubric Footnotes .. [#inet_ntoa] The argument to inet_ntoa() differs from the POSIX specification because Scala Native supports only passing structures by reference. See code for details and usage. +.. [#getopt_no_longer_posix_2018] getopt.scala, introduced in PR `Fix #202: Support assignment to extern variables #1348 `_ + is no longer part of POSIX 2018 and will be unified to unistd.scala in the future. +.. [#monetary_varargs] See file for limit on number of variable arguments. Continue to :ref:`communitylib`. diff --git a/docs/user/interop.rst b/docs/user/interop.rst index 1546925bf8..d68c01fe01 100644 --- a/docs/user/interop.rst +++ b/docs/user/interop.rst @@ -338,7 +338,7 @@ runtime system, one has to be extra careful when working with unmanaged memory. This code will allocate 256 bytes that are going to be available until the enclosing method returns. Number of elements to be allocated is optional - and defaults to 1 otherwise. Memory is not zeroed out by default. + and defaults to 1 otherwise. Memory **is zeroed out** by default. When using stack allocated memory one has to be careful not to capture this memory beyond the lifetime of the method. Dereferencing stack allocated diff --git a/docs/user/sbt.rst b/docs/user/sbt.rst index 1f42b8cbf7..143b961a96 100644 --- a/docs/user/sbt.rst +++ b/docs/user/sbt.rst @@ -158,7 +158,7 @@ Since Name Type Description 0.1 ``nativeClangPP`` ``File`` Path to ``clang++`` command 0.1 ``nativeCompileOptions`` ``Seq[String]`` Extra options passed to clang verbatim during compilation 0.1 ``nativeLinkingOptions`` ``Seq[String]`` Extra options passed to clang verbatim during linking -0.1 ``nativeMode`` ``String`` One of ``"debug"``, ``"release-fast"`` or ``"release-full"`` (2) +0.1 ``nativeMode`` ``String`` One of ``"debug"``, ``"release-fast"``, ``"release-size"`` or ``"release-full"`` (2) 0.2 ``nativeGC`` ``String`` One of ``"none"``, ``"boehm"``, ``"immix"`` or ``"commix"`` (3) 0.3.3 ``nativeLinkStubs`` ``Boolean`` Whether to link ``@stub`` definitions, or to ignore them 0.4.0 ``nativeConfig`` ``NativeConfig`` Configuration of the Scala Native plugin @@ -188,14 +188,21 @@ Scala Native supports three distinct linking modes: Aliases to **release-full**. -2. **release-fast.** (introduced in 0.4.0) +3. **release-fast.** (introduced in 0.4.0) Optimize for runtime performance while still trying to keep quick compilation time and small emitted code size. Similar to clang's ``-O2`` with addition of link-time optimization over the whole application code. -3. **release-full.** (introduced in 0.4.0) +4. **release-size.** (introduced in 0.4.10) + + Optimize for reduced output size while still trying to keep + quick compilation time and relatively fast runtime performance. + Similar to clang's ``-Oz`` with addition of link-time optimization over + the whole application code. + +5. **release-full.** (introduced in 0.4.0) Optimized for best runtime performance, even if hurts compilation time and code size. This modes includes a number of more aggresive optimizations diff --git a/javalib/src/main/scala/java/io/InputStream.scala b/javalib/src/main/scala/java/io/InputStream.scala index 62e9437e7d..e4f56122ec 100644 --- a/javalib/src/main/scala/java/io/InputStream.scala +++ b/javalib/src/main/scala/java/io/InputStream.scala @@ -1,5 +1,7 @@ package java.io +import java.{util => ju} + abstract class InputStream extends Closeable { def read(): Int @@ -32,6 +34,81 @@ abstract class InputStream extends Closeable { } } + // Allow obvious implementation of readAllBytes() to work on both Java 9 & 11 + def readNBytesImpl(len: Int): Array[Byte] = { + if (len < 0) + throw new IllegalArgumentException("len < 0") + + def readBytes(len: Int): ByteArrayOutputStream = { + val limit = Math.min(len, 1024) + + val storage = new ByteArrayOutputStream(limit) // can grow itself + val buffer = new Array[Byte](limit) + + var remaining = len + + while (remaining > 0) { + val nRead = read(buffer, 0, limit) + + if (nRead == -1) remaining = 0 // EOF + else { + storage.write(buffer, 0, nRead) + remaining -= nRead + } + } + + storage + } + + /* To stay within the documented 2 * len memory bound for this method, + * ensure that the temporary intermediate read buffer is out of scope + * and released before calling toByteArray(). + */ + + readBytes(len).toByteArray() + } + + /** Java 9 + */ + def readAllBytes(): Array[Byte] = readNBytesImpl(Integer.MAX_VALUE) + + /** Java 9 + */ + def readNBytes(buffer: Array[Byte], off: Int, len: Int): Int = { + ju.Objects.requireNonNull(buffer) + + if ((off < 0) || (len < 0) || (len > buffer.length - off)) { + val range = s"Range [${off}, ${off} + ${len})" + throw new IndexOutOfBoundsException( + s"${range} out of bounds for length ${buffer.length}" + ) + } + + if (len == 0) 0 + else { + var totalBytesRead = 0 + var remaining = len + var offset = off + + while (remaining > 0) { + val nRead = read(buffer, offset, remaining) + + if (nRead == -1) remaining = 0 // EOF + else { + totalBytesRead += nRead + remaining -= nRead + offset += nRead + } + } + + totalBytesRead + } + } + + /** Java 11 + */ + def readNBytes(len: Int): Array[Byte] = readNBytesImpl(len) + def skip(n: Long): Long = { var skipped = 0 while (skipped < n && read() != -1) skipped += 1 @@ -48,4 +125,25 @@ abstract class InputStream extends Closeable { throw new IOException("Reset not supported") def markSupported(): Boolean = false + + /** Java 9 + */ + def transferTo(out: OutputStream): Long = { + val limit = 1024 + val buffer = new Array[Byte](limit) + + var nTransferred = 0L + var done = false + + while (!done) { + val nRead = readNBytes(buffer, 0, limit) + if (nRead == 0) done = true // EOF + else { + out.write(buffer, 0, nRead) + nTransferred += nRead + } + } + + nTransferred + } } diff --git a/javalib/src/main/scala/java/lang/Character.scala b/javalib/src/main/scala/java/lang/Character.scala index e1ebd7784a..8bb0780891 100644 --- a/javalib/src/main/scala/java/lang/Character.scala +++ b/javalib/src/main/scala/java/lang/Character.scala @@ -182,8 +182,8 @@ class Character(val _value: scala.Char) object Character { final val TYPE = scala.Predef.classOf[scala.scalanative.runtime.PrimitiveChar] - final val MIN_VALUE = '\u0000' - final val MAX_VALUE = '\uffff' + final val MIN_VALUE: Char = '\u0000' + final val MAX_VALUE: Char = '\uffff' final val SIZE = 16 final val BYTES = 2 @@ -224,12 +224,12 @@ object Character { final val MIN_RADIX = 2 final val MAX_RADIX = 36 - final val MIN_HIGH_SURROGATE = '\uD800' - final val MAX_HIGH_SURROGATE = '\uDBFF' - final val MIN_LOW_SURROGATE = '\uDC00' - final val MAX_LOW_SURROGATE = '\uDFFF' - final val MIN_SURROGATE = MIN_HIGH_SURROGATE - final val MAX_SURROGATE = MAX_LOW_SURROGATE + final val MIN_HIGH_SURROGATE: Char = '\uD800' + final val MAX_HIGH_SURROGATE: Char = '\uDBFF' + final val MIN_LOW_SURROGATE: Char = '\uDC00' + final val MAX_LOW_SURROGATE: Char = '\uDFFF' + final val MIN_SURROGATE: Char = MIN_HIGH_SURROGATE + final val MAX_SURROGATE: Char = MAX_LOW_SURROGATE final val MIN_CODE_POINT = 0 final val MAX_CODE_POINT = 0x10ffff diff --git a/javalib/src/main/scala/java/lang/IEEE754Helpers.scala b/javalib/src/main/scala/java/lang/IEEE754Helpers.scala index a87620f8c7..8c75810e48 100644 --- a/javalib/src/main/scala/java/lang/IEEE754Helpers.scala +++ b/javalib/src/main/scala/java/lang/IEEE754Helpers.scala @@ -21,7 +21,7 @@ private[java] object IEEE754Helpers { // DO NOT USE STRING INTERPOLATION with an interior double quote ("), // a.k.a Unicode "QUOTATION MARK" (\u0022). // Double quote failing interpolated strings is a longstanding - // bug in many Scala versions, including 2.11.n, 2.12.n, & 2.13.2. + // bug in many Scala versions, including 2.12.n, & 2.13.2. // See URLS: // https://github.com/scala/bug/issues/6476 // https://github.com/scala/scala/pull/8830 diff --git a/javalib/src/main/scala/java/lang/Iterable.scala b/javalib/src/main/scala/java/lang/Iterable.scala index d69e0a2dba..f0276e25b6 100644 --- a/javalib/src/main/scala/java/lang/Iterable.scala +++ b/javalib/src/main/scala/java/lang/Iterable.scala @@ -5,12 +5,9 @@ package java.lang import java.util.Iterator import java.util.function.Consumer -import scala.scalanative.annotation.JavaDefaultMethod - trait Iterable[T] { def iterator(): Iterator[T] - @JavaDefaultMethod def forEach(action: Consumer[_ >: T]): Unit = { val iter = iterator() while (iter.hasNext()) diff --git a/javalib/src/main/scala/java/lang/System.scala b/javalib/src/main/scala/java/lang/System.scala index 48a504e158..0987792769 100644 --- a/javalib/src/main/scala/java/lang/System.scala +++ b/javalib/src/main/scala/java/lang/System.scala @@ -91,16 +91,19 @@ object System { var err: PrintStream = new PrintStream(new FileOutputStream(FileDescriptor.err)) - private val systemProperties = loadProperties() - Platform.setOSProps { (key: CString, value: CString) => - val _ = systemProperties.setProperty(fromCString(key), fromCString(value)) - } - def lineSeparator(): String = { if (Platform.isWindows()) "\r\n" else "\n" } + private lazy val systemProperties0 = loadProperties() + private lazy val systemProperties = { + Platform.setOSProps { (key: CString, value: CString) => + systemProperties0.setProperty(fromCString(key), fromCString(value)) + () + } + systemProperties0 + } def getProperties(): Properties = systemProperties def clearProperty(key: String): String = diff --git a/javalib/src/main/scala/java/lang/ref/WeakReferenceRegistry.scala b/javalib/src/main/scala/java/lang/ref/WeakReferenceRegistry.scala index e6ac8ece36..1301086c9b 100644 --- a/javalib/src/main/scala/java/lang/ref/WeakReferenceRegistry.scala +++ b/javalib/src/main/scala/java/lang/ref/WeakReferenceRegistry.scala @@ -11,7 +11,7 @@ import scala.scalanative.runtime.GC */ private[java] object WeakReferenceRegistry { private var weakRefList: immutable.List[WeakReference[_]] = - immutable.List() + Nil private val postGCHandlerMap : mutable.HashMap[WeakReference[_], Function0[Unit]] = diff --git a/javalib/src/main/scala/java/net/InetAddress.scala b/javalib/src/main/scala/java/net/InetAddress.scala index 0050b63fe0..55839cda2a 100644 --- a/javalib/src/main/scala/java/net/InetAddress.scala +++ b/javalib/src/main/scala/java/net/InetAddress.scala @@ -29,6 +29,8 @@ import scala.scalanative.posix.sys.socket._ import scala.scalanative.posix.time.{time_t, time, difftime} import scala.scalanative.posix.unistd +import scala.scalanative.meta.LinktimeInfo.{isLinux, isMac} + /* Design note: * Much of java.net, both in JVM and Scala Native defines or assumes * the ipAddress field to have either 4 or 16 bytes. @@ -333,7 +335,8 @@ object InetAddress { val gaiStatus = getaddrinfo(toCString(host), null, hints, addrinfo) if (gaiStatus != 0) { - if (gaiStatus == EAI_NONAME) { + val mappedStatus = mapGaiStatus(gaiStatus) + if (mappedStatus == EAI_NONAME) { val ifIndex = host.indexOf('%') val hasInterface = (ifIndex >= 0) if (!hasInterface) { @@ -355,8 +358,8 @@ object InetAddress { ) } } else { - val gaiMsg = SocketHelpers.getGaiErrorMessage(gaiStatus) - throw new IOException(gaiMsg) + val gaiMsg = SocketHelpers.getGaiErrorMessage(mappedStatus) + throw new UnknownHostException(host + ": " + gaiMsg) } } else try { @@ -436,12 +439,7 @@ object InetAddress { if (gaiStatus != 0) { val gaiMsg = SocketHelpers.getGaiErrorMessage(gaiStatus) - val ex = - if (gaiStatus == EAI_NONAME) - new UnknownHostException(host + ": " + gaiMsg) - else - new IOException(gaiMsg) - throw ex + throw new UnknownHostException(host + ": " + gaiMsg) } else try { val preferIPv6 = SocketHelpers.getPreferIPv6Addresses() @@ -624,9 +622,10 @@ object InetAddress { val gaiStatus = getaddrinfo(toCString(host), null, hints, ret) if (gaiStatus != 0) { - if (gaiStatus != EAI_NONAME) { - val gaiMsg = SocketHelpers.getGaiErrorMessage(gaiStatus) - throw new IOException(gaiMsg) + val mappedStatus = mapGaiStatus(gaiStatus) + if (mappedStatus != EAI_NONAME) { + val gaiMsg = SocketHelpers.getGaiErrorMessage(mappedStatus) + throw new UnknownHostException(host + ": " + gaiMsg) } } else try { @@ -645,6 +644,35 @@ object InetAddress { (ptrInt(2) == 0xffff0000) && (ptrLong(0) == 0x0L) } + private def mapGaiStatus(gaiStatus: Int): Int = { + /* This is where some arcane Operating System specific behavior + * comes to puddle and pool. This method is not for small children + * or maintainers with good taste & practice. + * + * EAI_NODATA was removed from RFC3493 "Basic Socket Interface Extensions + * for IPv6" in February 2003. EAI_NONAME was introduced and is the + * contemporary idiom. Although it is remove (i.e. well past deprecated), + * EAI_NODATA can be returned by Linux & macOS in some poorly defined + * circumstances. + * + * The magic integer values for Linux & macOS are hardcoded + * because they are extremely unlikely to change after all this time. + * + * For consistency of the reported message, map EAI_NODATA to EAI_NONAME. + * Both will return "UnknownHostException". + */ + + // EAI_NODATA was removed from FreeBSD a decade or more ago. + val EAI_NODATA = + if (isLinux) -5 + else if (isMac) 7 + else Integer.MAX_VALUE // placeholder, will never match + + if (gaiStatus == EAI_NONAME) gaiStatus + else if (gaiStatus == EAI_NODATA) EAI_NONAME + else gaiStatus + } + def getAllByName(host: String): Array[InetAddress] = { if ((host == null) || (host.length == 0)) { /* The obvious recursive call to getAllByName("localhost") does not diff --git a/javalib/src/main/scala/java/net/UnixPlainSocketImpl.scala b/javalib/src/main/scala/java/net/UnixPlainSocketImpl.scala index 9e9280ff3c..1b45e3c546 100644 --- a/javalib/src/main/scala/java/net/UnixPlainSocketImpl.scala +++ b/javalib/src/main/scala/java/net/UnixPlainSocketImpl.scala @@ -57,14 +57,16 @@ private[net] class UnixPlainSocketImpl extends AbstractPlainSocketImpl { case _ => if ((revents & POLLNVAL) != 0) { - throw new ConnectException( - s"connect failed, invalid poll request: ${revents}" - ) - } else if ((revents & (POLLERR | POLLHUP)) != 0) { - throw new ConnectException( - s"connect failed, POLLERR or POLLHUP set: ${revents}" - ) - } + val msg = s"connect failed, invalid poll request: ${revents}" + throw new ConnectException(msg) + } else if ((revents & (POLLIN | POLLHUP)) != 0) { + // Not enough information at this point to report remote host:port. + val msg = "Connection refused" + throw new ConnectException(msg) + } else if ((revents & POLLERR) != 0) { // an error was recognized. + val msg = s"connect failed, poll POLLERR: ${revents}" + throw new ConnectException(msg) + } // else should be POLLOUT - Open for Business, ignore XSI bits if set } } diff --git a/javalib/src/main/scala/java/nio/charset/Charset.scala b/javalib/src/main/scala/java/nio/charset/Charset.scala index f4550df3c6..a9fe4f33b1 100644 --- a/javalib/src/main/scala/java/nio/charset/Charset.scala +++ b/javalib/src/main/scala/java/nio/charset/Charset.scala @@ -77,6 +77,18 @@ object Charset { def isSupported(charsetName: String): Boolean = CharsetMap.contains(charsetName.toLowerCase) + def availableCharsets(): java.util.SortedMap[String, Charset] = + availableCharsetsResult + + private lazy val availableCharsetsResult = { + val m = + new java.util.TreeMap[String, Charset](String.CASE_INSENSITIVE_ORDER) + allNativeCharsets.foreach { c => + m.put(c.name(), c) + } + Collections.unmodifiableSortedMap(m) + } + private lazy val CharsetMap = { val m = mutable.Map.empty[String, Charset] // TODO Check if a better map is needed @@ -140,4 +152,7 @@ object Charset { m } + private def allNativeCharsets = + Array(US_ASCII, ISO_8859_1, UTF_8, UTF_16BE, UTF_16LE, UTF_16) + } diff --git a/javalib/src/main/scala/java/nio/file/Files.scala b/javalib/src/main/scala/java/nio/file/Files.scala index cac6d515ab..4baae0e71a 100644 --- a/javalib/src/main/scala/java/nio/file/Files.scala +++ b/javalib/src/main/scala/java/nio/file/Files.scala @@ -31,9 +31,10 @@ import java.util.stream.{Stream, WrappedScalaStream} import scalanative.unsigned._ import scalanative.unsafe._ import scalanative.libc._ +import scalanative.libc.errno.errno import scalanative.posix.{dirent, fcntl, limits, unistd} import dirent._ -import scalanative.posix.errno.{EEXIST, ENOTEMPTY} +import scalanative.posix.errno.{EEXIST, ENOENT, ENOTEMPTY} import java.nio.file.StandardCopyOption.{COPY_ATTRIBUTES, REPLACE_EXISTING} import scalanative.nio.fs.unix.UnixException @@ -201,26 +202,41 @@ object Files { throw new IOException() } - def createLink(link: Path, existing: Path): Path = { - def tryCreateHardLink() = Zone { implicit z => - if (isWindows) - CreateHardLinkW( + def createLink(link: Path, existing: Path): Path = Zone { implicit z => + if (isWindows) { + if (exists(link, Array.empty)) { + throw new FileAlreadyExistsException(link.toString) + } else { + val created = CreateHardLinkW( toCWideStringUTF16LE(link.toString), toCWideStringUTF16LE(existing.toString), securityAttributes = null ) - else - unistd.link( - toCString(existing.toString()), - toCString(link.toString()) - ) == 0 - } - if (exists(link, Array.empty)) { - throw new FileAlreadyExistsException(link.toString) - } else if (tryCreateHardLink()) { - link + if (created) { + link + } else { + throw new IOException("Cannot create link") + } + } + } else { - throw new IOException("Cannot create link") + val rtn = unistd.link( + toCString(existing.toString()), + toCString(link.toString()) + ) + + if (rtn == 0) { + link + } else { + val e = errno + if (e == EEXIST) + throw new FileAlreadyExistsException(link.toString) + else if (e == ENOENT) + throw new NoSuchFileException(link.toString, existing.toString, null) + else + throw new IOException(fromCString(string.strerror(e))) + } + } } @@ -349,7 +365,7 @@ object Files { val ps = path.toString if (stdio.remove(toCString(ps)) == -1) { // For historical reasons, some systems report ENOTEMPTY as EEXIST - val fixedErrno = if (errno.errno == EEXIST) ENOTEMPTY else errno.errno + val fixedErrno = if (errno == EEXIST) ENOTEMPTY else errno throw PosixException(ps, fixedErrno) } } @@ -569,7 +585,7 @@ object Files { val sourceCString = toCString(sourceAbs) val targetCString = toCString(targetAbs) if (stdio.rename(sourceCString, targetCString) != 0) { - throw UnixException(target.toString, errno.errno) + throw UnixException(target.toString, errno) } } } @@ -646,6 +662,9 @@ object Files { !exists(path, options) def readAllBytes(path: Path): Array[Byte] = Zone { implicit z => + /* if 'path' does not exist at all, should get + * java.nio.file.NoSuchFileException here. + */ val pathSize: Long = size(path) if (!pathSize.isValidInt) { throw new OutOfMemoryError("Required array size too large") @@ -672,8 +691,15 @@ object Files { } } } else { + errno = 0 val pathCString = toCString(path.toString) val fd = fcntl.open(pathCString, fcntl.O_RDONLY, 0.toUInt) + + if (fd == -1) { + val msg = fromCString(string.strerror(errno)) + throw new IOException(s"error opening path '${path}': ${msg}") + } + try { var offset = 0 var read = 0 @@ -683,7 +709,7 @@ object Files { }) { offset += read } - if (read == -1) throw UnixException(path.toString, errno.errno) + if (read == -1) throw UnixException(path.toString, errno) } finally { unistd.close(fd) } @@ -780,7 +806,7 @@ object Files { buf, limits.PATH_MAX - `1U` ) == -1) { - throw UnixException(link.toString, errno.errno) + throw UnixException(link.toString, errno) } fromCString(buf) } diff --git a/javalib/src/main/scala/java/nio/file/Path.scala b/javalib/src/main/scala/java/nio/file/Path.scala index eaf757a607..38093c3230 100644 --- a/javalib/src/main/scala/java/nio/file/Path.scala +++ b/javalib/src/main/scala/java/nio/file/Path.scala @@ -35,3 +35,18 @@ trait Path extends Comparable[Path] with Iterable[Path] with Watchable { def toString(): String def toUri(): URI } + +object Path { + private lazy val fs = FileSystems.getDefault() + // Introduced in Java 11 + def of(path: String, paths: Array[String]): Path = fs.getPath(path, paths) + def of(uri: URI): Path = if (uri.getScheme() == null) { + throw new IllegalArgumentException("Missing scheme") + } else if (uri.getScheme().toLowerCase == "file") { + fs.getPath(uri.getPath(), Array.empty) + } else { + throw new FileSystemNotFoundException( + s"Provider ${uri.getScheme()} is not installed." + ) + } +} diff --git a/javalib/src/main/scala/java/util/ArrayDeque.scala b/javalib/src/main/scala/java/util/ArrayDeque.scala index c12b477a90..00d5990856 100644 --- a/javalib/src/main/scala/java/util/ArrayDeque.scala +++ b/javalib/src/main/scala/java/util/ArrayDeque.scala @@ -8,7 +8,7 @@ * https://gee.cs.oswego.edu/dl/concurrency-interest/index.html */ -package java.util; +package java.util import java.io.Serializable import java.util.function.Consumer @@ -17,35 +17,43 @@ import java.util.function.UnaryOperator import ArrayDeque._ +object ArrayDeque { + + /** The maximum size of array to allocate. Some VMs reserve some header words + * in an array. Attempts to allocate larger arrays may result in + * OutOfMemoryError: Requested array size exceeds VM limit + */ + private val MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8 + +} + /** Resizable-array implementation of the {@link Deque} interface. Array deques * have no capacity restrictions; they grow as necessary to support usage. They * are not thread-safe; in the absence of external synchronization, they do not * support concurrent access by multiple threads. Null elements are prohibited. - * This class is likely to be faster than {@link Stack} when used as a stack, + * This class is likely to be faster than java.util.Stack when used as a stack, * and faster than {@link LinkedList} when used as a queue. * - *

Most {@code ArrayDeque} operations run in amortized constant time. - * Exceptions include {@link #remove(Object) remove}, {@link - * #removeFirstOccurrence removeFirstOccurrence}, {@link #removeLastOccurrence - * removeLastOccurrence}, {@link #contains contains}, {@link #iterator - * iterator.remove()}, and the bulk operations, all of which run in linear - * time. + * Exceptions include remove, {@link #removeFirstOccurrence + * removeFirstOccurrence}, {@link #removeLastOccurrence removeLastOccurrence}, + * {@link #contains contains}, {@link #iterator iterator.remove()}, and the + * bulk operations, all of which run in linear time. * - *

The iterators returned by this class's {@link #iterator() iterator} - * method are fail-fast: If the deque is modified at any time after - * the iterator is created, in any way except through the iterator's own {@code - * remove} method, the iterator will generally throw a {@link - * ConcurrentModificationException}. Thus, in the face of concurrent + *

The iterators returned by this class's {@link #iterator iterator} method + * are fail-fast: If the deque is modified at any time after the + * iterator is created, in any way except through the iterator's own {@code + * remove} method, the iterator will generally throw a + * ConcurrentModificationException. Thus, in the face of concurrent * modification, the iterator fails quickly and cleanly, rather than risking * arbitrary, non-deterministic behavior at an undetermined time in the future. * *

Note that the fail-fast behavior of an iterator cannot be guaranteed as * it is, generally speaking, impossible to make any hard guarantees in the * presence of unsynchronized concurrent modification. Fail-fast iterators - * throw {@code ConcurrentModificationException} on a best-effort basis. - * Therefore, it would be wrong to write a program that depended on this - * exception for its correctness: the fail-fast behavior of iterators should - * be used only to detect bugs. + * throw ConcurrentModificationException on a best-effort basis. Therefore, it + * would be wrong to write a program that depended on this exception for its + * correctness: the fail-fast behavior of iterators should be used only to + * detect bugs. * *

This class and its iterator implement all of the optional * methods of the {@link Collection} and {@link Iterator} interfaces. @@ -60,16 +68,6 @@ import ArrayDeque._ * the type of elements held in this deque * @since 1.6 */ -object ArrayDeque { - - /** The maximum size of array to allocate. Some VMs reserve some header words - * in an array. Attempts to allocate larger arrays may result in - * OutOfMemoryError: Requested array size exceeds VM limit - */ - private val MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8 - -} - class ArrayDeque[E]( /** The array in which the elements of the deque are stored. All array cells * not holding deque elements are always null. The array always has at @@ -215,7 +213,7 @@ class ArrayDeque[E]( * * @param c * the collection whose elements are to be placed into the deque - * @throws NullPointerException + * @throws java.lang.NullPointerException * if the specified collection is null */ def this(c: Collection[_ <: E]) = { @@ -290,7 +288,7 @@ class ArrayDeque[E]( * * @param e * the element to add - * @throws NullPointerException + * @throws java.lang.NullPointerException * if the specified element is null */ def addFirst(e: E): Unit = { @@ -310,7 +308,7 @@ class ArrayDeque[E]( * * @param e * the element to add - * @throws NullPointerException + * @throws java.lang.NullPointerException * if the specified element is null */ def addLast(e: E): Unit = { @@ -332,7 +330,7 @@ class ArrayDeque[E]( * the elements to be inserted into this deque * @return * {@code true} if this deque changed as a result of the call - * @throws NullPointerException + * @throws java.lang.NullPointerException * if the specified collection or any of its elements are null */ override def addAll(c: Collection[_ <: E]): Boolean = { @@ -355,7 +353,7 @@ class ArrayDeque[E]( * the element to add * @return * {@code true} (as specified by {@link Deque#offerFirst}) - * @throws NullPointerException + * @throws java.lang.NullPointerException * if the specified element is null */ def offerFirst(e: E): Boolean = { @@ -369,7 +367,7 @@ class ArrayDeque[E]( * the element to add * @return * {@code true} (as specified by {@link Deque#offerLast}) - * @throws NullPointerException + * @throws java.lang.NullPointerException * if the specified element is null */ def offerLast(e: E): Boolean = { @@ -377,9 +375,7 @@ class ArrayDeque[E]( return true } - /** @throws NoSuchElementException - * {@inheritDoc} - */ + /** @throws NoSuchElementException */ def removeFirst(): E = { val e = pollFirst() if (e == null) @@ -388,9 +384,7 @@ class ArrayDeque[E]( return e } - /** @throws NoSuchElementException - * {@inheritDoc} - */ + /** @throws NoSuchElementException */ def removeLast(): E = { val e = pollLast() if (e == null) @@ -423,9 +417,7 @@ class ArrayDeque[E]( return e } - /** @throws NoSuchElementException - * {@inheritDoc} - */ + /** @throws NoSuchElementException */ def getFirst(): E = { val e = elementAt(elements, head) if (e == null) @@ -434,9 +426,7 @@ class ArrayDeque[E]( return e } - /** @throws NoSuchElementException - * {@inheritDoc} - */ + /** @throws NoSuchElementException */ def getLast(): E = { val es = elements val e = elementAt(es, dec(tail, es.length)) @@ -493,10 +483,12 @@ class ArrayDeque[E]( /** Removes the last occurrence of the specified element in this deque (when * traversing the deque from head to tail). If the deque does not contain the - * element, it is unchanged. More formally, removes the last element {@code - * e} such that {@code o.equals(e)} (if such an element exists). Returns - * {@code true} if this deque contained the specified element (or - * equivalently, if this deque changed as a result of the call). + * element, it is unchanged. + * + * More formally, removes the last element such that {@code o.equals(e)} (if + * such an element exists). Returns {@code true} if this deque contained the + * specified element (or equivalently, if this deque changed as a result of + * the call). * * @param o * element to be removed from this deque, if present @@ -536,7 +528,7 @@ class ArrayDeque[E]( * the element to add * @return * {@code true} (as specified by {@link Collection#add}) - * @throws NullPointerException + * @throws java.lang.NullPointerException * if the specified element is null */ override def add(e: E): Boolean = { @@ -552,7 +544,7 @@ class ArrayDeque[E]( * the element to add * @return * {@code true} (as specified by {@link Queue#offer}) - * @throws NullPointerException + * @throws java.lang.NullPointerException * if the specified element is null */ def offer(e: E): Boolean = { @@ -561,7 +553,7 @@ class ArrayDeque[E]( /** Retrieves and removes the head of the queue represented by this deque. * - * This method differs from {@link #poll() poll()} only in that it throws an + * This method differs from {@link #poll poll()} only in that it throws an * exception if this deque is empty. * *

This method is equivalent to {@link #removeFirst}. @@ -569,7 +561,6 @@ class ArrayDeque[E]( * @return * the head of the queue represented by this deque * @throws NoSuchElementException - * {@inheritDoc} */ def remove(): E = { return removeFirst() @@ -598,7 +589,6 @@ class ArrayDeque[E]( * @return * the head of the queue represented by this deque * @throws NoSuchElementException - * {@inheritDoc} */ def element(): E = { return getFirst() @@ -626,7 +616,7 @@ class ArrayDeque[E]( * * @param e * the element to push - * @throws NullPointerException + * @throws java.lang.NullPointerException * if the specified element is null */ def push(e: E): Unit = { @@ -636,13 +626,12 @@ class ArrayDeque[E]( /** Pops an element from the stack represented by this deque. In other words, * removes and returns the first element of this deque. * - *

This method is equivalent to {@link #removeFirst()}. + *

This method is equivalent to {@link #removeFirst}. * * @return * the element at the front of this deque (which is the top of the stack * represented by this deque) * @throws NoSuchElementException - * {@inheritDoc} */ def pop(): E = { return removeFirst() @@ -719,8 +708,8 @@ class ArrayDeque[E]( /** Returns an iterator over the elements in this deque. The elements will be * ordered from first (head) to last (tail). This is the same order that - * elements would be dequeued (via successive calls to {@link #remove} or - * popped (via successive calls to {@link #pop}). + * elements would be dequeued (via successive calls to remove or popped (via + * successive calls to {@link #pop}). * * @return * an iterator over the elements in this deque @@ -853,9 +842,9 @@ class ArrayDeque[E]( /** Creates a late-binding and * fail-fast {@link Spliterator} over the elements in this deque. * - *

The {@code Spliterator} reports {@link Spliterator#SIZED}, {@link - * Spliterator#SUBSIZED}, {@link Spliterator#ORDERED}, and {@link - * Spliterator#NONNULL}. Overriding implementations should document the + *

The {@code Spliterator} reports [[Spliterator.SIZED]], + * [[Spliterator.SUBSIZED]], [[Spliterator.ORDERED]], and + * [[Spliterator.NONNULL]]. Overriding implementations should document the * reporting of additional characteristic values. * * @return @@ -951,9 +940,7 @@ class ArrayDeque[E]( } } - /** @throws NullPointerException - * {@inheritDoc} - */ + /** @throws java.lang.NullPointerException */ override def forEach(action: Consumer[_ >: E]): Unit = { Objects.requireNonNull(action) val es = elements @@ -1003,25 +990,19 @@ class ArrayDeque[E]( // checkInvariants(); } - /** @throws NullPointerException - * {@inheritDoc} - */ + /** @throws java.lang.NullPointerException */ override def removeIf(filter: Predicate[_ >: E]): Boolean = { Objects.requireNonNull(filter) return bulkRemove(filter) } - /** @throws NullPointerException - * {@inheritDoc} - */ + /** @throws java.lang.NullPointerException */ override def removeAll(c: Collection[_]): Boolean = { Objects.requireNonNull(c) return bulkRemove(c.contains(_)) } - /** @throws NullPointerException - * {@inheritDoc} - */ + /** @throws java.lang.NullPointerException */ override def retainAll(c: Collection[_]): Boolean = { Objects.requireNonNull(c) return bulkRemove(!c.contains(_)) @@ -1177,7 +1158,7 @@ class ArrayDeque[E]( * specified element (or equivalently, if this deque changed as a result of * the call). * - *

This method is equivalent to {@link #removeFirstOccurrence(Object)}. + *

This method is equivalent to [[removeFirstOccurrence]]. * * @param o * element to be removed from this deque, if present @@ -1263,7 +1244,7 @@ class ArrayDeque[E]( * array has more elements than this deque), the element in the array * immediately following the end of the deque is set to {@code null}. * - *

Like the {@link #toArray()} method, this method acts as bridge between + *

Like the [[toArray()*]] method, this method acts as bridge between * array-based and collection-based APIs. Further, this method allows precise * control over the runtime type of the output array, and may, under certain * circumstances, be used to save allocation costs. @@ -1283,10 +1264,10 @@ class ArrayDeque[E]( * allocated for this purpose * @return * an array containing all of the elements in this deque - * @throws ArrayStoreException + * @throws java.lang.ArrayStoreException * if the runtime type of the specified array is not a supertype of the * runtime type of every element in this deque - * @throws NullPointerException + * @throws java.lang.NullPointerException * if the specified array is null */ override def toArray[T <: AnyRef](a: Array[T]): Array[T] = { diff --git a/javalib/src/main/scala/java/util/Collection.scala b/javalib/src/main/scala/java/util/Collection.scala index f71808912b..c9bfff6382 100644 --- a/javalib/src/main/scala/java/util/Collection.scala +++ b/javalib/src/main/scala/java/util/Collection.scala @@ -4,8 +4,6 @@ package java.util import java.util.function.Predicate -import scala.scalanative.annotation.JavaDefaultMethod - trait Collection[E] extends java.lang.Iterable[E] { def size(): Int def isEmpty(): Boolean @@ -19,7 +17,6 @@ trait Collection[E] extends java.lang.Iterable[E] { def addAll(c: Collection[_ <: E]): Boolean def removeAll(c: Collection[_]): Boolean - @JavaDefaultMethod def removeIf(filter: Predicate[_ >: E]): Boolean = { var result = false val iter = iterator() diff --git a/javalib/src/main/scala/java/util/Collections.scala b/javalib/src/main/scala/java/util/Collections.scala index 1633ebfbb9..1ecbe4590b 100644 --- a/javalib/src/main/scala/java/util/Collections.scala +++ b/javalib/src/main/scala/java/util/Collections.scala @@ -275,14 +275,14 @@ object Collections { // Differs from original type definition, original: [T <: jl.Comparable[_ >: T]], returning T def min[T <: AnyRef with jl.Comparable[T]](coll: Collection[_ <: T]): AnyRef = - min(coll, naturalComparator[T]) + min(coll, Comparator.naturalOrder[T]()) def min[T](coll: Collection[_ <: T], comp: Comparator[_ >: T]): T = coll.scalaOps.reduceLeft((a, b) => if (comp.compare(a, b) <= 0) a else b) // Differs from original type definition, original: [T <: jl.Comparable[_ >: T]], returning T def max[T <: AnyRef with jl.Comparable[T]](coll: Collection[_ <: T]): AnyRef = - max(coll, naturalComparator[T]) + max(coll, Comparator.naturalOrder[T]()) def max[T](coll: Collection[_ <: T], comp: Comparator[_ >: T]): T = coll.scalaOps.reduceLeft((a, b) => if (comp.compare(a, b) >= 0) a else b) @@ -639,13 +639,6 @@ object Collections { @inline private def modulo(a: Int, b: Int): Int = ((a % b) + b) % b - @inline - private def naturalComparator[T <: jl.Comparable[T]]: Comparator[T] = { - new Comparator[T] with Serializable { - final def compare(o1: T, o2: T): Int = o1.compareTo(o2) - } - } - private trait WrappedEquals { protected def inner: AnyRef diff --git a/javalib/src/main/scala/java/util/Comparator.scala b/javalib/src/main/scala/java/util/Comparator.scala index e06d0d41b0..c4c696c366 100644 --- a/javalib/src/main/scala/java/util/Comparator.scala +++ b/javalib/src/main/scala/java/util/Comparator.scala @@ -1,14 +1,181 @@ -// Ported from Scala.js commit SHA1: 9dc4d5b dated: 2018-10-11 +// Ported from Scala.js commit 00e462d dated: 2023-01-22 package java.util -import scala.scalanative.annotation.JavaDefaultMethod +import java.util.function._ + +// scalastyle:off equals.hash.code + +/* A note about serializability: + * + * The JDK documentation states that returned comparators are serializable if + * their respective elements (Comparators / Functions) are serializable. + * + * Experimentation on `nullsFirst` has shown that the returned comparator always + * implements `Serializable` (and supposedly relies on the serialization + * mechanism itself to fail when it is unable to serialize a field). + * + * Our implementation mimics this behavior. + */ + +trait Comparator[A] { self => + import Comparator._ -trait Comparator[A] { def compare(o1: A, o2: A): Int def equals(obj: Any): Boolean - @JavaDefaultMethod def reversed(): Comparator[A] = Collections.reverseOrder(this) + + @inline + def thenComparing(other: Comparator[_ >: A]): Comparator[A] = { + Objects.requireNonNull(other) + new Comparator[A] with Serializable { + def compare(o1: A, o2: A) = { + val cmp = self.compare(o1, o2) + if (cmp != 0) cmp + else other.compare(o1, o2) + } + } + } + + def thenComparing[U]( + keyExtractor: Function[_ >: A, _ <: U], + keyComparator: Comparator[_ >: U] + ): Comparator[A] = { + thenComparing(comparing[A, U](keyExtractor, keyComparator)) + } + + /* Should be U <: Comparable[_ >: U] but scalac fails with + * > illegal cyclic reference involving type U + */ + def thenComparing[U <: Comparable[U]]( + keyExtractor: Function[_ >: A, _ <: U] + ): Comparator[A] = { + thenComparing(comparing[A, U](keyExtractor)) + } + + def thenComparingInt(keyExtractor: ToIntFunction[_ >: A]): Comparator[A] = + thenComparing(comparingInt(keyExtractor)) + + def thenComparingLong(keyExtractor: ToLongFunction[_ >: A]): Comparator[A] = + thenComparing(comparingLong(keyExtractor)) + + def thenComparingDouble( + keyExtractor: ToDoubleFunction[_ >: A] + ): Comparator[A] = + thenComparing(comparingDouble(keyExtractor)) + +} + +object Comparator { + + /* Should be T <: Comparable[_ >: T] but scalac fails with + * > illegal cyclic reference involving type U + */ + def reverseOrder[T <: Comparable[T]](): Comparator[T] = + naturalOrder[T]().reversed() + + /* Should be T <: Comparable[_ >: T] but scalac fails with + * > illegal cyclic reference involving type U + */ + @inline + def naturalOrder[T <: Comparable[T]](): Comparator[T] = + ReusableNaturalComparator.asInstanceOf[Comparator[T]] + + /* Not the same object as NaturalComparator. + * + * Otherwise we'll get null back from TreeSet#comparator() (see #4796). + */ + private object ReusableNaturalComparator extends Comparator[Any] { + def compare(o1: Any, o2: Any): Int = + o1.asInstanceOf[Comparable[Any]].compareTo(o2) + } + + @inline + def nullsFirst[T](comparator: Comparator[_ >: T]): Comparator[T] = + new Comparator[T] with Serializable { + def compare(o1: T, o2: T): Int = { + if (o1 == null && o2 == null) 0 + else if (o1 == null) -1 + else if (o2 == null) 1 + else if (comparator == null) 0 + else comparator.compare(o1, o2) + } + } + + @inline + def nullsLast[T](comparator: Comparator[_ >: T]): Comparator[T] = + new Comparator[T] with Serializable { + def compare(o1: T, o2: T): Int = { + if (o1 == null && o2 == null) 0 + else if (o1 == null) 1 + else if (o2 == null) -1 + else if (comparator == null) 0 + else comparator.compare(o1, o2) + } + } + + @inline + def comparing[T, U]( + keyExtractor: Function[_ >: T, _ <: U], + keyComparator: Comparator[_ >: U] + ): Comparator[T] = { + Objects.requireNonNull(keyExtractor) + Objects.requireNonNull(keyComparator) + new Comparator[T] with Serializable { + def compare(o1: T, o2: T): Int = + keyComparator.compare(keyExtractor(o1), keyExtractor(o2)) + } + } + + /* Should be U <: Comparable[_ >: U] but scalac fails with + * > illegal cyclic reference involving type U + */ + @inline + def comparing[T, U <: Comparable[U]]( + keyExtractor: Function[_ >: T, _ <: U] + ): Comparator[T] = { + Objects.requireNonNull(keyExtractor) + new Comparator[T] with Serializable { + def compare(o1: T, o2: T): Int = + keyExtractor(o1).compareTo(keyExtractor(o2)) + } + } + + @inline + def comparingInt[T](keyExtractor: ToIntFunction[_ >: T]): Comparator[T] = { + Objects.requireNonNull(keyExtractor) + new Comparator[T] with Serializable { + def compare(o1: T, o2: T): Int = + Integer.compare( + keyExtractor.applyAsInt(o1), + keyExtractor.applyAsInt(o2) + ) + } + } + + @inline + def comparingLong[T](keyExtractor: ToLongFunction[_ >: T]): Comparator[T] = { + Objects.requireNonNull(keyExtractor) + new Comparator[T] with Serializable { + def compare(o1: T, o2: T): Int = + java.lang.Long + .compare(keyExtractor.applyAsLong(o1), keyExtractor.applyAsLong(o2)) + } + } + + @inline + def comparingDouble[T]( + keyExtractor: ToDoubleFunction[_ >: T] + ): Comparator[T] = { + Objects.requireNonNull(keyExtractor) + new Comparator[T] with Serializable { + def compare(o1: T, o2: T): Int = + java.lang.Double.compare( + keyExtractor.applyAsDouble(o1), + keyExtractor.applyAsDouble(o2) + ) + } + } } diff --git a/javalib/src/main/scala/java/util/Iterator.scala b/javalib/src/main/scala/java/util/Iterator.scala index 3f38145e84..0200d1a44a 100644 --- a/javalib/src/main/scala/java/util/Iterator.scala +++ b/javalib/src/main/scala/java/util/Iterator.scala @@ -2,19 +2,15 @@ package java.util -import scala.scalanative.annotation.JavaDefaultMethod - import java.util.function.Consumer trait Iterator[E] { def hasNext(): Boolean def next(): E - @JavaDefaultMethod def remove(): Unit = throw new UnsupportedOperationException("remove") - @JavaDefaultMethod def forEachRemaining(action: Consumer[_ >: E]): Unit = { while (hasNext()) action.accept(next()) diff --git a/javalib/src/main/scala/java/util/List.scala b/javalib/src/main/scala/java/util/List.scala index 44a4202430..fc7ba3ff04 100644 --- a/javalib/src/main/scala/java/util/List.scala +++ b/javalib/src/main/scala/java/util/List.scala @@ -4,17 +4,13 @@ package java.util import java.util.function.UnaryOperator -import scala.scalanative.annotation.JavaDefaultMethod - trait List[E] extends Collection[E] { - @JavaDefaultMethod def replaceAll(operator: UnaryOperator[E]): Unit = { val iter = listIterator() while (iter.hasNext()) iter.set(operator.apply(iter.next())) } - @JavaDefaultMethod def sort(c: Comparator[_ >: E]): Unit = { val arrayBuf = toArray() Arrays.sort[AnyRef with E](arrayBuf.asInstanceOf[Array[AnyRef with E]], c) diff --git a/javalib/src/main/scala/java/util/Map.scala b/javalib/src/main/scala/java/util/Map.scala index 4cf8b96b74..e20219d89d 100644 --- a/javalib/src/main/scala/java/util/Map.scala +++ b/javalib/src/main/scala/java/util/Map.scala @@ -4,8 +4,6 @@ package java.util import java.util.function.{BiConsumer, BiFunction, Function} -import scala.scalanative.annotation.JavaDefaultMethod - import ScalaOps._ trait Map[K, V] { @@ -24,24 +22,20 @@ trait Map[K, V] { def equals(o: Any): Boolean def hashCode(): Int - @JavaDefaultMethod def getOrDefault(key: Any, defaultValue: V): V = if (containsKey(key)) get(key) else defaultValue - @JavaDefaultMethod def forEach(action: BiConsumer[_ >: K, _ >: V]): Unit = { for (entry <- entrySet().scalaOps) action.accept(entry.getKey(), entry.getValue()) } - @JavaDefaultMethod def replaceAll(function: BiFunction[_ >: K, _ >: V, _ <: V]): Unit = { for (entry <- entrySet().scalaOps) entry.setValue(function.apply(entry.getKey(), entry.getValue())) } - @JavaDefaultMethod def putIfAbsent(key: K, value: V): V = { val prevValue = get(key) if (prevValue == null) @@ -50,7 +44,6 @@ trait Map[K, V] { prevValue } - @JavaDefaultMethod def remove(key: Any, value: Any): Boolean = { if (containsKey(key) && Objects.equals(get(key), value)) { remove(key) @@ -60,7 +53,6 @@ trait Map[K, V] { } } - @JavaDefaultMethod def replace(key: K, oldValue: V, newValue: V): Boolean = { if (containsKey(key) && Objects.equals(get(key), oldValue)) { put(key, newValue) @@ -70,12 +62,10 @@ trait Map[K, V] { } } - @JavaDefaultMethod def replace(key: K, value: V): V = if (containsKey(key)) put(key, value) else null.asInstanceOf[V] - @JavaDefaultMethod def computeIfAbsent(key: K, mappingFunction: Function[_ >: K, _ <: V]): V = { val oldValue = get(key) if (oldValue != null) { @@ -88,7 +78,6 @@ trait Map[K, V] { } } - @JavaDefaultMethod def computeIfPresent( key: K, remappingFunction: BiFunction[_ >: K, _ >: V, _ <: V] @@ -103,7 +92,6 @@ trait Map[K, V] { } } - @JavaDefaultMethod def compute( key: K, remappingFunction: BiFunction[_ >: K, _ >: V, _ <: V] @@ -127,7 +115,6 @@ trait Map[K, V] { newValue } - @JavaDefaultMethod def merge( key: K, value: V, diff --git a/javalib/src/main/scala/java/util/NaturalComparator.scala b/javalib/src/main/scala/java/util/NaturalComparator.scala index a33354d7bf..b4b1e86760 100644 --- a/javalib/src/main/scala/java/util/NaturalComparator.scala +++ b/javalib/src/main/scala/java/util/NaturalComparator.scala @@ -25,7 +25,9 @@ package java.util * Scala.js is configured with compliant `asInstanceOf`s. The behavior is * otherwise undefined. */ -private[util] object NaturalComparator extends Comparator[Any] { +private[util] object NaturalComparator + extends Comparator[Any] + with Serializable { def compare(o1: Any, o2: Any): Int = o1.asInstanceOf[Comparable[Any]].compareTo(o2) diff --git a/javalib/src/main/scala/java/util/RedBlackTree.scala b/javalib/src/main/scala/java/util/RedBlackTree.scala index 5011001ed6..e0f2cacdd3 100644 --- a/javalib/src/main/scala/java/util/RedBlackTree.scala +++ b/javalib/src/main/scala/java/util/RedBlackTree.scala @@ -1,4 +1,5 @@ -// Ported form Scala.js, revision 6819668, dated 7 Oct 2020 +// Ported from Scala.js commit def516f dated: 2023-01-22 + /* * Scala.js (https://www.scala-js.org/) * @@ -15,9 +16,7 @@ package java.util import scala.annotation.tailrec -/** The red-black tree implementation used by `TreeSet`s. - * - * It could also be used by `TreeMap`s in the future. +/** The red-black tree implementation used by `TreeSet`s and `TreeMap`s. * * This implementation was copied and adapted from * `scala.collection.mutable.RedBlackTree` as found in Scala 2.13.0. @@ -247,7 +246,13 @@ private[util] object RedBlackTree { def get[A, B](tree: Tree[A, B], key: Any)(implicit comp: Comparator[_ >: A] ): B = { - nullableNodeFlatMap(getNode(tree.root, key))(_.value) + nullableNodeFlatMap(getNode(tree, key))(_.value) + } + + def getNode[A, B](tree: Tree[A, B], key: Any)(implicit + comp: Comparator[_ >: A] + ): Node[A, B] = { + getNode(tree.root, key) } @tailrec @@ -511,10 +516,10 @@ private[util] object RedBlackTree { def delete[A, B](tree: Tree[A, B], key: Any)(implicit comp: Comparator[_ >: A] - ): B = { + ): Node[A, B] = { nullableNodeFlatMap(getNode(tree.root, key)) { node => deleteNode(tree, node) - node.value + node } } @@ -658,10 +663,17 @@ private[util] object RedBlackTree { /** Returns `null.asInstanceOf[A]` if `node eq null`, otherwise `node.key`. */ @inline - private def nullableNodeKey[A, B](node: Node[A, B]): A = + def nullableNodeKey[A, B](node: Node[A, B]): A = if (node eq null) null.asInstanceOf[A] else node.key + /** Returns `null.asInstanceOf[B]` if `node eq null`, otherwise `node.value`. + */ + @inline + def nullableNodeValue[A, B](node: Node[A, B]): B = + if (node eq null) null.asInstanceOf[B] + else node.value + /** Returns the node that follows `node` in an in-order tree traversal. * * If `node` has the maximum key (and is, therefore, the last node), this diff --git a/javalib/src/main/scala/java/util/Spliterator.scala b/javalib/src/main/scala/java/util/Spliterator.scala index 2ef9cff98c..6e90a3dfd1 100644 --- a/javalib/src/main/scala/java/util/Spliterator.scala +++ b/javalib/src/main/scala/java/util/Spliterator.scala @@ -1,7 +1,6 @@ package java.util import java.util.function.Consumer -import scala.scalanative.annotation.JavaDefaultMethod import Spliterator._ @@ -22,18 +21,14 @@ trait Spliterator[T] { def estimateSize(): Long - @JavaDefaultMethod def forEachRemaining(action: Consumer[_ >: T]): Unit = while (tryAdvance(action)) {} - @JavaDefaultMethod def getComparator(): Comparator[_ >: T] = throw new IllegalStateException() - @JavaDefaultMethod def getExactSizeIfKnown(): Long = if (hasCharacteristics(SIZED)) estimateSize() else -1L - @JavaDefaultMethod def hasCharacteristics(chars: Int): Boolean = (characteristics() & chars) == chars diff --git a/javalib/src/main/scala/java/util/TreeMap.scala b/javalib/src/main/scala/java/util/TreeMap.scala new file mode 100644 index 0000000000..337c141656 --- /dev/null +++ b/javalib/src/main/scala/java/util/TreeMap.scala @@ -0,0 +1,730 @@ +// Ported from Scala.js commit def516f dated: 2023-01-22 + +package java.util + +import java.lang.Cloneable +import java.util.{RedBlackTree => RB} +import java.util.function.{Function, BiFunction} + +class TreeMap[K, V] private (tree: RB.Tree[K, V])(implicit + comp: Comparator[_ >: K] +) extends AbstractMap[K, V] + with NavigableMap[K, V] + with Cloneable + with Serializable { + + def this() = this(RB.Tree.empty[K, V])(NaturalComparator) + + def this(comparator: Comparator[_ >: K]) = + this(RB.Tree.empty[K, V])(NaturalComparator.select(comparator)) + + def this(m: Map[K, V]) = { + this() + putAll(m) + } + + def this(m: SortedMap[K, V]) = { + this(RB.fromOrderedEntries(m.entrySet().iterator(), m.size()))( + NaturalComparator.select(m.comparator()) + ) + } + + override def size(): Int = RB.size(tree) + + override def containsKey(key: Any): Boolean = RB.contains(tree, key) + + override def containsValue(value: Any): Boolean = { + // scalastyle:off return + val iter = RB.valuesIterator(tree) + while (iter.hasNext()) { + if (Objects.equals(value, iter.next())) + return true + } + false + // scalastyle:on return + } + + override def get(key: Any): V = RB.get(tree, key) + + def comparator(): Comparator[_ >: K] = + NaturalComparator.unselect(comp) + + def firstKey(): K = { + if (isEmpty()) + throw new NoSuchElementException() + RB.minKey(tree) + } + + def lastKey(): K = { + if (isEmpty()) + throw new NoSuchElementException() + RB.maxKey(tree) + } + + override def putAll(map: Map[_ <: K, _ <: V]): Unit = + map.forEach((k, v) => put(k, v)) + + override def put(key: K, value: V): V = + RB.insert(tree, key, value) + + override def computeIfAbsent( + key: K, + mappingFunction: Function[_ >: K, _ <: V] + ): V = { + val node = RB.getNode(tree, key) + + if (node eq null) { + val newValue = mappingFunction(key) + if (newValue != null) + put(key, newValue) + newValue + } else if (node.getValue() == null) { + val newValue = mappingFunction(key) + if (newValue != null) + updateNodeValue(node, newValue) + newValue + } else { + node.getValue() + } + } + + override def computeIfPresent( + key: K, + remappingFunction: BiFunction[_ >: K, _ >: V, _ <: V] + ): V = { + val node = RB.getNode(tree, key) + if ((node ne null) && node.getValue() != null) + updateNodeValue(node, remappingFunction(key, node.getValue())) + else + null.asInstanceOf[V] + } + + override def compute( + key: K, + remappingFunction: BiFunction[_ >: K, _ >: V, _ <: V] + ): V = { + val node = RB.getNode(tree, key) + if (node eq null) { + val newValue = remappingFunction(key, null.asInstanceOf[V]) + if (newValue != null) + put(key, newValue) + newValue + } else { + updateNodeValue(node, remappingFunction(key, node.getValue())) + } + } + + override def merge( + key: K, + value: V, + remappingFunction: BiFunction[_ >: V, _ >: V, _ <: V] + ): V = { + Objects.requireNonNull(value) + + val node = RB.getNode(tree, key) + if (node eq null) { + put(key, value) + value + } else { + val oldValue = node.getValue() + val newValue = + if (oldValue == null) value + else remappingFunction(oldValue, value) + + updateNodeValue(node, newValue) + } + } + + /** Common code for functions above. + * + * - Sets value to newValue if it is non-null + * - deletes the node if newValue is null. + * + * @returns + * newValue + */ + private def updateNodeValue(node: RB.Node[K, V], newValue: V): V = { + if (newValue == null) + RB.deleteNode(tree, node) + else + node.setValue(newValue) + newValue + } + + override def remove(key: Any): V = + RB.nullableNodeValue(RB.delete(tree, key)) + + override def clear(): Unit = RB.clear(tree) + + override def clone(): Object = new TreeMap(tree.treeCopy())(comp) + + def firstEntry(): Map.Entry[K, V] = RB.minNode(tree) + + def lastEntry(): Map.Entry[K, V] = RB.maxNode(tree) + + def pollFirstEntry(): Map.Entry[K, V] = { + val node = RB.minNode(tree) + if (node ne null) + RB.deleteNode(tree, node) + node + } + + def pollLastEntry(): Map.Entry[K, V] = { + val node = RB.maxNode(tree) + if (node ne null) + RB.deleteNode(tree, node) + node + } + + def lowerEntry(key: K): Map.Entry[K, V] = + RB.maxNodeBefore(tree, key, RB.ExclusiveBound) + + def lowerKey(key: K): K = + RB.maxKeyBefore(tree, key, RB.ExclusiveBound) + + def floorEntry(key: K): Map.Entry[K, V] = + RB.maxNodeBefore(tree, key, RB.InclusiveBound) + + def floorKey(key: K): K = + RB.maxKeyBefore(tree, key, RB.InclusiveBound) + + def ceilingEntry(key: K): Map.Entry[K, V] = + RB.minNodeAfter(tree, key, RB.InclusiveBound) + + def ceilingKey(key: K): K = + RB.minKeyAfter(tree, key, RB.InclusiveBound) + + def higherEntry(key: K): Map.Entry[K, V] = + RB.minNodeAfter(tree, key, RB.ExclusiveBound) + + def higherKey(key: K): K = + RB.minKeyAfter(tree, key, RB.ExclusiveBound) + + override def keySet(): Set[K] = navigableKeySet() + + def navigableKeySet(): NavigableSet[K] = { + new TreeSet.Projection( + tree, + null.asInstanceOf[K], + RB.NoBound, + null.asInstanceOf[K], + RB.NoBound, + null.asInstanceOf[V] + ) + } + + def descendingKeySet(): NavigableSet[K] = { + new TreeSet.DescendingProjection( + tree, + null.asInstanceOf[K], + RB.NoBound, + null.asInstanceOf[K], + RB.NoBound, + null.asInstanceOf[V] + ) + } + + override def values(): Collection[V] = new AbstractCollection[V] { + def iterator(): Iterator[V] = RB.valuesIterator(tree) + + def size(): Int = RB.size(tree) + + override def contains(o: Any): Boolean = containsValue(o) + + override def clear(): Unit = RB.clear(tree) + } + + def entrySet(): Set[Map.Entry[K, V]] = { + new TreeMap.ProjectedEntrySet( + tree, + null.asInstanceOf[K], + RB.NoBound, + null.asInstanceOf[K], + RB.NoBound + ) + } + + def descendingMap(): NavigableMap[K, V] = { + new TreeMap.DescendingProjection( + tree, + null.asInstanceOf[K], + RB.NoBound, + null.asInstanceOf[K], + RB.NoBound + ) + } + + def subMap( + fromKey: K, + fromInclusive: Boolean, + toKey: K, + toInclusive: Boolean + ): NavigableMap[K, V] = { + new TreeMap.Projection( + tree, + fromKey, + RB.boundKindFromIsInclusive(fromInclusive), + toKey, + RB.boundKindFromIsInclusive(toInclusive) + ) + } + + def headMap(toKey: K, inclusive: Boolean): NavigableMap[K, V] = { + new TreeMap.Projection( + tree, + null.asInstanceOf[K], + RB.NoBound, + toKey, + RB.boundKindFromIsInclusive(inclusive) + ) + } + + def tailMap(fromKey: K, inclusive: Boolean): NavigableMap[K, V] = { + new TreeMap.Projection( + tree, + fromKey, + RB.boundKindFromIsInclusive(inclusive), + null.asInstanceOf[K], + RB.NoBound + ) + } + + def subMap(fromKey: K, toKey: K): SortedMap[K, V] = + subMap(fromKey, true, toKey, false) + + def headMap(toKey: K): SortedMap[K, V] = + headMap(toKey, false) + + def tailMap(fromKey: K): SortedMap[K, V] = + tailMap(fromKey, true) +} + +private object TreeMap { + private class ProjectedEntrySet[K, V]( + tree: RB.Tree[K, V], + lowerBound: K, + lowerKind: RB.BoundKind, + upperBound: K, + upperKind: RB.BoundKind + )(implicit protected val comp: Comparator[_ >: K]) + extends AbstractSet[Map.Entry[K, V]] { + + def iterator(): Iterator[Map.Entry[K, V]] = + RB.projectionIterator(tree, lowerBound, lowerKind, upperBound, upperKind) + + def size(): Int = + RB.projectionSize(tree, lowerBound, lowerKind, upperBound, upperKind) + + override def contains(o: Any): Boolean = o match { + case o: Map.Entry[_, _] if isWithinBounds(o.getKey()) => + val node = RB.getNode(tree, o.getKey()) + (node ne null) && Objects.equals(node.getValue(), o.getValue()) + case _ => + false + } + + override def remove(o: Any): Boolean = o match { + case o: Map.Entry[_, _] if isWithinBounds(o.getKey()) => + val node = RB.getNode(tree, o.getKey()) + if ((node ne null) && Objects.equals(node.getValue(), o.getValue())) { + RB.deleteNode(tree, node) + true + } else { + false + } + case _ => + false + } + + private def isWithinBounds(key: Any): Boolean = + RB.isWithinLowerBound(key, lowerBound, lowerKind) && RB + .isWithinUpperBound(key, upperBound, upperKind) + } + + private abstract class AbstractProjection[K, V]( + protected val tree: RB.Tree[K, V], + protected val lowerBound: K, + protected val lowerKind: RB.BoundKind, + protected val upperBound: K, + protected val upperKind: RB.BoundKind + )(implicit protected val comp: Comparator[_ >: K]) + extends AbstractMap[K, V] + with NavigableMap[K, V] { + + // To be implemented by the two concrete subclasses, depending on the order + + protected def nextNode(key: K, boundKind: RB.BoundKind): RB.Node[K, V] + protected def previousNode(key: K, boundKind: RB.BoundKind): RB.Node[K, V] + + protected def subMapGeneric( + newFromKey: K = null.asInstanceOf[K], + newFromBoundKind: RB.BoundKind = RB.NoBound, + newToKey: K = null.asInstanceOf[K], + newToBoundKind: RB.BoundKind = RB.NoBound + ): NavigableMap[K, V] + + // Implementation of most of the NavigableMap API + + override def size(): Int = + RB.projectionSize(tree, lowerBound, lowerKind, upperBound, upperKind) + + override def isEmpty(): Boolean = + RB.projectionIsEmpty(tree, lowerBound, lowerKind, upperBound, upperKind) + + override def containsKey(key: Any): Boolean = + isWithinBounds(key) && RB.contains(tree, key) + + override def get(key: Any): V = { + if (!isWithinBounds(key)) + null.asInstanceOf[V] + else + RB.get(tree, key) + } + + override def put(key: K, value: V): V = { + if (!isWithinBounds(key)) + throw new IllegalArgumentException + RB.insert(tree, key, value) + } + + override def remove(key: Any): V = { + val oldNode = + if (isWithinBounds(key)) RB.delete(tree, key) + else null + RB.nullableNodeValue(oldNode) + } + + def entrySet(): Set[Map.Entry[K, V]] = + new ProjectedEntrySet(tree, lowerBound, lowerKind, upperBound, upperKind) + + def lowerEntry(key: K): Map.Entry[K, V] = + previousNode(key, RB.ExclusiveBound) + + def lowerKey(key: K): K = + RB.nullableNodeKey(previousNode(key, RB.ExclusiveBound)) + + def floorEntry(key: K): Map.Entry[K, V] = + previousNode(key, RB.InclusiveBound) + + def floorKey(key: K): K = + RB.nullableNodeKey(previousNode(key, RB.InclusiveBound)) + + def ceilingEntry(key: K): Map.Entry[K, V] = + nextNode(key, RB.InclusiveBound) + + def ceilingKey(key: K): K = + RB.nullableNodeKey(nextNode(key, RB.InclusiveBound)) + + def higherEntry(key: K): Map.Entry[K, V] = + nextNode(key, RB.ExclusiveBound) + + def higherKey(key: K): K = + RB.nullableNodeKey(nextNode(key, RB.ExclusiveBound)) + + def firstKey(): K = { + val e = firstEntry() + if (e eq null) + throw new NoSuchElementException + e.getKey() + } + + def lastKey(): K = { + val e = lastEntry() + if (e eq null) + throw new NoSuchElementException + e.getKey() + } + + def subMap( + fromKey: K, + fromInclusive: Boolean, + toKey: K, + toInclusive: Boolean + ): NavigableMap[K, V] = { + subMapGeneric( + fromKey, + RB.boundKindFromIsInclusive(fromInclusive), + toKey, + RB.boundKindFromIsInclusive(toInclusive) + ) + } + + def headMap(toKey: K, inclusive: Boolean): NavigableMap[K, V] = { + subMapGeneric( + newToKey = toKey, + newToBoundKind = RB.boundKindFromIsInclusive(inclusive) + ) + } + + def tailMap(fromKey: K, inclusive: Boolean): NavigableMap[K, V] = { + subMapGeneric( + newFromKey = fromKey, + newFromBoundKind = RB.boundKindFromIsInclusive(inclusive) + ) + } + + def subMap(fromKey: K, toKey: K): SortedMap[K, V] = + subMap(fromKey, true, toKey, false) + + def headMap(toKey: K): SortedMap[K, V] = + headMap(toKey, false) + + def tailMap(fromKey: K): SortedMap[K, V] = + tailMap(fromKey, true) + + // Common implementation of pollFirstEntry() and pollLastEntry() + + @inline + protected final def pollLowerEntry(): Map.Entry[K, V] = { + val node = RB.minNodeAfter(tree, lowerBound, lowerKind) + if (node ne null) { + if (isWithinUpperBound(node.key)) { + RB.deleteNode(tree, node) + node + } else { + null + } + } else { + null + } + } + + @inline + protected final def pollUpperEntry(): Map.Entry[K, V] = { + val node = RB.maxNodeBefore(tree, upperBound, upperKind) + if (node ne null) { + if (isWithinLowerBound(node.key)) { + RB.deleteNode(tree, node) + node + } else { + null + } + } else { + null + } + } + + // Helpers + + protected final def isWithinBounds(key: Any): Boolean = + isWithinLowerBound(key) && isWithinUpperBound(key) + + protected final def isWithinLowerBound(key: Any): Boolean = + RB.isWithinLowerBound(key, lowerBound, lowerKind) + + protected final def isWithinUpperBound(key: Any): Boolean = + RB.isWithinUpperBound(key, upperBound, upperKind) + + protected final def ifWithinLowerBound(node: RB.Node[K, V]): RB.Node[K, V] = + if (node != null && isWithinLowerBound(node.key)) node + else null + + protected final def ifWithinUpperBound(node: RB.Node[K, V]): RB.Node[K, V] = + if (node != null && isWithinUpperBound(node.key)) node + else null + } + + private final class Projection[K, V]( + tree0: RB.Tree[K, V], + fromKey0: K, + fromBoundKind0: RB.BoundKind, + toKey0: K, + toBoundKind0: RB.BoundKind + )(implicit comp: Comparator[_ >: K]) + extends AbstractProjection[K, V]( + tree0, + fromKey0, + fromBoundKind0, + toKey0, + toBoundKind0 + ) { + + // Access fields under a different name, more appropriate for some uses + + @inline private def fromKey: K = lowerBound + @inline private def fromBoundKind: RB.BoundKind = lowerKind + @inline private def toKey: K = upperBound + @inline private def toBoundKind: RB.BoundKind = upperKind + + /* Implementation of the abstract methods from AbstractProjection + * Some are marked `@inline` for the likely case where + * `DescendingProjection` is not reachable at all and hence + * dead-code-eliminated. + */ + + @inline + protected def nextNode(key: K, boundKind: RB.BoundKind): RB.Node[K, V] = + ifWithinUpperBound(RB.minNodeAfter(tree, key, boundKind)) + + @inline + protected def previousNode(key: K, boundKind: RB.BoundKind): RB.Node[K, V] = + ifWithinLowerBound(RB.maxNodeBefore(tree, key, boundKind)) + + protected def subMapGeneric( + newFromKey: K, + newFromBoundKind: RB.BoundKind, + newToKey: K, + newToBoundKind: RB.BoundKind + ): NavigableMap[K, V] = { + val intersectedFromBound = RB.intersectLowerBounds( + new RB.Bound(fromKey, fromBoundKind), + new RB.Bound(newFromKey, newFromBoundKind) + ) + val intersectedToBound = RB.intersectUpperBounds( + new RB.Bound(toKey, toBoundKind), + new RB.Bound(newToKey, newToBoundKind) + ) + new Projection( + tree, + intersectedFromBound.bound, + intersectedFromBound.kind, + intersectedToBound.bound, + intersectedToBound.kind + ) + } + + // Methods of the NavigableMap API that are not implemented in AbstractProjection + + def comparator(): Comparator[_ >: K] = + NaturalComparator.unselect(comp) + + def firstEntry(): Map.Entry[K, V] = + nextNode(fromKey, fromBoundKind) + + def lastEntry(): Map.Entry[K, V] = + previousNode(toKey, toBoundKind) + + @noinline + def pollFirstEntry(): Map.Entry[K, V] = + pollLowerEntry() + + @noinline + def pollLastEntry(): Map.Entry[K, V] = + pollUpperEntry() + + def navigableKeySet(): NavigableSet[K] = { + new TreeSet.Projection( + tree, + fromKey, + fromBoundKind, + toKey, + toBoundKind, + null.asInstanceOf[V] + ) + } + + def descendingKeySet(): NavigableSet[K] = { + new TreeSet.DescendingProjection( + tree, + toKey, + toBoundKind, + fromKey, + fromBoundKind, + null.asInstanceOf[V] + ) + } + + def descendingMap(): NavigableMap[K, V] = { + new DescendingProjection(tree, toKey, toBoundKind, fromKey, fromBoundKind) + } + } + + private final class DescendingProjection[K, V]( + tree0: RB.Tree[K, V], + fromKey0: K, + fromBoundKind0: RB.BoundKind, + toKey0: K, + toBoundKind0: RB.BoundKind + )(implicit comp: Comparator[_ >: K]) + extends AbstractProjection[K, V]( + tree0, + toKey0, + toBoundKind0, + fromKey0, + fromBoundKind0 + ) { + + // Access fields under a different name, more appropriate for some uses + + @inline private def fromKey: K = upperBound + @inline private def fromBoundKind: RB.BoundKind = upperKind + @inline private def toKey: K = lowerBound + @inline private def toBoundKind: RB.BoundKind = lowerKind + + // Implementation of the abstract methods from AbstractProjection + + protected def nextNode(key: K, boundKind: RB.BoundKind): RB.Node[K, V] = + ifWithinLowerBound(RB.maxNodeBefore(tree, key, boundKind)) + + protected def previousNode(key: K, boundKind: RB.BoundKind): RB.Node[K, V] = + ifWithinUpperBound(RB.minNodeAfter(tree, key, boundKind)) + + protected def subMapGeneric( + newFromKey: K, + newFromBoundKind: RB.BoundKind, + newToKey: K, + newToBoundKind: RB.BoundKind + ): NavigableMap[K, V] = { + val intersectedFromBound = RB.intersectUpperBounds( + new RB.Bound(fromKey, fromBoundKind), + new RB.Bound(newFromKey, newFromBoundKind) + ) + val intersectedToBound = RB.intersectLowerBounds( + new RB.Bound(toKey, toBoundKind), + new RB.Bound(newToKey, newToBoundKind) + ) + new Projection( + tree, + intersectedFromBound.bound, + intersectedFromBound.kind, + intersectedToBound.bound, + intersectedToBound.kind + ) + } + + // Methods of the NavigableMap API that are not implemented in AbstractProjection + + def comparator(): Comparator[_ >: K] = + Collections.reverseOrder(NaturalComparator.unselect(comp)) + + def firstEntry(): Map.Entry[K, V] = + nextNode(fromKey, fromBoundKind) + + def lastEntry(): Map.Entry[K, V] = + previousNode(toKey, toBoundKind) + + @noinline + def pollFirstEntry(): Map.Entry[K, V] = + pollUpperEntry() + + @noinline + def pollLastEntry(): Map.Entry[K, V] = + pollLowerEntry() + + def navigableKeySet(): NavigableSet[K] = { + new TreeSet.DescendingProjection( + tree, + fromKey, + fromBoundKind, + toKey, + toBoundKind, + null.asInstanceOf[V] + ) + } + + def descendingKeySet(): NavigableSet[K] = { + new TreeSet.Projection( + tree, + toKey, + toBoundKind, + fromKey, + fromBoundKind, + null.asInstanceOf[V] + ) + } + + def descendingMap(): NavigableMap[K, V] = { + new Projection(tree, toKey, toBoundKind, fromKey, fromBoundKind) + } + } +} diff --git a/javalib/src/main/scala/java/util/TreeSet.scala b/javalib/src/main/scala/java/util/TreeSet.scala index 2d22870fc9..e22a3e22bc 100644 --- a/javalib/src/main/scala/java/util/TreeSet.scala +++ b/javalib/src/main/scala/java/util/TreeSet.scala @@ -1,4 +1,5 @@ -// Ported from Scala.js, revision: 730ee11, dated 9 Aug 2019 +// Ported from Scala.js commit def516f dated: 2023-01-22 + /* * Scala.js (https://www.scala-js.org/) * @@ -59,7 +60,8 @@ class TreeSet[E] private (tree: RB.Tree[E, Any])(implicit null.asInstanceOf[E], RB.NoBound, null.asInstanceOf[E], - RB.NoBound + RB.NoBound, + () ) } @@ -92,7 +94,8 @@ class TreeSet[E] private (tree: RB.Tree[E, Any])(implicit fromElement, RB.boundKindFromIsInclusive(fromInclusive), toElement, - RB.boundKindFromIsInclusive(toInclusive) + RB.boundKindFromIsInclusive(toInclusive), + () ) } @@ -102,7 +105,8 @@ class TreeSet[E] private (tree: RB.Tree[E, Any])(implicit null.asInstanceOf[E], RB.NoBound, toElement, - RB.boundKindFromIsInclusive(inclusive) + RB.boundKindFromIsInclusive(inclusive), + () ) } @@ -112,7 +116,8 @@ class TreeSet[E] private (tree: RB.Tree[E, Any])(implicit fromElement, RB.boundKindFromIsInclusive(inclusive), null.asInstanceOf[E], - RB.NoBound + RB.NoBound, + () ) } @@ -176,13 +181,14 @@ class TreeSet[E] private (tree: RB.Tree[E, Any])(implicit new TreeSet(tree.treeCopy())(comp) } -private object TreeSet { - private abstract class AbstractProjection[E]( - protected val tree: RB.Tree[E, Any], +private[util] object TreeSet { + private[util] abstract class AbstractProjection[E, V]( + protected val tree: RB.Tree[E, V], protected val lowerBound: E, protected val lowerKind: RB.BoundKind, protected val upperBound: E, - protected val upperKind: RB.BoundKind + protected val upperKind: RB.BoundKind, + private val valueForAdd: V )(implicit protected val comp: Comparator[_ >: E]) extends AbstractSet[E] with NavigableSet[E] { @@ -211,9 +217,11 @@ private object TreeSet { isWithinBounds(o) && RB.contains(tree, o) override def add(e: E): Boolean = { + if (valueForAdd == null) + throw new UnsupportedOperationException if (!isWithinBounds(e)) throw new IllegalArgumentException - RB.insert(tree, e, ()) == null + RB.insert(tree, e, valueForAdd) == null } override def remove(o: Any): Boolean = @@ -322,19 +330,21 @@ private object TreeSet { else null.asInstanceOf[E] } - private final class Projection[E]( - tree0: RB.Tree[E, Any], + private[util] final class Projection[E, V]( + tree0: RB.Tree[E, V], fromElement0: E, fromBoundKind0: RB.BoundKind, toElement0: E, - toBoundKind0: RB.BoundKind + toBoundKind0: RB.BoundKind, + valueForAdd: V )(implicit comp: Comparator[_ >: E]) - extends AbstractProjection[E]( + extends AbstractProjection[E, V]( tree0, fromElement0, fromBoundKind0, toElement0, - toBoundKind0 + toBoundKind0, + valueForAdd ) { // Access fields under a different name, more appropriate for some uses @@ -377,7 +387,8 @@ private object TreeSet { intersectedFromBound.bound, intersectedFromBound.kind, intersectedToBound.bound, - intersectedToBound.kind + intersectedToBound.kind, + valueForAdd ) } @@ -423,7 +434,8 @@ private object TreeSet { toElement, toBoundKind, fromElement, - fromBoundKind + fromBoundKind, + valueForAdd ) def descendingIterator(): Iterator[E] = @@ -436,19 +448,21 @@ private object TreeSet { ) } - private final class DescendingProjection[E]( - tree0: RB.Tree[E, Any], + private[util] final class DescendingProjection[E, V]( + tree0: RB.Tree[E, V], fromElement0: E, fromBoundKind0: RB.BoundKind, toElement0: E, - toBoundKind0: RB.BoundKind + toBoundKind0: RB.BoundKind, + valueForAdd: V )(implicit comp: Comparator[_ >: E]) - extends AbstractProjection[E]( + extends AbstractProjection[E, V]( tree0, toElement0, toBoundKind0, fromElement0, - fromBoundKind0 + fromBoundKind0, + valueForAdd ) { // Access fields under a different name, more appropriate for some uses @@ -485,7 +499,8 @@ private object TreeSet { intersectedFromBound.bound, intersectedFromBound.kind, intersectedToBound.bound, - intersectedToBound.kind + intersectedToBound.kind, + valueForAdd ) } @@ -526,7 +541,14 @@ private object TreeSet { pollLower() def descendingSet(): NavigableSet[E] = - new Projection(tree, toElement, toBoundKind, fromElement, fromBoundKind) + new Projection( + tree, + toElement, + toBoundKind, + fromElement, + fromBoundKind, + valueForAdd + ) def descendingIterator(): Iterator[E] = RB.projectionKeysIterator( diff --git a/javalib/src/main/scala/java/util/concurrent/CountDownLatch.scala b/javalib/src/main/scala/java/util/concurrent/CountDownLatch.scala new file mode 100644 index 0000000000..c525b3648c --- /dev/null +++ b/javalib/src/main/scala/java/util/concurrent/CountDownLatch.scala @@ -0,0 +1,13 @@ +package java.util.concurrent + +// No-op stub defined to allow usage of lazy vals with -Ylightweight-lazy-vals +class CountDownLatch(count: Int) { + + def await(): Unit = () + + def await(timeout: Long, unit: TimeUnit): Boolean = true + + def countDown(): Unit = () + + def getCount(): Long = 0L +} diff --git a/javalib/src/main/scala/java/util/concurrent/Flow.scala b/javalib/src/main/scala/java/util/concurrent/Flow.scala new file mode 100644 index 0000000000..0199dee3ca --- /dev/null +++ b/javalib/src/main/scala/java/util/concurrent/Flow.scala @@ -0,0 +1,30 @@ +// Ported from Scala.js commit: fb20d6f dated: 2023-01-20 + +package java.util.concurrent + +import scalanative.annotation.alwaysinline + +object Flow { + + @alwaysinline def defaultBufferSize(): Int = 256 + + trait Processor[T, R] extends Subscriber[T] with Publisher[R] + + @FunctionalInterface + trait Publisher[T] { + def subscribe(subscriber: Subscriber[_ >: T]): Unit + } + + trait Subscriber[T] { + def onSubscribe(subscription: Subscription): Unit + def onNext(item: T): Unit + def onError(throwable: Throwable): Unit + def onComplete(): Unit + } + + trait Subscription { + def request(n: Long): Unit + def cancel(): Unit + } + +} diff --git a/javalib/src/main/scala/java/util/function/BiConsumer.scala b/javalib/src/main/scala/java/util/function/BiConsumer.scala index 77f69cfb85..04d0e1a5ea 100644 --- a/javalib/src/main/scala/java/util/function/BiConsumer.scala +++ b/javalib/src/main/scala/java/util/function/BiConsumer.scala @@ -1,16 +1,12 @@ -// Corresponds to Scala.js commit: f86ed6 c2f5a43 dated: 2020-09-06 -// Design note: Do not use lambdas with Scala Native and Scala 2.11 +// Ported from Scala.js commit: f86ed6 c2f5a43 dated: 2020-09-06 package java.util.function -import scala.scalanative.annotation.JavaDefaultMethod - trait BiConsumer[T, U] { self => def accept(t: T, u: U): Unit - @JavaDefaultMethod def andThen(after: BiConsumer[T, U]): BiConsumer[T, U] = new BiConsumer[T, U]() { override def accept(t: T, u: U): Unit = { diff --git a/javalib/src/main/scala/java/util/function/BiFunction.scala b/javalib/src/main/scala/java/util/function/BiFunction.scala index c4007e2d0a..7eabfeb804 100644 --- a/javalib/src/main/scala/java/util/function/BiFunction.scala +++ b/javalib/src/main/scala/java/util/function/BiFunction.scala @@ -1,14 +1,10 @@ -// Corresponds to Scala.js commit: d3a9711 dated: 2020-09-06 -// Design note: Do not use lambdas with Scala Native and Scala 2.11 +// Ported from Scala.js commit: d3a9711 dated: 2020-09-06 package java.util.function -import scala.scalanative.annotation.JavaDefaultMethod - trait BiFunction[T, U, R] { self => def apply(t: T, u: U): R - @JavaDefaultMethod def andThen[V](after: Function[_ >: R, _ <: V]): BiFunction[T, U, V] = { new BiFunction[T, U, V] { def apply(t: T, u: U): V = after.apply(self.apply(t, u)) diff --git a/javalib/src/main/scala/java/util/function/BiPredicate.scala b/javalib/src/main/scala/java/util/function/BiPredicate.scala index f9df09dbca..bc160335d6 100644 --- a/javalib/src/main/scala/java/util/function/BiPredicate.scala +++ b/javalib/src/main/scala/java/util/function/BiPredicate.scala @@ -1,27 +1,22 @@ -// Influenced by Scala.js commit: 0c27b64 dated: 2020-09-06 +// Ported from Scala.js commit: 0c27b64 dated: 2020-09-06 package java.util.function -import scala.scalanative.annotation.JavaDefaultMethod - trait BiPredicate[T, U] { self => def test(t: T, u: U): Boolean - @JavaDefaultMethod def and(other: BiPredicate[_ >: T, _ >: U]): BiPredicate[T, U] = new BiPredicate[T, U] { override def test(t: T, u: U): Boolean = self.test(t, u) && other.test(t, u) } - @JavaDefaultMethod def negate(): BiPredicate[T, U] = new BiPredicate[T, U] { override def test(t: T, u: U): Boolean = !self.test(t, u) } - @JavaDefaultMethod def or(other: BiPredicate[_ >: T, _ >: U]): BiPredicate[T, U] = new BiPredicate[T, U] { override def test(t: T, u: U): Boolean = diff --git a/javalib/src/main/scala/java/util/function/Consumer.scala b/javalib/src/main/scala/java/util/function/Consumer.scala index 6aa42c1120..5a093ad3e7 100644 --- a/javalib/src/main/scala/java/util/function/Consumer.scala +++ b/javalib/src/main/scala/java/util/function/Consumer.scala @@ -1,11 +1,8 @@ package java.util.function -import scala.scalanative.annotation.JavaDefaultMethod - trait Consumer[T] { self => def accept(t: T): Unit - @JavaDefaultMethod def andThen(after: Consumer[T]): Consumer[T] = new Consumer[T]() { def accept(t: T): Unit = { self.accept(t) diff --git a/javalib/src/main/scala/java/util/function/Function.scala b/javalib/src/main/scala/java/util/function/Function.scala index ef562c5c7b..9abd8c367e 100644 --- a/javalib/src/main/scala/java/util/function/Function.scala +++ b/javalib/src/main/scala/java/util/function/Function.scala @@ -1,22 +1,15 @@ -// Influenced Scala.js commit: eb637e3 dated: 2020-09-06 -// -// Design Note: Once Scala Native no longer supports Scala 2.11, -// OK to use Scala.js code with lambdas. +// Ported from Scala.js commit: eb637e3 dated: 2020-09-06 package java.util.function -import scala.scalanative.annotation.JavaDefaultMethod - trait Function[T, R] { self => def apply(t: T): R - @JavaDefaultMethod def andThen[V](after: Function[_ >: R, _ <: V]): Function[T, V] = new Function[T, V] { override def apply(t: T): V = after.apply(self.apply(t)) } - @JavaDefaultMethod def compose[V](before: Function[_ >: V, _ <: T]): Function[V, R] = new Function[V, R] { override def apply(v: V): R = self.apply(before.apply(v)) diff --git a/javalib/src/main/scala/java/util/function/IntUnaryOperator.scala b/javalib/src/main/scala/java/util/function/IntUnaryOperator.scala index b57456edb7..821195415a 100644 --- a/javalib/src/main/scala/java/util/function/IntUnaryOperator.scala +++ b/javalib/src/main/scala/java/util/function/IntUnaryOperator.scala @@ -2,18 +2,14 @@ package java.util.function -import scala.scalanative.annotation.JavaDefaultMethod - @FunctionalInterface trait IntUnaryOperator { def applyAsInt(operand: Int): Int - @JavaDefaultMethod def andThen(after: IntUnaryOperator): IntUnaryOperator = { (i: Int) => after.applyAsInt(applyAsInt(i)) } - @JavaDefaultMethod def compose(before: IntUnaryOperator): IntUnaryOperator = { (i: Int) => applyAsInt(before.applyAsInt(i)) } diff --git a/javalib/src/main/scala/java/util/function/Predicate.scala b/javalib/src/main/scala/java/util/function/Predicate.scala index eaaf708d7d..8524858a9d 100644 --- a/javalib/src/main/scala/java/util/function/Predicate.scala +++ b/javalib/src/main/scala/java/util/function/Predicate.scala @@ -4,13 +4,10 @@ package java.util.function import java.{util => ju} -import scala.scalanative.annotation.JavaDefaultMethod - @FunctionalInterface trait Predicate[T] { self => def test(t: T): Boolean - @JavaDefaultMethod def and(other: Predicate[_ >: T]): Predicate[T] = { new Predicate[T] { def test(t: T): Boolean = @@ -18,7 +15,6 @@ trait Predicate[T] { self => } } - @JavaDefaultMethod def negate(): Predicate[T] = { new Predicate[T] { def test(t: T): Boolean = @@ -26,7 +22,6 @@ trait Predicate[T] { self => } } - @JavaDefaultMethod def or(other: Predicate[_ >: T]): Predicate[T] = { new Predicate[T] { def test(t: T): Boolean = diff --git a/javalib/src/main/scala/java/util/function/ToDoubleFunction.scala b/javalib/src/main/scala/java/util/function/ToDoubleFunction.scala new file mode 100644 index 0000000000..5dbc90aed0 --- /dev/null +++ b/javalib/src/main/scala/java/util/function/ToDoubleFunction.scala @@ -0,0 +1,8 @@ +// Ported from Scala.js commit 00e462d dated: 2023-01-22 + +package java.util.function + +@FunctionalInterface +trait ToDoubleFunction[T] { + def applyAsDouble(t: T): Double +} diff --git a/javalib/src/main/scala/java/util/function/ToIntFunction.scala b/javalib/src/main/scala/java/util/function/ToIntFunction.scala new file mode 100644 index 0000000000..b55440d307 --- /dev/null +++ b/javalib/src/main/scala/java/util/function/ToIntFunction.scala @@ -0,0 +1,8 @@ +// Ported from Scala.js commit 00e462d dated: 2023-01-22 + +package java.util.function + +@FunctionalInterface +trait ToIntFunction[T] { + def applyAsInt(t: T): Int +} diff --git a/javalib/src/main/scala/java/util/function/ToLongFunction.scala b/javalib/src/main/scala/java/util/function/ToLongFunction.scala new file mode 100644 index 0000000000..b1847833ac --- /dev/null +++ b/javalib/src/main/scala/java/util/function/ToLongFunction.scala @@ -0,0 +1,8 @@ +// Ported from Scala.js commit 00e462d dated: 2023-01-22 + +package java.util.function + +@FunctionalInterface +trait ToLongFunction[T] { + def applyAsLong(t: T): Long +} diff --git a/junit-plugin/src/main/scala-2/scala/scalanative/junit/plugin/ScalaNativeJUnitPlugin.scala b/junit-plugin/src/main/scala-2/scala/scalanative/junit/plugin/ScalaNativeJUnitPlugin.scala index d27081bf31..b8955575c2 100644 --- a/junit-plugin/src/main/scala-2/scala/scalanative/junit/plugin/ScalaNativeJUnitPlugin.scala +++ b/junit-plugin/src/main/scala-2/scala/scalanative/junit/plugin/ScalaNativeJUnitPlugin.scala @@ -145,14 +145,16 @@ class ScalaNativeJUnitPlugin(val global: Global) extends NscPlugin { genCallOnModule( bootSym, Names.beforeClass, - testClass.companionModule, - JUnitAnnots.BeforeClass + testClass, + JUnitAnnots.BeforeClass, + callParentsFirst = true ), genCallOnModule( bootSym, Names.afterClass, - testClass.companionModule, - JUnitAnnots.AfterClass + testClass, + JUnitAnnots.AfterClass, + callParentsFirst = false ), genCallOnParam(bootSym, Names.before, testClass, JUnitAnnots.Before), genCallOnParam(bootSym, Names.after, testClass, JUnitAnnots.After), @@ -186,16 +188,28 @@ class ScalaNativeJUnitPlugin(val global: Global) extends NscPlugin { private def genCallOnModule( owner: ClassSymbol, name: TermName, - module: Symbol, - annot: Symbol + testClass: Symbol, + annot: Symbol, + callParentsFirst: Boolean ): DefDef = { val sym = owner.newMethodSymbol(name) sym.setInfoAndEnter(MethodType(Nil, definitions.UnitTpe)) + val symbols = { + val all = (testClass :: testClass.ancestors) + if (callParentsFirst) all.reverse + else all + } + + // Filter out annotations found in the companion of trait for compliance with the JVM val (publicCalls, nonPublicCalls) = - annotatedMethods(module, annot).partition(_.isPublic) + symbols + .filterNot(_.isTraitOrInterface) + .flatMap(sym => annotatedMethods(sym.companionModule, annot)) + .partition(_.isPublic) if (nonPublicCalls.nonEmpty) { + val module = testClass.companionModule.orElse(testClass) globalError( pos = module.pos, s"Methods marked with ${annot.nameString} annotation in $module must be public" @@ -203,7 +217,7 @@ class ScalaNativeJUnitPlugin(val global: Global) extends NscPlugin { } val calls = publicCalls - .map(gen.mkMethodCall(Ident(module), _, Nil, Nil)) + .map(gen.mkMethodCall(_, Nil, Nil)) .toList typer.typedDefDef(newDefDef(sym, Block(calls: _*))()) diff --git a/junit-plugin/src/main/scala-3/scala/scalanative/junit/plugin/ScalaNativeJUnitBootstrappers.scala b/junit-plugin/src/main/scala-3/scala/scalanative/junit/plugin/ScalaNativeJUnitBootstrappers.scala index c0d91636bf..f2f478499e 100644 --- a/junit-plugin/src/main/scala-3/scala/scalanative/junit/plugin/ScalaNativeJUnitBootstrappers.scala +++ b/junit-plugin/src/main/scala-3/scala/scalanative/junit/plugin/ScalaNativeJUnitBootstrappers.scala @@ -103,14 +103,16 @@ class ScalaNativeJUnitBootstrappers extends PluginPhase { genCallOnModule( classSym, junitNme.beforeClass, - testClass.companionModule, - junitdefn.BeforeClassAnnotClass + testClass, + junitdefn.BeforeClassAnnotClass, + callParentsFirst = true ), genCallOnModule( classSym, junitNme.afterClass, - testClass.companionModule, - junitdefn.AfterClassAnnotClass + testClass, + junitdefn.AfterClassAnnotClass, + callParentsFirst = false ), genCallOnParam( classSym, @@ -152,8 +154,9 @@ class ScalaNativeJUnitBootstrappers extends PluginPhase { private def genCallOnModule( owner: ClassSymbol, name: TermName, - module: Symbol, - annot: Symbol + testClass: Symbol, + annot: Symbol, + callParentsFirst: Boolean )(using Context): DefDef = { val sym = newSymbol( owner, @@ -162,15 +165,48 @@ class ScalaNativeJUnitBootstrappers extends PluginPhase { MethodType(Nil, Nil, defn.UnitType) ).entered + extension (sym: Symbol) + def isTraitOrInterface: Boolean = + sym.is(Trait) || sym.isAllOf(JavaInterface) + DefDef( sym, { - if (module.exists) { - val calls = annotatedMethods(module.moduleClass.asClass, annot) - .map(m => Apply(ref(module).select(m), Nil)) - Block(calls, unitLiteral) - } else { - unitLiteral + val allParents = List + .unfold(testClass.info.parents) { parents => + parents.flatMap(_.parents) match { + case Nil => None + case next => Some((parents ::: next), next) + } + } + .flatten + .distinct + + val symbols = { + val all = testClass.info :: allParents + if callParentsFirst then all.reverse else all } + + // Filter out annotations found in the companion of trait for compliance with the JVM + val (publicCalls, nonPublicCalls) = + symbols + .filterNot(_.classSymbol.isTraitOrInterface) + .map(_.classSymbol.companionModule) + .filter(_.exists) + .flatMap(s => annotatedMethods(s.moduleClass.asClass, annot)) + .partition(_.isPublic) + + if (nonPublicCalls.nonEmpty) { + val module = testClass.companionModule.orElse(testClass) + report.error( + s"Methods marked with ${annot.showName} annotation in $module must be public", + module.orElse(owner).srcPos + ) + } + + Block( + publicCalls.map(m => Apply(ref(m), Nil)), + unitLiteral + ) } ) } diff --git a/junit-runtime/src/main/scala/org/junit/Assert.scala b/junit-runtime/src/main/scala/org/junit/Assert.scala index 74762f5568..257fe97482 100644 --- a/junit-runtime/src/main/scala/org/junit/Assert.scala +++ b/junit-runtime/src/main/scala/org/junit/Assert.scala @@ -110,6 +110,7 @@ object Assert { def assertNotEquals(unexpected: Float, actual: Float, delta: Float): Unit = assertNotEquals(null, unexpected, actual, delta) + // This deprecation should not be removed, it mapping the deprecation in the JUnit library to match bevaiour on the JVM @deprecated( "Use assertEquals(double expected, double actual, double " + "epsilon) instead", @@ -123,6 +124,7 @@ object Assert { ) } + // This deprecation should not be removed, it mapping the deprecation in the JUnit library to match bevaiour on the JVM @deprecated( "Use assertEquals(String message, double expected, double " + "actual, double epsilon) instead", diff --git a/junit-test/outputs/scala/scalanative/junit/BeforeAndAfterClassTestAssertions_.txt b/junit-test/outputs/scala/scalanative/junit/BeforeAndAfterClassTestAssertions_.txt new file mode 100644 index 0000000000..2a6fa2cfb0 --- /dev/null +++ b/junit-test/outputs/scala/scalanative/junit/BeforeAndAfterClassTestAssertions_.txt @@ -0,0 +1,6 @@ +ldTest run started +ldTest scala.scalanative.junit.BeforeAndAfterClassTest.test started +ldTest scala.scalanative.junit.BeforeAndAfterClassTest.test finished, took