Ben Weidig created TAP5-2807:
--------------------------------
Summary: Plastic: NoClassDefFoundError: I (ClassNotFoundException:
I) when InstructionBuilder processes 'int' type information in certain sequences
Key: TAP5-2807
URL: https://issues.apache.org/jira/browse/TAP5-2807
Project: Tapestry 5
Issue Type: Bug
Components: plastic
Affects Versions: 5.9.0
Reporter: Ben Weidig
h2. Problem
Plastic's InstructionBuilder (or maybe the subsequent class
finalization/loading process) can lead to a java.lang.NoClassDefFoundError: I
at runtime.
This error originates from PlasticClassLoader, indicating an attempt to load a
class literally named "I" (the JVM's internal type descriptor for int).
Other primitives might be affected as well, but I encountered it while working
with int;
h2. Context & Trigger
I was trying to use the InstructionBuilder in PropertyConduitSourceImpl to
implement the RANGEOP for subexpressions, meaning support for expressions with
int primitives for the IntegerRange(int from, int to) constructor as part of a
list literal like "[true, 1..2]".
The arguments for the IntegerRange constructor were derived from a
sub-expression, requiring a sequence of InstructionBuilder calls to:
# Evaluate sub-expressions (e.g., an INTEGER node from ANTLR, initially
yielding a long).
# Box primitives if necessary (e.g., long to Long using
builder.boxPrimitive("long")).
# Potentially coerce object types to java.lang.Integer (e.g., using
builder.invoke() on a delegate method that takes int.class as a Class parameter
via builder.loadTypeConstant(int.class)).
# Ensure a java.lang.Integer object is on the stack (using
builder.checkcast(Integer.class)).
# Obtain an int primitive from the Integer object (e.g., using
builder.invoke(Integer.class.getMethod("intValue")) because
builder.unboxPrimitive("int") also triggered this error).
h2. Key Findings
*Simple Constructor Invocation Works:*
Generating bytecode to call new IntegerRange(1, 2) using
{code:java}
builder.loadConstant(1);
builder.loadConstant(2);
builder.invokeConstructor(IntegerRange.class, int.class, int.class);
{code}
does not trigger the NoClassDefFoundError: I.
This suggests InstructionBuilder.invokeConstructor() itself handles int.class
argument types correctly for descriptor generation.
*Error Linked to Type Handling Sequence:*
The NoClassDefFoundError: I only appears when the more complex sequence of
InstructionBuilder calls (boxing, coercion involving
loadTypeConstant(int.class), checkcasting, and obtaining the int primitive via
invoke(Integer.intValue())) is used to prepare arguments for the IntegerRange
constructor.
*Error Timing:*
Diagnostic System.out.println statements confirm that all InstructionBuilder
calls in the problematic sequence _appear_ to complete. The
NoClassDefFoundError: I occurs later, when the generated method (e.g., the
PropertyConduit.get()) is actually executed.
h2. Hypothesis
The NoClassDefFoundError: I suggests that one or more InstructionBuilder
methods, when processing int type information (either as a string like "int", a
Class object like int.class, or as a return/parameter type of an invoked
method), has an unintended side-effect.
This likely involves Plastic's internal type system or class finalization
process incorrectly interpreting the JVM descriptor "I" as a class name to be
loaded by PlasticClassLoader.
The specific InstructionBuilder methods involved in the failing sequence
include:
* builder.boxPrimitive(String primitiveTypeName): when primitiveTypeName is
"long" but the underlying issue might be general to how primitive type strings
are handled if "int" also causes it in other contexts
* builder.loadTypeConstant(Class primitiveClass): when primitiveClass is
int.class
* builder.invoke(Method method): when the method involves int.class as a
parameter type, or returns a primitive int like Integer.intValue()
* The previously attempted builder.unboxPrimitive("int") also led to the error.
h2. Impact
This potential bug in Plastic makes it difficult to reliably generate bytecode
that involves handling and converting various types to int primitives using the
InstructionBuilder API, hindering the implementation of features that require
such dynamic code generation.
In my case, I couldn't implement the RANGEOP for subexpressions.
h2. Steps to Reproduce
# Obtain an InstructionBuilder for a method.
# Execute a sequence of operations similar to the "Context & Trigger" section:
** Push a long primitive.
** Call builder.boxPrimitive("long").
** Call builder.loadTypeConstant(int.class).
** Simulate a coercion call that uses this int.class constant and results in
an Integer object.
** Call builder.checkcast(Integer.class).
** Call builder.invoke(Integer.class.getMethod("intValue")).
# Use the resulting int as an argument to another method or constructor.
# Instantiate and invoke the generated method.
# Observe java.lang.NoClassDefFoundError: I.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)