Changes on the development line (the branch "master") that do not fit the rules of the 3.1.x maintenance line (see "Release lines" in Compatibility); when and as which version they are released is open. What they mean for a project that moves from a 3.1.x release is described in Development line.
JaninoOption.JAVAC_COMPLIANCE, or the system property
org.codehaus.janino.javacCompliance=true, which sets the initial options of every compiler that is
created afterwards; an explicit options(...) call replaces them). The default is unchanged (the
compatibility mode). The compliance mode is incomplete by design; so far, it corrects three deviations: a
Boolean operand of || or && is unboxed, so that Z ||
true throws a NullPointerException for a null Z
(issue #40); overload resolution considers
variable arity methods only after boxing (JLS 15.12.2), so that f(1) invokes
f(Object), not f(int...); and assert statements are disabled unless
assertions are enabled for the class, through a synthetic field $assertionsDisabled like with
JAVAC. See Development
line. The record tests (InvalidCodeTest, LanguageSupportTest) and the
differential tests run in both modes. The class files of the compatibility mode are unchanged.
LegacyDifferentialTest, which compiles every recorded case of InvalidCodeTest and
LanguageSupportTest with JANINO 3.1.12, the reference of the compatibility mode, and compares the
behavior: where the compatibility mode differs from 3.1.12, the record states the behavior of 3.1.12
(legacy:) and the issue or deviation ID that explains the difference (id:), so that
an unrecorded difference, e.g. a regression, fails the test. 372 of the 925 recorded cases differ, each with
its correction. No change of the compiler.
LegacyDifferentialTest, are registered as the exceptions A-07
(a cast of the clone of an array to an unrelated array type, issue #58) and A-08 (a duplicate annotation on a
parameter, issue #61). No change of the compiler.
Boolean operand of || or && is unboxed also when the other
operand is a constant that determines the result, like with JAVAC: Z || true and Z &&
false throw a NullPointerException for a null Z; deviation
S-01, part of the compliance mode of issue
#103. The compatibility mode keeps the behavior of JANINO 3.1.12 (no exception), and its class files are
unchanged.
class P { class Q extends P { ... } }, also an anonymous
or local subclass of P in P) refers to its enclosing instance like with JAVAC:
P.this is the enclosing instance of Q, not Q itself (JLS 15.8.4), and
the simple name of a private member of P denotes the member of the enclosing
instance, because private members are not inherited (JLS 8.2); deviation S-11. As a
consequence, the compliance mode rejects the access to a private member of a superclass through
the subclass (L-20, L-45) and B.this for a superclass B of an enclosing class
(L-46), like JAVAC. In both modes, compiling P.this as a value of type P no longer
fails with an InternalCompilerException when the compiler's assertions are enabled. The class
files of the compatibility mode are unchanged.
Byte, Short or Character and a constant of type
int that is representable in its unboxed type give the unboxed type (z ? B : 5 is a
byte, not an int), and with a constant condition, the operands are subject to binary
numeric promotion (true ? s : c is an int, not a short); deviation
S-04. As a consequence, the compliance mode rejects code that is valid only with the type of the
compatibility mode (L-47), and accepts code that the compatibility mode rejects because of the type
int, e.g. byte x = z ? B : 5; (D-07). Also in the compliance mode, a conditional
expression with a constant condition is a constant expression only if all three operands are constant
(JLS 15.28), so that "a" + (true ? "b" : s) with a variable s is not an interned
constant, and such an expression is rejected where a constant expression is required (deviation S-12, L-48).
ExpressionDifferentialTest now generates such conditional expressions in the compliance mode.
The class files of the compatibility mode are unchanged.
copyOf(new int[] { 4 }) with copyOf(int...) and
copyOf(Number...), which Apache Calcite generates. Now the compliance mode applies variable arity
methods like JAVAC (JLS 15.12.2): in phases 1 and 2, a variable arity method is applicable like a fixed arity
method whose last parameter has the array type, and it competes with its parameter types; only in phase 3, the
arguments are expanded into an array. As a consequence, in the compliance mode, f(new int[] { 1
}) invokes f(int...) rather than f(Object), and f(null) passes
null to f(Object...) rather than an array with one element (deviation S-13);
f(1, new int[] { 1, 2 }) with f(Integer i, Object... a) is accepted (D-08), and
f(4) with f(int...) and f(Number...) is rejected as ambiguous (L-49),
like JAVAC. The class files of the compatibility mode are unchanged.
assert statement that refers to a parameter or a local variable was resolved
before the variable was known: assert o != null; with a parameter o was rejected
("Expression "o" is not an rvalue"), and where a field had the same name, the condition silently read the
field. Now such a condition is compiled like any other expression. The class files of the compatibility mode
are unchanged.
int i = 09; ("...; digit '9' not allowed in octal literal"), an initializer in an interface
("...; an interface cannot declare an initializer"), this(...) or super(...) that is
not the first statement of a constructor ("...; an explicit constructor invocation is only allowed as the
first statement of a constructor body"), a non-static field referenced from a static context ("...;
non-static field "x" cannot be referenced from a static context"), and a qualified type name like
java.foo.Bar that denotes no type ("...; no type "java.foo.Bar" found"). The typo in
"Duplication access modifier" is fixed ("Duplicate access modifier"). For cyclic inheritance, the check for a
circularity recursed until a StackOverflowError, which was reported as "Compilation unit is
nested too deeply" (JavaSourceClassLoader threw the StackOverflowError); now the
cycle is detected and reported as "Class circularity detected for "A"" (or "Interface circularity detected for
"I""). The same code is rejected as before, and the class files are unchanged.
InnerClasses entry and no EnclosingMethod attribute, so
that the reflection API did not recognize them: for class L {} and new Object() {}
in a method of P, getSimpleName() returned "P$L" and
"P$1" instead of "L" and "", isLocalClass() and
isAnonymousClass() returned false, and getEnclosingClass(),
getEnclosingMethod() and getEnclosingConstructor() returned null. Like
JAVAC, JANINO now writes the entry into the class file of the local or anonymous class and into that of the
enclosing class, and the EnclosingMethod attribute; the class flags are ACC_SUPER,
like with JAVAC, without the bit 0x0002, which is not a class flag. Unlike JAVAC, an anonymous
class keeps the flag ACC_FINAL, so that getModifiers() still returns
final and the default serialVersionUID of a serializable anonymous class is the
same as with earlier versions. The binary names of local classes are unchanged (P$L, where JAVAC
writes P$1L). The class files of local and anonymous classes and of the classes that enclose them
change accordingly; the instructions are unchanged.
strictfp class had the bit 0x0800 (ACC_STRICT, a method flag) in its
flags; it is clear now, like with JAVAC. The JVM ignored the bit, so the behavior is unchanged.
AbstractTraverser now descends into the initializers of local variables and fields and into the
values of array initializers; before, it visited such an rvalue only through traverseRvalue(), but
not its subordinate nodes (e.g. the operands, the arguments of a method invocation, or the body of an anonymous
class). Subclasses of AbstractTraverser see these nodes now. The traversers of
guessParameterNames() and of the effectively final analysis, which descended into initializers
themselves since 3.1.18 and 3.1.17, use the base class again; their results are unchanged. The class files are
unchanged.
0x0002 (private) and 0x0008 (static), which are not class
flags, and a member class had no ACC_SUPER; now they are as with JAVAC, except that a
protected member type keeps ACC_PROTECTED next to ACC_PUBLIC. The JVM
ignored these bits, and the reflection API reads the modifiers from the InnerClasses attribute,
which is unchanged; so the behavior is unchanged, including the default serialVersionUID of
serializable member classes. The class files of member types change accordingly; their size does not.
AbstractTraverser
did not visit the constants of an enum declaration (traverseEnumConstant() was never invoked), so
that its subclasses saw neither the arguments of an enum constant, nor its class body, nor its annotations. Now
it visits them. Subclasses of AbstractTraverser see these nodes now. JANINO's own traversers (the
effectively final analysis and guessParameterNames()) do not look into enum constants, so that
their results are unchanged. The class files are unchanged.
final local variable of a primitive type or of type String with a constant
initializer is now a constant variable (JLS 4.12.4), like with JAVAC, so that case k:,
byte b = k; and while (y) { return 1; } with final int k = 3; and
final boolean y = true; compile, (u + "b") == "ab" with
final String u = "a"; is true, and z ? b : k with a byte b
has the type byte (deviations S-17 and D-09). The variable stays a constant in a local or anonymous
class that uses it. The compatibility mode is unchanged. ConstantExpressionDifferentialTest now
generates such variables in the compliance mode.
true || z and false && z with a variable z are no longer
constant expressions (JLS 15.28 requires both operands to be constant), and neither are
null == null and "a" + null (the null literal is not a constant
expression), like with JAVAC: such
string concatenations are new strings instead of interned constants, static final boolean F = true ||
z; is initialized in the static initializer instead of getting a ConstantValue attribute,
case (false && z) ? 1 : 2: and while (true || z) { return 1; } without a
return after it are rejected, and a statement after while (null == null); compiles (deviations
S-14, S-19, L-50 and D-11). The compatibility mode is unchanged.
f(1) with f(int...) and
f(Object...) or f(Serializable...), and f() with
f(char...) and f(Object...), are rejected as ambiguous (deviation L-49). Like JAVAC,
the compliance mode compares the variable arity parameter types for as many parameters as the longer parameter
list or the arguments have, so that f(1) with f(int, Object...) and
f(int...) is ambiguous as well. The compatibility mode is unchanged.
t3(8) with
String t3(final int c) { class L { int c = 10; String l() { return "" + c; } } ... } reads the
field ("10") instead of the parameter, and such code compiles where the variable is not
effectively final (deviations S-15 and D-13). The compatibility mode is unchanged.
<clinit> had the access flags ACC_PUBLIC, ACC_STATIC
(0x0009), in an interface ACC_PUBLIC, ACC_STATIC, ACC_ABSTRACT
(0x0409); now it has ACC_STATIC (0x0008) only, like with JAVAC (JVMS
4.6). The JVM ignored the other flags, and neither the reflection API nor the default
serialVersionUID sees them, so the behavior is unchanged. The class files of all classes and
interfaces with a static initializer, an initialized static field or enum constants change in these two
bytes; their size does not.
this or the instantiation of an inner class in a static initializer or in the initializer of a
static field was reported as No current instance available in static method, although there is
no method; now the message names the context, No current instance available in static
initializer or ... in static field initializer, like for this. In a static
method, the message is unchanged. The same code is rejected as before.
Fourteen fixes in the compiler, and JAR files without the dummy signature. The most important fixes concern
code that compiled, but did not run as written: two local classes with the same name in one class got the same
binary name, so that the code used the wrong class, without any error (issue #143); super.m() for
a private or static method of the superclass failed at run time (issues #136 and
#150), and so did super.a(o, v) for a variable arity method that the superclass implements from an
interface (issue #160). The other fixes concern code that was rejected or crashed the compiler: invocations of
overloaded and variable arity methods (issues #127, #134, #147, #156 and #157), array types of parameterized
types (issue #138), static methods imported twice (issue #133), if statements with empty branches
and a condition of type Boolean (issue #153), and members inherited through an accessible subclass
(issue #154). No code that 3.1.19 compiled is rejected now.
Object... or
Serializable... was rejected when another argument of the invocation needs boxing or unboxing,
e.g. f(1, new int[] { 1, 2 }) with f(Integer i, Object... a) (Assignment
conversion not possible from type "int[]" to type "java.lang.Object[]"): JANINO passed the
int[] as the array itself. Now it passes the array as the only element of a, like
JAVAC (JLS 15.12.2.4). Only invocations that were rejected change; where JAVAC rejects the invocation as well,
e.g. with f(Integer i, Number... a), the message is the same as before. The class files of all
other code are unchanged.
List<Object>[], was rejected in several places: as the type of a
local variable or the return type of a method in a script (IDENTIFIER expected instead of
'['); as the type of a local variable in the first statement of a constructor and in a
for statement (Expression "java.util.List<Object>" is not an rvalue); as the
target type of a cast and the type of a lambda parameter (')' expected instead of '['); and as
the first type argument of the type of a local variable or of a cast, e.g. Map<List<String>[],
String> m (One of '> ,' expected instead of '['). The parser did not read the
brackets after the type arguments there. Now these declarations and casts compile, like with JAVAC; a lambda
expression is still not compiled (Compilation of lambda expression NYI). Only code that was
rejected changes; syntax errors, e.g. List<Object>[1] a, are reported with the same
message as before. The class files of all other code are unchanged.
import static
java.lang.Math.abs; twice, or with two static-import-on-demand declarations of the same class, made the
compiler fail with an internal compiler error when the method was invoked (Two non-abstract methods
"public static int java.lang.Math.abs(int)" have the same parameter types, declaring type and return
type): JANINO counted the method once per declaration. Now it counts each imported member once, and
the code compiles, like with JAVAC. Only code that failed with an internal compiler error changes; the class
files of all other code are unchanged.
null argument made the compiler fail with an internal
compiler error. A variable arity method whose trailing arguments do not convert to its component type counted
as applicable by fixed arity against the leading arguments only, e.g. m(int...) for
m(null, (short) 1, 'c') (ArrayIndexOutOfBoundsException); now such a method is
applicable by fixed arity only if there are as many arguments as parameters, and the invocation selects
m(Integer[], Short, int), like JAVAC. And the message Invocation of constructor/method with
argument type(s) ... is ambiguous failed for a null argument
(StringIndexOutOfBoundsException); now it names its type (null type), like the
message No applicable constructor/method found. Only invocations that failed with an internal
compiler error change, and those that were rejected as ambiguous because of such a method, e.g.
m(ia, b) (Integer[], Byte) with m(Number...) and
m(Integer[], Byte...); the class files of all other code are unchanged.
m(ia)
(int[]) with m(int...) and m(int[], Serializable...), or
m(new Object[0], (byte) 1, 1L) with m(Object[], byte, Long...) and
m(Object...) (ArrayIndexOutOfBoundsException, or Two non-abstract methods ...
have the same parameter types, declaring type and return type, depending on the order of the
declarations): JANINO compared the parameter types without comparing their numbers. Now both invocations are
reported as ambiguous (Invocation of constructor/method with argument type(s) ... is
ambiguous); JAVAC reports the second one as ambiguous as well, and selects m(int...) for
the first one, which JANINO applies only by variable arity. Only invocations that failed with an internal
compiler error change; the class files of all other code are unchanged.
super.m() for a
private method m() of the superclass P, in an inner, local, anonymous
or static member class that extends P and is declared in P, also as
Q.super.m() from a class nested in such a class Q, compiled into an invocation that
failed at run time with a NoSuchMethodError ('java.lang.String P.m()'): JANINO
compiles a private instance method as a static method m$, but invoked
m() through super by its original name and descriptor. Now the invocation calls the
method like the simple name m() does, and yields the result that JAVAC's code yields. Only code
that failed with a NoSuchMethodError changes; the class files of all other code are unchanged.
L in each of two methods of P, or a local class
and a member class with the same name, got the same binary name P$L, and only one class file was
kept. The code that used the other class failed at run time with a NoSuchMethodError, or used the
wrong class without any error. Now a local class gets a number only where its name would collide, like with
JAVAC (JLS 13.1): the first local class L is still named P$L, the second one
P$2L, and a local class L next to a member class L is named
P$1L; the member class keeps its name. The names of all other local classes are unchanged, and so
are the class files of all other code.
super.f("a", "b") with f(String... s),
was rejected (Assignment conversion not possible from type "String" to type "String[]"), and
super.f() without arguments made the compiler fail with an internal compiler error
(Operand stack underflow); likewise Outer.super.f(...) from an inner class and
Interface.super.f(...) for a default method. JANINO passed the arguments without collecting the
variable arity arguments into an array. Now it collects them, like JAVAC; super.f(null) still
passes null as the array, like JAVAC. Only code that was rejected or failed with an internal
compiler error changes; the class files of all other code are unchanged.
super.s() for a
static method s() of the superclass compiled into an invocation that failed at run
time with an IncompatibleClassChangeError (Expecting non-static method ...): JANINO
invoked the method with invokespecial on this. Outer.super.s() from an
inner class made the compiler fail with an internal compiler error
(ArrayIndexOutOfBoundsException). Now both invoke the method like the simple name
s() does, and yield the result that JAVAC's code yields. Only code that failed changes; the class
files of all other code are unchanged.
super.a(o, v)
for a variable arity method a(Object o, Object... v) that the superclass implements from an
interface, also from a default method, compiled into a class that failed: the JVM of Java 25 rejects it
(VerifyError: Bad invokespecial instruction: interface method to invoke is not in a direct
superinterface), older JVMs throw an IncompatibleClassChangeError at the invocation. JANINO
found
each of two variable arity methods with the same parameter types more specific than the other, so that
overload resolution selected the method of the interface instead of the method of the class. Now it selects
the method of the class, like with fixed arity and like JAVAC. Since the fix of issue #157, the same happened
with super.a(o, this, v), which was rejected before. Only code that failed changes; the class
files of all other code are unchanged.
if statement
whose branches are empty, with a condition of type Boolean, e.g. if (b) {},
if (b); or if (b) {} else {}, was rejected (Not a boolean
expression): JANINO evaluated the condition only for its side effects and did not unbox it. Now it
unboxes the condition, like with a non-empty branch and like JAVAC; with b == null, the
statement throws a NullPointerException. Only code that was rejected changes; the class files of
all other code are unchanged.
g(1, 2, 3) with g(int...) and g(double...),
f("s", 1) with f(Object, int...) and f(String, int...), or
new X(c, t) with the constructors X(IClass c, X t, IType... args) and
X(IClass c, IType... args): JANINO compared the component type of the variable arity parameter
of one method with the array type of the other. Now, when that comparison finds no most specific method,
the rule of JLS 15.12.2.5 for an invocation by variable arity selects it, like JAVAC. Like JAVAC, it compares
the variable arity parameter types for as many parameters as the longer parameter list or the arguments
have, so that m(1) with m(int, Object...) and m(int...) is reported
as ambiguous, as before, and m(1) with m(int, long...), m(int...) and
m(long...) selects m(int...)
(issue #169). Invocations that
compiled before select the same method as before, also g(1, 2) with g(int...)
and g(Object...), which JAVAC reports as ambiguous. Only code that was rejected changes; the
class files of all other code are unchanged.
public or
protected member of a class that is not accessible, inherited through an accessible subclass,
was rejected from another package, e.g. p.B.F, p.B.m(), super.m() or
the simple name F in a subclass of B, with public class B extends A
and a package-private class A ("p.A" is inaccessible from this package, No
applicable constructor/method found): JANINO required the class that declares the member to be
accessible. Now, by JLS 6.6.1, the type through which the member is accessed counts, and the class file
references the member through that type (JLS 13.1), like JAVAC. Invocations that compiled before select the
same method as before, also p.B.s("x") with B.s(Object) and
A.s(String), where JAVAC selects A.s(String). Only code that was rejected changes;
the class files of all other code are unchanged.
META-INF/DUMMY.SF, META-INF/DUMMY.DSA). The signature proved nothing, because the
certificate and its key are public, but the JVM verified it whenever it loaded JANINO: on each start of a
JVM, that took about 110 to 140 ms, a third of the time until JANINO had compiled its first class. Projects
that put JANINO into a fat JAR no longer need to exclude the signature files. The classes, the OSGi headers
and the automatic module names are unchanged; to check the integrity of the files, see
Security / integrity.
Five fixes in the compiler. The most important one: a qualified superclass method invocation
P.super.m() in an inner class that is itself a subclass of P invoked the method on
the wrong instance, without any error (issue #110, since 3.1.16). The other fixes concern code that did not
compile or did not run: an inner class whose superclass extends the enclosing class failed verification (issue
#97), a protected member type could not be used from a subclass in another package (issue #87),
conditional expressions with a constant condition in static final fields and annotations crashed
the compiler or were rejected (issue #113), and floating-point literals with a leading zero were rejected
(issue #96). No code that compiled before is rejected now.
09.5, 00.5, 07e1, 07f or
08., was rejected (e.g. ';' expected instead of '9.5'), because the scanner read
the digits after the leading zero as an octal integer literal. Now these literals compile like with JAVAC
(JLS 3.10.2), except for a literal with an underscore directly before the first digit 8 or
9, e.g. 0_9.5, which is still rejected. An integer literal with a leading zero and
the digit 8 or 9, e.g. 09 or 0189, is still rejected
with the same message as before. The class files of all other code are unchanged.
static final field of a class, e.g.
static final int K = DEBUG ? 1 : 2; with static final boolean DEBUG = false;,
made the compilation fail with an InternalCompilerException (with the compiler's assertions
enabled, an AssertionError): JANINO checks that the operand which the condition does not select
compiles, and did that without the code context of a method. The same held for such an expression as the
value of an annotation element in parentheses, @A((true ? 1 : 2)); without parentheses,
@A(true ? 1 : 2), and with the operator ||, @B(X || Y), the parser
rejected the element value, because it accepted only a conditional-and expression there. Now these
declarations compile like with JAVAC, and the field is a constant variable; an operand that does not
compile is still reported. The class files of all other code are unchanged.
P.super.m() (issue #22,
since 3.1.16) in an inner class of P that is itself a subclass of P, e.g.
class Q extends P in class P extends B, invoked the superclass method on the
instance of Q instead of the enclosing instance of Q; likewise in a class nested in
Q and in an anonymous or local subclass of P. Now the instance is the lexically
enclosing instance of class P, like with JAVAC. Only the class files of such invocations change;
the class files of all other code are unchanged.
class R extends Q with
class Q extends P in class P, compiled into a class that the JVM rejected
(VerifyError): the constructor of R passed its own, still uninitialized instance
instead of the enclosing instance of R to the constructor of Q, because JANINO
searched for the enclosing instance of the superclass starting with R itself, which is a
subclass of P. The same held for anonymous and local subclasses of Q in a method
of R. Now the search starts with the class that encloses R, like with JAVAC. Only
the class files of such constructors change; the class files of all other code are unchanged.
protected member
type (class, interface or enum) could not be used from a subclass of the enclosing type in another package,
although JANINO compiled the code: the JVM threw an IllegalAccessError, e.g. for
new ProtS() in package b; class B extends a.A { ... } with
protected static class ProtS in a.A. The JVM knows no protected classes and grants
access from another package only to a class whose flags contain ACC_PUBLIC; JAVAC sets it for a
protected member type, and so does JANINO now. Unlike JAVAC, JANINO keeps the flag
ACC_PROTECTED, which the JVM ignores, so that JANINO, when it reads such a class file, still sees
a protected type. The class files of protected member types change by this one bit;
those of all other code are unchanged. The modifiers that the reflection API reports are unchanged.
private member type
of a class in another package, e.g. java.util.ArrayList.Itr.class; JANINO compiles it, and the
JVM throws an IllegalAccessError when it resolves the reference. The behavior is unchanged.
protected member types (the flag
ACC_PUBLIC, issue #87), for the constructors of the inner classes of issue #97, which the JVM
rejected before, and for the invocations of issue #110, which invoked the method on the wrong instance
before; the code of issues #113 and #96 did not compile before. For all workloads of the benchmarks,
including JANINO's own sources and the code that Apache Spark generates, the class files are the same as with
3.1.18, byte for byte, and the compile time and the allocated memory are the same as with 3.1.18 within the
measurement error. The test suites of Apache Calcite and of Spark Catalyst pass with this release as with the
JANINO that the projects declare.
The first release of the 3.1.x maintenance line (see "Release lines" in Compatibility): one fix in the parameter name guessing of the evaluators, smaller JAVADOC JARs, and tests that use the API of JANINO the way Apache Spark and Apache Calcite do. The compiler itself is unchanged.
ExpressionEvaluator.guessParameterNames() and ScriptEvaluator.guessParameterNames()
did not find the names in the initializers of local variables and fields and in array initializers, e.g.
x in int y = x; return y; and a and b in
new int[] { a, b }. They find them now; a name that occurs only in such an initializer and
denotes a field of the superclass is now guessed to be a parameter, like the same name in a statement.
AbstractTraverser, which does not descend into such initializers either, is public API and stays
unchanged in the 3.1.x line, so that its subclasses see the same nodes as before.
DownstreamApiTest now also uses the API of JANINO the way Apache Calcite does
(ISimpleCompiler, IClassBodyEvaluator, ClassBodyEvaluator with a
Scanner, and a subclass of JavaSourceClassLoader); COMPATIBILITY.md lists the
members that Calcite uses.
guessParameterNames() (issue #76, above).
Local and anonymous classes can now access effectively final local variables (Java 8), and seven defects are
fixed that exist in 3.1.12 already: the type of array.clone(), local classes with modifiers, the
scope of the resource variables of try-with-resources statements, the access to protected members
of the superclass of an enclosing class, two internal compiler errors, and a throws clause whose
exception type has an all-uppercase name. No code that compiled before is rejected now, and the class files of
such code are unchanged, with two small exceptions (see the compatibility entry below). The known deviations
from JAVAC are documented in
Differences between Janino and
javac.
clone() of an
array had the type Object instead of the array type (JLS 10.7): int[] b = a.clone();
and a.clone().length were rejected, and a.clone()[0] was an internal compiler error.
The invocation is now followed by a cast to the array type, like with JAVAC; the class files of code with the
explicit cast, (int[]) a.clone(), are unchanged, and where the clone was used as an
Object, the class file contains one more checkcast instruction.
final class L {}, abstract class L {},
strictfp class L {}, @A class L {}) was a syntax error. The modifiers that JLS 14.3
allows are accepted now, and the modifiers and the annotations are written to the class file; other
modifiers (e.g. static, public) are rejected, like before.
o[0] with Object o) was an internal compiler error when the
expression was a method or constructor argument, e.g. String.valueOf(o[0]). It is now rejected
with "Subscript not allowed on non-array type", like in every other context.
try (R r = new R()) { ... }, was not accessible: neither in the
block, nor in the initializers of the following resources, nor in an anonymous or local class declared in
the block (Unknown variable or type "r"); only the Java 9 form with an existing variable,
try (r) { ... }, worked. The resource variable is now in scope from its declarator to the end
of the block, like with JAVAC; it is not accessible in the catch clauses and the
finally clause. The class files of code that compiled before are unchanged. A resource
variable with the name of a local variable in scope, which JAVAC rejects, keeps compiling; in the block, the
name now denotes the resource variable, like with a catch parameter of that name.
protected
member that an enclosing class inherits from a class in another package, accessed from an inner class
(in, P.this.in or p.in in class P extends FilterInputStream {
class Q { ... } }, P.this.clone(), registerAsParallelCapable() in an inner
class of a subclass of ClassLoader), compiled into a class that throws an
IllegalAccessError or fails to verify, because the JVM permits the access only to the subclass
itself. Like JAVAC, Janino now generates a synthetic static accessor method in the enclosing class
(access$000, access$002, ...) that the inner class invokes. Also, the access to
such a member through P.super (P.super.in), and from a static nested class or a
local class in a static method of the subclass, was rejected; it is accepted now, like by JAVAC. The class
files of code that compiled before are unchanged.
throws clause of a method or constructor whose simple name consists of uppercase letters only
(throws X, throws IOEXC) was taken for a type parameter and ignored, so the
catch of such an exception thrown by the method was rejected ("Catch clause is unreachable"),
and the Exceptions attribute of the method lacked the exception. Only a type parameter that
is declared as such is ignored now; the catch clause is reachable, and the attribute is
complete, like with JAVAC. For compatibility, two leniencies remain: an invocation of a method that
declares such an exception need neither catch nor declare it (see JAVAC_DIFFERENCES.md, section 3), and
an all-uppercase name in a throws clause that denotes no type is still ignored. The class
files of code that compiled before are unchanged, except that the Exceptions attribute of a
method or constructor with such a throws clause now lists the exception.
catch parameter or the variable of an enhanced
for statement of the enclosing method, constructor or initializer that is not declared
final, if the variable is effectively final (JLS 8.1.3, Java 8); before, such an access was
rejected ("Cannot access non-final local variable"). Janino applies a conservative rule: the variable has an
initializer, or is a parameter, and its name is not assigned, incremented or decremented anywhere in the
enclosing method, including nested classes; a variable without an initializer that is assigned once, and a
variable whose name is assigned in another block or in a nested class, are still rejected (see
JAVAC_DIFFERENCES.md, section 2). The generated code is the same as for a final variable; the
class files of code that compiled before are unchanged.
final loop variable of a basic for statement
(for (final int i = 0; i < n; ) { ... }) was an internal compiler error ("Synthetic field
"val$i" neither maps a synthetic field of an enclosing instance nor a local variable"); the variable is
found now, like a variable that is declared in the loop body. The same applies to a loop variable that is
effectively final (issue #24, above). The class files of code that compiled before are unchanged.
janino-benchmarks (not released) gained the workload SPARK_TPCDS: the
code that Apache Spark 4.2.0 generates for twelve TPC-DS queries, compiled the way Spark compiles it, so
that the compile time of such code can be compared between JANINO versions.
checkcast instruction where
the clone of an array was used as an Object (#58) and for the Exceptions attribute
of a method or constructor whose throws clause names an exception type with an all-uppercase
name (#65); the compilation of JANINO's own sources and of the code that Apache Spark generates yields the
same class files as with 3.1.16, byte for byte. Measured interleaved against 3.1.16 on JDK 25
(janino-benchmarks, with and without JIT), the compile time is unchanged within the measurement
error on all workloads, including the code that Spark generates; the memory that a compilation allocates
changes by less than 1 % (0.1 to 0.9 % more on the synthetic workloads, 0.8 % less on the Spark code).
Invalid code that JANINO accepts is still accepted
(issue #33).
DownstreamApiTest, which uses the API
of JANINO the way Spark does. New pages: CHANGES_SINCE_3.1.12.md, a summary of the changes since 3.1.12 for
projects that consider an upgrade, and COMPATIBILITY.md, what stays the same.
Two language features that Janino parsed, but did not compile, are implemented now: multi-catch clauses and qualified superclass method invocations. Most fixes concern conditional expressions, annotations, enums and invalid code that compiled into class files that the JVM rejects; all of the defects exist in 3.1.12 already, except #51 (a regression of 3.1.15) and #57 (since 3.1.13). The known deviations from JAVAC are documented in Differences between Janino and javac.
char operand and a negative byte or short constant
(e.g. z ? c : (short) -1) were rejected. They now have the type int, like with JAVAC;
before 3.1.15, some of them compiled with the wrong type char, or into code that failed at run
time. The same applies to an int constant that is not representable in the type of the other
operand (e.g. z ? b : 500 with a byte b), and to a byte,
short or char operand with a long, float or
double constant (e.g. z ? c : 1L), which were rejected before. The class files of
code that compiled before are unchanged.
"" + (true ? 1 : 2.0) was
"1" instead of "1.0", and Object o = true ? 1 : 2L; compiled into a
class file that the JVM rejects. As a consequence, such an expression is now rejected where its type does not
fit, like by JAVAC: byte b = true ? 1 : 2L; compiled before, with the value of the selected
operand.
byte, short or char and the other
operand has a different numeric type were rejected, e.g. z ? b : 5 (now of type
byte, like by JAVAC) and z ? s : c (now of type int). The types of
such expressions that compiled before are unchanged (see
issue #38).
P.s(), or an unqualified s(), in
a class P that implements I is accepted, as it was with interfaces from class
files.
abstract static, private abstract, abstract native,
abstract synchronized, abstract strictfp, final volatile), the same
interface twice in an implements or extends clause, the access to a
protected member of a class in another package through an expression whose type is neither the
accessing class nor a subclass of it (e.g. o.clone() with an Object o, JLS
6.6.2.1), the invocation of a protected constructor with new from another package
(JLS 6.6.2.2), and the use of a local variable that is not definitely assigned, e.g. one that is assigned only
in the body of a loop or in one branch of an if statement (some of these cases failed with an
InternalCompilerException before). The access through an expression that is cast to the
superclass (((Object) this).clone()), or through a local variable of the superclass type, is
rejected as well, like by JAVAC, although the JVM loaded such classes before.
do statement, or in the right operand of
&& or ||, compiled into class files that the JVM rejects ("Instruction type
does not match stack map", "Inconsistent stackmap frames"), e.g.
int x; do { x = 1; } while (z); return x;. The stack map frame of a branch target is now the
merge of all paths to it. In addition, a local variable that is assigned in the condition of a
while or for statement, or of a conditional expression, can now be read in the body
or in the operands (for (int i = 0; (x = i) < n; i++) { use(x); }), which failed with an
InternalCompilerException in all versions. The class files of code that compiled correctly
before are unchanged, except that the constant pool entries of a class with for statements may
be ordered differently.
class P { @Retention(RUNTIME) @interface A {} }) were
invisible at run time, because a member annotation type was compiled as an ordinary interface. It is now an
annotation type (A.class.isAnnotation()), and the InnerClasses attribute records
the implicit modifiers of member types (JLS 8.5.1, 9.5): Class.getModifiers() of a member
interface now includes static, abstract and interface, of a member
enum static and final, and of the member types of an interface public
and static; Class.isMemberClass(), getDeclaringClass() and
getSimpleName() work for member interfaces. A single-element annotation of a type without an
element value is rejected with a compile error instead of an AssertionError. Member
annotation types are now parsed like top-level ones: type parameters, an extends clause and
default or static methods are rejected, like by JAVAC, and a class that implements
a member annotation type must implement annotationType(). The class files of member interfaces,
member enums and member annotation types change accordingly; all other class files are unchanged.
Method.getParameterAnnotations() returned no annotations. The
RuntimeVisibleParameterAnnotations and RuntimeInvisibleParameterAnnotations
attributes are generated now; their num_parameters is the number of parameters of the method
descriptor, which includes the parameters that Janino prepends to the declared ones (see
JAVAC_DIFFERENCES.md). The class files of methods with parameter annotations change
accordingly.
public and static (JLS 9.5): new I.C() from a
static context was rejected ("No current instance available"), and the class files of such member types
lacked the public flag. An enum declared in an interface was rejected with a syntax
error.
enum E { A { String n() { return "a"; } }, ... }) were ignored: the methods declared
there did not override the methods of the enum, and an enum with an abstract method was rejected. Like with
JAVAC, a constant with a class body is now an instance of an anonymous subclass of the enum
(E$1, E.A.getClass() != E.class), with its own methods, fields, initializers and
member classes; an enum with an abstract method is implicitly abstract, and each of its constants must have
a class body that implements the method. The class files of enums change: an enum without constant class
bodies now has the final flag, like with JAVAC (JLS 8.9); otherwise, the class files of code that
compiled correctly before are unchanged.
catch (IOException | SQLException e), JLS7 14.20) are compiled now; before, they were parsed,
but rejected ("Multi-type CATCH parameters NYI"). Each alternative gets an exception table entry with the
common handler; the type of the parameter is the nearest common superclass of the alternatives (not their
least upper bound with the interfaces that they implement, see JAVAC_DIFFERENCES.md); the
parameter is implicitly final, and throw e; rethrows the alternatives, not the
superclass. Like JAVAC, Janino rejects an assignment to the parameter, and alternatives that are related by
subclassing. In addition, a catch parameter whose type is not a Throwable, e.g.
catch (Object e) or catch (java.io.Serializable e), is rejected with a compile
error; before, such code compiled into a class file that the JVM rejects. The class files of code that
compiled before are unchanged.
catch clause could access neither the catch parameter nor a local variable
that is declared before the try statement; the compiler failed
with an InternalCompilerException ("Synthetic field ... neither maps a synthetic field of an
enclosing instance nor a local variable").
Interface.super.meth() invokes a default method of a direct superinterface (JLS8 15.12.1;
invokespecial of the interface method, which requires target version 8 or later);
Outer.super.meth() invokes a superclass method of a lexically enclosing class from an inner
class; like JAVAC, Janino generates a synthetic static accessor method (access$001, ...) in the
enclosing class for that. Like JAVAC, Janino rejects an interface that is not a direct superinterface, or
that another direct supertype extends, an abstract method, and a class that is neither the current class nor
an enclosing class.
Outer.super.fld from an inner class was rejected ("Cannot cast"); it compiled
only within the class Outer itself.
final flag of enums (#44); the constant pool entries of classes with for statements
may be ordered differently (#57). The compilation of Janino's own sources yields the same instructions as with
3.1.15. Measured interleaved against 3.1.15, the compile time is unchanged within the measurement error, and
the memory that a compilation allocates grows by 0.2 to 1 %. Some code that Janino accepted, but JAVAC
rejects, is now rejected as well: the code of #54, which the JVM rejected anyway, except for the access to a
protected member through an expression of a superclass type, e.g.
((Object) this).clone(), which the JVM loaded in some cases; a constant conditional expression of
type long, float or double where a narrower type is required, e.g.
byte b = true ? 1 : 2L; (#55); the forms of a member annotation type that only an ordinary
interface permits, e.g. type parameters or an extends clause (#43); and a catch
parameter whose type is not a Throwable (#21), which the JVM rejected anyway. Other invalid code
that Janino accepts is still accepted (issue #33).
The first release of this fork on MAVEN Central, under the new group ID io.github.janino-lts (see
Packages); the Java packages, the OSGi bundle symbolic names and the automatic
module names are unchanged. Most fixes concern constant expressions, conversions and annotations; all of the
defects exist in 3.1.12 already.
a[i] += x or I += 2 with an Integer I, used as an
expression) was wrong, failed the verification, or crashed the compiler.
char and from char to short generated wrong opcodes, e.g.
(int) (char) b was -1 instead of 65535 for b == -1.
+ and - did not promote byte, short and
char to int, e.g. "" + (+'a') was "a" and
-Byte.MIN_VALUE was -128.
final fields of type
char with a constant initializer were rejected, and final fields of a wrapper or
other reference type with a constant initializer (e.g. static final Integer I = 5;) were rejected
or caused a ClassFormatError.
boolean,
byte, char and short constants of fields from class files were treated
as int constants, e.g. "" + A.F + A.C was "197" instead of
"truea".
long l() default 0; or
@Ann(l = 5)) could not be read through reflection.
null (e.g. null + "a") crashed the compiler.
int x; x++;); these are compile errors now.
cookFiles(...) methods of IMultiCookable did not name the files in compile errors,
and cookFiles(String[]) always failed with a NullPointerException.
NullPointerException when a directory could not be listed (e.g. an extension directory that is a
file or does not exist), in JarDirectoriesResourceFinder and
ClassLoaders.getFileResources().
StringResource(String, String, Charset) and
MapResourceFinder.addResource(String, String, Charset). The "jdk" SimpleCompiler (and
the "jdk" evaluators) no longer replace characters that the platform default charset cannot represent with
?.
org.codehaus.janino.Compiler) now reports the correct values of the
options -g:lines and -g:vars with -verbose.
janino-benchmarks
(issue #25), the compilation of Janino's own
sources is about 6 % faster than with 3.1.12 and allocates about 16 % less memory. Code with many nested
try / finally statements compiles somewhat slower than with 3.1.12, because the code
that Janino generates for them since 3.1.14 is correct, but larger.
byte b = -Byte.MIN_VALUE; (#39), a statement after while (A.F) {} with a
boolean constant true from a class file (#46), and the code of #30, which the JVM
rejected. Other invalid code that Janino accepts is still accepted
(issue #33). As a side effect of #45, the valid
conditional expressions true ? c : (short) -1 and false ? c : (short) -1 (with a
char c, also with other negative byte or short constants) are now
rejected; JAVAC accepts them with the type int. Before, they compiled with the wrong type
char, or into code that failed at run time. Fixed in 3.1.16
(issue #51).
A bug fix release for the code generation of try statements. Some of the defects are regressions
of 3.1.13, whose default target version 8 (was 6) disabled the JVM's fallback to its old bytecode verifier;
others exist in 3.1.12 already.
VerifyError when a
finally block cannot complete normally (e.g. try { return x; } finally { return y; }),
and VerifyErrors and internal compiler errors when a return, break or
continue statement leaves a try block whose finally block contains a
try statement, a synchronized statement, or a break or
continue statement. The return value is now saved in a local variable while the
finally block executes.
finally block was
executed twice when a catch clause handled an exception, and when the copy of a
finally block that is executed before a return, break or
continue statement threw an exception (which was also caught by the catch clauses of
the same try statement).
return, break or
continue, add the exceptions from close() as suppressed exceptions, and close the
resources before the catch clauses and the finally block are executed (JLS 14.20.3).
finally block was
executed twice (and the monitor released twice) when a jump statement left a synchronized
statement inside a try statement, and the finally block threw an exception.
VerifyErrors after a try statement whose try block cannot complete
normally, e.g. try { return Integer.parseInt(s); } catch (A e) { ... } catch (B e) { ... }
followed by more code.
org.codehaus.commons.compiler.Sandbox) is now deprecated; use
sandbox policies and the SandboxExecutor instead, which
work on all JREs from Java 8 on.
SandboxExecutor executes code under
SandboxLimits (timeout, number of "ticks", allocated memory). Janino inserts the checks of the
new class Guard into code that it compiles with a sandbox policy (at the beginning of every loop
body and every method and constructor body, and before array creations); once a limit is exceeded, the code
is stopped, even if it catches the SandboxLimitExceededError. Code compiled by the JDK-based
implementation is only subject to the timeout. See section 3.5 of
Restricting Untrusted Code with a
Sandbox Policy.
SandboxPolicy.Builder.requireExecutor(): The checks that Janino inserts throw an
IllegalStateException if the code is executed outside of a SandboxExecutor (e.g. a
static initializer when the application instantiates the generated class, or the toString()
method of a returned object), so that sandboxed code cannot run without limits by accident.
finalize() method, because the JVM calls it
on its finalizer thread, outside of any SandboxExecutor.
DeepCopier can now be compiled by Janino itself.
Sandbox.confine() can escape from the sandbox by calling
AccessController.doPrivileged() itself): The new method
ICookable.setProtectionDomain() defines the generated classes with a restricted protection
domain; see Security. The default behavior
is unchanged.
AccessController, AccessControlContext,
ProtectionDomain, Policy, Security and
javax.security.auth.Subject through class-wide rules.
Sandbox.confine() (e.g.
anonymous classes) are now defined with the same protection domain as the other generated classes. Before,
they got a different protection domain, which caused a SecurityException ("signer information
does not match") with the signed Janino JARs.
SandboxViolation.getLocation(); the CompileException is located at the first
violation, and all violations are reported through the compile error handler, if one is set. To determine the
line numbers, the compilers generate debugging information (source file and line numbers) automatically when a
sandbox policy is set. Location.toString() omits the column if it is 0 ("the entire line").
FUNCTIONAL, STREAMS, MATH,
REGEX, JAVA_TIME, TEXT and UTILITIES; see section 4.2 of
Restricting Untrusted Code with a
Sandbox Policy.
parallel() of IntStream, LongStream
and DoubleStream (only BaseStream.parallel() was on the list), and
ZoneRulesProvider, which changes JVM-global state.
setTargetVersion(8). The previous default
can be restored with -Dorg.codehaus.janino.UnitCompiler.defaultTargetVersion=6.
StackMapTable attribute for a do statement whose body ends with
a continue statement (VerifyError "Instruction type does not match stack map" with
target version 7 and higher).
ICookable.setSandboxPolicy(),
AbstractJavaSourceClassLoader.setSandboxPolicy(), and the new package org.codehaus.commons.compiler.sandbox
(SandboxPolicy, BytecodeVerifier, ...). Unlike the security-manager-based sandbox, a
sandbox policy never allows Class.forName().
org.codehaus.commons.compiler.Sandbox) now fails with a descriptive
UnsupportedOperationException (instead of an ExceptionInInitializerError) on JREs
without security manager support (Java 24 and later, and Java 18 through 23 without
-Djava.security.manager=allow). New method Sandbox.isSupported().
AbstractJavaSourceClassLoader.setTargetVersion(int).AccessController.doPrivileged() are now made through
org.codehaus.commons.compiler.util.Privileged, so that Janino keeps working on future JREs that no
longer provide that API.
In this release, many classes were moved from module janino to module commons-compiler,
thus their package declarations changed.
The "important" APIs, however, remain unchanged. All in all, it was decided to release this as a "minor" release.
?: operator no longer issues an error when the MHS is not assignable from the RHS and vice
versa; instead, the expression type is the "common base type" of the MHS and RHS.
java.lang.NoSuchMethodError: java.nio.CharBuffer.rewind()Ljava/nio/CharBuffer; Cast to "Buffer" is
the workaround.
enum.values()" and "enum.valueOf(String)".
After a relative long period of little development, new features appear on the horizon! Enums and annotations finally made it into JANINO...
META-INF/MANIFEST.MF file of the JANINO JARs.CompilerFactoryFactory.getSpecificationVersion() and
ICompilerFactory.getImplementationVersion().
CompileException was moved from package org.codehaus.janino to package
org.codehaus.commons.compiler (and so were its buddies Location,
LocatedException and CausedException).
ParseException and ScanException were removed; all methods that previously threw
one or more of CompileException, ParseException and ScanException now
throw only CompileException. (This is an API simplification honoring the fact that applications
rarely want to differentiate between scan, parse and compilation problems).
The core functionality of JANINO (expression evaluator, script evaluator, class body evaluator, simple compiler
and Java source class loader) was extracted to new interfaces declared in the new package
org.codehaus.commons.compiler, while the original API remains unchanged.
A second implementation of the org.codehaus.commons.compiler interfaces was added, based on the
javax.tools.JavaCompiler API that exists since JDK 1.6. This implementation is available in the
commons-compiler-jdk.jar file.
Rather than instantiating e.g. org.codehaus.janino.ExpressionEvaluator directly, applications can now
alternatively call
org.codehaus.commons.compiler.CompilerFactoryFactory.getDefaultCompilerFactory().newExpressionEvaluator(),
which will use the first implementation of IExpressionEvaluator on the class path (be it
janino.jar or commons-compiler-jdk.jar, or yet another implementation).
The JUNIT regression test suite was restructured so that if differentiates between implementation-independent tests, which are automatically executed against every implementation on the class path, and implementation-specific tests.
Also, the following bugs were fixed:
RuntimeException if cook() is called more than once.janino.jar is now signed with a dummy certificate.janino.jar is now published as http://www.janino.net/lib/janino.jar,
so remote systems can always download the latest version from a fixed URL.
import static
java.util.Collections.EMPTY_MAP), types (e.g. import static java.util.Map.Entry) and
methods (e.g. import static java.util.Arrays.asList)
import static
java.util.Collections.*), types (e.g. import static java.util.Map.*) and methods (e.g.
import static java.util.Arrays.*)
ExpressionEvaluator and ScriptEvaluator with the capability to compile more
than one expression/script in one evaluator. This greatly improves the runtime performance and reduces the JVM
resource usage if you need to compile many expressions/scripts at the same time.
ClassLoader that was set with setParentClassLoader(). In other words: The
expression/script/class body/compilation unit executes in a "sandbox", restricted by a ClassLoader
with minimal peepholes to the classes you specify. This also resolves issue JANINO-66: Security: restrict
package access
TunnelException with ClassNotFoundException(Throwable ex), which makes
exception handling much clearer. In retrospect, TunnelExeption has proven to be a bad concept.
Java.Xxx PUBLIC for AST manipulation.*Evaluators.Scanner.close() and all file-related Scanner constructors are now deprecated, because
(A) they sometimes left input files open and (B) the logic was confusing - it is now strictly the caller's
responsibility to open and close input files.
*Evaluator and the SimpleCompiler classes. Important
notice: ScriptEvaluator.evaluate() had to be renamed to
ScriptEvaluator.getClazz() due to a method signature clash with
ExpressionEvaluator.evaluate().
*Evaluator and the SimpleComplier classes.JaninoTests for class instance creation (basically JLS2 section 15.9.1).Scanner now warns about misplaced DOC comments -- hence we need to have a
WarningHandler installed also on the Scanner.
.class files could not be instantiated.Java class hierarchy -- basically the "uplink" of the AST objects is no
longer set by a constructor parameter, but by the constructor of the superordinate AST object. As a
consequence, the creation order of AST objects is no longer top-down, but bottom-up. This makes code that
creates an AST significantly shorter.
UnitCompiler is now context-free, i.e. it can resolve types, constant values etc.
without compiling the unit. This is necessary for the upcoming JGrep utility (see below).
JGrep utility -- a command line tool that analyses a set of source files
in a highly configurable way.
FormalParameter PUBLIC.Enumerator and EnumeratorSet classes, inspired by "Effective Java".CachingJavaSourceClassLoader.Compiler.compile(Resource[]).org.codehaus.janino.Compiler is now based on the new
ResourceCreator class, so they can not only be stored in files but anywhere (e.g. in a database).
(Requested by Andreas Schmidt 2005-10-19.)
Traverser for static code analysis.visit(...) methods to accept(...) for conformance with GoF "Design Patterns".
@deprecated.Java to UnitCompiler.
This involved some minor incompatible changes:
Java.CompileException to CompileException.Visitor to Visitor.ComprehensiveVisitor.CompilationUnit, VariableDeclarator and FormalParameter
from the visitors, because they are not virtual.
Scanner.Location a top-level class.PackageDeclaration and ImportDeclaration are now AST objects.ResourceFinder.findResource() no longer returns a URL object, but a more abstract
ResourceFinder.Resource object (requested by Michael Orth 2005-03-22).
ByteArrayClassLoader for thread safety.optionalParentClassLoader parameter of
all *Evaluator classes (see JAVADOC of EvaluatorBase(ClassLoader)).
equals()code> methods to ByteArrayClassLoader and EvaluatorBase.Scanner now allows for explicit initialization of line number and column number (useful if the Java code begins only in the middle of a file.)Iterators like
NestedIterator, TransformingIterator, and the IteratorCollection (a
Collection that reads its elements from a delegate Iterator while it is being
iterated for the first time).
net.janino.tools.Disassembler now has a verbose mode.IClasses.getConstantValue().BufferedReaders when reading from files.IClass now caches the results of its abstract methods.IClassLoader.loadIClass no longer throws ClassNotFoundExceptions, but returns null (save creation of a lot of objects).SimpleCompiler, ClassBodyEvaluator and ScriptEvaluator now add SOURCE
and LINES debugging info, so file name and line number appear in exceptions thrown by generated code.
(Requested by Mike Hebert 2004-09-11.)
optionalFileName parameter of net.janino.Scanner's constructors is null
and system property net.janino.source_debugging.enable is "true". The source code file is written
to net.janino.source_debugging.dir or the system's default temp directory. (You need to add that
directory to your debugger's "source lookup path".)
EvaluatorBase generated no output.net.janino.UnparseVisitor.net.janino.UnparseVisitor that unparses a parsed construct, e.g. a compilation
unit (requested by Mark Proctor).
net.janino.Java public to allow for generation of "synthetic"
constructs (requested by Mark Proctor).
net.janino.Scanner.LocatedException for all JANINO-related exceptions.net.janino.ExpressionEvaluator now supports null expression types; this auto-wraps
primitive values as necessary. (Requested by Amir Bar-Or 2004-05-31).
A major milestone... Janino now supports member types, local classes and anonymous classes. Therefore you can legitimally call a fully implemented JavaTM compiler!
A good time to open a bottle of champagne...
net.janino.Compiler now reports up to 20 compile errors before it gives up. (Notice that scanning,
parsing and I/O exceptions are still fatal and terminate compilation.)
NullPointerException in the AntCompilerAdapter.ExpressionEvaluator and the ScriptEvaluator.
Compiler and AntCompilerAdapter now provide for the
"extdirs" and "bootclasspath" features.
AntCompilerAdapter now honors most of the options that ANT passes to it.IClassLoader
used to load classes defined outside the current compilation unit are now stored in thread-local globals. The
IClassLoader also holds the formerly global IClass cache, which caused a memory leak (reported by
Fabrice Carnet 2003-05-20).
IClass.isAssignableFrom(net.janino.IClass)
did not handle primitive types (reported by Fabrice Carnet 2003-05-20).
ClassLoader, if
that is a ByteArrayClassLoader. This is
necessary if the expressions, scripts and classes need to refer to each other. (Requested by Fabirce Carnet
2003-05-20.)
java.lang.NullPointerException.
net.janino.ClassLoaderIClassLoader.ReflectionIClass holds a map that guarantees that
java.lang.Classes and IClasses map
one-to-one. This posed a memory leak. (Reported by Fabrice Carnet 2003-05-20.)
ClassBodyEvaluator.java.lang.ClassLoader used to load the just-compiled class now uses as its parent the current
thread's context class loader (reported by Fabrice Carnet 2003-03-26).
break and continue.