/*
* Copyright 2013 Guidewire Software, Inc.
*/
package gw.internal.gosu.ir.compiler.bytecode;
import gw.internal.ext.org.objectweb.asm.ClassWriter;
import gw.internal.ext.org.objectweb.asm.ClassReader;
import gw.internal.ext.org.objectweb.asm.ClassVisitor;
import gw.internal.ext.org.objectweb.asm.Opcodes;
import gw.internal.ext.org.objectweb.asm.FieldVisitor;
import gw.internal.ext.org.objectweb.asm.MethodVisitor;
import gw.internal.ext.org.objectweb.asm.Label;
import gw.internal.ext.org.objectweb.asm.AnnotationVisitor;
import gw.internal.ext.org.objectweb.asm.util.TraceClassVisitor;
import gw.internal.ext.org.objectweb.asm.util.CheckClassAdapter;
import gw.internal.gosu.compiler.DebugFlag;
import gw.lang.ir.IRClass;
import gw.lang.ir.IRType;
import gw.lang.ir.IRSymbol;
import gw.lang.ir.IRAnnotation;
import gw.lang.ir.statement.IRFieldDecl;
import gw.lang.ir.statement.IRMethodStatement;
import gw.lang.reflect.Modifier;
import gw.lang.reflect.gs.BytecodeOptions;
import java.io.StringWriter;
import java.io.PrintWriter;
import java.util.List;
public class IRClassCompiler extends AbstractBytecodeCompiler
{
private static boolean COMPILE_WITH_DEBUG_INFO = true;
//## todo: this s/b configurable
public static final int JAVA_VER = Opcodes.V1_6;
private ClassVisitor _cv;
private IRClass _irClass;
public static byte[] compileClass( IRClass irClass, boolean debug )
{
boolean alreadyDebugging = DebugFlag.isDebugFlagsOn();
if( !alreadyDebugging && debug )
{
DebugFlag.setDebugFlagsOn();
}
try
{
return new IRClassCompiler( irClass ).compile( );
}
finally
{
if( !alreadyDebugging )
{
DebugFlag.setDebugFlagsOff();
}
}
}
public IRClassCompiler(IRClass irClass) {
_irClass = irClass;
}
private byte[] compile( )
{
ClassWriter writer = new GosuClassWriter();
StringWriter trace = configClassVisitor( writer );
try
{
compileClassHeader();
addSourceFileRef();
compileInnerClasses();
compileFields();
compileMethods();
addAnnotations();
_cv.visitEnd();
}
finally
{
if( BytecodeOptions.shouldDebug( _irClass.getName() ))
{
System.out.println( "========================================================================" );
System.out.println( _irClass.getName() );
System.out.println( "========================================================================" );
System.out.println( trace );
}
}
byte[] bytes = writer.toByteArray();
// verify( bytes );
return bytes;
}
public void addAnnotations() {
for (IRAnnotation annotation : _irClass.getAnnotations() ) {
AnnotationVisitor annotationVisitor = _cv.visitAnnotation(annotation.getDescriptor().getDescriptor(), annotation.isInclude());
new IRAnnotationCompiler( annotationVisitor, annotation ).compile();
}
}
private StringWriter configClassVisitor( ClassWriter writer )
{
_cv = writer;
StringWriter trace = null;
if( isDebugFlagSet( DebugFlag.TRACE ) || BytecodeOptions.shouldDebug( _irClass.getName() ) )
{
trace = new StringWriter();
_cv = new TraceClassVisitor( _cv, new PrintWriter( trace ) );
if( isDebugFlagSet( DebugFlag.ASM_CHECKER ) )
{
_cv = new CheckClassAdapter( _cv );
}
}
return trace;
}
public static void verify( byte[] bytes )
{
if( isDebugFlagSet( DebugFlag.VERIFY ) )
{
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter( sw );
CheckClassAdapter.verify( new ClassReader( bytes ), false, pw );
String out = sw.toString();
if( out.length() > 0 )
{
System.out.println( out );
}
}
}
static public boolean isDebugFlagSet( DebugFlag flag )
{
return DebugFlag.getDebugFlags().contains( flag );
}
private void compileClassHeader()
{
int modifiers = _irClass.getModifiers();
_cv.visit( JAVA_VER,
modifiers,
_irClass.getThisType().getSlashName(),
getClassSignature(),
_irClass.getSuperType().getSlashName(),
getInterfaceNames() );
}
/**
* Deals with generics.
*
* Since Gosu generics are parameterized explicitly in the bytecode world,
* we'll probably not ever conform to java generics.
*/
private String getClassSignature()
{
return null;
}
private String[] getInterfaceNames()
{
List<? extends IRType> interfaces = _irClass.getInterfaces();
if( interfaces == null || interfaces.isEmpty() )
{
return null;
}
String[] ifaceNames = new String[interfaces.size()];
for( int i = 0; i < ifaceNames.length; i++ )
{
IRType iface = interfaces.get( i );
ifaceNames[i] = iface.getSlashName();
}
return ifaceNames;
}
private void addSourceFileRef() {
_cv.visitSource( _irClass.getSourceFile(), null );
}
private void compileInnerClasses()
{
for( IRClass.InnerClassInfo innerClass : _irClass.getInnerClasses() )
{
visitInnerClass( innerClass );
}
}
private void visitInnerClass( IRClass.InnerClassInfo innerClass )
{
_cv.visitInnerClass( innerClass.getInnerClass().getSlashName(),
innerClass.getEnclosingType().getSlashName(),
innerClass.getInnerClass().getRelativeName(),
innerClass.getModifiers());
}
private void compileFields() {
for( IRFieldDecl field : _irClass.getFields() )
{
FieldVisitor fv = _cv.visitField( field.getModifiers(),
field.getName(),
field.getType().getDescriptor(),
null,
field.getValue() );
for (IRAnnotation annotation : field.getAnnotations() ) {
AnnotationVisitor annotationVisitor = fv.visitAnnotation(annotation.getDescriptor().getDescriptor(), annotation.isInclude());
new IRAnnotationCompiler( annotationVisitor, annotation ).compile();
}
fv.visitEnd();
}
}
private void compileMethods() {
for ( IRMethodStatement method : _irClass.getMethods() ) {
compileMethod( method );
}
}
private void compileMethod( IRMethodStatement method )
{
MethodVisitor mv = _cv.visitMethod( method.getModifiers(),
method.getName(),
getMethodDescriptor( method ),
null, null );
for( IRAnnotation annotation : method.getAnnotations() )
{
AnnotationVisitor annotationVisitor = mv.visitAnnotation( annotation.getDescriptor().getDescriptor(), annotation.isInclude() );
new IRAnnotationCompiler( annotationVisitor, annotation ).compile();
}
for( IRSymbol param: method.getParameters() )
{
List<IRAnnotation> paramAnnotations = param.getAnnotations();
if( paramAnnotations != null )
{
int i = 0;
for( IRAnnotation annotation: paramAnnotations )
{
AnnotationVisitor annotationVisitor = mv.visitParameterAnnotation( i++, annotation.getDescriptor().getDescriptor(), annotation.isInclude() );
new IRAnnotationCompiler( annotationVisitor, annotation ).compile();
}
}
}
if( method.getMethodBody() != null )
{
mv.visitCode();
IRBytecodeContext context = new IRBytecodeContext( mv );
if( !Modifier.isStatic( method.getModifiers() ) )
{
context.indexThis( _irClass.getThisType() );
}
context.indexSymbols( method.getParameters() );
IRBytecodeCompiler.compileIRElement( method.getMethodBody(), context );
terminateFunction( context );
}
mv.visitEnd();
}
private void terminateFunction( IRBytecodeContext context )
{
context.popScope();
MethodVisitor mv = context.getMv();
Label endLabel = new Label();
context.visitLabel( endLabel );
if( COMPILE_WITH_DEBUG_INFO )
{
context.visitLocalVars();
}
//mv.visitMaxs( context.getMaxScopeSize(), context.getLocalCount() );
mv.visitMaxs( 0, 0 );
}
public static String getMethodDescriptor(IRMethodStatement m) {
StringBuilder sb = new StringBuilder();
sb.append("(");
for (IRSymbol param : m.getParameters()) {
sb.append(param.getType().getDescriptor());
}
sb.append(")");
sb.append(m.getReturnType().getDescriptor());
return sb.toString();
}
@Override
public String toString()
{
return "Compiling: " + _irClass.getName();
}
}