/* * Copyright 2013 Guidewire Software, Inc. */ package gw.internal.gosu.ir.transform; import gw.internal.ext.org.objectweb.asm.Opcodes; import gw.internal.gosu.annotations.AnnotationMap; import gw.internal.gosu.ir.nodes.GosuClassIRType; import gw.internal.gosu.ir.nodes.IRMethod; import gw.internal.gosu.ir.nodes.IRMethodFactory; import gw.internal.gosu.ir.nodes.JavaClassIRType; import gw.internal.gosu.ir.transform.statement.AssertStatementTransformer; import gw.internal.gosu.ir.transform.statement.FieldInitializerTransformer; import gw.internal.gosu.ir.transform.util.AccessibilityUtil; import gw.internal.gosu.ir.transform.util.IRTypeResolver; import gw.internal.gosu.ir.transform.util.NameResolver; import gw.internal.gosu.parser.AbstractDynamicSymbol; import gw.internal.gosu.parser.AnnotationBuilder; import gw.internal.gosu.parser.BlockClass; import gw.internal.gosu.parser.DynamicFunctionSymbol; import gw.internal.gosu.parser.EnumCodePropertySymbol; import gw.internal.gosu.parser.EnumDisplayNamePropertySymbol; import gw.internal.gosu.parser.EnumNamePropertySymbol; import gw.internal.gosu.parser.EnumValueOfFunctionSymbol; import gw.internal.gosu.parser.EnumValuesFunctionSymbol; import gw.internal.gosu.parser.GosuAnnotationInfo; import gw.internal.gosu.parser.GosuClass; import gw.internal.gosu.parser.ICompilableTypeInternal; import gw.internal.gosu.parser.IGosuClassInternal; import gw.internal.gosu.parser.IGosuEnhancementInternal; import gw.internal.gosu.parser.IGosuTemplateInternal; import gw.internal.gosu.parser.IJavaTypeInternal; import gw.internal.gosu.parser.MemberFieldSymbol; import gw.internal.gosu.parser.ParameterizedDynamicFunctionSymbol; import gw.internal.gosu.parser.Symbol; import gw.internal.gosu.parser.TemplateRenderFunctionSymbol; import gw.internal.gosu.parser.TypeLord; import gw.internal.gosu.parser.statements.ClassStatement; import gw.internal.gosu.parser.statements.StatementList; import gw.internal.gosu.parser.statements.VarStatement; import gw.internal.gosu.runtime.GosuRuntimeMethods; import gw.lang.ir.IRAnnotation; import gw.lang.ir.IRClass; import gw.lang.ir.IRExpression; import gw.lang.ir.IRStatement; import gw.lang.ir.IRSymbol; import gw.lang.ir.IRType; import gw.lang.ir.IRTypeConstants; import gw.lang.ir.SyntheticIRType; import gw.lang.ir.expression.IRMethodCallExpression; import gw.lang.ir.statement.IRFieldDecl; import gw.lang.ir.statement.IRMethodCallStatement; import gw.lang.ir.statement.IRMethodStatement; import gw.lang.ir.statement.IRNoOpStatement; import gw.lang.ir.statement.IRReturnStatement; import gw.lang.ir.statement.IRStatementList; import gw.lang.parser.ICapturedSymbol; import gw.lang.parser.IDynamicFunctionSymbol; import gw.lang.parser.IDynamicPropertySymbol; import gw.lang.parser.IProgramClassFunctionSymbol; import gw.lang.parser.IStatement; import gw.lang.parser.ISymbol; import gw.lang.parser.Keyword; import gw.lang.parser.expressions.IVarStatement; import gw.lang.parser.statements.IFunctionStatement; import gw.lang.reflect.IAnnotationInfo; import gw.lang.reflect.IMethodInfo; import gw.lang.reflect.IModifierInfo; import gw.lang.reflect.IParameterInfo; import gw.lang.reflect.IRelativeTypeInfo; import gw.lang.reflect.IType; import gw.lang.reflect.Modifier; import gw.lang.reflect.TypeSystem; import gw.lang.reflect.gs.BytecodeOptions; import gw.lang.reflect.gs.GosuClassPathThing; import gw.lang.reflect.gs.IExternalSymbolMap; import gw.lang.reflect.gs.IGenericTypeVariable; import gw.lang.reflect.gs.IGosuClass; import gw.lang.reflect.gs.IGosuProgram; import gw.lang.reflect.java.IJavaClassInfo; import gw.lang.reflect.java.IJavaClassMethod; import gw.lang.reflect.java.IJavaMethodInfo; import gw.lang.reflect.java.IJavaType; import gw.lang.reflect.java.JavaTypes; import gw.util.GosuExceptionUtil; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; /** */ public class GosuClassTransformer extends AbstractElementTransformer<ClassStatement> { public static final String ENUM_VALUES_FIELD = "ENUM$VALUES"; private IGosuClassInternal _gsClass; private EnumOrdinalCounter _enumCounter; private IRClass _irClass; private GosuClassTransformationContext _context; private boolean _bHasAsserts; public static IRClass compile( IGosuClassInternal gsClass ) { GosuClassTransformer cc = new GosuClassTransformer( gsClass ); return cc.compile(); } public static IRClass compileInterfaceMethodsClass( IGosuClassInternal gsClass ) { GosuClassTransformer cc = new GosuClassTransformer( gsClass ); return cc.compileInterfaceMethodsClass(); } private GosuClassTransformer( IGosuClassInternal gsClass ) { super( null, (ClassStatement)gsClass.getClassStatement() ); _gsClass = gsClass; _enumCounter = new EnumOrdinalCounter(); _context = new GosuClassTransformationContext( this, _gsClass ); setCc( _context ); } private IRClass compile() { if( !_gsClass.isValid() || !_gsClass.isDefinitionsCompiled() ) { //noinspection ThrowableResultOfMethodCallIgnored throw GosuExceptionUtil.forceThrow( _gsClass.getParseResultsException() ); } _irClass = new IRClass(); compileClassHeader(); addSourceFileRef(); compileInnerClasses(); compileStaticInitializer(); compileFields(); compileConstructors(); compileMethods(); addAnnotations(); return _irClass; } public IRClass compileInterfaceMethodsClass() { if( getGosuClass().isInterface() ) { _irClass = new IRClass(); //simplified header _irClass.setName( _gsClass.getInterfaceMethodsClassName() ); _irClass.setThisType( new SyntheticIRType( Object.class, _gsClass.getInterfaceMethodsClassName(), GosuClass.ANNOTATION_METHODS_FOR_INTERFACE_INNER_CLASS ) ); _irClass.setModifiers( Opcodes.ACC_PUBLIC ); _irClass.setSuperType( getDescriptor( Object.class ) ); addSourceFileRef(); addEvalAnnotationMethod( _irClass ); return _irClass; } else { throw new IllegalStateException( "Cannot create an interface methods class for a non-interface" ); } } private void addAnnotations() { List<IRAnnotation> annotations = getIRAnnotations( (List)_gsClass.getTypeInfo().getAnnotations() ); _irClass.setAnnotations( annotations ); } private List<IRAnnotation> getIRAnnotations( List<? extends IAnnotationInfo> gosuAnnotations ) { List<IRAnnotation> annotations = new ArrayList<IRAnnotation>(); Set<IType> alreadyCreated = new HashSet<IType>(); for( IAnnotationInfo ai : gosuAnnotations ) { if( ai instanceof GosuAnnotationInfo ) { GosuAnnotationInfo gai = (GosuAnnotationInfo)ai; IType type = ai.getType(); if( BytecodeOptions.isGenerateAnnotationsToClassFiles() && !alreadyCreated.contains(type) && gai.getRawAnnotation().shouldPersistToClass() ) { alreadyCreated.add( ai.getType() ); annotations.add( new IRAnnotation( getDescriptor( ai.getType() ), gai.getRawAnnotation().shouldRetainAtRuntime(), ai ) ); } } else { IType type = ai.getType(); if( BytecodeOptions.isGenerateAnnotationsToClassFiles() && !alreadyCreated.contains( type ) && hasRetentionPolicy( ai, RetentionPolicy.SOURCE ) ) { alreadyCreated.add( ai.getType() ); annotations.add( new IRAnnotation( getDescriptor( ai.getType() ), hasRetentionPolicy( ai, RetentionPolicy.RUNTIME ), ai ) ); } } } return annotations; } private boolean hasRetentionPolicy( IAnnotationInfo annotation, RetentionPolicy policy ) { List<IAnnotationInfo> annotationsOfType = annotation.getType().getTypeInfo().getAnnotationsOfType( TypeSystem.get( Retention.class ) ); for( IAnnotationInfo retention : annotationsOfType ) { if( retention != null && retention.getFieldValue( "value" ).equals( policy.name() ) ) { return true; } } return false; } private void compileInnerClasses() { if( _gsClass.getEnclosingType() != null ) { IGosuClassInternal thisInnerClass = _gsClass; visitInnerClass( thisInnerClass ); } for( IGosuClass innerClass : _gsClass.getInnerClasses() ) { visitInnerClass( innerClass ); } } private void visitInnerClass( IGosuClass innerClass ) { _irClass.addInnerClass( getDescriptor( innerClass ), getDescriptor( innerClass.getEnclosingType() ), getClassModifiers( innerClass, true ) ); } private void compileStaticInitializer() { List<IRStatement> initStatements = new ArrayList<IRStatement>(); IRMethodCallExpression bootstrapGosuWhenInitiatedViaClassfile = buildMethodCall( JavaClassIRType.get( GosuClassPathThing.class ), "init", false, IRTypeConstants.pBOOLEAN(), Collections.<IRType>emptyList(), null, Collections.<IRExpression>emptyList() ); initStatements.add( new IRMethodCallStatement( bootstrapGosuWhenInitiatedViaClassfile ) ); List<IRSymbol> syms = new ArrayList<IRSymbol>( 1 ); if( isProgramOrEnclosedInProgram( getGosuClass() ) ) { //## hack: Because we pass in the external symbols map everywhere through this cluster labyrinth of intermediate glory // (instead of grabbing it from the program like we used to and should still be doing) // we are stuck with this hack and have no way of actually getting the params. IRSymbol symbolsParam = new IRSymbol( GosuFragmentTransformer.SYMBOLS_PARAM_NAME, getDescriptor( IExternalSymbolMap.class ), true ); IRStatement nullExternalSymbols = buildAssignment( symbolsParam, pushNull() ); initStatements.add( nullExternalSymbols ); syms.add( symbolsParam ); } setUpFunctionContext( false, syms ); initializeStaticFields( initStatements ); if( initStatements.size() > 0 ) { initStatements.add( new IRReturnStatement() ); IRMethodStatement clinit = new IRMethodStatement( new IRStatementList( false, initStatements ), "<clinit>", Opcodes.ACC_STATIC, IRTypeConstants.pVOID(), Collections.<IRSymbol>emptyList() ); _irClass.addMethod( clinit ); } } private void compileFields() { addInstanceFields(); addStaticFields(); addOuterThisField(); addCapturedSymbolFields(); addTypeParamFields(); } private void addInstanceFields() { for( IVarStatement field : getOrderedFields() ) { IRFieldDecl fieldDecl = new IRFieldDecl( getModifiers( (AbstractDynamicSymbol)field.getSymbol() ), field.getIdentifierName().toString(), getDescriptor( field.getType() ), null ); fieldDecl.setAnnotations( getIRAnnotations( makeAnnotationInfos( ((VarStatement)field).getAnnotations(), getGosuClass().getTypeInfo() ) ) ); _irClass.addField( fieldDecl ); } } private void addStaticFields() { for( IVarStatement field : _gsClass.getStaticFields() ) { IRFieldDecl fieldDecl = new IRFieldDecl( getModifiers( (Symbol)field.getSymbol() ), field.getIdentifierName().toString(), getDescriptor( field.getType() ), null ); fieldDecl.setAnnotations( getIRAnnotations( makeAnnotationInfos( ((VarStatement)field).getAnnotations(), getGosuClass().getTypeInfo() ) ) ); _irClass.addField( fieldDecl ); } // Enums automatically get a synthetic field that holds an array of all enum values // in the appropriate order and which is used to implement values() method. The field // is intialized statically, immediately after all the enum constants have been initialized. if( _gsClass.isEnum() ) { int iModifiers = Opcodes.ACC_STATIC | Opcodes.ACC_FINAL | Opcodes.ACC_SYNTHETIC; iModifiers |= (BytecodeOptions.isSingleServingLoader() ? Opcodes.ACC_PUBLIC : Opcodes.ACC_PRIVATE); IRFieldDecl fieldDecl = new IRFieldDecl( iModifiers, ENUM_VALUES_FIELD, getDescriptor( _gsClass.getArrayType() ), null ); _irClass.addField( fieldDecl ); } } private void addOuterThisField() { if( isNonStaticInnerClass() && !_gsClass.isInterface() ) { int iModifiers = Opcodes.ACC_FINAL + Opcodes.ACC_SYNTHETIC; iModifiers |= (BytecodeOptions.isSingleServingLoader() ? Opcodes.ACC_PUBLIC : 0); IRFieldDecl fieldDecl = new IRFieldDecl( iModifiers, getOuterThisFieldName(), getDescriptor( getRuntimeEnclosingType( _gsClass ) ), null ); _irClass.addField( fieldDecl ); } } private void addCapturedSymbolFields() { if( _gsClass.isInterface() ) { return; } Map<String, ICapturedSymbol> capturedSymbols = _gsClass.getCapturedSymbols(); if( capturedSymbols != null ) { for( ICapturedSymbol sym : capturedSymbols.values() ) { int iModifiers = Opcodes.ACC_FINAL | Opcodes.ACC_SYNTHETIC; iModifiers |= (BytecodeOptions.isSingleServingLoader() ? Opcodes.ACC_PUBLIC : Opcodes.ACC_PRIVATE); IRFieldDecl fieldDecl = new IRFieldDecl( iModifiers, CAPTURED_VAR_PREFIX + sym.getName(), getDescriptor( sym.getType().getArrayType() ), null ); _irClass.addField( fieldDecl ); } } if( requiresExternalSymbolCapture( _gsClass ) ) { int iModifiers = Opcodes.ACC_FINAL | Opcodes.ACC_SYNTHETIC; iModifiers |= (BytecodeOptions.isSingleServingLoader() ? Opcodes.ACC_PUBLIC : Opcodes.ACC_PRIVATE); IRFieldDecl fieldDecl = new IRFieldDecl( iModifiers, GosuFragmentTransformer.SYMBOLS_PARAM_NAME, getDescriptor( IExternalSymbolMap.class ), null ); _irClass.addField( fieldDecl ); } } private void addTypeParamFields() { IGosuClassInternal gsClass = _gsClass; if( gsClass.isGenericType() && !gsClass.isInterface() ) { for( IGenericTypeVariable genTypeVar : gsClass.getGenericTypeVariables() ) { int iModifiers = Opcodes.ACC_FINAL + Opcodes.ACC_SYNTHETIC; iModifiers |= (BytecodeOptions.isSingleServingLoader() ? Opcodes.ACC_PUBLIC : 0); IRFieldDecl fieldDecl = new IRFieldDecl( iModifiers, TYPE_PARAM_PREFIX + genTypeVar.getName(), getDescriptor( IType.class ), null ); _irClass.addField( fieldDecl ); } } while( gsClass.isAnonymous() ) { IDynamicFunctionSymbol dfs = getEnclosingDFS( gsClass ); if( dfs == null ) { break; } for( IGenericTypeVariable genTypeVar : getTypeVarsForDFS( dfs ) ) { int iModifiers = Opcodes.ACC_FINAL + Opcodes.ACC_SYNTHETIC; iModifiers |= (BytecodeOptions.isSingleServingLoader() ? Opcodes.ACC_PUBLIC : 0); IRFieldDecl fieldDecl = new IRFieldDecl( iModifiers, TYPE_PARAM_PREFIX + genTypeVar.getName(), getDescriptor( IType.class ), null ); _irClass.addField( fieldDecl ); } gsClass = (IGosuClassInternal)dfs.getGosuClass(); } } public boolean isNonStaticInnerClass() { return !_gsClass.isStatic() && _gsClass.getEnclosingType() != null; } public String getOuterThisFieldName() { return Keyword.KW_this + "$" + (_gsClass.getDepth() - 1); } private void compileConstructors() { for( DynamicFunctionSymbol dfs : _gsClass.getConstructorFunctions() ) { int iModifiers = getModifiers( dfs ); List<IRSymbol> parameters = new ArrayList<IRSymbol>(); maybeGetOuterThisParamType( parameters ); maybeGetCapturedSymbolTypes( parameters ); maybeGetTypeVarSymbolTypesForConstructor( parameters ); maybeGetEnumSuperConstructorSymbols( parameters ); for( ISymbol param : dfs.getArgs() ) { String name = param.getName(); if( isBlockInvoke( dfs ) ) { name = name + "$$blockParam"; } else if( param.isValueBoxed() ) { name = name + "$$unboxedParam"; } parameters.add( makeParamSymbol( param, name ) ); } IRStatement methodBody; IFunctionStatement stmt = dfs.getDeclFunctionStmt(); if( stmt == null && !_gsClass.isAnonymous() ) // Anonymous classes always have one constructor with no declaration, but they might need explicit compilation due to the super() call therein { setUpFunctionContext( true, parameters ); methodBody = compileDefaultCtorBody(); } else { setUpFunctionContext( dfs, true, parameters ); FunctionStatementTransformer funcStmtCompiler = new FunctionStatementTransformer( dfs, _context ); methodBody = funcStmtCompiler.compile(); } IRMethodStatement methodStatement = new IRMethodStatement( methodBody, "<init>", iModifiers, IRTypeConstants.pVOID(), parameters ); _irClass.addMethod( methodStatement ); } } private IRSymbol makeParamSymbol( ISymbol param, String name ) { IRSymbol irSym = new IRSymbol( name, getDescriptor( param.getType() ), false ); IModifierInfo modifierInfo = param.getModifierInfo(); if( modifierInfo != null && modifierInfo.getAnnotations() != null ) { irSym.setAnnotations( getIRAnnotations( makeAnnotationInfos( modifierInfo.getAnnotations(), getGosuClass().getTypeInfo() ) ) ); } return irSym; } private void maybeGetTypeVarSymbolTypesForConstructor( List<IRSymbol> parameters ) { if( _gsClass.isGenericType() ) { addTypeParamDescriptor( parameters, Arrays.asList( _gsClass.getGenericTypeVariables() ) ); } appendTypeVarsFromEnclosingFunctions( parameters, _gsClass ); } private void maybeGetEnumSuperConstructorSymbols( List<IRSymbol> parameters ) { if( _gsClass.isEnum() ) { parameters.add( new IRSymbol( ENUM_PARAM_PREFIX + "name", getDescriptor( String.class ), false ) ); parameters.add( new IRSymbol( ENUM_PARAM_PREFIX + "ordinal", getDescriptor( int.class ), false ) ); } } private void maybeAddImplicitEnhancementParameters( DynamicFunctionSymbol dfs, List<IRSymbol> parameters ) { if( isCompilingEnhancement() && !dfs.isStatic() ) { parameters.add( new IRSymbol( ENHANCEMENT_THIS_REF, getDescriptor( getGosuEnhancement().getEnhancedType() ), false ) ); } } private void maybeAddImplicitExternalSymbolsParameter( DynamicFunctionSymbol dfs, List<IRSymbol> parameters ) { if( ((_cc().getGosuClass() instanceof IGosuProgram) || (dfs.isStatic() && isProgramOrEnclosedInProgram( _cc().getGosuClass() ))) && !(dfs instanceof IProgramClassFunctionSymbol) && !(dfs instanceof TemplateRenderFunctionSymbol) ) { parameters.add( new IRSymbol( GosuFragmentTransformer.SYMBOLS_PARAM_NAME, getDescriptor( IExternalSymbolMap.class ), false ) ); } } private void maybeGetTypeVarSymbolTypes( DynamicFunctionSymbol dfs, List<IRSymbol> parameters ) { addTypeParamDescriptor( parameters, getTypeVarsForDFS( dfs ) ); } private void addTypeParamDescriptor( List<IRSymbol> parameters, List<IGenericTypeVariable> genTypeVars ) { for( int i = 0; i < genTypeVars.size(); i++ ) { parameters.add( new IRSymbol( getTypeVarParamName( genTypeVars.get( i ) ), getDescriptor( IType.class ), false ) ); } } private void appendTypeVarsFromEnclosingFunctions( List<IRSymbol> parameters, IGosuClassInternal gsClass ) { while( gsClass.isAnonymous() ) { IDynamicFunctionSymbol dfs = getEnclosingDFS( gsClass ); if( dfs == null ) { break; } addTypeParamDescriptor( parameters, getTypeVarsForDFS( dfs ) ); gsClass = (IGosuClassInternal)dfs.getGosuClass(); } } private void maybeGetCapturedSymbolTypes( List<IRSymbol> parameters ) { Map<String, ICapturedSymbol> capturedSymbols = _gsClass.getCapturedSymbols(); if( capturedSymbols != null ) { for( ICapturedSymbol sym : capturedSymbols.values() ) { parameters.add( new IRSymbol( getCapturedSymbolParameterName( sym ), getDescriptor( sym.getType().getArrayType() ), false ) ); } } // The external symbols map is itself always considered captured if( requiresExternalSymbolCapture( _gsClass ) ) { parameters.add( new IRSymbol( GosuFragmentTransformer.SYMBOLS_PARAM_NAME + "arg", getDescriptor( IExternalSymbolMap.class ), true ) ); } } private void maybeGetOuterThisParamType( List<IRSymbol> parameters ) { if( isNonStaticInnerClass() ) { IType enclosingType = getRuntimeEnclosingType( _gsClass ); parameters.add( new IRSymbol( _context.getOuterThisParamName(), getDescriptor( enclosingType ), false ) ); } } private IRStatement compileDefaultCtorBody() { List<IRStatement> statements = new ArrayList<IRStatement>(); maybeAssignOuterRef( statements ); initCapturedSymbolFields( statements ); initTypeVarFields( statements ); List<IRExpression> superArgs = new ArrayList<IRExpression>(); maybePushSupersEnclosingThisRef( superArgs ); pushCapturedSymbols( _cc().getSuperType(), superArgs, false ); int iTypeParams = pushTypeParametersForConstructor( null, _cc().getSuperType(), superArgs ); pushEnumSuperConstructorArguments( superArgs ); IType[] superParameterTypes = IType.EMPTY_ARRAY; if( _gsClass.isEnum() ) { // If the super type is Enum, it explicitly takes a String and an int superParameterTypes = new IType[]{JavaTypes.STRING(), JavaTypes.pINT()}; } IRType[] params = getConstructorParamTypes( superParameterTypes, iTypeParams, _cc().getSuperType() ); IRMethod irMethod = IRMethodFactory.createConstructorIRMethod( _cc().getSuperType(), params ); statements.add( new IRMethodCallStatement( callSpecialMethod( getDescriptor( _cc().getSuperType() ), irMethod, pushThis(), superArgs ) ) ); initializeInstanceFields( statements ); statements.add( new IRReturnStatement() ); return new IRStatementList( false, statements ); } public void maybeAssignOuterRef( List<IRStatement> statements ) { if( isNonStaticInnerClass() ) { // Inner class' outer ref e.g., 'this$0' statements.add( setInstanceField( _gsClass, getOuterThisFieldName(), getDescriptor( getRuntimeEnclosingType( _gsClass ) ), AccessibilityUtil.forOuter(), pushThis(), identifier( _context.getSymbol( _context.getOuterThisParamName() ) ) ) ); } } public void maybePushSupersEnclosingThisRef( List<IRExpression> arguments ) { IType superType = _gsClass.getSupertype(); if( isNonStaticInnerClass( superType ) ) { IGosuClass typeToPass = (IGosuClass)superType.getEnclosingType(); ICompilableTypeInternal outerType = _gsClass.getEnclosingType(); if( outerType == typeToPass || (typeToPass != null && outerType != null && typeToPass.isAssignableFrom( outerType )) ) { arguments.add( identifier( _context.getSymbol( _context.getOuterThisParamName() ) ) ); } else { do { // _mv.visitVarInsn( Opcodes.ALOAD, 1 ); // callStaticMethod( // outerType, getAccessibilityForOuter(), OUTER_ACCESS, outerType.getEnclosingType(), outerType ); outerType = outerType.getEnclosingType(); } while( outerType != typeToPass ); } } } private void compileMethods() { for( IDynamicFunctionSymbol idfs : _gsClass.getStaticFunctions() ) { compileMethod( (DynamicFunctionSymbol)idfs ); } // Create an ordered set from the list because the list may contain duplicates e.g., same dfs for super and overridden generic method. LinkedHashSet<IDynamicFunctionSymbol> methodSet = new LinkedHashSet<IDynamicFunctionSymbol>( _gsClass.getMemberFunctions() ); for( IDynamicFunctionSymbol idfs : methodSet ) { compileMethod( (DynamicFunctionSymbol)idfs ); compileBridgeMethods( (DynamicFunctionSymbol)idfs ); } if( !_gsClass.isInterface() && !isCompilingEnhancement() && !_cc().compilingBlock() ) { compileIntrinsicTypePropertyGetter(); } if( _gsClass.isEnum() ) { compileEnumValuesMethod(); compileEnumAllValuesPropertyGetter(); compileEnumValueOfMethod(); compileEnumValuePropertyGetter(); compileEnumCodePropertyGetter(); compileEnumOrdinalPropertyGetter(); compileEnumDisplayNamePropertyGetter(); compileEnumNamePropertyGetter(); } if( isNonStaticInnerClass() && !_gsClass.isInterface() ) { compileOuterAccessMethod(); } compileMainMethod(); compileAnnotationGetter(); } /** * Generates a synthetic method if: * - method overrides with covariant return type * - for each in ancestry e.g., Square f() -> Rectangle f() -> Shape f(). * - method overrides and specifies a concrete type for a parameter that is a * type variable in the super e.g., B extends A<String> where B#foo( o: String ) -> A#foo( o: T ) */ private void compileBridgeMethods( DynamicFunctionSymbol dfs ) { if( dfs.isAbstract() ) { return; } while( dfs.isOverride() ) { DynamicFunctionSymbol superDfs = dfs.getSuperDfs(); while( superDfs instanceof ParameterizedDynamicFunctionSymbol ) { superDfs = ((ParameterizedDynamicFunctionSymbol)superDfs).getBackingDfs(); } if( genProxyCovariantBridgeMethod( dfs, superDfs ) ) { return; } IRType superRetDescriptor = getDescriptorNoStructures( superDfs.getReturnType() ); IRType overrideRetDescriptor = getDescriptorNoStructures( dfs.getReturnType() ); String superParamDescriptors = getParameterDescriptors( superDfs.getArgTypes() ); String overrideParamDescriptors = getParameterDescriptors( dfs.getArgTypes() ); // e.g., foo( o: String ) -> foo( o: T ) if( !superRetDescriptor.equals( overrideRetDescriptor ) || !overrideParamDescriptors.equals( superParamDescriptors ) ) { // The parameters include all type variable args and all parameters from the DFS List<IRSymbol> parameters = new ArrayList<IRSymbol>(); maybeGetTypeVarSymbolTypes( superDfs, parameters ); for( ISymbol arg : superDfs.getArgs() ) { parameters.add( new IRSymbol( arg.getName(), getDescriptor( arg.getType() ), false ) ); } setUpFunctionContext( superDfs, true, parameters ); // The body of the method is just a call through to the method on this object List<IRExpression> methodCallArgs = new ArrayList<IRExpression>(); maybePassTypeParams( dfs, methodCallArgs ); for( int i = 0; i < dfs.getArgs().size(); i++ ) { // The dfs and superDfs have to have the same number of args. So we go through and pull out the parameter based on // the superDfs name, and cast it to the type of the dfs arg if it's not already compatible ISymbol dfsArg = dfs.getArgs().get( i ); ISymbol superDfsArg = superDfs.getArgs().get( i ); IRExpression arg = identifier( _context.getSymbol( superDfsArg.getName() ) ); IRType expectedType = getDescriptor( dfsArg.getType() ); if( !expectedType.isAssignableFrom( arg.getType() ) ) { arg = buildCast( expectedType, arg ); } methodCallArgs.add( arg ); } IRMethod irMethod = IRMethodFactory.createIRMethod( _gsClass, NameResolver.getFunctionName( dfs ), dfs.getReturnType(), getParamsIncludingTypeParams( dfs ), AccessibilityUtil.forSymbol( dfs ), false ); IRExpression methodCall = callMethod( irMethod, pushThis(), methodCallArgs ); IRStatement methodBody; if( superDfs.getReturnType() != JavaTypes.pVOID() ) { methodBody = new IRReturnStatement( null, methodCall ); } else { methodBody = new IRStatementList( false, new IRMethodCallStatement( methodCall ), new IRReturnStatement() ); } IRMethodStatement bridgeMethod = new IRMethodStatement( methodBody, NameResolver.getFunctionName( superDfs ), makeModifiersForBridgeMethod( getModifiers( dfs ) ), superRetDescriptor, parameters ); _irClass.addMethod( bridgeMethod ); } else { IGosuClassInternal gsProxyClass = superDfs.getGosuClass(); if( gsProxyClass != null && gsProxyClass.isProxy() ) { addCovarientProxyBridgeMethods( superDfs ); break; } } dfs = superDfs; } } private int makeModifiersForBridgeMethod( int modifiers ) { return (modifiers | Opcodes.ACC_BRIDGE | Opcodes.ACC_SYNTHETIC) & ~Opcodes.ACC_ABSTRACT; } /** * Add a bridge method for a Java interface method that is not only implemented * by a method in this Gosu class, but is also itself a covariant "override" of * its super interface E.g., * <pre> * interface JavaBase { * public CharSequence makeText(); * } * * interface JavaSub extends JavaBase { * public String makeText(); * } * * class GosuSub extends JavaSub { * function makeText() : String ... * } * </pre> * Here we need for this class to define a bridge method implementing JavaBase#makeText() : CharSequence */ private void addCovarientProxyBridgeMethods( DynamicFunctionSymbol dfs ) { IGosuClassInternal gsProxyClass = dfs.getGosuClass(); if( gsProxyClass == null || !gsProxyClass.isProxy() ) { // Not a proxy class so no java method to override return; } if( dfs.getReturnType().isPrimitive() ) { // Void or primitive return means no covariant override possible return; } IJavaTypeInternal javaType = (IJavaTypeInternal)gsProxyClass.getJavaType(); if( javaType.isInterface() ) { IJavaClassMethod m = getMethodOverridableFromDfs( dfs, javaType.getBackingClassInfo() ); if( m != null && Modifier.isAbstract( m.getModifiers() ) ) { genInterfaceProxyBridgeMethod( m, javaType.getBackingClassInfo() ); } } } private IJavaClassMethod getMethodOverridableFromDfs( DynamicFunctionSymbol dfs, IJavaClassInfo declaringClass ) { String strName = dfs.getDisplayName(); IJavaClassMethod m = null; if( strName.startsWith( "@" ) ) { strName = strName.substring( 1 ); try { m = declaringClass.getDeclaredMethod( "get" + strName, getClassInfos( dfs.getArgTypes() ) ); } catch( NoSuchMethodException e ) { try { m = declaringClass.getDeclaredMethod( "is" + strName, getClassInfos( dfs.getArgTypes() ) ); } catch( NoSuchMethodException e1 ) { // ignore } } } else { try { m = declaringClass.getDeclaredMethod( strName, getClassInfos( dfs.getArgTypes() ) ); } catch( NoSuchMethodException e ) { // ignore } } return m; } private void genInterfaceProxyBridgeMethod( IJavaClassMethod m, IJavaClassInfo iJavaClassInfo ) { for( IJavaClassInfo iface : iJavaClassInfo.getInterfaces() ) { try { IJavaClassMethod bridge = iface.getDeclaredMethod( m.getName(), m.getParameterTypes() ); if( !bridge.getReturnType().equals( m.getReturnType() ) ) { genBridgeMethod( bridge, m ); m = bridge; } } catch( Exception e ) { // ignore } genInterfaceProxyBridgeMethod( m, iface ); } } private void genBridgeMethod( IJavaClassMethod bridge, IJavaClassMethod m ) { IRType superRetDescriptor = getDescriptor( bridge.getReturnType() ); IRType overrideRetDescriptor = getDescriptor( m.getReturnType() ); String superParamDescriptors = getParameterDescriptors( bridge.getParameterTypes() ); String overrideParamDescriptors = getParameterDescriptors( m.getParameterTypes() ); // e.g., foo( o: String ) -> foo( o: T ) if( !superRetDescriptor.equals( overrideRetDescriptor ) || !overrideParamDescriptors.equals( superParamDescriptors ) ) { // The parameters include all type variable args and all parameters from the DFS List<IRSymbol> parameters = new ArrayList<IRSymbol>(); for( IJavaClassInfo param : bridge.getParameterTypes() ) { parameters.add( new IRSymbol( param.getName(), getDescriptor( param ), false ) ); } setUpFunctionContext( true, parameters ); // The body of the method is just a call through to the method on this object List<IRExpression> methodCallArgs = new ArrayList<IRExpression>(); IJavaClassInfo[] params = m.getParameterTypes(); for( int i = 0; i < params.length; i++ ) { // The dfs and superDfs have to have the same number of args. So we go through and pull out the parameter based on // the superDfs name, and cast it to the type of the dfs arg if it's not already compatible IJavaClassInfo param = params[i]; IJavaClassInfo superParam = bridge.getParameterTypes()[i]; IRExpression arg = identifier( _context.getSymbol( superParam.getName() ) ); IRType expectedType = getDescriptor( param ); if( !expectedType.isAssignableFrom( arg.getType() ) ) { arg = buildCast( expectedType, arg ); } methodCallArgs.add( arg ); } IRMethod irMethod = IRMethodFactory.createIRMethod( _gsClass, m.getName(), getDescriptor( m.getReturnType() ), getIRTypes( m.getParameterTypes() ), IRelativeTypeInfo.Accessibility.fromModifiers( m.getModifiers() ), false ); IRExpression methodCall = callMethod( irMethod, pushThis(), methodCallArgs ); IRStatement methodBody = new IRReturnStatement( null, methodCall ); IRMethodStatement bridgeMethod = new IRMethodStatement( methodBody, bridge.getName(), makeModifiersForBridgeMethod( m.getModifiers() ), superRetDescriptor, parameters ); _irClass.addMethod( bridgeMethod ); } } /** * ##hack: * Potentially generates a bridge method for an overridden method where the super method is in a proxy * and the proxy is for a Java interface having param types that are transformed to non-bytecode types * in the type system. * E.g., A guidewire platform plugin interface may declare UserBase or the like as a parameter type, * which is always represented as the corresponding app derivative: UserBase -> CC's User. Essentially, * we need to generate a bridge method to make the otherwise unsavory covariant parameter types work * in method overrides. */ private boolean genProxyCovariantBridgeMethod( DynamicFunctionSymbol dfs, DynamicFunctionSymbol superDfs ) { IGosuClassInternal superType = (IGosuClassInternal)superDfs.getScriptPart().getContainingType(); if( superType.isProxy() ) { IJavaType javaType = superType.getJavaType(); javaType = (IJavaType)TypeLord.getDefaultParameterizedType( javaType ); IType[] dfsArgTypes = dfs.getArgTypes(); IType[] defDfsArgTypes = new IType[dfsArgTypes.length]; for( int i = 0; i < dfsArgTypes.length; i++ ) { defDfsArgTypes[i] = TypeLord.getDefaultParameterizedTypeWithTypeVars( dfsArgTypes[i] ); } IJavaMethodInfo mi = (IJavaMethodInfo)((IRelativeTypeInfo)javaType.getTypeInfo()).getMethod( javaType, NameResolver.getFunctionName( dfs ), defDfsArgTypes ); if( mi == null ) { // Probably a generic method; the caller will gen bridge method for this return false; } IJavaClassMethod method = mi.getMethod(); IJavaClassInfo[] paramClasses = method.getParameterTypes(); for( int i = 0; i < paramClasses.length; i++ ) { if( !AbstractElementTransformer.isBytecodeType( defDfsArgTypes[i] ) ) { String dfsParamClass = getDescriptor( defDfsArgTypes[i] ).getName().replace( '$', '.' ); if( !dfsParamClass.equals( paramClasses[i].getName().replace( '$', '.' ) ) ) { makeCovariantParamBridgeMethod( dfs, superDfs, method ); return true; } } } if( !AbstractElementTransformer.isBytecodeType( superDfs.getReturnType() ) ) { String returnClassName = getDescriptor( method.getReturnClassInfo() ).getName(); String superReturnClassName = getDescriptor( superDfs.getReturnType() ).getName(); if( !returnClassName.equals( superReturnClassName ) ) { makeCovariantParamBridgeMethod( dfs, superDfs, method ); return true; } } } return false; } private void makeCovariantParamBridgeMethod( DynamicFunctionSymbol dfs, DynamicFunctionSymbol superDfs, IJavaClassMethod method ) { IJavaClassInfo[] paramTypes = method.getParameterTypes(); IType[] chainedMethodParams = getParamsIncludingTypeParams( dfs ); List<IRSymbol> parameters = new ArrayList<IRSymbol>(); for( int i = 0; i < paramTypes.length; i++ ) { parameters.add( new IRSymbol( "arg" + i, getDescriptor( paramTypes[i] ), false ) ); } setUpFunctionContext( superDfs, true, parameters ); List<IRExpression> args = new ArrayList<IRExpression>(); for( int i = 0; i < parameters.size(); i++ ) { IRExpression arg = identifier( parameters.get( i ) ); // If the parameter on the method we're calling through to is not assignable from the type // of the parameter to this method, then we need to wrap the identifier in a check cast. Presumably // this is true for at least one of the parameters to the method, but not for all of them IRType chainedParamType = getDescriptor( chainedMethodParams[i] ); if( !chainedParamType.isAssignableFrom( arg.getType() ) ) { arg = buildCast( chainedParamType, arg ); } args.add( arg ); } IRMethod irMethod = IRMethodFactory.createIRMethod( _gsClass, NameResolver.getFunctionName( dfs ), dfs.getReturnType(), chainedMethodParams, AccessibilityUtil.forSymbol( dfs ), false ); IRExpression methodCall = callMethod( irMethod, pushThis(), args ); IRStatementList methodBody = new IRStatementList( true ); if( !method.getReturnType().getName().equals( Void.TYPE.getName() ) ) { methodBody.addStatement( new IRReturnStatement( null, methodCall ) ); } else { methodBody.addStatement( buildMethodCall( methodCall ) ); methodBody.addStatement( buildReturn() ); } IRMethodStatement bridgeMethod = new IRMethodStatement( methodBody, method.getName(), makeModifiersForBridgeMethod( getModifiers( dfs ) ), getDescriptor( method.getReturnClassInfo() ), parameters ); _irClass.addMethod( bridgeMethod ); } private IType[] getParamsIncludingTypeParams( DynamicFunctionSymbol dfs ) { List<IGenericTypeVariable> typeVars = getTypeVarsForDFS( dfs ); final IType[] argTypes = dfs.getArgTypes(); IType[] paramTypes = new IType[typeVars.size() + argTypes.length]; System.arraycopy( argTypes, 0, paramTypes, typeVars.size(), argTypes.length ); for( int i = 0; i < typeVars.size(); i++ ) { paramTypes[i] = JavaTypes.ITYPE(); } return paramTypes; } private void maybePassTypeParams( DynamicFunctionSymbol dfs, List<IRExpression> args ) { IType type = dfs.getType(); if( type.isGenericType() ) { IGenericTypeVariable[] typeVars = type.getGenericTypeVariables(); for( int i = 0; i < typeVars.length; i++ ) { args.add( identifier( _context.getSymbol( getTypeVarParamName( typeVars[i] ) ) ) ); } } } private void compileOuterAccessMethod() { int iModifiers = Opcodes.ACC_STATIC + Opcodes.ACC_SYNTHETIC; iModifiers |= (BytecodeOptions.isSingleServingLoader() ? Opcodes.ACC_PUBLIC : 0); IRSymbol staticThis = new IRSymbol( "staticThis", getDescriptor( _gsClass ), false ); setUpFunctionContext( false, Collections.singletonList( staticThis ) ); IRStatement body = new IRReturnStatement( null, getInstanceField( _gsClass, getOuterThisFieldName(), getDescriptor( getRuntimeEnclosingType( _gsClass ) ), AccessibilityUtil.forOuter(), identifier( staticThis ) ) ); _irClass.addMethod( new IRMethodStatement( body, OUTER_ACCESS, iModifiers, getDescriptor( getRuntimeEnclosingType( _gsClass ) ), Collections.singletonList( staticThis ) ) ); } private void compileMethod( DynamicFunctionSymbol dfs ) { if( isGosuObjectMethod( dfs ) || isStaticEnumMethod( dfs ) ) { return; } if( !(dfs instanceof TemplateRenderFunctionSymbol) && getGosuClass() instanceof IGosuTemplateInternal ) { // Only compile renderXxx() methods on templates return; } List<IRSymbol> parameters = new ArrayList<IRSymbol>(); maybeAddImplicitEnhancementParameters( dfs, parameters ); maybeGetTypeVarSymbolTypes( dfs, parameters ); maybeAddImplicitExternalSymbolsParameter( dfs, parameters ); for( ISymbol param : dfs.getArgs() ) { String name = param.getName(); if( isBlockInvoke( dfs ) ) { name = name + "$$blockParam"; } else if( param.isValueBoxed() ) { name = name + "$$unboxedParam"; } if( param.getName().equals( "p0" ) && param.getType().equals( JavaTypes.IEXTERNAL_SYMBOL_MAP() ) ) { name = GosuFragmentTransformer.SYMBOLS_PARAM_NAME; } parameters.add( makeParamSymbol( param, name ) ); } IRStatement methodBody; if( !dfs.isAbstract() ) { IStatement stmt = (IStatement)dfs.getValueDirectly(); if( stmt != null ) { setUpFunctionContext( dfs, !dfs.isStatic() && !isCompilingEnhancement(), parameters ); FunctionStatementTransformer funcStmtCompiler = new FunctionStatementTransformer( dfs, _context ); methodBody = funcStmtCompiler.compile(); } else { methodBody = new IRNoOpStatement(); // Label label = new Label(); // _mv.visitLabel( label ); // _mv.visitLineNumber( dfs.getDeclFunctionStmt().getLineNum(), label ); // _mv.visitMaxs( 0, 0 ); } } else { methodBody = null; } IRMethodStatement method = new IRMethodStatement( methodBody, NameResolver.getFunctionName( dfs ), getModifiers( dfs ), getDescriptor( dfs.getReturnType() ), parameters ); method.setAnnotations( getIRAnnotations( makeAnnotationInfos( dfs.getModifierInfo().getAnnotations(), getGosuClass().getTypeInfo() ) ) ); _irClass.addMethod( method ); } private boolean isStaticEnumMethod( DynamicFunctionSymbol dfs ) { return getGosuClass().isEnum() && (dfs instanceof EnumValueOfFunctionSymbol || dfs instanceof EnumValuesFunctionSymbol); } private boolean isGosuObjectMethod( DynamicFunctionSymbol dfs ) { return dfs.getScriptPart() != null && dfs.getScriptPart().getContainingType() == getGosuObjectInterface(); } private IGosuClassInternal getGosuObjectInterface() { return IGosuClassInternal.Util.getGosuClassFrom( JavaTypes.IGOSU_OBJECT() ); } private void compileIntrinsicTypePropertyGetter() { setUpFunctionContext( true, Collections.<IRSymbol>emptyList() ); //## todo: Cache the type... IRExpression getTypeExpression = callStaticMethod( GosuRuntimeMethods.class, "getType", new Class[]{Object.class}, Collections.singletonList( pushThis() ) ); if( _gsClass.isGenericType() ) { getTypeExpression = callMethod( IType.class, "getParameterizedType", new Class[]{IType[].class}, getTypeExpression, Collections.singletonList( makeArrayOfTypeParameters() ) ); } IRStatement methodBody = new IRReturnStatement( null, getTypeExpression ); IRMethodStatement method = new IRMethodStatement( methodBody, "getIntrinsicType", // Note this is synthetic as a hacky way to hide property from tools (e.g. hibernate) Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC, getDescriptor( IType.class ), Collections.<IRSymbol>emptyList() ); _irClass.addMethod( method ); } private void compileMainMethod() { if( !(getGosuClass() instanceof IGosuProgram) ) { return; } // // Simply constructs an instance of the program and then invokes the evaluate() method. // List<IType> paramTypes = new ArrayList<IType>(); IMethodInfo evaluateMethod = null; for( IMethodInfo mi : getGosuClass().getTypeInfo().getMethods() ) { if( mi.getName().startsWith( "evaluate(" ) ) { evaluateMethod = mi; for( IParameterInfo param : mi.getParameters() ) { IType paramType = param.getFeatureType(); paramTypes.add( paramType ); } break; } } IRExpression newProgram = buildNewExpression( IRTypeResolver.getDescriptor( getGosuClass() ), Collections.<IRType>emptyList(), Collections.<IRExpression>emptyList() ); IRMethod evaluateIRMethod = IRMethodFactory.createIRMethod( getGosuClass(), "evaluate", evaluateMethod.getReturnType(), paramTypes.toArray( new IType[paramTypes.size()] ), IRelativeTypeInfo.Accessibility.PUBLIC, false ); IRExpression callEvaluate = callMethod( evaluateIRMethod, newProgram, Collections.singletonList( nullLiteral() ) ); IRStatement methodBody = new IRStatementList( true, buildMethodCall( callEvaluate ), buildReturn() ); IRMethodStatement method = new IRMethodStatement( methodBody, "main", Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, getDescriptor( void.class ), Collections.singletonList( new IRSymbol( "args", getDescriptor( String[].class ), false ) ) ); _irClass.addMethod( method ); } private void compileAnnotationGetter() { if( !getGosuClass().isInterface() ) { addEvalAnnotationMethod( _irClass ); } } private void addEvalAnnotationMethod( IRClass irClass ) { List<StatementList> annotationInitMethods = getGosuClass().getAnnotationInitialization(); IRType annMapIRClass = JavaClassIRType.get( AnnotationMap.class ); IRSymbol builderSymbol = new IRSymbol( AnnotationBuilder.BUILDER_SYMBOL.get().getName(), annMapIRClass, false ); for( int i = 0; i < annotationInitMethods.size(); i++ ) { StatementList initMethod = annotationInitMethods.get( i ); List<IRSymbol> args = new ArrayList<IRSymbol>(); String ending = ""; if( i != 0 ) { ending = "" + i; args.add( builderSymbol ); } setUpFunctionContext( false, args ); IRStatementList statement = (IRStatementList)StatementTransformer.compile( _context, initMethod ); if( i < annotationInitMethods.size() - 1 ) { IRMethod method = IRMethodFactory.createIRMethod( getGosuClass(), GosuClass.EVAL_ANNOTATIONS_METHOD + (i + 1), JavaClassIRType.get( Map.class ), Arrays.asList( annMapIRClass ), IRelativeTypeInfo.Accessibility.PUBLIC, true ); statement.addStatement( new IRReturnStatement( null, callMethod( method, null, exprList( identifier( builderSymbol ) ) ) ) ); } IRMethodStatement method = new IRMethodStatement( statement, GosuClass.EVAL_ANNOTATIONS_METHOD + ending, Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, getDescriptor( Map.class ), args ); irClass.addMethod( method ); } } private void compileEnumValuesMethod() { setUpFunctionContext( true, Collections.<IRSymbol>emptyList() ); int numberOfEnumFields = 0; for( IVarStatement varStatement : _gsClass.getStaticFields() ) { if( varStatement.isEnumConstant() ) { numberOfEnumFields++; } } // We want to generate approximately the following code: // Foo[] temp = new Foo[numberOfEnumFields]; // System.arrayCopy(ENUM_VALUES_FIELD, 0, temp, 0, numberOfEnumFields); // return temp; List<IRStatement> bodyStatements = new ArrayList<IRStatement>(); IRSymbol tempSymbol = _context.makeAndIndexTempSymbol( getDescriptor( _gsClass.getArrayType() ) ); IRStatement arrayCreation = buildAssignment( tempSymbol, newArray( getDescriptor( _gsClass ), numericLiteral( numberOfEnumFields ) ) ); bodyStatements.add( arrayCreation ); IRExpression arrayCopy = callStaticMethod( System.class, "arraycopy", new Class[]{Object.class, int.class, Object.class, int.class, int.class}, exprList( getStaticField( _gsClass, ENUM_VALUES_FIELD, getDescriptor( _gsClass.getArrayType() ), IRelativeTypeInfo.Accessibility.PUBLIC ), numericLiteral( 0 ), identifier( tempSymbol ), numericLiteral( 0 ), numericLiteral( numberOfEnumFields ) ) ); bodyStatements.add( buildMethodCall( arrayCopy ) ); bodyStatements.add( new IRReturnStatement( null, identifier( tempSymbol ) ) ); IRMethodStatement method = new IRMethodStatement( new IRStatementList( true, bodyStatements ), "values", Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, getDescriptor( _gsClass.getArrayType() ), Collections.<IRSymbol>emptyList() ); _irClass.addMethod( method ); } private void compileEnumAllValuesPropertyGetter() { setUpFunctionContext( true, Collections.<IRSymbol>emptyList() ); IRMethod valuesMethod = IRMethodFactory.createIRMethod( _gsClass, "values", _gsClass.getArrayType(), new IType[0], IRelativeTypeInfo.Accessibility.PUBLIC, true ); IRExpression result = callStaticMethod( Arrays.class, "asList", new Class[]{Object[].class}, exprList( callMethod( valuesMethod, null, exprList() ) ) ); IRMethodStatement method = new IRMethodStatement( new IRStatementList( true, new IRReturnStatement( null, result ) ), "getAllValues", Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, getDescriptor( List.class ), Collections.<IRSymbol>emptyList() ); _irClass.addMethod( method ); } private void compileEnumValueOfMethod() { IRSymbol argSymbol = new IRSymbol( "arg", IRTypeConstants.STRING(), false ); setUpFunctionContext( true, Collections.singletonList( argSymbol ) ); IRExpression result = callStaticMethod( Enum.class, "valueOf", new Class[]{Class.class, String.class}, exprList( classLiteral( getDescriptor( _gsClass ) ), identifier( argSymbol ) ) ); IRMethodStatement method = new IRMethodStatement( new IRStatementList( true, new IRReturnStatement( null, checkCast( _gsClass, result ) ) ), "valueOf", Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC, getDescriptor( _gsClass ), Collections.singletonList( argSymbol ) ); _irClass.addMethod( method ); } private void compileEnumValuePropertyGetter() { setUpFunctionContext( true, Collections.<IRSymbol>emptyList() ); IRMethodStatement method = new IRMethodStatement( new IRStatementList( true, new IRReturnStatement( null, pushThis() ) ), "getValue", Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL, IRTypeConstants.OBJECT(), Collections.<IRSymbol>emptyList() ); _irClass.addMethod( method ); } private void compileEnumCodePropertyGetter() { // Don't bother if the class already has the property defined IDynamicPropertySymbol existingProperty = getGosuClass().getMemberProperty( "Code" ); if( existingProperty != null && !(existingProperty instanceof EnumCodePropertySymbol) ) { return; } setUpFunctionContext( true, Collections.<IRSymbol>emptyList() ); IRExpression returnValue = callMethod( Enum.class, "name", new Class[0], pushThis(), exprList() ); IRMethodStatement method = new IRMethodStatement( new IRStatementList( true, new IRReturnStatement( null, returnValue ) ), "getCode", Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL, IRTypeConstants.STRING(), Collections.<IRSymbol>emptyList() ); _irClass.addMethod( method ); } private void compileEnumOrdinalPropertyGetter() { setUpFunctionContext( true, Collections.<IRSymbol>emptyList() ); IRExpression returnValue = callMethod( Enum.class, "ordinal", new Class[0], pushThis(), exprList() ); IRMethodStatement method = new IRMethodStatement( new IRStatementList( true, new IRReturnStatement( null, returnValue ) ), "getOrdinal", Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL, IRTypeConstants.pINT(), Collections.<IRSymbol>emptyList() ); _irClass.addMethod( method ); } private void compileEnumNamePropertyGetter() { // Don't bother if the class already has the property defined IDynamicPropertySymbol existingProperty = getGosuClass().getMemberProperty( (String)"Name" ); if( existingProperty != null && !(existingProperty instanceof EnumNamePropertySymbol) ) { return; } setUpFunctionContext( true, Collections.<IRSymbol>emptyList() ); IRExpression returnValue = callMethod( Enum.class, "name", new Class[0], pushThis(), exprList() ); IRMethodStatement method = new IRMethodStatement( new IRStatementList( true, new IRReturnStatement( null, returnValue ) ), "getName", Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL, IRTypeConstants.STRING(), Collections.<IRSymbol>emptyList() ); _irClass.addMethod( method ); } private void compileEnumDisplayNamePropertyGetter() { // Don't bother if the class already has the property defined IDynamicPropertySymbol existingProperty = getGosuClass().getMemberProperty( (String)"DisplayName" ); if( existingProperty != null && !(existingProperty instanceof EnumDisplayNamePropertySymbol) ) { return; } setUpFunctionContext( true, Collections.<IRSymbol>emptyList() ); IRExpression returnValue = callMethod( Enum.class, "toString", new Class[0], pushThis(), exprList() ); IRMethodStatement method = new IRMethodStatement( new IRStatementList( true, new IRReturnStatement( null, returnValue ) ), "getDisplayName", Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL, IRTypeConstants.STRING(), Collections.<IRSymbol>emptyList() ); _irClass.addMethod( method ); } private IRExpression makeArrayOfTypeParameters() { IGenericTypeVariable[] genTypeVars = _gsClass.getGenericTypeVariables(); List<IRExpression> values = new ArrayList<IRExpression>(); for( IGenericTypeVariable gv : genTypeVars ) { values.add( getInstanceField( _gsClass, TYPE_PARAM_PREFIX + gv.getName(), IRTypeConstants.ITYPE(), AccessibilityUtil.forTypeParameter(), pushThis() ) ); } return buildInitializedArray(IRTypeConstants.ITYPE(), values ); } public void initializeInstanceFields( List<IRStatement> statements ) { List<IVarStatement> fields = getOrderedFields(); for( IVarStatement field : fields ) { statements.add( FieldInitializerTransformer.compile( _context, field ) ); } } public void initTypeVarFields( List<IRStatement> statements ) { IGosuClassInternal gsClass = _gsClass; if( gsClass.isGenericType() ) { for( IGenericTypeVariable genTypeVar : gsClass.getGenericTypeVariables() ) { statements.add( setInstanceField( gsClass, TYPE_PARAM_PREFIX + genTypeVar.getName(), IRTypeConstants.ITYPE(), AccessibilityUtil.forTypeParameter(), pushThis(), identifier( _context.getSymbol( getTypeVarParamName( genTypeVar ) ) ) ) ); } } while( gsClass.isAnonymous() ) { IDynamicFunctionSymbol dfs = getEnclosingDFS( gsClass ); if( dfs == null ) { break; } for( IGenericTypeVariable genTypeVar : getTypeVarsForDFS( dfs ) ) { statements.add( setInstanceField( _gsClass, TYPE_PARAM_PREFIX + genTypeVar.getName(), IRTypeConstants.ITYPE(), AccessibilityUtil.forTypeParameter(), pushThis(), identifier( _context.getSymbol( getTypeVarParamName( genTypeVar ) ) ) ) ); } gsClass = (IGosuClassInternal)dfs.getGosuClass(); } } public void initCapturedSymbolFields( List<IRStatement> statements ) { Map<String, ICapturedSymbol> capturedSymbols = _gsClass.getCapturedSymbols(); if( capturedSymbols != null ) { @SuppressWarnings({"UnusedDeclaration"}) int iIndex = 1; // one for 'this', and... iIndex += (_gsClass.isStatic() ? 0 : 1); // ...one for outer 'this' (if non-static) for( ICapturedSymbol sym : capturedSymbols.values() ) { statements.add( setInstanceField( _gsClass, CAPTURED_VAR_PREFIX + sym.getName(), getDescriptor( sym.getType().getArrayType() ), AccessibilityUtil.forCapturedVar(), pushThis(), identifier( _context.getSymbol( getCapturedSymbolParameterName( sym ) ) ) ) ); } } if( requiresExternalSymbolCapture( _gsClass ) ) { statements.add( setInstanceField( _gsClass, GosuFragmentTransformer.SYMBOLS_PARAM_NAME, getDescriptor( IExternalSymbolMap.class ), AccessibilityUtil.forCapturedVar(), pushThis(), identifier( _context.getSymbol( GosuFragmentTransformer.SYMBOLS_PARAM_NAME + "arg" ) ) ) ); } } public void initializeStaticFields( List<IRStatement> statements ) { boolean needToCompileEnumValuesField = _gsClass.isEnum(); List<IVarStatement> fields = _gsClass.getStaticFields(); for( IVarStatement field : fields ) { if( field.isEnumConstant() ) { _enumCounter.increment( field.getIdentifierName().toString() ); } else if( needToCompileEnumValuesField && !field.isEnumConstant() ) { // We want to insert the values field directly after the last constant field compileEnumValuesFieldInitializer( statements ); needToCompileEnumValuesField = false; } statements.add( FieldInitializerTransformer.compile( _context, field ) ); } // Initialize the field here just in case the enum class had no constants if( needToCompileEnumValuesField ) { compileEnumValuesFieldInitializer( statements ); } if( getGosuClass().hasAssertions() ) { statements.add( initializeAssertionsDisabledField() ); } } private IRStatement initializeAssertionsDisabledField() { return buildFieldSet( _irClass.getThisType(), AssertStatementTransformer.$_ASSERTIONS_DISABLED, IRTypeConstants.pBOOLEAN(), null, buildEquals( buildMethodCall( Class.class, "desiredAssertionStatus", boolean.class, new Class[0], classLiteral( GosuClassIRType.get( getGosuClass() ) ), Collections.<IRExpression>emptyList() ), booleanLiteral( false ) ) ); } private void compileEnumValuesFieldInitializer( List<IRStatement> statements ) { List<IVarStatement> enumFields = new ArrayList<IVarStatement>(); for( IVarStatement varStatement : _gsClass.getStaticFields() ) { if( varStatement.isEnumConstant() ) { enumFields.add( varStatement ); } } List<IRExpression> values = new ArrayList<IRExpression>(); for( int i = 0; i < enumFields.size(); i++ ) { values.add( getStaticField( _gsClass, enumFields.get( i ).getIdentifierName().toString(), getDescriptor( enumFields.get( i ).getType() ), IRelativeTypeInfo.Accessibility.PUBLIC ) ); } IRExpression arrayBuilder = buildInitializedArray( getDescriptor( _gsClass ), values ); statements.add( setStaticField( _gsClass, ENUM_VALUES_FIELD, getDescriptor( _gsClass.getArrayType() ), IRelativeTypeInfo.Accessibility.PUBLIC, arrayBuilder ) ); } private List<IVarStatement> getOrderedFields() { //noinspection unchecked List<MemberFieldSymbol> fields = new ArrayList( _gsClass.getMemberFieldIndexByName().values() ); Collections.sort( fields, new Comparator<MemberFieldSymbol>() { public int compare( MemberFieldSymbol o1, MemberFieldSymbol o2 ) { return o1.getIndex() - o2.getIndex(); } } ); List<IVarStatement> fieldStmts = new ArrayList<IVarStatement>( fields.size() ); for( MemberFieldSymbol field : fields ) { fieldStmts.add( _gsClass.getMemberField( (String)field.getName() ) ); } return fieldStmts; } private IRType getDescriptorNoStructures( IType type ) { if( type instanceof IGosuClassInternal && ((IGosuClassInternal)type).isStructure() ) { type = JavaTypes.OBJECT(); } return getDescriptor( type ); } private String getParameterDescriptors( IType[] types ) { StringBuilder sb = new StringBuilder(); for( IType type : types ) { if( type instanceof IGosuClassInternal && ((IGosuClassInternal)type).isStructure() ) { type = JavaTypes.OBJECT(); } sb.append( getDescriptor( type ).getName() ); } return sb.toString(); } private String getParameterDescriptors( IJavaClassInfo[] iJavaClassInfos ) { StringBuffer sb = new StringBuffer(); for( IJavaClassInfo type : iJavaClassInfos ) { sb.append( getDescriptor( type ).getName() ); } return sb.toString(); } private String getParameterDescriptors( Class[] types ) { StringBuilder sb = new StringBuilder(); for( Class type : types ) { sb.append( getDescriptor( type ).getName() ); } return sb.toString(); } private void compileClassHeader() { _irClass.setName( _gsClass.getName() ); _irClass.setThisType( getDescriptor( _gsClass ) ); _irClass.setModifiers( getClassModifiers() ); _irClass.setSuperType( getSuperSlashName() ); for( IRType iface : getInterfaceNames() ) { _irClass.addInterface( iface ); } } private IRType getSuperSlashName() { return _gsClass.getSupertype() == null ? getDescriptor( Object.class ) : getDescriptor( _gsClass.getSupertype() ); } private void addSourceFileRef() { String sourceFileRef = _context.getSourceFileRef(); if( sourceFileRef != null ) { _irClass.setSourceFile( sourceFileRef ); } } private int getClassModifiers() { return getClassModifiers( _gsClass, false ); } private int getClassModifiers( IGosuClass gsClass, boolean bForInnerClass ) { int iModifiers = 0; if( gsClass == _gsClass ) { if( gsClass.isInterface() ) { iModifiers |= Opcodes.ACC_INTERFACE; } else { if( gsClass.isEnum() ) { iModifiers |= Opcodes.ACC_ENUM | Opcodes.ACC_FINAL; } if( !bForInnerClass ) { iModifiers |= Opcodes.ACC_SUPER; } } } int iGsModifiers = gsClass.getModifiers(); if( Modifier.isPublic( iGsModifiers ) || (!Modifier.isInternal( iGsModifiers ) && !Modifier.isPrivate( iGsModifiers )) || BytecodeOptions.isSingleServingLoader() ) { iModifiers |= Opcodes.ACC_PUBLIC; } else if( bForInnerClass && gsClass.getEnclosingType() != null && Modifier.isPrivate( iGsModifiers ) && !BytecodeOptions.isSingleServingLoader() ) { iModifiers |= Opcodes.ACC_PRIVATE; } if( Modifier.isFinal( iGsModifiers ) ) { iModifiers |= Opcodes.ACC_FINAL; } if( Modifier.isAbstract( iGsModifiers ) || gsClass.isInterface() ) { iModifiers |= Opcodes.ACC_ABSTRACT; } if( bForInnerClass && (Modifier.isStatic( iGsModifiers ) || (gsClass.getEnclosingType() != null && gsClass.isInterface())) ) { iModifiers |= Opcodes.ACC_STATIC; } return iModifiers; } private IRType[] getInterfaceNames() { IType[] interfaces = _gsClass.getInterfaces(); if( interfaces == null || interfaces.length == 0 ) { return new IRType[0]; } List<IRType> ifaceNames = new ArrayList<IRType>(); for( IType iface : interfaces ) { if( iface == getGosuObjectInterface() ) { iface = JavaTypes.IGOSU_CLASS_OBJECT(); } IRType irInterface = getDescriptor( iface ); if( !ifaceNames.contains( irInterface ) ) { ifaceNames.add( irInterface ); } } return ifaceNames.toArray( new IRType[ifaceNames.size()] ); } public IGosuEnhancementInternal getGosuEnhancement() { return (IGosuEnhancementInternal)_gsClass; } public void pushEnumNameAndOrdinal( IType type, List<IRExpression> args ) { if( type.isEnum() ) { args.add( pushConstant( _enumCounter.getNextEnumName() ) ); args.add( pushConstant( _enumCounter.getNextEnumOrdinal() ) ); } } public void setHasAsserts() { if( _bHasAsserts ) { return; } _bHasAsserts = true; IRFieldDecl fieldDecl = new IRFieldDecl( 0x1018, AssertStatementTransformer.$_ASSERTIONS_DISABLED, getDescriptor( JavaTypes.pBOOLEAN() ), null ); _irClass.addField( fieldDecl ); } // // public void addCtxMethod( List<IType> args ) // { // _ctxMethods.add( args ); // } // static final class EnumOrdinalCounter { private int _nextEnumOrdinal = -1; private String _nextEnumName; public int getNextEnumOrdinal() { return _nextEnumOrdinal; } public String getNextEnumName() { return _nextEnumName; } private void increment( String name ) { _nextEnumOrdinal++; _nextEnumName = name; } } // ------------------------------------------- Additions public boolean isBlockInvoke( DynamicFunctionSymbol dfs ) { return dfs.getDisplayName().equals( BlockClass.INVOKE_METHOD_NAME ) && _context.compilingBlock(); } private void setUpFunctionContext( boolean instanceMethod, List<IRSymbol> params ) { _context.initBodyContext( !instanceMethod ); _context.pushScope( instanceMethod ); _context.putSymbols( params ); } private void setUpFunctionContext( DynamicFunctionSymbol dfs, boolean instanceMethod, List<IRSymbol> params ) { _context.initBodyContext( dfs.isStatic(), dfs ); _context.pushScope( instanceMethod ); _context.putSymbols( params ); } @Override public String toString() { return "Transforming Class: " + getGosuClass().getName(); } }