diff --git a/spec/java.common.lsl b/spec/java.common.lsl index a2a6349b..b25ed6c0 100644 --- a/spec/java.common.lsl +++ b/spec/java.common.lsl @@ -28,6 +28,8 @@ annotation packageprivate (); annotation public (); +annotation abstract (); + annotation extends ( canonicalClassName: string, ); diff --git a/spec/java/io/Console.lsl b/spec/java/io/Console.lsl new file mode 100644 index 00000000..8651deef --- /dev/null +++ b/spec/java/io/Console.lsl @@ -0,0 +1,27 @@ +//#! pragma: non-synthesizable +libsl "1.1.0"; + +library std + version "11" + language "Java" + url "https://github.com/openjdk/jdk11/blob/master/src/java.base/share/classes/java/io/Console.java"; + +// imports + +import java/io/Flushable; + + +// primary semantic types + +@final type Console + is java.io.Console + for Flushable +{ +} + +// see java.io.Console#istty +val CONSOLE_IS_TTY: boolean = action SYMBOLIC("boolean"); + + +// global aliases and type overrides + diff --git a/spec/java/io/FileDescriptor.lsl b/spec/java/io/FileDescriptor.lsl new file mode 100644 index 00000000..66920edb --- /dev/null +++ b/spec/java/io/FileDescriptor.lsl @@ -0,0 +1,24 @@ +//#! pragma: non-synthesizable +libsl "1.1.0"; + +library std + version "11" + language "Java" + url "https://github.com/openjdk/jdk11/blob/master/src/java.base/share/classes/java/io/FileDescriptor.java"; + +// imports + +import java/lang/Object; + + +// primary semantic types + +@public type FileDescriptor + is java.io.FileDescriptor + for Object +{ + fun valid(): boolean; + + @throws(["java.io.SyncFailedException"]) + fun sync(): void; +} diff --git a/spec/java/lang/Byte.lsl b/spec/java/lang/Byte.lsl index ac5ee8ac..5319da67 100644 --- a/spec/java/lang/Byte.lsl +++ b/spec/java/lang/Byte.lsl @@ -17,8 +17,9 @@ import java/lang/Number; @FunctionalInterface("byteValue") @final type Byte is java.lang.Byte - for Comparable, Number, byte + for Comparable, Number { + // WARNING: use 'byteValue' to get primitive value } diff --git a/spec/java/lang/Character.lsl b/spec/java/lang/Character.lsl index 009754d9..64249f78 100644 --- a/spec/java/lang/Character.lsl +++ b/spec/java/lang/Character.lsl @@ -9,6 +9,7 @@ library std // imports import java/io/Serializable; +import java/lang/CharSequence; import java/lang/Comparable; @@ -17,11 +18,13 @@ import java/lang/Comparable; @FunctionalInterface("charValue") @final type Character is java.lang.Character - for Comparable, Serializable, char + for Comparable, Serializable { + // WARNING: use 'charValue' to get primitive value + fun *.charValue(): char; - @static fun offsetByCodePoints(seq: CharSequence, index: int, codePointOffset: int): int; + @static fun *.offsetByCodePoints(seq: CharSequence, index: int, codePointOffset: int): int; } diff --git a/spec/java/lang/ClassLoader.lsl b/spec/java/lang/ClassLoader.lsl new file mode 100644 index 00000000..5cbe3d09 --- /dev/null +++ b/spec/java/lang/ClassLoader.lsl @@ -0,0 +1,24 @@ +//#! pragma: non-synthesizable +libsl "1.1.0"; + +library std + version "11" + language "Java" + url "https://github.com/openjdk/jdk11/blob/master/src/java.base/share/classes/java/lang/ClassLoader.java"; + +// imports + +import java/lang/Object; + + +// primary semantic types + +@final type ClassLoader + is java.lang.ClassLoader + for Object +{ +} + + +// global aliases and type overrides + diff --git a/spec/java/lang/Double.lsl b/spec/java/lang/Double.lsl index d4ddcc40..fc6ff7e3 100644 --- a/spec/java/lang/Double.lsl +++ b/spec/java/lang/Double.lsl @@ -9,6 +9,7 @@ library std // imports import java/lang/Comparable; +import java/lang/Class; import java/lang/Number; @@ -17,14 +18,44 @@ import java/lang/Number; @FunctionalInterface("doubleValue") @final type Double is java.lang.Double - for Comparable, Number, double + for Comparable, Number { -} + // WARNING: use 'doubleValue' to get primitive value + + @static fun *.isNaN(x: double): boolean; -val DOUBLE_POSITIVE_INFINITY: double = 1.0 / 0.0; -val DOUBLE_NEGATIVE_INFINITY: double = -1.0 / 0.0; -val DOUBLE_NAN: double = 0.0 / 0.0; + @static fun *.isFinite(x: double): boolean; + @static fun *.isInfinite(x: double): boolean; + + @static fun *.valueOf(x: double): Number; // #problem: self-reference +} // global aliases and type overrides +@extends("java.lang.Number") +@implements("java.lang.Comparable") +@final type LSLDouble + is java.lang.Double + for Double +{ + @private @static val serialVersionUID: long = -9172774392245257468L; + + @static val BYTES: int = 8; + @static val SIZE: int = 64; + + @static val MAX_EXPONENT: int = 1023; + @static val MIN_EXPONENT: int = -1022; + + // #problem: no support for scientific notation + @static val MAX_VALUE: double = action DEBUG_DO("1.7976931348623157E308"); + @static val MIN_VALUE: double = action DEBUG_DO("4.9E-324"); + @static val MIN_NORMAL: double = action DEBUG_DO("2.2250738585072014E-308"); + + @static val POSITIVE_INFINITY: double = 1.0 / 0.0; + @static val NEGATIVE_INFINITY: double = -1.0 / 0.0; + + @static val NaN: double = 0.0 / 0.0; + + @static val TYPE: Class = action TYPE_OF("Double"); +} diff --git a/spec/java/lang/Double.main.lsl b/spec/java/lang/Double.main.lsl new file mode 100644 index 00000000..273e4447 --- /dev/null +++ b/spec/java/lang/Double.main.lsl @@ -0,0 +1,396 @@ +libsl "1.1.0"; + +library std + version "11" + language "Java" + url "https://github.com/openjdk/jdk11/blob/master/src/java.base/share/classes/java/lang/Double.java"; + +// imports + +import java/lang/Object; +import java/lang/String; + +import java/lang/Double; + + +// automata + +automaton DoubleAutomaton +( + var value: double = 0.0, +) +: LSLDouble +{ + // states and shifts + + initstate Allocated; + state Initialized; + + shift Allocated -> Initialized by [ + // constructors + `` (LSLDouble, String), + `` (LSLDouble, double), + + // static operations + compare, + doubleToLongBits, + doubleToRawLongBits, + hashCode (double), + isFinite, + isInfinite (double), + isNaN (double), + longBitsToDouble, + max, + min, + parseDouble, + sum, + toHexString, + toString (double), + valueOf (String), + valueOf (double), + ]; + + shift Initialized -> self by [ + // instance methods + byteValue, + compareTo, + doubleValue, + equals, + floatValue, + hashCode (LSLDouble), + intValue, + isInfinite (LSLDouble), + isNaN (LSLDouble), + longValue, + shortValue, + toString (LSLDouble), + ]; + + // internal variables + + // utilities + + @static proc _getRawBits (v: double): long + { + if (v != v) // a NaN? + result = 9221120237041090560L; + else if (1.0 / v == NEGATIVE_INFINITY) // is it a "-0.0" ? + result = -9223372036854775808L; + else if (v == 0.0) + result = 0L; + else if (v == POSITIVE_INFINITY) + result = 9218868437227405312L; + else if (v == NEGATIVE_INFINITY) + result = -4503599627370496L; + else + { + // #todo: find more sophisticated approach + result = action SYMBOLIC("long"); + + action ASSUME(result != 9221120237041090560L); + action ASSUME(result != -9223372036854775808L); + action ASSUME(result != 0L); + action ASSUME(result != 9218868437227405312L); + action ASSUME(result != -4503599627370496L); + + if (v < 0.0) + action ASSUME(result < 0L); + else + action ASSUME(result > 0L); + } + } + + + @throws(["java.lang.NumberFormatException"]) + @static proc _parse (str: String): double + { + if (str == null) + action THROW_NEW("java.lang.NullPointerException", []); + + // #todo: add implementation if necessary + action TODO(); + } + + + // constructors + + @throws(["java.lang.NumberFormatException"]) + @Phantom constructor *.`` (@target self: LSLDouble, s: String) + { + // NOTE: using original method + } + + + constructor *.`` (@target self: LSLDouble, v: double) + { + this.value = v; + } + + + // static methods + + @static fun *.compare (a: double, b: double): int + { + // #problem: does not catch (-0.0, 0.0) + if (a == b || a != a || b != b) // include NaN's + { + result = 0; + } + else + { + if (a < b) + result = -1; + else + result = +1; + } + } + + + @static fun *.doubleToLongBits (value: double): long + { + result = _getRawBits(value); + } + + + @static fun *.doubleToRawLongBits (value: double): long + { + result = _getRawBits(value); + } + + + @static fun *.hashCode (value: double): int + { + result = _getRawBits(value) as int; + } + + + @static fun *.isFinite (d: double): boolean + { + // behaving similarly to Math.abs + if (d <= 0.0) + d = 0.0 - d; + + result = d <= MAX_VALUE; + } + + + @static fun *.isInfinite (v: double): boolean + { + result = (v == POSITIVE_INFINITY) || + (v == NEGATIVE_INFINITY); + } + + + @static fun *.isNaN (v: double): boolean + { + result = v != v; + } + + + @static fun *.longBitsToDouble (value: long): double + { + if (value == 9221120237041090560L) + result = NaN; + else if (value == -9223372036854775808L) + result = -0.0; + else if (value == 0L) + result = 0.0; + else if (value == 9218868437227405312L) + result = POSITIVE_INFINITY; + else if (value == -4503599627370496L) + result = NEGATIVE_INFINITY; + else + { + // #todo: find more sophisticated approach + result = action SYMBOLIC("double"); + + action ASSUME(result != 0.0); + action ASSUME(result == result); + action ASSUME(result != POSITIVE_INFINITY); + action ASSUME(result != NEGATIVE_INFINITY); + + if (value < 0L) + action ASSUME(result < 0.0); + else + action ASSUME(result > 0.0); + } + } + + + @static fun *.max (a: double, b: double): double + { + if (a != a) // catching NaN's + result = a; + else if (a == 0.0 && b == 0.0 && 1.0 / a == NEGATIVE_INFINITY) // catching '-0.0' + result = b; + else if (a >= b) + result = a; + else + result = b; + } + + + @static fun *.min (a: double, b: double): double + { + if (a != a) // catching NaN's + result = a; + else if (a == 0.0 && b == 0.0 && 1.0 / b == NEGATIVE_INFINITY) // catching '-0.0' + result = b; + else if (a <= b) + result = a; + else + result = b; + } + + + @throws(["java.lang.NumberFormatException"]) + @Phantom @static fun *.parseDouble (s: String): double + { + // NOTE: using original method + result = _parse(s); + } + + + @static fun *.sum (a: double, b: double): double + { + result = a + b; + } + + + @static fun *.toHexString (d: double): String + { + if (d != d) result = "NaN"; + else if (d == POSITIVE_INFINITY) result = "Infinity"; + else if (d == NEGATIVE_INFINITY) result = "-Infinity"; + else if (1.0 / d == NEGATIVE_INFINITY) result = "-0x0.0p0"; + else if (d == 0.0f) result = "0x0.0p0"; + else if (d == 1.0f) result = "0x1.0p0"; + else if (d == -1.0f) result = "-0x1.0p0"; + else + { + // #todo: add implementation if necessary + result = action SYMBOLIC("java.lang.String"); + action ASSUME(result != null); + val len: int = action CALL_METHOD(result, "length", []); + action ASSUME(len >= 7); // 0x1.0p0 + action ASSUME(len <= 22); // 0x1.fffffffffffffp1023 + } + } + + + @static fun *.toString (d: double): String + { + result = action OBJECT_TO_STRING(d); + } + + + @throws(["java.lang.NumberFormatException"]) + @Phantom @static fun *.valueOf (s: String): Double + { + // NOTE: using original method + result = new DoubleAutomaton(state = Initialized, + value = _parse(s) + ); + } + + + @static fun *.valueOf (d: double): Double + { + result = new DoubleAutomaton(state = Initialized, + value = d + ); + } + + + // methods + + fun *.byteValue (@target self: LSLDouble): byte + { + result = this.value as byte; + } + + + fun *.compareTo (@target self: LSLDouble, anotherDouble: LSLDouble): int + { + val a: double = this.value; + val b: double = DoubleAutomaton(anotherDouble).value; + + // #problem: does not catch (-0.0, 0.0) + if (a == b || a != a || b != b) // include NaN's + { + result = 0; + } + else + { + if (a < b) + result = -1; + else + result = +1; + } + } + + + fun *.doubleValue (@target self: LSLDouble): double + { + result = this.value; + } + + + fun *.equals (@target self: LSLDouble, obj: Object): boolean + { + if (obj is Double) + result = this.value == DoubleAutomaton(obj).value; + else + result = false; + } + + + fun *.floatValue (@target self: LSLDouble): float + { + result = this.value as float; + } + + + fun *.hashCode (@target self: LSLDouble): int + { + result = _getRawBits(this.value) as int; + } + + + fun *.intValue (@target self: LSLDouble): int + { + result = this.value as int; + } + + + fun *.isInfinite (@target self: LSLDouble): boolean + { + result = (this.value == POSITIVE_INFINITY) || + (this.value == NEGATIVE_INFINITY); + } + + + fun *.isNaN (@target self: LSLDouble): boolean + { + result = this.value != this.value; + } + + + fun *.longValue (@target self: LSLDouble): long + { + result = this.value as long; + } + + + fun *.shortValue (@target self: LSLDouble): short + { + result = this.value as short; + } + + + fun *.toString (@target self: LSLDouble): String + { + result = action OBJECT_TO_STRING(this.value); + } + +} diff --git a/spec/java/lang/Float.lsl b/spec/java/lang/Float.lsl index 08001ca6..fff96c1e 100644 --- a/spec/java/lang/Float.lsl +++ b/spec/java/lang/Float.lsl @@ -9,6 +9,7 @@ library std // imports import java/lang/Comparable; +import java/lang/Class; import java/lang/Number; @@ -17,10 +18,45 @@ import java/lang/Number; @FunctionalInterface("floatValue") @final type Float is java.lang.Float - for Comparable, Number, float, double + for Comparable, Number { + // WARNING: use 'floatValue' to get primitive value + + @static fun *.isNaN(x: float): boolean; + + @static fun *.isFinite(x: float): boolean; + @static fun *.isInfinite(x: float): boolean; + + @static fun *.valueOf(x: float): Number; // #problem: self-reference } // global aliases and type overrides +@extends("java.lang.Number") +@implements("java.lang.Comparable") +@final type LSLFloat + is java.lang.Float + for Float +{ + @private @static val serialVersionUID: long = -2671257302660747028L; + + @static val BYTES: int = 4; + @static val SIZE: int = 32; + + @static val MAX_EXPONENT: int = 127; + @static val MIN_EXPONENT: int = -126; + + // #problem: no "scientific" notation support + @static val MAX_VALUE: float = action DEBUG_DO("3.4028235e+38f"); + @static val MIN_VALUE: float = action DEBUG_DO("1.4e-45f"); + + @static val MIN_NORMAL: float = action DEBUG_DO("1.17549435e-38f"); + + @static val NEGATIVE_INFINITY: float = -1.0f / 0.0f; + @static val POSITIVE_INFINITY: float = 1.0f / 0.0f; + @static val NaN: float = 0.0f / 0.0f; + + // #problem: unable to get reference to primitive type + @static val TYPE: Class = action TYPE_OF("Float"); // preventing recursion +} diff --git a/spec/java/lang/Float.main.lsl b/spec/java/lang/Float.main.lsl new file mode 100644 index 00000000..4ecc52a9 --- /dev/null +++ b/spec/java/lang/Float.main.lsl @@ -0,0 +1,403 @@ +libsl "1.1.0"; + +library std + version "11" + language "Java" + url "https://github.com/openjdk/jdk11/blob/master/src/java.base/share/classes/java/lang/Float.java"; + +// imports + +import java/lang/String; + +import java/lang/Float; + + +// automata + +automaton FloatAutomaton +( + var value: float = 0.0f, +) +: LSLFloat +{ + // states and shifts + + initstate Allocated; + state Initialized; + + shift Allocated -> Initialized by [ + // constructors + `` (LSLFloat, String), + `` (LSLFloat, double), + `` (LSLFloat, float), + + // static operations + compare, + floatToIntBits, + floatToRawIntBits, + hashCode (float), + intBitsToFloat, + isFinite, + isInfinite (float), + isNaN (float), + max, + min, + parseFloat, + sum, + toHexString, + toString (float), + valueOf (String), + valueOf (float), + ]; + + shift Initialized -> self by [ + // instance methods + byteValue, + compareTo, + doubleValue, + equals, + floatValue, + hashCode (LSLFloat), + intValue, + isInfinite (LSLFloat), + isNaN (LSLFloat), + longValue, + shortValue, + toString (LSLFloat), + ]; + + // internal variables + + // utilities + + @static proc _getRawBits (v: float): int + { + if (v != v) // a NaN? + result = 2143289344; + else if (1.0f / v == NEGATIVE_INFINITY) // is it a "-0.0" ? + result = -2147483648; + else if (v == 0.0f) + result = 0; + else if (v == POSITIVE_INFINITY) + result = 2139095040; + else if (v == NEGATIVE_INFINITY) + result = -8388608; + else + { + // #todo: find more sophisticated approach + result = action SYMBOLIC("int"); + + action ASSUME(result != 2143289344); + action ASSUME(result != -2147483648); + action ASSUME(result != 0); + action ASSUME(result != 2139095040); + action ASSUME(result != -8388608); + + if (v < 0.0f) + action ASSUME(result < 0); + else + action ASSUME(result > 0); + } + } + + + @throws(["java.lang.NumberFormatException"]) + @static proc _parse (str: String): float + { + if (str == null) + action THROW_NEW("java.lang.NullPointerException", []); + + // #todo: add implementation if necessary + action TODO(); + } + + + // constructors + + @throws(["java.lang.NumberFormatException"]) + @Phantom constructor *.`` (@target self: LSLFloat, s: String) + { + // NOTE: using the original method + this.value = _parse(s); + } + + + constructor *.`` (@target self: LSLFloat, v: double) + { + this.value = v as float; + } + + + constructor *.`` (@target self: LSLFloat, v: float) + { + this.value = v; + } + + + // static methods + + @static fun *.compare (a: float, b: float): int + { + // #problem: does not catch (-0.0, 0.0) + if (a == b || a != a || b != b) // include NaN's + { + result = 0; + } + else + { + if (a < b) + result = -1; + else + result = +1; + } + } + + + @static fun *.floatToIntBits (value: float): int + { + result = _getRawBits(value); + } + + + @static fun *.floatToRawIntBits (value: float): int + { + result = _getRawBits(value); + } + + + @static fun *.hashCode (value: float): int + { + result = _getRawBits(value); + } + + + @static fun *.intBitsToFloat (value: int): float + { + if (value == 2143289344) + result = NaN; + else if (value == -2147483648) + result = -0.0f; + else if (value == 0) + result = 0.0f; + else if (value == 2139095040) + result = POSITIVE_INFINITY; + else if (value == -8388608) + result = NEGATIVE_INFINITY; + else + { + // #todo: find more sophisticated approach + result = action SYMBOLIC("float"); + + action ASSUME(result != 0.0f); + action ASSUME(result == result); + action ASSUME(result != POSITIVE_INFINITY); + action ASSUME(result != NEGATIVE_INFINITY); + + if (value < 0) + action ASSUME(result < 0.0f); + else + action ASSUME(result > 0.0f); + } + } + + + @static fun *.isFinite (f: float): boolean + { + // behaving similarly to Math.abs + if (f <= 0.0f) + f = 0.0f - f; + + result = f <= MAX_VALUE; + } + + + @static fun *.isInfinite (v: float): boolean + { + result = (v == POSITIVE_INFINITY) || + (v == NEGATIVE_INFINITY); + } + + + @static fun *.isNaN (v: float): boolean + { + result = v != v; + } + + + @static fun *.max (a: float, b: float): float + { + if (a != a) // catching NaN's + result = a; + else if (a == 0.0f && b == 0.0f && 1.0f / a == NEGATIVE_INFINITY) // catching '-0.0' + result = b; + else if (a >= b) + result = a; + else + result = b; + } + + + @static fun *.min (a: float, b: float): float + { + if (a != a) // catching NaN's + result = a; + else if (a == 0.0f && b == 0.0f && 1.0f / b == NEGATIVE_INFINITY) // catching '-0.0' + result = b; + else if (a <= b) + result = a; + else + result = b; + } + + + @throws(["java.lang.NumberFormatException"]) + @Phantom @static fun *.parseFloat (s: String): float + { + // NOTE: using the original method + result = _parse(s); + } + + + @static fun *.sum (a: float, b: float): float + { + result = a + b; + } + + + @static fun *.toHexString (f: float): String + { + if (f != f) result = "NaN"; + else if (f == POSITIVE_INFINITY) result = "Infinity"; + else if (f == NEGATIVE_INFINITY) result = "-Infinity"; + else if (1.0f / f == NEGATIVE_INFINITY) result = "-0x0.0p0"; + else if (f == 0.0f) result = "0x0.0p0"; + else if (f == 1.0f) result = "0x1.0p0"; + else if (f == -1.0f) result = "-0x1.0p0"; + else + { + // #todo: add implementation if necessary + result = action SYMBOLIC("java.lang.String"); + action ASSUME(result != null); + val len: int = action CALL_METHOD(result, "length", []); + action ASSUME(len >= 7); // 0x1.0p0 + action ASSUME(len <= 14); // 0x1.fffffep127 + } + } + + + @static fun *.toString (f: float): String + { + result = action OBJECT_TO_STRING(f); + } + + + @throws(["java.lang.NumberFormatException"]) + @Phantom @static fun *.valueOf (s: String): Float + { + // NOTE: using the original version + result = new FloatAutomaton(state = Initialized, + value = _parse(s) + ); + } + + + @static fun *.valueOf (f: float): Float + { + result = new FloatAutomaton(state = Initialized, + value = f + ); + } + + + // methods + + fun *.byteValue (@target self: LSLFloat): byte + { + result = this.value as byte; + } + + + fun *.compareTo (@target self: LSLFloat, anotherFloat: LSLFloat): int + { + val a: float = this.value; + val b: float = FloatAutomaton(anotherFloat).value; + + // #problem: does not catch (-0.0, 0.0) + if (a == b || a != a || b != b) // include NaN's + { + result = 0; + } + else + { + if (a < b) + result = -1; + else + result = +1; + } + } + + + fun *.doubleValue (@target self: LSLFloat): double + { + result = this.value as double; + } + + + fun *.equals (@target self: LSLFloat, obj: Object): boolean + { + if (obj is Float) + result = this.value == FloatAutomaton(obj).value; + else + result = false; + } + + + fun *.floatValue (@target self: LSLFloat): float + { + result = this.value; + } + + + fun *.hashCode (@target self: LSLFloat): int + { + result = _getRawBits(this.value); + } + + + fun *.intValue (@target self: LSLFloat): int + { + result = this.value as int; + } + + + fun *.isInfinite (@target self: LSLFloat): boolean + { + result = (this.value == POSITIVE_INFINITY) || + (this.value == NEGATIVE_INFINITY); + } + + + fun *.isNaN (@target self: LSLFloat): boolean + { + result = this.value != this.value; + } + + + fun *.longValue (@target self: LSLFloat): long + { + result = this.value as long; + } + + + fun *.shortValue (@target self: LSLFloat): short + { + result = this.value as short; + } + + + fun *.toString (@target self: LSLFloat): String + { + result = action OBJECT_TO_STRING(this.value); + } + +} diff --git a/spec/java/lang/Integer.lsl b/spec/java/lang/Integer.lsl index ebc02c09..b2ddbfc3 100644 --- a/spec/java/lang/Integer.lsl +++ b/spec/java/lang/Integer.lsl @@ -8,6 +8,7 @@ library std // imports +import java/lang/Class; import java/lang/Comparable; import java/lang/Number; @@ -17,10 +18,73 @@ import java/lang/Number; @FunctionalInterface("intValue") @final type Integer is java.lang.Integer - for Comparable, Number, int + for Comparable, Number { + // WARNING: use 'intValue' to get primitive value + + @static fun *.valueOf(x: int): Number; // #problem: self-reference } // global aliases and type overrides +@extends("java.lang.Number") +@implements("java.lang.Comparable") +@final type LSLInteger + is java.lang.Integer + for Integer +{ + @private @static val serialVersionUID: long = 1360826667806852920L; + + @public @static val MIN_VALUE: int = -2147483648; + @public @static val MAX_VALUE: int = 2147483647; + + // #problem: unable to get reference to primitive type + @public @static val TYPE: Class = action TYPE_OF("Integer"); // preventing recursion + + @public @static val SIZE: int = 32; + @public @static val BYTES: int = 4; + + // WARNING: do not change! + @static val digits: array = [ + '0' , '1' , '2' , '3' , '4' , '5' , + '6' , '7' , '8' , '9' , 'a' , 'b' , + 'c' , 'd' , 'e' , 'f' , 'g' , 'h' , + 'i' , 'j' , 'k' , 'l' , 'm' , 'n' , + 'o' , 'p' , 'q' , 'r' , 's' , 't' , + 'u' , 'v' , 'w' , 'x' , 'y' , 'z' , + ]; + + // WARNING: do not change! + @static val DigitTens: array = [ + '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', + '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', + '2', '2', '2', '2', '2', '2', '2', '2', '2', '2', + '3', '3', '3', '3', '3', '3', '3', '3', '3', '3', + '4', '4', '4', '4', '4', '4', '4', '4', '4', '4', + '5', '5', '5', '5', '5', '5', '5', '5', '5', '5', + '6', '6', '6', '6', '6', '6', '6', '6', '6', '6', + '7', '7', '7', '7', '7', '7', '7', '7', '7', '7', + '8', '8', '8', '8', '8', '8', '8', '8', '8', '8', + '9', '9', '9', '9', '9', '9', '9', '9', '9', '9', + ]; + + // WARNING: do not change! + @static val DigitOnes: array = [ + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + ]; + + // WARNING: do not change! + @static val sizeTable: array = [ + 9, 99, 999, 9999, 99999, 999999, 9999999, 99999999, 999999999, 2147483647 + ]; +} diff --git a/spec/java/lang/Integer.main.lsl b/spec/java/lang/Integer.main.lsl new file mode 100644 index 00000000..e3622ccd --- /dev/null +++ b/spec/java/lang/Integer.main.lsl @@ -0,0 +1,535 @@ +libsl "1.1.0"; + +library std + version "11" + language "Java" + url "https://github.com/openjdk/jdk11/blob/master/src/java.base/share/classes/java/lang/Integer.java"; + +// imports + +import java/lang/CharSequence; +import java/lang/Object; +import java/lang/String; + +import java/lang/Integer; + + +// automata + +automaton IntegerAutomaton +( + @private var value: int // WARNING: do not rename! +) +: LSLInteger +{ + // states and shifts + + initstate Initialized; + + shift Initialized -> self by [ + // constructors + `` (LSLInteger, String), + `` (LSLInteger, int), + + // static operations + bitCount, + compare, + compareUnsigned, + decode, + divideUnsigned, + getInteger (String), + getInteger (String, Integer), + getInteger (String, int), + hashCode (int), + highestOneBit, + lowestOneBit, + max, + min, + numberOfLeadingZeros, + numberOfTrailingZeros, + parseInt (CharSequence, int, int, int), + parseInt (String), + parseInt (String, int), + parseUnsignedInt (CharSequence, int, int, int), + parseUnsignedInt (String), + parseUnsignedInt (String, int), + remainderUnsigned, + reverse, + reverseBytes, + rotateLeft, + rotateRight, + signum, + sum, + toBinaryString, + toHexString, + toOctalString, + toString (int), + toString (int, int), + toUnsignedLong, + toUnsignedString (int), + toUnsignedString (int, int), + valueOf (String), + valueOf (String, int), + valueOf (int), + + // instance methods + byteValue, + compareTo, + doubleValue, + equals, + floatValue, + hashCode (LSLInteger), + intValue, + longValue, + shortValue, + toString (LSLInteger), + ]; + + // internal variables + + // utilities + + @throws(["java.lang.NumberFormatException"]) + @static proc _parse (str: String): int + { + if (str == null) + action THROW_NEW("java.lang.NullPointerException", []); + + // #todo: add implementation if necessary + action TODO(); + } + + + // constructors + + @throws(["java.lang.NumberFormatException"]) + @Phantom constructor *.`` (@target self: LSLInteger, s: String) + { + // NOTE: using the original method + this.value = _parse(s); + } + + + constructor *.`` (@target self: LSLInteger, v: int) + { + this.value = v; + } + + + // static methods + + @static fun *.bitCount (i: int): int + { + // direct adaptation from the JDK + i = i - ((i >>> 1) & 1431655765); + i = (i & 858993459) + ((i >>> 2) & 858993459); + i = (i + (i >>> 4)) & 252645135; + i = i + (i >>> 8); + i = i + (i >>> 16); + result = i & 63; + } + + + @static fun *.compare (x: int, y: int): int + { + if (x == y) + { + result = 0; + } + else + { + if (x < y) + result = -1; + else + result = +1; + } + } + + + @static fun *.compareUnsigned (x: int, y: int): int + { + x += MIN_VALUE; + y += MIN_VALUE; + + if (x == y) + { + result = 0; + } + else + { + if (x < y) + result = -1; + else + result = +1; + } + } + + + @throws(["java.lang.NumberFormatException"]) + @Phantom @static fun *.decode (nm: String): Integer + { + // NOTE: using the original method + } + + + @static fun *.divideUnsigned (dividend: int, divisor: int): int + { + val unsignedDividend: long = dividend as long & 4294967295L; + val unsignedDivisor: long = divisor as long & 4294967295L; + result = (unsignedDividend / unsignedDivisor) as int; + } + + + @Phantom @static fun *.getInteger (nm: String): Integer + { + // NOTE: using the original method + } + + + @Phantom @static fun *.getInteger (nm: String, _val: Integer): Integer + { + // NOTE: using the original method + } + + + @Phantom @static fun *.getInteger (nm: String, _val: int): Integer + { + // NOTE: using the original method + } + + + @static fun *.hashCode (value: int): int + { + result = value; + } + + + @static fun *.highestOneBit (i: int): int + { + // direct adaptation from the JDK + i |= (i >> 1); + i |= (i >> 2); + i |= (i >> 4); + i |= (i >> 8); + i |= (i >> 16); + result = i - (i >>> 1); + } + + + @static fun *.lowestOneBit (i: int): int + { + // direct adaptation from the JDK + result = i & -i; + } + + + @static fun *.max (a: int, b: int): int + { + if (a > b) + result = a; + else + result = b; + } + + + @static fun *.min (a: int, b: int): int + { + if (a < b) + result = a; + else + result = b; + } + + + @static fun *.numberOfLeadingZeros (i: int): int + { + if (i == 0) + { + result = 32; + } + else + { + // direct adaptation from the JDK + result = 1; + + if (i >>> 16 == 0) { result += 16; i <<= 16; } + if (i >>> 24 == 0) { result += 8; i <<= 8; } + if (i >>> 28 == 0) { result += 4; i <<= 4; } + if (i >>> 30 == 0) { result += 2; i <<= 2; } + + result -= i >>> 31; + } + } + + + @static fun *.numberOfTrailingZeros (i: int): int + { + if (i == 0) + { + result = 32; + } + else + { + // direct adaptation from the JDK + var y: int = 0; + result = 31; + + y = i << 16; if (y != 0) { result -= 16; i = y; } + y = i << 8; if (y != 0) { result -= 8; i = y; } + y = i << 4; if (y != 0) { result -= 4; i = y; } + y = i << 2; if (y != 0) { result -= 2; i = y; } + + result -= ((i << 1) >>> 31); + } + } + + + @throws(["java.lang.NumberFormatException"]) + @Phantom @static fun *.parseInt (s: CharSequence, beginIndex: int, endIndex: int, radix: int): int + { + // NOTE: using the original method + } + + + @throws(["java.lang.NumberFormatException"]) + @Phantom @static fun *.parseInt (s: String): int + { + // NOTE: using the original method + } + + + @throws(["java.lang.NumberFormatException"]) + @Phantom @static fun *.parseInt (s: String, radix: int): int + { + // NOTE: using the original method + } + + + @throws(["java.lang.NumberFormatException"]) + @Phantom @static fun *.parseUnsignedInt (s: CharSequence, beginIndex: int, endIndex: int, radix: int): int + { + // NOTE: using the original method + } + + + @throws(["java.lang.NumberFormatException"]) + @Phantom @static fun *.parseUnsignedInt (s: String): int + { + // NOTE: using the original method + } + + + @throws(["java.lang.NumberFormatException"]) + @Phantom @static fun *.parseUnsignedInt (s: String, radix: int): int + { + // NOTE: using the original method + } + + + @static fun *.remainderUnsigned (dividend: int, divisor: int): int + { + val unsignedDividend: long = dividend as long & 4294967295L; + val unsignedDivisor: long = divisor as long & 4294967295L; + result = (unsignedDividend % unsignedDivisor) as int; + } + + + @static fun *.reverse (i: int): int + { + // direct adaptation from the JDK + i = (i & 1431655765) << 1 | (i >>> 1) & 1431655765; + i = (i & 858993459) << 2 | (i >>> 2) & 858993459; + i = (i & 252645135) << 4 | (i >>> 4) & 252645135; + i = (i << 24) | ((i & 65280) << 8) | + ((i >>> 8) & 65280) | (i >>> 24); + + result = i; + } + + + @static fun *.reverseBytes (i: int): int + { + // direct adaptation from the JDK + result = ((i >>> 24) ) | + ((i >> 8) & 65280) | + ((i << 8) & 16711680) | + ((i << 24)); + } + + + @static fun *.rotateLeft (i: int, distance: int): int + { + // direct adaptation from the JDK + result = (i << distance) | (i >>> -distance); + } + + + @static fun *.rotateRight (i: int, distance: int): int + { + // direct adaptation from the JDK + result = (i >>> distance) | (i << -distance); + } + + + @static fun *.signum (i: int): int + { + // direct adaptation from the JDK + result = (i >> 31) | (-i >>> 31); + } + + + @static fun *.sum (a: int, b: int): int + { + result = a + b; + } + + + @Phantom @static fun *.toBinaryString (i: int): String + { + // NOTE: using the original method + } + + + @Phantom @static fun *.toHexString (i: int): String + { + // NOTE: using the original method + } + + + @Phantom @static fun *.toOctalString (i: int): String + { + // NOTE: using the original method + } + + + @static fun *.toString (i: int): String + { + result = action OBJECT_TO_STRING(i); + } + + + @Phantom @static fun *.toString (i: int, radix: int): String + { + // NOTE: using the original method + } + + + @static fun *.toUnsignedLong (x: int): long + { + // direct adaptation from the JDK + result = x as long & 4294967295L; + } + + + @Phantom @static fun *.toUnsignedString (i: int): String + { + // NOTE: using the original method + } + + + @Phantom @static fun *.toUnsignedString (i: int, radix: int): String + { + // NOTE: using the original method + } + + + @throws(["java.lang.NumberFormatException"]) + @Phantom @static fun *.valueOf (s: String): Integer + { + // NOTE: using the original method + } + + + @throws(["java.lang.NumberFormatException"]) + @Phantom @static fun *.valueOf (s: String, radix: int): Integer + { + // NOTE: using the original method + } + + + @static fun *.valueOf (i: int): Integer + { + result = new IntegerAutomaton(state = Initialized, + value = i + ); + } + + + // methods + + fun *.byteValue (@target self: LSLInteger): byte + { + result = this.value as byte; + } + + + fun *.compareTo (@target self: LSLInteger, anotherInteger: LSLInteger): int + { + val x: int = this.value; + val y: int = IntegerAutomaton(anotherInteger).value; + + if (x == y) + { + result = 0; + } + else + { + if (x < y) + result = -1; + else + result = +1; + } + } + + + fun *.doubleValue (@target self: LSLInteger): double + { + result = this.value as double; + } + + + fun *.equals (@target self: LSLInteger, obj: Object): boolean + { + if (obj is Integer) + result = this.value == IntegerAutomaton(obj).value; + else + result = false; + } + + + fun *.floatValue (@target self: LSLInteger): float + { + result = this.value as float; + } + + + fun *.hashCode (@target self: LSLInteger): int + { + result = this.value; + } + + + fun *.intValue (@target self: LSLInteger): int + { + result = this.value; + } + + + fun *.longValue (@target self: LSLInteger): long + { + result = this.value as long; + } + + + fun *.shortValue (@target self: LSLInteger): short + { + result = this.value as short; + } + + + fun *.toString (@target self: LSLInteger): String + { + result = action OBJECT_TO_STRING(this.value); + } + +} diff --git a/spec/java/lang/Long.lsl b/spec/java/lang/Long.lsl index ce483d9a..6511945c 100644 --- a/spec/java/lang/Long.lsl +++ b/spec/java/lang/Long.lsl @@ -17,8 +17,9 @@ import java/lang/Number; @FunctionalInterface("longValue") @final type Long is java.lang.Long - for Comparable, Number, long + for Comparable, Number { + // WARNING: use 'longValue' to get primitive value } diff --git a/spec/java/lang/Object.main.lsl b/spec/java/lang/Object.main.lsl index 97f4f10a..1729e014 100644 --- a/spec/java/lang/Object.main.lsl +++ b/spec/java/lang/Object.main.lsl @@ -8,6 +8,7 @@ library std // imports import java/lang/Class; +import java/lang/Cloneable; import java/lang/Object; import java/lang/String; @@ -25,7 +26,7 @@ automaton ObjectAutomaton shift Initialized -> self by [ // constructors - LSLObject, + ``, // instance methods equals, @@ -46,9 +47,9 @@ automaton ObjectAutomaton // constructors - @Phantom constructor *.LSLObject (@target self: LSLObject) + @Phantom constructor *.`` (@target self: LSLObject) { - // NOTE: using the original method + // WARNING: Using the original method here. Do not change! (infinite recursion otherwise) } @@ -62,9 +63,18 @@ automaton ObjectAutomaton } - @Phantom @protected fun *.clone (@target self: LSLObject): Object + @throws(["java.lang.CloneNotSupportedException"]) + @protected fun *.clone (@target self: LSLObject): Object { - // NOTE: using the original method + if (self is Cloneable == false) + action THROW_NEW("java.lang.CloneNotSupportedException", []); + + result = action SYMBOLIC("java.lang.Object"); + action ASSUME(result != null); + + val thisType: Class = action TYPE_OF(self); + val cloneType: Class = action TYPE_OF(result); + action ASSUME(thisType == cloneType); } @@ -94,8 +104,8 @@ automaton ObjectAutomaton fun *.toString (@target self: LSLObject): String { - result = action SYMBOLIC("java.lang.String"); - action ASSUME(result != null); + // #todo: use class name and a random hex string + result = "java.lang.Object@735b5592"; } @@ -122,7 +132,7 @@ automaton ObjectAutomaton // special: static initialization - @Phantom @static fun *.__clinit__ (): void + @Phantom @static fun *.`` (): void { action DO_NOTHING(); } diff --git a/spec/java/lang/Readable.lsl b/spec/java/lang/Readable.lsl new file mode 100644 index 00000000..4d2ab23d --- /dev/null +++ b/spec/java/lang/Readable.lsl @@ -0,0 +1,26 @@ +//#! pragma: non-synthesizable +libsl "1.1.0"; + +library std + version "11" + language "Java" + url "https://github.com/openjdk/jdk11/blob/master/src/java.base/share/classes/java/lang/Readable.java"; + +// imports + +import java/lang/Object; +import java/nio/Buffer; + + +// primary semantic types + +@interface type Readable + is java.lang.Readable + for Object +{ + @throws(["java.io.IOException"]) + fun *.read(cb: Buffer): int; // #problem: cyclic dependency +} + + +// global aliases and type overrides diff --git a/spec/java/lang/SecurityManager.lsl b/spec/java/lang/SecurityManager.lsl new file mode 100644 index 00000000..afd38168 --- /dev/null +++ b/spec/java/lang/SecurityManager.lsl @@ -0,0 +1,36 @@ +//#! pragma: non-synthesizable +libsl "1.1.0"; + +library std + version "11" + language "Java" + url "https://github.com/openjdk/jdk11/blob/master/src/java.base/share/classes/java/lang/SecurityManager.java"; + +// imports + +import java/lang/Object; +import java/lang/String; +import java/security/Permission; + + +// primary semantic types + +@public type SecurityManager + is java.lang.SecurityManager + for Object +{ + fun *.checkPermission(perm: Permission): void; + + fun *.checkPropertiesAccess(): void; + + fun *.checkPropertyAccess(key: String): void; +} + + +// global aliases and type overrides + +@public type LSLSecurityManager + is java.lang.SecurityManager + for SecurityManager +{ +} diff --git a/spec/java/lang/SecurityManager.main.lsl b/spec/java/lang/SecurityManager.main.lsl new file mode 100644 index 00000000..3fb0143a --- /dev/null +++ b/spec/java/lang/SecurityManager.main.lsl @@ -0,0 +1,411 @@ +libsl "1.1.0"; + +library std + version "11" + language "Java" + url "https://github.com/openjdk/jdk11/blob/master/src/java.base/share/classes/java/lang/SecurityManager.java"; + +// imports + +import java/io/FileDescriptor; +import java/lang/Object; +import java/lang/SecurityManager; +import java/lang/String; +import java/lang/Thread; +import java/lang/ThreadGroup; +import java/net/InetAddress; +import java/security/Permission; +import java/security/AccessControlContext; + + +// automata + +automaton SecurityManagerAutomaton +( +) +: LSLSecurityManager +{ + // states and shifts + + initstate Allocated; + state Initialized; + + shift Allocated -> Initialized by [ + // constructors + ``, + ]; + + shift Initialized -> self by [ + // instance methods + checkAccept, + checkAccess (LSLSecurityManager, Thread), + checkAccess (LSLSecurityManager, ThreadGroup), + checkConnect (LSLSecurityManager, String, int), + checkConnect (LSLSecurityManager, String, int, Object), + checkCreateClassLoader, + checkDelete, + checkExec, + checkExit, + checkLink, + checkListen, + checkMulticast (LSLSecurityManager, InetAddress), + checkMulticast (LSLSecurityManager, InetAddress, byte), + checkPackageAccess, + checkPackageDefinition, + checkPermission (LSLSecurityManager, Permission), + checkPermission (LSLSecurityManager, Permission, Object), + checkPrintJobAccess, + checkPropertiesAccess, + checkPropertyAccess, + checkRead (LSLSecurityManager, FileDescriptor), + checkRead (LSLSecurityManager, String), + checkRead (LSLSecurityManager, String, Object), + checkSecurityAccess, + checkSetFactory, + checkWrite (LSLSecurityManager, FileDescriptor), + checkWrite (LSLSecurityManager, String), + getSecurityContext, + getThreadGroup, + ]; + + // internal variables + + // utilities + + @AutoInline @Phantom proc _throwNPE (): void + { + action THROW_NEW("java.lang.NullPointerException", []); + } + + + @AutoInline @Phantom proc _throwIAE (): void + { + action THROW_NEW("java.lang.IllegalArgumentException", []); + } + + + proc _do_checkPermission (perm: Permission): void + { + if (action SYMBOLIC("boolean")) + action THROW_NEW("java.security.AccessControlException", [ + "access denied " /* + action OBJECT_TO_STRING(perm) */, + perm + ]); + } + + + // constructors + + constructor *.`` (@target self: LSLSecurityManager) + { + _do_checkPermission( + action DEBUG_DO("new RuntimePermission(\"createSecurityManager\")") + ); + } + + + // static methods + + // methods + + fun *.checkAccept (@target self: LSLSecurityManager, host: String, port: int): void + { + if (host == null) + _throwNPE(); + + // 'host' correctness check is too complex + if (action SYMBOLIC("boolean")) + _throwIAE(); + + _do_checkPermission( + null // #todo + ); + } + + + fun *.checkAccess (@target self: LSLSecurityManager, t: Thread): void + { + if (t == null) + action THROW_NEW("java.lang.NullPointerException", ["thread can't be null"]); + + // #todo: check thread group? + + _do_checkPermission( + null // #todo + ); + } + + + fun *.checkAccess (@target self: LSLSecurityManager, g: ThreadGroup): void + { + if (g == null) + action THROW_NEW("java.lang.NullPointerException", ["thread group can't be null"]); + + // #todo: check thread group? + + _do_checkPermission( + null // #todo + ); + } + + + fun *.checkConnect (@target self: LSLSecurityManager, host: String, port: int): void + { + if (host == null) + action THROW_NEW("java.lang.NullPointerException", ["host can't be null"]); + + // host correctness check is too complex + if (action SYMBOLIC("boolean")) + _throwIAE(); + + _do_checkPermission( + null // #todo + ); + } + + + fun *.checkConnect (@target self: LSLSecurityManager, host: String, port: int, context: Object): void + { + if (host == null) + action THROW_NEW("java.lang.NullPointerException", ["host can't be null"]); + + // 'host' correctness check is too complex + if (action SYMBOLIC("boolean")) + _throwIAE(); + + _do_checkPermission( + null // #todo + ); + } + + + fun *.checkCreateClassLoader (@target self: LSLSecurityManager): void + { + _do_checkPermission( + null // #todo + ); + } + + + fun *.checkDelete (@target self: LSLSecurityManager, file: String): void + { + if (file == null) + _throwNPE(); + + // 'action' check during construction of a FilePermission object does not throw an exception + + _do_checkPermission( + null // #todo + ); + } + + + fun *.checkExec (@target self: LSLSecurityManager, cmd: String): void + { + if (cmd == null) + _throwNPE(); + + // 'action' check during construction of a FilePermission object does not throw an exception + + _do_checkPermission( + null // #todo + ); + } + + + fun *.checkExit (@target self: LSLSecurityManager, status: int): void + { + _do_checkPermission( + null // #todo + ); + } + + + fun *.checkLink (@target self: LSLSecurityManager, lib: String): void + { + if (lib == null) + _throwNPE(); + + _do_checkPermission( + null // #todo + ); + } + + + fun *.checkListen (@target self: LSLSecurityManager, port: int): void + { + _do_checkPermission( + null // #todo + ); + } + + + fun *.checkMulticast (@target self: LSLSecurityManager, maddr: InetAddress): void + { + if (maddr == null) + _throwNPE(); + + _do_checkPermission( + null // #todo + ); + } + + + fun *.checkMulticast (@target self: LSLSecurityManager, maddr: InetAddress, ttl: byte): void + { + if (maddr == null) + _throwNPE(); + + _do_checkPermission( + null // #todo + ); + } + + + fun *.checkPackageAccess (@target self: LSLSecurityManager, pkg: String): void + { + if (pkg == null) + _throwNPE(); + + _do_checkPermission( + null // #todo + ); + } + + + fun *.checkPackageDefinition (@target self: LSLSecurityManager, pkg: String): void + { + if (pkg == null) + _throwNPE(); + + _do_checkPermission( + null // #todo + ); + } + + + fun *.checkPermission (@target self: LSLSecurityManager, perm: Permission): void + { + if (perm == null) + _throwNPE(); + + _do_checkPermission(perm); + } + + + fun *.checkPermission (@target self: LSLSecurityManager, perm: Permission, context: Object): void + { + if (context is AccessControlContext) + { + if (perm == null) + _throwNPE(); + + _do_checkPermission(perm); + } + else + { + action THROW_NEW("java.lang.SecurityException", []); + } + } + + + fun *.checkPrintJobAccess (@target self: LSLSecurityManager): void + { + _do_checkPermission( + null // #todo + ); + } + + + fun *.checkPropertiesAccess (@target self: LSLSecurityManager): void + { + _do_checkPermission( + null // #todo + ); + } + + + fun *.checkPropertyAccess (@target self: LSLSecurityManager, key: String): void + { + _do_checkPermission( + null // #todo + ); + } + + + fun *.checkRead (@target self: LSLSecurityManager, fd: FileDescriptor): void + { + _do_checkPermission( + null // #todo + ); + } + + + fun *.checkRead (@target self: LSLSecurityManager, file: String): void + { + _do_checkPermission( + null // #todo + ); + } + + + fun *.checkRead (@target self: LSLSecurityManager, file: String, context: Object): void + { + _do_checkPermission( + null // #todo + ); + } + + + fun *.checkSecurityAccess (@target self: LSLSecurityManager, _target: String): void + { + if (_target == null) + _throwNPE(); + + if (action CALL_METHOD(_target, "isEmpty", [])) + _throwIAE(); + + _do_checkPermission( + null // #todo + ); + } + + + fun *.checkSetFactory (@target self: LSLSecurityManager): void + { + _do_checkPermission( + null // #todo + ); + } + + + fun *.checkWrite (@target self: LSLSecurityManager, fd: FileDescriptor): void + { + _do_checkPermission( + null // #todo + ); + } + + + fun *.checkWrite (@target self: LSLSecurityManager, file: String): void + { + _do_checkPermission( + null // #todo + ); + } + + + fun *.getSecurityContext (@target self: LSLSecurityManager): Object + { + result = action SYMBOLIC("java.security.AccessControlContext"); + action ASSUME(result != null); + } + + + @Phantom fun *.getThreadGroup (@target self: LSLSecurityManager): ThreadGroup + { + // NOTE: using the original method - Thread modelling is a separate task on its own + // #todo: return valid thread group object + } + +} diff --git a/spec/java/lang/Short.lsl b/spec/java/lang/Short.lsl index de8647e6..43346d5d 100644 --- a/spec/java/lang/Short.lsl +++ b/spec/java/lang/Short.lsl @@ -17,8 +17,9 @@ import java/lang/Number; @FunctionalInterface("shortValue") @final type Short is java.lang.Short - for Comparable, Number, short + for Comparable, Number { + // WARNING: use 'shortValue' to get primitive value } diff --git a/spec/java/lang/StringBuffer.main.lsl b/spec/java/lang/StringBuffer.main.lsl index 0d7f8050..aaac6c51 100644 --- a/spec/java/lang/StringBuffer.main.lsl +++ b/spec/java/lang/StringBuffer.main.lsl @@ -26,10 +26,10 @@ automaton StringBufferAutomaton shift Allocated -> Initialized by [ // constructors - StringBuffer (StringBuffer), - StringBuffer (StringBuffer, CharSequence), - StringBuffer (StringBuffer, String), - StringBuffer (StringBuffer, int), + `` (StringBuffer), + `` (StringBuffer, CharSequence), + `` (StringBuffer, String), + `` (StringBuffer, int), ]; shift Initialized -> self by [ @@ -297,14 +297,14 @@ automaton StringBufferAutomaton // constructors - constructor *.StringBuffer (@target self: StringBuffer) + constructor *.`` (@target self: StringBuffer) { // This constructor's body is empty, because in original class is used byte array and this initializes 16 size; // In this realization is used "String" instead of to array; And this string initializes in "internal variables"; } - constructor *.StringBuffer (@target self: StringBuffer, seq: CharSequence) + constructor *.`` (@target self: StringBuffer, seq: CharSequence) { if (seq == null) _throwNPE(); @@ -313,7 +313,7 @@ automaton StringBufferAutomaton } - constructor *.StringBuffer (@target self: StringBuffer, str: String) + constructor *.`` (@target self: StringBuffer, str: String) { if (str == null) _throwNPE(); @@ -322,7 +322,7 @@ automaton StringBufferAutomaton } - constructor *.StringBuffer (@target self: StringBuffer, capacity: int) + constructor *.`` (@target self: StringBuffer, capacity: int) { // This constructor's body is empty, because in original class is used byte array and this initializes 16 + capacity size; // In this realization is used "String" instead of to array; And this string initializes in "internal variables"; @@ -342,7 +342,7 @@ automaton StringBufferAutomaton @synchronized fun *.append (@target self: StringBuffer, s: CharSequence, start: int, end: int): StringBuffer { var seqLength: int = 4; - var seq: String = "null"; + var seq: CharSequence = "null"; if (s != null) { seq = s; @@ -543,8 +543,8 @@ automaton StringBufferAutomaton // within java.lang.AbstractStringBuilder fun *.chars (@target self: StringBuffer): IntStream { - var intStorage: array = action ARRAY_NEW("int", this.length); - var storageChars: array = action CALL_METHOD(this.storage, "toCharArray", []); + val intStorage: array = action ARRAY_NEW("int", this.length); + val storageChars: array = action CALL_METHOD(this.storage, "toCharArray", []); var i: int = 0; action LOOP_FOR( @@ -552,12 +552,11 @@ automaton StringBufferAutomaton _toIntArray_loop(i, intStorage, storageChars) ); - val handlers: list = action LIST_NEW(); result = new IntStreamAutomaton(state = Initialized, - storage = intStorage, - length = this.length, - closeHandlers = handlers - ); + storage = intStorage, + length = this.length, + closeHandlers = action LIST_NEW() + ); } @@ -596,8 +595,8 @@ automaton StringBufferAutomaton // within java.lang.AbstractStringBuilder fun *.codePoints (@target self: StringBuffer): IntStream { - var intStorage: array = action ARRAY_NEW("int", this.length); - var storageChars: array = action CALL_METHOD(this.storage, "toCharArray", []); + val intStorage: array = action ARRAY_NEW("int", this.length); + val storageChars: array = action CALL_METHOD(this.storage, "toCharArray", []); var i: int = 0; action LOOP_FOR( @@ -605,12 +604,11 @@ automaton StringBufferAutomaton _toIntArray_loop(i, intStorage, storageChars) ); - val handlers: list = action LIST_NEW(); result = new IntStreamAutomaton(state = Initialized, - storage = intStorage, - length = this.length, - closeHandlers = handlers - ); + storage = intStorage, + length = this.length, + closeHandlers = action LIST_NEW() + ); } @@ -698,7 +696,7 @@ automaton StringBufferAutomaton { _checkOffset(dstOffset); var len: int = 4; - var new_s: String = "null"; + var new_s: CharSequence = "null"; if (s != null) { len = action CALL_METHOD(s, "length", []); @@ -714,7 +712,7 @@ automaton StringBufferAutomaton { _checkOffset(dstOffset); var len: int = 4; - var new_s: String = "null"; + var new_s: CharSequence = "null"; if (s != null) { len = action CALL_METHOD(s, "length", []); @@ -901,9 +899,7 @@ automaton StringBufferAutomaton { _checkIndex(index); - // result = action DEBUG_DO("Character.offsetByCodePoints(this.storage, index, codePointOffset)"); //current - - result = action CALL_METHOD(null as Character, "offsetByCodePoints", [this.storage, index, codePointOffset]) //need + result = action CALL_METHOD(null as Character, "offsetByCodePoints", [this.storage, index, codePointOffset]); } diff --git a/spec/java/lang/StringBuilder.main.lsl b/spec/java/lang/StringBuilder.main.lsl index 19ecf383..ab8ab54b 100644 --- a/spec/java/lang/StringBuilder.main.lsl +++ b/spec/java/lang/StringBuilder.main.lsl @@ -28,10 +28,10 @@ automaton StringBuilderAutomaton shift Allocated -> Initialized by [ // constructors - StringBuilder (StringBuilder), - StringBuilder (StringBuilder, CharSequence), - StringBuilder (StringBuilder, String), - StringBuilder (StringBuilder, int), + `` (StringBuilder), + `` (StringBuilder, CharSequence), + `` (StringBuilder, String), + `` (StringBuilder, int), ]; shift Initialized -> self by [ @@ -299,14 +299,14 @@ automaton StringBuilderAutomaton // constructors - constructor *.StringBuilder (@target self: StringBuilder) + constructor *.`` (@target self: StringBuilder) { // This constructor's body is empty, because in original class is used byte array and this initializes 16 size; // In this realization is used "String" instead of to array; And this string initializes in "internal variables"; } - constructor *.StringBuilder (@target self: StringBuilder, seq: CharSequence) + constructor *.`` (@target self: StringBuilder, seq: CharSequence) { if (seq == null) _throwNPE(); @@ -315,7 +315,7 @@ automaton StringBuilderAutomaton } - constructor *.StringBuilder (@target self: StringBuilder, str: String) + constructor *.`` (@target self: StringBuilder, str: String) { if (str == null) _throwNPE(); @@ -324,7 +324,7 @@ automaton StringBuilderAutomaton } - constructor *.StringBuilder (@target self: StringBuilder, capacity: int) + constructor *.`` (@target self: StringBuilder, capacity: int) { // This constructor's body is empty, because in original class is used byte array and this initializes 16 + capacity size; // In this realization is used "String" instead of to array; And this string initializes in "internal variables"; @@ -1047,8 +1047,8 @@ automaton StringBuilderAutomaton // within java.lang.AbstractStringBuilder fun *.codePoints (@target self: StringBuilder): IntStream { - var intStorage: array = action ARRAY_NEW("int", this.length); - var storageChars: array = action CALL_METHOD(this.storage, "toCharArray", []); + val intStorage: array = action ARRAY_NEW("int", this.length); + val storageChars: array = action CALL_METHOD(this.storage, "toCharArray", []); var i: int = 0; action LOOP_FOR( @@ -1056,20 +1056,19 @@ automaton StringBuilderAutomaton _toIntArray_loop(i, intStorage, storageChars) ); - val handlers: list = action LIST_NEW(); result = new IntStreamAutomaton(state = Initialized, - storage = intStorage, - length = this.length, - closeHandlers = handlers - ); + storage = intStorage, + length = this.length, + closeHandlers = action LIST_NEW() + ); } // within java.lang.AbstractStringBuilder fun *.chars (@target self: StringBuilder): IntStream { - var intStorage: array = action ARRAY_NEW("int", this.length); - var storageChars: array = action CALL_METHOD(this.storage, "toCharArray", []); + val intStorage: array = action ARRAY_NEW("int", this.length); + val storageChars: array = action CALL_METHOD(this.storage, "toCharArray", []); var i: int = 0; action LOOP_FOR( @@ -1077,12 +1076,11 @@ automaton StringBuilderAutomaton _toIntArray_loop(i, intStorage, storageChars) ); - val handlers: list = action LIST_NEW(); result = new IntStreamAutomaton(state = Initialized, - storage = intStorage, - length = this.length, - closeHandlers = handlers - ); + storage = intStorage, + length = this.length, + closeHandlers = action LIST_NEW() + ); } diff --git a/spec/java/lang/System.StdOut.lsl b/spec/java/lang/System.StdOut.lsl new file mode 100644 index 00000000..b717cfc7 --- /dev/null +++ b/spec/java/lang/System.StdOut.lsl @@ -0,0 +1,347 @@ +libsl "1.1.0"; + +library std + version "11" + language "Java" + url "https://github.com/openjdk/jdk11/blob/master/src/java.base/share/classes/java/io/PrintStream.java"; + +// imports + +import java/lang/CharSequence; +import java/lang/Object; +import java/lang/String; +import java/util/Locale; + +import java/lang/System; +import libsl/utils/VoidOutputStream; + + +// automata + +automaton System_PrintStreamAutomaton +( +) +: System_PrintStream +{ + // states and shifts + + initstate Initialized; + + shift Initialized -> self by [ + // instance methods + append (System_PrintStream, CharSequence), + append (System_PrintStream, CharSequence, int, int), + append (System_PrintStream, char), + checkError, + close, + flush, + format (System_PrintStream, Locale, String, array), + format (System_PrintStream, String, array), + print (System_PrintStream, Object), + print (System_PrintStream, String), + print (System_PrintStream, boolean), + print (System_PrintStream, char), + print (System_PrintStream, array), + print (System_PrintStream, double), + print (System_PrintStream, float), + print (System_PrintStream, int), + print (System_PrintStream, long), + printf (System_PrintStream, Locale, String, array), + printf (System_PrintStream, String, array), + println (System_PrintStream), + println (System_PrintStream, Object), + println (System_PrintStream, String), + println (System_PrintStream, boolean), + println (System_PrintStream, char), + println (System_PrintStream, array), + println (System_PrintStream, double), + println (System_PrintStream, float), + println (System_PrintStream, int), + println (System_PrintStream, long), + write (System_PrintStream, array), + write (System_PrintStream, array, int, int), + write (System_PrintStream, int), + ]; + + // internal variables + + var closed: boolean = false; + var error: boolean = false; + + + // utilities + + @AutoInline @Phantom proc _throwNPE (): void + { + action THROW_NEW("java.lang.NullPointerException", []); + } + + + @AutoInline @Phantom proc _checkOpen (): void + { + if (this.closed) + this.error = true; + } + + + // constructors + + // static methods + + // methods + + fun *.append (@target self: System_PrintStream, csq: CharSequence): PrintStream + { + _checkOpen(); + + result = self as Object as PrintStream; + } + + + fun *.append (@target self: System_PrintStream, csq: CharSequence, start: int, end: int): PrintStream + { + if (csq == null) + csq = "null"; + + val size: int = action CALL_METHOD(csq, "length", []); + if (start < 0 || end >= size) + action THROW_NEW("java.lang.StringIndexOutOfBoundsException", []); + + _checkOpen(); + + result = self as Object as PrintStream; + } + + + fun *.append (@target self: System_PrintStream, c: char): PrintStream + { + _checkOpen(); + + result = self as Object as PrintStream; + } + + + fun *.checkError (@target self: System_PrintStream): boolean + { + result = this.error; + } + + + fun *.close (@target self: System_PrintStream): void + { + // #todo: add synchronization for parallel executions + this.closed = true; + } + + + fun *.flush (@target self: System_PrintStream): void + { + // #todo: add synchronization for parallel executions + _checkOpen(); + } + + + @varargs fun *.format (@target self: System_PrintStream, l: Locale, format: String, args: array): PrintStream + { + if (format == null) + _throwNPE(); + + _checkOpen(); + + result = self as Object as PrintStream; + } + + + @varargs fun *.format (@target self: System_PrintStream, format: String, args: array): PrintStream + { + if (format == null) + _throwNPE(); + + _checkOpen(); + + result = self as Object as PrintStream; + } + + + fun *.print (@target self: System_PrintStream, obj: Object): void + { + _checkOpen(); + } + + + fun *.print (@target self: System_PrintStream, s: String): void + { + _checkOpen(); + } + + + fun *.print (@target self: System_PrintStream, b: boolean): void + { + _checkOpen(); + } + + + fun *.print (@target self: System_PrintStream, c: char): void + { + _checkOpen(); + } + + + fun *.print (@target self: System_PrintStream, s: array): void + { + if (s == null) + _throwNPE(); + + _checkOpen(); + } + + + fun *.print (@target self: System_PrintStream, d: double): void + { + _checkOpen(); + } + + + fun *.print (@target self: System_PrintStream, f: float): void + { + _checkOpen(); + } + + + fun *.print (@target self: System_PrintStream, i: int): void + { + _checkOpen(); + } + + + fun *.print (@target self: System_PrintStream, l: long): void + { + _checkOpen(); + } + + + @varargs fun *.printf (@target self: System_PrintStream, l: Locale, format: String, args: array): PrintStream + { + if (l == null || format == null || args == null) + _throwNPE(); + + _checkOpen(); + + result = self as Object as PrintStream; + } + + + @varargs fun *.printf (@target self: System_PrintStream, format: String, args: array): PrintStream + { + if (format == null || args == null) + _throwNPE(); + + _checkOpen(); + + result = self as Object as PrintStream; + } + + + fun *.println (@target self: System_PrintStream): void + { + _checkOpen(); + } + + + fun *.println (@target self: System_PrintStream, x: Object): void + { + _checkOpen(); + } + + + fun *.println (@target self: System_PrintStream, x: String): void + { + _checkOpen(); + } + + + fun *.println (@target self: System_PrintStream, x: boolean): void + { + _checkOpen(); + } + + + fun *.println (@target self: System_PrintStream, x: char): void + { + _checkOpen(); + } + + + fun *.println (@target self: System_PrintStream, x: array): void + { + if (x == null) + _throwNPE(); + + _checkOpen(); + } + + + fun *.println (@target self: System_PrintStream, x: double): void + { + _checkOpen(); + } + + + fun *.println (@target self: System_PrintStream, x: float): void + { + _checkOpen(); + } + + + fun *.println (@target self: System_PrintStream, x: int): void + { + _checkOpen(); + } + + + fun *.println (@target self: System_PrintStream, x: long): void + { + _checkOpen(); + } + + + @throws(["java.io.IOException"]) + // within java.io.OutputStream + fun *.write (@target self: System_PrintStream, b: array): void + { + if (b == null) + _throwNPE(); + + _checkOpen(); + } + + + fun *.write (@target self: System_PrintStream, buf: array, off: int, len: int): void + { + if (buf == null) + _throwNPE(); + + val size: int = action ARRAY_SIZE(buf); + if (off < 0 || off + len > size) + action THROW_NEW("java.lang.IndexOutOfBoundsException", []); + + _checkOpen(); + } + + + fun *.write (@target self: System_PrintStream, b: int): void + { + _checkOpen(); + } + + + // special methods + + @Phantom @static fun *.`` (): array + { + result = [ + null as OutputStream, + ]; + } + +} diff --git a/spec/java/lang/System.lsl b/spec/java/lang/System.lsl new file mode 100644 index 00000000..54ca6299 --- /dev/null +++ b/spec/java/lang/System.lsl @@ -0,0 +1,90 @@ +//#! pragma: non-synthesizable +libsl "1.1.0"; + +library std + version "11" + language "Java" + url "https://github.com/openjdk/jdk11/blob/master/src/java.base/share/classes/java/lang/System.java"; + +// imports + +import java/io/Console; +import java/io/InputStream; +import java/io/PrintStream; +import java/lang/Integer; +import java/lang/Object; +import java/lang/SecurityManager; +import java/lang/String; +import java/lang/Throwable; +import java/util/Properties; + + +// primary semantic types + +@final type System + is java.lang.System + for Object +{ +} + + +@final type System_Logger_Level + is java.lang.System.Logger.Level + for Object +{ + // #problem: enum constants +} + + +@interface type System_Logger + is java.lang.System.Logger + for Object +{ + fun *.getName(): String; + + fun *.isLoggable(level: System_Logger_Level): boolean; + + fun *.log(level: System_Logger_Level, msg: String): void; + + fun *.log(level: System_Logger_Level, obj: Object): void; + + fun *.log(level: System_Logger_Level, msg: String, thrown: Throwable): void; +} + + +// global aliases and type overrides + +@final type LSLSystem + is java.lang.System + for System +{ + @private @static val propsMap: map = action MAP_NEW(); + + @private @static @volatile var security: SecurityManager = null; // WARNING: do not change! + @private @static var props: Properties = null; // WARNING: do not change! + + // #todo: attach I/O streams from this + @private @static var console: Console = null; + + @public @static var in: InputStream = null; // WARNING: do not change! + @public @static var out: PrintStream = null; // WARNING: do not change! + @public @static var err: PrintStream = null; // WARNING: do not change! + + @private @static val NANOTIME_BEGINNING_OF_TIME: long = 1000L; + @private @static val NANOTIME_WARP_MAX: long = 1000L; + + @private @static val identityHashCodeMap: map = action IDENTITY_MAP_NEW(); +} + +// WARNING: declaration order is important! +val SYSTEM_IS_MAC: boolean = action SYMBOLIC("boolean"); +val SYSTEM_IS_WINDOWS: boolean = !SYSTEM_IS_MAC && action SYMBOLIC("boolean"); + + +@GenerateMe +@extends("java.io.PrintStream") +@public @final type System_PrintStream + is java.lang.System_PrintStream + for PrintStream +{ +} diff --git a/spec/java/lang/System.main.lsl b/spec/java/lang/System.main.lsl new file mode 100644 index 00000000..92eb39e4 --- /dev/null +++ b/spec/java/lang/System.main.lsl @@ -0,0 +1,651 @@ +libsl "1.1.0"; + +library std + version "11" + language "Java" + url "https://github.com/openjdk/jdk11/blob/master/src/java.base/share/classes/java/lang/System.java"; + +// imports + +import java/io/Console; +import java/io/InputStream; +import java/io/PrintStream; +import java/lang/Object; +import java/lang/SecurityManager; +import java/lang/String; +import java/nio/channels/Channel; +import java/util/Map; +import java/util/Properties; +import java/util/ResourceBundle; + +import jdk/internal/misc/VM; // #problem: does not exist in Java-8 +import sun/misc/Version; +import sun/misc/VM; // #problem: does not exist in Java-11 + +import java/lang/System; + +import libsl/utils/SymbolicInputStream; + + +// automata + +automaton SystemAutomaton +( +) +: LSLSystem +{ + // states and shifts + + initstate Initialized; + + shift Initialized -> self by [ + // constructors + ``, + + // static operations + arraycopy, + clearProperty, + console, + currentTimeMillis, + exit, + gc, + getLogger (String), + getLogger (String, ResourceBundle), + getProperties, + getProperty (String), + getProperty (String, String), + getSecurityManager, + getenv (), + getenv (String), + identityHashCode, + inheritedChannel, + lineSeparator, + load, + loadLibrary, + mapLibraryName, + nanoTime, + runFinalization, + setErr, + setIn, + setOut, + setProperties, + setProperty, + setSecurityManager, + ]; + + // internal variables + + // utilities + + @AutoInline @Phantom proc _throwNPE (): void + { + action THROW_NEW("java.lang.NullPointerException", []); + } + + + @AutoInline @Phantom proc _checkKey (key: String): void + { + if (key == null) + action THROW_NEW("java.lang.NullPointerException", ["key can't be null"]); + + if (action CALL_METHOD(key, "length", []) == 0) + action THROW_NEW("java.lang.NullPointerException", ["key can't be empty"]); + } + + + @static proc _initProperties (): void + { + // prepare raw values + val pm: map = propsMap; // NOTE: avoiding unwanted frequent static field access + + // NOTE: symbolic JRE version is too expensive to use + val javaVersion: int = 8; + + // NOTE: valid symbolic name is too expensive to use and construct + val userName: String = "Admin"; + + // convert it into properties + + action MAP_SET(pm, "file.encoding", "Cp1251"); + action MAP_SET(pm, "sun.io.unicode.encoding", "UnicodeLittle"); + action MAP_SET(pm, "sun.jnu.encoding", "Cp1251"); + action MAP_SET(pm, "sun.stderr.encoding", "cp866"); + action MAP_SET(pm, "sun.stdout.encoding", "cp866"); + + val versionStrings: array = [ + "0", "1", "2", "3", "4", "5", "6", "7", + "8", + "9", + "10", + "11", + "12", "13", "14", "15", + ]; + val versionString: String = versionStrings[javaVersion]; + + action MAP_SET(pm, "java.specification.name", "Java Platform API Specification"); + action MAP_SET(pm, "java.specification.vendor", "Oracle Corporation"); + action MAP_SET(pm, "java.specification.version", versionString); + action MAP_SET(pm, "java.vm.info", "mixed mode"); + action MAP_SET(pm, "java.vm.name", "OpenJDK 64-Bit Server VM"); + action MAP_SET(pm, "java.vm.specification.name", "Java Virtual Machine Specification"); + action MAP_SET(pm, "java.vm.specification.vendor", "Oracle Corporation"); + action MAP_SET(pm, "java.vm.specification.version", versionString); + action MAP_SET(pm, "java.vm.vendor", "Eclipse Adoptium"); + action MAP_SET(pm, "java.vm.version", versionString + ".0.362+9"); + + action MAP_SET(pm, "java.library.path", "C:\\Program Files\\Eclipse Adoptium\\jdk-8.0.362.9-hotspot\\bin;C:\\Windows\\Sun\\Java\\bin;C:\\Windows\\system32;."); + action MAP_SET(pm, "java.home", "C:\\Program Files\\Eclipse Adoptium\\jdk-8.0.362.9-hotspot"); + action MAP_SET(pm, "sun.boot.library.path", "C:\\Program Files\\Eclipse Adoptium\\jdk-8.0.362.9-hotspot\\bin"); + action MAP_SET(pm, "java.io.tmpdir", "T:\\Temp\\"); + action MAP_SET(pm, "java.class.path", "."); + + if (SYSTEM_IS_WINDOWS) + { + action MAP_SET(pm, "file.separator", "\\"); + action MAP_SET(pm, "line.separator", "\r\n"); + action MAP_SET(pm, "path.separator", ";"); + } + else + { + action MAP_SET(pm, "file.separator", "/"); + action MAP_SET(pm, "line.separator", "\n"); + action MAP_SET(pm, "path.separator", ":"); + } + + action MAP_SET(pm, "user.country", "RU"); + action MAP_SET(pm, "user.country.format", "US"); + action MAP_SET(pm, "user.language", "ru"); + + val bytecodeVersions: array = [ + "?", /* 0? */ + "?", /* 1? */ + "?", /* 2? */ + "?", /* 3? */ + "?", /* 4? */ + "49.0", /* Java SE 5 */ + "50.0", /* Java SE 6 */ + "51.0", /* Java SE 7 */ + "52.0", /* Java SE 8 */ + "53.0", /* Java SE 9 */ + "54.0", /* Java SE 10 */ + "55.0", /* Java SE 11 */ + "?", /* 12? */ + "?", /* 13? */ + "?", /* 14? */ + "?", /* 15? */ + ]; + action MAP_SET(pm, "java.class.version", bytecodeVersions[javaVersion]); + + action MAP_SET(pm, "os.arch", "amd64"); + action MAP_SET(pm, "os.name", "Windows 10"); + action MAP_SET(pm, "os.version", "10.0"); + action MAP_SET(pm, "sun.arch.data.model", "64"); + action MAP_SET(pm, "sun.cpu.endian", "little"); + action MAP_SET(pm, "sun.cpu.isalist", "amd64"); + action MAP_SET(pm, "sun.desktop", "windows"); + + action MAP_SET(pm, "user.dir", "D:\\Company\\Prod\\Service"); + action MAP_SET(pm, "user.home", "C:\\Users\\" + userName); + action MAP_SET(pm, "user.name", userName); + action MAP_SET(pm, "user.script", ""); + action MAP_SET(pm, "user.timezone", ""); + action MAP_SET(pm, "user.variant", ""); + + // unknown misc stuff + action MAP_SET(pm, "sun.java.command", "org.example.MainClass"); // #problem: main class + action MAP_SET(pm, "awt.toolkit", "sun.awt.windows.WToolkit"); + action MAP_SET(pm, "java.awt.graphicsenv", "sun.awt.Win32GraphicsEnvironment"); + action MAP_SET(pm, "java.awt.printerjob", "sun.awt.windows.WPrinterJob"); + action MAP_SET(pm, "sun.java.launcher", "SUN_STANDARD"); + action MAP_SET(pm, "sun.management.compiler", "HotSpot 64-Bit Tiered Compilers"); + action MAP_SET(pm, "sun.nio.MaxDirectMemorySize", "-1"); + action MAP_SET(pm, "sun.os.patch.level", ""); + action MAP_SET(pm, "java.vm.compressedOopsMode", "Zero based"); + action MAP_SET(pm, "jdk.boot.class.path.append", ""); + action MAP_SET(pm, "jdk.debug", "release"); + + // #problem: no approximation for Properties + props = null;//new Properties(84); + // #todo: init 'props' from the map above + } + + + @AutoInline @Phantom proc _checkIO (): void + { + val sm: SecurityManager = security; + if (sm != null) + action CALL_METHOD(sm, "checkPermission", [ + action DEBUG_DO("new RuntimePermission(\"setIO\")") + ]); + } + + + // constructors + + @private constructor *.`` (@target self: LSLSystem) + { + // doing nothing - this is a (singleton) utility class + } + + + // static methods + + @Phantom @static fun *.arraycopy (src: Object, srcPos: int, dest: Object, destPos: int, length: int): void + { + // WARNING: do not approximate this method - infinite recursion! + } + + + @static fun *.clearProperty (key: String): String + { + _checkKey(key); + + val sm: SecurityManager = security; + if (sm != null) + action CALL_METHOD(sm, "checkPermission", [ + action DEBUG_DO("new java.util.PropertyPermission(key, \"write\")") + ]); + + val pm: map = propsMap; + if (action MAP_HAS_KEY(pm, key)) + { + result = action MAP_GET(pm, key); + + // #todo: remove key from 'props' + + action MAP_REMOVE(pm, key); + } + } + + + @static fun *.console (): Console + { + result = console; + } + + + @Phantom @static fun *.currentTimeMillis (): long + { + // #todo: use NANOTIME_WARP_MAX and NANOTIME_BEGINNING_OF_TIME + action TODO(); + } + + + @static fun *.exit (status: int): void + { + // #problem: not way to forcibly shutdown the program execution + action ERROR("Unexpected shutdown"); + } + + + @static fun *.gc (): void + { + // this have no effect during symbolic execution + } + + + @Phantom @static fun *.getLogger (name: String): System_Logger + { + // NOTE: using the original method + } + + + @Phantom @static fun *.getLogger (name: String, bundle: ResourceBundle): System_Logger + { + // NOTE: using the original method + } + + + @static fun *.getProperties (): Properties + { + val sm: SecurityManager = security; + if (sm != null) + action CALL_METHOD(sm, "checkPropertiesAccess", []); + + result = props; + } + + + @static fun *.getProperty (key: String): String + { + _checkKey(key); + + val sm: SecurityManager = security; + if (sm != null) + action CALL_METHOD(sm, "checkPropertyAccess", [key]); + + val pm: map = propsMap; + if (action MAP_HAS_KEY(pm, key)) + result = action MAP_GET(pm, key); + else + result = null; + } + + + @static fun *.getProperty (key: String, def: String): String + { + _checkKey(key); + + val sm: SecurityManager = security; + if (sm != null) + action CALL_METHOD(sm, "checkPropertyAccess", [key]); + + val pm: map = propsMap; + if (action MAP_HAS_KEY(pm, key)) + result = action MAP_GET(pm, key); + else + result = def; + } + + + @static fun *.getSecurityManager (): SecurityManager + { + result = security; + } + + + @Phantom @static fun *.getenv (): Map + { + // NOTE: using the original method + + val sm: SecurityManager = security; + if (sm != null) + action CALL_METHOD(sm, "checkPermission", [ + action DEBUG_DO("new RuntimePermission(\"getenv.*\")") + ]); + + // #todo: provide an actual map with symbolic values + } + + + @static fun *.getenv (name: String): String + { + val sm: SecurityManager = security; + if (sm != null) + action CALL_METHOD(sm, "checkPermission", [ + action DEBUG_DO("new RuntimePermission(\"getenv.\".concat(name))") + ]); + + result = action SYMBOLIC("java.lang.String"); + action ASSUME(result != null); + + val len: int = action CALL_METHOD(result, "length", []); + action ASSUME(len >= 0); + action ASSUME(len < 250); + } + + + @static fun *.identityHashCode (x: Object): int + { + if (x == null) + { + result = 0; + } + else if (action MAP_HAS_KEY(identityHashCodeMap, x)) + { + val value: Integer = action MAP_GET(identityHashCodeMap, x); + action ASSUME(value != null); + result = action CALL_METHOD(value, "intValue", []); + } + else + { + // sequential unique numbers + result = action MAP_SIZE(identityHashCodeMap); + + val hash: Integer = action CALL_METHOD(null as Integer, "valueOf", [result]); + action MAP_SET(identityHashCodeMap, x, hash); + } + } + + + @throws(["java.io.IOException"]) + @Phantom @static fun *.inheritedChannel (): Channel + { + // NOTE: using the original method + } + + + @static fun *.lineSeparator (): String + { + result = action MAP_GET(propsMap, "line.separator"); + } + + + @static fun *.load (filename: String): void + { + if (filename == null) + _throwNPE(); + + // faking underlying checks and loading procedures + if (action SYMBOLIC("boolean")) action THROW_NEW("java.lang.SecurityException", [""]); + if (action SYMBOLIC("boolean")) action THROW_NEW("java.lang.UnsatisfiedLinkError", [""]); + } + + + @static fun *.loadLibrary (libname: String): void + { + if (libname == null) + _throwNPE(); + + // faking underlying checks and loading procedures + if (action SYMBOLIC("boolean")) action THROW_NEW("java.lang.SecurityException", [""]); + if (action SYMBOLIC("boolean")) action THROW_NEW("java.lang.UnsatisfiedLinkError", [""]); + } + + + @static fun *.mapLibraryName (libname: String): String + { + if (libname == null) + _throwNPE(); + + // https://github.com/openjdk/jdk8/blob/master/jdk/src/share/native/java/lang/System.c#L466 + val len: int = action CALL_METHOD(libname, "length", []); + if (len > 240) + action THROW_NEW("java.lang.IllegalArgumentException", ["name too long"]); + + if (SYSTEM_IS_WINDOWS) + result = libname + ".dll"; + else if (SYSTEM_IS_MAC) + result = "lib" + libname + ".dylib"; + else + result = "lib" + libname + ".so"; + } + + + @Phantom @static fun *.nanoTime (): long + { + // #todo: use NANOTIME_WARP_MAX and NANOTIME_BEGINNING_OF_TIME + action TODO(); + } + + + @static fun *.runFinalization (): void + { + // #problem: it is not possible to call finalizer method on an arbitrary object + } + + + @static fun *.setErr (stream: PrintStream): void + { + _checkIO(); + err = stream; + } + + + @static fun *.setIn (stream: InputStream): void + { + _checkIO(); + in = stream; + } + + + @static fun *.setOut (stream: PrintStream): void + { + _checkIO(); + out = stream; + } + + + @static fun *.setProperties (p: Properties): void + { + val sm: SecurityManager = security; + if (sm != null) + action CALL_METHOD(sm, "checkPropertiesAccess", []); + + // #todo: improve after there will be some implementation for Properties + props = p; + } + + + @static fun *.setProperty (key: String, value: String): String + { + _checkKey(key); + + val sm: SecurityManager = security; + if (sm != null) + action CALL_METHOD(sm, "checkPermission", [ + action DEBUG_DO("new java.util.PropertyPermission(key, \"write\")") + ]); + + // #todo: update 'props' + + val pm: map = propsMap; + if (action MAP_HAS_KEY(pm, key)) + result = action MAP_GET(pm, key); + else + result = null; + + action MAP_SET(pm, key, value); + } + + + @static fun *.setSecurityManager (s: SecurityManager): void + { + val sm: SecurityManager = security; + if (sm != null) + action CALL_METHOD(sm, "checkPermission", [ + action DEBUG_DO("new RuntimePermission(\"setSecurityManager\")") + ]); + + // #problem: no protection domain checks here + + security = s; + } + + + // methods + + // special: internal class initialization by the JRE + + @private @static proc initPhase1 (): void // WARNING: do not change signature! + { + _initProperties(); + + //action CALL_METHOD(null as VM, "saveAndRemoveProperties", [props]); + + //action CALL_METHOD(null as StaticProperty, "javaHome", []); + //VersionProps.init(); + + // configure the standard streams + val newInput: InputStream = new SymbolicInputStreamAutomaton(state = Initialized, + maxSize = 1000, + supportMarks = false, + ); + // #todo: unsafe operations in BufferedInputStream + in = newInput;//action DEBUG_DO("new java.io.BufferedInputStream(newInput)"); + out = new System_PrintStreamAutomaton(state = Initialized); + err = new System_PrintStreamAutomaton(state = Initialized); + + // #problem: no OS signal support + //Terminator.setup(); + + // JDK comment: system is fully initialized + //action CALL_METHOD(null as VM, "initializeOSEnvironment", []); <- NPE + + // #problem: no thread support + //val current: Thread = action CALL_METHOD(null as Thread, "currentThread", []); + //val threadGroup: ThreadGroup = action CALL_METHOD(current, "getThreadGroup", []); + //action CALL_METHOD(threadGroup, "add", [current]); + + // #problem: unable to call or model 'java.lang.System#setJavaLangAccess' + + // #problem: too complex, package-private + //action CALL_METHOD(null as ClassLoader, "initLibraryPaths", []); + + // JDK comment: IMPORTANT: Ensure that this remains the last initialization action! + action CALL_METHOD(null as VM, "initLevel", [1]); + } + + + @private @static proc initPhase2 (): int // WARNING: do not change signature! + { + // #problem: java.lang.System#bootLayer initialization + + // JDK comment: module system initialized + action CALL_METHOD(null as VM, "initLevel", [2]); + + // JDK comment: JNI_OK + result = 0; + } + + + @private @static proc initPhase3 (): void // WARNING: do not change signature! + { + // #problem: reflective access during security manager initialization + // #todo: check property 'java.security.manager' + security = null;//new SecurityManagerAutomaton(state = Initialized); + + // JDK comment: initializing the system class loader + action CALL_METHOD(null as VM, "initLevel", [3]); + + // #problem: java.lang.ClassLoader#initSystemClassLoader is package-private + + // JDK comment: system is fully initialized + action CALL_METHOD(null as VM, "initLevel", [4]); + } + + + // NOTE: not using this + @Phantom @private @static proc init_as_java8 (): void + { + // #todo: use dedicated Properties automaton + props = action DEBUG_DO("new Properties()"); + + _initProperties(); + + action CALL_METHOD(null as SUN_Version, "init", []); + + // configure the standard streams + val newInput: InputStream = new SymbolicInputStreamAutomaton(state = Initialized, + maxSize = 1000, + supportMarks = false, + ); + // #todo: unsafe operations in BufferedInputStream + in = newInput;//action DEBUG_DO("new java.io.BufferedInputStream(newInput)"); + out = new System_PrintStreamAutomaton(state = Initialized); + err = new System_PrintStreamAutomaton(state = Initialized); + + // #problem: thread group initialization + + // #problem: unable to call or model 'java.lang.System#setJavaLangAccess' + + action CALL_METHOD(null as SUN_VM, "booted", []); + } + + + // special: static initialization + + @Phantom @static fun *.`` (): void + { + // #problem: version-specific initialization + + initPhase1(); + initPhase2(); + initPhase3(); + + /* + init_as_java8(); + */ + } + +} diff --git a/spec/java/lang/Thread.lsl b/spec/java/lang/Thread.lsl new file mode 100644 index 00000000..6b5284c4 --- /dev/null +++ b/spec/java/lang/Thread.lsl @@ -0,0 +1,20 @@ +//#! pragma: non-synthesizable +libsl "1.1.0"; + +library std + version "11" + language "Java" + url "https://github.com/openjdk/jdk11/blob/master/src/java.base/share/classes/java/lang/Thread.java"; + +// imports + +import java/lang/Object; + + +// primary semantic types + +@public type Thread + is java.lang.Thread + for Object +{ +} diff --git a/spec/java/lang/ThreadGroup.lsl b/spec/java/lang/ThreadGroup.lsl new file mode 100644 index 00000000..04881531 --- /dev/null +++ b/spec/java/lang/ThreadGroup.lsl @@ -0,0 +1,20 @@ +//#! pragma: non-synthesizable +libsl "1.1.0"; + +library std + version "11" + language "Java" + url "https://github.com/openjdk/jdk11/blob/master/src/java.base/share/classes/java/lang/ThreadGroup.java"; + +// imports + +import java/lang/Object; + + +// primary semantic types + +@public type ThreadGroup + is java.lang.ThreadGroup + for Object +{ +} diff --git a/spec/java/lang/Throwable.main.lsl b/spec/java/lang/Throwable.main.lsl index 47bfff96..8e7e58bd 100644 --- a/spec/java/lang/Throwable.main.lsl +++ b/spec/java/lang/Throwable.main.lsl @@ -29,11 +29,11 @@ automaton ThrowableAutomaton shift Initialized -> self by [ // constructors - Throwable (LSLThrowable), - Throwable (LSLThrowable, String), - Throwable (LSLThrowable, String, Throwable), - Throwable (LSLThrowable, String, Throwable, boolean, boolean), - Throwable (LSLThrowable, Throwable), + `` (LSLThrowable), + `` (LSLThrowable, String), + `` (LSLThrowable, String, Throwable), + `` (LSLThrowable, String, Throwable, boolean, boolean), + `` (LSLThrowable, Throwable), // instance methods addSuppressed, @@ -57,36 +57,36 @@ automaton ThrowableAutomaton // constructors - @Phantom constructor *.Throwable (@target self: LSLThrowable) + @Phantom constructor *.`` (@target self: LSLThrowable) { // Note: using the original method action TODO(); } - @Phantom constructor *.Throwable (@target self: LSLThrowable, message: String) + @Phantom constructor *.`` (@target self: LSLThrowable, message: String) { // Note: using the original method action TODO(); } - @Phantom constructor *.Throwable (@target self: LSLThrowable, message: String, cause: Throwable) + @Phantom constructor *.`` (@target self: LSLThrowable, message: String, cause: Throwable) { // Note: using the original method action TODO(); } - @Phantom @protected constructor *.Throwable (@target self: LSLThrowable, - message: String, cause: Throwable, enableSuppression: boolean, writableStackTrace: boolean) + @Phantom @protected constructor *.`` (@target self: LSLThrowable, + message: String, cause: Throwable, enableSuppression: boolean, writableStackTrace: boolean) { // Note: using the original method action TODO(); } - @Phantom constructor *.Throwable (@target self: LSLThrowable, cause: Throwable) + @Phantom constructor *.`` (@target self: LSLThrowable, cause: Throwable) { // Note: using the original method action TODO(); diff --git a/spec/java/net/InetAddress.lsl b/spec/java/net/InetAddress.lsl new file mode 100644 index 00000000..fe59270c --- /dev/null +++ b/spec/java/net/InetAddress.lsl @@ -0,0 +1,20 @@ +//#! pragma: non-synthesizable +libsl "1.1.0"; + +library std + version "11" + language "Java" + url "https://github.com/openjdk/jdk11/blob/master/src/java.base/share/classes/java/net/InetAddress.java"; + +// imports + +import java/lang/Object; + + +// primary semantic types + +@public type InetAddress + is java.net.InetAddress + for Object +{ +} diff --git a/spec/java/nio/CharBuffer.lsl b/spec/java/nio/CharBuffer.lsl new file mode 100644 index 00000000..5a1c2dbf --- /dev/null +++ b/spec/java/nio/CharBuffer.lsl @@ -0,0 +1,27 @@ +//#! pragma: non-synthesizable +libsl "1.1.0"; + +library std + version "11" + language "Java" + url "https://github.com/openjdk/jdk11/blob/master/src/java.base/share/classes/java/nio/CharBuffer.java"; + +// imports + +import java/lang/Appendable; +import java/lang/CharSequence; +import java/lang/Comparable; +import java/lang/Readable; +import java/nio/Buffer; + + +// primary semantic types + +@abstract type CharBuffer + is java.nio.CharBuffer + for Buffer, Comparable, Appendable, CharSequence, Readable +{ +} + + +// global aliases and type overrides diff --git a/spec/java/nio/channels/Channel.lsl b/spec/java/nio/channels/Channel.lsl new file mode 100644 index 00000000..8606ba73 --- /dev/null +++ b/spec/java/nio/channels/Channel.lsl @@ -0,0 +1,24 @@ +//#! pragma: non-synthesizable +libsl "1.1.0"; + +library std + version "11" + language "Java" + url "https://github.com/openjdk/jdk11/blob/master/src/java.base/share/classes/java/nio/channels/Channel.java"; + +// imports + +import java/io/Closeable; + + +// primary semantic types + +@interface type Channel + is java.nio.channels.Channel + for Closeable +{ +} + + +// global aliases and type overrides + diff --git a/spec/java/nio/charset/Charset.lsl b/spec/java/nio/charset/Charset.lsl new file mode 100644 index 00000000..757522f9 --- /dev/null +++ b/spec/java/nio/charset/Charset.lsl @@ -0,0 +1,30 @@ +//#! pragma: non-synthesizable +libsl "1.1.0"; + +library std + version "11" + language "Java" + url "https://github.com/openjdk/jdk11/blob/master/src/java.base/share/classes/java/nio/charset/Charset.java"; + +// imports + +import java/lang/Comparable; + + +// primary semantic types + +@abstract type Charset + is java.nio.charset.Charset + for Comparable +{ +} + + +// global aliases and type overrides + +@implements("java.lang.Comparable") +@abstract type LSLCharset + is java.nio.charset.Charset + for Charset +{ +} diff --git a/spec/java/nio/charset/Charset.main.lsl b/spec/java/nio/charset/Charset.main.lsl new file mode 100644 index 00000000..edb006f2 --- /dev/null +++ b/spec/java/nio/charset/Charset.main.lsl @@ -0,0 +1,180 @@ +//#! pragma: non-synthesizable +libsl "1.1.0"; + +library std + version "11" + language "Java" + url "https://github.com/openjdk/jdk11/blob/master/src/java.base/share/classes/java/nio/charset/Charset.java"; + +// imports + +import java/lang/Object; +import java/lang/String; +import java/nio/ByteBuffer; +import java/nio/CharBuffer; +import java/util/Locale; +import java/util/Set; +import java/util/SortedMap; + +import java/nio/charset/Charset; + + +// automata + +automaton CharsetAutomaton +( +) +: LSLCharset +{ + // states and shifts + + initstate Allocated; + state Initialized; + + shift Allocated -> Initialized by [ + // constructors + ``, + + // static operations + availableCharsets, + defaultCharset, + forName, + isSupported, + ]; + + shift Initialized -> self by [ + // instance methods + aliases, + canEncode, + compareTo, + decode, + displayName (LSLCharset), + displayName (LSLCharset, Locale), + encode (LSLCharset, CharBuffer), + encode (LSLCharset, String), + equals, + hashCode, + isRegistered, + name, + toString, + ]; + + // internal variables + + // utilities + + // constructors + + @protected constructor *.`` (@target self: LSLCharset, canonicalName: String, aliases: array) + { + action TODO(); + } + + + // static methods + + @static fun *.availableCharsets (): SortedMap + { + action TODO(); + } + + + @static fun *.defaultCharset (): LSLCharset + { + action TODO(); + } + + + @static fun *.forName (charsetName: String): LSLCharset + { + action TODO(); + } + + + @static fun *.isSupported (charsetName: String): boolean + { + action TODO(); + } + + + // methods + + @final fun *.aliases (@target self: LSLCharset): Set + { + action TODO(); + } + + + fun *.canEncode (@target self: LSLCharset): boolean + { + // just like the original + result = true; + } + + + @final fun *.compareTo (@target self: LSLCharset, that: Charset): int + { + action TODO(); + } + + + @final fun *.decode (@target self: LSLCharset, bb: ByteBuffer): CharBuffer + { + action TODO(); + } + + + fun *.displayName (@target self: LSLCharset): String + { + action TODO(); + } + + + fun *.displayName (@target self: LSLCharset, locale: Locale): String + { + action TODO(); + } + + + @final fun *.encode (@target self: LSLCharset, cb: CharBuffer): ByteBuffer + { + action TODO(); + } + + + @final fun *.encode (@target self: LSLCharset, str: String): ByteBuffer + { + action TODO(); + } + + + @final fun *.equals (@target self: LSLCharset, ob: Object): boolean + { + action TODO(); + } + + + @final fun *.hashCode (@target self: LSLCharset): int + { + action TODO(); + } + + + @final fun *.isRegistered (@target self: LSLCharset): boolean + { + action TODO(); + } + + + @final fun *.name (@target self: LSLCharset): String + { + action TODO(); + } + + + @final fun *.toString (@target self: LSLCharset): String + { + action TODO(); + } + +} diff --git a/spec/java/security/AccessControlContext.lsl b/spec/java/security/AccessControlContext.lsl new file mode 100644 index 00000000..cf7ca478 --- /dev/null +++ b/spec/java/security/AccessControlContext.lsl @@ -0,0 +1,20 @@ +//#! pragma: non-synthesizable +libsl "1.1.0"; + +library std + version "11" + language "Java" + url "https://github.com/openjdk/jdk11/blob/master/src/java.base/share/classes/java/security/AccessControlContext.java"; + +// imports + +import java/lang/Object; + + +// primary semantic types + +@public type AccessControlContext + is java.security.AccessControlContext + for Object +{ +} diff --git a/spec/java/security/Permission.lsl b/spec/java/security/Permission.lsl index e0dba90c..b4cbb384 100644 --- a/spec/java/security/Permission.lsl +++ b/spec/java/security/Permission.lsl @@ -19,9 +19,9 @@ type Permission is java.security.Permission for Guard, Serializable { - fun getName(): String; + fun *.getName(): String; - fun getActions(): String; + fun *.getActions(): String; // #problem: cyclic reference //fun newPermissionCollection(): PermissionCollection; diff --git a/spec/java/security/Provider.lsl b/spec/java/security/Provider.lsl index 3feedd17..b3f7dcb1 100644 --- a/spec/java/security/Provider.lsl +++ b/spec/java/security/Provider.lsl @@ -8,28 +8,25 @@ library std // imports -import java/util/Properties; +import java/lang/Object; import java/lang/String; +import java/util/Properties; import java/util/Set; -import java/lang/Object; // primary semantic types -@extends("java.util.Properties") @abstract type Provider is java.security.Provider for Properties { - @private @static val serialVersionUID: long = -4298000515446427739L; - fun *.getName(): String; fun *.getServices(): Set; } -@static type Provider_Service +type Provider_Service is java.security.Provider.Service for Object { @@ -39,4 +36,5 @@ import java/lang/Object; } -// global aliases and type overrides \ No newline at end of file +// global aliases and type overrides + diff --git a/spec/java/security/SecureRandom.lsl b/spec/java/security/SecureRandom.lsl index f58c2e45..7780abb5 100644 --- a/spec/java/security/SecureRandom.lsl +++ b/spec/java/security/SecureRandom.lsl @@ -13,8 +13,7 @@ import java/util/Random; // primary semantic types -@extends("java.util.Random") -@public type SecureRandom +type SecureRandom is java.security.SecureRandom for Random { @@ -23,12 +22,12 @@ import java/util/Random; // global aliases and type overrides -// a replacement type for automata construction @extends("java.util.Random") -@public type SecureRandomLSL +type SecureRandomLSL is java.security.SecureRandom for SecureRandom { @private @static val serialVersionUID: long = 4940670005562187L; + @private @static val defaultProvidersMap: map = action MAP_NEW(); -} \ No newline at end of file +} diff --git a/spec/java/security/SecureRandom.main.lsl b/spec/java/security/SecureRandom.main.lsl index 9619b91a..c7fe8802 100644 --- a/spec/java/security/SecureRandom.main.lsl +++ b/spec/java/security/SecureRandom.main.lsl @@ -25,7 +25,7 @@ automaton SecureRandomAutomaton ( var provider: Provider, var algorithm: String, - var defaultProvider: boolean + var defaultProvider: boolean, ) : SecureRandomLSL { @@ -36,10 +36,11 @@ automaton SecureRandomAutomaton shift Allocated -> Initialized by [ // constructors - SecureRandom (SecureRandom), - SecureRandom (SecureRandom, SecureRandomSpi, Provider), - SecureRandom (SecureRandom, SecureRandomSpi, Provider, String), - SecureRandom (SecureRandom, array), + `` (SecureRandom), + `` (SecureRandom, SecureRandomSpi, Provider), + `` (SecureRandom, SecureRandomSpi, Provider, String), + `` (SecureRandom, array), + // static operations getInstance (String), getInstance (String, Provider), @@ -94,7 +95,7 @@ automaton SecureRandomAutomaton @AutoInline @Phantom proc _throwIE (): void { - action THROW_NEW("java.lang.InternalError", []); + action ERROR("java.lang.InternalError"); // action THROW_NEW("java.lang.InternalError", []); } @@ -112,13 +113,17 @@ automaton SecureRandomAutomaton proc _getDefaultPRNG (): void { + /* this.provider = action SYMBOLIC("java.security.Provider"); - this.algorithm = action SYMBOLIC("java.lang.String"); + this.algorithm = action SYMBOLIC("java.lang.String"); action ASSUME(action CALL_METHOD(this.algorithm, "length", []) > 0); if (this.provider == null || this.algorithm == null) _throwIE(); + */ + this.provider = null; // #problem: approximate the provider (symbolic is too dificult for the USVM) + this.algorithm = "SHA1PRNG"; // #todo: get from the provider this.defaultProvider = _isDefaultProvider(this.provider); } @@ -126,26 +131,30 @@ automaton SecureRandomAutomaton @static proc _isDefaultProvider (curProvider: Provider): boolean { - val providerName: String = action CALL_METHOD(curProvider, "getName", []); - result = action MAP_HAS_KEY(this.defaultProvidersMap, providerName); - } - - - @static proc _nextBytes (result: array, numBytes: int): void - { - val symbolicArray: array = action SYMBOLIC_ARRAY("byte", numBytes); - action ARRAY_COPY(symbolicArray, 0, result, 0, numBytes); + if (curProvider == null) + { + result = false; // no visible effect on anything + } + else + { + val providerName: String = action CALL_METHOD(curProvider, "getName", []); + result = action MAP_HAS_KEY(this.defaultProvidersMap, providerName); + } } proc _generateRandomIntegerArrayWithBounds (size: int, randomNumberOrigin: int, randomNumberBound: int): array { result = action SYMBOLIC_ARRAY("int", size); + + // #problem: too complex for symbolic execution + /* var i: int = 0; action LOOP_FOR( i, 0, size, +1, checkIntBounds_loop(i, result, randomNumberOrigin, randomNumberBound) ); + */ } @@ -159,11 +168,15 @@ automaton SecureRandomAutomaton proc _generateRandomLongArrayWithBounds (size: int, randomNumberOrigin: long, randomNumberBound: long): array { result = action SYMBOLIC_ARRAY("long", size); + + // #problem: too complex for symbolic execution + /* var i: int = 0; action LOOP_FOR( i, 0, size, +1, checkLongBounds_loop(i, result, randomNumberOrigin, randomNumberBound) ); + */ } @@ -177,11 +190,15 @@ automaton SecureRandomAutomaton proc _generateRandomDoubleArrayWithBounds (size: int, randomNumberOrigin: double, randomNumberBound: double): array { result = action SYMBOLIC_ARRAY("double", size); + + // #problem: too complex for symbolic execution + /* var i: int = 0; action LOOP_FOR( i, 0, size, +1, checkDoubleBounds_loop(i, result, randomNumberOrigin, randomNumberBound) ); + */ } @@ -191,32 +208,31 @@ automaton SecureRandomAutomaton action ASSUME(item == item); action ASSUME(item >= randomNumberOrigin); action ASSUME(item < randomNumberBound); + result[i] = item; } // constructors - constructor *.SecureRandom (@target self: SecureRandom) + constructor *.`` (@target self: SecureRandom) { _getDefaultPRNG(); } - // #question: do we need such realization of constructor ? Or it must be empty because this is "protected" ? - @protected constructor *.SecureRandom (@target self: SecureRandom, secureRandomSpi: SecureRandomSpi, provider: Provider) + @protected constructor *.`` (@target self: SecureRandom, secureRandomSpi: SecureRandomSpi, provider: Provider) { action ERROR("Protected constructor call"); } - // #question: do we need such realization of constructor ? Or it must be empty because this is "private" ? - @private constructor *.SecureRandom (@target self: SecureRandom, secureRandomSpi: SecureRandomSpi, provider: Provider, algorithm: String) + @private constructor *.`` (@target self: SecureRandom, secureRandomSpi: SecureRandomSpi, provider: Provider, algorithm: String) { action ERROR("Private constructor call"); } - constructor *.SecureRandom (@target self: SecureRandom, seed: array) + constructor *.`` (@target self: SecureRandom, seed: array) { _getDefaultPRNG(); } @@ -299,9 +315,7 @@ automaton SecureRandomAutomaton // #note this property can be disabled with SecurityManager. We assume that this situation is impossible, // because we can't check enable/disable property status. And we can't invoke "AccessController.doPrivileged" method like in source code; - val propertyName: String = "securerandom.strongAlgorithms"; - val property: String = action CALL_METHOD(null as Security, "getProperty", [propertyName]); - + val property: String = action CALL_METHOD(null as Security, "getProperty", ["securerandom.strongAlgorithms"]); if (property == null) _throwNSAE(); @@ -327,8 +341,8 @@ automaton SecureRandomAutomaton { if (numBytes < 0) _throwIAE(); - result = action ARRAY_NEW("byte", numBytes); - _nextBytes(result, numBytes); + + result = action SYMBOLIC_ARRAY("byte", numBytes); } @@ -350,7 +364,9 @@ automaton SecureRandomAutomaton { if (randomNumberOrigin >= randomNumberBound) _throwIAE(); + result = new DoubleStreamAutomaton(state = Initialized, + // #problem: too complex for symbolic execution storage = _generateRandomDoubleArrayWithBounds(MAX_RANDOM_STREAM_SIZE, randomNumberOrigin, randomNumberBound), length = MAX_RANDOM_STREAM_SIZE, closeHandlers = action LIST_NEW(), @@ -364,6 +380,7 @@ automaton SecureRandomAutomaton var size: int = streamSize as int; if (size < 0) _throwIAE(); + // WARNING: this is our special constraint; We must constraint infinite stream for USVM. if (size > MAX_RANDOM_STREAM_SIZE) size = MAX_RANDOM_STREAM_SIZE; @@ -384,6 +401,7 @@ automaton SecureRandomAutomaton _throwIAE(); if (randomNumberOrigin >= randomNumberBound) _throwIAE(); + // WARNING: this is our special constraint; We must constraint infinite stream for USVM. if (size > MAX_RANDOM_STREAM_SIZE) size = MAX_RANDOM_STREAM_SIZE; @@ -401,8 +419,7 @@ automaton SecureRandomAutomaton if (numBytes < 0) _throwIAE(); - result = action ARRAY_NEW("byte", numBytes); - _nextBytes(result, numBytes); + result = action SYMBOLIC_ARRAY("byte", numBytes); } @@ -434,6 +451,7 @@ automaton SecureRandomAutomaton { if (randomNumberOrigin >= randomNumberBound) _throwIAE(); + result = new IntStreamAutomaton(state = Initialized, storage = _generateRandomIntegerArrayWithBounds(MAX_RANDOM_STREAM_SIZE, randomNumberOrigin, randomNumberBound), length = MAX_RANDOM_STREAM_SIZE, @@ -448,6 +466,7 @@ automaton SecureRandomAutomaton var size: int = streamSize as int; if (size < 0) _throwIAE(); + // WARNING: this is our special constraint; We must constraint infinite stream for USVM. if (size > MAX_RANDOM_STREAM_SIZE) size = MAX_RANDOM_STREAM_SIZE; @@ -468,6 +487,7 @@ automaton SecureRandomAutomaton _throwIAE(); if (randomNumberOrigin >= randomNumberBound) _throwIAE(); + // WARNING: this is our special constraint; We must constraint infinite stream for USVM. if (size > MAX_RANDOM_STREAM_SIZE) size = MAX_RANDOM_STREAM_SIZE; @@ -497,6 +517,7 @@ automaton SecureRandomAutomaton var size: int = streamSize as int; if (size < 0) _throwIAE(); + // WARNING: this is our special constraint; We must constraint infinite stream for USVM. if (size > MAX_RANDOM_STREAM_SIZE) size = MAX_RANDOM_STREAM_SIZE; @@ -514,6 +535,7 @@ automaton SecureRandomAutomaton { if (randomNumberOrigin >= randomNumberBound) _throwIAE(); + result = new LongStreamAutomaton(state = Initialized, storage = _generateRandomLongArrayWithBounds(MAX_RANDOM_STREAM_SIZE, randomNumberOrigin, randomNumberBound), length = MAX_RANDOM_STREAM_SIZE, @@ -530,6 +552,7 @@ automaton SecureRandomAutomaton _throwIAE(); if (randomNumberOrigin >= randomNumberBound) _throwIAE(); + // WARNING: this is our special constraint; We must constraint infinite stream for USVM. if (size > MAX_RANDOM_STREAM_SIZE) size = MAX_RANDOM_STREAM_SIZE; @@ -551,13 +574,9 @@ automaton SecureRandomAutomaton fun *.nextBytes (@target self: SecureRandom, bytes: array): void { - _nextBytes(bytes, action ARRAY_SIZE(bytes)); - } - - - @Phantom proc nextBytes_loop (i: int, bytes: array): void - { - bytes[i] = action SYMBOLIC("byte"); + val len: int = action ARRAY_SIZE(bytes); + val src: array = action SYMBOLIC_ARRAY("byte", len); + action ARRAY_COPY(src, 0, bytes, 0, len); } @@ -636,26 +655,30 @@ automaton SecureRandomAutomaton // special: static initialization - @Phantom @static fun *.__clinit__ (): void + @Phantom @static fun *.`` (): void { // #note: list of default providers https://docs.oracle.com/javase/9/security/oracleproviders.htm#JSSEC-GUID-F41EE1C9-DD6A-4BAB-8979-EB7654094029 - action MAP_SET(defaultProvidersMap, "SUN", SOMETHING); - action MAP_SET(defaultProvidersMap, "SunRsaSign", SOMETHING); - action MAP_SET(defaultProvidersMap, "SunJSSE", SOMETHING); - action MAP_SET(defaultProvidersMap, "SunJCE", SOMETHING); - action MAP_SET(defaultProvidersMap, "Apple", SOMETHING); - - action MAP_SET(defaultProvidersMap, "JdkLDAP", SOMETHING); - action MAP_SET(defaultProvidersMap, "SunJGSS", SOMETHING); - action MAP_SET(defaultProvidersMap, "SunSASL", SOMETHING); - action MAP_SET(defaultProvidersMap, "SunPCSC", SOMETHING); - action MAP_SET(defaultProvidersMap, "XMLDSig", SOMETHING); - action MAP_SET(defaultProvidersMap, "SunPKCS11", SOMETHING); - action MAP_SET(defaultProvidersMap, "SunEC", SOMETHING); - action MAP_SET(defaultProvidersMap, "SunMSCAPI", SOMETHING); - action MAP_SET(defaultProvidersMap, "OracleUcrypto", SOMETHING); - action MAP_SET(defaultProvidersMap, "JdkSASL", SOMETHING); + // limiting static field access + val dpMap: map = defaultProvidersMap; + val o: Object = SOMETHING; + + action MAP_SET(dpMap, "SUN", o); + action MAP_SET(dpMap, "SunRsaSign", o); + action MAP_SET(dpMap, "SunJSSE", o); + action MAP_SET(dpMap, "SunJCE", o); + action MAP_SET(dpMap, "Apple", o); + + action MAP_SET(dpMap, "JdkLDAP", o); + action MAP_SET(dpMap, "SunJGSS", o); + action MAP_SET(dpMap, "SunSASL", o); + action MAP_SET(dpMap, "SunPCSC", o); + action MAP_SET(dpMap, "XMLDSig", o); + action MAP_SET(dpMap, "SunPKCS11", o); + action MAP_SET(dpMap, "SunEC", o); + action MAP_SET(dpMap, "SunMSCAPI", o); + action MAP_SET(dpMap, "OracleUcrypto", o); + action MAP_SET(dpMap, "JdkSASL", o); } } \ No newline at end of file diff --git a/spec/java/security/SecureRandomSpi.lsl b/spec/java/security/SecureRandomSpi.lsl index 3c49b3d7..c30d7c7c 100644 --- a/spec/java/security/SecureRandomSpi.lsl +++ b/spec/java/security/SecureRandomSpi.lsl @@ -13,13 +13,12 @@ import java/io/Serializable; // primary semantic types -@implements("java.io.Serializable") @abstract type SecureRandomSpi is java.security.SecureRandomSpi for Serializable { - @private @static @final var serialVersionUID: long = -2991854161009191830L; } -// global aliases and type overrides \ No newline at end of file +// global aliases and type overrides + diff --git a/spec/java/security/Security.lsl b/spec/java/security/Security.lsl index 947d978e..1950cd42 100644 --- a/spec/java/security/Security.lsl +++ b/spec/java/security/Security.lsl @@ -18,10 +18,12 @@ import java/security/Provider; is java.security.Security for Object { - @static *.getProviders(): array; + @static fun *.getProviders(): array; - @static *.getProperty(key: String): String; + @static fun *.getProperty(key: String): String; } -// global aliases and type overrides \ No newline at end of file +// global aliases and type overrides + + diff --git a/spec/java/util/ArrayList.ListIterator.lsl b/spec/java/util/ArrayList.ListIterator.lsl index 7210f1fb..b17199c7 100644 --- a/spec/java/util/ArrayList.ListIterator.lsl +++ b/spec/java/util/ArrayList.ListIterator.lsl @@ -84,7 +84,7 @@ automaton ArrayList_ListIteratorAutomaton // relax state/error discovery process action ASSUME(this.parent != null); - result = this.cursor != ArrayListAutomaton(this.parent).length; + result = this.cursor != action LIST_SIZE(ArrayListAutomaton(this.parent).storage); } @@ -98,12 +98,8 @@ automaton ArrayList_ListIteratorAutomaton val parentStorage: list = ArrayListAutomaton(this.parent).storage; val i: int = this.cursor; - if (i >= ArrayListAutomaton(this.parent).length) - action THROW_NEW("java.util.NoSuchElementException", []); - - // iterrator validity check if (i >= action LIST_SIZE(parentStorage)) - _throwCME(); + action THROW_NEW("java.util.NoSuchElementException", []); this.cursor = i + 1; this.lastRet = i; @@ -156,8 +152,6 @@ automaton ArrayList_ListIteratorAutomaton ArrayListAutomaton(this.parent).modCount += 1; action LIST_REMOVE(pStorage, this.lastRet); - - ArrayListAutomaton(this.parent).length -= 1; } this.cursor = this.lastRet; @@ -203,8 +197,6 @@ automaton ArrayList_ListIteratorAutomaton ArrayListAutomaton(this.parent).modCount += 1; action LIST_INSERT_AT(pStorage, i, e); - - ArrayListAutomaton(this.parent).length += 1; } this.cursor = i + 1; @@ -222,15 +214,11 @@ automaton ArrayList_ListIteratorAutomaton action THROW_NEW("java.lang.NullPointerException", []); var i: int = this.cursor; - val size: int = ArrayListAutomaton(this.parent).length; + val es: list = ArrayListAutomaton(this.parent).storage; + val size: int = action LIST_SIZE(es); if (i < size) { - val es: list = ArrayListAutomaton(this.parent).storage; - - if (i >= action LIST_SIZE(es)) - _throwCME(); - // using this exact loop form here due to coplex termination expression action LOOP_WHILE( i < size && ArrayListAutomaton(this.parent).modCount == this.expectedModCount, diff --git a/spec/java/util/ArrayList.Spliterator.lsl b/spec/java/util/ArrayList.Spliterator.lsl index 92a2cca3..111856de 100644 --- a/spec/java/util/ArrayList.Spliterator.lsl +++ b/spec/java/util/ArrayList.Spliterator.lsl @@ -25,7 +25,7 @@ automaton ArrayList_SpliteratorAutomaton shift Allocated -> Initialized by [ // constructors - ArrayList_Spliterator, + ``, ]; shift Initialized -> self by [ @@ -64,11 +64,11 @@ automaton ArrayList_SpliteratorAutomaton proc _getFence (): int { // JDK comment: initialize fence to size on first use - if (this.fence < 0) + if (this.fence == -1) { action ASSUME(this.parent != null); this.expectedModCount = ArrayListAutomaton(this.parent).modCount; - this.fence = ArrayListAutomaton(this.parent).length; + this.fence = action LIST_SIZE(ArrayListAutomaton(this.parent).storage); } result = this.fence; @@ -77,7 +77,7 @@ automaton ArrayList_SpliteratorAutomaton // constructors - @private constructor *.ArrayList_Spliterator ( + @private constructor *.`` ( @target self: ArrayList_Spliterator, _this: ArrayList, origin: int, fence: int, expectedModCount: int) @@ -115,15 +115,15 @@ automaton ArrayList_SpliteratorAutomaton var hi: int = this.fence; var mc: int = this.expectedModCount; - if (hi < 0) + if (hi == -1) { - hi = ArrayListAutomaton(this.parent).length; + hi = action LIST_SIZE(a); mc = ArrayListAutomaton(this.parent).modCount; } var i: int = this.index; this.index = hi; - if (i < 0 || hi > ArrayListAutomaton(this.parent).length) + if (i < 0 || hi > action LIST_SIZE(a)) _throwCME(); action LOOP_FOR( diff --git a/spec/java/util/ArrayList.SubList.ListIterator.lsl b/spec/java/util/ArrayList.SubList.ListIterator.lsl new file mode 100644 index 00000000..6dc5c6a2 --- /dev/null +++ b/spec/java/util/ArrayList.SubList.ListIterator.lsl @@ -0,0 +1,246 @@ +libsl "1.1.0"; + +library std + version "11" + language "Java" + url "https://github.com/openjdk/jdk11/blob/master/src/java.base/share/classes/java/util/ArrayList.java"; + +// imports + +import java/util/ArrayList; + + +// automata + +automaton ArrayList_SubList_ListIteratorAutomaton +( + var root: ArrayList, + var sublist: ArrayList_SubList, + var cursor: int, + var expectedModCount: int, + var offset: int, + var size: int, +) +: ArrayList_SubList_ListIterator +{ + // states and shifts + + initstate Initialized; + + shift Initialized -> self by [ + add, + forEachRemaining, + hasNext, + hasPrevious, + next, + nextIndex, + previous, + previousIndex, + remove, + set, + ]; + + + // local variables + + var lastRet: int = -1; + + + // utilities + + @AutoInline @Phantom proc _throwCME (): void + { + action THROW_NEW("java.util.ConcurrentModificationException", []); + } + + + proc _checkForComodification (): void + { + val modCount: int = ArrayListAutomaton(this.root).modCount; + if (modCount != this.expectedModCount) + _throwCME(); + } + + + // methods + + fun *.hasPrevious (@target self: ArrayList_SubList_ListIterator): boolean + { + result = this.cursor != 0; + } + + + fun *.nextIndex (@target self: ArrayList_SubList_ListIterator): int + { + result = this.cursor; + } + + + fun *.previousIndex (@target self: ArrayList_SubList_ListIterator): int + { + result = this.cursor - 1; + } + + + fun *.hasNext (@target self: ArrayList_SubList_ListIterator): boolean + { + result = this.cursor != this.size; + } + + + fun *.next (@target self: ArrayList_SubList_ListIterator): Object + { + // relax state/error discovery process + action ASSUME(this.root != null); + + _checkForComodification(); + + val rootStorage: list = ArrayListAutomaton(this.root).storage; + val i: int = this.offset + this.cursor; + // iterrator validity check + if (i >= action LIST_SIZE(rootStorage)) + action THROW_NEW("java.util.NoSuchElementException", []); + + this.lastRet = this.cursor; + this.cursor += 1; + + result = action LIST_GET(rootStorage, i); + } + + + fun *.previous (@target self: ArrayList_SubList_ListIterator): Object + { + // relax state/error discovery process + action ASSUME(this.root != null); + + _checkForComodification(); + + val i: int = this.offset + this.cursor - 1; + if (i < this.offset) + action THROW_NEW("java.util.NoSuchElementException", []); + + // iterrator validity check + val rootStorage: list = ArrayListAutomaton(this.root).storage; + if (i >= action LIST_SIZE(rootStorage)) + _throwCME(); + + this.cursor -= 1; + this.lastRet = this.cursor; + + result = action LIST_GET(rootStorage, i); + } + + + fun *.remove (@target self: ArrayList_SubList_ListIterator): void + { + // relax state/error discovery process + action ASSUME(this.root != null); + + if (this.lastRet < 0) + action THROW_NEW("java.lang.IllegalStateException", []); + + _checkForComodification(); + + if (this.lastRet >= this.size) + { + _throwCME(); + } + else + { + ArrayListAutomaton(this.root)._deleteElement(this.offset + this.lastRet); + + ArrayList_SubListAutomaton(this.sublist)._updateSizeAndModCount(-1); + this.expectedModCount = ArrayListAutomaton(this.root).modCount; + this.size -= 1; + } + + this.cursor = this.lastRet; + this.lastRet = -1; + } + + + fun *.set (@target self: ArrayList_SubList_ListIterator, e: Object): void + { + // relax state/error discovery process + action ASSUME(this.root != null); + + if (this.lastRet < 0) + action THROW_NEW("java.lang.IllegalStateException", []); + + _checkForComodification(); + + val rootStorage: list = ArrayListAutomaton(this.root).storage; + + val index: int = this.offset + this.lastRet; + if (index >= action LIST_SIZE(rootStorage)) + _throwCME(); + else + action LIST_SET(rootStorage, index, e); + } + + + fun *.add (@target self: ArrayList_SubList_ListIterator, e: Object): void + { + // relax state/error discovery process + action ASSUME(this.root != null); + + _checkForComodification(); + + val i: int = this.offset + this.cursor; + + if (this.offset + this.lastRet > action LIST_SIZE(ArrayListAutomaton(this.root).storage)) + { + _throwCME(); + } + else + { + ArrayListAutomaton(this.root)._addElement(i, e); + + ArrayList_SubListAutomaton(this.sublist)._updateSizeAndModCount(+1); + this.expectedModCount = ArrayListAutomaton(this.root).modCount; + this.size += 1; + } + + this.cursor += 1; + this.lastRet = -1; + } + + + fun *.forEachRemaining (@target self: ArrayList_SubList_ListIterator, userAction: Consumer): void + { + // relax state/error discovery process + action ASSUME(this.root != null); + + if (userAction == null) + action THROW_NEW("java.lang.NullPointerException", []); + + var i: int = this.cursor; + if (i < this.size) + { + i += this.offset; + val es: list = ArrayListAutomaton(this.root).storage; + + if (i >= action LIST_SIZE(es)) + _throwCME(); + + val end: int = this.offset + this.size; + + action LOOP_FOR( + i, i, end, +1, + forEachRemaining_loop(userAction, es, i) + ); + + // JDK NOTE: "update once at end to reduce heap write traffic" + this.cursor = i - this.offset; + this.lastRet = this.cursor - 1; + _checkForComodification(); + } + } + + @Phantom proc forEachRemaining_loop (userAction: Consumer, es: list, i: int): void + { + val item: Object = action LIST_GET(es, i); + action CALL(userAction, [item]); + } + +} diff --git a/spec/java/util/ArrayList.SubList.Spliterator.lsl b/spec/java/util/ArrayList.SubList.Spliterator.lsl index e38facdf..ee63f1e9 100644 --- a/spec/java/util/ArrayList.SubList.Spliterator.lsl +++ b/spec/java/util/ArrayList.SubList.Spliterator.lsl @@ -59,7 +59,7 @@ automaton ArrayList_SubList_SpliteratorAutomaton proc _getFence (): int { // JDK comment: initialize fence to size on first use - if (this.fence < 0) + if (this.fence == -1) { action ASSUME(this.parent != null); this.expectedModCount = ArrayList_SubListAutomaton(this.parent).modCount; @@ -102,7 +102,7 @@ automaton ArrayList_SubList_SpliteratorAutomaton var hi: int = this.fence; var mc: int = this.expectedModCount; - if (hi < 0) + if (hi == -1) { hi = ArrayList_SubListAutomaton(this.parent).length; mc = ArrayList_SubListAutomaton(this.parent).modCount; diff --git a/spec/java/util/ArrayList.SubList.lsl b/spec/java/util/ArrayList.SubList.lsl index 05380629..e8600f37 100644 --- a/spec/java/util/ArrayList.SubList.lsl +++ b/spec/java/util/ArrayList.SubList.lsl @@ -29,8 +29,8 @@ automaton ArrayList_SubListAutomaton shift Allocated -> Initialized by [ // constructors - SubList (ArrayList_SubList, ArrayList, int, int), - SubList (ArrayList_SubList, ArrayList_SubList, int, int), + `` (ArrayList_SubList, ArrayList, int, int), + `` (ArrayList_SubList, ArrayList_SubList, int, int), ]; shift Initialized -> self by [ @@ -81,6 +81,12 @@ automaton ArrayList_SubListAutomaton } + @AutoInline @Phantom proc _checkForComodification(): void + { + ArrayListAutomaton(this.root)._checkForComodification(this.modCount); + } + + proc _addAllElements (index: int, c: Collection): boolean { action ASSUME(this.root != null); @@ -97,14 +103,14 @@ automaton ArrayList_SubListAutomaton { result = true; - ArrayListAutomaton(this.root)._checkForComodification(this.modCount); + _checkForComodification(); ArrayListAutomaton(this.root)._addAllElements(effectiveIndex, c); _updateSizeAndModCount(collectionSize); } } - proc _updateSizeAndModCount (sizeChange: int): void + @KeepVisible proc _updateSizeAndModCount (sizeChange: int): void { action ASSUME(this.root != null); @@ -133,12 +139,11 @@ automaton ArrayList_SubListAutomaton { action ASSUME(this.root != null); - ArrayListAutomaton(this.root)._checkForComodification(this.modCount); + _checkForComodification(); val parentStorage: list = ArrayListAutomaton(this.root).storage; - val parentLength: int = ArrayListAutomaton(this.root).length; - val index: int = action LIST_FIND(parentStorage, o, 0, parentLength); - if (index >= 0) + val index: int = action LIST_FIND(parentStorage, o, this.offset, this.offset + this.length); + if (index != -1) result = index - this.offset; else result = -1; @@ -154,16 +159,39 @@ automaton ArrayList_SubListAutomaton } + proc _batchRemove (c: Collection, complement: boolean): boolean + { + action ASSUME(this.root != null); + _checkForComodification(); + + if (this.length != 0) + { + val oldRootLength: int = action LIST_SIZE(ArrayListAutomaton(this.root).storage); + + result = ArrayListAutomaton(this.root)._batchRemove(c, complement, this.offset, this.offset + this.length); + if (result) + { + val newRootLength: int = action LIST_SIZE(ArrayListAutomaton(this.root).storage); + _updateSizeAndModCount(newRootLength - oldRootLength); + } + } + else + { + result = false; + } + } + + // constructors - constructor *.SubList (@target self: ArrayList_SubList, root: ArrayList, fromIndex: int, toIndex: int) + constructor *.`` (@target self: ArrayList_SubList, root: ArrayList, fromIndex: int, toIndex: int) { // #problem: this constructor is useless action NOT_IMPLEMENTED("inaccessible constructor"); } - @private constructor *.SubList (@target self: ArrayList_SubList, parent: ArrayList_SubList, fromIndex: int, toIndex: int) + @private constructor *.`` (@target self: ArrayList_SubList, parent: ArrayList_SubList, fromIndex: int, toIndex: int) { // #problem: this constructor is useless action NOT_IMPLEMENTED("inaccessible constructor"); @@ -179,8 +207,9 @@ automaton ArrayList_SubListAutomaton { action ASSUME(this.root != null); + _checkForComodification(); + val effectiveIndex: int = this.offset + this.length; - ArrayListAutomaton(this.root)._checkForComodification(this.modCount); ArrayListAutomaton(this.root)._addElement(effectiveIndex, e); _updateSizeAndModCount(+1); @@ -191,8 +220,9 @@ automaton ArrayList_SubListAutomaton { action ASSUME(this.root != null); + _checkForComodification(); + val effectiveIndex: int = this.offset + index; - ArrayListAutomaton(this.root)._checkForComodification(this.modCount); ArrayListAutomaton(this.root)._addElement(effectiveIndex, element); _updateSizeAndModCount(+1); @@ -214,13 +244,39 @@ automaton ArrayList_SubListAutomaton // within java.util.AbstractList fun *.clear (@target self: ArrayList_SubList): void { - action TODO(); + action ASSUME(this.root != null); + _checkForComodification(); + + val size: int = this.length; + if (size != 0) + { + action ASSUME(size > 0); + val end: int = this.offset - 1; + val start: int = end + size; + + val rootStorage: list = ArrayListAutomaton(this.root).storage; + + var i: int = 0; + action LOOP_FOR( + i, start, end, -1, + clear_loop(i, rootStorage) + ); + + ArrayListAutomaton(this.root).modCount += 1; + + _updateSizeAndModCount(-size); + } + } + + @Phantom proc clear_loop (i: int, rootStorage: list): void + { + action LIST_REMOVE(rootStorage, i); } fun *.contains (@target self: ArrayList_SubList, o: Object): boolean { - result = _indexOfElement(o) >= 0; + result = _indexOfElement(o) != -1; } @@ -269,7 +325,7 @@ automaton ArrayList_SubListAutomaton @Phantom proc containsAll_loop_optimized (rootStorage: list, end: int, otherStorage: list, i: int, result: boolean): void { val item: Object = action LIST_GET(otherStorage, i); - result = action LIST_FIND(rootStorage, item, this.offset, end) >= 0; + result = action LIST_FIND(rootStorage, item, this.offset, end) != -1; i += 1; } @@ -277,7 +333,7 @@ automaton ArrayList_SubListAutomaton @Phantom proc containsAll_loop_regular (rootStorage: list, end: int, iter: Iterator, result: boolean): void { val item: Object = action CALL_METHOD(iter, "next", []); - result = action LIST_FIND(rootStorage, item, this.offset, end) >= 0; + result = action LIST_FIND(rootStorage, item, this.offset, end) != -1; } @@ -301,7 +357,7 @@ automaton ArrayList_SubListAutomaton if (result) { result = ArrayListAutomaton(this.root)._equalsRange(o as List, this.offset, this.offset + this.length); - ArrayListAutomaton(this.root)._checkForComodification(this.modCount); + _checkForComodification(); } } } @@ -313,6 +369,7 @@ automaton ArrayList_SubListAutomaton { if (this.length != 0) { + action ASSUME(this.length > 0); action ASSUME(this.root != null); val rootStorage: list = ArrayListAutomaton(this.root).storage; @@ -344,10 +401,10 @@ automaton ArrayList_SubListAutomaton { action ASSUME(this.root != null); - val effectiveIndex: int = this.offset + index; ArrayListAutomaton(this.root)._checkValidIndex(index, this.length); - ArrayListAutomaton(this.root)._checkForComodification(this.modCount); + _checkForComodification(); + val effectiveIndex: int = this.offset + index; result = action LIST_GET(ArrayListAutomaton(this.root).storage, effectiveIndex); } @@ -358,6 +415,7 @@ automaton ArrayList_SubListAutomaton if (this.length != 0) { + action ASSUME(this.length > 0); action ASSUME(this.root != null); val rootStorage: list = ArrayListAutomaton(this.root).storage; @@ -368,7 +426,7 @@ automaton ArrayList_SubListAutomaton hashCode_loop(i, rootStorage, result) ); - ArrayListAutomaton(this.root)._checkForComodification(this.modCount); + _checkForComodification(); } } @@ -394,26 +452,74 @@ automaton ArrayList_SubListAutomaton fun *.iterator (@target self: ArrayList_SubList): Iterator { - action TODO(); + result = new ArrayList_SubList_ListIteratorAutomaton(state = Initialized, + root = this.root, + sublist = self, + cursor = 0, + expectedModCount = this.modCount, + offset = this.offset, + size = this.length, + ); } fun *.lastIndexOf (@target self: ArrayList_SubList, o: Object): int { - action TODO(); + action ASSUME(this.root != null); + _checkForComodification(); + + if (this.length == 0) + { + result = -1; + } + else + { + action ASSUME(this.length > 0); + + val end: int = this.offset + this.length; + val rootStorage: list = ArrayListAutomaton(this.root).storage; + + result = action LIST_FIND(rootStorage, o, this.offset, end); + if (result != -1) + { + // there should be no elements to the right of the previously found position + val nextIndex: int = result + 1; + if (nextIndex < end) + { + val rightIndex: int = action LIST_FIND(rootStorage, o, nextIndex, end); + action ASSUME(rightIndex == -1); + } + + result -= this.offset; + } + } } // within java.util.AbstractList fun *.listIterator (@target self: ArrayList_SubList): ListIterator { - action TODO(); + result = new ArrayList_SubList_ListIteratorAutomaton(state = Initialized, + root = this.root, + sublist = self, + cursor = 0, + expectedModCount = this.modCount, + offset = this.offset, + size = this.length, + ); } fun *.listIterator (@target self: ArrayList_SubList, index: int): ListIterator { - action TODO(); + result = new ArrayList_SubList_ListIteratorAutomaton(state = Initialized, + root = this.root, + sublist = self, + cursor = index, + expectedModCount = this.modCount, + offset = this.offset, + size = this.length, + ); } @@ -433,11 +539,11 @@ automaton ArrayList_SubListAutomaton val rootStorage: list = ArrayListAutomaton(this.root).storage; val index: int = action LIST_FIND(rootStorage, o, this.offset, end); - result = index >= 0; + result = index != -1; if (result) { - ArrayListAutomaton(this.root)._checkForComodification(this.modCount); + _checkForComodification(); ArrayListAutomaton(this.root)._deleteElement(index); _updateSizeAndModCount(-1); @@ -449,10 +555,10 @@ automaton ArrayList_SubListAutomaton { action ASSUME(this.root != null); - val effectiveIndex: int = this.offset + index; - ArrayListAutomaton(this.root)._checkValidIndex(index, this.length); - ArrayListAutomaton(this.root)._checkForComodification(this.modCount); + _checkForComodification(); + + val effectiveIndex: int = this.offset + index; result = ArrayListAutomaton(this.root)._deleteElement(effectiveIndex); _updateSizeAndModCount(-1); @@ -461,30 +567,44 @@ automaton ArrayList_SubListAutomaton fun *.removeAll (@target self: ArrayList_SubList, c: Collection): boolean { - result = true; - - if (this.length != 0) - { - action TODO(); - } + _batchRemove(c, false); } fun *.removeIf (@target self: ArrayList_SubList, filter: Predicate): boolean { - action TODO(); + action ASSUME(this.root != null); + _checkForComodification(); + + val size: int = this.length; + if (size != 0) + { + val oldRootLength: int = action LIST_SIZE(ArrayListAutomaton(this.root).storage); + + result = ArrayListAutomaton(this.root)._removeIf(filter, this.offset, this.offset + this.length); + if (result) + { + val newRootLength: int = action LIST_SIZE(ArrayListAutomaton(this.root).storage); + _updateSizeAndModCount(newRootLength - oldRootLength); + } + } + else + { + result = false; + } } fun *.replaceAll (@target self: ArrayList_SubList, operator: UnaryOperator): void { - action TODO(); + action ASSUME(this.root != null); + ArrayListAutomaton(this.root)._replaceAllRange(operator, this.offset, this.offset + this.length); } fun *.retainAll (@target self: ArrayList_SubList, c: Collection): boolean { - action TODO(); + _batchRemove(c, true); } @@ -492,11 +612,11 @@ automaton ArrayList_SubListAutomaton { action ASSUME(this.root != null); - val effectiveIndex: int = this.offset + index; ArrayListAutomaton(this.root)._checkValidIndex(index, this.length); - ArrayListAutomaton(this.root)._checkForComodification(this.modCount); + _checkForComodification(); val parentStorage: list = ArrayListAutomaton(this.root).storage; + val effectiveIndex: int = this.offset + index; result = action LIST_GET(parentStorage, effectiveIndex); action LIST_SET(parentStorage, effectiveIndex, element); } @@ -506,7 +626,7 @@ automaton ArrayList_SubListAutomaton { action ASSUME(this.root != null); - ArrayListAutomaton(this.root)._checkForComodification(this.modCount); + _checkForComodification(); result = this.length; } @@ -514,92 +634,9 @@ automaton ArrayList_SubListAutomaton // within java.util.List fun *.sort (@target self: ArrayList_SubList, c: Comparator): void { - if (this.length != 0) - { - // Java has no unsigned primitive data types - action ASSUME(this.length > 0); - action ASSUME(this.root != null); - - ArrayListAutomaton(this.root)._checkForComodification(this.modCount); - val rootStorage: list = ArrayListAutomaton(this.root).storage; - - // prepare common variables - val baseLimit: int = this.offset + this.length; - val outerLimit: int = baseLimit - 1; - var innerLimit: int = 0; - var i: int = 0; - var j: int = 0; - - // check the comparator - if (c == null) - { - // using Comparable::compareTo as a comparator - - // plain bubble sorting algorithm - action LOOP_FOR( - i, this.offset, outerLimit, +1, - sort_loop_outer_noComparator(i, j, baseLimit, innerLimit, rootStorage) - ); - } - else - { - // using the provided comparator - - // plain bubble sorting algorithm (with a comparator) - action LOOP_FOR( - i, this.offset, outerLimit, +1, - sort_loop_outer(i, j, baseLimit, innerLimit, rootStorage, c) - ); - } - - this.modCount = ArrayListAutomaton(this.root).modCount; - } - } - - @Phantom proc sort_loop_outer_noComparator (i: int, j: int, baseLimit: int, innerLimit: int, rootStorage: list): void - { - innerLimit = baseLimit - i - 1; - action LOOP_FOR( - j, this.offset, innerLimit, +1, - sort_loop_inner_noComparator(j, rootStorage) - ); - } - - @Phantom proc sort_loop_inner_noComparator (j: int, rootStorage: list): void - { - val idxA: int = j; - val idxB: int = j + 1; - val a: Object = action LIST_GET(rootStorage, idxA); - val b: Object = action LIST_GET(rootStorage, idxB); - - if (action CALL_METHOD(a as Comparable, "compareTo", [b]) > 0) - { - action LIST_SET(rootStorage, idxA, b); - action LIST_SET(rootStorage, idxB, a); - } - } - - @Phantom proc sort_loop_outer (i: int, j: int, baseLimit: int, innerLimit: int, rootStorage: list, c: Comparator): void - { - innerLimit = baseLimit - i - 1; - action LOOP_FOR( - j, this.offset, innerLimit, +1, - sort_loop_inner(j, rootStorage, c) - ); - } - - @Phantom proc sort_loop_inner (j: int, rootStorage: list, c: Comparator): void - { - val idxA: int = j; - val idxB: int = j + 1; - val a: Object = action LIST_GET(rootStorage, idxA); - val b: Object = action LIST_GET(rootStorage, idxB); - - if (action CALL(c, [a, b]) > 0) - { - action LIST_SET(rootStorage, idxA, b); - action LIST_SET(rootStorage, idxB, a); - } + action ASSUME(this.root != null); + ArrayListAutomaton(this.root)._do_sort(this.offset, this.offset + this.length, c); + this.modCount = ArrayListAutomaton(this.root).modCount; } @@ -638,7 +675,7 @@ automaton ArrayList_SubListAutomaton fun *.toArray (@target self: ArrayList_SubList): array { action ASSUME(this.root != null); - ArrayListAutomaton(this.root)._checkForComodification(this.modCount); + _checkForComodification(); result = action ARRAY_NEW("java.lang.Object", this.length); @@ -667,7 +704,7 @@ automaton ArrayList_SubListAutomaton val aSize: int = action ARRAY_SIZE(a); action ASSUME(this.root != null); - ArrayListAutomaton(this.root)._checkForComodification(this.modCount); + _checkForComodification(); result = action ARRAY_NEW("java.lang.Object", this.length); @@ -685,7 +722,7 @@ automaton ArrayList_SubListAutomaton fun *.toArray (@target self: ArrayList_SubList, a: array): array { action ASSUME(this.root != null); - ArrayListAutomaton(this.root)._checkForComodification(this.modCount); + _checkForComodification(); val aSize: int = action ARRAY_SIZE(a); if (aSize < this.length) diff --git a/spec/java/util/ArrayList.lsl b/spec/java/util/ArrayList.lsl index f3b314ac..f373efa1 100644 --- a/spec/java/util/ArrayList.lsl +++ b/spec/java/util/ArrayList.lsl @@ -71,3 +71,12 @@ import java/util/Spliterator; for Spliterator { } + + +@GenerateMe +@implements("java.util.ListIterator") +@public @final type ArrayList_SubList_ListIterator + is java.util.ArrayList_SubList$ListIterator // NOTE: do not use inner classes + for ListIterator +{ +} diff --git a/spec/java/util/ArrayList.main.lsl b/spec/java/util/ArrayList.main.lsl index 3385223c..d850fb89 100644 --- a/spec/java/util/ArrayList.main.lsl +++ b/spec/java/util/ArrayList.main.lsl @@ -7,6 +7,8 @@ library std // imports +import java/util/stream/Stream; + import java/util/ArrayList; @@ -14,8 +16,7 @@ import java/util/ArrayList; automaton ArrayListAutomaton ( - var storage: list, - @transient var length: int + var storage: list ) : ArrayList { @@ -26,9 +27,9 @@ automaton ArrayListAutomaton shift Allocated -> Initialized by [ // constructors - ArrayList (ArrayList), - ArrayList (ArrayList, Collection), - ArrayList (ArrayList, int), + `` (ArrayList), + `` (ArrayList, Collection), + `` (ArrayList, int), ]; shift Initialized -> self by [ @@ -86,7 +87,7 @@ automaton ArrayListAutomaton if (index < 0 || length <= index) { //val idx: String = action OBJECT_TO_STRING(index); - //val len: String = action OBJECT_TO_STRING(this.length); + //val len: String = action OBJECT_TO_STRING(action LIST_SIZE(this.storage)); //val message: String = "Index "+ idx + " out of bounds for length "+ len; action THROW_NEW("java.lang.IndexOutOfBoundsException", []); } @@ -95,10 +96,10 @@ automaton ArrayListAutomaton @KeepVisible proc _rangeCheckForAdd (index: int): void { - if (index > this.length || index < 0) + if (index > action LIST_SIZE(this.storage) || index < 0) { //val idx: String = action OBJECT_TO_STRING(index); - //val len: String = action OBJECT_TO_STRING(this.length); + //val len: String = action OBJECT_TO_STRING(action LIST_SIZE(this.storage)); //val message: String = "Index: " + idx + ", Size: " + len; action THROW_NEW("java.lang.IndexOutOfBoundsException", []); } @@ -107,12 +108,12 @@ automaton ArrayListAutomaton @KeepVisible proc _addAllElements (index: int, c: Collection): boolean { - val oldLength: int = this.length; + val oldLength: int = action LIST_SIZE(this.storage); if (c has ArrayListAutomaton) { val otherStorage: list = ArrayListAutomaton(c).storage; - val otherLength: int = ArrayListAutomaton(c).length; + val otherLength: int = action LIST_SIZE(otherStorage); action ASSUME(otherStorage != null); action ASSUME(otherLength >= 0); @@ -132,7 +133,7 @@ automaton ArrayListAutomaton ); } - result = oldLength != this.length; + result = oldLength != action LIST_SIZE(this.storage); if (result) this.modCount += 1; } @@ -143,7 +144,6 @@ automaton ArrayListAutomaton action LIST_INSERT_AT(this.storage, index, item); index += 1; - this.length += 1; } @Phantom proc _addAllElements_loop_regular (iter: Iterator, index: int): void @@ -152,7 +152,6 @@ automaton ArrayListAutomaton action LIST_INSERT_AT(this.storage, index, item); index += 1; - this.length += 1; } @@ -190,12 +189,11 @@ automaton ArrayListAutomaton @KeepVisible proc _deleteElement (index: int): Object { - _checkValidIndex(index, this.length); + _checkValidIndex(index, action LIST_SIZE(this.storage)); result = action LIST_GET(this.storage, index); action LIST_REMOVE(this.storage, index); - this.length -= 1; this.modCount += 1; } @@ -206,14 +204,13 @@ automaton ArrayListAutomaton action LIST_INSERT_AT(this.storage, index, element); - this.length += 1; this.modCount += 1; } proc _setElement (index: int, element: Object): Object { - _checkValidIndex(index, this.length); + _checkValidIndex(index, action LIST_SIZE(this.storage)); result = action LIST_GET(this.storage, index); action LIST_SET(this.storage, index, element); @@ -226,7 +223,7 @@ automaton ArrayListAutomaton } - proc _replaceAllRange (i: int, end: int, op: UnaryOperator): void + @KeepVisible proc _replaceAllRange (op: UnaryOperator, i: int, end: int): void { val expectedModCount: int = this.modCount; @@ -253,10 +250,11 @@ automaton ArrayListAutomaton if (filter == null) _throwNPE(); - val oldLength: int = this.length; + val oldLength: int = action LIST_SIZE(this.storage); val expectedModCount: int = this.modCount; // remove elements from the back first + action ASSUME(start <= end); var i: int = 0; action LOOP_FOR( i, end - 1, start, -1, @@ -265,17 +263,14 @@ automaton ArrayListAutomaton _checkForComodification(expectedModCount); - result = oldLength != this.length; + result = oldLength != action LIST_SIZE(this.storage); } @Phantom proc _removeIf_loop (i: int, filter: Predicate): void { val item: Object = action LIST_GET(this.storage, i); if (action CALL(filter, [item])) - { action LIST_REMOVE(this.storage, i); - this.length -= 1; - } } @@ -284,47 +279,32 @@ automaton ArrayListAutomaton result = true; var i: int = from; - var otherLength: int = 0; var otherStorage: list = null; if (other has ArrayListAutomaton) { - otherLength = ArrayListAutomaton(other).length; - action ASSUME(otherLength >= 0); + otherStorage = ArrayListAutomaton(other).storage; // assumptions: no multithreading, from == 0 - result = to == otherLength; + result = to == action LIST_SIZE(otherStorage); if (result) - { - otherStorage = ArrayListAutomaton(other).storage; - action ASSUME(otherStorage != null); - action LOOP_WHILE( result && i < to, _equalsRange_loop_optimized(i, otherStorage, result) ); - } } else if (other has ArrayList_SubListAutomaton) { - otherLength = ArrayList_SubListAutomaton(other).length; - action ASSUME(otherLength >= 0); + val otherRoot: ArrayList = ArrayList_SubListAutomaton(other).root; + otherStorage = ArrayListAutomaton(otherRoot).storage; // assumptions: no multithreading, from >= 0 - result = to == otherLength; + result = to == ArrayList_SubListAutomaton(other).length; if (result) - { - val otherRoot: ArrayList = ArrayList_SubListAutomaton(other).root; - action ASSUME(otherRoot != null); - - otherStorage = ArrayListAutomaton(otherRoot).storage; - action ASSUME(otherStorage != null); - action LOOP_WHILE( result && i < to, _equalsRange_loop_optimized(i, otherStorage, result) ); - } } else { @@ -361,35 +341,203 @@ automaton ArrayListAutomaton proc _makeStream (parallel: boolean): Stream { - // #todo: use custom stream implementation - result = action SYMBOLIC("java.util.stream.Stream"); - action ASSUME(result != null); - action ASSUME(action CALL_METHOD(result, "isParallel", []) == parallel); + val count: int = action LIST_SIZE(this.storage); + val items: array = action ARRAY_NEW("java.lang.Object", count); + + var i: int = 0; + action LOOP_FOR( + i, 0, count, +1, + _makeStream_loop(i, items) + ); + + // #problem: unable to catch concurrent modifications during stream processing + + result = new StreamAutomaton(state = Initialized, + storage = items, + length = count, + closeHandlers = action LIST_NEW(), + isParallel = parallel, + ); + } + + @Phantom proc _makeStream_loop (i: int, items: array): void + { + items[i] = action LIST_GET(this.storage, i); + } + + + @KeepVisible proc _batchRemove (c: Collection, complement: boolean, start: int, end: int): boolean + { + val oldLength: int = action LIST_SIZE(this.storage); + if (oldLength == 0 || start >= end) + { + result = false; + } + else + { + val otherLength: int = action CALL_METHOD(c, "size", []); + if (otherLength == 0) + { + result = false; + } + else + { + action ASSUME(otherLength > 0); + + var i: int = 0; + start -= 1; + end -= 1; + + if (c has ArrayListAutomaton) + { + val otherStorage: list = ArrayListAutomaton(c).storage; + action ASSUME(otherStorage != null); + + action LOOP_FOR( + i, end, start, -1, + _batchRemove_loop_optimized(i, otherStorage, complement) + ); + } + else + { + action LOOP_FOR( + i, end, start, -1, + _batchRemove_loop_regular(i, c, complement) + ); + } + + result = oldLength != action LIST_SIZE(this.storage); + } + } + } + + @Phantom proc _batchRemove_loop_optimized (i: int, otherStorage: list, complement: boolean): void + { + val item: Object = action LIST_GET(this.storage, i); + if ((action LIST_FIND(otherStorage, item, 0, action LIST_SIZE(this.storage)) == -1) == complement) + _deleteElement(i); + } + + @Phantom proc _batchRemove_loop_regular (i: int, c: Collection, complement: boolean): void + { + val item: Object = action LIST_GET(this.storage, i); + if (action CALL_METHOD(c, "contains", [item]) != complement) + _deleteElement(i); + } + + + // sort every item in the list within [start, end) range + @KeepVisible proc _do_sort (start: int, end: int, c: Comparator): void + { + if (start < end) + { + val expectedModCount: int = this.modCount; + + // Java has no unsigned primitive data types + action ASSUME(start >= 0); + action ASSUME(end > 0); + + // plain bubble sorting algorithm + val outerLimit: int = end - 1; + var innerLimit: int = 0; + var i: int = 0; + var j: int = 0; + + // check the comparator + if (c == null) + { + // using Comparable::compareTo as a comparator + + // plain bubble sorting algorithm + action LOOP_FOR( + i, start, outerLimit, +1, + sort_loop_outer_noComparator(i, j, innerLimit, start, end) + ); + } + else + { + // using the provided comparator + + // plain bubble sorting algorithm (with a comparator) + action LOOP_FOR( + i, start, outerLimit, +1, + sort_loop_outer(i, j, innerLimit, start, end, c) + ); + } + + _checkForComodification(expectedModCount); + } + + this.modCount += 1; + } + + @Phantom proc sort_loop_outer_noComparator (i: int, j: int, innerLimit: int, start: int, end: int): void + { + innerLimit = end - i - 1; + action LOOP_FOR( + j, start, innerLimit, +1, + sort_loop_inner_noComparator(j) + ); + } + + @Phantom proc sort_loop_inner_noComparator (j: int): void + { + val idxA: int = j; + val idxB: int = j + 1; + val a: Object = action LIST_GET(this.storage, idxA); + val b: Object = action LIST_GET(this.storage, idxB); + + if (action CALL_METHOD(a as Comparable, "compareTo", [b]) > 0) + { + action LIST_SET(this.storage, idxA, b); + action LIST_SET(this.storage, idxB, a); + } + } + + @Phantom proc sort_loop_outer (i: int, j: int, innerLimit: int, start: int, end: int, c: Comparator): void + { + innerLimit = end - i - 1; + action LOOP_FOR( + j, start, innerLimit, +1, + sort_loop_inner(j, c) + ); + } + + @Phantom proc sort_loop_inner (j: int, c: Comparator): void + { + val idxA: int = j; + val idxB: int = j + 1; + val a: Object = action LIST_GET(this.storage, idxA); + val b: Object = action LIST_GET(this.storage, idxB); + + if (action CALL(c, [a, b]) > 0) + { + action LIST_SET(this.storage, idxA, b); + action LIST_SET(this.storage, idxB, a); + } } // constructors - constructor *.ArrayList (@target self: ArrayList) + constructor *.`` (@target self: ArrayList) { this.storage = action LIST_NEW(); - this.length = 0; } - constructor *.ArrayList (@target self: ArrayList, c: Collection) + constructor *.`` (@target self: ArrayList, c: Collection) { if (c == null) _throwNPE(); this.storage = action LIST_NEW(); - this.length = 0; _addAllElements(0, c); } - constructor *.ArrayList (@target self: ArrayList, initialCapacity: int) + constructor *.`` (@target self: ArrayList, initialCapacity: int) { if (initialCapacity < 0) { @@ -397,7 +545,6 @@ automaton ArrayListAutomaton action THROW_NEW("java.lang.IllegalArgumentException", []); } this.storage = action LIST_NEW(); - this.length = 0; } @@ -405,9 +552,8 @@ automaton ArrayListAutomaton fun *.add (@target self: ArrayList, e: Object): boolean { - action LIST_INSERT_AT(this.storage, this.length, e); + action LIST_INSERT_AT(this.storage, action LIST_SIZE(this.storage), e); - this.length += 1; this.modCount += 1; result = true; @@ -422,7 +568,7 @@ automaton ArrayListAutomaton fun *.addAll (@target self: ArrayList, c: Collection): boolean { - result = _addAllElements(this.length, c); + result = _addAllElements(action LIST_SIZE(this.storage), c); } @@ -436,7 +582,6 @@ automaton ArrayListAutomaton fun *.clear (@target self: ArrayList): void { this.storage = action LIST_NEW(); - this.length = 0; this.modCount += 1; } @@ -444,18 +589,17 @@ automaton ArrayListAutomaton fun *.clone (@target self: ArrayList): Object { val storageCopy: list = action LIST_NEW(); - action LIST_COPY(this.storage, storageCopy, 0, 0, this.length); + action LIST_COPY(this.storage, storageCopy, 0, 0, action LIST_SIZE(this.storage)); result = new ArrayListAutomaton(state = Initialized, storage = storageCopy, - length = this.length ); } fun *.contains (@target self: ArrayList, o: Object): boolean { - result = action LIST_FIND(this.storage, o, 0, this.length) >= 0; + result = action LIST_FIND(this.storage, o, 0, action LIST_SIZE(this.storage)) != -1; } @@ -467,9 +611,9 @@ automaton ArrayListAutomaton if (c has ArrayListAutomaton) { val otherStorage: list = ArrayListAutomaton(c).storage; - val otherLength: int = ArrayListAutomaton(c).length; - action ASSUME(otherStorage != null); + + val otherLength: int = action LIST_SIZE(otherStorage); action ASSUME(otherLength >= 0); var i: int = 0; @@ -491,7 +635,7 @@ automaton ArrayListAutomaton @Phantom proc containsAll_loop_optimized (otherStorage: list, i: int, result: boolean): void { val item: Object = action LIST_GET(otherStorage, i); - result = action LIST_FIND(this.storage, item, 0, this.length) >= 0; + result = action LIST_FIND(this.storage, item, 0, action LIST_SIZE(this.storage)) != -1; i += 1; } @@ -499,7 +643,7 @@ automaton ArrayListAutomaton @Phantom proc containsAll_loop_regular (iter: Iterator, result: boolean): void { val item: Object = action CALL_METHOD(iter, "next", []); - result = action LIST_FIND(this.storage, item, 0, this.length) >= 0; + result = action LIST_FIND(this.storage, item, 0, action LIST_SIZE(this.storage)) != -1; } @@ -522,11 +666,9 @@ automaton ArrayListAutomaton { val expectedModCount: int = this.modCount; val otherExpectedModCount: int = ArrayListAutomaton(other).modCount; - val otherStorage: list = ArrayListAutomaton(other).storage; - val otherLength: int = ArrayListAutomaton(other).length; - if (this.length == otherLength) + if (action LIST_SIZE(this.storage) == action LIST_SIZE(otherStorage)) result = action OBJECT_EQUALS(this.storage, otherStorage); else result = false; @@ -551,7 +693,7 @@ automaton ArrayListAutomaton var i: int = 0; action LOOP_WHILE( - this.modCount == expectedModCount && i < this.length, + this.modCount == expectedModCount && i < action LIST_SIZE(this.storage), forEach_loop(i, _action) ); @@ -569,7 +711,7 @@ automaton ArrayListAutomaton fun *.get (@target self: ArrayList, index: int): Object { - _checkValidIndex(index, this.length); + _checkValidIndex(index, action LIST_SIZE(this.storage)); result = action LIST_GET(this.storage, index); } @@ -583,13 +725,13 @@ automaton ArrayListAutomaton fun *.indexOf (@target self: ArrayList, o: Object): int { - result = action LIST_FIND(this.storage, o, 0, this.length); + result = action LIST_FIND(this.storage, o, 0, action LIST_SIZE(this.storage)); } fun *.isEmpty (@target self: ArrayList): boolean { - result = this.length == 0; + result = action LIST_SIZE(this.storage) == 0; } @@ -605,16 +747,31 @@ automaton ArrayListAutomaton fun *.lastIndexOf (@target self: ArrayList, o: Object): int { - result = action LIST_FIND(this.storage, o, 0, this.length); - if (result != -1) + result = -1; + + val size: int = action LIST_SIZE(this.storage); + if (size != 0) { - // there should be no elements to the right of the previously found position - val nextIndex: int = result + 1; - if (nextIndex < this.length) - { - val rightIndex: int = action LIST_FIND(this.storage, o, nextIndex, this.length); - action ASSUME(rightIndex == -1); - } + action ASSUME(size > 0); + + val items: list = this.storage; + + var i: int = 0; + action LOOP_FOR( + i, size - 1, -1, -1, + lastIndexOf_loop(i, items, o, result) + ); + } + } + + @Phantom proc lastIndexOf_loop (i: int, items: list, o: Object, result: int): void + { + val e: Object = action LIST_GET(items, i); + + if (action OBJECT_EQUALS(o, e)) + { + result = i; + action LOOP_BREAK(); } } @@ -650,10 +807,8 @@ automaton ArrayListAutomaton fun *.remove (@target self: ArrayList, o: Object): boolean { - val index: int = action LIST_FIND(this.storage, o, 0, this.length); - - result = index >= 0; - + val index: int = action LIST_FIND(this.storage, o, 0, action LIST_SIZE(this.storage)); + result = index != -1; if (result) _deleteElement(index); } @@ -667,56 +822,13 @@ automaton ArrayListAutomaton fun *.removeAll (@target self: ArrayList, c: Collection): boolean { - val oldLength: int = this.length; - - if (c has ArrayListAutomaton) - { - val otherStorage: list = ArrayListAutomaton(c).storage; - val otherLength: int = ArrayListAutomaton(c).length; - - action ASSUME(otherStorage != null); - action ASSUME(otherLength >= 0); - - var i: int = 0; - action LOOP_FOR( - i, 0, otherLength, +1, - removeAll_loop_optimized(i, otherStorage) - ); - } - else - { - val iter: Iterator = action CALL_METHOD(c, "iterator", []); - action LOOP_WHILE( - action CALL_METHOD(iter, "hasNext", []), - removeAll_loop_regular(iter) - ); - } - - result = oldLength != this.length; - } - - @Phantom proc removeAll_loop_optimized (i: int, otherStorage: list): void - { - val o: Object = action LIST_GET(otherStorage, i); - val index: int = action LIST_FIND(this.storage, o, 0, this.length); - - if (index >= 0) - _deleteElement(index); - } - - @Phantom proc removeAll_loop_regular (iter: Iterator): void - { - val o: Object = action CALL_METHOD(iter, "next", []); - val index: int = action LIST_FIND(this.storage, o, 0, this.length); - - if (index >= 0) - _deleteElement(index); + result = _batchRemove(c, /* complement = */false, 0, action LIST_SIZE(this.storage)); } fun *.removeIf (@target self: ArrayList, filter: Predicate): boolean { - result = _removeIf(filter, 0, this.length); + result = _removeIf(filter, 0, action LIST_SIZE(this.storage)); } @@ -725,56 +837,14 @@ automaton ArrayListAutomaton if (op == null) _throwNPE(); - _replaceAllRange(0, this.length, op); + _replaceAllRange(op, 0, action LIST_SIZE(this.storage)); this.modCount += 1; } fun *.retainAll (@target self: ArrayList, c: Collection): boolean { - val oldLength: int = this.length; - var i: int = 0; - - if (c has ArrayListAutomaton) - { - val otherStorage: list = ArrayListAutomaton(c).storage; - val otherLength: int = ArrayListAutomaton(c).length; - - action ASSUME(otherStorage != null); - action ASSUME(otherLength >= 0); - - action LOOP_FOR( - i, this.length - 1, 0, -1, - retainAll_loop_optimized(i, otherStorage, otherLength) - ); - } - else - { - action LOOP_FOR( - i, this.length - 1, 0, -1, - retainAll_loop_regular(i, c) - ); - } - - result = oldLength != this.length; - } - - @Phantom proc retainAll_loop_optimized (i: int, otherStorage: list, otherLength: int): void - { - val item: Object = action LIST_GET(this.storage, i); - val otherHasItem: boolean = action LIST_FIND(otherStorage, item, 0, otherLength) >= 0; - - if (!otherHasItem) - _deleteElement(i); - } - - @Phantom proc retainAll_loop_regular (i: int, c: Collection): void - { - val item: Object = action LIST_GET(this.storage, i); - val otherHasItem: boolean = action CALL_METHOD(c, "contains", [item]); - - if (!otherHasItem) - _deleteElement(i); + result = _batchRemove(c, /* complement = */true, 0, action LIST_SIZE(this.storage)); } @@ -786,97 +856,13 @@ automaton ArrayListAutomaton fun *.size (@target self: ArrayList): int { - result = this.length; + result = action LIST_SIZE(this.storage); } fun *.sort (@target self: ArrayList, c: Comparator): void { - if (this.length != 0) - { - val expectedModCount: int = this.modCount; - - // Java has no unsigned primitive data types - action ASSUME(this.length > 0); - - // plain bubble sorting algorithm - val outerLimit: int = this.length - 1; - var innerLimit: int = 0; - var i: int = 0; - var j: int = 0; - - // check the comparator - if (c == null) - { - // using Comparable::compareTo as a comparator - - // plain bubble sorting algorithm - action LOOP_FOR( - i, 0, outerLimit, +1, - sort_loop_outer_noComparator(i, j, innerLimit) - ); - } - else - { - // using the provided comparator - - // plain bubble sorting algorithm (with a comparator) - action LOOP_FOR( - i, 0, outerLimit, +1, - sort_loop_outer(i, j, innerLimit, c) - ); - } - - _checkForComodification(expectedModCount); - } - - this.modCount += 1; - } - - @Phantom proc sort_loop_outer_noComparator (i: int, j: int, innerLimit: int): void - { - innerLimit = this.length - i - 1; - action LOOP_FOR( - j, 0, innerLimit, +1, - sort_loop_inner_noComparator(j) - ); - } - - @Phantom proc sort_loop_inner_noComparator (j: int): void - { - val idxA: int = j; - val idxB: int = j + 1; - val a: Object = action LIST_GET(this.storage, idxA); - val b: Object = action LIST_GET(this.storage, idxB); - - if (action CALL_METHOD(a as Comparable, "compareTo", [b]) > 0) - { - action LIST_SET(this.storage, idxA, b); - action LIST_SET(this.storage, idxB, a); - } - } - - @Phantom proc sort_loop_outer (i: int, j: int, innerLimit: int, c: Comparator): void - { - innerLimit = this.length - i - 1; - action LOOP_FOR( - j, 0, innerLimit, +1, - sort_loop_inner(j, c) - ); - } - - @Phantom proc sort_loop_inner (j: int, c: Comparator): void - { - val idxA: int = j; - val idxB: int = j + 1; - val a: Object = action LIST_GET(this.storage, idxA); - val b: Object = action LIST_GET(this.storage, idxB); - - if (action CALL(c, [a, b]) > 0) - { - action LIST_SET(this.storage, idxA, b); - action LIST_SET(this.storage, idxB, a); - } + _do_sort(0, action LIST_SIZE(this.storage), c); } @@ -897,7 +883,7 @@ automaton ArrayListAutomaton fun *.subList (@target self: ArrayList, fromIndex: int, toIndex: int): List { - _subListRangeCheck(fromIndex, toIndex, this.length); + _subListRangeCheck(fromIndex, toIndex, action LIST_SIZE(this.storage)); result = new ArrayList_SubListAutomaton(state = Initialized, root = self, @@ -911,7 +897,7 @@ automaton ArrayListAutomaton fun *.toArray (@target self: ArrayList): array { - val len: int = this.length; + val len: int = action LIST_SIZE(this.storage); result = action ARRAY_NEW("java.lang.Object", len); var i: int = 0; @@ -935,7 +921,7 @@ automaton ArrayListAutomaton val a: array = action CALL_METHOD(generator, "apply", [0]) as array; val aLen: int = action ARRAY_SIZE(a); - val len: int = this.length; + val len: int = action LIST_SIZE(this.storage); // #problem: a.getClass() should be called to construct a type-valid array (USVM issue) result = action ARRAY_NEW("java.lang.Object", len); @@ -950,7 +936,7 @@ automaton ArrayListAutomaton fun *.toArray (@target self: ArrayList, a: array): array { val aLen: int = action ARRAY_SIZE(a); - val len: int = this.length; + val len: int = action LIST_SIZE(this.storage); if (aLen < len) // #problem: a.getClass() should be called to construct a type-valid array (USVM issue) diff --git a/spec/java/util/Dictionary.lsl b/spec/java/util/Dictionary.lsl index 3c1766bc..42a4b90e 100644 --- a/spec/java/util/Dictionary.lsl +++ b/spec/java/util/Dictionary.lsl @@ -34,4 +34,5 @@ import java/util/Enumeration; } -// global aliases and type overrides \ No newline at end of file +// global aliases and type overrides + diff --git a/spec/java/util/HashSet.KeyIterator.lsl b/spec/java/util/HashSet.KeyIterator.lsl index 6773f7b8..c857d705 100644 --- a/spec/java/util/HashSet.KeyIterator.lsl +++ b/spec/java/util/HashSet.KeyIterator.lsl @@ -37,7 +37,7 @@ automaton HashSet_KeyIteratorAutomaton shift Allocated -> Initialized by [ // constructors // Problem: here mustn't be "HashMap"; What must be here ? What we must to do with constructor ? - HashSet_KeyIterator, + ``, ]; shift Initialized -> self by [ @@ -63,7 +63,7 @@ automaton HashSet_KeyIteratorAutomaton // constructors - @private constructor *.HashSet_KeyIterator (@target self: HashSet_KeyIterator, source: HashMap) + @private constructor *.`` (@target self: HashSet_KeyIterator, source: HashMap) { action ERROR("Private constructor call"); } @@ -74,7 +74,9 @@ automaton HashSet_KeyIteratorAutomaton fun *.hasNext (@target self: HashSet_KeyIterator): boolean { action ASSUME(this.parent != null); - val length: int = HashSetAutomaton(this.parent).length; + + val parentStorage: map = HashSetAutomaton(this.parent).storage; + val length: int = action MAP_SIZE(parentStorage); result = this.index < length; } @@ -84,7 +86,8 @@ automaton HashSet_KeyIteratorAutomaton action ASSUME(this.parent != null); _checkForComodification(); - val length: int = HashSetAutomaton(this.parent).length; + val parentStorage: map = HashSetAutomaton(this.parent).storage; + val length: int = action MAP_SIZE(parentStorage); val atValidPosition: boolean = this.index < length; if (!atValidPosition) action THROW_NEW("java.util.NoSuchElementException", []); @@ -105,7 +108,8 @@ automaton HashSet_KeyIteratorAutomaton { action ASSUME(this.parent != null); - val length: int = HashSetAutomaton(this.parent).length; + val parentStorage: map = HashSetAutomaton(this.parent).storage; + val length: int = action MAP_SIZE(parentStorage); val atValidPosition: boolean = this.index < length; if (!atValidPosition || !this.nextWasCalled) action THROW_NEW("java.lang.IllegalStateException", []); @@ -114,7 +118,6 @@ automaton HashSet_KeyIteratorAutomaton _checkForComodification(); - val parentStorage: map = HashSetAutomaton(this.parent).storage; action MAP_REMOVE(parentStorage, this.currentKey); this.expectedModCount = HashSetAutomaton(this.parent).modCount; @@ -128,7 +131,8 @@ automaton HashSet_KeyIteratorAutomaton if (userAction == null) action THROW_NEW("java.lang.NullPointerException", []); - val length: int = HashSetAutomaton(this.parent).length; + val parentStorage: map = HashSetAutomaton(this.parent).storage; + val length: int = action MAP_SIZE(parentStorage); var i: int = this.index; action LOOP_WHILE( diff --git a/spec/java/util/HashSet.Spliterator.lsl b/spec/java/util/HashSet.Spliterator.lsl index 597191d1..4b859140 100644 --- a/spec/java/util/HashSet.Spliterator.lsl +++ b/spec/java/util/HashSet.Spliterator.lsl @@ -34,7 +34,7 @@ automaton HashSet_KeySpliteratorAutomaton shift Allocated -> Initialized by [ // constructors - HashSet_KeySpliterator + `` ]; shift Initialized -> self by [ @@ -59,7 +59,7 @@ automaton HashSet_KeySpliteratorAutomaton if (hi < 0) { val parentStorage: map = HashSetAutomaton(this.parent).storage; - this.est = HashSetAutomaton(this.parent).length; + this.est = action MAP_SIZE(parentStorage); this.expectedModCount = HashSetAutomaton(this.parent).modCount; this.fence = this.est; // That's right ? @@ -86,7 +86,8 @@ automaton HashSet_KeySpliteratorAutomaton // constructors - @private constructor *.HashSet_KeySpliterator (@target self: HashSet_KeySpliterator, source: HashMap, origin: int, fence: int, est: int, expectedModCount: int) + @private constructor *.`` (@target self: HashSet_KeySpliterator, + source: HashMap, origin: int, fence: int, est: int, expectedModCount: int) { this.index = origin; this.fence = fence; @@ -108,12 +109,13 @@ automaton HashSet_KeySpliteratorAutomaton { action ASSUME(this.parent != null); - var mask: int = 0; - val length: int = HashSetAutomaton(this.parent).length; + result = 0; + val parentStorage: map = HashSetAutomaton(this.parent).storage; + val length: int = action MAP_SIZE(parentStorage); if (this.fence < 0 || this.est == length) - mask = SPLITERATOR_SIZED; + result = SPLITERATOR_SIZED; - result = mask | SPLITERATOR_DISTINCT; + result |= SPLITERATOR_DISTINCT; } @@ -127,7 +129,8 @@ automaton HashSet_KeySpliteratorAutomaton var hi: int = this.fence; var mc: int = this.expectedModCount; var i: int = this.index; - val length: int = HashSetAutomaton(this.parent).length; + val parentStorage: map = HashSetAutomaton(this.parent).storage; + val length: int = action MAP_SIZE(parentStorage); if(hi < 0) { diff --git a/spec/java/util/HashSet.main.lsl b/spec/java/util/HashSet.main.lsl index 0b522962..2bcdc1a5 100644 --- a/spec/java/util/HashSet.main.lsl +++ b/spec/java/util/HashSet.main.lsl @@ -9,21 +9,22 @@ library std // imports import java/lang/Object; -import java/util/HashSet; import java/util/function/IntFunction; import java/util/function/Consumer; import java/util/function/Predicate; import java/util/Collection; import java/util/Iterator; import java/util/Spliterator; +import java/util/stream/Stream; + +import java/util/HashSet; // automata automaton HashSetAutomaton ( - var storage: map = null, - @transient var length: int = 0 + var storage: map ) : HashSet { @@ -34,10 +35,10 @@ automaton HashSetAutomaton shift Allocated -> Initialized by [ // constructors - HashSet(HashSet), - HashSet(HashSet, Collection), - HashSet(HashSet, int, float), - HashSet(HashSet, int, float, boolean) + `` (HashSet), + `` (HashSet, Collection), + `` (HashSet, int, float), + `` (HashSet, int, float, boolean) ]; shift Initialized -> self by [ @@ -55,9 +56,9 @@ automaton HashSetAutomaton spliterator, stream, parallelStream, - toArray(HashSet), - toArray(HashSet, array), - toArray(HashSet, IntFunction), + toArray (HashSet), + toArray (HashSet, array), + toArray (HashSet, IntFunction), toString, // write operations @@ -87,7 +88,7 @@ automaton HashSetAutomaton proc _addAllElements (c: Collection): boolean { - val lengthBeforeAdd: int = this.length; + val lengthBeforeAdd: int = action MAP_SIZE(this.storage); val iter: Iterator = action CALL_METHOD(c, "iterator", []); action LOOP_WHILE( @@ -95,7 +96,7 @@ automaton HashSetAutomaton _addAllElements_loop(iter) ); - if (lengthBeforeAdd != this.length) + if (lengthBeforeAdd != action MAP_SIZE(this.storage)) { this.modCount += 1; result = true; @@ -110,13 +111,9 @@ automaton HashSetAutomaton @Phantom proc _addAllElements_loop(iter: Iterator): void { val key: Object = action CALL_METHOD(iter, "next", []); - val hasKey: boolean = action MAP_HAS_KEY(this.storage, key); - if (!hasKey) - { + if (action MAP_HAS_KEY(this.storage, key) == false) action MAP_SET(this.storage, key, SOMETHING); - this.length += 1; - } } @@ -126,22 +123,54 @@ automaton HashSetAutomaton } + proc _makeStream (parallel: boolean): Stream + { + val unseen: map = action MAP_CLONE(this.storage); + + val count: int = action MAP_SIZE(unseen); + val items: array = action ARRAY_NEW("java.lang.Object", count); + + var i: int = 0; + action LOOP_FOR( + i, 0, count, +1, + _makeStream_loop(i, items, unseen) + ); + + // #problem: unable to catch concurrent modifications during stream processing + + result = new StreamAutomaton(state = Initialized, + storage = items, + length = count, + closeHandlers = action LIST_NEW(), + isParallel = parallel, + ); + } + + @Phantom proc _makeStream_loop (i: int, items: array, unseen: map): void + { + val key: Object = action MAP_GET_ANY_KEY(unseen); + action MAP_REMOVE(unseen, key); + + items[i] = key; + } + + // constructors - constructor *.HashSet (@target self: HashSet) + constructor *.`` (@target self: HashSet) { this.storage = action MAP_NEW(); } - constructor *.HashSet (@target self: HashSet, c: Collection) + constructor *.`` (@target self: HashSet, c: Collection) { this.storage = action MAP_NEW(); _addAllElements(c); } - constructor *.HashSet (@target self: HashSet, initialCapacity: int) + constructor *.`` (@target self: HashSet, initialCapacity: int) { if (initialCapacity < 0) { @@ -153,7 +182,7 @@ automaton HashSetAutomaton } - constructor *.HashSet (@target self: HashSet, initialCapacity: int, loadFactor: float) + constructor *.`` (@target self: HashSet, initialCapacity: int, loadFactor: float) { if (initialCapacity < 0) { @@ -161,7 +190,7 @@ automaton HashSetAutomaton action THROW_NEW("java.lang.IllegalArgumentException", []); } - if (loadFactor <= 0 || action DEBUG_DO("Float.isNaN(loadFactor)")) + if (loadFactor <= 0 || loadFactor != loadFactor /* NaN */) { // val loadFactorStr: String = "Illegal load factor: " + action OBJECT_TO_STRING(loadFactor); action THROW_NEW("java.lang.IllegalArgumentException", []); @@ -171,7 +200,7 @@ automaton HashSetAutomaton } - @private constructor *.HashSet (@target self: HashSet, initialCapacity: int, loadFactor: float, dummy: boolean) + @private constructor *.`` (@target self: HashSet, initialCapacity: int, loadFactor: float, dummy: boolean) { action ERROR("Private constructor call"); } @@ -189,10 +218,7 @@ automaton HashSetAutomaton } else { - this.length += 1; - action MAP_SET(this.storage, obj, SOMETHING); - result = true; } @@ -202,27 +228,22 @@ automaton HashSetAutomaton fun *.clear (@target self: HashSet): void { - this.length = 0; this.storage = action MAP_NEW(); - this.modCount += 1; } fun *.clone (@target self: HashSet): Object { - val storageCopy: map = action MAP_CLONE(this.storage); - result = new HashSetAutomaton(state = Initialized, - storage = storageCopy, - length = this.length + storage = action MAP_CLONE(this.storage) ); } fun *.contains (@target self: HashSet, obj: Object): boolean { - if (this.length == 0) + if (action MAP_SIZE(this.storage) == 0) result = false; else result = action MAP_HAS_KEY(this.storage, obj); @@ -231,7 +252,7 @@ automaton HashSetAutomaton fun *.isEmpty (@target self: HashSet): boolean { - result = this.length == 0; + result = action MAP_SIZE(this.storage) == 0; } @@ -248,11 +269,9 @@ automaton HashSetAutomaton fun *.remove (@target self: HashSet, obj: Object): boolean { - val hasKey: boolean = action MAP_HAS_KEY(this.storage, obj); - if (hasKey) + if (action MAP_HAS_KEY(this.storage, obj)) { action MAP_REMOVE(this.storage, obj); - this.length -= 1; this.modCount += 1; result = true; } @@ -265,18 +284,18 @@ automaton HashSetAutomaton fun *.size (@target self: HashSet): int { - result = this.length; + result = action MAP_SIZE(this.storage); } fun *.spliterator (@target self: HashSet): Spliterator { - val keysStorageArray: array = action ARRAY_NEW("java.lang.Object", this.length); + val keysStorageArray: array = action ARRAY_NEW("java.lang.Object", action MAP_SIZE(this.storage)); val unseenKeys: map = action MAP_CLONE(this.storage); var i: int = 0; action LOOP_FOR( - i, 0, this.length, +1, + i, 0, action MAP_SIZE(this.storage), +1, fromMapToArray_loop(i, keysStorageArray, unseenKeys) ); @@ -309,16 +328,14 @@ automaton HashSetAutomaton } else { - val isSameType: boolean = action OBJECT_SAME_TYPE(self, other); - if (isSameType) + if (other has HashSetAutomaton) { val expectedModCount: int = this.modCount; val otherExpectedModCount: int = HashSetAutomaton(other).modCount; val otherStorage: map = HashSetAutomaton(other).storage; - val otherLength: int = HashSetAutomaton(other).length; - if (this.length == otherLength) + if (action MAP_SIZE(this.storage) == action MAP_SIZE(otherStorage)) result = action OBJECT_EQUALS(this.storage, otherStorage); else result = false; @@ -348,10 +365,10 @@ automaton HashSetAutomaton val expectedModCount: int = this.modCount; val otherSize: int = action CALL_METHOD(c, "size", []); val iter: Iterator = action CALL_METHOD(c, "iterator", []); - val lengthBeforeRemoving: int = this.length; + val lengthBeforeRemoving: int = action MAP_SIZE(this.storage); var i: int = 0; - if (this.length > otherSize) + if (action MAP_SIZE(this.storage) > otherSize) { action LOOP_WHILE( action CALL_METHOD(iter, "hasNext", []), @@ -363,7 +380,7 @@ automaton HashSetAutomaton val unseenKeys: map = action MAP_CLONE(this.storage); action LOOP_WHILE( - i < this.length, + i < action MAP_SIZE(this.storage), _removeAllElements_loop_indirect(i, c, unseenKeys) ); } @@ -371,20 +388,16 @@ automaton HashSetAutomaton _checkForComodification(expectedModCount); this.modCount += 1; // If length changed, it means that at least one element was deleted - result = lengthBeforeRemoving != this.length; + result = lengthBeforeRemoving != action MAP_SIZE(this.storage); } @Phantom proc removeAllElements_loop_direct (iter: Iterator): void { val key: Object = action CALL_METHOD(iter, "next", []); - val isKeyExist: boolean = action MAP_HAS_KEY(this.storage, key); - if (isKeyExist) - { + if (action MAP_HAS_KEY(this.storage, key)) action MAP_REMOVE(this.storage, key); - this.length -= 1; - } } @@ -393,13 +406,8 @@ automaton HashSetAutomaton val key: Object = action MAP_GET_ANY_KEY(unseenKeys); action MAP_REMOVE(unseenKeys, key); - val isCollectionContainsKey: boolean = action CALL_METHOD(c, "contains", [key]); - - if (isCollectionContainsKey) - { + if (action CALL_METHOD(c, "contains", [key])) action MAP_REMOVE(this.storage, key); - this.length -= 1; - } i += 1; } @@ -407,7 +415,7 @@ automaton HashSetAutomaton fun *.toArray (@target self: HashSet): array { - val len: int = this.length; + val len: int = action MAP_SIZE(this.storage); result = action ARRAY_NEW("java.lang.Object", len); val expectedModCount: int = this.modCount; val unseenKeys: map = action MAP_CLONE(this.storage); @@ -435,7 +443,7 @@ automaton HashSetAutomaton { val expectedModCount: int = this.modCount; val aLen: int = action ARRAY_SIZE(a); - val len: int = this.length; + val len: int = action MAP_SIZE(this.storage); val unseenKeys: map = action MAP_CLONE(this.storage); var i: int = 0; @@ -449,8 +457,8 @@ automaton HashSetAutomaton toArray_loop(i, unseenKeys, result) ); - if (aLen > this.length) - result[this.length] = null; + if (aLen > len) + result[len] = null; _checkForComodification(expectedModCount); } @@ -461,7 +469,7 @@ automaton HashSetAutomaton if (generator == null) _throwNPE(); - val len: int = this.length; + val len: int = action MAP_SIZE(this.storage); result = action CALL(generator, [0]) as array; val expectedModCount: int = this.modCount; val unseenKeys: map = action MAP_CLONE(this.storage); @@ -515,7 +523,7 @@ automaton HashSetAutomaton if (c == null) _throwNPE(); - val lengthBeforeAdd: int = this.length; + val lengthBeforeAdd: int = action MAP_SIZE(this.storage); val iter: Iterator = action CALL_METHOD(c, "iterator", []); action LOOP_WHILE( @@ -523,7 +531,7 @@ automaton HashSetAutomaton _retainAllElements_loop(iter) ); - if (lengthBeforeAdd != this.length) + if (lengthBeforeAdd != action MAP_SIZE(this.storage)) { this.modCount += 1; result = true; @@ -538,13 +546,9 @@ automaton HashSetAutomaton @Phantom proc _retainAllElements_loop(iter: Iterator): void { val key: Object = action CALL_METHOD(iter, "next", []); - val hasKey: boolean = action MAP_HAS_KEY(this.storage, key); - if (!hasKey) - { + if (action MAP_HAS_KEY(this.storage, key) == false) action MAP_REMOVE(this.storage, key); - this.length -= 1; - } } @@ -553,7 +557,7 @@ automaton HashSetAutomaton if (filter == null) _throwNPE(); - val lengthBeforeAdd: int = this.length; + val lengthBeforeAdd: int = action MAP_SIZE(this.storage); val expectedModCount: int = this.modCount; var i: int = 0; val unseenKeys: map = action MAP_CLONE(this.storage); @@ -564,7 +568,7 @@ automaton HashSetAutomaton ); _checkForComodification(expectedModCount); - if (lengthBeforeAdd != this.length) + if (lengthBeforeAdd != action MAP_SIZE(this.storage)) { this.modCount += 1; result = true; @@ -581,13 +585,8 @@ automaton HashSetAutomaton val key: Object = action MAP_GET_ANY_KEY(unseenKeys); action MAP_REMOVE(unseenKeys, key); - var isDelete: boolean = action CALL(filter, [key]); - - if(isDelete) - { + if(action CALL(filter, [key])) action MAP_REMOVE(this.storage, key); - this.length -= 1; - } i += 1; } @@ -599,11 +598,12 @@ automaton HashSetAutomaton _throwNPE(); var i: int = 0; + val count: int = action MAP_SIZE(this.storage); val expectedModCount: int = this.modCount; val unseenKeys: map = action MAP_CLONE(this.storage); action LOOP_WHILE( - i < this.length, + i < count, forEach_loop(i, unseenKeys, userAction) ); @@ -625,18 +625,14 @@ automaton HashSetAutomaton // within java.util.Collection fun *.stream (@target self: HashSet): Stream { - // #todo: use custom stream implementation - result = action SYMBOLIC("java.util.stream.Stream"); - action ASSUME(result != null); + result = _makeStream(/* parallel = */ false); } // within java.util.Collection fun *.parallelStream (@target self: HashSet): Stream { - // #todo: use custom stream implementation - result = action SYMBOLIC("java.util.stream.Stream"); - action ASSUME(result != null); + result = _makeStream(/* parallel = */ true); } @@ -659,6 +655,38 @@ automaton HashSetAutomaton // within java.util.AbstractCollection fun *.toString (@target self: HashSet): String { - result = action OBJECT_TO_STRING(this.storage); + val items: map = this.storage; + var count: int = action MAP_SIZE(items); + + if (count == 0) + { + result = "[]"; + } + else + { + action ASSUME(count > 0); + + result = "["; + + val unseen: map = action MAP_CLONE(items); + action LOOP_WHILE( + count != 0, + toString_loop(unseen, count, result) + ); + + result += "]"; + } + } + + @Phantom proc toString_loop (unseen: map, count: int, result: String): void + { + val key: Object = action MAP_GET_ANY_KEY(unseen); + action MAP_REMOVE(unseen, key); + + result += action OBJECT_TO_STRING(key); + + if (count > 1) + result += ", "; + count -= 1; } } \ No newline at end of file diff --git a/spec/java/util/Hashtable.lsl b/spec/java/util/Hashtable.lsl index 9fa1d3bf..cd3c4db4 100644 --- a/spec/java/util/Hashtable.lsl +++ b/spec/java/util/Hashtable.lsl @@ -16,16 +16,12 @@ import java/util/Map; // primary semantic types -@extends("java.util.Dictionary") -@implements("java.util.Map") -@implements("java.lang.Cloneable") -@implements("java.io.Serializable") -@public type Hashtable +type Hashtable is java.util.Hashtable for Dictionary, Map, Cloneable, Serializable { - @private @static val serialVersionUID: long = 1421746759512286392L; } -// global aliases and type overrides \ No newline at end of file +// global aliases and type overrides + diff --git a/spec/java/util/LinkedHashSet.KeyIterator.lsl b/spec/java/util/LinkedHashSet.KeyIterator.lsl index 9c440f39..03a43232 100644 --- a/spec/java/util/LinkedHashSet.KeyIterator.lsl +++ b/spec/java/util/LinkedHashSet.KeyIterator.lsl @@ -36,7 +36,7 @@ automaton LinkedHashSet_KeyIteratorAutomaton shift Allocated -> Initialized by [ // constructors - LinkedHashSet_KeyIterator, + ``, ]; shift Initialized -> self by [ @@ -62,7 +62,7 @@ automaton LinkedHashSet_KeyIteratorAutomaton // constructors - @private constructor *.LinkedHashSet_KeyIterator (@target self: LinkedHashSet_KeyIterator, source: HashMap) + @private constructor *.`` (@target self: LinkedHashSet_KeyIterator, source: HashMap) { action ERROR("Private constructor call"); } @@ -73,8 +73,10 @@ automaton LinkedHashSet_KeyIteratorAutomaton fun *.hasNext (@target self: LinkedHashSet_KeyIterator): boolean { action ASSUME(this.parent != null); - val length: int = LinkedHashSetAutomaton(this.parent).length; - result = this.index < length; + + val parentStorage: map = LinkedHashSetAutomaton(this.parent).storage; + + result = this.index < action MAP_SIZE(parentStorage); } @@ -83,8 +85,9 @@ automaton LinkedHashSet_KeyIteratorAutomaton action ASSUME(this.parent != null); _checkForComodification(); - val length: int = LinkedHashSetAutomaton(this.parent).length; - val atValidPosition: boolean = this.index < length; + val parentStorage: map = LinkedHashSetAutomaton(this.parent).storage; + + val atValidPosition: boolean = this.index < action MAP_SIZE(parentStorage); if (!atValidPosition) action THROW_NEW("java.util.NoSuchElementException", []); @@ -104,8 +107,9 @@ automaton LinkedHashSet_KeyIteratorAutomaton { action ASSUME(this.parent != null); - val length: int = LinkedHashSetAutomaton(this.parent).length; - val atValidPosition: boolean = this.index < length; + val parentStorage: map = LinkedHashSetAutomaton(this.parent).storage; + + val atValidPosition: boolean = this.index < action MAP_SIZE(parentStorage); if (!atValidPosition || !this.nextWasCalled) action THROW_NEW("java.lang.IllegalStateException", []); @@ -113,7 +117,6 @@ automaton LinkedHashSet_KeyIteratorAutomaton _checkForComodification(); - val parentStorage: map = LinkedHashSetAutomaton(this.parent).storage; action MAP_REMOVE(parentStorage, this.currentKey); this.expectedModCount = LinkedHashSetAutomaton(this.parent).modCount; @@ -127,7 +130,8 @@ automaton LinkedHashSet_KeyIteratorAutomaton if (userAction == null) action THROW_NEW("java.lang.NullPointerException", []); - val length: int = LinkedHashSetAutomaton(this.parent).length; + val parentStorage: map = LinkedHashSetAutomaton(this.parent).storage; + val length: int = action MAP_SIZE(parentStorage); var i: int = this.index; action LOOP_WHILE( diff --git a/spec/java/util/LinkedHashSet.Spliterator.lsl b/spec/java/util/LinkedHashSet.Spliterator.lsl index f356fbc5..0254aba7 100644 --- a/spec/java/util/LinkedHashSet.Spliterator.lsl +++ b/spec/java/util/LinkedHashSet.Spliterator.lsl @@ -34,7 +34,7 @@ automaton LinkedHashSet_KeySpliteratorAutomaton shift Allocated -> Initialized by [ // constructors - LinkedHashSet_KeySpliterator + `` ]; shift Initialized -> self by [ @@ -59,7 +59,7 @@ automaton LinkedHashSet_KeySpliteratorAutomaton if (hi < 0) { val parentStorage: map = LinkedHashSetAutomaton(this.parent).storage; - this.est = LinkedHashSetAutomaton(this.parent).length; + this.est = action MAP_SIZE(parentStorage); this.expectedModCount = LinkedHashSetAutomaton(this.parent).modCount; this.fence = this.est; // That's right ? @@ -86,7 +86,7 @@ automaton LinkedHashSet_KeySpliteratorAutomaton // constructors - @private constructor *.LinkedHashSet_KeySpliterator (@target self: LinkedHashSet_KeySpliterator, source: HashMap, origin: int, fence: int, est: int, expectedModCount: int) + @private constructor *.`` (@target self: LinkedHashSet_KeySpliterator, source: HashMap, origin: int, fence: int, est: int, expectedModCount: int) { this.index = origin; this.fence = fence; @@ -108,12 +108,13 @@ automaton LinkedHashSet_KeySpliteratorAutomaton { action ASSUME(this.parent != null); - var mask: int = 0; - val length: int = LinkedHashSetAutomaton(this.parent).length; - if (this.fence < 0 || this.est == length) - mask = SPLITERATOR_SIZED; + val parentStorage: map = LinkedHashSetAutomaton(this.parent).storage; - result = mask | SPLITERATOR_DISTINCT; + result = 0; + if (this.fence < 0 || this.est == action MAP_SIZE(parentStorage)) + result = SPLITERATOR_SIZED; + + result |= SPLITERATOR_DISTINCT; } @@ -127,7 +128,9 @@ automaton LinkedHashSet_KeySpliteratorAutomaton var hi: int = this.fence; var mc: int = this.expectedModCount; var i: int = this.index; - val length: int = LinkedHashSetAutomaton(this.parent).length; + + val parentStorage: map = LinkedHashSetAutomaton(this.parent).storage; + val length: int = action MAP_SIZE(parentStorage); if(hi < 0) { diff --git a/spec/java/util/LinkedHashSet.main.lsl b/spec/java/util/LinkedHashSet.main.lsl index 932ecfe4..1c6c2fd9 100644 --- a/spec/java/util/LinkedHashSet.main.lsl +++ b/spec/java/util/LinkedHashSet.main.lsl @@ -8,20 +8,22 @@ library std // imports import java/lang/Object; -import java/util/LinkedHashSet; import java/util/function/IntFunction; import java/util/function/Consumer; import java/util/function/Predicate; import java/util/Collection; import java/util/Iterator; import java/util/Spliterator; +import java/util/stream/Stream; + +import java/util/LinkedHashSet; + // automata automaton LinkedHashSetAutomaton ( - var storage: map = null, - @transient var length: int = 0 + var storage: map ) : LinkedHashSet { @@ -32,10 +34,10 @@ automaton LinkedHashSetAutomaton shift Allocated -> Initialized by [ // constructors - LinkedHashSet (LinkedHashSet), - LinkedHashSet (LinkedHashSet, Collection), - LinkedHashSet (LinkedHashSet, int), - LinkedHashSet (LinkedHashSet, int, float), + `` (LinkedHashSet), + `` (LinkedHashSet, Collection), + `` (LinkedHashSet, int), + `` (LinkedHashSet, int, float), ]; shift Initialized -> self by [ @@ -73,6 +75,7 @@ automaton LinkedHashSetAutomaton @transient var modCount: int = 0; + // utilities @KeepVisible proc _checkForComodification (expectedModCount: int): void @@ -84,7 +87,7 @@ automaton LinkedHashSetAutomaton proc _addAllElements (c: Collection): boolean { - val lengthBeforeAdd: int = this.length; + val lengthBeforeAdd: int = action MAP_SIZE(this.storage); val iter: Iterator = action CALL_METHOD(c, "iterator", []); action LOOP_WHILE( @@ -92,7 +95,7 @@ automaton LinkedHashSetAutomaton _addAllElements_loop(iter) ); - if (lengthBeforeAdd != this.length) + if (lengthBeforeAdd != action MAP_SIZE(this.storage)) { this.modCount += 1; result = true; @@ -107,13 +110,9 @@ automaton LinkedHashSetAutomaton @Phantom proc _addAllElements_loop(iter: Iterator): void { val key: Object = action CALL_METHOD(iter, "next", []); - val hasKey: boolean = action MAP_HAS_KEY(this.storage, key); - if (!hasKey) - { + if (action MAP_HAS_KEY(this.storage, key) == false) action MAP_SET(this.storage, key, SOMETHING); - this.length += 1; - } } @@ -123,22 +122,54 @@ automaton LinkedHashSetAutomaton } + proc _makeStream (parallel: boolean): Stream + { + val unseen: map = action MAP_CLONE(this.storage); + + val count: int = action MAP_SIZE(unseen); + val items: array = action ARRAY_NEW("java.lang.Object", count); + + var i: int = 0; + action LOOP_FOR( + i, 0, count, +1, + _makeStream_loop(i, items, unseen) + ); + + // #problem: unable to catch concurrent modifications during stream processing + + result = new StreamAutomaton(state = Initialized, + storage = items, + length = count, + closeHandlers = action LIST_NEW(), + isParallel = parallel, + ); + } + + @Phantom proc _makeStream_loop (i: int, items: array, unseen: map): void + { + val key: Object = action MAP_GET_ANY_KEY(unseen); + action MAP_REMOVE(unseen, key); + + items[i] = key; + } + + // constructors - constructor *.LinkedHashSet (@target self: LinkedHashSet) + constructor *.`` (@target self: LinkedHashSet) { this.storage = action MAP_NEW(); } - constructor *.LinkedHashSet (@target self: LinkedHashSet, c: Collection) + constructor *.`` (@target self: LinkedHashSet, c: Collection) { this.storage = action MAP_NEW(); _addAllElements(c); } - constructor *.LinkedHashSet (@target self: LinkedHashSet, initialCapacity: int) + constructor *.`` (@target self: LinkedHashSet, initialCapacity: int) { if (initialCapacity < 0) { @@ -150,7 +181,7 @@ automaton LinkedHashSetAutomaton } - constructor *.LinkedHashSet (@target self: LinkedHashSet, initialCapacity: int, loadFactor: float) + constructor *.`` (@target self: LinkedHashSet, initialCapacity: int, loadFactor: float) { if (initialCapacity < 0) { @@ -158,7 +189,7 @@ automaton LinkedHashSetAutomaton action THROW_NEW("java.lang.IllegalArgumentException", []); } - if (loadFactor <= 0 || action DEBUG_DO("Float.isNaN(loadFactor)")) + if (loadFactor <= 0 || loadFactor != loadFactor /* NaN */) { // val loadFactorStr: String = "Illegal load factor: " + action OBJECT_TO_STRING(loadFactor); action THROW_NEW("java.lang.IllegalArgumentException", []); @@ -180,10 +211,7 @@ automaton LinkedHashSetAutomaton } else { - this.length += 1; - action MAP_SET(this.storage, obj, SOMETHING); - result = true; } @@ -193,27 +221,22 @@ automaton LinkedHashSetAutomaton fun *.clear (@target self: LinkedHashSet): void { - this.length = 0; this.storage = action MAP_NEW(); - this.modCount +=1; } fun *.clone (@target self: LinkedHashSet): Object { - val storageCopy: map = action MAP_CLONE(this.storage); - result = new LinkedHashSetAutomaton(state = Initialized, - storage = storageCopy, - length = this.length + storage = action MAP_CLONE(this.storage) ); } fun *.contains (@target self: LinkedHashSet, obj: Object): boolean { - if (this.length == 0) + if (action MAP_SIZE(this.storage) == 0) result = false; else result = action MAP_HAS_KEY(this.storage, obj); @@ -222,7 +245,7 @@ automaton LinkedHashSetAutomaton fun *.isEmpty (@target self: LinkedHashSet): boolean { - result = this.length == 0; + result = action MAP_SIZE(this.storage) == 0; } @@ -239,11 +262,9 @@ automaton LinkedHashSetAutomaton fun *.remove (@target self: LinkedHashSet, obj: Object): boolean { - val hasKey: boolean = action MAP_HAS_KEY(this.storage, obj); - if (hasKey) + if (action MAP_HAS_KEY(this.storage, obj)) { action MAP_REMOVE(this.storage, obj); - this.length -= 1; this.modCount += 1; result = true; } @@ -256,18 +277,18 @@ automaton LinkedHashSetAutomaton fun *.size (@target self: LinkedHashSet): int { - result = this.length; + result = action MAP_SIZE(this.storage); } fun *.spliterator (@target self: LinkedHashSet): Spliterator { - val keysStorageArray: array = action ARRAY_NEW("java.lang.Object", this.length); + val keysStorageArray: array = action ARRAY_NEW("java.lang.Object", action MAP_SIZE(this.storage)); val unseenKeys: map = action MAP_CLONE(this.storage); var i: int = 0; action LOOP_FOR( - i, 0, this.length, +1, + i, 0, action MAP_SIZE(this.storage), +1, fromMapToArray_loop(i, keysStorageArray, unseenKeys) ); @@ -299,16 +320,14 @@ automaton LinkedHashSetAutomaton } else { - val isSameType: boolean = action OBJECT_SAME_TYPE(self, other); - if (isSameType) + if (other has LinkedHashSetAutomaton) { val expectedModCount: int = this.modCount; val otherExpectedModCount: int = LinkedHashSetAutomaton(other).modCount; val otherStorage: map = LinkedHashSetAutomaton(other).storage; - val otherLength: int = LinkedHashSetAutomaton(other).length; - if (this.length == otherLength) + if (action MAP_SIZE(this.storage) == action MAP_SIZE(otherStorage)) result = action OBJECT_EQUALS(this.storage, otherStorage); else result = false; @@ -338,10 +357,10 @@ automaton LinkedHashSetAutomaton val expectedModCount: int = this.modCount; val otherSize: int = action CALL_METHOD(c, "size", []); val iter: Iterator = action CALL_METHOD(c, "iterator", []); - val lengthBeforeRemoving: int = this.length; + val lengthBeforeRemoving: int = action MAP_SIZE(this.storage); var i: int = 0; - if (this.length > otherSize) + if (action MAP_SIZE(this.storage) > otherSize) { action LOOP_WHILE( action CALL_METHOD(iter, "hasNext", []), @@ -352,7 +371,7 @@ automaton LinkedHashSetAutomaton { val unseenKeys: map = action MAP_CLONE(this.storage); action LOOP_WHILE( - i < this.length, + i < action MAP_SIZE(this.storage), _removeAllElements_loop_indirect(i, c, unseenKeys) ); } @@ -360,20 +379,16 @@ automaton LinkedHashSetAutomaton _checkForComodification(expectedModCount); this.modCount += 1; // If length changed, it means that at least one element was deleted - result = lengthBeforeRemoving != this.length; + result = lengthBeforeRemoving != action MAP_SIZE(this.storage); } @Phantom proc removeAllElements_loop_direct (iter: Iterator): void { val key: Object = action CALL_METHOD(iter, "next", []); - val isKeyExist: boolean = action MAP_HAS_KEY(this.storage, key); - if (isKeyExist) - { + if (action MAP_HAS_KEY(this.storage, key)) action MAP_REMOVE(this.storage, key); - this.length -= 1; - } } @@ -382,13 +397,8 @@ automaton LinkedHashSetAutomaton val key: Object = action MAP_GET_ANY_KEY(unseenKeys); action MAP_REMOVE(unseenKeys, key); - val isCollectionContainsKey: boolean = action CALL_METHOD(c, "contains", [key]); - - if (isCollectionContainsKey) - { + if (action CALL_METHOD(c, "contains", [key])) action MAP_REMOVE(this.storage, key); - this.length -= 1; - } i += 1; } @@ -396,7 +406,7 @@ automaton LinkedHashSetAutomaton fun *.toArray (@target self: LinkedHashSet): array { - val len: int = this.length; + val len: int = action MAP_SIZE(this.storage); result = action ARRAY_NEW("java.lang.Object", len); val expectedModCount: int = this.modCount; val unseenKeys: map = action MAP_CLONE(this.storage); @@ -424,7 +434,7 @@ automaton LinkedHashSetAutomaton { val expectedModCount: int = this.modCount; val aLen: int = action ARRAY_SIZE(a); - val len: int = this.length; + val len: int = action MAP_SIZE(this.storage); val unseenKeys: map = action MAP_CLONE(this.storage); var i: int = 0; @@ -438,8 +448,8 @@ automaton LinkedHashSetAutomaton toArray_loop(i, unseenKeys, result) ); - if (aLen > this.length) - result[this.length] = null; + if (aLen > len) + result[len] = null; _checkForComodification(expectedModCount); } @@ -450,7 +460,7 @@ automaton LinkedHashSetAutomaton if (generator == null) _throwNPE(); - val len: int = this.length; + val len: int = action MAP_SIZE(this.storage); result = action CALL(generator, [0]) as array; val expectedModCount: int = this.modCount; val unseenKeys: map = action MAP_CLONE(this.storage); @@ -504,7 +514,7 @@ automaton LinkedHashSetAutomaton if (c == null) _throwNPE(); - val lengthBeforeAdd: int = this.length; + val lengthBeforeAdd: int = action MAP_SIZE(this.storage); val iter: Iterator = action CALL_METHOD(c, "iterator", []); action LOOP_WHILE( @@ -512,7 +522,7 @@ automaton LinkedHashSetAutomaton _retainAllElements_loop(iter) ); - if (lengthBeforeAdd != this.length) + if (lengthBeforeAdd != action MAP_SIZE(this.storage)) { this.modCount += 1; result = true; @@ -527,13 +537,9 @@ automaton LinkedHashSetAutomaton @Phantom proc _retainAllElements_loop(iter: Iterator): void { val key: Object = action CALL_METHOD(iter, "next", []); - val hasKey: boolean = action MAP_HAS_KEY(this.storage, key); - if (!hasKey) - { + if (action MAP_HAS_KEY(this.storage, key) == false) action MAP_REMOVE(this.storage, key); - this.length -= 1; - } } @@ -542,7 +548,7 @@ automaton LinkedHashSetAutomaton if (filter == null) _throwNPE(); - val lengthBeforeAdd: int = this.length; + val lengthBeforeAdd: int = action MAP_SIZE(this.storage); val expectedModCount: int = this.modCount; var i: int = 0; val unseenKeys: map = action MAP_CLONE(this.storage); @@ -553,7 +559,7 @@ automaton LinkedHashSetAutomaton ); _checkForComodification(expectedModCount); - if (lengthBeforeAdd != this.length) + if (lengthBeforeAdd != action MAP_SIZE(this.storage)) { this.modCount += 1; result = true; @@ -570,13 +576,8 @@ automaton LinkedHashSetAutomaton val key: Object = action MAP_GET_ANY_KEY(unseenKeys); action MAP_REMOVE(unseenKeys, key); - var isDelete: boolean = action CALL(filter, [key]); - - if(isDelete) - { + if(action CALL(filter, [key])) action MAP_REMOVE(this.storage, key); - this.length -= 1; - } i += 1; } @@ -592,7 +593,7 @@ automaton LinkedHashSetAutomaton val unseenKeys: map = action MAP_CLONE(this.storage); action LOOP_WHILE( - i < this.length, + i < action MAP_SIZE(this.storage), forEach_loop(i, unseenKeys, userAction) ); @@ -614,18 +615,14 @@ automaton LinkedHashSetAutomaton // within java.util.Collection fun *.stream (@target self: LinkedHashSet): Stream { - // #todo: use custom stream implementation - result = action SYMBOLIC("java.util.stream.Stream"); - action ASSUME(result != null); + result = _makeStream(/* parallel = */ false); } // within java.util.Collection fun *.parallelStream (@target self: LinkedHashSet): Stream { - // #todo: use custom stream implementation - result = action SYMBOLIC("java.util.stream.Stream"); - action ASSUME(result != null); + result = _makeStream(/* parallel = */ true); } @@ -648,6 +645,38 @@ automaton LinkedHashSetAutomaton // within java.util.AbstractCollection fun *.toString (@target self: LinkedHashSet): String { - result = action OBJECT_TO_STRING(this.storage); + val items: map = this.storage; + var count: int = action MAP_SIZE(items); + + if (count == 0) + { + result = "[]"; + } + else + { + action ASSUME(count > 0); + + result = "["; + + val unseen: map = action MAP_CLONE(items); + action LOOP_WHILE( + count != 0, + toString_loop(unseen, count, result) + ); + + result += "]"; + } + } + + @Phantom proc toString_loop (unseen: map, count: int, result: String): void + { + val key: Object = action MAP_GET_ANY_KEY(unseen); + action MAP_REMOVE(unseen, key); + + result += action OBJECT_TO_STRING(key); + + if (count > 1) + result += ", "; + count -= 1; } } \ No newline at end of file diff --git a/spec/java/util/LinkedList.ListIterator.lsl b/spec/java/util/LinkedList.ListIterator.lsl new file mode 100644 index 00000000..eb83e030 --- /dev/null +++ b/spec/java/util/LinkedList.ListIterator.lsl @@ -0,0 +1,243 @@ +libsl "1.1.0"; + +library std + version "11" + language "Java" + url "https://github.com/openjdk/jdk11/blob/master/src/java.base/share/classes/java/util/LinkedList.java"; + +// imports + +import java/util/LinkedList; + + +// automata + +automaton LinkedList_ListIteratorAutomaton +( + var parent: LinkedList, + var cursor: int, + var expectedModCount: int +) +: LinkedList_ListIterator +{ + // states and shifts + + initstate Initialized; + + shift Initialized -> self by [ + add, + forEachRemaining, + hasNext, + hasPrevious, + next, + nextIndex, + previous, + previousIndex, + remove, + set, + ]; + + + // local variables + + var lastRet: int = -1; + + + // utilities + + @AutoInline @Phantom proc _throwCME (): void + { + action THROW_NEW("java.util.ConcurrentModificationException", []); + } + + + proc _checkForComodification (): void + { + val modCount: int = LinkedListAutomaton(this.parent).modCount; + if (modCount != this.expectedModCount) + _throwCME(); + } + + + // methods + + fun *.hasPrevious (@target self: LinkedList_ListIterator): boolean + { + result = this.cursor != 0; + } + + + fun *.nextIndex (@target self: LinkedList_ListIterator): int + { + result = this.cursor; + } + + + fun *.previousIndex (@target self: LinkedList_ListIterator): int + { + result = this.cursor - 1; + } + + + fun *.hasNext (@target self: LinkedList_ListIterator): boolean + { + // relax state/error discovery process + action ASSUME(this.parent != null); + + result = this.cursor != action LIST_SIZE(LinkedListAutomaton(this.parent).storage); + } + + + fun *.next (@target self: LinkedList_ListIterator): Object + { + // relax state/error discovery process + action ASSUME(this.parent != null); + + _checkForComodification(); + + val parentStorage: list = LinkedListAutomaton(this.parent).storage; + + val i: int = this.cursor; + if (i >= action LIST_SIZE(parentStorage)) + action THROW_NEW("java.util.NoSuchElementException", []); + + this.cursor = i + 1; + this.lastRet = i; + + result = action LIST_GET(parentStorage, i); + } + + + fun *.previous (@target self: LinkedList_ListIterator): Object + { + // relax state/error discovery process + action ASSUME(this.parent != null); + + _checkForComodification(); + + val parentStorage: list = LinkedListAutomaton(this.parent).storage; + + val i: int = this.cursor - 1; + if (i < 0) + action THROW_NEW("java.util.NoSuchElementException", []); + + // iterrator validity check + if (i >= action LIST_SIZE(parentStorage)) + _throwCME(); + + this.cursor = i; + this.lastRet = i; + + result = action LIST_GET(parentStorage, i); + } + + + fun *.remove (@target self: LinkedList_ListIterator): void + { + // relax state/error discovery process + action ASSUME(this.parent != null); + + if (this.lastRet < 0) + action THROW_NEW("java.lang.IllegalStateException", []); + + _checkForComodification(); + + val pStorage: list = LinkedListAutomaton(this.parent).storage; + if (this.lastRet >= action LIST_SIZE(pStorage)) + { + _throwCME(); + } + else + { + LinkedListAutomaton(this.parent).modCount += 1; + + action LIST_REMOVE(pStorage, this.lastRet); + } + + this.cursor = this.lastRet; + this.lastRet = -1; + this.expectedModCount = LinkedListAutomaton(this.parent).modCount; + } + + + fun *.set (@target self: LinkedList_ListIterator, e: Object): void + { + // relax state/error discovery process + action ASSUME(this.parent != null); + + if (this.lastRet < 0) + action THROW_NEW("java.lang.IllegalStateException", []); + + _checkForComodification(); + + val pStorage: list = LinkedListAutomaton(this.parent).storage; + if (this.lastRet >= action LIST_SIZE(pStorage)) + _throwCME(); + else + action LIST_SET(pStorage, this.lastRet, e); + } + + + fun *.add (@target self: LinkedList_ListIterator, e: Object): void + { + // relax state/error discovery process + action ASSUME(this.parent != null); + + _checkForComodification(); + + val i: int = this.cursor; + + val pStorage: list = LinkedListAutomaton(this.parent).storage; + if (this.lastRet > action LIST_SIZE(pStorage)) + { + _throwCME(); + } + else + { + LinkedListAutomaton(this.parent).modCount += 1; + + action LIST_INSERT_AT(pStorage, i, e); + } + + this.cursor = i + 1; + this.lastRet = -1; + this.expectedModCount = LinkedListAutomaton(this.parent).modCount; + } + + + fun *.forEachRemaining (@target self: LinkedList_ListIterator, userAction: Consumer): void + { + // relax state/error discovery process + action ASSUME(this.parent != null); + + if (userAction == null) + action THROW_NEW("java.lang.NullPointerException", []); + + var i: int = this.cursor; + val es: list = LinkedListAutomaton(this.parent).storage; + val size: int = action LIST_SIZE(es); + + if (i < size) + { + // using this exact loop form here due to coplex termination expression + action LOOP_WHILE( + i < size && LinkedListAutomaton(this.parent).modCount == this.expectedModCount, + forEachRemaining_loop(userAction, es, i) + ); + + // JDK NOTE: "update once at end to reduce heap write traffic" + this.cursor = i; + this.lastRet = i - 1; + _checkForComodification(); + } + } + + @Phantom proc forEachRemaining_loop (userAction: Consumer, es: list, i: int): void + { + val item: Object = action LIST_GET(es, i); + action CALL(userAction, [item]); + + i += 1; + } + +} diff --git a/spec/java/util/LinkedList.Spliterator.lsl b/spec/java/util/LinkedList.Spliterator.lsl new file mode 100644 index 00000000..5f102715 --- /dev/null +++ b/spec/java/util/LinkedList.Spliterator.lsl @@ -0,0 +1,215 @@ +libsl "1.1.0"; + +library std + version "11" + language "Java" + url "https://github.com/openjdk/jdk11/blob/master/src/java.base/share/classes/java/util/LinkedList.java"; + +// imports + +import java/util/LinkedList; + + +// automata + +automaton LinkedList_SpliteratorAutomaton +( + var parent: LinkedList +) +: LinkedList_Spliterator +{ + // states and shifts + + initstate Allocated; + state Initialized; + + shift Allocated -> Initialized by [ + // constructors + ``, + ]; + + shift Initialized -> self by [ + // instance methods + characteristics, + estimateSize, + forEachRemaining, + getComparator, + getExactSizeIfKnown, + hasCharacteristics, + tryAdvance, + trySplit, + ]; + + // internal variables + + var index: int = 0; + var fence: int = -1; + var expectedModCount: int = 0; + + + // utilities + + @AutoInline @Phantom proc _throwNPE (): void + { + action THROW_NEW("java.lang.NullPointerException", []); + } + + + @AutoInline @Phantom proc _throwCME (): void + { + action THROW_NEW("java.util.ConcurrentModificationException", []); + } + + + proc _getFence (): int + { + // JDK comment: initialize fence to size on first use + if (this.fence == -1) + { + action ASSUME(this.parent != null); + this.expectedModCount = LinkedListAutomaton(this.parent).modCount; + this.fence = action LIST_SIZE(LinkedListAutomaton(this.parent).storage); + } + + result = this.fence; + } + + + // constructors + + @private constructor *.`` (@target self: LinkedList_Spliterator, + _this: LinkedList, + origin: int, fence: int, expectedModCount: int) + { + // #problem: translator cannot generate and refer to private and/or inner classes, so this is effectively useless + action NOT_IMPLEMENTED("inaccessible constructor"); + } + + + // static methods + + // methods + + fun *.characteristics (@target self: LinkedList_Spliterator): int + { + result = SPLITERATOR_ORDERED | SPLITERATOR_SIZED | SPLITERATOR_SUBSIZED; + } + + + fun *.estimateSize (@target self: LinkedList_Spliterator): long + { + result = _getFence() - this.index; + } + + + fun *.forEachRemaining (@target self: LinkedList_Spliterator, _action: Consumer): void + { + if (_action == null) + _throwNPE(); + + action ASSUME(this.parent != null); + val a: list = LinkedListAutomaton(this.parent).storage; + if (a == null) + _throwCME(); + + var hi: int = this.fence; + var mc: int = this.expectedModCount; + if (hi == -1) + { + hi = action LIST_SIZE(a); + mc = LinkedListAutomaton(this.parent).modCount; + } + + var i: int = this.index; + this.index = hi; + if (i < 0 || hi > action LIST_SIZE(a)) + _throwCME(); + + action LOOP_FOR( + i, i, hi, +1, + forEachRemaining_loop(i, a, _action) + ); + + if (mc != LinkedListAutomaton(this.parent).modCount) + _throwCME(); + } + + @Phantom proc forEachRemaining_loop (i: int, a: list, _action: Consumer): void + { + val item: Object = action LIST_GET(a, i); + action CALL(_action, [item]); + } + + + // within java.util.Spliterator + @Phantom fun *.getComparator (@target self: LinkedList_Spliterator): Comparator + { + // NOTE: using the original method + } + + + // within java.util.Spliterator + fun *.getExactSizeIfKnown (@target self: LinkedList_Spliterator): long + { + result = _getFence() - this.index; + } + + + // within java.util.Spliterator + @Phantom fun *.hasCharacteristics (@target self: LinkedList_Spliterator, characteristics: int): boolean + { + // NOTE: using the original method + } + + + fun *.tryAdvance (@target self: LinkedList_Spliterator, _action: Consumer): boolean + { + if (_action == null) + _throwNPE(); + + val hi: int = _getFence(); + val i: int = this.index; + + if (i < hi) + { + action ASSUME(this.parent != null); + + this.index = i + 1; + + val parentStorage: list = LinkedListAutomaton(this.parent).storage; + val item: Object = action LIST_GET(parentStorage, i); + action CALL(_action, [item]); + + if (LinkedListAutomaton(this.parent).modCount != this.expectedModCount) + _throwCME(); + + result = true; + } + else + { + result = false; + } + } + + + fun *.trySplit (@target self: LinkedList_Spliterator): Spliterator + { + val hi: int = _getFence(); + val lo: int = this.index; + val mid: int = (lo + hi) >>> 1; + + // JDK comment: divide range in half unless too small + if (lo >= mid) + result = null; + else + result = new LinkedList_SpliteratorAutomaton(state = Initialized, + parent = this.parent, + index = lo, + fence = mid, + expectedModCount = this.expectedModCount, + ); + + this.index = mid; + } + +} diff --git a/spec/java/util/LinkedList.SubList.ListIterator.lsl b/spec/java/util/LinkedList.SubList.ListIterator.lsl new file mode 100644 index 00000000..3a70d9d0 --- /dev/null +++ b/spec/java/util/LinkedList.SubList.ListIterator.lsl @@ -0,0 +1,246 @@ +libsl "1.1.0"; + +library std + version "11" + language "Java" + url "https://github.com/openjdk/jdk11/blob/master/src/java.base/share/classes/java/util/LinkedList.java"; + +// imports + +import java/util/LinkedList; + + +// automata + +automaton LinkedList_SubList_ListIteratorAutomaton +( + var root: LinkedList, + var sublist: LinkedList_SubList, + var cursor: int, + var expectedModCount: int, + var offset: int, + var size: int, +) +: LinkedList_SubList_ListIterator +{ + // states and shifts + + initstate Initialized; + + shift Initialized -> self by [ + add, + forEachRemaining, + hasNext, + hasPrevious, + next, + nextIndex, + previous, + previousIndex, + remove, + set, + ]; + + + // local variables + + var lastRet: int = -1; + + + // utilities + + @AutoInline @Phantom proc _throwCME (): void + { + action THROW_NEW("java.util.ConcurrentModificationException", []); + } + + + proc _checkForComodification (): void + { + val modCount: int = LinkedListAutomaton(this.root).modCount; + if (modCount != this.expectedModCount) + _throwCME(); + } + + + // methods + + fun *.hasPrevious (@target self: LinkedList_SubList_ListIterator): boolean + { + result = this.cursor != 0; + } + + + fun *.nextIndex (@target self: LinkedList_SubList_ListIterator): int + { + result = this.cursor; + } + + + fun *.previousIndex (@target self: LinkedList_SubList_ListIterator): int + { + result = this.cursor - 1; + } + + + fun *.hasNext (@target self: LinkedList_SubList_ListIterator): boolean + { + result = this.cursor != this.size; + } + + + fun *.next (@target self: LinkedList_SubList_ListIterator): Object + { + // relax state/error discovery process + action ASSUME(this.root != null); + + _checkForComodification(); + + val rootStorage: list = LinkedListAutomaton(this.root).storage; + val i: int = this.offset + this.cursor; + // iterrator validity check + if (i >= action LIST_SIZE(rootStorage)) + action THROW_NEW("java.util.NoSuchElementException", []); + + this.lastRet = this.cursor; + this.cursor += 1; + + result = action LIST_GET(rootStorage, i); + } + + + fun *.previous (@target self: LinkedList_SubList_ListIterator): Object + { + // relax state/error discovery process + action ASSUME(this.root != null); + + _checkForComodification(); + + val i: int = this.offset + this.cursor - 1; + if (i < this.offset) + action THROW_NEW("java.util.NoSuchElementException", []); + + // iterrator validity check + val rootStorage: list = LinkedListAutomaton(this.root).storage; + if (i >= action LIST_SIZE(rootStorage)) + _throwCME(); + + this.cursor -= 1; + this.lastRet = this.cursor; + + result = action LIST_GET(rootStorage, i); + } + + + fun *.remove (@target self: LinkedList_SubList_ListIterator): void + { + // relax state/error discovery process + action ASSUME(this.root != null); + + if (this.lastRet < 0) + action THROW_NEW("java.lang.IllegalStateException", []); + + _checkForComodification(); + + if (this.lastRet >= this.size) + { + _throwCME(); + } + else + { + LinkedListAutomaton(this.root)._unlinkAny(this.offset + this.lastRet); + + LinkedList_SubListAutomaton(this.sublist)._updateSizeAndModCount(-1); + this.expectedModCount = LinkedListAutomaton(this.root).modCount; + this.size -= 1; + } + + this.cursor = this.lastRet; + this.lastRet = -1; + } + + + fun *.set (@target self: LinkedList_SubList_ListIterator, e: Object): void + { + // relax state/error discovery process + action ASSUME(this.root != null); + + if (this.lastRet < 0) + action THROW_NEW("java.lang.IllegalStateException", []); + + _checkForComodification(); + + val rootStorage: list = LinkedListAutomaton(this.root).storage; + + val index: int = this.offset + this.lastRet; + if (index >= action LIST_SIZE(rootStorage)) + _throwCME(); + else + action LIST_SET(rootStorage, index, e); + } + + + fun *.add (@target self: LinkedList_SubList_ListIterator, e: Object): void + { + // relax state/error discovery process + action ASSUME(this.root != null); + + _checkForComodification(); + + val i: int = this.offset + this.cursor; + + if (this.offset + this.lastRet > action LIST_SIZE(LinkedListAutomaton(this.root).storage)) + { + _throwCME(); + } + else + { + LinkedListAutomaton(this.root)._linkAny(i, e); + + LinkedList_SubListAutomaton(this.sublist)._updateSizeAndModCount(+1); + this.expectedModCount = LinkedListAutomaton(this.root).modCount; + this.size += 1; + } + + this.cursor += 1; + this.lastRet = -1; + } + + + fun *.forEachRemaining (@target self: LinkedList_SubList_ListIterator, userAction: Consumer): void + { + // relax state/error discovery process + action ASSUME(this.root != null); + + if (userAction == null) + action THROW_NEW("java.lang.NullPointerException", []); + + var i: int = this.cursor; + if (i < this.size) + { + i += this.offset; + val es: list = LinkedListAutomaton(this.root).storage; + + if (i >= action LIST_SIZE(es)) + _throwCME(); + + val end: int = this.offset + this.size; + + action LOOP_FOR( + i, i, end, +1, + forEachRemaining_loop(userAction, es, i) + ); + + // JDK NOTE: "update once at end to reduce heap write traffic" + this.cursor = i - this.offset; + this.lastRet = this.cursor - 1; + _checkForComodification(); + } + } + + @Phantom proc forEachRemaining_loop (userAction: Consumer, es: list, i: int): void + { + val item: Object = action LIST_GET(es, i); + action CALL(userAction, [item]); + } + +} diff --git a/spec/java/util/LinkedList.SubList.Spliterator.lsl b/spec/java/util/LinkedList.SubList.Spliterator.lsl new file mode 100644 index 00000000..1065da67 --- /dev/null +++ b/spec/java/util/LinkedList.SubList.Spliterator.lsl @@ -0,0 +1,204 @@ +libsl "1.1.0"; + +library std + version "11" + language "Java" + url "https://github.com/openjdk/jdk11/blob/master/src/java.base/share/classes/java/util/LinkedList.java"; + +// imports + +import java/util/LinkedList; + + +// automata + +automaton LinkedList_SubList_SpliteratorAutomaton +( + var root: LinkedList, + var parent: LinkedList_SubList, +) +: LinkedList_SubList_Spliterator +{ + // states and shifts + + initstate Initialized; + + shift Initialized -> self by [ + // instance methods + characteristics, + estimateSize, + forEachRemaining, + getComparator, + getExactSizeIfKnown, + hasCharacteristics, + tryAdvance, + trySplit, + ]; + + // internal variables + + var index: int = 0; + var fence: int = -1; + var expectedModCount: int = 0; + + + // utilities + + @AutoInline @Phantom proc _throwNPE (): void + { + action THROW_NEW("java.lang.NullPointerException", []); + } + + + @AutoInline @Phantom proc _throwCME (): void + { + action THROW_NEW("java.util.ConcurrentModificationException", []); + } + + + proc _getFence (): int + { + // JDK comment: initialize fence to size on first use + if (this.fence == -1) + { + action ASSUME(this.parent != null); + this.expectedModCount = LinkedList_SubListAutomaton(this.parent).modCount; + this.fence = LinkedList_SubListAutomaton(this.parent).length; + } + + result = this.fence; + } + + + // constructors + + // static methods + + // methods + + fun *.characteristics (@target self: LinkedList_SubList_Spliterator): int + { + result = SPLITERATOR_ORDERED | SPLITERATOR_SIZED | SPLITERATOR_SUBSIZED; + } + + + fun *.estimateSize (@target self: LinkedList_SubList_Spliterator): long + { + result = _getFence() - this.index; + } + + + fun *.forEachRemaining (@target self: LinkedList_SubList_Spliterator, _action: Consumer): void + { + if (_action == null) + _throwNPE(); + + action ASSUME(this.root != null); + action ASSUME(this.parent != null); + + val a: list = LinkedListAutomaton(this.root).storage; + if (a == null) + _throwCME(); + + var hi: int = this.fence; + var mc: int = this.expectedModCount; + if (hi == -1) + { + hi = LinkedList_SubListAutomaton(this.parent).length; + mc = LinkedList_SubListAutomaton(this.parent).modCount; + } + + var i: int = this.index; + this.index = hi; + if (i < 0 || hi > LinkedList_SubListAutomaton(this.parent).length) + _throwCME(); + + action LOOP_FOR( + i, i, hi, +1, + forEachRemaining_loop(i, a, _action) + ); + + if (mc != LinkedList_SubListAutomaton(this.parent).modCount) + _throwCME(); + } + + @Phantom proc forEachRemaining_loop (i: int, a: list, _action: Consumer): void + { + val item: Object = action LIST_GET(a, i); + action CALL(_action, [item]); + } + + + // within java.util.Spliterator + @Phantom fun *.getComparator (@target self: LinkedList_SubList_Spliterator): Comparator + { + // NOTE: using the original method + } + + + // within java.util.Spliterator + fun *.getExactSizeIfKnown (@target self: LinkedList_SubList_Spliterator): long + { + result = _getFence() - this.index; + } + + + // within java.util.Spliterator + @Phantom fun *.hasCharacteristics (@target self: LinkedList_SubList_Spliterator, characteristics: int): boolean + { + // NOTE: using the original method + } + + + fun *.tryAdvance (@target self: LinkedList_SubList_Spliterator, _action: Consumer): boolean + { + if (_action == null) + _throwNPE(); + + val hi: int = _getFence(); + val i: int = this.index; + + if (i < hi) + { + action ASSUME(this.root != null); + + this.index = i + 1; + + val rootStorage: list = LinkedListAutomaton(this.root).storage; + val item: Object = action LIST_GET(rootStorage, i); + action CALL(_action, [item]); + + if (LinkedListAutomaton(this.root).modCount != this.expectedModCount) + _throwCME(); + + result = true; + } + else + { + result = false; + } + } + + + fun *.trySplit (@target self: LinkedList_SubList_Spliterator): Spliterator + { + val hi: int = _getFence(); + val lo: int = this.index; + val mid: int = (lo + hi) >>> 1; + + // JDK comment: divide range in half unless too small + if (lo >= mid) + result = null; + else + result = new LinkedList_SubList_SpliteratorAutomaton(state = Initialized, + root = this.root, + parent = this.parent, + index = lo, + fence = mid, + expectedModCount = this.expectedModCount, + ); + + this.index = mid; + } + +} diff --git a/spec/java/util/LinkedList.SubList.lsl b/spec/java/util/LinkedList.SubList.lsl new file mode 100644 index 00000000..4ba9feb1 --- /dev/null +++ b/spec/java/util/LinkedList.SubList.lsl @@ -0,0 +1,785 @@ +libsl "1.1.0"; + +library std + version "11" + language "Java" + url "https://github.com/openjdk/jdk11/blob/master/src/java.base/share/classes/java/util/LinkedList.java"; + +// imports + +import java/util/LinkedList; + + +// automata + +automaton LinkedList_SubListAutomaton +( + var root: LinkedList, + var parentList: LinkedList_SubList, + var offset: int, + var length: int, + var modCount: int, +) +: LinkedList_SubList +{ + // states and shifts + + initstate Allocated; + state Initialized; + + shift Allocated -> Initialized by [ + // constructors + `` (LinkedList_SubList, LinkedList, int, int), + `` (LinkedList_SubList, LinkedList_SubList, int, int), + ]; + + shift Initialized -> self by [ + // instance methods + add (LinkedList_SubList, Object), + add (LinkedList_SubList, int, Object), + addAll (LinkedList_SubList, Collection), + addAll (LinkedList_SubList, int, Collection), + clear, + contains, + containsAll, + equals, + forEach, + get, + hashCode, + indexOf, + isEmpty, + iterator, + lastIndexOf, + listIterator (LinkedList_SubList), + listIterator (LinkedList_SubList, int), + parallelStream, + remove (LinkedList_SubList, Object), + remove (LinkedList_SubList, int), + removeAll, + removeIf, + replaceAll, + retainAll, + set, + size, + sort, + spliterator, + stream, + subList, + toArray (LinkedList_SubList), + toArray (LinkedList_SubList, IntFunction), + toArray (LinkedList_SubList, array), + toString, + ]; + + // internal variables + + // utilities + + @AutoInline @Phantom proc _throwNPE (): void + { + action THROW_NEW("java.lang.NullPointerException", []); + } + + + @AutoInline @Phantom proc _checkForComodification(): void + { + LinkedListAutomaton(this.root)._checkForComodification(this.modCount); + } + + + proc _addAllElements (index: int, c: Collection): boolean + { + action ASSUME(this.root != null); + + val effectiveIndex: int = this.offset + index; + LinkedListAutomaton(this.root)._checkPositionIndex(effectiveIndex); + + val collectionSize: int = action CALL_METHOD(c, "size", []); + if (collectionSize == 0) + { + result = false; + } + else + { + result = true; + + _checkForComodification(); + LinkedListAutomaton(this.root)._addAllElements(effectiveIndex, c); + _updateSizeAndModCount(collectionSize); + } + } + + + @KeepVisible proc _updateSizeAndModCount (sizeChange: int): void + { + action ASSUME(this.root != null); + + // update self first + this.length += sizeChange; + this.modCount = LinkedListAutomaton(this.root).modCount; + + // then propagate changes up the chain + var aList: LinkedList_SubList = this.parentList; + action LOOP_WHILE( + aList != null, + _updateSizeAndModCount_loop(aList, sizeChange) + ); + } + + @Phantom proc _updateSizeAndModCount_loop (aList: LinkedList_SubList, sizeChange: int): void + { + LinkedList_SubListAutomaton(aList).length += sizeChange; + LinkedList_SubListAutomaton(aList).modCount = this.modCount; + + aList = LinkedList_SubListAutomaton(aList).parentList; + } + + + proc _indexOfElement (o: Object): int + { + action ASSUME(this.root != null); + + _checkForComodification(); + val parentStorage: list = LinkedListAutomaton(this.root).storage; + + val index: int = action LIST_FIND(parentStorage, o, this.offset, this.offset + this.length); + if (index != -1) + result = index - this.offset; + else + result = -1; + } + + + proc _makeStream (parallel: boolean): Stream + { + // #todo: use custom stream implementation + result = action SYMBOLIC("java.util.stream.Stream"); + action ASSUME(result != null); + action ASSUME(action CALL_METHOD(result, "isParallel", []) == parallel); + } + + + proc _batchRemove (c: Collection, complement: boolean): boolean + { + action ASSUME(this.root != null); + _checkForComodification(); + + if (this.length != 0) + { + val oldRootLength: int = action LIST_SIZE(LinkedListAutomaton(this.root).storage); + + result = LinkedListAutomaton(this.root)._batchRemove(c, complement, this.offset, this.offset + this.length); + if (result) + { + val newRootLength: int = action LIST_SIZE(LinkedListAutomaton(this.root).storage); + _updateSizeAndModCount(newRootLength - oldRootLength); + } + } + else + { + result = false; + } + } + + + // constructors + + constructor *.`` (@target self: LinkedList_SubList, root: LinkedList, fromIndex: int, toIndex: int) + { + // #problem: this constructor is useless + action NOT_IMPLEMENTED("inaccessible constructor"); + } + + + @private constructor *.`` (@target self: LinkedList_SubList, parent: LinkedList_SubList, fromIndex: int, toIndex: int) + { + // #problem: this constructor is useless + action NOT_IMPLEMENTED("inaccessible constructor"); + } + + + // static methods + + // methods + + // within java.util.AbstractList + fun *.add (@target self: LinkedList_SubList, e: Object): boolean + { + action ASSUME(this.root != null); + + _checkForComodification(); + + val effectiveIndex: int = this.offset + this.length; + LinkedListAutomaton(this.root)._linkAny(effectiveIndex, e); + + _updateSizeAndModCount(+1); + } + + + fun *.add (@target self: LinkedList_SubList, index: int, element: Object): void + { + action ASSUME(this.root != null); + + _checkForComodification(); + + val effectiveIndex: int = this.offset + index; + LinkedListAutomaton(this.root)._linkAny(effectiveIndex, element); + + _updateSizeAndModCount(+1); + } + + + fun *.addAll (@target self: LinkedList_SubList, c: Collection): boolean + { + _addAllElements(this.length, c); + } + + + fun *.addAll (@target self: LinkedList_SubList, index: int, c: Collection): boolean + { + _addAllElements(index, c); + } + + + // within java.util.AbstractList + fun *.clear (@target self: LinkedList_SubList): void + { + action ASSUME(this.root != null); + _checkForComodification(); + + val size: int = this.length; + if (size != 0) + { + action ASSUME(size > 0); + val end: int = this.offset - 1; + val start: int = end + size; + + val rootStorage: list = LinkedListAutomaton(this.root).storage; + + var i: int = 0; + action LOOP_FOR( + i, start, end, -1, + clear_loop(i, rootStorage) + ); + + LinkedListAutomaton(this.root).modCount += 1; + + _updateSizeAndModCount(-size); + } + } + + @Phantom proc clear_loop (i: int, rootStorage: list): void + { + action LIST_REMOVE(rootStorage, i); + } + + + fun *.contains (@target self: LinkedList_SubList, o: Object): boolean + { + result = _indexOfElement(o) != -1; + } + + + // within java.util.AbstractCollection + fun *.containsAll (@target self: LinkedList_SubList, c: Collection): boolean + { + result = true; + + if (!action CALL_METHOD(c, "isEmpty", [])) + { + action ASSUME(this.root != null); + + val rootStorage: list = LinkedListAutomaton(this.root).storage; + val end: int = this.offset + this.length; + + if (c has LinkedList_SubListAutomaton) + { + val otherRoot: LinkedList = LinkedList_SubListAutomaton(c).root; + action ASSUME(otherRoot != null); + + val otherStorage: list = LinkedListAutomaton(otherRoot).storage; + val otherOffset: int = LinkedList_SubListAutomaton(c).offset; + val otherEnd: int = otherOffset + LinkedList_SubListAutomaton(c).length; + + action ASSUME(otherStorage != null); + action ASSUME(otherOffset >= 0); + action ASSUME(otherEnd >= 0); + + var i: int = otherOffset; + action LOOP_WHILE( + result && i < otherEnd, + containsAll_loop_optimized(rootStorage, end, otherStorage, i, result) + ); + } + else + { + val iter: Iterator = action CALL_METHOD(c, "iterator", []); + action LOOP_WHILE( + result && action CALL_METHOD(iter, "hasNext", []), + containsAll_loop_regular(rootStorage, end, iter, result) + ); + } + } + } + + @Phantom proc containsAll_loop_optimized (rootStorage: list, end: int, otherStorage: list, i: int, result: boolean): void + { + val item: Object = action LIST_GET(otherStorage, i); + result = action LIST_FIND(rootStorage, item, this.offset, end) != -1; + + i += 1; + } + + @Phantom proc containsAll_loop_regular (rootStorage: list, end: int, iter: Iterator, result: boolean): void + { + val item: Object = action CALL_METHOD(iter, "next", []); + result = action LIST_FIND(rootStorage, item, this.offset, end) != -1; + } + + + fun *.equals (@target self: LinkedList_SubList, o: Object): boolean + { + if (o == self) + { + result = true; + } + else + { + result = o has LinkedList_SubListAutomaton; + if (result) + { + action ASSUME(this.root != null); + + val otherLength: int = LinkedList_SubListAutomaton(o).length; + action ASSUME(otherLength >= 0); + + result = this.length == otherLength; + if (result) + { + result = LinkedListAutomaton(this.root)._equalsRange(o as List, this.offset, this.offset + this.length); + _checkForComodification(); + } + } + } + } + + + // within java.lang.Iterable + fun *.forEach (@target self: LinkedList_SubList, _action: Consumer): void + { + if (this.length != 0) + { + action ASSUME(this.length > 0); + action ASSUME(this.root != null); + + val rootStorage: list = LinkedListAutomaton(this.root).storage; + val expectedModCount: int = LinkedListAutomaton(this.root).modCount; + + this.modCount = expectedModCount; + + var i: int = this.offset; + val end: int = this.offset + this.length; + action LOOP_WHILE( + i < end && LinkedListAutomaton(this.root).modCount == expectedModCount, + forEach_loop(i, rootStorage, _action) + ); + + LinkedListAutomaton(this.root)._checkForComodification(expectedModCount); + } + } + + @Phantom proc forEach_loop (i: int, rootStorage: list, _action: Consumer): void + { + val item: Object = action LIST_GET(rootStorage, i); + action CALL(_action, [item]); + + i += 1; + } + + + fun *.get (@target self: LinkedList_SubList, index: int): Object + { + action ASSUME(this.root != null); + + LinkedListAutomaton(this.root)._checkElementIndex(index, this.length); + _checkForComodification(); + + val effectiveIndex: int = this.offset + index; + result = action LIST_GET(LinkedListAutomaton(this.root).storage, effectiveIndex); + } + + + fun *.hashCode (@target self: LinkedList_SubList): int + { + result = 1; + + if (this.length != 0) + { + action ASSUME(this.length > 0); + action ASSUME(this.root != null); + val rootStorage: list = LinkedListAutomaton(this.root).storage; + + var i: int = this.offset; + val end: int = this.offset + this.length; + action LOOP_FOR( + i, i, end, +1, + hashCode_loop(i, rootStorage, result) + ); + + _checkForComodification(); + } + } + + @Phantom proc hashCode_loop (i: int, rootStorage: list, result: int): void + { + val item: Object = action LIST_GET(rootStorage, i); + result = 31 * result + action OBJECT_HASH_CODE(item); + } + + + fun *.indexOf (@target self: LinkedList_SubList, o: Object): int + { + result = _indexOfElement(o); + } + + + // within java.util.AbstractCollection + fun *.isEmpty (@target self: LinkedList_SubList): boolean + { + result = this.length == 0; + } + + + fun *.iterator (@target self: LinkedList_SubList): Iterator + { + result = new LinkedList_SubList_ListIteratorAutomaton(state = Initialized, + root = this.root, + sublist = self, + cursor = 0, + expectedModCount = this.modCount, + offset = this.offset, + size = this.length, + ); + } + + + fun *.lastIndexOf (@target self: LinkedList_SubList, o: Object): int + { + action ASSUME(this.root != null); + _checkForComodification(); + + if (this.length == 0) + { + result = -1; + } + else + { + action ASSUME(this.length > 0); + + val end: int = this.offset + this.length; + val rootStorage: list = LinkedListAutomaton(this.root).storage; + + result = action LIST_FIND(rootStorage, o, this.offset, end); + if (result != -1) + { + // there should be no elements to the right of the previously found position + val nextIndex: int = result + 1; + if (nextIndex < end) + { + val rightIndex: int = action LIST_FIND(rootStorage, o, nextIndex, end); + action ASSUME(rightIndex == -1); + } + + result -= this.offset; + } + } + } + + + // within java.util.AbstractList + fun *.listIterator (@target self: LinkedList_SubList): ListIterator + { + result = new LinkedList_SubList_ListIteratorAutomaton(state = Initialized, + root = this.root, + sublist = self, + cursor = 0, + expectedModCount = this.modCount, + offset = this.offset, + size = this.length, + ); + } + + + fun *.listIterator (@target self: LinkedList_SubList, index: int): ListIterator + { + result = new LinkedList_SubList_ListIteratorAutomaton(state = Initialized, + root = this.root, + sublist = self, + cursor = index, + expectedModCount = this.modCount, + offset = this.offset, + size = this.length, + ); + } + + + // within java.util.Collection + fun *.parallelStream (@target self: LinkedList_SubList): Stream + { + result = _makeStream(/* parallel = */true); + } + + + // within java.util.AbstractCollection + fun *.remove (@target self: LinkedList_SubList, o: Object): boolean + { + action ASSUME(this.root != null); + + val end: int = this.offset + this.length; + val rootStorage: list = LinkedListAutomaton(this.root).storage; + + val index: int = action LIST_FIND(rootStorage, o, this.offset, end); + result = index != -1; + + if (result) + { + _checkForComodification(); + LinkedListAutomaton(this.root)._unlinkAny(index); + + _updateSizeAndModCount(-1); + } + } + + + fun *.remove (@target self: LinkedList_SubList, index: int): Object + { + action ASSUME(this.root != null); + + LinkedListAutomaton(this.root)._checkElementIndex(index, this.length); + _checkForComodification(); + + val effectiveIndex: int = this.offset + index; + result = LinkedListAutomaton(this.root)._unlinkAny(effectiveIndex); + + _updateSizeAndModCount(-1); + } + + + fun *.removeAll (@target self: LinkedList_SubList, c: Collection): boolean + { + _batchRemove(c, false); + } + + + fun *.removeIf (@target self: LinkedList_SubList, filter: Predicate): boolean + { + action ASSUME(this.root != null); + _checkForComodification(); + + val size: int = this.length; + if (size != 0) + { + val oldRootLength: int = action LIST_SIZE(LinkedListAutomaton(this.root).storage); + + result = LinkedListAutomaton(this.root)._removeIf(filter, this.offset, this.offset + this.length); + if (result) + { + val newRootLength: int = action LIST_SIZE(LinkedListAutomaton(this.root).storage); + _updateSizeAndModCount(newRootLength - oldRootLength); + } + } + else + { + result = false; + } + } + + + fun *.replaceAll (@target self: LinkedList_SubList, operator: UnaryOperator): void + { + action ASSUME(this.root != null); + LinkedListAutomaton(this.root)._replaceAllRange(operator, this.offset, this.offset + this.length); + } + + + fun *.retainAll (@target self: LinkedList_SubList, c: Collection): boolean + { + _batchRemove(c, true); + } + + + fun *.set (@target self: LinkedList_SubList, index: int, element: Object): Object + { + action ASSUME(this.root != null); + + LinkedListAutomaton(this.root)._checkElementIndex(index, this.length); + _checkForComodification(); + + val parentStorage: list = LinkedListAutomaton(this.root).storage; + val effectiveIndex: int = this.offset + index; + result = action LIST_GET(parentStorage, effectiveIndex); + action LIST_SET(parentStorage, effectiveIndex, element); + } + + + fun *.size (@target self: LinkedList_SubList): int + { + action ASSUME(this.root != null); + + _checkForComodification(); + result = this.length; + } + + + // within java.util.List + fun *.sort (@target self: LinkedList_SubList, c: Comparator): void + { + action ASSUME(this.root != null); + LinkedListAutomaton(this.root)._do_sort(this.offset, this.offset + this.length, c); + this.modCount = LinkedListAutomaton(this.root).modCount; + } + + + fun *.spliterator (@target self: LinkedList_SubList): Spliterator + { + result = new LinkedList_SubList_SpliteratorAutomaton(state = Initialized, + root = this.root, + parent = self, + ); + } + + + // within java.util.Collection + fun *.stream (@target self: LinkedList_SubList): Stream + { + result = _makeStream(/* parallel = */false); + } + + + fun *.subList (@target self: LinkedList_SubList, fromIndex: int, toIndex: int): List + { + action ASSUME(this.root != null); + + LinkedListAutomaton(this.root)._subListRangeCheck(fromIndex, toIndex, this.length); + + result = new LinkedList_SubListAutomaton(state = Initialized, + root = this.root, + parentList = self, + offset = this.offset + fromIndex, + length = toIndex - fromIndex, + modCount = this.modCount, + ); + } + + + fun *.toArray (@target self: LinkedList_SubList): array + { + action ASSUME(this.root != null); + _checkForComodification(); + + result = action ARRAY_NEW("java.lang.Object", this.length); + + val rootStorage: list = LinkedListAutomaton(this.root).storage; + val end: int = this.offset + this.length; + var i: int = 0; + var j: int = 0; + action LOOP_FOR( + i, this.offset, end, +1, + toArray_loop(i, j, result, rootStorage) + ); + } + + @Phantom proc toArray_loop (i: int, j: int, result: array, rootStorage: list): void + { + result[j] = action LIST_GET(rootStorage, i); + j += 1; + } + + + // within java.util.Collection + fun *.toArray (@target self: LinkedList_SubList, generator: IntFunction): array + { + // acting just like JDK + val a: array = action CALL(generator, [0]) as array; + val aSize: int = action ARRAY_SIZE(a); + + action ASSUME(this.root != null); + _checkForComodification(); + + result = action ARRAY_NEW("java.lang.Object", this.length); + + val rootStorage: list = LinkedListAutomaton(this.root).storage; + val end: int = this.offset + this.length; + var i: int = 0; + var j: int = 0; + action LOOP_FOR( + i, this.offset, end, +1, + toArray_loop(i, j, result, rootStorage) + ); + } + + + fun *.toArray (@target self: LinkedList_SubList, a: array): array + { + action ASSUME(this.root != null); + _checkForComodification(); + + val aSize: int = action ARRAY_SIZE(a); + if (aSize < this.length) + // #problem: a.getClass() should be called to construct a type-valid array (USVM issue) + a = action ARRAY_NEW("java.lang.Object", this.length); + + result = a; + + val rootStorage: list = LinkedListAutomaton(this.root).storage; + val end: int = this.offset + this.length; + var i: int = 0; + var j: int = 0; + action LOOP_FOR( + i, this.offset, end, +1, + toArray_loop(i, j, result, rootStorage) + ); + + if (aSize > this.length) + result[aSize] = null; + } + + + // within java.util.AbstractCollection + fun *.toString (@target self: LinkedList_SubList): String + { + if (this.length == 0) + { + result = "[]"; + } + else + { + result = "["; + + action ASSUME(this.root != null); + + val rootStorage: list = LinkedListAutomaton(this.root).storage; + + var i: int = this.offset; + val end: int = this.offset + this.length; + var counter: int = this.length; + action LOOP_FOR( + i, i, end, +1, + toString_loop(i, rootStorage, result, counter) + ); + + result += "]"; + } + } + + @Phantom proc toString_loop (i: int, rootStorage: list, result: String, counter: int): void + { + val item: Object = action LIST_GET(rootStorage, i); + result += action OBJECT_TO_STRING(item); + + counter -= 1; + if (counter != 0) + result += ", "; + } + +} diff --git a/spec/java/util/LinkedList.lsl b/spec/java/util/LinkedList.lsl index 0e981a07..a8392315 100644 --- a/spec/java/util/LinkedList.lsl +++ b/spec/java/util/LinkedList.lsl @@ -15,7 +15,7 @@ import java/util/Deque; import java/util/List; -// local semantic types +// primary types @extends("java.util.AbstractSequentialList") @implements("java.util.List") @@ -29,3 +29,52 @@ import java/util/List; @private @static val serialVersionUID: long = 876323262645176354L; } + +// new/introduced types + +@GenerateMe +@implements("java.util.ListIterator") +@public @final type LinkedList_ListIterator + is java.util.LinkedList_ListItr // NOTE: do not use inner classes + for ListIterator +{ +} + + +@GenerateMe +@implements("java.util.Spliterator") +@public @final type LinkedList_Spliterator + is java.util.LinkedList_Spliterator // NOTE: do not use inner classes + for Spliterator +{ +} + + +@GenerateMe +@extends("java.util.AbstractList") +@implements("java.util.List") +@implements("java.util.RandomAccess") +@public @final type LinkedList_SubList + is java.util.LinkedList_SubList // NOTE: do not use inner classes + for List +{ +} + + +@GenerateMe +@implements("java.util.Spliterator") +@public @final type LinkedList_SubList_Spliterator + // NOTE: using a '$' sign here to hint on a potential solution to inability to overwrite private constructors + is java.util.LinkedList_SubList$Spliterator + for Spliterator +{ +} + + +@GenerateMe +@implements("java.util.ListIterator") +@public @final type LinkedList_SubList_ListIterator + is java.util.LinkedList_SubList$ListIterator // NOTE: do not use inner classes + for ListIterator +{ +} diff --git a/spec/java/util/LinkedList.automata.lsl b/spec/java/util/LinkedList.main.lsl similarity index 57% rename from spec/java/util/LinkedList.automata.lsl rename to spec/java/util/LinkedList.main.lsl index 89e84ed9..79fbf280 100644 --- a/spec/java/util/LinkedList.automata.lsl +++ b/spec/java/util/LinkedList.main.lsl @@ -7,6 +7,8 @@ library std // imports +import java/util/stream/Stream; + import java/util/LinkedList; @@ -14,8 +16,7 @@ import java/util/LinkedList; automaton LinkedListAutomaton ( - var storage: list, - @transient var size: int + var storage: list ) : LinkedList { @@ -26,8 +27,8 @@ automaton LinkedListAutomaton shift Allocated -> Initialized by [ // constructors - LinkedList (LinkedList), - LinkedList (LinkedList, Collection), + `` (LinkedList), + `` (LinkedList, Collection), ]; shift Initialized -> self by [ @@ -98,6 +99,12 @@ automaton LinkedListAutomaton // utilities + @AutoInline @Phantom proc _throwNPE (): void + { + action THROW_NEW("java.lang.NullPointerException", []); + } + + @KeepVisible proc _checkForComodification (expectedModCount: int): void { if (this.modCount != expectedModCount) @@ -105,56 +112,77 @@ automaton LinkedListAutomaton } - proc _unlinkAny (index: int): Object + // checks range [from, to) against [0, size) + @KeepVisible proc _subListRangeCheck (fromIndex: int, toIndex: int, size: int): void + { + if (fromIndex < 0) + { + //val message1: String = "fromIndex = " + action OBJECT_TO_STRING(fromIndex); + action THROW_NEW("java.lang.IndexOutOfBoundsException", []); + } + + if (toIndex > size) + { + //val message2: String = "toIndex = " + action OBJECT_TO_STRING(toIndex); + action THROW_NEW("java.lang.IndexOutOfBoundsException", []); + } + + if (fromIndex > toIndex) + { + //val from: String = action OBJECT_TO_STRING(fromIndex); + //val to: String = action OBJECT_TO_STRING(toIndex); + //val message3: String = "fromIndex(" + from + ") > toIndex(" + to + ")"; + action THROW_NEW("java.lang.IllegalArgumentException", []); + } + } + + + @KeepVisible proc _unlinkAny (index: int): Object { result = action LIST_GET(this.storage, index); action LIST_REMOVE(this.storage, index); - - this.size -= 1; this.modCount += 1; } - proc _linkAny (index: int, e: Object): void + @KeepVisible proc _linkAny (index: int, e: Object): void { action LIST_INSERT_AT(this.storage, index, e); - - this.size += 1; this.modCount += 1; } - proc _checkElementIndex (index: int): void + @KeepVisible proc _checkElementIndex (index: int, size: int): void { - if (!_isValidIndex(index)) + if (!_isValidIndex(index, size)) { //val message: String = // "Index: " + action OBJECT_TO_STRING(index) + - // ", Size: " + action OBJECT_TO_STRING(this.size); + // ", Size: " + action OBJECT_TO_STRING(size); action THROW_NEW("java.lang.IndexOutOfBoundsException", []); } } - proc _isValidIndex (index: int): boolean + proc _isValidIndex (index: int, size: int): boolean { - result = 0 <= index && index < this.size; + result = 0 <= index && index < size; } proc _isPositionIndex (index: int): boolean { - result = 0 <= index && index <= this.size; + result = 0 <= index && index <= action LIST_SIZE(this.storage); } - proc _checkPositionIndex (index: int): void + @KeepVisible proc _checkPositionIndex (index: int): void { if (!_isPositionIndex(index)) { //val message: String = // "Index: " + action OBJECT_TO_STRING(index) + - // ", Size: " + action OBJECT_TO_STRING(this.size); + // ", Size: " + action OBJECT_TO_STRING(action LIST_SIZE(this.storage)); action THROW_NEW("java.lang.IndexOutOfBoundsException", []); } } @@ -162,7 +190,7 @@ automaton LinkedListAutomaton proc _unlinkFirst (): Object { - if (this.size == 0) + if (action LIST_SIZE(this.storage) == 0) action THROW_NEW("java.util.NoSuchElementException", []); result = _unlinkAny(0); @@ -171,21 +199,19 @@ automaton LinkedListAutomaton proc _unlinkByFirstEqualsObject (o: Object): boolean { - val index: int = action LIST_FIND(this.storage, o, 0, this.size); - - result = index >= 0; + val index: int = action LIST_FIND(this.storage, o, 0, action LIST_SIZE(this.storage)); + result = index != -1; if (result) { action LIST_REMOVE(this.storage, index); - - this.size -= 1; this.modCount += 1; } } - proc _addAllElements (index: int, @Parameterized(["E"]) c: Collection): boolean + @KeepVisible proc _addAllElements (index: int, @Parameterized(["E"]) c: Collection): boolean { + _checkPositionIndex(index); // #todo: add optimized version when 'C' is this automaton (HAS operator is required) val iter: Iterator = action CALL_METHOD(c, "iterator", []); @@ -205,46 +231,339 @@ automaton LinkedListAutomaton action LIST_INSERT_AT(this.storage, index, item); index += 1; - this.size += 1; } proc _getFirstElement (): Object { - if (this.size == 0) + if (action LIST_SIZE(this.storage) == 0) action THROW_NEW("java.util.NoSuchElementException", []); result = action LIST_GET(this.storage, 0); } + @KeepVisible proc _replaceAllRange (op: UnaryOperator, i: int, end: int): void + { + val expectedModCount: int = this.modCount; + + action LOOP_WHILE( + this.modCount == expectedModCount && i < end, + _replaceAllRange_loop(i, op) + ); + + _checkForComodification(expectedModCount); + } + + @Phantom proc _replaceAllRange_loop (i: int, op: UnaryOperator): void + { + val oldItem: Object = action LIST_GET(this.storage, i); + val newItem: Object = action CALL(op, [oldItem]); + action LIST_SET(this.storage, i, newItem); + + i += 1; + } + + + @KeepVisible proc _removeIf (filter: Predicate, start: int, end: int): boolean + { + if (filter == null) + _throwNPE(); + + val oldSize: int = action LIST_SIZE(this.storage); + val expectedModCount: int = this.modCount; + + // remove elements from the back first + action ASSUME(start <= end); + var i: int = 0; + action LOOP_FOR( + i, end - 1, start, -1, + _removeIf_loop(i, filter) + ); + + _checkForComodification(expectedModCount); + + result = oldSize != action LIST_SIZE(this.storage); + } + + @Phantom proc _removeIf_loop (i: int, filter: Predicate): void + { + val item: Object = action LIST_GET(this.storage, i); + + if (action CALL(filter, [item])) + action LIST_REMOVE(this.storage, i); + } + + + @KeepVisible proc _equalsRange (other: List, from: int, to: int): boolean + { + result = true; + var i: int = from; + + var otherLength: int = 0; + var otherStorage: list = null; + + if (other has LinkedListAutomaton) + { + otherStorage = LinkedListAutomaton(other).storage; + otherLength = action LIST_SIZE(otherStorage); + + // assumptions: no multithreading, from == 0 + result = to == otherLength; + if (result) + action LOOP_WHILE( + result && i < to, + _equalsRange_loop_optimized(i, otherStorage, result) + ); + } + /*else if (other has LinkedList_SubListAutomaton) + { + otherLength = LinkedList_SubListAutomaton(other).size; + action ASSUME(otherLength >= 0); + + // assumptions: no multithreading, from >= 0 + result = to == otherLength; + if (result) + { + val otherRoot: LinkedList = LinkedList_SubListAutomaton(other).root; + action ASSUME(otherRoot != null); + + otherStorage = LinkedListAutomaton(otherRoot).storage; + action ASSUME(otherStorage != null); + + action LOOP_WHILE( + result && i < to, + _equalsRange_loop_optimized(i, otherStorage, result) + ); + } + }*/ + else + { + val iter: Iterator = action CALL_METHOD(other, "iterator", []); + action LOOP_WHILE( + result && i < to && action CALL_METHOD(iter, "hasNext", []), + _equalsRange_loop_regular(iter, i, result) + ); + + result &= !action CALL_METHOD(iter, "hasNext", []); + } + } + + @Phantom proc _equalsRange_loop_optimized (i: int, otherStorage: list, result: boolean): void + { + val a: Object = action LIST_GET(otherStorage, i); + val b: Object = action LIST_GET(this.storage, i); + + result = action OBJECT_EQUALS(a, b); + + i += 1; + } + + @Phantom proc _equalsRange_loop_regular (iter: Iterator, i: int, result: boolean): void + { + val a: Object = action CALL_METHOD(iter, "next", []); + val b: Object = action LIST_GET(this.storage, i); + + result = action OBJECT_EQUALS(a, b); + + i += 1; + } + + proc _makeStream (parallel: boolean): Stream { - // #todo: use custom stream implementation - result = action SYMBOLIC("java.util.stream.Stream"); - action ASSUME(result != null); - action ASSUME(action CALL_METHOD(result, "isParallel", []) == parallel); + val count: int = action LIST_SIZE(this.storage); + val items: array = action ARRAY_NEW("java.lang.Object", count); + + var i: int = 0; + action LOOP_FOR( + i, 0, count, +1, + _makeStream_loop(i, items) + ); + + // #problem: unable to catch concurrent modifications during stream processing + + result = new StreamAutomaton(state = Initialized, + storage = items, + length = count, + closeHandlers = action LIST_NEW(), + isParallel = parallel, + ); + } + + @Phantom proc _makeStream_loop (i: int, items: array): void + { + items[i] = action LIST_GET(this.storage, i); + } + + + @KeepVisible proc _batchRemove (c: Collection, complement: boolean, start: int, end: int): boolean + { + val oldSize: int = action LIST_SIZE(this.storage); + if (oldSize == 0 || start >= end) + { + result = false; + } + else + { + val otherLength: int = action CALL_METHOD(c, "size", []); + if (otherLength == 0) + { + result = false; + } + else + { + action ASSUME(otherLength > 0); + + var i: int = 0; + start -= 1; + end -= 1; + + if (c has LinkedListAutomaton) + { + val otherStorage: list = LinkedListAutomaton(c).storage; + action ASSUME(otherStorage != null); + + action LOOP_FOR( + i, end, start, -1, + _batchRemove_loop_optimized(i, otherStorage, complement) + ); + } + else + { + action LOOP_FOR( + i, end, start, -1, + _batchRemove_loop_regular(i, c, complement) + ); + } + + result = oldSize != action LIST_SIZE(this.storage); + } + } + } + + @Phantom proc _batchRemove_loop_optimized (i: int, otherStorage: list, complement: boolean): void + { + val item: Object = action LIST_GET(this.storage, i); + if ((action LIST_FIND(otherStorage, item, 0, action LIST_SIZE(this.storage)) == -1) == complement) + _unlinkAny(i); + } + + @Phantom proc _batchRemove_loop_regular (i: int, c: Collection, complement: boolean): void + { + val item: Object = action LIST_GET(this.storage, i); + if (action CALL_METHOD(c, "contains", [item]) != complement) + _unlinkAny(i); + } + + + @KeepVisible proc _do_sort (start: int, end: int, c: Comparator): void + { + if (start < end) + { + val expectedModCount: int = this.modCount; + + // Java has no unsigned primitive data types + action ASSUME(start >= 0); + action ASSUME(end > 0); + + // plain bubble sorting algorithm + val outerLimit: int = end - 1; + var innerLimit: int = 0; + var i: int = 0; + var j: int = 0; + + // check the comparator + if (c == null) + { + // using Comparable::compareTo as a comparator + + // plain bubble sorting algorithm + action LOOP_FOR( + i, start, outerLimit, +1, + sort_loop_outer_noComparator(i, j, innerLimit, start, end) + ); + } + else + { + // using the provided comparator + + // plain bubble sorting algorithm (with a comparator) + action LOOP_FOR( + i, start, outerLimit, +1, + sort_loop_outer(i, j, innerLimit, start, end, c) + ); + } + + _checkForComodification(expectedModCount); + } + + this.modCount += 1; + } + + @Phantom proc sort_loop_outer_noComparator (i: int, j: int, innerLimit: int, start: int, end: int): void + { + innerLimit = end - i - 1; + action LOOP_FOR( + j, start, innerLimit, +1, + sort_loop_inner_noComparator(j) + ); + } + + @Phantom proc sort_loop_inner_noComparator (j: int): void + { + val idxA: int = j; + val idxB: int = j + 1; + val a: Object = action LIST_GET(this.storage, idxA); + val b: Object = action LIST_GET(this.storage, idxB); + + if (action CALL_METHOD(a as Comparable, "compareTo", [b]) > 0) + { + action LIST_SET(this.storage, idxA, b); + action LIST_SET(this.storage, idxB, a); + } + } + + @Phantom proc sort_loop_outer (i: int, j: int, innerLimit: int, start: int, end: int, c: Comparator): void + { + innerLimit = end - i - 1; + action LOOP_FOR( + j, start, innerLimit, +1, + sort_loop_inner(j, c) + ); + } + + @Phantom proc sort_loop_inner (j: int, c: Comparator): void + { + val idxA: int = j; + val idxB: int = j + 1; + val a: Object = action LIST_GET(this.storage, idxA); + val b: Object = action LIST_GET(this.storage, idxB); + + if (action CALL(c, [a, b]) > 0) + { + action LIST_SET(this.storage, idxA, b); + action LIST_SET(this.storage, idxB, a); + } } // constructors - constructor *.LinkedList (@target self: LinkedList) + constructor *.`` (@target self: LinkedList) { this.storage = action LIST_NEW(); - this.size = 0; } - constructor *.LinkedList (@target self: LinkedList, c: Collection) + constructor *.`` (@target self: LinkedList, c: Collection) { if (c == null) - action THROW_NEW("java.lang.NullPointerException", []); + _throwNPE(); this.storage = action LIST_NEW(); - this.size = 0; - _addAllElements(this.size, c); + _addAllElements(action LIST_SIZE(this.storage), c); } @@ -252,7 +571,7 @@ automaton LinkedListAutomaton fun *.add (@target self: LinkedList, e: Object): boolean { - _linkAny(this.size, e); + _linkAny(action LIST_SIZE(this.storage), e); result = true; } @@ -266,7 +585,7 @@ automaton LinkedListAutomaton fun *.addAll (@target self: LinkedList, c: Collection): boolean { - result = _addAllElements(this.size, c); + result = _addAllElements(action LIST_SIZE(this.storage), c); } @@ -284,14 +603,13 @@ automaton LinkedListAutomaton fun *.addLast (@target self: LinkedList, e: Object): void { - _linkAny(this.size, e); + _linkAny(action LIST_SIZE(this.storage), e); } fun *.clear (@target self: LinkedList): void { this.storage = action LIST_NEW(); - this.size = 0; this.modCount += 1; } @@ -299,18 +617,17 @@ automaton LinkedListAutomaton fun *.clone (@target self: LinkedList): Object { val storageCopy: list = action LIST_NEW(); - action LIST_COPY(this.storage, storageCopy, 0, 0, this.size); + action LIST_COPY(this.storage, storageCopy, 0, 0, action LIST_SIZE(this.storage)); result = new LinkedListAutomaton(state = Initialized, - storage = storageCopy, - size = this.size + storage = storageCopy ); } fun *.contains (@target self: LinkedList, o: Object): boolean { - result = action LIST_FIND(this.storage, o, 0, this.size) >= 0; + result = action LIST_FIND(this.storage, o, 0, action LIST_SIZE(this.storage)) != -1; } @@ -322,7 +639,7 @@ automaton LinkedListAutomaton if (c has LinkedListAutomaton) { val otherStorage: list = LinkedListAutomaton(c).storage; - val otherSize: int = LinkedListAutomaton(c).size; + val otherSize: int = action LIST_SIZE(otherStorage); action ASSUME(otherStorage != null); action ASSUME(otherSize >= 0); @@ -346,7 +663,7 @@ automaton LinkedListAutomaton @Phantom proc containsAll_loop_optimized (otherStorage: list, i: int, result: boolean): void { val item: Object = action LIST_GET(otherStorage, i); - result = action LIST_FIND(this.storage, item, 0, this.size) >= 0; + result = action LIST_FIND(this.storage, item, 0, action LIST_SIZE(this.storage)) != -1; i += 1; } @@ -354,7 +671,7 @@ automaton LinkedListAutomaton @Phantom proc containsAll_loop_regular (iter: Iterator, result: boolean): void { val item: Object = action CALL_METHOD(iter, "next", []); - result = action LIST_FIND(this.storage, item, 0, this.size) >= 0; + result = action LIST_FIND(this.storage, item, 0, action LIST_SIZE(this.storage)) != -1; } @@ -390,9 +707,8 @@ automaton LinkedListAutomaton val otherExpectedModCount: int = LinkedListAutomaton(o).modCount; val otherStorage: list = LinkedListAutomaton(o).storage; - val otherSize: int = LinkedListAutomaton(o).size; - if (this.size == otherSize) + if (action LIST_SIZE(this.storage) == action LIST_SIZE(otherStorage)) { result = action OBJECT_EQUALS(this.storage, otherStorage); } @@ -416,14 +732,13 @@ automaton LinkedListAutomaton fun *.forEach (@target self: LinkedList, _action: Consumer): void { if (_action == null) - action THROW_NEW("java.lang.NullPointerException", []); + _throwNPE(); val expectedModCount: int = this.modCount; - val length: int = this.size; var i: int = 0; action LOOP_WHILE( - this.modCount == expectedModCount && i < length, + this.modCount == expectedModCount && i < action LIST_SIZE(this.storage), forEach_loop(i, _action) ); @@ -441,7 +756,7 @@ automaton LinkedListAutomaton fun *.get (@target self: LinkedList, index: int): Object { - _checkElementIndex(index); + _checkElementIndex(index, action LIST_SIZE(this.storage)); result = action LIST_GET(this.storage, index); } @@ -454,10 +769,10 @@ automaton LinkedListAutomaton fun *.getLast (@target self: LinkedList): Object { - if (this.size == 0) + if (action LIST_SIZE(this.storage) == 0) action THROW_NEW("java.util.NoSuchElementException", []); - result = action LIST_GET(this.storage, this.size - 1); + result = action LIST_GET(this.storage, action LIST_SIZE(this.storage) - 1); } @@ -470,43 +785,55 @@ automaton LinkedListAutomaton fun *.indexOf (@target self: LinkedList, o: Object): int { - result = action LIST_FIND(this.storage, o, 0, this.size); + result = action LIST_FIND(this.storage, o, 0, action LIST_SIZE(this.storage)); } // within java.util.AbstractCollection fun *.isEmpty (@target self: LinkedList): boolean { - result = this.size == 0; + result = action LIST_SIZE(this.storage) == 0; } // within java.util.AbstractSequentialList fun *.iterator (@target self: LinkedList): Iterator { - // #problem: not implemented - /* - result = new ListItr(state = Created, - expectedModCount = this.modCounter + result = new LinkedList_ListIteratorAutomaton(state = Initialized, + parent = self, + cursor = 0, + expectedModCount = this.modCount ); - */ - result = action SYMBOLIC("java.util.Iterator"); - action ASSUME(result != null); } fun *.lastIndexOf (@target self: LinkedList, o: Object): int { - result = action LIST_FIND(this.storage, o, 0, this.size); - if (result != -1) + result = -1; + + val size: int = action LIST_SIZE(this.storage); + if (size != 0) { - // there should be no elements to the right of the previously found position - val nextIndex: int = result + 1; - if (nextIndex < this.size) - { - val rightIndex: int = action LIST_FIND(this.storage, o, nextIndex, this.size); - action ASSUME(rightIndex == -1); - } + action ASSUME(size > 0); + + val items: list = this.storage; + + var i: int = 0; + action LOOP_FOR( + i, size - 1, -1, -1, + lastIndexOf_loop(i, items, o, result) + ); + } + } + + @Phantom proc lastIndexOf_loop (i: int, items: list, o: Object, result: int): void + { + val e: Object = action LIST_GET(items, i); + + if (action OBJECT_EQUALS(o, e)) + { + result = i; + action LOOP_BREAK(); } } @@ -514,14 +841,11 @@ automaton LinkedListAutomaton // within java.util.AbstractList fun *.listIterator (@target self: LinkedList): ListIterator { - // #problem: not implemented - /* - result = new ListItr(state = Created, - expectedModCount = this.modCounter + result = new LinkedList_ListIteratorAutomaton(state = Initialized, + parent = self, + cursor = 0, + expectedModCount = this.modCount ); - */ - result = action SYMBOLIC("java.util.ListIterator"); - action ASSUME(result != null); } @@ -529,20 +853,17 @@ automaton LinkedListAutomaton { _checkPositionIndex(index); - // #problem: not implemented - /* - result = new ListItr(state = Created, - expectedModCount = this.modCounter + result = new LinkedList_ListIteratorAutomaton(state = Initialized, + parent = self, + cursor = index, + expectedModCount = this.modCount ); - */ - result = action SYMBOLIC("java.util.ListIterator"); - action ASSUME(result != null); } fun *.offer (@target self: LinkedList, e: Object): boolean { - _linkAny(this.size, e); + _linkAny(action LIST_SIZE(this.storage), e); result = true; } @@ -556,7 +877,7 @@ automaton LinkedListAutomaton fun *.offerLast (@target self: LinkedList, e: Object): boolean { - _linkAny(this.size, e); + _linkAny(action LIST_SIZE(this.storage), e); result = true; } @@ -570,7 +891,7 @@ automaton LinkedListAutomaton fun *.peek (@target self: LinkedList): Object { - if (this.size == 0) + if (action LIST_SIZE(this.storage) == 0) result = null; else result = action LIST_GET(this.storage, 0); @@ -579,7 +900,7 @@ automaton LinkedListAutomaton fun *.peekFirst (@target self: LinkedList): Object { - if (this.size == 0) + if (action LIST_SIZE(this.storage) == 0) result = null; else result = action LIST_GET(this.storage, 0); @@ -588,16 +909,16 @@ automaton LinkedListAutomaton fun *.peekLast (@target self: LinkedList): Object { - if (this.size == 0) + if (action LIST_SIZE(this.storage) == 0) result = null; else - result = action LIST_GET(this.storage, this.size - 1); + result = action LIST_GET(this.storage, action LIST_SIZE(this.storage) - 1); } fun *.poll (@target self: LinkedList): Object { - if (this.size == 0) + if (action LIST_SIZE(this.storage) == 0) result = null; else result = _unlinkAny(0); @@ -606,7 +927,7 @@ automaton LinkedListAutomaton fun *.pollFirst (@target self: LinkedList): Object { - if (this.size == 0) + if (action LIST_SIZE(this.storage) == 0) result = null; else result = _unlinkAny(0); @@ -615,10 +936,10 @@ automaton LinkedListAutomaton fun *.pollLast (@target self: LinkedList): Object { - if (this.size == 0) + if (action LIST_SIZE(this.storage) == 0) result = null; else - result = _unlinkAny(this.size - 1); + result = _unlinkAny(action LIST_SIZE(this.storage) - 1); } @@ -648,7 +969,7 @@ automaton LinkedListAutomaton fun *.remove (@target self: LinkedList, index: int): Object { - _checkElementIndex(index); + _checkElementIndex(index, action LIST_SIZE(this.storage)); result = _unlinkAny(index); } @@ -656,7 +977,7 @@ automaton LinkedListAutomaton // within java.util.AbstractCollection fun *.removeAll (@target self: LinkedList, c: Collection): boolean { - action TODO(); + result = _batchRemove(c, /* complement = */false, 0, action LIST_SIZE(this.storage)); } @@ -675,22 +996,22 @@ automaton LinkedListAutomaton // within java.util.Collection fun *.removeIf (@target self: LinkedList, filter: Predicate): boolean { - action TODO(); + result = _removeIf(filter, 0, action LIST_SIZE(this.storage)); } fun *.removeLast (@target self: LinkedList): Object { - if (this.size == 0) + if (action LIST_SIZE(this.storage) == 0) action THROW_NEW("java.util.NoSuchElementException", []); - result = _unlinkAny(this.size - 1); + result = _unlinkAny(action LIST_SIZE(this.storage) - 1); } fun *.removeLastOccurrence (@target self: LinkedList, o: Object): boolean { - val index: int = action LIST_FIND(this.storage, o, 0, this.size); + val index: int = action LIST_FIND(this.storage, o, 0, action LIST_SIZE(this.storage)); if (index == -1) { result = false; @@ -701,151 +1022,63 @@ automaton LinkedListAutomaton // there should be no elements to the right of the previously found position val nextIndex: int = index + 1; - if (nextIndex < this.size) + if (nextIndex < action LIST_SIZE(this.storage)) { - val rightIndex: int = action LIST_FIND(this.storage, o, nextIndex, this.size); + val rightIndex: int = action LIST_FIND(this.storage, o, nextIndex, action LIST_SIZE(this.storage)); action ASSUME(rightIndex == -1); } // actual removal and associated modifications action LIST_REMOVE(this.storage, index); - this.size -= 1; this.modCount += 1; } } // within java.util.List - fun *.replaceAll (@target self: LinkedList, operator: UnaryOperator): void + fun *.replaceAll (@target self: LinkedList, op: UnaryOperator): void { - action TODO(); + if (op == null) + _throwNPE(); + + _replaceAllRange(op, 0, action LIST_SIZE(this.storage)); + this.modCount += 1; } // within java.util.AbstractCollection fun *.retainAll (@target self: LinkedList, c: Collection): boolean { - action TODO(); + result = _batchRemove(c, /* complement = */true, 0, action LIST_SIZE(this.storage)); } fun *.set (@target self: LinkedList, index: int, element: Object): Object { - _checkElementIndex(index); - action LIST_SET(this.storage, index, element); + _checkElementIndex(index, action LIST_SIZE(this.storage)); result = action LIST_GET(this.storage, index); + action LIST_SET(this.storage, index, element); } fun *.size (@target self: LinkedList): int { - result = this.size; + result = action LIST_SIZE(this.storage); } // within java.util.List fun *.sort (@target self: LinkedList, c: Comparator): void { - if (this.size != 0) - { - val expectedModCount: int = this.modCount; - - // Java has no unsigned primitive data types - action ASSUME(this.size > 0); - - // plain bubble sorting algorithm - val outerLimit: int = this.size - 1; - var innerLimit: int = 0; - var i: int = 0; - var j: int = 0; - - // check the comparator - if (c == null) - { - // using Comparable::compareTo as a comparator - - // plain bubble sorting algorithm - action LOOP_FOR( - i, 0, outerLimit, +1, - sort_loop_outer_noComparator(i, j, innerLimit) - ); - } - else - { - // using the provided comparator - - // plain bubble sorting algorithm (with a comparator) - action LOOP_FOR( - i, 0, outerLimit, +1, - sort_loop_outer(i, j, innerLimit, c) - ); - } - - _checkForComodification(expectedModCount); - } - - this.modCount += 1; - } - - @Phantom proc sort_loop_outer_noComparator (i: int, j: int, innerLimit: int): void - { - innerLimit = this.size - i - 1; - action LOOP_FOR( - j, 0, innerLimit, +1, - sort_loop_inner_noComparator(j) - ); - } - - @Phantom proc sort_loop_inner_noComparator (j: int): void - { - val idxA: int = j; - val idxB: int = j + 1; - val a: Object = action LIST_GET(this.storage, idxA); - val b: Object = action LIST_GET(this.storage, idxB); - - if (action CALL_METHOD(a as Comparable, "compareTo", [b]) > 0) - { - action LIST_SET(this.storage, idxA, b); - action LIST_SET(this.storage, idxB, a); - } - } - - @Phantom proc sort_loop_outer (i: int, j: int, innerLimit: int, c: Comparator): void - { - innerLimit = this.size - i - 1; - action LOOP_FOR( - j, 0, innerLimit, +1, - sort_loop_inner(j, c) - ); - } - - @Phantom proc sort_loop_inner (j: int, c: Comparator): void - { - val idxA: int = j; - val idxB: int = j + 1; - val a: Object = action LIST_GET(this.storage, idxA); - val b: Object = action LIST_GET(this.storage, idxB); - - if (action CALL(c, [a, b]) > 0) - { - action LIST_SET(this.storage, idxA, b); - action LIST_SET(this.storage, idxB, a); - } + _do_sort(0, action LIST_SIZE(this.storage), c); } fun *.spliterator (@target self: LinkedList): Spliterator { - // #problem: not implemented - /* - result = new LLSpliterator(state=Initialized, + result = new LinkedList_SpliteratorAutomaton(state = Initialized, parent = self, - est = -1, - expectedModCount = 0 ); - */ - result = action SYMBOLIC("java.util.Spliterator"); - action ASSUME(result != null); } @@ -859,13 +1092,21 @@ automaton LinkedListAutomaton // within java.util.AbstractList fun *.subList (@target self: LinkedList, fromIndex: int, toIndex: int): List { - action TODO(); + _subListRangeCheck(fromIndex, toIndex, action LIST_SIZE(this.storage)); + + result = new LinkedList_SubListAutomaton(state = Initialized, + root = self, + parentList = null, + offset = fromIndex, + length = toIndex - fromIndex, + modCount = this.modCount, + ); } fun *.toArray (@target self: LinkedList): array { - val len: int = this.size; + val len: int = action LIST_SIZE(this.storage); result = action ARRAY_NEW("java.lang.Object", len); var i: int = 0; @@ -889,7 +1130,7 @@ automaton LinkedListAutomaton val a: array = action CALL_METHOD(generator, "apply", [0]) as array; val aLen: int = action ARRAY_SIZE(a); - val len: int = this.size; + val len: int = action LIST_SIZE(this.storage); // #problem: a.getClass() should be called to construct a type-valid array (USVM issue) result = action ARRAY_NEW("java.lang.Object", len); @@ -904,7 +1145,7 @@ automaton LinkedListAutomaton fun *.toArray (@target self: LinkedList, a: array): array { val aLen: int = action ARRAY_SIZE(a); - val len: int = this.size; + val len: int = action LIST_SIZE(this.storage); var i: int = 0; if (aLen < len) @@ -939,349 +1180,3 @@ automaton LinkedListAutomaton } } - - - - - -/* -@Private -@Implements("java.util.ListIterator") -@WrapperMeta( - src="java.util.ListItr", - dst="org.utbot.engine.overrides.collections.UtListItr", - matchInterfaces=true, -) -automaton ListItr: int( - var index: int = 0, - var expectedModCount: int, - var nextWasCalled: boolean = false, - var prevWasCalled: boolean = false -) -{ - initstate Initialized; - - // constructors - shift Allocated -> Initialized by [ - ListItr(int) - ]; - - shift Initialized -> self by [ - // read operations - hasNext, - hasPrevious, - nextIndex, - previousIndex, - - // write operations - next, - remove, - previous, - set, - add, - forEachRemaining - ]; - - //constructors - - constructor ListItr (startIndex: int) - { - index = startIndex; - } - - - //methods - - fun *.hasNext (): boolean - { - result = index < self.parent.size; - } - - - fun *.next (): Object - { - checkForComodification(); - val atValidPosition = index < self.parent.length; - - if (!atValidPosition) - { - action THROW_NEW("java.util.NoSuchElementException", []); - } - - result = action LIST_GET(self.parent.storage, index); - index = index + 1; - nextWasCalled = true; - prevWasCalled = false; - } - - - fun *.hasPrevious (): boolean - { - result = index > 0; - } - - - fun *.previous (): Object - { - checkForComodification(); - val atValidPosition = index > 0; - - if (!atValidPosition) - { - action THROW_NEW("java.util.NoSuchElementException", []); - } - - index = index - 1; - result = action LIST_GET(self.parent.storage, index); - prevWasCalled = true; - nextWasCalled = false; - } - - - fun *.nextIndex (): int - { - result = index; - } - - - fun *.previousIndex (): int - { - result = index - 1; - } - - - fun *.remove (): void - { - checkForComodification(); - - if (!nextWasCalled && !prevWasCalled) - { - action THROW_NEW("java.lang.IllegalStateException", []); - } - - if (nextWasCalled) - { - self.parent._unlinkAny(index - 1); - nextWasCalled = false; - } - else - { - self.parent._unlinkAny(index); - index = index - 1; - prevWasCalled = false; - } - - expectedModCount = expectedModCount + 1; - } - - - fun *.set (e: Object): void - { - if (!nextWasCalled && !prevWasCalled) - { - action THROW_NEW("java.lang.IllegalStateException", []); - } - - checkForComodification(); - - if (nextWasCalled) - { - action LIST_SET(storage, index - 1, e); - } - else - { - action LIST_SET(storage, index, e); - } - } - - - fun *.add (e: Object): void - { - checkForComodification(); - val hasNextPosition = index < self.parent.length; - - - if (!hasNextPosition) - { - self.parent.linkAny(self.parent.size, e); - } - else - { - self.parent.linkAny(index, e); - } - - nextWasCalled = false; - prevWasCalled = false; - - index = index + 1; - expectedModCount = expectedModCount + 1; - } - - - fun *.forEachRemaining (action: Consumer): void - { - // #problem - action NOT_IMPLEMENTED(); - } - - - - proc checkForComodification (): void - { - if (self.parent.modCount != expectedModCount) - { - action THROW_NEW("java.util.ConcurrentModificationException", []); - } - } - -} -*/ - - -/* -@Private -@Implements("java.util.Iterator") -@WrapperMeta( - src="java.util.ListItr", - dst="org.utbot.engine.overrides.collections.UtDescendingIterator", - matchInterfaces=true, -) -automaton DescendingIterator: int( - var index: int = 0, - var expectedModCount: int, - var nextWasCalled: boolean = false) -{ - - initstate Initialized; - - shift Initialized -> self by [ - // read operations - hasNext, - - // write operations - next, - remove, - forEachRemaining - ]; - - - fun *.next (): Object - { - checkForComodification(); - val atValidPosition = index > 0; - - if (!atValidPosition) - { - action THROW_NEW("java.util.NoSuchElementException", []); - } - - index = index - 1; - result = action LIST_GET(self.parent.storage, index); - nextWasCalled = true; - } - - - fun *.hasNext (): boolean - { - result = index > 0; - } - - - fun *.remove (): void - { - checkForComodification(); - - if (!nextWasCalled) - { - action THROW_NEW("java.lang.IllegalStateException", []); - } - - self.parent._unlinkAny(index); - index = index - 1; - nextWasCalled = false; - - expectedModCount = expectedModCount + 1; - } - - - fun *.forEachRemaining (action: Consumer): void - { - // #problem - action NOT_IMPLEMENTED(); - } - - - proc checkForComodification (): void - { - if (self.parent.modCount != expectedModCount) - { - action THROW_NEW("java.util.ConcurrentModificationException", []); - } - } - -} -*/ - - - -/* -@Private -@Implements("java.util.Spliterator") -@WrapperMeta( - src="java.util.ListItr", - dst="org.utbot.engine.overrides.collections.UtLLSpliterator", - matchInterfaces=true, -) -automaton LLSpliterator: int( - @Final var BATCH_UNIT: int = 1 << 10, - @Final var MAX_BATCH: int = 1 << 25, - @Final var list: LinkedList, - est: int, - expectedModCount: int, - batch: int -) -{ - - //constructors - - constructor LLSpliterator (list: LinkedList, est: int, expectedModCount: int) - { - action NOT_IMPLEMENTED(); - } - - //sub's - - proc getEst (): int - { - action NOT_IMPLEMENTED(); - } - - //methods - - fun *.estimateSize (): long - { - action NOT_IMPLEMENTED(); - } - - fun *.trySplit (): Spliterator - { - action NOT_IMPLEMENTED(); - } - - fun *.forEachRemaining (action: Consumer): void - { - action NOT_IMPLEMENTED(); - } - - fun *.tryAdvance (action: Consumer): boolean - { - action NOT_IMPLEMENTED(); - } - - fun *.characteristics (): int - { - action NOT_IMPLEMENTED(); - } - -} -*/ diff --git a/spec/java/util/List.main.lsl b/spec/java/util/List.main.lsl index 96944667..19f74a6d 100644 --- a/spec/java/util/List.main.lsl +++ b/spec/java/util/List.main.lsl @@ -71,7 +71,6 @@ automaton ListAutomaton result = new ArrayListAutomaton(state = Initialized, storage = data, - length = size, ); } @@ -87,7 +86,6 @@ automaton ListAutomaton { result = new ArrayListAutomaton(state = Initialized, storage = action LIST_NEW(), - length = 0, ); } @@ -100,7 +98,6 @@ automaton ListAutomaton result = new ArrayListAutomaton(state = Initialized, storage = data, - length = 1, ); } @@ -114,7 +111,6 @@ automaton ListAutomaton result = new ArrayListAutomaton(state = Initialized, storage = data, - length = 2, ); } @@ -129,7 +125,6 @@ automaton ListAutomaton result = new ArrayListAutomaton(state = Initialized, storage = data, - length = 3, ); } @@ -145,7 +140,6 @@ automaton ListAutomaton result = new ArrayListAutomaton(state = Initialized, storage = data, - length = 4, ); } @@ -162,7 +156,6 @@ automaton ListAutomaton result = new ArrayListAutomaton(state = Initialized, storage = data, - length = 5, ); } @@ -180,7 +173,6 @@ automaton ListAutomaton result = new ArrayListAutomaton(state = Initialized, storage = data, - length = 6, ); } @@ -199,7 +191,6 @@ automaton ListAutomaton result = new ArrayListAutomaton(state = Initialized, storage = data, - length = 7, ); } @@ -219,7 +210,6 @@ automaton ListAutomaton result = new ArrayListAutomaton(state = Initialized, storage = data, - length = 8, ); } @@ -240,7 +230,6 @@ automaton ListAutomaton result = new ArrayListAutomaton(state = Initialized, storage = data, - length = 9, ); } @@ -262,7 +251,6 @@ automaton ListAutomaton result = new ArrayListAutomaton(state = Initialized, storage = data, - length = 10, ); } @@ -284,7 +272,6 @@ automaton ListAutomaton result = new ArrayListAutomaton(state = Initialized, storage = data, - length = size, ); } diff --git a/spec/java/util/Locale.lsl b/spec/java/util/Locale.lsl new file mode 100644 index 00000000..5b9e85dc --- /dev/null +++ b/spec/java/util/Locale.lsl @@ -0,0 +1,24 @@ +//#! pragma: non-synthesizable +libsl "1.1.0"; + +library std + version "11" + language "Java" + url "https://github.com/openjdk/jdk11/blob/master/src/java.base/share/classes/java/util/Locale.java"; + +// imports + +import java/io/Serializable; +import java/lang/Cloneable; + + +// primary semantic types + +@final type Locale + is java.util.Locale + for Cloneable, Serializable +{ +} + + +// global aliases and type overrides diff --git a/spec/java/util/Optional.automata.lsl b/spec/java/util/Optional.automata.lsl index 0ece893e..5436e337 100644 --- a/spec/java/util/Optional.automata.lsl +++ b/spec/java/util/Optional.automata.lsl @@ -40,8 +40,8 @@ automaton OptionalAutomaton shift Allocated -> Initialized by [ // constructors - LSLOptional (LSLOptional), - LSLOptional (LSLOptional, Object), + `` (LSLOptional), + `` (LSLOptional, Object), // static methods empty, @@ -81,7 +81,7 @@ automaton OptionalAutomaton // constructors - @private constructor *.LSLOptional (@target @Parameterized(["T"]) self: LSLOptional) + @private constructor *.`` (@target @Parameterized(["T"]) self: LSLOptional) { action ERROR("Private constructor call"); /*assigns this.value; @@ -91,7 +91,7 @@ automaton OptionalAutomaton } - @private constructor *.LSLOptional (@target @Parameterized(["T"]) self: LSLOptional, obj: Object) + @private constructor *.`` (@target @Parameterized(["T"]) self: LSLOptional, obj: Object) { action ERROR("Private constructor call"); /*requires obj != null; diff --git a/spec/java/util/OptionalDouble.automata.lsl b/spec/java/util/OptionalDouble.automata.lsl index 3112617b..590642e3 100644 --- a/spec/java/util/OptionalDouble.automata.lsl +++ b/spec/java/util/OptionalDouble.automata.lsl @@ -40,8 +40,8 @@ automaton OptionalDoubleAutomaton shift Allocated -> Initialized by [ // constructors - LSLOptionalDouble (LSLOptionalDouble), - LSLOptionalDouble (LSLOptionalDouble, double), + `` (LSLOptionalDouble), + `` (LSLOptionalDouble, double), // static methods empty, @@ -76,13 +76,13 @@ automaton OptionalDoubleAutomaton // constructors - @private constructor *.LSLOptionalDouble (@target self: LSLOptionalDouble) + @private constructor *.`` (@target self: LSLOptionalDouble) { action NOT_IMPLEMENTED("this method can be called using reflection only"); } - @private constructor *.LSLOptionalDouble (@target self: LSLOptionalDouble, x: double) + @private constructor *.`` (@target self: LSLOptionalDouble, x: double) { action NOT_IMPLEMENTED("this method can be called using reflection only"); } diff --git a/spec/java/util/OptionalInt.automata.lsl b/spec/java/util/OptionalInt.automata.lsl index a5a92523..5ccccc15 100644 --- a/spec/java/util/OptionalInt.automata.lsl +++ b/spec/java/util/OptionalInt.automata.lsl @@ -40,8 +40,8 @@ automaton OptionalIntAutomaton shift Allocated -> Initialized by [ // constructors - LSLOptionalInt (LSLOptionalInt), - LSLOptionalInt (LSLOptionalInt, int), + `` (LSLOptionalInt), + `` (LSLOptionalInt, int), // static methods empty, @@ -76,13 +76,13 @@ automaton OptionalIntAutomaton // constructors - @private constructor *.LSLOptionalInt (@target self: LSLOptionalInt) + @private constructor *.`` (@target self: LSLOptionalInt) { action NOT_IMPLEMENTED("this method can be called using reflection only"); } - @private constructor *.LSLOptionalInt (@target self: LSLOptionalInt, x: int) + @private constructor *.`` (@target self: LSLOptionalInt, x: int) { action NOT_IMPLEMENTED("this method can be called using reflection only"); } diff --git a/spec/java/util/OptionalLong.automata.lsl b/spec/java/util/OptionalLong.automata.lsl index 353a33ed..39113ec8 100644 --- a/spec/java/util/OptionalLong.automata.lsl +++ b/spec/java/util/OptionalLong.automata.lsl @@ -40,8 +40,8 @@ automaton OptionalLongAutomaton shift Allocated -> Initialized by [ // constructors - LSLOptionalLong (LSLOptionalLong), - LSLOptionalLong (LSLOptionalLong, long), + `` (LSLOptionalLong), + `` (LSLOptionalLong, long), // static methods empty, @@ -76,13 +76,13 @@ automaton OptionalLongAutomaton // constructors - @private constructor *.LSLOptionalLong (@target self: LSLOptionalLong) + @private constructor *.`` (@target self: LSLOptionalLong) { action NOT_IMPLEMENTED("this method can be called using reflection only"); } - @private constructor *.LSLOptionalLong (@target self: LSLOptionalLong, x: long) + @private constructor *.`` (@target self: LSLOptionalLong, x: long) { action NOT_IMPLEMENTED("this method can be called using reflection only"); } diff --git a/spec/java/util/Properties.lsl b/spec/java/util/Properties.lsl index ee709ac2..1582e65d 100644 --- a/spec/java/util/Properties.lsl +++ b/spec/java/util/Properties.lsl @@ -20,4 +20,5 @@ type Properties } -// global aliases and type overrides \ No newline at end of file +// global aliases and type overrides + diff --git a/spec/java/util/Random.lsl b/spec/java/util/Random.lsl index 576f1c2b..9df2819f 100644 --- a/spec/java/util/Random.lsl +++ b/spec/java/util/Random.lsl @@ -24,4 +24,4 @@ import java/io/ObjectStreamField; @private @static val serialPersistentFields: array = []; } -val MAX_RANDOM_STREAM_SIZE: int = 100 +val MAX_RANDOM_STREAM_SIZE: int = 100; diff --git a/spec/java/util/Random.main.lsl b/spec/java/util/Random.main.lsl index 522aed1e..88cf8b6a 100644 --- a/spec/java/util/Random.main.lsl +++ b/spec/java/util/Random.main.lsl @@ -30,8 +30,8 @@ automaton RandomAutomaton shift Allocated -> Initialized by [ // constructors - Random (Random), - Random (Random, long), + `` (Random), + `` (Random, long), ]; shift Initialized -> self by [ @@ -134,13 +134,13 @@ automaton RandomAutomaton // constructors - constructor *.Random (@target self: Random) + constructor *.`` (@target self: Random) { action DO_NOTHING(); } - constructor *.Random (@target self: Random, seed: long) + constructor *.`` (@target self: Random, seed: long) { action DO_NOTHING(); } @@ -357,7 +357,7 @@ automaton RandomAutomaton @synchronized fun *.nextGaussian (@target self: Random): double { result = action SYMBOLIC("double"); - val isNaN: boolean = action DEBUG_DO("Double.isNaN(result)"); + val isNaN: boolean = result != result; action ASSUME(isNaN == false); } diff --git a/spec/java/util/ResourceBundle.lsl b/spec/java/util/ResourceBundle.lsl new file mode 100644 index 00000000..2dc65a97 --- /dev/null +++ b/spec/java/util/ResourceBundle.lsl @@ -0,0 +1,24 @@ +//#! pragma: non-synthesizable +libsl "1.1.0"; + +library std + version "11" + language "Java" + url "https://github.com/openjdk/jdk11/blob/master/src/java.base/share/classes/java/util/ResourceBundle.java"; + +// imports + +import java/lang/Object; + + +// primary semantic types + +@abstract type ResourceBundle + is java.util.ResourceBundle + for Object +{ +} + + +// global aliases and type overrides + diff --git a/spec/java/util/SortedMap.lsl b/spec/java/util/SortedMap.lsl new file mode 100644 index 00000000..414f8ca4 --- /dev/null +++ b/spec/java/util/SortedMap.lsl @@ -0,0 +1,24 @@ +//#! pragma: non-synthesizable +libsl "1.1.0"; + +library std + version "11" + language "Java" + url "https://github.com/openjdk/jdk11/blob/master/src/java.base/share/classes/java/util/SortedMap.java"; + +// imports + +import java/util/Map; + + +// primary semantic types + +@interface type SortedMap + is java.util.SortedMap + for Map +{ +} + + +// global aliases and type overrides + diff --git a/spec/java/util/Spliterators.ArraySpliterator.lsl b/spec/java/util/Spliterators.ArraySpliterator.lsl new file mode 100644 index 00000000..f322bf9e --- /dev/null +++ b/spec/java/util/Spliterators.ArraySpliterator.lsl @@ -0,0 +1,236 @@ +libsl "1.1.0"; + +library std + version "11" + language "Java" + url "https://github.com/openjdk/jdk11/blob/master/src/java.base/share/classes/java/util/Spliterators.java"; + +// imports + +import java/lang/Object; +import java/util/Comparator; +import java/util/Spliterator; +import java/util/function/Consumer; + +import java/util/Spliterators; + + +// automata + +automaton Spliterators_ArraySpliteratorAutomaton +( + var array: array, + var index: int = 0, + var fence: int = -1, + var characteristics: int = 0, +) +: Spliterators_ArraySpliterator +{ + // states and shifts + + initstate Allocated; + state Initialized; + + shift Allocated -> Initialized by [ + // constructors + `` (Spliterators_ArraySpliterator, array, int), + `` (Spliterators_ArraySpliterator, array, int, int, int), + ]; + + shift Initialized -> self by [ + // instance methods + characteristics, + estimateSize, + forEachRemaining (Spliterators_ArraySpliterator, Consumer), + forEachRemaining (Spliterators_ArraySpliterator, Object), + getComparator, + getExactSizeIfKnown, + hasCharacteristics, + tryAdvance (Spliterators_ArraySpliterator, Consumer), + tryAdvance (Spliterators_ArraySpliterator, Object), + trySplit, + ]; + + // internal variables + + // utilities + + @AutoInline @Phantom proc _throwNPE (): void + { + action THROW_NEW("java.lang.NullPointerException", []); + } + + + @AutoInline @Phantom proc _throwISE (): void + { + action THROW_NEW("java.lang.IllegalStateException", []); + } + + + proc _hasCharacteristics (_characteristics: int): boolean + { + result = (this.characteristics & _characteristics) == _characteristics; + } + + + // constructors + + constructor *.`` (@target self: Spliterators_ArraySpliterator, + arr: array, additionalCharacteristics: int) + { + // WARNING: unused + + this.array = arr; + this.index = 0; + this.fence = action ARRAY_SIZE(arr); + this.characteristics = additionalCharacteristics | SPLITERATOR_SIZED | SPLITERATOR_SUBSIZED; + } + + + constructor *.`` (@target self: Spliterators_ArraySpliterator, + arr: array, origin: int, pFence: int, additionalCharacteristics: int) + { + // WARNING: unused + + this.array = arr; + this.index = origin; + this.fence = pFence; + this.characteristics = additionalCharacteristics | SPLITERATOR_SIZED | SPLITERATOR_SUBSIZED; + } + + + // static methods + + // methods + + fun *.characteristics (@target self: Spliterators_ArraySpliterator): int + { + result = this.characteristics; + } + + + fun *.estimateSize (@target self: Spliterators_ArraySpliterator): long + { + result = (this.fence - this.index) as long; + } + + + @AutoInline @Phantom proc _forEachRemaining (_action: Consumer): void + { + if (_action == null) + _throwNPE(); + + val a: array = this.array; + + var hi: int = this.fence; + var i: int = this.index; + this.index = hi; + + action LOOP_FOR( + i, i, hi, +1, + _forEachRemaining_loop(i, a, _action) + ); + } + + @Phantom proc _forEachRemaining_loop (i: int, a: array, _action: Consumer): void + { + val item: Object = a[i]; + action CALL(_action, [item]); + } + + + fun *.forEachRemaining (@target self: Spliterators_ArraySpliterator, _action: Consumer): void + { + _forEachRemaining(_action); // WARNING: inlined call! + } + + + @Phantom fun *.forEachRemaining (@target self: Spliterators_ArraySpliterator, userAction: Object): void + { + // NOTE: using the original method due to Java Compiler error "name clash" + + val _action: Consumer = userAction as Consumer; + _forEachRemaining(_action); // WARNING: inlined call! + } + + + fun *.getComparator (@target self: Spliterators_ArraySpliterator): Comparator + { + if (_hasCharacteristics(SPLITERATOR_SORTED)) + result = null; + else + _throwISE(); + } + + + fun *.getExactSizeIfKnown (@target self: Spliterators_ArraySpliterator): long + { + result = (this.fence - this.index) as long; + } + + + fun *.hasCharacteristics (@target self: Spliterators_ArraySpliterator, _characteristics: int): boolean + { + result = _hasCharacteristics(_characteristics); + } + + + @AutoInline @Phantom proc _tryAdvance (_action: Consumer): boolean + { + if (_action == null) + _throwNPE(); + + val hi: int = this.fence; + val i: int = this.index; + + if (i < hi) + { + this.index = i + 1; + + val item: Object = this.array[i]; + action CALL(_action, [item]); + + result = true; + } + else + { + result = false; + } + } + + + fun *.tryAdvance (@target self: Spliterators_ArraySpliterator, _action: Consumer): boolean + { + _tryAdvance(_action); // WARNING: inlined call! + } + + + @Phantom fun *.tryAdvance (@target self: Spliterators_ArraySpliterator, userAction: Object): boolean + { + // NOTE: using the original method due to Java Compiler error "name clash" + + val _action: Consumer = userAction as Consumer; + _tryAdvance(_action); // WARNING: inlined call! + } + + + fun *.trySplit (@target self: Spliterators_ArraySpliterator): Spliterator + { + val hi: int = this.fence; + val lo: int = this.index; + val mid: int = (lo + hi) >>> 1; + + if (lo >= mid) + result = null; + else + result = new Spliterators_ArraySpliteratorAutomaton(state = Initialized, + array = this.array, + index = lo, + fence = mid, + characteristics = this.characteristics, + ); + + this.index = mid; + } + +} diff --git a/spec/java/util/Spliterators.DoubleArraySpliterator.lsl b/spec/java/util/Spliterators.DoubleArraySpliterator.lsl new file mode 100644 index 00000000..62e491be --- /dev/null +++ b/spec/java/util/Spliterators.DoubleArraySpliterator.lsl @@ -0,0 +1,250 @@ +libsl "1.1.0"; + +library std + version "11" + language "Java" + url "https://github.com/openjdk/jdk11/blob/master/src/java.base/share/classes/java/util/Spliterators.java"; + +// imports + +import java/util/Comparator; +import java/util/Spliterator; +import java/util/function/Consumer; +import java/util/function/DoubleConsumer; + +import java/util/Spliterators; + + +// automata + +automaton Spliterators_DoubleArraySpliteratorAutomaton +( + var array: array, + var index: int = 0, + var fence: int = -1, + var characteristics: int = 0, +) +: Spliterators_DoubleArraySpliterator +{ + // states and shifts + + initstate Allocated; + state Initialized; + + shift Allocated -> Initialized by [ + // constructors + `` (Spliterators_DoubleArraySpliterator, array, int), + `` (Spliterators_DoubleArraySpliterator, array, int, int, int), + ]; + + shift Initialized -> self by [ + // instance methods + characteristics, + estimateSize, + forEachRemaining (Spliterators_DoubleArraySpliterator, Consumer), + forEachRemaining (Spliterators_DoubleArraySpliterator, DoubleConsumer), + forEachRemaining (Spliterators_DoubleArraySpliterator, Object), + getComparator, + getExactSizeIfKnown, + hasCharacteristics, + tryAdvance (Spliterators_DoubleArraySpliterator, Consumer), + tryAdvance (Spliterators_DoubleArraySpliterator, DoubleConsumer), + tryAdvance (Spliterators_DoubleArraySpliterator, Object), + trySplit, + ]; + + // internal variables + + // utilities + + @AutoInline @Phantom proc _throwNPE (): void + { + action THROW_NEW("java.lang.NullPointerException", []); + } + + + @AutoInline @Phantom proc _throwISE (): void + { + action THROW_NEW("java.lang.IllegalStateException", []); + } + + + proc _hasCharacteristics (_characteristics: int): boolean + { + result = (this.characteristics & _characteristics) == _characteristics; + } + + + // constructors + + constructor *.`` (@target self: Spliterators_DoubleArraySpliterator, + arr: array, additionalCharacteristics: int) + { + // WARNING: unused + + this.array = arr; + this.index = 0; + this.fence = action ARRAY_SIZE(arr); + this.characteristics = additionalCharacteristics | SPLITERATOR_SIZED | SPLITERATOR_SUBSIZED; + } + + + constructor *.`` (@target self: Spliterators_DoubleArraySpliterator, + arr: array, origin: int, pFence: int, additionalCharacteristics: int) + { + // WARNING: unused + + this.array = arr; + this.index = origin; + this.fence = pFence; + this.characteristics = additionalCharacteristics | SPLITERATOR_SIZED | SPLITERATOR_SUBSIZED; + } + + + // static methods + + // methods + + fun *.characteristics (@target self: Spliterators_DoubleArraySpliterator): int + { + result = this.characteristics; + } + + + fun *.estimateSize (@target self: Spliterators_DoubleArraySpliterator): long + { + result = (this.fence - this.index) as long; + } + + + @AutoInline @Phantom proc _forEachRemaining (_action: Consumer): void + { + if (_action == null) + _throwNPE(); + + val a: array = this.array; + + var hi: int = this.fence; + var i: int = this.index; + this.index = hi; + + action LOOP_FOR( + i, i, hi, +1, + _forEachRemaining_loop(i, a, _action) + ); + } + + @Phantom proc _forEachRemaining_loop (i: int, a: array, _action: Consumer): void + { + val item: double = a[i]; + action CALL(_action, [item]); + } + + + fun *.forEachRemaining (@target self: Spliterators_DoubleArraySpliterator, _action: DoubleConsumer): void + { + _forEachRemaining(_action); // WARNING: inlined call! + } + + + fun *.forEachRemaining (@target self: Spliterators_DoubleArraySpliterator, _action: Consumer): void + { + _forEachRemaining(_action); // WARNING: inlined call! + } + + + @Phantom fun *.forEachRemaining (@target self: Spliterators_DoubleArraySpliterator, userAction: Object): void + { + // NOTE: using the original method due to Java Compiler error "name clash" + + val _action: DoubleConsumer = userAction as DoubleConsumer; + _forEachRemaining(_action); // WARNING: inlined call! + } + + + fun *.getComparator (@target self: Spliterators_DoubleArraySpliterator): Comparator + { + if (_hasCharacteristics(SPLITERATOR_SORTED)) + result = null; + else + _throwISE(); + } + + + fun *.getExactSizeIfKnown (@target self: Spliterators_DoubleArraySpliterator): long + { + result = (this.fence - this.index) as long; + } + + + fun *.hasCharacteristics (@target self: Spliterators_DoubleArraySpliterator, _characteristics: int): boolean + { + result = _hasCharacteristics(_characteristics); + } + + + @AutoInline @Phantom proc _tryAdvance (_action: Consumer): boolean + { + if (_action == null) + _throwNPE(); + + val hi: int = this.fence; + val i: int = this.index; + + if (i < hi) + { + this.index = i + 1; + + val item: double = this.array[i]; + action CALL(_action, [item]); + + result = true; + } + else + { + result = false; + } + } + + + fun *.tryAdvance (@target self: Spliterators_DoubleArraySpliterator, _action: DoubleConsumer): boolean + { + _tryAdvance(_action); // WARNING: inlined call! + } + + + fun *.tryAdvance (@target self: Spliterators_DoubleArraySpliterator, _action: Consumer): boolean + { + _tryAdvance(_action); // WARNING: inlined call! + } + + + @Phantom fun *.tryAdvance (@target self: Spliterators_DoubleArraySpliterator, userAction: Object): boolean + { + // NOTE: using the original method due to Java Compiler error "name clash" + + val _action: DoubleConsumer = userAction as DoubleConsumer; + _tryAdvance(_action); // WARNING: inlined call! + } + + + fun *.trySplit (@target self: Spliterators_DoubleArraySpliterator): Spliterator_OfDouble + { + val hi: int = this.fence; + val lo: int = this.index; + val mid: int = (lo + hi) >>> 1; + + if (lo >= mid) + result = null; + else + result = new Spliterators_DoubleArraySpliteratorAutomaton(state = Initialized, + array = this.array, + index = lo, + fence = mid, + characteristics = this.characteristics, + ); + + this.index = mid; + } + +} diff --git a/spec/java/util/Spliterators.IntArraySpliterator.lsl b/spec/java/util/Spliterators.IntArraySpliterator.lsl new file mode 100644 index 00000000..b127a888 --- /dev/null +++ b/spec/java/util/Spliterators.IntArraySpliterator.lsl @@ -0,0 +1,250 @@ +libsl "1.1.0"; + +library std + version "11" + language "Java" + url "https://github.com/openjdk/jdk11/blob/master/src/java.base/share/classes/java/util/Spliterators.java"; + +// imports + +import java/util/Comparator; +import java/util/Spliterator; +import java/util/function/Consumer; +import java/util/function/IntConsumer; + +import java/util/Spliterators; + + +// automata + +automaton Spliterators_IntArraySpliteratorAutomaton +( + var array: array, + var index: int = 0, + var fence: int = -1, + var characteristics: int = 0, +) +: Spliterators_IntArraySpliterator +{ + // states and shifts + + initstate Allocated; + state Initialized; + + shift Allocated -> Initialized by [ + // constructors + `` (Spliterators_IntArraySpliterator, array, int), + `` (Spliterators_IntArraySpliterator, array, int, int, int), + ]; + + shift Initialized -> self by [ + // instance methods + characteristics, + estimateSize, + forEachRemaining (Spliterators_IntArraySpliterator, Consumer), + forEachRemaining (Spliterators_IntArraySpliterator, IntConsumer), + forEachRemaining (Spliterators_IntArraySpliterator, Object), + getComparator, + getExactSizeIfKnown, + hasCharacteristics, + tryAdvance (Spliterators_IntArraySpliterator, Consumer), + tryAdvance (Spliterators_IntArraySpliterator, IntConsumer), + tryAdvance (Spliterators_IntArraySpliterator, Object), + trySplit, + ]; + + // internal variables + + // utilities + + @AutoInline @Phantom proc _throwNPE (): void + { + action THROW_NEW("java.lang.NullPointerException", []); + } + + + @AutoInline @Phantom proc _throwISE (): void + { + action THROW_NEW("java.lang.IllegalStateException", []); + } + + + proc _hasCharacteristics (_characteristics: int): boolean + { + result = (this.characteristics & _characteristics) == _characteristics; + } + + + // constructors + + constructor *.`` (@target self: Spliterators_IntArraySpliterator, + arr: array, additionalCharacteristics: int) + { + // WARNING: unused + + this.array = arr; + this.index = 0; + this.fence = action ARRAY_SIZE(arr); + this.characteristics = additionalCharacteristics | SPLITERATOR_SIZED | SPLITERATOR_SUBSIZED; + } + + + constructor *.`` (@target self: Spliterators_IntArraySpliterator, + arr: array, origin: int, pFence: int, additionalCharacteristics: int) + { + // WARNING: unused + + this.array = arr; + this.index = origin; + this.fence = pFence; + this.characteristics = additionalCharacteristics | SPLITERATOR_SIZED | SPLITERATOR_SUBSIZED; + } + + + // static methods + + // methods + + fun *.characteristics (@target self: Spliterators_IntArraySpliterator): int + { + result = this.characteristics; + } + + + fun *.estimateSize (@target self: Spliterators_IntArraySpliterator): long + { + result = (this.fence - this.index) as long; + } + + + @AutoInline @Phantom proc _forEachRemaining (_action: Consumer): void + { + if (_action == null) + _throwNPE(); + + val a: array = this.array; + + var hi: int = this.fence; + var i: int = this.index; + this.index = hi; + + action LOOP_FOR( + i, i, hi, +1, + _forEachRemaining_loop(i, a, _action) + ); + } + + @Phantom proc _forEachRemaining_loop (i: int, a: array, _action: Consumer): void + { + val item: int = a[i]; + action CALL(_action, [item]); + } + + + fun *.forEachRemaining (@target self: Spliterators_IntArraySpliterator, _action: Consumer): void + { + _forEachRemaining(_action); // WARNING: inlined call! + } + + + fun *.forEachRemaining (@target self: Spliterators_IntArraySpliterator, _action: IntConsumer): void + { + _forEachRemaining(_action); // WARNING: inlined call! + } + + + @Phantom fun *.forEachRemaining (@target self: Spliterators_IntArraySpliterator, userAction: Object): void + { + // NOTE: using the original method due to Java Compiler error "name clash" + + val _action: IntConsumer = userAction as IntConsumer; + _forEachRemaining(_action); // WARNING: inlined call! + } + + + fun *.getComparator (@target self: Spliterators_IntArraySpliterator): Comparator + { + if (_hasCharacteristics(SPLITERATOR_SORTED)) + result = null; + else + _throwISE(); + } + + + fun *.getExactSizeIfKnown (@target self: Spliterators_IntArraySpliterator): long + { + result = (this.fence - this.index) as long; + } + + + fun *.hasCharacteristics (@target self: Spliterators_IntArraySpliterator, _characteristics: int): boolean + { + result = _hasCharacteristics(_characteristics); + } + + + @AutoInline @Phantom proc _tryAdvance (_action: Consumer): boolean + { + if (_action == null) + _throwNPE(); + + val hi: int = this.fence; + val i: int = this.index; + + if (i < hi) + { + this.index = i + 1; + + val item: int = this.array[i]; + action CALL(_action, [item]); + + result = true; + } + else + { + result = false; + } + } + + + fun *.tryAdvance (@target self: Spliterators_IntArraySpliterator, _action: IntConsumer): boolean + { + _tryAdvance(_action); // WARNING: inlined call! + } + + + fun *.tryAdvance (@target self: Spliterators_IntArraySpliterator, _action: Consumer): boolean + { + _tryAdvance(_action); // WARNING: inlined call! + } + + + @Phantom fun *.tryAdvance (@target self: Spliterators_IntArraySpliterator, userAction: Object): boolean + { + // NOTE: using the original method due to Java Compiler error "name clash" + + val _action: IntConsumer = userAction as IntConsumer; + _tryAdvance(_action); // WARNING: inlined call! + } + + + fun *.trySplit (@target self: Spliterators_IntArraySpliterator): Spliterator_OfInt + { + val hi: int = this.fence; + val lo: int = this.index; + val mid: int = (lo + hi) >>> 1; + + if (lo >= mid) + result = null; + else + result = new Spliterators_IntArraySpliteratorAutomaton(state = Initialized, + array = this.array, + index = lo, + fence = mid, + characteristics = this.characteristics, + ); + + this.index = mid; + } + +} diff --git a/spec/java/util/Spliterators.LongArraySpliterator.lsl b/spec/java/util/Spliterators.LongArraySpliterator.lsl new file mode 100644 index 00000000..69dd8267 --- /dev/null +++ b/spec/java/util/Spliterators.LongArraySpliterator.lsl @@ -0,0 +1,250 @@ +libsl "1.1.0"; + +library std + version "11" + language "Java" + url "https://github.com/openjdk/jdk11/blob/master/src/java.base/share/classes/java/util/Spliterators.java"; + +// imports + +import java/util/Comparator; +import java/util/Spliterator; +import java/util/function/Consumer; +import java/util/function/LongConsumer; + +import java/util/Spliterators; + + +// automata + +automaton Spliterators_LongArraySpliteratorAutomaton +( + var array: array, + var index: int = 0, + var fence: int = -1, + var characteristics: int = 0, +) +: Spliterators_LongArraySpliterator +{ + // states and shifts + + initstate Allocated; + state Initialized; + + shift Allocated -> Initialized by [ + // constructors + `` (Spliterators_LongArraySpliterator, array, int), + `` (Spliterators_LongArraySpliterator, array, int, int, int), + ]; + + shift Initialized -> self by [ + // instance methods + characteristics, + estimateSize, + forEachRemaining (Spliterators_LongArraySpliterator, Consumer), + forEachRemaining (Spliterators_LongArraySpliterator, LongConsumer), + forEachRemaining (Spliterators_LongArraySpliterator, Object), + getComparator, + getExactSizeIfKnown, + hasCharacteristics, + tryAdvance (Spliterators_LongArraySpliterator, Consumer), + tryAdvance (Spliterators_LongArraySpliterator, LongConsumer), + tryAdvance (Spliterators_LongArraySpliterator, Object), + trySplit, + ]; + + // internal variables + + // utilities + + @AutoInline @Phantom proc _throwNPE (): void + { + action THROW_NEW("java.lang.NullPointerException", []); + } + + + @AutoInline @Phantom proc _throwISE (): void + { + action THROW_NEW("java.lang.IllegalStateException", []); + } + + + proc _hasCharacteristics (_characteristics: int): boolean + { + result = (this.characteristics & _characteristics) == _characteristics; + } + + + // constructors + + constructor *.`` (@target self: Spliterators_LongArraySpliterator, + arr: array, additionalCharacteristics: int) + { + // WARNING: unused + + this.array = arr; + this.index = 0; + this.fence = action ARRAY_SIZE(arr); + this.characteristics = additionalCharacteristics | SPLITERATOR_SIZED | SPLITERATOR_SUBSIZED; + } + + + constructor *.`` (@target self: Spliterators_LongArraySpliterator, + arr: array, origin: int, pFence: int, additionalCharacteristics: int) + { + // WARNING: unused + + this.array = arr; + this.index = origin; + this.fence = pFence; + this.characteristics = additionalCharacteristics | SPLITERATOR_SIZED | SPLITERATOR_SUBSIZED; + } + + + // static methods + + // methods + + fun *.characteristics (@target self: Spliterators_LongArraySpliterator): int + { + result = this.characteristics; + } + + + fun *.estimateSize (@target self: Spliterators_LongArraySpliterator): long + { + result = (this.fence - this.index) as long; + } + + + @AutoInline @Phantom proc _forEachRemaining (_action: Consumer): void + { + if (_action == null) + _throwNPE(); + + val a: array = this.array; + + var hi: int = this.fence; + var i: int = this.index; + this.index = hi; + + action LOOP_FOR( + i, i, hi, +1, + _forEachRemaining_loop(i, a, _action) + ); + } + + @Phantom proc _forEachRemaining_loop (i: int, a: array, _action: Consumer): void + { + val item: long = a[i]; + action CALL(_action, [item]); + } + + + fun *.forEachRemaining (@target self: Spliterators_LongArraySpliterator, _action: Consumer): void + { + _forEachRemaining(_action); // WARNING: inlined call! + } + + + fun *.forEachRemaining (@target self: Spliterators_LongArraySpliterator, _action: LongConsumer): void + { + _forEachRemaining(_action); // WARNING: inlined call! + } + + + @Phantom fun *.forEachRemaining (@target self: Spliterators_LongArraySpliterator, userAction: Object): void + { + // NOTE: using the original method due to Java Compiler error "name clash" + + val _action: LongConsumer = userAction as LongConsumer; + _forEachRemaining(_action); // WARNING: inlined call! + } + + + fun *.getComparator (@target self: Spliterators_LongArraySpliterator): Comparator + { + if (_hasCharacteristics(SPLITERATOR_SORTED)) + result = null; + else + _throwISE(); + } + + + fun *.getExactSizeIfKnown (@target self: Spliterators_LongArraySpliterator): long + { + result = (this.fence - this.index) as long; + } + + + fun *.hasCharacteristics (@target self: Spliterators_LongArraySpliterator, _characteristics: int): boolean + { + result = _hasCharacteristics(_characteristics); + } + + + @AutoInline @Phantom proc _tryAdvance (_action: Consumer): boolean + { + if (_action == null) + _throwNPE(); + + val hi: int = this.fence; + val i: int = this.index; + + if (i < hi) + { + this.index = i + 1; + + val item: long = this.array[i]; + action CALL(_action, [item]); + + result = true; + } + else + { + result = false; + } + } + + + fun *.tryAdvance (@target self: Spliterators_LongArraySpliterator, _action: LongConsumer): boolean + { + _tryAdvance(_action); // WARNING: inlined call! + } + + + fun *.tryAdvance (@target self: Spliterators_LongArraySpliterator, _action: Consumer): boolean + { + _tryAdvance(_action); // WARNING: inlined call! + } + + + @Phantom fun *.tryAdvance (@target self: Spliterators_LongArraySpliterator, userAction: Object): boolean + { + // NOTE: using the original method due to Java Compiler error "name clash" + + val _action: LongConsumer = userAction as LongConsumer; + _tryAdvance(_action); // WARNING: inlined call! + } + + + fun *.trySplit (@target self: Spliterators_LongArraySpliterator): Spliterator_OfLong + { + val hi: int = this.fence; + val lo: int = this.index; + val mid: int = (lo + hi) >>> 1; + + if (lo >= mid) + result = null; + else + result = new Spliterators_LongArraySpliteratorAutomaton(state = Initialized, + array = this.array, + index = lo, + fence = mid, + characteristics = this.characteristics, + ); + + this.index = mid; + } + +} diff --git a/spec/java/util/Spliterators.lsl b/spec/java/util/Spliterators.lsl new file mode 100644 index 00000000..4fdbcab7 --- /dev/null +++ b/spec/java/util/Spliterators.lsl @@ -0,0 +1,79 @@ +//#! pragma: non-synthesizable +libsl "1.1.0"; + +library std + version "11" + language "Java" + url "https://github.com/openjdk/jdk11/blob/master/src/java.base/share/classes/java/util/Spliterators.java"; + +// imports + +import java/lang/Object; +import java/util/Iterator; +import java/util/PrimitiveIterator; +import java/util/Spliterator; + + +// primary semantic types + +@final type Spliterators + is java.util.Spliterators + for Object +{ + // #todo: add automata implementations + + @static fun *.emptySpliterator(): Spliterator; + + @static fun *.emptyDoubleSpliterator(): Spliterator_OfDouble; + + @static fun *.emptyIntSpliterator(): Spliterator_OfInt; + + @static fun *.emptyLongSpliterator(): Spliterator_OfLong; + + @static fun *.iterator(spliterator: Spliterator): Iterator; + + @static fun *.iterator(spliterator: Spliterator_OfDouble): PrimitiveIterator_OfDouble; + + @static fun *.iterator(spliterator: Spliterator_OfInt): PrimitiveIterator_OfInt; + + @static fun *.iterator(spliterator: Spliterator_OfLong): PrimitiveIterator_OfLong; +} + + +// global aliases and type overrides + +@GenerateMe +@implements("java.util.Spliterator") +@final type Spliterators_ArraySpliterator + is java.util.Spliterators_ArraySpliterator // #problem: private class + for Spliterator +{ +} + + +@GenerateMe +@implements("java.util.Spliterator.OfDouble") +@final type Spliterators_DoubleArraySpliterator + is java.util.Spliterators_DoubleArraySpliterator // #problem: private class + for Spliterator_OfDouble +{ +} + + +@GenerateMe +@implements("java.util.Spliterator.OfInt") +@final type Spliterators_IntArraySpliterator + is java.util.Spliterators_IntArraySpliterator // #problem: private class + for Spliterator_OfInt +{ +} + + +@GenerateMe +@implements("java.util.Spliterator.OfLong") +@final type Spliterators_LongArraySpliterator + is java.util.Spliterators_LongArraySpliterator // #problem: private class + for Spliterator_OfLong +{ +} + diff --git a/spec/java/util/Spliterators.main.lsl b/spec/java/util/Spliterators.main.lsl new file mode 100644 index 00000000..5cc8b7d1 --- /dev/null +++ b/spec/java/util/Spliterators.main.lsl @@ -0,0 +1,284 @@ +libsl "1.1.0"; + +library std + version "11" + language "Java" + url "https://github.com/openjdk/jdk11/blob/master/src/java.base/share/classes/java/util/Spliterators.java"; + +// imports + +import java/lang/Object; +import java/util/Collection; +import java/util/Iterator; +import java/util/PrimitiveIterator; +import java/util/Spliterator; + +import java/util/Spliterators; + + +// automata + +automaton SpliteratorsAutomaton +( +) +: Spliterators +{ + // states and shifts + + initstate Allocated; + + shift Allocated -> self by [ + // constructors + ``, + + // static operations + emptyDoubleSpliterator, + emptyIntSpliterator, + emptyLongSpliterator, + emptySpliterator, + iterator (Spliterator_OfDouble), + iterator (Spliterator_OfInt), + iterator (Spliterator_OfLong), + iterator (Spliterator), + spliterator (Collection, int), + spliterator (Iterator, long, int), + spliterator (array, int), + spliterator (array, int, int, int), + spliterator (PrimitiveIterator_OfDouble, long, int), + spliterator (PrimitiveIterator_OfInt, long, int), + spliterator (PrimitiveIterator_OfLong, long, int), + spliterator (array, int), + spliterator (array, int, int, int), + spliterator (array, int), + spliterator (array, int, int, int), + spliterator (array, int), + spliterator (array, int, int, int), + spliteratorUnknownSize (Iterator, int), + spliteratorUnknownSize (PrimitiveIterator_OfDouble, int), + spliteratorUnknownSize (PrimitiveIterator_OfInt, int), + spliteratorUnknownSize (PrimitiveIterator_OfLong, int), + ]; + + // internal variables + + // utilities + + // constructors + + @private constructor *.`` (@target self: Spliterators) + { + // nothing - this is a utility class + } + + + // static methods + + @Phantom @static fun *.emptyDoubleSpliterator (): Spliterator_OfDouble + { + // #todo: using the original method for now + action TODO(); + } + + + @Phantom @static fun *.emptyIntSpliterator (): Spliterator_OfInt + { + // #todo: using the original method for now + action TODO(); + } + + + @Phantom @static fun *.emptyLongSpliterator (): Spliterator_OfLong + { + // #todo: using the original method for now + action TODO(); + } + + + @Phantom @static fun *.emptySpliterator (): Spliterator + { + // #todo: using the original method for now + action TODO(); + } + + + @Phantom @static fun *.iterator (spliterator: Spliterator_OfDouble): PrimitiveIterator_OfDouble + { + // #todo: using the original method for now + action TODO(); + } + + + @Phantom @static fun *.iterator (spliterator: Spliterator_OfInt): PrimitiveIterator_OfInt + { + // #todo: using the original method for now + action TODO(); + } + + + @Phantom @static fun *.iterator (spliterator: Spliterator_OfLong): PrimitiveIterator_OfLong + { + // #todo: using the original method for now + action TODO(); + } + + + @Phantom @static fun *.iterator (spliterator: Spliterator): Iterator + { + // #todo: using the original method for now + action TODO(); + } + + + @Phantom @static fun *.spliterator (c: Collection, characteristics: int): Spliterator + { + // #todo: using the original method for now + action TODO(); + } + + + @Phantom @static fun *.spliterator (iterator: Iterator, size: long, characteristics: int): Spliterator + { + // #todo: using the original method for now + action TODO(); + } + + + @static fun *.spliterator (arr: array, additionalCharacteristics: int): Spliterator + { + result = new Spliterators_ArraySpliteratorAutomaton(state = Initialized, + array = arr, + index = 0, + fence = action ARRAY_SIZE(arr), + characteristics = additionalCharacteristics | SPLITERATOR_SIZED | SPLITERATOR_SUBSIZED, + ); + } + + + @static fun *.spliterator (arr: array, fromIndex: int, toIndex: int, additionalCharacteristics: int): Spliterator + { + result = new Spliterators_ArraySpliteratorAutomaton(state = Initialized, + array = arr, + index = fromIndex, + fence = toIndex, + characteristics = additionalCharacteristics | SPLITERATOR_SIZED | SPLITERATOR_SUBSIZED, + ); + } + + + @Phantom @static fun *.spliterator (iterator: PrimitiveIterator_OfDouble, size: long, characteristics: int): Spliterator_OfDouble + { + // #todo: using the original method for now + action TODO(); + } + + + @Phantom @static fun *.spliterator (iterator: PrimitiveIterator_OfInt, size: long, characteristics: int): Spliterator_OfInt + { + // #todo: using the original method for now + action TODO(); + } + + + @Phantom @static fun *.spliterator (iterator: PrimitiveIterator_OfLong, size: long, characteristics: int): Spliterator_OfLong + { + // #todo: using the original method for now + action TODO(); + } + + + @static fun *.spliterator (arr: array, additionalCharacteristics: int): Spliterator_OfDouble + { + result = new Spliterators_DoubleArraySpliteratorAutomaton(state = Initialized, + array = arr, + index = 0, + fence = action ARRAY_SIZE(arr), + characteristics = additionalCharacteristics | SPLITERATOR_SIZED | SPLITERATOR_SUBSIZED, + ); + } + + + @static fun *.spliterator (arr: array, fromIndex: int, toIndex: int, additionalCharacteristics: int): Spliterator_OfDouble + { + result = new Spliterators_DoubleArraySpliteratorAutomaton(state = Initialized, + array = arr, + index = fromIndex, + fence = toIndex, + characteristics = additionalCharacteristics | SPLITERATOR_SIZED | SPLITERATOR_SUBSIZED, + ); + } + + + @static fun *.spliterator (arr: array, additionalCharacteristics: int): Spliterator_OfInt + { + result = new Spliterators_IntArraySpliteratorAutomaton(state = Initialized, + array = arr, + index = 0, + fence = action ARRAY_SIZE(arr), + characteristics = additionalCharacteristics | SPLITERATOR_SIZED | SPLITERATOR_SUBSIZED, + ); + } + + + @static fun *.spliterator (arr: array, fromIndex: int, toIndex: int, additionalCharacteristics: int): Spliterator_OfInt + { + result = new Spliterators_IntArraySpliteratorAutomaton(state = Initialized, + array = arr, + index = fromIndex, + fence = toIndex, + characteristics = additionalCharacteristics | SPLITERATOR_SIZED | SPLITERATOR_SUBSIZED, + ); + } + + + @static fun *.spliterator (arr: array, additionalCharacteristics: int): Spliterator_OfLong + { + result = new Spliterators_LongArraySpliteratorAutomaton(state = Initialized, + array = arr, + index = 0, + fence = action ARRAY_SIZE(arr), + characteristics = additionalCharacteristics | SPLITERATOR_SIZED | SPLITERATOR_SUBSIZED, + ); + } + + + @static fun *.spliterator (arr: array, fromIndex: int, toIndex: int, additionalCharacteristics: int): Spliterator_OfLong + { + result = new Spliterators_LongArraySpliteratorAutomaton(state = Initialized, + array = arr, + index = fromIndex, + fence = toIndex, + characteristics = additionalCharacteristics | SPLITERATOR_SIZED | SPLITERATOR_SUBSIZED, + ); + } + + + @Phantom @static fun *.spliteratorUnknownSize (iterator: Iterator, characteristics: int): Spliterator + { + // #todo: using the original method for now + action TODO(); + } + + + @Phantom @static fun *.spliteratorUnknownSize (iterator: PrimitiveIterator_OfDouble, characteristics: int): Spliterator_OfDouble + { + // #todo: using the original method for now + action TODO(); + } + + + @Phantom @static fun *.spliteratorUnknownSize (iterator: PrimitiveIterator_OfInt, characteristics: int): Spliterator_OfInt + { + // #todo: using the original method for now + action TODO(); + } + + + @Phantom @static fun *.spliteratorUnknownSize (iterator: PrimitiveIterator_OfLong, characteristics: int): Spliterator_OfLong + { + // #todo: using the original method for now + action TODO(); + } + + + // methods +} diff --git a/spec/java/util/concurrent/atomic/AtomicBoolean.lsl b/spec/java/util/concurrent/atomic/AtomicBoolean.lsl new file mode 100644 index 00000000..baa8394b --- /dev/null +++ b/spec/java/util/concurrent/atomic/AtomicBoolean.lsl @@ -0,0 +1,35 @@ +//#! pragma: non-synthesizable +libsl "1.1.0"; + +library std + version "11" + language "Java" + url "https://github.com/openjdk/jdk11/blob/master/src/java.base/share/classes/java/util/concurrent/atomic/AtomicBoolean.java"; + +// imports + +import java/io/Serializable; + + +// primary semantic types + +type AtomicBoolean + is java.util.concurrent.atomic.AtomicBoolean + for Serializable +{ +} + + +// global aliases and type overrides + +@implements("java.io.Serializable") +type LSLAtomicBoolean + is java.util.concurrent.atomic.AtomicBoolean + for AtomicBoolean +{ + @private @static val serialVersionUID: long = 4654671469794556979L; + + @private @static val FALSE: int = 0; + @private @static val TRUE: int = 1; +} + diff --git a/spec/java/util/concurrent/atomic/AtomicBoolean.main.lsl b/spec/java/util/concurrent/atomic/AtomicBoolean.main.lsl new file mode 100644 index 00000000..53bf6dbe --- /dev/null +++ b/spec/java/util/concurrent/atomic/AtomicBoolean.main.lsl @@ -0,0 +1,337 @@ +libsl "1.1.0"; + +library std + version "11" + language "Java" + url "https://github.com/openjdk/jdk11/blob/master/src/java.base/share/classes/java/util/concurrent/atomic/AtomicBoolean.java"; + +// imports + +import java/lang/String; +import java/util/concurrent/atomic/AtomicBoolean; + + +// automata + +automaton AtomicBooleanAutomaton +( + @private @volatile var value: int = 0, // WARNING: do not rename or change the type! +) +: LSLAtomicBoolean +{ + // states and shifts + + initstate Allocated; + state Initialized; + + shift Allocated -> Initialized by [ + // constructors + `` (LSLAtomicBoolean), + `` (LSLAtomicBoolean, boolean), + ]; + + shift Initialized -> self by [ + // instance methods + compareAndExchange, + compareAndExchangeAcquire, + compareAndExchangeRelease, + compareAndSet, + get, + getAcquire, + getAndSet, + getOpaque, + getPlain, + lazySet, + set, + setOpaque, + setPlain, + setRelease, + toString, + weakCompareAndSet, + weakCompareAndSetAcquire, + weakCompareAndSetPlain, + weakCompareAndSetRelease, + weakCompareAndSetVolatile, + ]; + + // internal variables + + // utilities + + // constructors + + constructor *.`` (@target self: LSLAtomicBoolean) + { + this.value = FALSE; + } + + + constructor *.`` (@target self: LSLAtomicBoolean, initialValue: boolean) + { + if (initialValue) + this.value = TRUE; + else + this.value = FALSE; + } + + + // static methods + + // methods + + @final fun *.compareAndExchange (@target self: LSLAtomicBoolean, expectedValue: boolean, newValue: boolean): boolean + { + result = this.value != FALSE; + if (result == expectedValue) + { + if (newValue) + this.value = TRUE; + else + this.value = FALSE; + } + } + + + @final fun *.compareAndExchangeAcquire (@target self: LSLAtomicBoolean, expectedValue: boolean, newValue: boolean): boolean + { + // #problem: unable to model memory effects here + result = this.value != FALSE; + if (result == expectedValue) + { + if (newValue) + this.value = TRUE; + else + this.value = FALSE; + } + } + + + @final fun *.compareAndExchangeRelease (@target self: LSLAtomicBoolean, expectedValue: boolean, newValue: boolean): boolean + { + // #problem: unable to model memory effects here + result = this.value != FALSE; + if (result == expectedValue) + { + if (newValue) + this.value = TRUE; + else + this.value = FALSE; + } + } + + + @final fun *.compareAndSet (@target self: LSLAtomicBoolean, expectedValue: boolean, newValue: boolean): boolean + { + val currentValue: boolean = this.value != FALSE; + if (currentValue == expectedValue) + { + result = true; + + if (newValue) + this.value = TRUE; + else + this.value = FALSE; + } + else + { + result = false; + } + } + + + @final fun *.get (@target self: LSLAtomicBoolean): boolean + { + // NOTE: same as in the original + result = this.value != FALSE; + } + + + @final fun *.getAcquire (@target self: LSLAtomicBoolean): boolean + { + // #problem: unable to model memory effects here + result = this.value != FALSE; + } + + + @final fun *.getAndSet (@target self: LSLAtomicBoolean, newValue: boolean): boolean + { + result = this.value != FALSE; + + if (newValue) + this.value = TRUE; + else + this.value = FALSE; + } + + + @final fun *.getOpaque (@target self: LSLAtomicBoolean): boolean + { + // #problem: unable to model memory effects here + result = this.value != FALSE; + } + + + @final fun *.getPlain (@target self: LSLAtomicBoolean): boolean + { + result = this.value != FALSE; + } + + + @final fun *.lazySet (@target self: LSLAtomicBoolean, newValue: boolean): void + { + // #problem: delayed assignment in multithreaded environment + if (newValue) + this.value = TRUE; + else + this.value = FALSE; + } + + + @final fun *.set (@target self: LSLAtomicBoolean, newValue: boolean): void + { + if (newValue) + this.value = TRUE; + else + this.value = FALSE; + } + + + @final fun *.setOpaque (@target self: LSLAtomicBoolean, newValue: boolean): void + { + // #problem: unable to model memory effects here + if (newValue) + this.value = TRUE; + else + this.value = FALSE; + } + + + @final fun *.setPlain (@target self: LSLAtomicBoolean, newValue: boolean): void + { + // #problem: unable to model memory effects here + if (newValue) + this.value = TRUE; + else + this.value = FALSE; + } + + + @final fun *.setRelease (@target self: LSLAtomicBoolean, newValue: boolean): void + { + // #problem: unable to model memory effects here + if (newValue) + this.value = TRUE; + else + this.value = FALSE; + } + + + fun *.toString (@target self: LSLAtomicBoolean): String + { + if (this.value == FALSE) + result = "false"; + else + result = "true"; + } + + + fun *.weakCompareAndSet (@target self: LSLAtomicBoolean, expectedValue: boolean, newValue: boolean): boolean + { + // #problem: delayed assignment in multithreaded environment + val currentValue: boolean = this.value != FALSE; + if (currentValue == expectedValue) + { + result = true; + + if (newValue) + this.value = TRUE; + else + this.value = FALSE; + } + else + { + result = false; + } + } + + + @final fun *.weakCompareAndSetAcquire (@target self: LSLAtomicBoolean, expectedValue: boolean, newValue: boolean): boolean + { + // #problem: delayed assignment in multithreaded environment + val currentValue: boolean = this.value != FALSE; + if (currentValue == expectedValue) + { + result = true; + + if (newValue) + this.value = TRUE; + else + this.value = FALSE; + } + else + { + result = false; + } + } + + + fun *.weakCompareAndSetPlain (@target self: LSLAtomicBoolean, expectedValue: boolean, newValue: boolean): boolean + { + // #problem: delayed assignment in multithreaded environment + val currentValue: boolean = this.value != FALSE; + if (currentValue == expectedValue) + { + result = true; + + if (newValue) + this.value = TRUE; + else + this.value = FALSE; + } + else + { + result = false; + } + } + + + @final fun *.weakCompareAndSetRelease (@target self: LSLAtomicBoolean, expectedValue: boolean, newValue: boolean): boolean + { + // #problem: delayed assignment in multithreaded environment + val currentValue: boolean = this.value != FALSE; + if (currentValue == expectedValue) + { + result = true; + + if (newValue) + this.value = TRUE; + else + this.value = FALSE; + } + else + { + result = false; + } + } + + + @final fun *.weakCompareAndSetVolatile (@target self: LSLAtomicBoolean, expectedValue: boolean, newValue: boolean): boolean + { + // #problem: delayed assignment in multithreaded environment + val currentValue: boolean = this.value != FALSE; + if (currentValue == expectedValue) + { + result = true; + + if (newValue) + this.value = TRUE; + else + this.value = FALSE; + } + else + { + result = false; + } + } + +} diff --git a/spec/java/util/concurrent/atomic/AtomicInteger.lsl b/spec/java/util/concurrent/atomic/AtomicInteger.lsl new file mode 100644 index 00000000..84e81a32 --- /dev/null +++ b/spec/java/util/concurrent/atomic/AtomicInteger.lsl @@ -0,0 +1,33 @@ +//#! pragma: non-synthesizable +libsl "1.1.0"; + +library std + version "11" + language "Java" + url "https://github.com/openjdk/jdk11/blob/master/src/java.base/share/classes/java/util/concurrent/atomic/AtomicInteger.java"; + +// imports + +import java/io/Serializable; +import java/lang/Number; + + +// primary semantic types + +type AtomicInteger + is java.util.concurrent.atomic.AtomicInteger + for Number, Serializable +{ +} + + +// global aliases and type overrides + +@extends("java.lang.Number") +@implements("java.io.Serializable") +type LSLAtomicInteger + is java.util.concurrent.atomic.AtomicInteger + for AtomicInteger +{ + @private @static val serialVersionUID: long = 6214790243416807050L; +} diff --git a/spec/java/util/concurrent/atomic/AtomicInteger.main.lsl b/spec/java/util/concurrent/atomic/AtomicInteger.main.lsl new file mode 100644 index 00000000..fe261391 --- /dev/null +++ b/spec/java/util/concurrent/atomic/AtomicInteger.main.lsl @@ -0,0 +1,358 @@ +libsl "1.1.0"; + +library std + version "11" + language "Java" + url "https://github.com/openjdk/jdk11/blob/master/src/java.base/share/classes/java/util/concurrent/atomic/AtomicInteger.java"; + +// imports + +import java/lang/String; +import java/util/function/IntBinaryOperator; +import java/util/function/IntUnaryOperator; + +import java/util/concurrent/atomic/AtomicInteger; + + +// automata + +automaton AtomicIntegerAutomaton +( + @private @volatile var value: int = 0, // WARNING: do not rename! +) +: LSLAtomicInteger +{ + // states and shifts + + initstate Allocated; + state Initialized; + + shift Allocated -> Initialized by [ + // constructors + `` (LSLAtomicInteger), + `` (LSLAtomicInteger, int), + ]; + + shift Initialized -> self by [ + // instance methods + accumulateAndGet, + addAndGet, + byteValue, + compareAndExchange, + compareAndExchangeAcquire, + compareAndExchangeRelease, + compareAndSet, + decrementAndGet, + doubleValue, + floatValue, + get, + getAcquire, + getAndAccumulate, + getAndAdd, + getAndDecrement, + getAndIncrement, + getAndSet, + getAndUpdate, + getOpaque, + getPlain, + incrementAndGet, + intValue, + lazySet, + longValue, + set, + setOpaque, + setPlain, + setRelease, + shortValue, + toString, + updateAndGet, + weakCompareAndSet, + weakCompareAndSetAcquire, + weakCompareAndSetPlain, + weakCompareAndSetRelease, + weakCompareAndSetVolatile, + ]; + + // internal variables + + // utilities + + // constructors + + constructor *.`` (@target self: LSLAtomicInteger) + { + this.value = 0; + } + + + constructor *.`` (@target self: LSLAtomicInteger, initialValue: int) + { + this.value = initialValue; + } + + + // static methods + + // methods + + @final fun *.accumulateAndGet (@target self: LSLAtomicInteger, x: int, accumulatorFunction: IntBinaryOperator): int + { + result = action CALL(accumulatorFunction, [this.value, x]); + this.value = result; + } + + + @final fun *.addAndGet (@target self: LSLAtomicInteger, delta: int): int + { + result = this.value + delta; + this.value = result; + } + + + // within java.lang.Number + fun *.byteValue (@target self: LSLAtomicInteger): byte + { + result = this.value as byte; + } + + + @final fun *.compareAndExchange (@target self: LSLAtomicInteger, expectedValue: int, newValue: int): int + { + result = this.value; + if (result == expectedValue) + this.value = newValue; + } + + + @final fun *.compareAndExchangeAcquire (@target self: LSLAtomicInteger, expectedValue: int, newValue: int): int + { + // #problem: unable to model memory side-effects + result = this.value; + if (result == expectedValue) + this.value = newValue; + } + + + @final fun *.compareAndExchangeRelease (@target self: LSLAtomicInteger, expectedValue: int, newValue: int): int + { + // #problem: unable to model memory side-effects + result = this.value; + if (result == expectedValue) + this.value = newValue; + } + + + @final fun *.compareAndSet (@target self: LSLAtomicInteger, expectedValue: int, newValue: int): boolean + { + result = this.value == expectedValue; + if (result) + this.value = newValue; + } + + + @final fun *.decrementAndGet (@target self: LSLAtomicInteger): int + { + result = this.value - 1; + this.value = result; + } + + + fun *.doubleValue (@target self: LSLAtomicInteger): double + { + result = this.value as double; + } + + + fun *.floatValue (@target self: LSLAtomicInteger): float + { + result = this.value as float; + } + + + @final fun *.get (@target self: LSLAtomicInteger): int + { + result = this.value; + } + + + @final fun *.getAcquire (@target self: LSLAtomicInteger): int + { + // #problem: unable to model memory side-effects + result = this.value; + } + + + @final fun *.getAndAccumulate (@target self: LSLAtomicInteger, x: int, accumulatorFunction: IntBinaryOperator): int + { + result = this.value; + this.value = action CALL(accumulatorFunction, [result, x]); + } + + + @final fun *.getAndAdd (@target self: LSLAtomicInteger, delta: int): int + { + result = this.value; + this.value = result + delta; + } + + + @final fun *.getAndDecrement (@target self: LSLAtomicInteger): int + { + result = this.value; + this.value = result - 1; + } + + + @final fun *.getAndIncrement (@target self: LSLAtomicInteger): int + { + result = this.value; + this.value = result + 1; + } + + + @final fun *.getAndSet (@target self: LSLAtomicInteger, newValue: int): int + { + result = this.value; + this.value = newValue; + } + + + @final fun *.getAndUpdate (@target self: LSLAtomicInteger, updateFunction: IntUnaryOperator): int + { + result = this.value; + this.value = action CALL(updateFunction, [result]); + } + + + @final fun *.getOpaque (@target self: LSLAtomicInteger): int + { + // #problem: unable to model memory side-effects + result = this.value; + } + + + @final fun *.getPlain (@target self: LSLAtomicInteger): int + { + // #problem: unable to model memory side-effects + result = this.value; + } + + + @final fun *.incrementAndGet (@target self: LSLAtomicInteger): int + { + result = this.value + 1; + this.value = result; + } + + + fun *.intValue (@target self: LSLAtomicInteger): int + { + result = this.value; + } + + + @final fun *.lazySet (@target self: LSLAtomicInteger, newValue: int): void + { + // #problem: unable to delay variable update + this.value = newValue; + } + + + fun *.longValue (@target self: LSLAtomicInteger): long + { + result = this.value as long; + } + + + @final fun *.set (@target self: LSLAtomicInteger, newValue: int): void + { + this.value = newValue; + } + + + @final fun *.setOpaque (@target self: LSLAtomicInteger, newValue: int): void + { + // #problem: unable to model memory side-effects + this.value = newValue; + } + + + @final fun *.setPlain (@target self: LSLAtomicInteger, newValue: int): void + { + // #problem: unable to model memory side-effects + this.value = newValue; + } + + + @final fun *.setRelease (@target self: LSLAtomicInteger, newValue: int): void + { + // #problem: unable to model memory side-effects + this.value = newValue; + } + + + // within java.lang.Number + fun *.shortValue (@target self: LSLAtomicInteger): short + { + result = this.value as short; + } + + + fun *.toString (@target self: LSLAtomicInteger): String + { + result = action OBJECT_TO_STRING(this.value); + } + + + @final fun *.updateAndGet (@target self: LSLAtomicInteger, updateFunction: IntUnaryOperator): int + { + result = action CALL(updateFunction, [this.value]); + this.value = result; + } + + + @final fun *.weakCompareAndSet (@target self: LSLAtomicInteger, expectedValue: int, newValue: int): boolean + { + // #problem: unable to delay variable update + result = this.value == expectedValue; + if (result) + this.value = newValue; + } + + + @final fun *.weakCompareAndSetAcquire (@target self: LSLAtomicInteger, expectedValue: int, newValue: int): boolean + { + // #problem: unable to model memory side-effects + result = this.value == expectedValue; + if (result) + this.value = newValue; + } + + + @final fun *.weakCompareAndSetPlain (@target self: LSLAtomicInteger, expectedValue: int, newValue: int): boolean + { + // #problem: unable to model memory side-effects + result = this.value == expectedValue; + if (result) + this.value = newValue; + } + + + @final fun *.weakCompareAndSetRelease (@target self: LSLAtomicInteger, expectedValue: int, newValue: int): boolean + { + // #problem: unable to model memory side-effects + result = this.value == expectedValue; + if (result) + this.value = newValue; + } + + + @final fun *.weakCompareAndSetVolatile (@target self: LSLAtomicInteger, expectedValue: int, newValue: int): boolean + { + // #problem: unable to model memory side-effects + result = this.value == expectedValue; + if (result) + this.value = newValue; + } + +} diff --git a/spec/java/util/concurrent/atomic/AtomicLong.lsl b/spec/java/util/concurrent/atomic/AtomicLong.lsl new file mode 100644 index 00000000..aeeb4622 --- /dev/null +++ b/spec/java/util/concurrent/atomic/AtomicLong.lsl @@ -0,0 +1,34 @@ +//#! pragma: non-synthesizable +libsl "1.1.0"; + +library std + version "11" + language "Java" + url "https://github.com/openjdk/jdk11/blob/master/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLong.java"; + +// imports + +import java/io/Serializable; +import java/lang/Number; + + +// primary semantic types + +type AtomicLong + is java.util.concurrent.atomic.AtomicLong + for Number, Serializable +{ +} + + +// global aliases and type overrides + +@extends("java.lang.Number") +@implements("java.io.Serializable") +type LSLAtomicLong + is java.util.concurrent.atomic.AtomicLong + for AtomicLong +{ + @private @static val serialVersionUID: long = 1927816293512124184L; +} + diff --git a/spec/java/util/concurrent/atomic/AtomicLong.main.lsl b/spec/java/util/concurrent/atomic/AtomicLong.main.lsl new file mode 100644 index 00000000..bb808ddc --- /dev/null +++ b/spec/java/util/concurrent/atomic/AtomicLong.main.lsl @@ -0,0 +1,358 @@ +libsl "1.1.0"; + +library std + version "11" + language "Java" + url "https://github.com/openjdk/jdk11/blob/master/src/java.base/share/classes/java/util/concurrent/atomic/AtomicLong.java"; + +// imports + +import java/lang/String; +import java/util/function/LongBinaryOperator; +import java/util/function/LongUnaryOperator; + +import java/util/concurrent/atomic/AtomicLong; + + +// automata + +automaton AtomicLongAutomaton +( + @private @volatile var value: long = 0L, // WARNING: do not rename! +) +: LSLAtomicLong +{ + // states and shifts + + initstate Allocated; + state Initialized; + + shift Allocated -> Initialized by [ + // constructors + `` (LSLAtomicLong), + `` (LSLAtomicLong, long), + ]; + + shift Initialized -> self by [ + // instance methods + accumulateAndGet, + addAndGet, + byteValue, + compareAndExchange, + compareAndExchangeAcquire, + compareAndExchangeRelease, + compareAndSet, + decrementAndGet, + doubleValue, + floatValue, + get, + getAcquire, + getAndAccumulate, + getAndAdd, + getAndDecrement, + getAndIncrement, + getAndSet, + getAndUpdate, + getOpaque, + getPlain, + incrementAndGet, + intValue, + lazySet, + longValue, + set, + setOpaque, + setPlain, + setRelease, + shortValue, + toString, + updateAndGet, + weakCompareAndSet, + weakCompareAndSetAcquire, + weakCompareAndSetPlain, + weakCompareAndSetRelease, + weakCompareAndSetVolatile, + ]; + + // internal variables + + // utilities + + // constructors + + constructor *.`` (@target self: LSLAtomicLong) + { + this.value = 0L; + } + + + constructor *.`` (@target self: LSLAtomicLong, initialValue: long) + { + this.value = initialValue; + } + + + // static methods + + // methods + + @final fun *.accumulateAndGet (@target self: LSLAtomicLong, x: long, accumulatorFunction: LongBinaryOperator): long + { + result = action CALL(accumulatorFunction, [this.value, x]); + this.value = result; + } + + + @final fun *.addAndGet (@target self: LSLAtomicLong, delta: long): long + { + result = this.value + delta; + this.value = result; + } + + + // within java.lang.Number + fun *.byteValue (@target self: LSLAtomicLong): byte + { + result = this.value as byte; + } + + + @final fun *.compareAndExchange (@target self: LSLAtomicLong, expectedValue: long, newValue: long): long + { + result = this.value; + if (result == expectedValue) + this.value = newValue; + } + + + @final fun *.compareAndExchangeAcquire (@target self: LSLAtomicLong, expectedValue: long, newValue: long): long + { + // #problem: unable to model memory side-effects + result = this.value; + if (result == expectedValue) + this.value = newValue; + } + + + @final fun *.compareAndExchangeRelease (@target self: LSLAtomicLong, expectedValue: long, newValue: long): long + { + // #problem: unable to model memory side-effects + result = this.value; + if (result == expectedValue) + this.value = newValue; + } + + + @final fun *.compareAndSet (@target self: LSLAtomicLong, expectedValue: long, newValue: long): boolean + { + result = this.value == expectedValue; + if (result) + this.value = newValue; + } + + + @final fun *.decrementAndGet (@target self: LSLAtomicLong): long + { + result = this.value - 1L; + this.value = result; + } + + + fun *.doubleValue (@target self: LSLAtomicLong): double + { + result = this.value as double; + } + + + fun *.floatValue (@target self: LSLAtomicLong): float + { + result = this.value as float; + } + + + @final fun *.get (@target self: LSLAtomicLong): long + { + result = this.value; + } + + + @final fun *.getAcquire (@target self: LSLAtomicLong): long + { + // #problem: unable to model memory side-effects + result = this.value; + } + + + @final fun *.getAndAccumulate (@target self: LSLAtomicLong, x: long, accumulatorFunction: LongBinaryOperator): long + { + result = this.value; + this.value = action CALL(accumulatorFunction, [result, x]); + } + + + @final fun *.getAndAdd (@target self: LSLAtomicLong, delta: long): long + { + result = this.value; + this.value = result + delta; + } + + + @final fun *.getAndDecrement (@target self: LSLAtomicLong): long + { + result = this.value; + this.value = result - 1L; + } + + + @final fun *.getAndIncrement (@target self: LSLAtomicLong): long + { + result = this.value; + this.value = result + 1L; + } + + + @final fun *.getAndSet (@target self: LSLAtomicLong, newValue: long): long + { + result = this.value; + this.value = newValue; + } + + + @final fun *.getAndUpdate (@target self: LSLAtomicLong, updateFunction: LongUnaryOperator): long + { + result = this.value; + this.value = action CALL(updateFunction, [result]); + } + + + @final fun *.getOpaque (@target self: LSLAtomicLong): long + { + // #problem: unable to model memory side-effects + result = this.value; + } + + + @final fun *.getPlain (@target self: LSLAtomicLong): long + { + // #problem: unable to model memory side-effects + result = this.value; + } + + + @final fun *.incrementAndGet (@target self: LSLAtomicLong): long + { + result = this.value + 1L; + this.value = result; + } + + + fun *.intValue (@target self: LSLAtomicLong): int + { + result = this.value as int; + } + + + @final fun *.lazySet (@target self: LSLAtomicLong, newValue: long): void + { + // #problem: unable to delay variable update + this.value = newValue; + } + + + fun *.longValue (@target self: LSLAtomicLong): long + { + result = this.value; + } + + + @final fun *.set (@target self: LSLAtomicLong, newValue: long): void + { + this.value = newValue; + } + + + @final fun *.setOpaque (@target self: LSLAtomicLong, newValue: long): void + { + // #problem: unable to model memory side-effects + this.value = newValue; + } + + + @final fun *.setPlain (@target self: LSLAtomicLong, newValue: long): void + { + // #problem: unable to model memory side-effects + this.value = newValue; + } + + + @final fun *.setRelease (@target self: LSLAtomicLong, newValue: long): void + { + // #problem: unable to model memory side-effects + this.value = newValue; + } + + + // within java.lang.Number + fun *.shortValue (@target self: LSLAtomicLong): short + { + result = this.value as short; + } + + + fun *.toString (@target self: LSLAtomicLong): String + { + result = action OBJECT_TO_STRING(this.value); + } + + + @final fun *.updateAndGet (@target self: LSLAtomicLong, updateFunction: LongUnaryOperator): long + { + result = action CALL(updateFunction, [this.value]); + this.value = result; + } + + + @final fun *.weakCompareAndSet (@target self: LSLAtomicLong, expectedValue: long, newValue: long): boolean + { + // #problem: unable to model memory side-effects + result = this.value == expectedValue; + if (result) + this.value = newValue; + } + + + @final fun *.weakCompareAndSetAcquire (@target self: LSLAtomicLong, expectedValue: long, newValue: long): boolean + { + // #problem: unable to model memory side-effects + result = this.value == expectedValue; + if (result) + this.value = newValue; + } + + + @final fun *.weakCompareAndSetPlain (@target self: LSLAtomicLong, expectedValue: long, newValue: long): boolean + { + // #problem: unable to model memory side-effects + result = this.value == expectedValue; + if (result) + this.value = newValue; + } + + + @final fun *.weakCompareAndSetRelease (@target self: LSLAtomicLong, expectedValue: long, newValue: long): boolean + { + // #problem: unable to model memory side-effects + result = this.value == expectedValue; + if (result) + this.value = newValue; + } + + + @final fun *.weakCompareAndSetVolatile (@target self: LSLAtomicLong, expectedValue: long, newValue: long): boolean + { + // #problem: unable to model memory side-effects + result = this.value == expectedValue; + if (result) + this.value = newValue; + } + +} diff --git a/spec/java/util/concurrent/atomic/AtomicReference.lsl b/spec/java/util/concurrent/atomic/AtomicReference.lsl new file mode 100644 index 00000000..525f1e92 --- /dev/null +++ b/spec/java/util/concurrent/atomic/AtomicReference.lsl @@ -0,0 +1,32 @@ +//#! pragma: non-synthesizable +libsl "1.1.0"; + +library std + version "11" + language "Java" + url "https://github.com/openjdk/jdk11/blob/master/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReference.java"; + +// imports + +import java/io/Serializable; + + +// primary semantic types + +type AtomicReference + is java.util.concurrent.atomic.AtomicReference + for Serializable +{ +} + + +// global aliases and type overrides + +@implements("java.io.Serializable") +type LSLAtomicReference + is java.util.concurrent.atomic.AtomicReference + for AtomicReference +{ + @private @static @final var serialVersionUID: long = -1848883965231344442L; +} + diff --git a/spec/java/util/concurrent/atomic/AtomicReference.main.lsl b/spec/java/util/concurrent/atomic/AtomicReference.main.lsl new file mode 100644 index 00000000..92dfdfaa --- /dev/null +++ b/spec/java/util/concurrent/atomic/AtomicReference.main.lsl @@ -0,0 +1,267 @@ +libsl "1.1.0"; + +library std + version "11" + language "Java" + url "https://github.com/openjdk/jdk11/blob/master/src/java.base/share/classes/java/util/concurrent/atomic/AtomicReference.java"; + +// imports + +import java/lang/Object; +import java/lang/String; +import java/util/function/BinaryOperator; +import java/util/function/UnaryOperator; + +import java/util/concurrent/atomic/AtomicReference; + + +// automata + +automaton AtomicReferenceAutomaton +( + @private @volatile var value: Object = null, // WARNING: do not rename! +) +: LSLAtomicReference +{ + // states and shifts + + initstate Allocated; + state Initialized; + + shift Allocated -> Initialized by [ + // constructors + `` (LSLAtomicReference), + `` (LSLAtomicReference, Object), + ]; + + shift Initialized -> self by [ + // instance methods + accumulateAndGet, + compareAndExchange, + compareAndExchangeAcquire, + compareAndExchangeRelease, + compareAndSet, + get, + getAcquire, + getAndAccumulate, + getAndSet, + getAndUpdate, + getOpaque, + getPlain, + lazySet, + set, + setOpaque, + setPlain, + setRelease, + toString, + updateAndGet, + weakCompareAndSet, + weakCompareAndSetAcquire, + weakCompareAndSetPlain, + weakCompareAndSetRelease, + weakCompareAndSetVolatile, + ]; + + // internal variables + + // utilities + + // constructors + + constructor *.`` (@target self: LSLAtomicReference) + { + this.value = null; + } + + + constructor *.`` (@target self: LSLAtomicReference, initialValue: Object) + { + this.value = initialValue; + } + + + // static methods + + // methods + + @final fun *.accumulateAndGet (@target self: LSLAtomicReference, x: Object, accumulatorFunction: BinaryOperator): Object + { + result = action CALL(accumulatorFunction, [this.value, x]); + this.value = result; + } + + + @final fun *.compareAndExchange (@target self: LSLAtomicReference, expectedValue: Object, newValue: Object): Object + { + result = this.value; + if (result == expectedValue) + this.value = newValue; + } + + + @final fun *.compareAndExchangeAcquire (@target self: LSLAtomicReference, expectedValue: Object, newValue: Object): Object + { + // #problem: unable to model memory side-effects + result = this.value; + if (result == expectedValue) + this.value = newValue; + } + + + @final fun *.compareAndExchangeRelease (@target self: LSLAtomicReference, expectedValue: Object, newValue: Object): Object + { + // #problem: unable to model memory side-effects + result = this.value; + if (result == expectedValue) + this.value = newValue; + } + + + @final fun *.compareAndSet (@target self: LSLAtomicReference, expectedValue: Object, newValue: Object): boolean + { + result = this.value == expectedValue; + if (result) + this.value = newValue; + } + + + @final fun *.get (@target self: LSLAtomicReference): Object + { + result = this.value; + } + + + @final fun *.getAcquire (@target self: LSLAtomicReference): Object + { + // #problem: unable to model memory side-effects + result = this.value; + } + + + @final fun *.getAndAccumulate (@target self: LSLAtomicReference, x: Object, accumulatorFunction: BinaryOperator): Object + { + result = this.value; + this.value = action CALL(accumulatorFunction, [result, x]); + } + + + @final fun *.getAndSet (@target self: LSLAtomicReference, newValue: Object): Object + { + result = this.value; + this.value = newValue; + } + + + @final fun *.getAndUpdate (@target self: LSLAtomicReference, updateFunction: UnaryOperator): Object + { + result = this.value; + this.value = action CALL(updateFunction, [result]); + } + + + @final fun *.getOpaque (@target self: LSLAtomicReference): Object + { + // #problem: unable to model memory side-effects + result = this.value; + } + + + @final fun *.getPlain (@target self: LSLAtomicReference): Object + { + // #problem: unable to model memory side-effects + result = this.value; + } + + + @final fun *.lazySet (@target self: LSLAtomicReference, newValue: Object): void + { + // #problem: unable to delay variable update + this.value = newValue; + } + + + @final fun *.set (@target self: LSLAtomicReference, newValue: Object): void + { + this.value = newValue; + } + + + @final fun *.setOpaque (@target self: LSLAtomicReference, newValue: Object): void + { + // #problem: unable to model memory side-effects + this.value = newValue; + } + + + @final fun *.setPlain (@target self: LSLAtomicReference, newValue: Object): void + { + // #problem: unable to model memory side-effects + this.value = newValue; + } + + + @final fun *.setRelease (@target self: LSLAtomicReference, newValue: Object): void + { + // #problem: unable to model memory side-effects + this.value = newValue; + } + + + fun *.toString (@target self: LSLAtomicReference): String + { + result = action OBJECT_TO_STRING(this.value); + } + + + @final fun *.updateAndGet (@target self: LSLAtomicReference, updateFunction: UnaryOperator): Object + { + result = action CALL(updateFunction, [this.value]); + this.value = result; + } + + + @final fun *.weakCompareAndSet (@target self: LSLAtomicReference, expectedValue: Object, newValue: Object): boolean + { + // #problem: unable to delay variable update + result = this.value == expectedValue; + if (result) + this.value = newValue; + } + + + @final fun *.weakCompareAndSetAcquire (@target self: LSLAtomicReference, expectedValue: Object, newValue: Object): boolean + { + // #problem: unable to model memory side-effects + result = this.value == expectedValue; + if (result) + this.value = newValue; + } + + + @final fun *.weakCompareAndSetPlain (@target self: LSLAtomicReference, expectedValue: Object, newValue: Object): boolean + { + // #problem: unable to model memory side-effects + result = this.value == expectedValue; + if (result) + this.value = newValue; + } + + + @final fun *.weakCompareAndSetRelease (@target self: LSLAtomicReference, expectedValue: Object, newValue: Object): boolean + { + // #problem: unable to model memory side-effects + result = this.value == expectedValue; + if (result) + this.value = newValue; + } + + + @final fun *.weakCompareAndSetVolatile (@target self: LSLAtomicReference, expectedValue: Object, newValue: Object): boolean + { + // #problem: unable to model memory side-effects + result = this.value == expectedValue; + if (result) + this.value = newValue; + } + +} diff --git a/spec/java/util/stream/DoubleStream.Spliterator.lsl b/spec/java/util/stream/DoubleStream.Spliterator.lsl deleted file mode 100644 index 9753b9dc..00000000 --- a/spec/java/util/stream/DoubleStream.Spliterator.lsl +++ /dev/null @@ -1,237 +0,0 @@ -libsl "1.1.0"; - -library std - version "11" - language "Java" - url "https://github.com/openjdk/jdk11/blob/master/src/java.base/share/classes/java/util/Spliterators.java"; - -// imports - -import java/util/stream/Stream; -import java/util/function/Consumer; -import java/util/Spliterator; -import java/util/Comparator; -import java/util/function/DoubleConsumer; - - -// automata - -automaton DoubleStreamSpliteratorAutomaton -( - var parent: DoubleStreamLSL, - var characteristics: int = 0, - var fence: int = -1, - var index: int = 0 -) -: DoubleStreamLSLSpliterator -{ - // states and shifts - - initstate Initialized; - - shift Initialized -> self by [ - characteristics, - trySplit, - forEachRemaining (DoubleStreamLSLSpliterator, DoubleConsumer), - forEachRemaining (DoubleStreamLSLSpliterator, Consumer), - tryAdvance (DoubleStreamLSLSpliterator, DoubleConsumer), - tryAdvance (DoubleStreamLSLSpliterator, Consumer), - estimateSize, - getComparator, - getExactSizeIfKnown, - hasCharacteristics, - ]; - - - // utilities - - @AutoInline @Phantom proc _throwNPE (): void - { - action THROW_NEW("java.lang.NullPointerException", []); - } - - - @AutoInline @Phantom proc _throwISE (): void - { - action THROW_NEW("java.lang.IllegalStateException", []); - } - - - proc _getFence (): int - { - // JDK comment: initialize fence to size on first use - if (this.fence < 0) - { - action ASSUME(this.parent != null); - this.fence = DoubleStreamAutomaton(this.parent).length; - } - result = this.fence; - } - - - proc _hasCharacteristics (_characteristics: int): boolean - { - result = (this.characteristics & _characteristics) == _characteristics; - } - - - // methods - - fun *.characteristics (@target self: DoubleStreamLSLSpliterator): int - { - result = this.characteristics; - } - - - fun *.trySplit (@target self: DoubleStreamLSLSpliterator): Spliterator_OfDouble - { - val hi: int = _getFence(); - val lo: int = this.index; - val mid: int = (lo + hi) >>> 1; - - if (lo >= mid) - result = null; - else - result = new DoubleStreamSpliteratorAutomaton(state = Initialized, - parent = this.parent, - index = lo, - fence = mid, - characteristics = this.characteristics, - ); - - this.index = mid; - } - - - fun *.forEachRemaining (@target self: DoubleStreamLSLSpliterator, _action: DoubleConsumer): void - { - if (_action == null) - _throwNPE(); - - action ASSUME(this.parent != null); - val a: array = DoubleStreamAutomaton(this.parent).storage; - - var hi: int = this.fence; - var i: int = this.index; - this.index = hi; - - action LOOP_FOR( - i, i, hi, +1, - forEachRemaining_DoubleConsumer_loop(i, a, _action) - ); - } - - - @Phantom proc forEachRemaining_DoubleConsumer_loop (i: int, a: array, _action: DoubleConsumer): void - { - val item: double = a[i]; - action CALL(_action, [item]); - } - - - fun *.forEachRemaining (@target self: DoubleStreamLSLSpliterator, _action: Consumer): void - { - if (_action == null) - _throwNPE(); - - action ASSUME(this.parent != null); - val a: array = DoubleStreamAutomaton(this.parent).storage; - - var hi: int = this.fence; - var i: int = this.index; - this.index = hi; - - action LOOP_FOR( - i, i, hi, +1, - forEachRemaining_Consumer_loop(i, a, _action) - ); - } - - - @Phantom proc forEachRemaining_Consumer_loop (i: int, a: array, _action: Consumer): void - { - val item: double = a[i]; - action CALL(_action, [item]); - } - - - fun *.tryAdvance (@target self: DoubleStreamLSLSpliterator, _action: DoubleConsumer): boolean - { - if (_action == null) - _throwNPE(); - - val hi: int = _getFence(); - val i: int = this.index; - - if (i < hi) - { - action ASSUME(this.parent != null); - - this.index = i + 1; - - val parentStorage: array = DoubleStreamAutomaton(this.parent).storage; - val item: double = parentStorage[i]; - action CALL(_action, [item]); - - result = true; - } - else - { - result = false; - } - } - - - fun *.tryAdvance (@target self: DoubleStreamLSLSpliterator, _action: Consumer): boolean - { - if (_action == null) - _throwNPE(); - - val hi: int = _getFence(); - val i: int = this.index; - - if (i < hi) - { - action ASSUME(this.parent != null); - - this.index = i + 1; - - val parentStorage: array = DoubleStreamAutomaton(this.parent).storage; - val item: double = parentStorage[i]; - action CALL(_action, [item]); - - result = true; - } - else - { - result = false; - } - } - - - fun *.estimateSize (@target self: DoubleStreamLSLSpliterator): long - { - result = _getFence() - this.index; - } - - - fun *.getComparator (@target self: DoubleStreamLSLSpliterator): Comparator - { - if (_hasCharacteristics(SPLITERATOR_SORTED)) - result = null; - else - _throwISE(); - } - - - fun *.getExactSizeIfKnown (@target self: DoubleStreamLSLSpliterator): long - { - result = _getFence() - this.index; - } - - - fun *.hasCharacteristics (@target self: DoubleStreamLSLSpliterator, _characteristics: int): boolean - { - result = _hasCharacteristics(_characteristics); - } -} \ No newline at end of file diff --git a/spec/java/util/stream/DoubleStream.lsl b/spec/java/util/stream/DoubleStream.lsl index 7c472ae9..979875c2 100644 --- a/spec/java/util/stream/DoubleStream.lsl +++ b/spec/java/util/stream/DoubleStream.lsl @@ -30,6 +30,10 @@ import java/util/Spliterator; is java.util.stream.DoubleStreamLSL for DoubleStream { + @private @static val DOUBLE_POSITIVE_INFINITY: double = 1.0 / 0.0; + @private @static val DOUBLE_NEGATIVE_INFINITY: double = -1.0 / 0.0; + + @private @static val DOUBLE_NAN: double = 0.0 / 0.0; } @@ -40,12 +44,3 @@ import java/util/Spliterator; for PrimitiveIterator_OfDouble { } - - -@GenerateMe -@implements("java.util.Spliterator.OfDouble") -@public type DoubleStreamLSLSpliterator - is java.util.DoubleStreamLSLSpliterator - for Spliterator_OfDouble -{ -} \ No newline at end of file diff --git a/spec/java/util/stream/DoubleStream.main.lsl b/spec/java/util/stream/DoubleStream.main.lsl index f30f09d4..28b65a64 100644 --- a/spec/java/util/stream/DoubleStream.main.lsl +++ b/spec/java/util/stream/DoubleStream.main.lsl @@ -7,7 +7,6 @@ library std // imports -import java/util/stream/DoubleStream; import java/lang/Double; import java/util/function/DoubleFunction; import java/util/function/DoublePredicate; @@ -20,6 +19,9 @@ import java/util/DoubleSummaryStatistics; import java/util/PrimitiveIterator; import java/util/OptionalDouble; +import java/util/stream/DoubleStream; +import java/util/Spliterators; + // automata @@ -134,12 +136,12 @@ automaton DoubleStreamAutomaton { if (this.length == 0) { - result = action DEBUG_DO("OptionalDouble.empty()"); + result = action CALL_METHOD(null as OptionalDouble, "empty", []); } else { val first: double = this.storage[0]; - result = action DEBUG_DO("OptionalDouble.of(first)"); + result = action CALL_METHOD(null as OptionalDouble, "of", [first]); } } @@ -178,7 +180,7 @@ automaton DoubleStreamAutomaton val element: double = this.storage[i]; result += element; - if (action DEBUG_DO("Double.isNaN(element)")) + if (element != element /* NaN */) anyNaN = true; if (element == DOUBLE_POSITIVE_INFINITY) @@ -684,7 +686,7 @@ automaton DoubleStreamAutomaton if (this.length == 0) { - result = action DEBUG_DO("OptionalDouble.empty()"); + result = action CALL_METHOD(null as OptionalDouble, "empty", []); } else if (this.length > 0) { @@ -696,7 +698,7 @@ automaton DoubleStreamAutomaton _accumulate_optional_loop(i, accumulator, value) ); - result = action DEBUG_DO("OptionalDouble.of(value)"); + result = action CALL_METHOD(null as OptionalDouble, "of", [value]); } _consume(); @@ -747,7 +749,7 @@ automaton DoubleStreamAutomaton if (this.length == 0) { - result = action DEBUG_DO("OptionalDouble.empty()"); + result = action CALL_METHOD(null as OptionalDouble, "empty", []); } else { @@ -759,7 +761,7 @@ automaton DoubleStreamAutomaton _find_min_loop(i, min) ); - result = action DEBUG_DO("OptionalDouble.of(min)"); + result = action CALL_METHOD(null as OptionalDouble, "of", [min]); } _consume(); @@ -779,7 +781,7 @@ automaton DoubleStreamAutomaton if (this.length == 0) { - result = action DEBUG_DO("OptionalDouble.empty()"); + result = action CALL_METHOD(null as OptionalDouble, "empty", []); } else { @@ -791,7 +793,7 @@ automaton DoubleStreamAutomaton _find_max_loop(i, max) ); - result = action DEBUG_DO("OptionalDouble.of(max)"); + result = action CALL_METHOD(null as OptionalDouble, "of", [max]); } _consume(); @@ -930,12 +932,11 @@ automaton DoubleStreamAutomaton { _checkConsumed(); - val default_characteristics: int = SPLITERATOR_ORDERED | SPLITERATOR_IMMUTABLE | SPLITERATOR_SIZED | SPLITERATOR_SUBSIZED; - result = new DoubleStreamSpliteratorAutomaton(state = Initialized, - parent = self, + result = new Spliterators_DoubleArraySpliteratorAutomaton(state = Initialized, + array = this.storage, index = 0, fence = this.length, - characteristics = default_characteristics, + characteristics = SPLITERATOR_ORDERED | SPLITERATOR_IMMUTABLE | SPLITERATOR_SIZED | SPLITERATOR_SUBSIZED, ); _consume(); @@ -1178,13 +1179,13 @@ automaton DoubleStreamAutomaton if (this.length == 0) { - result = action DEBUG_DO("OptionalDouble.empty()"); + result = action CALL_METHOD(null as OptionalDouble, "empty", []); } else { var curSum: double = _sum(); var divisionResult: double = curSum / this.length; - result = action DEBUG_DO("OptionalDouble.of(divisionResult)"); + result = action CALL_METHOD(null as OptionalDouble, "of", [divisionResult]); } _consume(); diff --git a/spec/java/util/stream/IntStream.Spliterator.lsl b/spec/java/util/stream/IntStream.Spliterator.lsl deleted file mode 100644 index 900dc87f..00000000 --- a/spec/java/util/stream/IntStream.Spliterator.lsl +++ /dev/null @@ -1,237 +0,0 @@ -libsl "1.1.0"; - -library std - version "11" - language "Java" - url "https://github.com/openjdk/jdk11/blob/master/src/java.base/share/classes/java/util/Spliterators.java"; - -// imports - -import java/util/stream/Stream; -import java/util/function/Consumer; -import java/util/Spliterator; -import java/util/Comparator; -import java/util/function/IntConsumer; - - -// automata - -automaton IntStreamSpliteratorAutomaton -( - var parent: IntStreamLSL, - var characteristics: int = 0, - var fence: int = -1, - var index: int = 0 -) -: IntStreamLSLSpliterator -{ - // states and shifts - - initstate Initialized; - - shift Initialized -> self by [ - characteristics, - trySplit, - forEachRemaining (IntStreamLSLSpliterator, IntConsumer), - forEachRemaining (IntStreamLSLSpliterator, Consumer), - tryAdvance (IntStreamLSLSpliterator, IntConsumer), - tryAdvance (IntStreamLSLSpliterator, Consumer), - estimateSize, - getComparator, - getExactSizeIfKnown, - hasCharacteristics, - ]; - - - // utilities - - @AutoInline @Phantom proc _throwNPE (): void - { - action THROW_NEW("java.lang.NullPointerException", []); - } - - - @AutoInline @Phantom proc _throwISE (): void - { - action THROW_NEW("java.lang.IllegalStateException", []); - } - - - proc _getFence (): int - { - // JDK comment: initialize fence to size on first use - if (this.fence < 0) - { - action ASSUME(this.parent != null); - this.fence = IntStreamAutomaton(this.parent).length; - } - result = this.fence; - } - - - proc _hasCharacteristics (_characteristics: int): boolean - { - result = (this.characteristics & _characteristics) == _characteristics; - } - - - // methods - - fun *.characteristics (@target self: IntStreamLSLSpliterator): int - { - result = this.characteristics; - } - - - fun *.trySplit (@target self: IntStreamLSLSpliterator): Spliterator_OfInt - { - val hi: int = _getFence(); - val lo: int = this.index; - val mid: int = (lo + hi) >>> 1; - - if (lo >= mid) - result = null; - else - result = new IntStreamSpliteratorAutomaton(state = Initialized, - parent = this.parent, - index = lo, - fence = mid, - characteristics = this.characteristics, - ); - - this.index = mid; - } - - - fun *.forEachRemaining (@target self: IntStreamLSLSpliterator, _action: IntConsumer): void - { - if (_action == null) - _throwNPE(); - - action ASSUME(this.parent != null); - val a: array = IntStreamAutomaton(this.parent).storage; - - var hi: int = this.fence; - var i: int = this.index; - this.index = hi; - - action LOOP_FOR( - i, i, hi, +1, - forEachRemaining_IntConsumer_loop(i, a, _action) - ); - } - - - @Phantom proc forEachRemaining_IntConsumer_loop (i: int, a: array, _action: IntConsumer): void - { - val item: int = a[i]; - action CALL(_action, [item]); - } - - - fun *.forEachRemaining (@target self: IntStreamLSLSpliterator, _action: Consumer): void - { - if (_action == null) - _throwNPE(); - - action ASSUME(this.parent != null); - val a: array = IntStreamAutomaton(this.parent).storage; - - var hi: int = this.fence; - var i: int = this.index; - this.index = hi; - - action LOOP_FOR( - i, i, hi, +1, - forEachRemaining_Consumer_loop(i, a, _action) - ); - } - - - @Phantom proc forEachRemaining_Consumer_loop (i: int, a: array, _action: Consumer): void - { - val item: int = a[i]; - action CALL(_action, [item]); - } - - - fun *.tryAdvance (@target self: IntStreamLSLSpliterator, _action: IntConsumer): boolean - { - if (_action == null) - _throwNPE(); - - val hi: int = _getFence(); - val i: int = this.index; - - if (i < hi) - { - action ASSUME(this.parent != null); - - this.index = i + 1; - - val parentStorage: array = IntStreamAutomaton(this.parent).storage; - val item: int = parentStorage[i]; - action CALL(_action, [item]); - - result = true; - } - else - { - result = false; - } - } - - - fun *.tryAdvance (@target self: IntStreamLSLSpliterator, _action: Consumer): boolean - { - if (_action == null) - _throwNPE(); - - val hi: int = _getFence(); - val i: int = this.index; - - if (i < hi) - { - action ASSUME(this.parent != null); - - this.index = i + 1; - - val parentStorage: array = IntStreamAutomaton(this.parent).storage; - val item: int = parentStorage[i]; - action CALL(_action, [item]); - - result = true; - } - else - { - result = false; - } - } - - - fun *.estimateSize (@target self: IntStreamLSLSpliterator): long - { - result = _getFence() - this.index; - } - - - fun *.getComparator (@target self: IntStreamLSLSpliterator): Comparator - { - if (_hasCharacteristics(SPLITERATOR_SORTED)) - result = null; - else - _throwISE(); - } - - - fun *.getExactSizeIfKnown (@target self: IntStreamLSLSpliterator): long - { - result = _getFence() - this.index; - } - - - fun *.hasCharacteristics (@target self: IntStreamLSLSpliterator, _characteristics: int): boolean - { - result = _hasCharacteristics(_characteristics); - } -} \ No newline at end of file diff --git a/spec/java/util/stream/IntStream.lsl b/spec/java/util/stream/IntStream.lsl index 785dc0f5..f771040d 100644 --- a/spec/java/util/stream/IntStream.lsl +++ b/spec/java/util/stream/IntStream.lsl @@ -40,12 +40,3 @@ import java/util/Spliterator; for PrimitiveIterator_OfInt { } - - -@GenerateMe -@implements("java.util.Spliterator.OfInt") -@public type IntStreamLSLSpliterator - is java.util.IntStreamLSLSpliterator - for Spliterator_OfInt -{ -} \ No newline at end of file diff --git a/spec/java/util/stream/IntStream.main.lsl b/spec/java/util/stream/IntStream.main.lsl index dc6b6dcb..9c243068 100644 --- a/spec/java/util/stream/IntStream.main.lsl +++ b/spec/java/util/stream/IntStream.main.lsl @@ -86,8 +86,8 @@ automaton IntStreamAutomaton var isParallel: boolean = false; var linkedOrConsumed: boolean = false; - // utilities - + // utilities + @AutoInline @Phantom proc _checkConsumed (): void { if (this.linkedOrConsumed) @@ -136,12 +136,12 @@ automaton IntStreamAutomaton { if (this.length == 0) { - result = action DEBUG_DO("OptionalInt.empty()"); + result = action CALL_METHOD(null as OptionalInt, "empty", []); } else { val first: int = this.storage[0]; - result = action DEBUG_DO("OptionalInt.of(first)"); + result = action CALL_METHOD(null as OptionalInt, "of", [first]); } } @@ -660,7 +660,7 @@ automaton IntStreamAutomaton if (this.length == 0) { - result = action DEBUG_DO("OptionalInt.empty()"); + result = action CALL_METHOD(null as OptionalInt, "empty", []); } else if (this.length > 0) { @@ -672,7 +672,7 @@ automaton IntStreamAutomaton _accumulate_optional_loop(i, accumulator, value) ); - result = action DEBUG_DO("OptionalInt.of(value)"); + result = action CALL_METHOD(null as OptionalInt, "of", [value]); } _consume(); @@ -723,7 +723,7 @@ automaton IntStreamAutomaton if (this.length == 0) { - result = action DEBUG_DO("OptionalInt.empty()"); + result = action CALL_METHOD(null as OptionalInt, "empty", []); } else { @@ -735,7 +735,7 @@ automaton IntStreamAutomaton _find_min_loop(i, min) ); - result = action DEBUG_DO("OptionalInt.of(min)"); + result = action CALL_METHOD(null as OptionalInt, "of", [min]); } _consume(); @@ -755,7 +755,7 @@ automaton IntStreamAutomaton if (this.length == 0) { - result = action DEBUG_DO("OptionalInt.empty()"); + result = action CALL_METHOD(null as OptionalInt, "empty", []); } else { @@ -767,7 +767,7 @@ automaton IntStreamAutomaton _find_max_loop(i, max) ); - result = action DEBUG_DO("OptionalInt.of(max)"); + result = action CALL_METHOD(null as OptionalInt, "of", [max]); } _consume(); @@ -906,12 +906,11 @@ automaton IntStreamAutomaton { _checkConsumed(); - val default_characteristics: int = SPLITERATOR_ORDERED | SPLITERATOR_IMMUTABLE | SPLITERATOR_SIZED | SPLITERATOR_SUBSIZED; - result = new IntStreamSpliteratorAutomaton(state = Initialized, - parent = self, + result = new Spliterators_IntArraySpliteratorAutomaton(state = Initialized, + array = this.storage, index = 0, fence = this.length, - characteristics = default_characteristics, + characteristics = SPLITERATOR_ORDERED | SPLITERATOR_IMMUTABLE | SPLITERATOR_SIZED | SPLITERATOR_SUBSIZED, ); _consume(); @@ -1238,13 +1237,13 @@ automaton IntStreamAutomaton if (this.length == 0) { - result = action DEBUG_DO("OptionalDouble.empty()"); + result = action CALL_METHOD(null as OptionalDouble, "empty", []); } else { var curSum: double = _sum(); var divisionResult: double = curSum / this.length; - result = action DEBUG_DO("OptionalDouble.of(divisionResult)"); + result = action CALL_METHOD(null as OptionalDouble, "of", [divisionResult]); } _consume(); diff --git a/spec/java/util/stream/LongStream.Spliterator.lsl b/spec/java/util/stream/LongStream.Spliterator.lsl deleted file mode 100644 index d8d581f1..00000000 --- a/spec/java/util/stream/LongStream.Spliterator.lsl +++ /dev/null @@ -1,237 +0,0 @@ -libsl "1.1.0"; - -library std - version "11" - language "Java" - url "https://github.com/openjdk/jdk11/blob/master/src/java.base/share/classes/java/util/Spliterators.java"; - -// imports - -import java/util/stream/Stream; -import java/util/function/Consumer; -import java/util/Spliterator; -import java/util/Comparator; -import java/util/function/LongConsumer; - - -// automata - -automaton LongStreamSpliteratorAutomaton -( - var parent: LongStreamLSL, - var characteristics: int = 0, - var fence: int = -1, - var index: int = 0 -) -: LongStreamLSLSpliterator -{ - // states and shifts - - initstate Initialized; - - shift Initialized -> self by [ - characteristics, - trySplit, - forEachRemaining (LongStreamLSLSpliterator, LongConsumer), - forEachRemaining (LongStreamLSLSpliterator, Consumer), - tryAdvance (LongStreamLSLSpliterator, LongConsumer), - tryAdvance (LongStreamLSLSpliterator, Consumer), - estimateSize, - getComparator, - getExactSizeIfKnown, - hasCharacteristics, - ]; - - - // utilities - - @AutoInline @Phantom proc _throwNPE (): void - { - action THROW_NEW("java.lang.NullPointerException", []); - } - - - @AutoInline @Phantom proc _throwISE (): void - { - action THROW_NEW("java.lang.IllegalStateException", []); - } - - - proc _getFence (): int - { - // JDK comment: initialize fence to size on first use - if (this.fence < 0) - { - action ASSUME(this.parent != null); - this.fence = LongStreamAutomaton(this.parent).length; - } - result = this.fence; - } - - - proc _hasCharacteristics (_characteristics: int): boolean - { - result = (this.characteristics & _characteristics) == _characteristics; - } - - - // methods - - fun *.characteristics (@target self: LongStreamLSLSpliterator): int - { - result = this.characteristics; - } - - - fun *.trySplit (@target self: LongStreamLSLSpliterator): Spliterator_OfLong - { - val hi: int = _getFence(); - val lo: int = this.index; - val mid: int = (lo + hi) >>> 1; - - if (lo >= mid) - result = null; - else - result = new LongStreamSpliteratorAutomaton(state = Initialized, - parent = this.parent, - index = lo, - fence = mid, - characteristics = this.characteristics, - ); - - this.index = mid; - } - - - fun *.forEachRemaining (@target self: LongStreamLSLSpliterator, _action: LongConsumer): void - { - if (_action == null) - _throwNPE(); - - action ASSUME(this.parent != null); - val a: array = LongStreamAutomaton(this.parent).storage; - - var hi: int = this.fence; - var i: int = this.index; - this.index = hi; - - action LOOP_FOR( - i, i, hi, +1, - forEachRemaining_LongConsumer_loop(i, a, _action) - ); - } - - - @Phantom proc forEachRemaining_LongConsumer_loop (i: int, a: array, _action: LongConsumer): void - { - val item: long = a[i]; - action CALL(_action, [item]); - } - - - fun *.forEachRemaining (@target self: LongStreamLSLSpliterator, _action: Consumer): void - { - if (_action == null) - _throwNPE(); - - action ASSUME(this.parent != null); - val a: array = LongStreamAutomaton(this.parent).storage; - - var hi: int = this.fence; - var i: int = this.index; - this.index = hi; - - action LOOP_FOR( - i, i, hi, +1, - forEachRemaining_Consumer_loop(i, a, _action) - ); - } - - - @Phantom proc forEachRemaining_Consumer_loop (i: int, a: array, _action: Consumer): void - { - val item: long = a[i]; - action CALL(_action, [item]); - } - - - fun *.tryAdvance (@target self: LongStreamLSLSpliterator, _action: LongConsumer): boolean - { - if (_action == null) - _throwNPE(); - - val hi: int = _getFence(); - val i: int = this.index; - - if (i < hi) - { - action ASSUME(this.parent != null); - - this.index = i + 1; - - val parentStorage: array = LongStreamAutomaton(this.parent).storage; - val item: long = parentStorage[i]; - action CALL(_action, [item]); - - result = true; - } - else - { - result = false; - } - } - - - fun *.tryAdvance (@target self: LongStreamLSLSpliterator, _action: Consumer): boolean - { - if (_action == null) - _throwNPE(); - - val hi: int = _getFence(); - val i: int = this.index; - - if (i < hi) - { - action ASSUME(this.parent != null); - - this.index = i + 1; - - val parentStorage: array = LongStreamAutomaton(this.parent).storage; - val item: long = parentStorage[i]; - action CALL(_action, [item]); - - result = true; - } - else - { - result = false; - } - } - - - fun *.estimateSize (@target self: LongStreamLSLSpliterator): long - { - result = _getFence() - this.index; - } - - - fun *.getComparator (@target self: LongStreamLSLSpliterator): Comparator - { - if (_hasCharacteristics(SPLITERATOR_SORTED)) - result = null; - else - _throwISE(); - } - - - fun *.getExactSizeIfKnown (@target self: LongStreamLSLSpliterator): long - { - result = _getFence() - this.index; - } - - - fun *.hasCharacteristics (@target self: LongStreamLSLSpliterator, _characteristics: int): boolean - { - result = _hasCharacteristics(_characteristics); - } -} \ No newline at end of file diff --git a/spec/java/util/stream/LongStream.lsl b/spec/java/util/stream/LongStream.lsl index bc12db95..e4b3aeb0 100644 --- a/spec/java/util/stream/LongStream.lsl +++ b/spec/java/util/stream/LongStream.lsl @@ -40,12 +40,3 @@ import java/util/Spliterator; for PrimitiveIterator_OfLong { } - - -@GenerateMe -@implements("java.util.Spliterator.OfLong") -@public type LongStreamLSLSpliterator - is java.util.LongStreamLSLSpliterator - for Spliterator_OfLong -{ -} \ No newline at end of file diff --git a/spec/java/util/stream/LongStream.main.lsl b/spec/java/util/stream/LongStream.main.lsl index 5209f990..d077a2b4 100644 --- a/spec/java/util/stream/LongStream.main.lsl +++ b/spec/java/util/stream/LongStream.main.lsl @@ -87,7 +87,7 @@ automaton LongStreamAutomaton var linkedOrConsumed: boolean = false; // utilities - + @AutoInline @Phantom proc _checkConsumed (): void { if (this.linkedOrConsumed) @@ -99,7 +99,7 @@ automaton LongStreamAutomaton { this.linkedOrConsumed = true; } - + @AutoInline @Phantom proc _throwNPE (): void { @@ -136,12 +136,12 @@ automaton LongStreamAutomaton { if (this.length == 0) { - result = action DEBUG_DO("OptionalLong.empty()"); + result = action CALL_METHOD(null as OptionalLong, "empty", []); } else { val first: long = this.storage[0]; - result = action DEBUG_DO("OptionalLong.of(first)"); + result = action CALL_METHOD(null as OptionalLong, "of", [first]); } } @@ -661,7 +661,7 @@ automaton LongStreamAutomaton if (this.length == 0) { - result = action DEBUG_DO("OptionalLong.empty()"); + result = action CALL_METHOD(null as OptionalLong, "empty", []); } else if (this.length > 0) { @@ -673,7 +673,7 @@ automaton LongStreamAutomaton _accumulate_optional_loop(i, accumulator, value) ); - result = action DEBUG_DO("OptionalLong.of(value)"); + result = action CALL_METHOD(null as OptionalLong, "of", [value]); } _consume(); @@ -724,7 +724,7 @@ automaton LongStreamAutomaton if (this.length == 0) { - result = action DEBUG_DO("OptionalLong.empty()"); + result = action CALL_METHOD(null as OptionalLong, "empty", []); } else { @@ -736,7 +736,7 @@ automaton LongStreamAutomaton _find_min_loop(i, min) ); - result = action DEBUG_DO("OptionalLong.of(min)"); + result = action CALL_METHOD(null as OptionalLong, "of", [min]); } _consume(); @@ -756,7 +756,7 @@ automaton LongStreamAutomaton if (this.length == 0) { - result = action DEBUG_DO("OptionalLong.empty()"); + result = action CALL_METHOD(null as OptionalLong, "empty", []); } else { @@ -768,7 +768,7 @@ automaton LongStreamAutomaton _find_max_loop(i, max) ); - result = action DEBUG_DO("OptionalLong.of(max)"); + result = action CALL_METHOD(null as OptionalLong, "of", [max]); } _consume(); @@ -907,12 +907,11 @@ automaton LongStreamAutomaton { _checkConsumed(); - val default_characteristics: int = SPLITERATOR_ORDERED | SPLITERATOR_IMMUTABLE | SPLITERATOR_SIZED | SPLITERATOR_SUBSIZED; - result = new LongStreamSpliteratorAutomaton(state = Initialized, - parent = self, + result = new Spliterators_LongArraySpliteratorAutomaton(state = Initialized, + array = this.storage, index = 0, fence = this.length, - characteristics = default_characteristics, + characteristics = SPLITERATOR_ORDERED | SPLITERATOR_IMMUTABLE | SPLITERATOR_SIZED | SPLITERATOR_SUBSIZED, ); _consume(); @@ -1197,13 +1196,13 @@ automaton LongStreamAutomaton if (this.length == 0) { - result = action DEBUG_DO("OptionalDouble.empty()"); + result = action CALL_METHOD(null as OptionalDouble, "empty", []); } else { var curSum: double = _sum(); var divisionResult: double = curSum / this.length; - result = action DEBUG_DO("OptionalDouble.of(divisionResult)"); + result = action CALL_METHOD(null as OptionalDouble, "of", [divisionResult]); } _consume(); diff --git a/spec/java/util/stream/Stream.Spliterator.lsl b/spec/java/util/stream/Stream.Spliterator.lsl deleted file mode 100644 index 762bf116..00000000 --- a/spec/java/util/stream/Stream.Spliterator.lsl +++ /dev/null @@ -1,182 +0,0 @@ -libsl "1.1.0"; - -library std - version "11" - language "Java" - url "https://github.com/openjdk/jdk11/blob/master/src/java.base/share/classes/java/util/Spliterators.java"; - -// imports - -import java/util/stream/Stream; -import java/util/function/Consumer; -import java/util/Spliterator; -import java/util/Comparator; - - -// #note - base class of this specification was: "java.util.Spliterators$ArraySpliterator"; -// automata - -automaton StreamSpliteratorAutomaton -( - var parent: StreamLSL, - var characteristics: int = 0, - var fence: int = -1, - var index: int = 0 -) -: StreamLSLSpliterator -{ - // states and shifts - - initstate Initialized; - - shift Initialized -> self by [ - characteristics, - trySplit, - forEachRemaining, - tryAdvance, - estimateSize, - getComparator, - getExactSizeIfKnown, - hasCharacteristics, - ]; - - - // utilities - - @AutoInline @Phantom proc _throwNPE (): void - { - action THROW_NEW("java.lang.NullPointerException", []); - } - - - @AutoInline @Phantom proc _throwISE (): void - { - action THROW_NEW("java.lang.IllegalStateException", []); - } - - - proc _getFence (): int - { - // JDK comment: initialize fence to size on first use - if (this.fence < 0) - { - action ASSUME(this.parent != null); - this.fence = StreamAutomaton(this.parent).length; - } - result = this.fence; - } - - - proc _hasCharacteristics (_characteristics: int): boolean - { - result = (this.characteristics & _characteristics) == _characteristics; - } - - - // methods - - fun *.characteristics (@target self: StreamLSLSpliterator): int - { - result = this.characteristics; - } - - - fun *.trySplit (@target self: StreamLSLSpliterator): Spliterator - { - val hi: int = _getFence(); - val lo: int = this.index; - val mid: int = (lo + hi) >>> 1; - - if (lo >= mid) - result = null; - else - result = new StreamSpliteratorAutomaton(state = Initialized, - parent = this.parent, - index = lo, - fence = mid, - characteristics = this.characteristics, - ); - - this.index = mid; - } - - - fun *.forEachRemaining (@target self: StreamLSLSpliterator, _action: Consumer): void - { - if (_action == null) - _throwNPE(); - - action ASSUME(this.parent != null); - val a: array = StreamAutomaton(this.parent).storage; - - var hi: int = this.fence; - var i: int = this.index; - this.index = hi; - - action LOOP_FOR( - i, i, hi, +1, - forEachRemaining_loop(i, a, _action) - ); - } - - - @Phantom proc forEachRemaining_loop (i: int, a: array, _action: Consumer): void - { - val item: Object = a[i]; - action CALL(_action, [item]); - } - - - fun *.tryAdvance (@target self: StreamLSLSpliterator, _action: Consumer): boolean - { - if (_action == null) - _throwNPE(); - - val hi: int = _getFence(); - val i: int = this.index; - - if (i < hi) - { - action ASSUME(this.parent != null); - - this.index = i + 1; - - val parentStorage: array = StreamAutomaton(this.parent).storage; - val item: Object = parentStorage[i]; - action CALL(_action, [item]); - - result = true; - } - else - { - result = false; - } - } - - - fun *.estimateSize (@target self: StreamLSLSpliterator): long - { - result = _getFence() - this.index; - } - - - fun *.getComparator (@target self: StreamLSLSpliterator): Comparator - { - if (_hasCharacteristics(SPLITERATOR_SORTED)) - result = null; - else - _throwISE(); - } - - - fun *.getExactSizeIfKnown (@target self: StreamLSLSpliterator): long - { - result = _getFence() - this.index; - } - - - fun *.hasCharacteristics (@target self: StreamLSLSpliterator, _characteristics: int): boolean - { - result = _hasCharacteristics(_characteristics); - } -} \ No newline at end of file diff --git a/spec/java/util/stream/Stream.lsl b/spec/java/util/stream/Stream.lsl index 7506963d..92bc9e25 100644 --- a/spec/java/util/stream/Stream.lsl +++ b/spec/java/util/stream/Stream.lsl @@ -48,12 +48,3 @@ import java/util/Spliterator; for Iterator { } - - -@GenerateMe -@implements("java.util.Spliterator") -@public type StreamLSLSpliterator - is java.util.StreamLSLSpliterator - for Spliterator -{ -} \ No newline at end of file diff --git a/spec/java/util/stream/Stream.main.lsl b/spec/java/util/stream/Stream.main.lsl index a93ebb89..b4907c4d 100644 --- a/spec/java/util/stream/Stream.main.lsl +++ b/spec/java/util/stream/Stream.main.lsl @@ -29,7 +29,9 @@ import java/util/stream/Collector; import java/util/stream/DoubleStream; import java/util/stream/IntStream; import java/util/stream/LongStream; + import java/util/stream/Stream; +import java/util/Spliterators; // automata @@ -151,12 +153,12 @@ automaton StreamAutomaton { if (this.length == 0) { - result = action DEBUG_DO("Optional.empty()"); + result = action CALL_METHOD(null as Optional, "empty", []); } else { val first: Object = this.storage[0]; - result = action DEBUG_DO("Optional.ofNullable(first)"); + result = action CALL_METHOD(null as Optional, "ofNullable", [first]); } } @@ -791,7 +793,7 @@ automaton StreamAutomaton if (this.length == 0) { - result = action DEBUG_DO("Optional.empty()"); + result = action CALL_METHOD(null as Optional, "empty", []); } else if (this.length > 0) { @@ -803,7 +805,7 @@ automaton StreamAutomaton _accumulate_optional_loop(i, accumulator, value) ); - result = action DEBUG_DO("Optional.ofNullable(value)"); + result = action CALL_METHOD(null as Optional, "ofNullable", [value]); } _consume(); @@ -908,7 +910,7 @@ automaton StreamAutomaton if (this.length == 0) { - result = action DEBUG_DO("Optional.empty()"); + result = action CALL_METHOD(null as Optional, "empty", []); } else { @@ -920,7 +922,7 @@ automaton StreamAutomaton _find_min_loop(i, comparator, min) ); - result = action DEBUG_DO("Optional.ofNullable(min)"); + result = action CALL_METHOD(null as Optional, "ofNullable", [min]); } _consume(); @@ -943,7 +945,7 @@ automaton StreamAutomaton if (this.length == 0) { - result = action DEBUG_DO("Optional.empty()"); + result = action CALL_METHOD(null as Optional, "empty", []); } else { @@ -955,7 +957,7 @@ automaton StreamAutomaton _find_max_loop(i, comparator, max) ); - result = action DEBUG_DO("Optional.ofNullable(max)"); + result = action CALL_METHOD(null as Optional, "ofNullable", [max]); } _consume(); @@ -1095,13 +1097,13 @@ automaton StreamAutomaton { _checkConsumed(); - val default_characteristics: int = SPLITERATOR_ORDERED | SPLITERATOR_SIZED | SPLITERATOR_SUBSIZED; - result = new StreamSpliteratorAutomaton(state = Initialized, - parent = self, + result = new Spliterators_ArraySpliteratorAutomaton(state = Initialized, + array = this.storage, index = 0, fence = this.length, - characteristics = default_characteristics, + characteristics = SPLITERATOR_ORDERED | SPLITERATOR_SIZED | SPLITERATOR_SUBSIZED, ); + _consume(); } diff --git a/spec/java/util/zip/CRC32.automaton.lsl b/spec/java/util/zip/CRC32.automaton.lsl index 3d59559c..127e1d52 100644 --- a/spec/java/util/zip/CRC32.automaton.lsl +++ b/spec/java/util/zip/CRC32.automaton.lsl @@ -26,7 +26,7 @@ automaton CRC32Automaton shift Allocated -> Initialized by [ // constructors - CRC32, + ``, ]; shift Initialized -> self by [ @@ -95,7 +95,7 @@ automaton CRC32Automaton // constructors - constructor *.CRC32 (@target self: CRC32) + constructor *.`` (@target self: CRC32) { // original constructor is empty } diff --git a/spec/jdk/internal/misc/VM.lsl b/spec/jdk/internal/misc/VM.lsl new file mode 100644 index 00000000..734ea298 --- /dev/null +++ b/spec/jdk/internal/misc/VM.lsl @@ -0,0 +1,58 @@ +//#! pragma: non-synthesizable +libsl "1.1.0"; + +library std + version "11" + language "Java" + url "https://github.com/openjdk/jdk11/blob/master/src/java.base/share/classes/jdk/internal/misc/VM.java"; + +// imports + +import java/lang/Object; +import java/lang/String; +import java/util/Map; +import java/util/Properties; + + +// primary semantic types + +type VM + is jdk.internal.misc.VM + for Object +{ + @static fun *.initLevel(): int; + @static fun *.initLevel(value: int): void; + + @static fun *.isModuleSystemInited(): boolean; + @static fun *.isBooted(): boolean; + @static fun *.isShutdown(): boolean; + + @static fun *.shutdown(): boolean; + + @static fun *.getSavedProperty(key: String): String; + @static fun *.getSavedProperties(): Map; + + @static fun *.saveAndRemoveProperties(props: Properties); + + @static fun *.initializeOSEnvironment(): void; + + @static fun *.getRuntimeArguments(): array; +} + + +val VM_JAVA_LANG_SYSTEM_INITED: int = 1; +val VM_MODULE_SYSTEM_INITED: int = 2; +val VM_SYSTEM_LOADER_INITIALIZING: int = 3; +val VM_SYSTEM_BOOTED: int = 4; +val VM_SYSTEM_SHUTDOWN: int = 5; + + +// global aliases and type overrides + +type LSLVM + is jdk.internal.misc.VM + for VM +{ + @private @static @volatile var initLevel: int = 0; // WARNING: do not rename or change the type! +} + diff --git a/spec/jdk/internal/misc/VM.main.lsl b/spec/jdk/internal/misc/VM.main.lsl new file mode 100644 index 00000000..23b8cea4 --- /dev/null +++ b/spec/jdk/internal/misc/VM.main.lsl @@ -0,0 +1,235 @@ +//#! pragma: non-synthesizable +libsl "1.1.0"; + +library std + version "11" + language "Java" + url "https://github.com/openjdk/jdk11/blob/master/src/java.base/share/classes/jdk/internal/misc/VM.java"; + +// imports + +import java/lang/Class; +import java/lang/ClassLoader; +import java/lang/Object; +import java/lang/String; +import java/util/Map; +import java/util/Properties; + +import jdk/internal/misc/VM; + + +// automata + +automaton VMAutomaton +( +) +: LSLVM +{ + // states and shifts + + initstate Initialized; + + shift Initialized -> self by [ + // static operations + addFinalRefCount, + awaitInitLevel, + getFinalRefCount, + getNanoTimeAdjustment, + getPeakFinalRefCount, + getRuntimeArguments, + getSavedProperties, + getSavedProperty, + getegid, + geteuid, + getgid, + getuid, + initLevel (), + initLevel (int), + initializeFromArchive, + initializeOSEnvironment, + isBooted, + isDirectMemoryPageAligned, + isModuleSystemInited, + isSetUID, + isShutdown, + isSystemDomainLoader, + latestUserDefinedLoader, + maxDirectMemory, + saveAndRemoveProperties, + shutdown, + toThreadState, + ]; + + // internal variables + + // utilities + + // constructors + + // static methods + + @static fun *.addFinalRefCount (n: int): void + { + action TODO(); + } + + + @throws(["java.lang.InterruptedException"]) + @static fun *.awaitInitLevel (value: int): void + { + action TODO(); + } + + + @static fun *.getFinalRefCount (): int + { + action TODO(); + } + + + @static fun *.getNanoTimeAdjustment (arg0: long): long + { + action TODO(); + } + + + @static fun *.getPeakFinalRefCount (): int + { + action TODO(); + } + + + @static fun *.getRuntimeArguments (): array + { + action TODO(); + } + + + @static fun *.getSavedProperties (): Map + { + action TODO(); + } + + + @static fun *.getSavedProperty (key: String): String + { + action TODO(); + } + + + @static fun *.getegid (): long + { + action TODO(); + } + + + @static fun *.geteuid (): long + { + action TODO(); + } + + + @static fun *.getgid (): long + { + action TODO(); + } + + + @static fun *.getuid (): long + { + action TODO(); + } + + + @static fun *.initLevel (): int + { + action TODO(); + } + + + @static fun *.initLevel (value: int): void + { + action TODO(); + } + + + @static fun *.initializeFromArchive (arg0: Class): void + { + action TODO(); + } + + + @static fun *.initializeOSEnvironment (): void + { + // doing nothing + } + + + @static fun *.isBooted (): boolean + { + action TODO(); + } + + + @static fun *.isDirectMemoryPageAligned (): boolean + { + action TODO(); + } + + + @static fun *.isModuleSystemInited (): boolean + { + action TODO(); + } + + + @static fun *.isSetUID (): boolean + { + action TODO(); + } + + + @static fun *.isShutdown (): boolean + { + action TODO(); + } + + + @static fun *.isSystemDomainLoader (loader: ClassLoader): boolean + { + action TODO(); + } + + + @static fun *.latestUserDefinedLoader (): ClassLoader + { + action TODO(); + } + + + @static fun *.maxDirectMemory (): long + { + action TODO(); + } + + + @static fun *.saveAndRemoveProperties (props: Properties): void + { + action TODO(); + } + + + @static fun *.shutdown (): void + { + action TODO(); + } + + + @static fun *.toThreadState (threadStatus: int): Thread_State + { + action TODO(); + } + + + // methods +} diff --git a/spec/jdk/internal/util/StaticProperty.lsl b/spec/jdk/internal/util/StaticProperty.lsl new file mode 100644 index 00000000..c7dcf78b --- /dev/null +++ b/spec/jdk/internal/util/StaticProperty.lsl @@ -0,0 +1,34 @@ +//#! pragma: non-synthesizable +libsl "1.1.0"; + +library std + version "11" + language "Java" + url "https://github.com/openjdk/jdk11/blob/master/src/java.base/share/classes/jdk/internal/util/StaticProperty.java"; + +// imports + +import java/lang/Object; +import java/lang/String; + + +// primary semantic types + +type StaticProperty + is jdk.internal.util.StaticProperty + for Object +{ + @static fun *.javaHome(): String; + + @static fun *.jdkSerialFilter(): String; + + @static fun *.userDir(): String; + + @static fun *.userHome(): String; + + @static fun *.userName(): String; +} + + +// global aliases and type overrides + diff --git a/spec/libsl/utils/SymbolicInputStream.lsl b/spec/libsl/utils/SymbolicInputStream.lsl new file mode 100644 index 00000000..2ca192d2 --- /dev/null +++ b/spec/libsl/utils/SymbolicInputStream.lsl @@ -0,0 +1,22 @@ +//#! pragma: non-synthesizable +libsl "1.1.0"; + +library std + version "11" + language "Java" + url "-"; + +// imports + +import java/io/InputStream; + + +// primary semantic types + +@GenerateMe +@extends("java.io.InputStream") +@public @final type SymbolicInputStream + is `libsl.utils.SymbolicInputStream` + for InputStream +{ +} diff --git a/spec/libsl/utils/SymbolicInputStream.main.lsl b/spec/libsl/utils/SymbolicInputStream.main.lsl new file mode 100644 index 00000000..1a341d73 --- /dev/null +++ b/spec/libsl/utils/SymbolicInputStream.main.lsl @@ -0,0 +1,311 @@ +libsl "1.1.0"; + +library std + version "11" + language "Java" + url "-"; + +// imports + +import libsl/utils/SymbolicInputStream; + + +// automata + +automaton SymbolicInputStreamAutomaton +( + val maxSize: int = 10000, + val supportMarks: boolean = false, +) +: SymbolicInputStream +{ + // states and shifts + + initstate Initialized; + + shift Initialized -> self by [ + // instance methods + available, + close, + mark, + markSupported, + read (SymbolicInputStream), + read (SymbolicInputStream, array), + read (SymbolicInputStream, array, int, int), + readAllBytes, + readNBytes (SymbolicInputStream, array, int, int), + readNBytes (SymbolicInputStream, int), + reset, + skip, + transferTo, + ]; + + // internal variables + + @volatile var dataSize: int = -1; + @volatile var data: array = null; + @volatile var closed: boolean = false; + @volatile var pos: int = 0; + var markPos: int = -1; + var markLimit: int = 0; + + + // utilities + + // #todo: enable synchronization when parallel execution will be added + /* @synchronized */ proc _initBuffer (): void + { + if (this.data == null) + { + action ASSUME(this.maxSize > 0); + + // choose a new size + val newSize: int = action SYMBOLIC("int"); + action ASSUME(0 <= newSize); + action ASSUME(newSize < this.maxSize); + this.dataSize = newSize; + + // "allocating" memory and "filling" it up with fake data + if (newSize == 0) + this.data = action ARRAY_NEW("byte", 0); + else + this.data = action SYMBOLIC_ARRAY("byte", newSize); + + action ASSUME(this.data != null); + action ASSUME(this.dataSize == action ARRAY_SIZE(this.data)); + } + } + + @AutoInline @Phantom proc _checkBuffer (): void + { + if (this.data == null) + _initBuffer(); // this should be a call because we do not have synchronization blocks yet + + action ASSUME(this.dataSize >= 0); + } + + + @throws(["java.io.IOException"]) // NOTE: useful for enhanced auto-inlining + @AutoInline @Phantom proc _ensureOpen (): void + { + if (this.closed) + action THROW_NEW("java.io.IOException", ["Stream closed"]); + + _checkBuffer(); + } + + + proc _checkFromIndexSize (fromIndex: int, size: int, length: int): void + { + // source: jdk.internal.util.Preconditions#checkFromIndexSize + if ((length | fromIndex | size) < 0 || size > length - fromIndex) + action THROW_NEW("java.lang.IndexOutOfBoundsException", ["Range [%s, %= this.markLimit) + this.markPos = -1; + } + + + proc _moveDataTo (dest: array, offset: int, count: int): int + { + result = 0; + + val available: int = this.dataSize - this.pos; + if (available != 0) + { + action ASSUME(available > 0); + // transfer everything if there are not enougth bytes left + if (available < count) + count = available; + + action ARRAY_COPY(this.data, this.pos, dest, offset, count); + _updatePosition(count); + + result = count; + } + } + + + // constructors + + // static methods + + // methods + + @throws(["java.io.IOException"]) + fun *.available (@target self: SymbolicInputStream): int + { + _ensureOpen(); + result = this.dataSize - this.pos; + } + + + @throws(["java.io.IOException"]) + fun *.close (@target self: SymbolicInputStream): void + { + this.closed = true; + } + + + // within java.io.InputStream + fun *.mark (@target self: SymbolicInputStream, readlimit: int): void + { + if (this.supportMarks) + { + this.markPos = this.pos; + this.markLimit = readlimit; + } + } + + + // within java.io.InputStream + fun *.markSupported (@target self: SymbolicInputStream): boolean + { + result = this.supportMarks; + } + + + @throws(["java.io.IOException"]) + fun *.read (@target self: SymbolicInputStream): int + { + _ensureOpen(); + result = -1; + } + + + @throws(["java.io.IOException"]) + // within java.io.InputStream + fun *.read (@target self: SymbolicInputStream, b: array): int + { + val len: int = action ARRAY_SIZE(b); + if (len == 0) + { + result = 0; + } + else + { + action ASSUME(len > 0); + _ensureOpen(); + result = _moveDataTo(b, 0, len); + } + } + + + @throws(["java.io.IOException"]) + fun *.read (@target self: SymbolicInputStream, b: array, off: int, len: int): int + { + _checkFromIndexSize(off, len, action ARRAY_SIZE(b)); + if (len == 0) + { + result = 0; + } + else + { + _ensureOpen(); + result = _moveDataTo(b, off, len); + } + } + + + @throws(["java.io.IOException"]) + fun *.readAllBytes (@target self: SymbolicInputStream): array + { + _ensureOpen(); + + if (this.pos == 0) + { + result = this.data; + _updatePosition(this.dataSize); + } + else if (this.pos == this.dataSize) + { + result = action ARRAY_NEW("byte", 0); + } + else + { + val len: int = this.dataSize - this.pos; + action ASSUME(len > 0); + + result = action ARRAY_NEW("byte", len); + _moveDataTo(result, 0, len); + } + } + + + @throws(["java.io.IOException"]) + fun *.readNBytes (@target self: SymbolicInputStream, b: array, off: int, len: int): int + { + _checkFromIndexSize(off, len, action ARRAY_SIZE(b)); + _ensureOpen(); + + if (len == 0) + result = 0; + else + result = _moveDataTo(b, off, len); + } + + + @throws(["java.io.IOException"]) + fun *.readNBytes (@target self: SymbolicInputStream, len: int): array + { + if (len < 0) + action THROW_NEW("java.lang.IllegalArgumentException", ["len < 0"]); + + _ensureOpen(); + if (len == 0) + { + result = action ARRAY_NEW("byte", 0); + } + else + { + result = action ARRAY_NEW("byte", len); + _moveDataTo(result, 0, len); + } + } + + + @throws(["java.io.IOException"]) + // within java.io.InputStream + fun *.reset (@target self: SymbolicInputStream): void + { + if (this.supportMarks) + { + _ensureOpen(); + if (this.markPos < 0) + action THROW_NEW("java.io.IOException", ["Resetting to invalid mark"]); + + this.pos = this.markPos; + } + else + { + action THROW_NEW("java.io.IOException", ["mark/reset not supported"]); + } + } + + + @throws(["java.io.IOException"]) + fun *.skip (@target self: SymbolicInputStream, n: long): long + { + _ensureOpen(); + result = 0L; + } + + + @throws(["java.io.IOException"]) + fun *.transferTo (@target self: SymbolicInputStream, out: OutputStream): long + { + if (out == null) + action THROW_NEW("java.lang.NullPointerException", []); + + _ensureOpen(); + result = 0L; + } + +} diff --git a/spec/libsl/utils/VoidInputStream.lsl b/spec/libsl/utils/VoidInputStream.lsl new file mode 100644 index 00000000..c38a9d1b --- /dev/null +++ b/spec/libsl/utils/VoidInputStream.lsl @@ -0,0 +1,22 @@ +//#! pragma: non-synthesizable +libsl "1.1.0"; + +library std + version "11" + language "Java" + url "https://github.com/openjdk/jdk11/blob/master/src/java.base/share/classes/java/io/InputStream.java#L81"; + +// imports + +import java/io/InputStream; + + +// primary semantic types + +@GenerateMe +@extends("java.io.InputStream") +@public @final type VoidInputStream + is `libsl.utils.VoidInputStream` + for InputStream +{ +} diff --git a/spec/libsl/utils/VoidInputStream.main.lsl b/spec/libsl/utils/VoidInputStream.main.lsl new file mode 100644 index 00000000..1ac24f28 --- /dev/null +++ b/spec/libsl/utils/VoidInputStream.main.lsl @@ -0,0 +1,195 @@ +libsl "1.1.0"; + +library std + version "11" + language "Java" + url "https://github.com/openjdk/jdk11/blob/master/src/java.base/share/classes/java/io/InputStream.java#L81"; + +// imports + +import libsl/utils/VoidInputStream; + + +// automata + +automaton VoidInputStreamAutomaton +( + @volatile var closed: boolean = false, +) +: VoidInputStream +{ + // states and shifts + + initstate Initialized; + + shift Initialized -> self by [ + // instance methods + available, + close, + mark, + markSupported, + read (VoidInputStream), + read (VoidInputStream, array), + read (VoidInputStream, array, int, int), + readAllBytes, + readNBytes (VoidInputStream, array, int, int), + readNBytes (VoidInputStream, int), + reset, + skip, + transferTo, + ]; + + // internal variables + + // utilities + + @throws(["java.io.IOException"]) // NOTE: useful for enhanced auto-inlining + @AutoInline @Phantom proc _ensureOpen (): void + { + if (this.closed) + action THROW_NEW("java.io.IOException", ["Stream closed"]); + } + + + proc _checkFromIndexSize (fromIndex: int, size: int, length: int): void + { + // source: jdk.internal.util.Preconditions#checkFromIndexSize + if ((length | fromIndex | size) < 0 || size > length - fromIndex) + action THROW_NEW("java.lang.IndexOutOfBoundsException", ["Range [%s, %): int + { + if (action ARRAY_SIZE(b) == 0) + { + result = 0; + } + else + { + _ensureOpen(); + result = -1; + } + } + + + @throws(["java.io.IOException"]) + fun *.read (@target self: VoidInputStream, b: array, off: int, len: int): int + { + _checkFromIndexSize(off, len, action ARRAY_SIZE(b)); + if (len == 0) + { + result = 0; + } + else + { + _ensureOpen(); + result = -1; + } + } + + + @throws(["java.io.IOException"]) + fun *.readAllBytes (@target self: VoidInputStream): array + { + _ensureOpen(); + result = action ARRAY_NEW("byte", 0); + } + + + @throws(["java.io.IOException"]) + fun *.readNBytes (@target self: VoidInputStream, b: array, off: int, len: int): int + { + _checkFromIndexSize(off, len, action ARRAY_SIZE(b)); + _ensureOpen(); + result = 0; + } + + + @throws(["java.io.IOException"]) + fun *.readNBytes (@target self: VoidInputStream, len: int): array + { + if (len < 0) + { + action THROW_NEW("java.lang.IllegalArgumentException", ["len < 0"]); + } + else + { + _ensureOpen(); + result = action ARRAY_NEW("byte", 0); + } + } + + + @throws(["java.io.IOException"]) + // within java.io.InputStream + @synchronized fun *.reset (@target self: VoidInputStream): void + { + action THROW_NEW("java.io.IOException", ["mark/reset not supported"]); + } + + + @throws(["java.io.IOException"]) + fun *.skip (@target self: VoidInputStream, n: long): long + { + _ensureOpen(); + result = 0L; + } + + + @throws(["java.io.IOException"]) + fun *.transferTo (@target self: VoidInputStream, out: OutputStream): long + { + if (out == null) + action THROW_NEW("java.lang.NullPointerException", []); + + _ensureOpen(); + result = 0L; + } + +} diff --git a/spec/libsl/utils/VoidOutputStream.lsl b/spec/libsl/utils/VoidOutputStream.lsl new file mode 100644 index 00000000..093f1115 --- /dev/null +++ b/spec/libsl/utils/VoidOutputStream.lsl @@ -0,0 +1,22 @@ +//#! pragma: non-synthesizable +libsl "1.1.0"; + +library std + version "11" + language "Java" + url "https://github.com/openjdk/jdk11/blob/master/src/java.base/share/classes/java/io/OutputStream.java#L67"; + +// imports + +import java/io/OutputStream; + + +// primary semantic types + +@GenerateMe +@extends("java.io.OutputStream") +@public @final type VoidOutputStream + is `libsl.utils.VoidOutputStream` + for OutputStream +{ +} diff --git a/spec/libsl/utils/VoidOutputStream.main.lsl b/spec/libsl/utils/VoidOutputStream.main.lsl new file mode 100644 index 00000000..2104bf4e --- /dev/null +++ b/spec/libsl/utils/VoidOutputStream.main.lsl @@ -0,0 +1,99 @@ +libsl "1.1.0"; + +library std + version "11" + language "Java" + url "https://github.com/openjdk/jdk11/blob/master/src/java.base/share/classes/java/io/OutputStream.java#L67"; + +// imports + +import libsl/utils/VoidOutputStream; + + +// automata + +automaton VoidOutputStreamAutomaton +( + @volatile var closed: boolean = false, +) +: VoidOutputStream +{ + // states and shifts + + initstate Initialized; + + shift Initialized -> self by [ + // instance methods + close, + flush, + write (VoidOutputStream, array), + write (VoidOutputStream, array, int, int), + write (VoidOutputStream, int), + ]; + + // internal variables + + // utilities + + @throws(["java.io.IOException"]) // NOTE: useful for enhanced auto-inlining + @AutoInline @Phantom proc _ensureOpen (): void + { + if (this.closed) + action THROW_NEW("java.io.IOException", ["Stream closed"]); + } + + + proc _checkFromIndexSize (fromIndex: int, size: int, length: int): void + { + // source: jdk.internal.util.Preconditions#checkFromIndexSize + if ((length | fromIndex | size) < 0 || size > length - fromIndex) + action THROW_NEW("java.lang.IndexOutOfBoundsException", ["Range [%s, %): void + { + if (b == null) + action THROW_NEW("java.lang.NullPointerException", []); + + _ensureOpen(); + } + + + @throws(["java.io.IOException"]) + fun *.write (@target self: VoidOutputStream, b: array, off: int, len: int): void + { + _checkFromIndexSize(off, len, action ARRAY_SIZE(b)); + _ensureOpen(); + } + + + @throws(["java.io.IOException"]) + fun *.write (@target self: VoidOutputStream, b: int): void + { + _ensureOpen(); + } + +} diff --git a/spec/sun/misc/VM.lsl b/spec/sun/misc/VM.lsl new file mode 100644 index 00000000..1dc96ad8 --- /dev/null +++ b/spec/sun/misc/VM.lsl @@ -0,0 +1,25 @@ +//#! pragma: non-synthesizable +libsl "1.1.0"; + +library std + version "11" + language "Java" + url "https://github.com/openjdk/jdk8/blob/master/jdk/src/share/classes/sun/misc/VM.java"; + +// imports + +import java/lang/Object; + + +// primary semantic types + +@interface type SUN_VM + is sun.misc.VM + for Object +{ + @static fun *.booted(): void; +} + + +// global aliases and type overrides + diff --git a/spec/sun/misc/Version.lsl b/spec/sun/misc/Version.lsl new file mode 100644 index 00000000..11ccf5bd --- /dev/null +++ b/spec/sun/misc/Version.lsl @@ -0,0 +1,25 @@ +//#! pragma: non-synthesizable +libsl "1.1.0"; + +library std + version "11" + language "Java" + url "https://github.com/openjdk/jdk8/blob/master/jdk/src/share/classes/sun/misc/Version.java.template"; + +// imports + +import java/lang/Object; + + +// primary semantic types + +@interface type SUN_Version + is sun.misc.Version + for Object +{ + @static fun *.init(): void; +} + + +// global aliases and type overrides + diff --git a/spec/translator.actions.lsl b/spec/translator.actions.lsl index 38167284..8373c84a 100644 --- a/spec/translator.actions.lsl +++ b/spec/translator.actions.lsl @@ -62,3 +62,10 @@ define action TODO (): void; // do nothing explicitly but detectable by the translator if needed define action DO_NOTHING (): void; + + +// usage example: action TYPE_OF("int") +// usage example: action TYPE_OF(obj) +define action TYPE_OF ( + valueOrName: any // string literal or a variable + ): any;