[Bug]: Spoon cannot transform file
- Dominant language
- Java
- Stars
- 2k
- Forks
- 392
- Avg merge
- 11h 24m
- Merged PRs (30d)
- 36
Description
### Describe the bug
I am using spoon to add comments to code blocks. For example when there is a "for" loop, I would add "start of for loop" to the code. I am using spoon's processor to achieve this. But when I run spoon processors on my files, I receive an error that I cannot understand. Do you have any idea what is going on?
### Source code you are trying to analyze/transform
```Java
public class Temp {
public void printHeaders()
{
int nbCols = columns.size();
for( int i = 0; i < nbCols; i++ )
columns.get( i ).hdrPrintsOn.print( null, new HdrInTreeTablePrinter( table, i ) );
}
@Override
public void print( Iterable data )
{
killCurrentEdit();
table.emptyTable();
if( data == null )
return;
// sort if needed
if( userComparator != null )
{
ArrayList tmp = new ArrayList();
for( T t : data )
{
int ins = Collections.binarySearch( tmp, t, userComparator );
if( ins >= 0 )
tmp.add( ins, t );
else
tmp.add( -ins - 1, t );
}
data = tmp;
}
// each row
for( T d : data )
{
Row row = table.addRow( null );
row.setRef( refMng.getRef( d ) );
printRow( d, row );
}
}
@Override
public void updateRow( T object )
{
// find the row associated with this object
int objectRef = refMng.getRef( object );
Row item = getRow( objectRef );
if( edition != null && refMng.getRef( edition.editedObject ) == objectRef )
killCurrentEdit();
Row insPos = userComparator != null ? getInsertPoint( object ) : item;
// insert or move the row to the right place and create a cell printer
if( item == null )
{
// new
if( insPos != null )
insPos = insPos.addBefore();
else
insPos = table.addRow( null );
insPos.setRef( refMng.getRef( object ) );
}
else
{
if( insPos != item )
{
// GWT.log( "is at row " + row + " but should be at " + insPos
// );
item.moveBefore( insPos );
insPos = item;
}
}
// print the row
printRow( object, insPos );
}
private Row getInsertPoint( T object )
{
ArrayList items = table.getItemChilds( null );
int nbRows = items.size();
int objectRef = refMng.getRef( object );
if( userComparator != null )
{
for( Row item : items )
{
int refAtRow = item.getRef();
if( userComparator.compare( object, refMng.getObject( refAtRow ) ) <= 0 )
{
if( refAtRow == objectRef )
{
// keep the same place only if lesser than the nxt item
// or if there is no next
if( items.indexOf( item ) == nbRows - 1 )
{
return item;
}
else
{
int refAtNextRow = getRefAtRow( item.getNextSiblingItem() );
if( userComparator.compare( object, refMng.getObject( refAtNextRow ) ) <= 0 )
return item;
}
}
else
{
return item;
}
}
}
}
return null;
}
@Override
public void deleteRow( int ref )
{
Row item = getRow( ref );
if( item == null )
return;
// to reset the edition state, just in case... (an only if we are
// editing on the row we delete)
if( edition != null && refMng.getRef( edition.editedObject ) == ref )
killCurrentEdit();
item.remove();
}
@Override
public void setComparator( Comparator comparator )
{
userComparator = comparator;
sortAndPrint( comparator );
}
public void sortAndPrint( final Comparator userComparator )
{
class It
{
Row item;
T object;
It( Row item, T object )
{
this.object = object;
this.item = item;
}
}
;
ArrayList its = new ArrayList();
// get all objects from the table
ArrayList childs = table.getItemChilds( null );
for( Row item : childs )
{
int ref = getRefAtRow( item );
its.add( new It( item, refMng.getObject( ref ) ) );
}
// sort them
Collections.sort( its, new Comparator()
{
@Override
public int compare( It o1, It o2 )
{
return userComparator.compare( o1.object, o2.object );
}
} );
// now reorder the lines
for( int j = 0; j < its.size(); j++ )
{
// item at position j ?
Row atJ = table.getItemChilds( null ).get( j );
// needed item is its[i].item
Row neededItem = its.get( j ).item;
if( atJ != neededItem )
{
// move its[i].item before item at position i
neededItem.moveBefore( atJ );
}
}
}
private int getRefAtRow( Row item )
{
return item.getRef();
}
// return the row index of the row associated to the object referenced as
// objectRef
// returns null if the row is not found
private Row getRow( int objectRef )
{
return table.getItemForRef( objectRef );
}
// returns a printer that can be used for the next call. a new one can be
// created
// WARNING : assumes the printer item is correctly initialized
private void printRow( T object, Row row )
{
// to reset the edition state, just in case...
if( edition != null && refMng.getRef( edition.editedObject ) == refMng.getRef( object ) )
killCurrentEdit();
// writes the element reference on the corresponding row element
// note that this does not create a hard link to the referenced object
// so
// no garbage is created here
row.setRef( refMng.getRef( object ) );
// each column
for( int i = 0; i < columns.size(); i++ )
columns.get( i ).prints.print( object, new CellInTreeTablePrinter( row, i ) );
}
private boolean beginEdit( Row item, int col )
{
int ref = getRefAtRow( item );
if( edition != null && ref == refMng.getRef( edition.editedObject ) )
return true; // already began !!!
killCurrentEdit();
Edits editMng = columns.get( col ).edits;
if( editMng == null )
return false;
// find the T object that has been clicked
T object = refMng.getObject( ref );
assert object != null;
if( object == null )
return false;
Element td = item.getTdElement( col );
edition = new EditionState();
// initialize and set the current edit
edition.editedCol = col;
edition.editedObject = object;
// create a Printer corresponding to this cell
edition.editedPrinter = new CellInTreeTablePrinter( item, col );
// create an editor and init it
// -2 to remove padding : HACK HACK HACK
edition.editedEditor = editMng.createEditor( edition.editedObject, edition.editedPrinter, onEdit, td.getOffsetWidth() - 2, td.getClientHeight() - 2 );
return true;
}
private static class CellPos
{
Row item;
int col;
CellPos( Row item, int col )
{
this.item = item;
this.col = col;
}
}
private final MouseDownHandler onTableMouseDown = new MouseDownHandler()
{
@Override
public void onMouseDown( MouseDownEvent event )
{
Element source = DOM.eventGetTarget( Event.as( event.getNativeEvent() ) );
// try to see if it's not on a th element
int hdr = table.getEventTargetHeaderIdx( source );
if( hdr < 0 )
return;
DragDrop.initiate( source, onDragDrop, hdr, Event.as( event.getNativeEvent() ) );
}
};
DragDrop.Callback onDragDrop = new DragDrop.Callback()
{
@Override
public String getGhostInnerHTML( Integer cookie, Element source )
{
class TempPrinter implements Printer
{
String html = null;
@Override
public void setWidget( Widget widget )
{
assert false;
}
@Override
public void setText( String text )
{
html = text;
}
@Override
public void setHTML( String html )
{
this.html = html;
}
}
TempPrinter printer = new TempPrinter();
columns.get( cookie ).hdrPrintsOn.print( null, printer );
// Due to asynchronism, it may be possible that the printer did not
// yet print anything, well... Too late, we use the source element
// instead !!
if( printer.html == null )
return "
return "
}
@Override
public void onDragDropFinished( Integer cookie, Element source, Element destination )
{
int newPos = table.getEventTargetHeaderIdx( destination );
if( newPos < 0 )
return;
ColumnMng dum = columns.get( cookie );
columns.set( cookie, columns.get( newPos ) );
columns.set( newPos, dum );
// Redraw headers
printHeaders();
// Redraw all objects in the table
ArrayList items = table.getItemChilds( null );
for( Row item : items )
{
int ref = item.getRef();
printRow( refMng.getObject( ref ), item );
}
}
};
private final Edits.Callback onEdit = new Edits.Callback()
{
@Override
public void cancelEdition()
{
killCurrentEdit();
}
@Override
public void validateEdition( boolean fJumpNext )
{
// just forget the edition state, but don't redraw
// that means we let the client continue drawing on its cell
if( edition != null && fJumpNext )
{
CellPos next = getNextPos( getRow( refMng.getRef( edition.editedObject ) ), edition.editedCol );
edition.close();
edition = null;
beginEdit( next.item, next.col );
}
}
};
// wait a bit to avoid ui jitter...
// use refMng in case the data change during the time waited
private void killCurrentEdit()
{
if( edition == null )
return;
// reprints the cell, with uptodate data
columns.get( edition.editedCol ).prints.print( refMng.getObject( refMng.getRef( edition.editedObject ) ), edition.editedPrinter );
edition.close();
edition = null;
}
CellPos getNextPos( Row item, int col )
{
// either on the same row
for( int c = col + 1; c < columns.size(); c++ )
{
if( columns.get( c ).edits != null )
return new CellPos( item, c );
}
// or on the next
item = item.getNextSiblingItem();
for( int c = 0; c < columns.size(); c++ )
{
if( columns.get( c ).edits != null )
return new CellPos( item, c );
}
return null;
}
}
```
### Source code for your Spoon processing
```Java
// file processors/ForProcessor.java
package spoon.processors;
import spoon.processing.AbstractProcessor;
import spoon.reflect.visitor.filter.TypeFilter;
import spoon.reflect.visitor.CtIterator;
import spoon.reflect.declaration.CtElement;
import spoon.reflect.code.CtFor;
import spoon.reflect.code.CtComment;
import spoon.reflect.factory.Factory;
import java.util.List;
import java.io.*;
public class ForProcessor extends AbstractProcessor {
public void process(CtFor element) {
element.addComment(element.getFactory().Code().createComment("START OF FOR LOOP STATEMENT",CtComment.CommentType.INLINE));
}
}
```
### Actual output
```Java
Exception in thread "main" java.lang.Runtime Exception: Inconsistent Stack public static @Nonnull IMicroNode convertToMicroNode (fi
nal @Nonnull Node aNode) {
ValueEnforcer.notNull(aNode, "Node");
IMicroNode ret;
final short nNodeType=aNode.getNode Type();
case Node. DOCUMENT_NODE :
{
ret = new MicroDocument ();
break;
}
case Node. DOCUMENT_TYPE_NODE :
{
final DocumentType aDT = (DocumentType) aNode;
ret = new MicroDocument Type (aDT.getName(), aDT.getPublicId(), aDT.getSystemId());
break;
}
case Node.ELEMENT_NODE :
final Element a Element = (Element) aNode;
final String sNamespaceURI = aElement.getNamespaceURI();
final IMicroElement eElement = ((sNamespaceURI != null)? new Micro Element(sNamespaceURI, aElement.getLocalName()): new MicroE
lement (aElement.getTagName()));
}
case Node. ELEMENT_NODE :
at
at spoon.support.compiler.jdt. JDTTree Builder.endVisit(JDTTree Builder.java:541)
at org.eclipse.jdt.internal.compiler.ast.MethodDeclaration. traverse(MethodDeclaration.java:440)
at org.eclipse.jdt.internal.compiler.ast. Type Declaration. traverse (Type Declaration.java:1699)
at org.eclipse.jdt.internal.compiler.ast.CompilationUnit Declaration. traverse (Compilation Unit Declaration.java:827)
at org.eclipse.jdt.internal.compiler.ast.CompilationUnit Declaration. traverse (CompilationUnit Declaration.java:788)
at spoon.support.compiler.jdt. JDTBased SpoonCompiler.traverseUnit Declaration (JDTBased SpoonCompiler.java:481)
at spoon.support.compiler.jdt. JDTBased SpoonCompiler.lambda$buildModel$0 (JDTBased SpoonCompiler.java:438)
at spoon.support.compiler.jdt. JDTBased SpoonCompiler.forEach CompilationUnit (JDTBased SpoonCompiler.java:465)
at spoon.support.compiler.jdt.JDTBased SpoonCompiler.buildModel (JDTBased Spoon Compiler.java:436)
at spoon.support.compiler.jdt.JDTBased SpoonCompiler.build Units And Model (JDTBased SpoonCompiler.java:373)
at spoon.support.compiler.jdt.JDTBased Spoon Compiler.buildSources (JDTBased SpoonCompiler.java:336)
at spoon.support.compiler.jdt. JDTBased SpoonCompiler.build(JDTBased Spoon Compiler.java:117)
at spoon.support.compiler.jdt. JDTBased SpoonCompiler.build(JDTBased SpoonCompiler.java:100)
at spoon. Launcher.buildModel (Launcher.java:782)
at spoon. Launcher.run(Launcher.java:733)
at spoon. Launcher.run(Launcher.java:112)
at spoon. Launcher.main(Launcher.java:105)
spoon.support.compiler.jdt.ContextBuilder.exit(ContextBuilder.java:122)
```
### Expected output
_No response_
### Spoon Version
10.2.0
### JVM Version
11
### What operating system are you using?
Linux
Contributor guide
Research direction
Reproduce the failure with the supplied source and processors/ForProcessor.java, then inspect spoon.support.compiler.jdt.JDTTreeBuilder.endVisit at line 541. Determine why processing the transformed input produces an inconsistent stack and verify the fix against the reported exception.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- java
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Needs clarification
- Newbie friendliness
- 15/100