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)

Reply via email to