package org.glassfish.pfl.dynamic.codegen.impl;
import java.lang.reflect.Modifier ;
import java.util.List ;
import java.util.ArrayList ;
import org.glassfish.pfl.dynamic.codegen.spi.Type ;
import org.glassfish.pfl.dynamic.codegen.spi.Variable ;
public class ASMSetupVisitor extends TreeWalker {
public enum Mode { PREPARE, VERIFY } ;
private enum VariableContext { REFERENCE, DEFINE_LOCAL, DEFINE_LOCAL_DEFINITION } ;
public static class ErrorReport {
public Node node ;
public String msg ;
}
public List<ErrorReport> getVerificationErrors() {
return errors ;
}
private void verificationError( Node node, String msg ) {
ErrorReport report = new ErrorReport() ;
report.node = node ;
report.msg = msg ;
errors.add( report ) ;
}
private VariableContext variableDefiningContext ;
private Mode mode ;
private SlotAllocator slotAllocator ;
private List<ErrorReport> errors ;
public ASMSetupVisitor( TreeWalkerContext context ) {
this( context, Mode.PREPARE ) ;
}
public ASMSetupVisitor( TreeWalkerContext context, Mode mode ) {
super( context ) ;
context.push( this ) ;
variableDefiningContext = VariableContext.REFERENCE ;
this.mode = mode ;
slotAllocator = null ;
errors = new ArrayList<ErrorReport>() ;
}
private boolean preparing() {
return mode == Mode.PREPARE ;
}
static class SlotAllocator {
private static int id = 0 ;
private int myId = id++ ;
private int current = 1 ;
public int getSlot( Type type ) {
int result = current ;
current += type.size() ;
return result ;
}
@Override
public String toString() {
return "SlotAllocator(" + myId + ")[current=" + current + "]" ;
}
}
@Override
public boolean preClassGenerator( ClassGeneratorImpl arg ) {
return true ;
}
@Override
public boolean preMethodGenerator( MethodGenerator arg ) {
slotAllocator = new SlotAllocator() ;
for (Variable var : arg.arguments()) {
ASMUtil.requiredEmitterType.set((VariableInternal) var,
ASMUtil.RequiredEmitterType.NONE);
}
return !Modifier.isAbstract( arg.modifiers() ) ;
}
@Override
public boolean methodGeneratorBeforeArguments( MethodGenerator arg ) {
variableDefiningContext = VariableContext.DEFINE_LOCAL ;
return true ;
}
@Override
public void methodGeneratorAfterArguments( MethodGenerator arg ) {
variableDefiningContext = VariableContext.REFERENCE ;
}
@Override
public void postMethodGenerator( MethodGenerator arg ) {
if (!arg.returnType().equals( Type._void() )) {
Variable var ;
if (preparing()) {
var = arg.body().exprFactory().variable(
arg.returnType(), "$$_returnVariable_$$" ) ;
ASMUtil.returnVariable.set( arg, var ) ;
} else {
var = ASMUtil.returnVariable.get( arg ) ;
}
defineLocalVariable( var ) ;
}
slotAllocator = null ;
}
@Override
public boolean classGeneratorBeforeFields( ClassGeneratorImpl arg ) {
return true ;
}
@Override
public void classGeneratorBeforeInitializer( ClassGeneratorImpl arg ) {
variableDefiningContext = VariableContext.REFERENCE ;
}
@Override
public void classGeneratorBeforeMethod( ClassGeneratorImpl arg ) {
variableDefiningContext = VariableContext.REFERENCE ;
}
@Override
public void classGeneratorBeforeConstructor( ClassGeneratorImpl arg ) {
variableDefiningContext = VariableContext.REFERENCE ;
}
@Override
public void postClassGenerator( ClassGeneratorImpl arg ) {
variableDefiningContext = VariableContext.REFERENCE ;
}
@Override
public boolean preBlockStatement( BlockStatement arg ) {
ASMUtil.lastStatement.set( arg, null ) ;
return true ;
}
@Override
public void blockStatementBeforeBodyStatement( BlockStatement arg, Statement stmt ) {
Statement lastStatement = ASMUtil.lastStatement.get( arg ) ;
if (lastStatement != null) {
ASMUtil.next.set(lastStatement, stmt);
}
ASMUtil.lastStatement.set( arg, stmt ) ;
}
@Override
public void postBlockStatement( BlockStatement arg ) {
}
@Override
public boolean preDefinitionStatement( DefinitionStatement arg ) {
variableDefiningContext = VariableContext.DEFINE_LOCAL_DEFINITION ;
if (preparing()) {
ASMUtil.requiredEmitterType.set( (VariableInternal)arg.var(),
ASMUtil.RequiredEmitterType.SETTER ) ;
} else {
if (ASMUtil.requiredEmitterType.get( (VariableInternal)arg.var()) !=
ASMUtil.RequiredEmitterType.SETTER ) {
verificationError(arg,
"Variable of definition statement should have "
+ "requiredEmitterType true");
}
}
return true ;
}
@Override
public boolean definitionStatementBeforeExpr( DefinitionStatement arg ) {
variableDefiningContext = VariableContext.REFERENCE ;
return true ;
}
@Override
public boolean preTryStatement( TryStatement arg ) {
if (!arg.finalPart().isEmpty()) {
Variable ucVar = arg.bodyPart().exprFactory().variable(
Type._class( "java.lang.Throwable" ),
"$$_uncaughtException_$$" ) ;
defineLocalVariable( ucVar ) ;
ASMUtil.uncaughtException.set( arg, ucVar ) ;
Variable raVar = arg.bodyPart().exprFactory().variable( Type._Object(),
"$$_returnAddress_$$" ) ;
defineLocalVariable( raVar ) ;
ASMUtil.returnAddress.set( arg, raVar ) ;
}
return true ;
}
@Override
public void tryStatementBeforeBlock( TryStatement arg,
Type type, Variable var, BlockStatement block ) {
ASMUtil.requiredEmitterType.set( (VariableInternal)var,
ASMUtil.RequiredEmitterType.NONE ) ;
defineLocalVariable( var ) ;
}
@Override
public boolean tryStatementBeforeFinalPart( TryStatement arg ) {
return true ;
}
@Override
public void postTryStatement( TryStatement arg ) {
}
@Override
public boolean preAssignmentStatement( AssignmentStatement arg ) {
ExpressionInternal left = arg.left() ;
assert left.isAssignable() ;
if (preparing()) {
ASMUtil.requiredEmitterType.set( left,
ASMUtil.RequiredEmitterType.SETTER ) ;
} else {
if (ASMUtil.requiredEmitterType.get(left) !=
ASMUtil.RequiredEmitterType.SETTER ) {
verificationError(arg,
"Left side of assignment statement should have "
+ "requiredEmitterType SETTER");
}
}
return true ;
}
@Override
public boolean preNonStaticFieldAccessExpression(
ExpressionFactory.NonStaticFieldAccessExpression arg ) {
initializeEmitter( arg ) ;
return true ;
}
@Override
public boolean preStaticFieldAccessExpression(
ExpressionFactory.StaticFieldAccessExpression arg ) {
initializeEmitter( arg ) ;
return true ;
}
@Override
public boolean preArrayIndexExpression(
ExpressionFactory.ArrayIndexExpression arg ) {
initializeEmitter( arg ) ;
return true ;
}
private void initializeEmitter(
ExpressionFactory.NonStaticFieldAccessExpression arg ) {
ASMUtil.RequiredEmitterType ret =
ASMUtil.requiredEmitterType.get( arg ) ;
EmitterFactory.Emitter em = null ;
if (ret != ASMUtil.RequiredEmitterType.NONE) {
em = EmitterFactory.makeEmitter(arg,
ret == ASMUtil.RequiredEmitterType.SETTER);
}
handleEmitter( arg, em ) ;
}
private void initializeEmitter(
ExpressionFactory.StaticFieldAccessExpression arg ) {
ASMUtil.RequiredEmitterType ret =
ASMUtil.requiredEmitterType.get( arg ) ;
EmitterFactory.Emitter em = null ;
if (ret != ASMUtil.RequiredEmitterType.NONE) {
em = EmitterFactory.makeEmitter(arg,
ret == ASMUtil.RequiredEmitterType.SETTER);
}
handleEmitter( arg, em ) ;
}
private void initializeEmitter(
ExpressionFactory.ArrayIndexExpression arg ) {
ASMUtil.RequiredEmitterType ret =
ASMUtil.requiredEmitterType.get( arg ) ;
EmitterFactory.Emitter em = null ;
if (ret != ASMUtil.RequiredEmitterType.NONE) {
em = EmitterFactory.makeEmitter(arg,
ret == ASMUtil.RequiredEmitterType.SETTER);
}
handleEmitter( arg, em ) ;
}
private void compareEmitter( String nodeType, Node arg,
EmitterFactory.Emitter expected, EmitterFactory.Emitter actual ) {
boolean error ;
if (actual == null) {
error = expected != null;
} else {
error = !(actual.equals(expected));
}
if (error) {
verificationError(arg, "Incorrect " + nodeType + ": expected "
+ expected + ", but found " + actual);
}
}
private void handleEmitter( Node arg, EmitterFactory.Emitter em ) {
if (preparing()) {
ASMUtil.emitter.set( arg, em ) ;
} else {
EmitterFactory.Emitter lem = ASMUtil.emitter.get( arg ) ;
compareEmitter( "emitter", arg, em, lem ) ;
}
}
private void initializeVariableEmitter( Variable param ) {
VariableInternal arg = (VariableInternal)param ;
EmitterFactory.Emitter em = null ;
ASMUtil.RequiredEmitterType ret =
ASMUtil.requiredEmitterType.get( arg ) ;
if (ret != ASMUtil.RequiredEmitterType.NONE) {
if (ret == ASMUtil.RequiredEmitterType.SETTER) {
em = ASMUtil.setEmitter.get( arg ) ;
} else {
em = ASMUtil.getEmitter.get( arg ) ;
}
handleEmitter( arg, em ) ;
}
}
private void defineLocalVariable( Variable arg ) {
allocateLocalVariable( arg ) ;
finishVariableDefinition( arg ) ;
}
private void allocateLocalVariable( Variable param ) {
VariableInternal arg = (VariableInternal)param ;
assert slotAllocator != null ;
int sfs = slotAllocator.getSlot( arg.type() ) ;
if (preparing()) {
ASMUtil.stackFrameSlot.set( arg, sfs ) ;
} else {
int slot = ASMUtil.stackFrameSlot.get( arg ) ;
if (slot != sfs) {
verificationError(arg, "Expected stackFrameSlot to be " + sfs
+ ", was " + slot);
}
}
}
private void finishVariableDefinition( Variable param ) {
VariableInternal arg = (VariableInternal)param ;
EmitterFactory.Emitter getter = EmitterFactory.makeEmitter( arg, false ) ;
EmitterFactory.Emitter setter = EmitterFactory.makeEmitter( arg, true ) ;
if (preparing()) {
ASMUtil.getEmitter.set( arg, getter ) ;
ASMUtil.setEmitter.set( arg, setter ) ;
} else {
EmitterFactory.Emitter lgetter = ASMUtil.getEmitter.get( arg ) ;
compareEmitter( "getEmitter", arg, getter, lgetter ) ;
EmitterFactory.Emitter lsetter = ASMUtil.setEmitter.get( arg ) ;
compareEmitter( "setEmitter", arg, setter, lsetter ) ;
}
}
@Override
public boolean preVariable( Variable arg ) {
switch (variableDefiningContext) {
case REFERENCE :
ASMUtil.requiredEmitterType.set( (VariableInternal)arg,
ASMUtil.RequiredEmitterType.GETTER ) ;
initializeVariableEmitter( arg ) ;
break ;
case DEFINE_LOCAL :
defineLocalVariable( arg ) ;
break ;
case DEFINE_LOCAL_DEFINITION:
defineLocalVariable( arg ) ;
initializeVariableEmitter( arg ) ;
break ;
default :
assert false ;
}
return false ;
}
@Override
public boolean preBinaryOperatorExpression( ExpressionFactory.BinaryOperatorExpression arg ) {
return true ;
}
@Override
public void binaryOperatorExpressionBeforeRight( ExpressionFactory.BinaryOperatorExpression arg ) {
}
@Override
public void postBinaryOperatorExpression( ExpressionFactory.BinaryOperatorExpression arg ) {
postExpression( arg ) ;
}
}