/*
* Copyright 2013 Guidewire Software, Inc.
*/
package gw.internal.gosu.ir.transform;
import gw.config.CommonServices;
import gw.internal.ext.org.objectweb.asm.Opcodes;
import gw.internal.ext.org.objectweb.asm.Type;
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.IRProperty;
import gw.internal.gosu.ir.nodes.JavaClassIRType;
import gw.internal.gosu.ir.nodes.SyntheticIRMethod;
import gw.internal.gosu.ir.transform.util.AccessibilityUtil;
import gw.internal.gosu.ir.transform.util.IRTypeResolver;
import gw.internal.gosu.ir.transform.util.RequiresReflectionDeterminer;
import gw.internal.gosu.parser.AbstractDynamicSymbol;
import gw.internal.gosu.parser.BeanAccess;
import gw.internal.gosu.parser.CompoundType;
import gw.internal.gosu.parser.DynamicFunctionSymbol;
import gw.internal.gosu.parser.Expression;
import gw.internal.gosu.parser.GosuAnnotationInfo;
import gw.internal.gosu.parser.GosuParser;
import gw.internal.gosu.parser.ICompilableTypeInternal;
import gw.internal.gosu.parser.IGosuAnnotation;
import gw.internal.gosu.parser.IGosuClassInternal;
import gw.internal.gosu.parser.IGosuTemplateInternal;
import gw.internal.gosu.parser.MetaType;
import gw.internal.gosu.parser.NewIntrospector;
import gw.internal.gosu.parser.ReducedDynamicFunctionSymbol;
import gw.internal.gosu.parser.ScopedDynamicSymbol;
import gw.internal.gosu.parser.Symbol;
import gw.internal.gosu.parser.TypeLord;
import gw.internal.gosu.parser.TypeVariableArrayType;
import gw.internal.gosu.parser.TypeVariableType;
import gw.internal.gosu.parser.expressions.BlockType;
import gw.internal.gosu.parser.fragments.GosuFragment;
import gw.internal.gosu.parser.types.FunctionLiteralType;
import gw.internal.gosu.runtime.GosuRuntimeMethods;
import gw.lang.ir.IRElement;
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.builder.IRArgConverter;
import gw.lang.ir.expression.IRArithmeticExpression;
import gw.lang.ir.expression.IRArrayLengthExpression;
import gw.lang.ir.expression.IRArrayLoadExpression;
import gw.lang.ir.expression.IRBooleanLiteral;
import gw.lang.ir.expression.IRCastExpression;
import gw.lang.ir.expression.IRCharacterLiteral;
import gw.lang.ir.expression.IRClassLiteral;
import gw.lang.ir.expression.IRCompositeExpression;
import gw.lang.ir.expression.IREqualityExpression;
import gw.lang.ir.expression.IRFieldGetExpression;
import gw.lang.ir.expression.IRIdentifier;
import gw.lang.ir.expression.IRMethodCallExpression;
import gw.lang.ir.expression.IRNegationExpression;
import gw.lang.ir.expression.IRNewArrayExpression;
import gw.lang.ir.expression.IRNewExpression;
import gw.lang.ir.expression.IRNullLiteral;
import gw.lang.ir.expression.IRNumericLiteral;
import gw.lang.ir.expression.IRPrimitiveTypeConversion;
import gw.lang.ir.expression.IRRelationalExpression;
import gw.lang.ir.expression.IRStringLiteralExpression;
import gw.lang.ir.expression.IRTernaryExpression;
import gw.lang.ir.statement.IRArrayStoreStatement;
import gw.lang.ir.statement.IRAssignmentStatement;
import gw.lang.ir.statement.IRFieldSetStatement;
import gw.lang.ir.statement.IRIfStatement;
import gw.lang.ir.statement.IRMethodCallStatement;
import gw.lang.ir.statement.IRNewStatement;
import gw.lang.ir.statement.IRReturnStatement;
import gw.lang.ir.statement.IRThrowStatement;
import gw.lang.parser.GlobalScope;
import gw.lang.parser.IAttributeSource;
import gw.lang.parser.IBlockClass;
import gw.lang.parser.ICapturedSymbol;
import gw.lang.parser.ICoercionManager;
import gw.lang.parser.ICustomExpressionRuntime;
import gw.lang.parser.IDynamicFunctionSymbol;
import gw.lang.parser.IExpression;
import gw.lang.parser.IParsedElement;
import gw.lang.parser.IReducedSymbol;
import gw.lang.parser.ISymbol;
import gw.lang.parser.Keyword;
import gw.lang.parser.statements.IFunctionStatement;
import gw.lang.reflect.FunctionType;
import gw.lang.reflect.IBlockType;
import gw.lang.reflect.IConstructorInfo;
import gw.lang.reflect.IEntityAccess;
import gw.lang.reflect.IFeatureInfo;
import gw.lang.reflect.IFunctionType;
import gw.lang.reflect.IMetaType;
import gw.lang.reflect.IParameterInfo;
import gw.lang.reflect.IPropertyInfo;
import gw.lang.reflect.IRelativeTypeInfo;
import gw.lang.reflect.IType;
import gw.lang.reflect.ITypeVariableType;
import gw.lang.reflect.Modifier;
import gw.lang.reflect.PropertyInfoDelegate;
import gw.lang.reflect.TypeSystem;
import gw.lang.reflect.gs.BytecodeOptions;
import gw.lang.reflect.gs.ICompilableType;
import gw.lang.reflect.gs.IExternalSymbolMap;
import gw.lang.reflect.gs.IGenericTypeVariable;
import gw.lang.reflect.gs.IGosuClass;
import gw.lang.reflect.gs.IGosuConstructorInfo;
import gw.lang.reflect.gs.IGosuEnhancement;
import gw.lang.reflect.gs.IGosuMethodInfo;
import gw.lang.reflect.gs.IGosuProgram;
import gw.lang.reflect.gs.IGosuVarPropertyInfo;
import gw.lang.reflect.java.IJavaClassInfo;
import gw.lang.reflect.java.IJavaClassMethod;
import gw.lang.reflect.java.IJavaConstructorInfo;
import gw.lang.reflect.java.IJavaType;
import gw.lang.reflect.java.JavaTypes;
import gw.lang.reflect.module.IModule;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
*/
public abstract class AbstractElementTransformer<T extends IParsedElement>
{
public static final String CTX_SYMBOL_SUFFIX = "_instr_ctx";
public static final String CTX_SYMBOL = FunctionBodyTransformationContext.TEMP_VAR_PREFIX + CTX_SYMBOL_SUFFIX;
public static final Map<String, ICustomExpressionRuntime> CUSTOM_RUNTIMES = Collections.synchronizedMap( new HashMap<String, ICustomExpressionRuntime>() );
public static final Class[] EMPTY_CLASS_ARRAY = new Class[0];
public static final String OUTER_ACCESS = "access$0";
public static final String CAPTURED_VAR_PREFIX = "val$";
public static final String ENHANCEMENT_TYPE_PARAM_PREFIX = "etypeparam$";
public static final String TYPE_PARAM_PREFIX = "typeparam$";
public static final String ENUM_PARAM_PREFIX = "enum$";
public static final Type OBJECT_TYPE = Type.getType( Object.class );
public static final String ENHANCEMENT_THIS_REF = "$that$";
private TopLevelTransformationContext _cc;
private T _parsedElement;
public AbstractElementTransformer( TopLevelTransformationContext cc, T parsedElem )
{
_cc = cc;
_parsedElement = parsedElem;
}
public static void clearCustomRuntimes() {
CUSTOM_RUNTIMES.clear();
}
public TopLevelTransformationContext _cc()
{
return _cc;
}
protected void setCc( TopLevelTransformationContext cc )
{
_cc = cc;
}
protected T getParsedElement()
{
return _parsedElement;
}
public ICompilableTypeInternal getGosuClass()
{
return _cc().getGosuClass();
}
public IRExpression callStaticMethod( Class cls, String strMethod, Class[] paramTypes, List<IRExpression> args )
{
return callMethod( cls, strMethod, paramTypes, null, args );
}
//TODO-sm Remove this method.
public IRExpression callMethod( Class cls, String strMethod, Class[] paramTypes, IRExpression root, List<IRExpression> args )
{
return callMethod( IRMethodFactory.createIRMethod( cls, strMethod, paramTypes ), root, args );
}
public IRExpression callMethod( IJavaClassInfo cls, String strMethod, Class[] paramTypes, IRExpression root, List<IRExpression> args )
{
return callMethod( IRMethodFactory.createIRMethod( cls, strMethod, paramTypes ), root, args );
}
public IRExpression callMethod( IRMethod method, IRExpression root, List<IRExpression> explicitArgs )
{
return callMethod( method.getOwningIRType(), method, root, Collections.<IRExpression>emptyList(), explicitArgs, null, false );
}
public IRExpression callMethod( IRMethod method, IRExpression root, List<IRExpression> explicitArgs, int[] namedArgOrder )
{
return callMethod( method.getOwningIRType(), method, root, Collections.<IRExpression>emptyList(), explicitArgs, namedArgOrder, false );
}
public IRExpression callMethod( IRMethod method, IRExpression root, List<IRExpression> explicitArgs, int[] namedArgOrder, boolean special )
{
return callMethod( method.getOwningIRType(), method, root, Collections.<IRExpression>emptyList(), explicitArgs, namedArgOrder, special );
}
public IRExpression callSpecialMethod( IRType rootType, IRMethod method, IRExpression root, List<IRExpression> explicitArgs )
{
return callMethod( rootType, method, root, Collections.<IRExpression>emptyList(), explicitArgs, null, true );
}
public IRExpression callSpecialMethod( IRType rootType, IRMethod method, IRExpression root, List<IRExpression> explicitArgs, int[] namedArgOrder )
{
return callSpecialMethod( rootType, method, root, Collections.<IRExpression>emptyList(), explicitArgs, namedArgOrder );
}
public IRExpression callSpecialMethod( IRType rootType, IRMethod method, IRExpression root, List<IRExpression> implicitArgs, List<IRExpression> explicitArgs, int[] namedArgOrder )
{
return callMethod( rootType, method, root, implicitArgs, explicitArgs, namedArgOrder, true );
}
private IRExpression callMethod( IRType rootType, IRMethod method, IRExpression root,
List<IRExpression> implicitArgs,
List<IRExpression> explicitArgs, int[] namedArgOrder,
boolean special )
{
IType owner = method.getOwningIType();
List<IRExpression> actualArgs = new ArrayList<IRExpression>();
List<IRElement> namedArgElements = handleNamedArgs( explicitArgs, namedArgOrder );
if( owner instanceof IGosuEnhancement && !method.isStatic() )
{
// For enhancements, we want argument/root evaluation to happen the same as for a normal method
// So we evaluate all the arguments to temp variables, then evaluate the root and null check-it,
// and lastly invoke the method
List<IRExpression> tempArgs = new ArrayList<IRExpression>();
pushEnhancementTypeParams( owner, tempArgs );
pushTypeParams( method, tempArgs );
if( shouldAddExternalSymbolsMapToCall( method ) )
{
actualArgs.add( pushExternalSymbolsMap() );
}
tempArgs.addAll( implicitArgs );
tempArgs.addAll( explicitArgs );
List<IRElement> compositeElements = new ArrayList<IRElement>();
// Store the root to a temp variable and null-check it
IRSymbol tempRoot = _cc().makeAndIndexTempSymbol( root.getType() );
compositeElements.add( buildAssignment( tempRoot, root ) );
// Add the temp named arg assignments (if any)
compositeElements.addAll( namedArgElements );
// Now store all the arguments to temp variables
for( IRExpression tempArg : tempArgs )
{
IRSymbol tempArgSymbol = _cc().makeAndIndexTempSymbol( tempArg.getType() );
compositeElements.add( buildAssignment( tempArgSymbol, tempArg ) );
actualArgs.add( identifier( tempArgSymbol ) );
}
// Null-check the temp root
compositeElements.add( nullCheckVar( tempRoot ) );
// Push the root expression on as the first argument to the method
actualArgs.add( 0, identifier( tempRoot ) );
// Now call the method as if it were a static method
compositeElements.add( callMethod( rootType, method, null, special, owner, actualArgs ) );
return new IRCompositeExpression( compositeElements );
}
else
{
// Otherwise, add in any implicit type parameter arguments, then call the method normally
pushTypeParams( method, actualArgs );
if( shouldAddExternalSymbolsMapToCall( method ) )
{
actualArgs.add( pushExternalSymbolsMap() );
}
actualArgs.addAll( implicitArgs );
actualArgs.addAll( explicitArgs );
if( !namedArgElements.isEmpty() )
{
List<IRElement> compositeElements = new ArrayList<IRElement>();
// Add the temp named arg assignments (if any)
compositeElements.addAll( namedArgElements );
compositeElements.add( callMethod( rootType, method, root, special, owner, actualArgs ) );
return new IRCompositeExpression( compositeElements );
}
else
{
return callMethod( rootType, method, root, special, owner, actualArgs );
}
}
}
/**
* Facilitates evaluation of named args in lexical order. E.g.,
* <pre>
* callMe( :param3 = expr1, :param1 = expr2, :param2 = expr3 )
* </pre>
* is parsed as:
* <pre>
* callMe( expr2, expr3, expr1 )
* </pre>
* which is compiled to:
* <pre>
* temp1 = expr1
* temp2 = expr2
* temp3 = expr3
* callMe( temp2, temp3, temp1 )
* </pre>
* @param explicitArgs The arg expressions in proper order for the target call. The expressions are replaced with the temp vars.
* @param namedArgOrder The lexical ordering of the args
* @return The temp var assignments in lexical order
*/
public List<IRElement> handleNamedArgs( List<IRExpression> explicitArgs, int[] namedArgOrder )
{
List<IRElement> namedArgElements = new ArrayList<IRElement>();
if( namedArgOrder != null && namedArgOrder.length > 0 )
{
assert namedArgOrder.length == explicitArgs.size();
//noinspection ForLoopReplaceableByForEach
for( int i = 0; i < namedArgOrder.length; i++ )
{
IRExpression argExpr = explicitArgs.get( namedArgOrder[i] );
IRSymbol tempArgSymbol = _cc().makeAndIndexTempSymbol( argExpr.getType() );
namedArgElements.add( buildAssignment( tempArgSymbol, argExpr ) );
explicitArgs.set( namedArgOrder[i], identifier( tempArgSymbol ) );
}
}
return namedArgElements;
}
private boolean shouldAddExternalSymbolsMapToCall( IRMethod method )
{
return (method.getOwningIType() instanceof IGosuProgram ||
(method.isStatic() && isProgramOrEnclosedInProgram( method.getOwningIType() ))) &&
!method.getName().equals("access$0") &&
!method.isGeneratedEnumMethod() &&
!(method.getOwningIType() instanceof IGosuTemplateInternal) &&
!(method instanceof SyntheticIRMethod);
}
private IRExpression callMethod(IRType rootType, IRMethod method, IRExpression root, boolean special, IType owner, List<IRExpression> actualArgs) {
if ( !special && _cc().shouldUseReflection( owner, method.getAccessibility() ) ) {
return callMethodReflectively( owner, method.getName(), method.getReturnType(), method.getAllParameterTypes(), root, actualArgs );
} else {
List<IRExpression> convertedArgs = new ArrayList<IRExpression>();
List<IRType> paramTypes = method.getAllParameterTypes();
for (int i = 0; i < actualArgs.size(); i++) {
convertedArgs.add( IRArgConverter.castOrConvertIfNecessary( paramTypes.get(i), actualArgs.get(i) ) );
}
IRMethodCallExpression result = buildMethodCall(method.getOwningIRType(), method.getName(), owner.isInterface(), method.getReturnType(), paramTypes, root, convertedArgs);
if ( special ) {
result.setSpecial( true );
}
return result;
}
}
private void pushEnhancementTypeParams( IType enhancementType, List<IRExpression> args )
{
if( enhancementType.isParameterizedType() )
{
for( IType typeParam : enhancementType.getTypeParameters() )
{
args.add( pushType( typeParam ) );
}
}
else if( enhancementType.isGenericType() )
{
for( IGenericTypeVariable typeVariable : enhancementType.getGenericTypeVariables() )
{
args.add( pushType( typeVariable.getTypeVariableDefinition().getType() ) );
}
}
}
private IRExpression callMethodReflectively( IType owner, String strMethod, IRType returnType, List<IRType> paramTypes,
IRExpression root, List<IRExpression> args)
{
// Our three arguments to getDeclaredMethods are the owners Class, the name of the method, and
// a Class[] containing the types of the parameters
List<IRExpression> argExprs = new ArrayList<IRExpression>();
argExprs.add( classLiteral( getDescriptor(owner ) ) );
argExprs.add( stringLiteral( strMethod ) );
List<IRExpression> paramClasses = new ArrayList<IRExpression>();
for (IRType paramType : paramTypes) {
paramClasses.add( classLiteral( paramType ) );
}
argExprs.add( buildInitializedArray( getDescriptor( Class.class ), paramClasses ) );
// So we're returning a call to "invoke" where the root is the result of call built to getDeclaredMethod,
// and the arguments are 1) the root expression of the method call (or the null literal, if that's null)
// and 2) an Object[] containing the values of all the arguments to the method
// We need to make sure all the args are boxed, since they're going into an Object[]
List<IRExpression> boxedArgs = new ArrayList<IRExpression>();
for (IRExpression arg : args) {
boxedArgs.add( boxValue( arg.getType(), arg ) );
}
argExprs.add( root == null ? pushNull() : root );
argExprs.add( buildInitializedArray(IRTypeConstants.OBJECT(), boxedArgs ) );
IRExpression invokeCall = buildMethodCall( GosuRuntimeMethods.class, "invokeMethod", Object.class, new Class[]{Class.class, String.class, Class[].class, Object.class, Object[].class},
null, argExprs );
if( returnType.isVoid() )
{
// As a side note, we don't need to worry about POPing anything here: if the method had a void return type, it should be wrapped in a statement
// anyway. The statement compiler will see that the underlying return type of its expression is Object instead of void,
// and will generate the POP instruction itself.
return invokeCall;
} else {
return unboxValueToType( returnType, invokeCall );
}
}
private void pushTypeParams( IRMethod irMethod, List<IRExpression> args )
{
IFunctionType funcType = irMethod.getFunctionType();
if( funcType == null || !irMethod.couldHaveTypeVariables() )
{
return;
}
if( funcType.isParameterizedType() )
{
IType[] typeParams = funcType.getTypeParameters();
for( IType typeParam : typeParams )
{
args.add( pushType( typeParam ) );
}
}
else if( irMethod.getTypeVariables() != null )
{
IGenericTypeVariable[] typeVars = irMethod.getTypeVariables();
for( IGenericTypeVariable typeVariable : typeVars )
{
args.add( pushType( typeVariable.getBoundingType() ) );
}
}
else if( funcType.isGenericType() )
{
for( IGenericTypeVariable typeVariable : funcType.getGenericTypeVariables() )
{
args.add( pushType( typeVariable.getTypeVariableDefinition().getType().getBoundingType() ) );
}
}
}
public IRStatement nullCheckVar( IRSymbol symbol )
{
return new IRIfStatement( new IREqualityExpression( identifier( symbol ), nullLiteral(), true ),
new IRThrowStatement( buildNewExpression( getDescriptor(NullPointerException.class), Collections.<IRType>emptyList(), exprList())),
null);
}
public IRExpression pushType( IType type )
{
return pushType( type, false );
}
public IRExpression pushType( IType type, boolean bKeepLiteralType )
{
IType genType = TypeLord.getPureGenericType( type );
if( genType == type &&
type instanceof IJavaType &&
(!type.isArray() || !TypeLord.getCoreType( type ).isParameterizedType()) &&
Modifier.isPublic( TypeLord.getCoreType( type ).getModifiers() ) )
{
Class backingClass = ((IJavaType)type).getBackingClass();
if( backingClass != null )
{
ClassLoader cl = backingClass.getClassLoader();
if( cl == null || cl == ClassLoader.getSystemClassLoader() )
{
// Push the class constant (better performing than resolving by name)
return callStaticMethod( TypeSystem.class, "get", new Class[] {Class.class},
exprList( pushConstant( backingClass ) ) );
}
}
}
if (type.isArray())
{
IRExpression componentType = pushType( type.getComponentType(), bKeepLiteralType );
return callMethod(IType.class, "getArrayType", new Class[0], componentType, exprList());
}
else if( genType instanceof TypeVariableType ||
genType instanceof TypeVariableArrayType )
{
return pushRuntimeTypeOfTypeVar( genType );
}
else if( type instanceof MetaType )
{
if( bKeepLiteralType && ((MetaType)type).isLiteral() )
{
return callStaticMethod( MetaType.class, "getLiteral", new Class[]{IType.class},
Collections.singletonList( pushType( ((MetaType)type).getType() ) ) );
}
else
{
return callStaticMethod( MetaType.class, "get", new Class[]{IType.class},
Collections.singletonList( pushType( ((MetaType)type).getType() ) ) );
}
}
else if( type instanceof IBlockType )
{
IBlockType blockType = (IBlockType)type;
List<IRExpression> args = new ArrayList<IRExpression>();
args.add( pushType( blockType.getReturnType() ) );
args.add( pushArrayOfTypes( blockType.getParameterTypes() ) );
args.add( pushArrayOfString( blockType.getParameterNames() ) );
//## todo: this won't work, IExpression is not a constant type
args.add( pushArrayOfDefValueExpr( blockType.getDefaultValueExpressions() ) );
return buildNewExpression( getDescriptor( BlockType.class ),
Arrays.asList(IRTypeConstants.ITYPE(), getDescriptor( IType[].class ), getDescriptor( String[].class ), getDescriptor( IExpression[].class ) ),
args );
}
else if( type instanceof FunctionLiteralType )
{
FunctionLiteralType funcLiteralType = (FunctionLiteralType)type;
List<IRExpression> args = new ArrayList<IRExpression>();
args.add( pushConstant(funcLiteralType.getSerializationClass() ) );
args.add( pushType( funcLiteralType.getSerializationSuperType() ) );
args.add( pushType( funcLiteralType.getSerializationReferenceType() ) );
args.add( pushConstant( funcLiteralType.getSerializationName() ) );
args.add( pushArrayOfTypes( funcLiteralType.getSerializationParameters() ) );
return callStaticMethod( FunctionLiteralType.class,
"resolve",
new Class[]{Class.class, IType.class, IType.class, CharSequence.class, IType[].class},
args
);
}
else if( type instanceof IFunctionType )
{
IFunctionType funcType = (IFunctionType)type;
List<IRExpression> args = new ArrayList<IRExpression>();
args.add( pushConstant(funcType.getName() ) );
args.add( pushType( funcType.getReturnType() ) );
args.add( pushArrayOfTypes( funcType.getParameterTypes() ) );
return buildNewExpression( getDescriptor( FunctionType.class ),
Arrays.asList(IRTypeConstants.STRING(), IRTypeConstants.ITYPE(), getDescriptor( IType[].class ) ),
args );
}
else if( type instanceof CompoundType )
{
CompoundType compoundType = (CompoundType)type;
List<IRElement> elements = new ArrayList<IRElement>();
IRSymbol tempSet = _cc().makeAndIndexTempSymbol( getDescriptor( HashSet.class ) );
// Create a HashSet
elements.add( buildAssignment( tempSet, buildNewExpression(getDescriptor( HashSet.class ), Collections.<IRType>emptyList(), Collections.<IRExpression>emptyList()) ) );
//Fill it in with the component types
for( IType iType : compoundType.getTypes() )
{
pushType( iType );
elements.add(new IRMethodCallStatement(
callMethod( Set.class, "add", new Class[]{Object.class},
identifier(tempSet),
Collections.singletonList( pushType( iType ) ) ) ) );
}
// The last expression is a reference to the temp symbol
elements.add( identifier( tempSet ) );
IRExpression setCreation = new IRCompositeExpression(elements);
return callStaticMethod( CompoundType.class, "get", new Class[]{Set.class},
Collections.singletonList(setCreation));
}
else
{
IModule module = getModule(genType);
IRExpression result = callStaticMethod( TypeSystem.class, "getByFullName", new Class[]{String.class, String.class},
Arrays.asList(pushConstant( genType.getName() ), module == null ? pushNull() : pushConstant( module.getName() ) ) );
if( type.isParameterizedType() && !isRecursivelyParameterized( type ) )
{
result = callMethod( IType.class, "getParameterizedType", new Class[]{IType[].class},
result,
Collections.singletonList(pushArrayOfTypes( type.getTypeParameters() ) ) );
}
return result;
}
}
private boolean isRecursivelyParameterized( IType type )
{
for( IType typeParam : type.getTypeParameters() )
{
if( typeParam == GosuParser.PENDING_BOUNDING_TYPE )
{
// type is errant because at least one of it's type variables recurse,
// therefore we can't load the default parameterized type for it.
return true;
}
}
return false;
}
protected IRExpression pushArrayOfString( String[] array )
{
if( array != null )
{
List<IRExpression> elements = new ArrayList<IRExpression>( array.length );
for( String o : array )
{
elements.add( pushConstant( o ) );
}
return buildInitializedArray( getDescriptor( array.getClass().getComponentType() ), elements );
}
return pushNull();
}
protected IRExpression pushArrayOfDefValueExpr( IExpression[] array )
{
if( array != null )
{
List<IRExpression> elements = new ArrayList<IRExpression>( array.length );
for( IExpression o : array )
{
elements.add( pushConstant( o ) );
}
return buildInitializedArray( getDescriptor( array.getClass().getComponentType() ), elements );
}
return pushNull();
}
public IRExpression pushArrayOfTypes( IType[] types )
{
List<IRExpression> values = new ArrayList<IRExpression>();
for (IType type : types) {
values.add( pushType( type ) );
}
return buildInitializedArray(IRTypeConstants.ITYPE(), values );
}
public static boolean requiresImplicitEnhancementArg( ReducedDynamicFunctionSymbol dfs )
{
return isEnhancementType( dfs.getGosuClass() ) && !dfs.isStatic();
}
public static boolean requiresImplicitEnhancementArg(IGosuMethodInfo mi)
{
return isEnhancementType( mi.getOwnersType() ) && !mi.isStatic();
}
public IJavaClassInfo[] getClassInfos( IType[] parameters )
{
IJavaClassInfo[] paramTypes = new IJavaClassInfo[parameters.length];
for( int i = 0; i < parameters.length; i++ )
{
IType paramType = parameters[i];
if ((paramType instanceof IJavaType)) {
paramTypes[i] = ((IJavaType)paramType).getBackingClassInfo();
} else {
IRType descriptor = getDescriptor( paramType );
int iDims = 0;
while( descriptor.isArray() ) {
descriptor = descriptor.getComponentType();
iDims++;
}
paramTypes[i] = TypeSystem.getJavaClassInfo( descriptor.getName(), getModule( paramType ));
while( iDims-- > 0 ) {
paramTypes[i] = paramTypes[i].getArrayType();
}
}
}
return paramTypes;
}
private IModule getModule(IType type) {
return TypeSystem.getGlobalModule();
}
public IType getConcreteType( IType type )
{
if( type != null )
{
if( type instanceof TypeVariableType )
{
return getConcreteType( ((TypeVariableType)type).getBoundingType() );
}
else if( type instanceof TypeVariableArrayType )
{
return getConcreteType( type.getComponentType() ).getArrayType();
}
}
return type;
}
public static IRType getDescriptor( IType type) {
return IRTypeResolver.getDescriptor( type );
}
public static IRType getDescriptor( IType type, boolean getConcreteTypeForMetaType)
{
return IRTypeResolver.getDescriptor( type, getConcreteTypeForMetaType );
}
public static IRType getDescriptor( Class cls )
{
return IRTypeResolver.getDescriptor( cls );
}
public static IRType getDescriptor( IJavaClassInfo cls )
{
return IRTypeResolver.getDescriptor( cls );
}
public IRExpression getDefaultConstIns( IType type )
{
if( type == JavaTypes.pBYTE()) {
return numericLiteral(Byte.valueOf((byte) 0));
}
else if (type == JavaTypes.pCHAR()) {
return numericLiteral(0);
}
else if (type == JavaTypes.pSHORT()) {
return numericLiteral(Short.valueOf((short) 0));
}
else if (type == JavaTypes.pINT()) {
return numericLiteral(0);
}
else if (type == JavaTypes.pBOOLEAN()) {
return booleanLiteral(false);
}
else if( type == JavaTypes.pLONG() )
{
return numericLiteral(Long.valueOf(0));
}
else if( type == JavaTypes.pFLOAT() )
{
return numericLiteral(Float.valueOf(0));
}
else if( type == JavaTypes.pDOUBLE() )
{
return numericLiteral(Double.valueOf(0));
}
else
{
return nullLiteral();
}
}
public int getModifiers( Symbol dfs )
{
int iAccModifiers;
int iDfsModifiers = dfs.getModifiers();
if( BytecodeOptions.isSingleServingLoader() )
{
if ( Modifier.isPrivate( iDfsModifiers ) && isReadObjectOrWriteObjectMethod( dfs ))
{
// Serialization demands that readObject and writeObject be private methods,
// so if we're compiling one of them we've got to actually make the thing private
iAccModifiers = Opcodes.ACC_PRIVATE;
}
else
{
// Otherwise, we generate everything as "public" so we don't have to worry about all the crazy
// classloader issues
iAccModifiers = Opcodes.ACC_PUBLIC;
}
}
else if( Modifier.isPublic( iDfsModifiers ) || ( Modifier.isProtected( iDfsModifiers ) && isCompilingEnhancement() ) )
{
// We compile protected enhancement methods as public so they'll be visible to subclasses without needing
// reflective hacks. Protected methods on enhancements should be discouraged: see PL-10398
iAccModifiers = Opcodes.ACC_PUBLIC;
}
else if( Modifier.isProtected( iDfsModifiers ) )
{
iAccModifiers = Opcodes.ACC_PROTECTED;
}
else if( Modifier.isInternal( iDfsModifiers ) )
{
iAccModifiers = 0;
}
else if( Modifier.isPrivate( iDfsModifiers ) )
{
if ( isReadObjectOrWriteObjectMethod( dfs ))
{
// Serialization demands that readObject and writeObject be private methods,
// so if we're compiling one of them we've got to actually make the thing private
iAccModifiers = Opcodes.ACC_PRIVATE;
}
else
{
// Otherwise, we generate private methods as "internal" access so that we don't have to
// generate bridge methods for the sake of inner class accesses to private members. Because
// bridge methods are an abomination and a moral outrage. We're angry about it. You should be too.
iAccModifiers = 0; // internal access
}
}
else
{
iAccModifiers = Opcodes.ACC_PUBLIC;
}
if( Modifier.isFinal( iDfsModifiers ) )
{
iAccModifiers |= Opcodes.ACC_FINAL;
}
if( Modifier.isStatic( iDfsModifiers ) )
{
iAccModifiers |= Opcodes.ACC_STATIC;
}
if( Modifier.isAbstract( iDfsModifiers ) )
{
iAccModifiers |= Opcodes.ACC_ABSTRACT;
}
if ( Modifier.isEnum( iDfsModifiers ) )
{
iAccModifiers |= Opcodes.ACC_ENUM;
}
if ( Modifier.isTransient( iDfsModifiers ) )
{
iAccModifiers |= Opcodes.ACC_TRANSIENT;
}
if( isCompilingEnhancement() ) // enhancement methods are always static
{
iAccModifiers |= Opcodes.ACC_STATIC;
}
return iAccModifiers;
}
// Determines if this is the special "readObject" or "writeObject" method used by serialization
// Returns true if that is the case, false otherwise
private boolean isReadObjectOrWriteObjectMethod( Symbol symbol ) {
if ( symbol instanceof DynamicFunctionSymbol ) {
DynamicFunctionSymbol dfs = (DynamicFunctionSymbol) symbol;
if (dfs.getDisplayName().equals("readObject")) {
IType[] argTypes = dfs.getArgTypes();
return argTypes != null &&
argTypes.length == 1 &&
argTypes[0].equals(JavaTypes.getJreType(java.io.ObjectInputStream.class)) &&
dfs.getReturnType().equals(JavaTypes.pVOID());
} else if (dfs.getDisplayName().equals("writeObject")) {
IType[] argTypes = dfs.getArgTypes();
return argTypes != null &&
argTypes.length == 1 &&
argTypes[0].equals(JavaTypes.getJreType(java.io.ObjectOutputStream.class)) &&
dfs.getReturnType().equals(JavaTypes.pVOID());
} else {
return false;
}
} else {
return false;
}
}
public boolean isCompilingEnhancement()
{
return _cc.compilingEnhancement();
}
public boolean isProgramOrEnclosedInProgram( IType type )
{
if( type == null )
{
return false;
}
if( type instanceof IGosuProgram )
{
return true;
}
return isProgramOrEnclosedInProgram( type.getEnclosingType() );
}
public IRExpression boxValue( IType lhsType, IRExpression root )
{
// If it's not a primitive, or the type is void (likely because it's a null expression), just return the expression back out
if( !lhsType.isPrimitive() || lhsType == JavaTypes.pVOID() )
{
return root;
}
if( lhsType == JavaTypes.pBOOLEAN() )
{
return callStaticMethod( Boolean.class, "valueOf", new Class[]{boolean.class}, Collections.singletonList(root) );
}
if( lhsType == JavaTypes.pBYTE() )
{
return callStaticMethod( Byte.class, "valueOf", new Class[]{byte.class}, Collections.singletonList(root) );
}
if( lhsType == JavaTypes.pCHAR() )
{
return callStaticMethod( AbstractElementTransformer.class, "valueOf", new Class[]{char.class}, Collections.singletonList(root) );
}
if( lhsType == JavaTypes.pSHORT() )
{
return callStaticMethod( Short.class, "valueOf", new Class[]{short.class}, Collections.singletonList(root) );
}
if( lhsType == JavaTypes.pINT() )
{
return callStaticMethod( Integer.class, "valueOf", new Class[]{int.class}, Collections.singletonList(root) );
}
else if( lhsType == JavaTypes.pLONG() )
{
return callStaticMethod( Long.class, "valueOf", new Class[]{long.class}, Collections.singletonList(root) );
}
else if( lhsType == JavaTypes.pFLOAT() )
{
return callStaticMethod( Float.class, "valueOf", new Class[]{float.class}, Collections.singletonList(root) );
}
else if( lhsType == JavaTypes.pDOUBLE() )
{
return callStaticMethod( Double.class, "valueOf", new Class[]{double.class}, Collections.singletonList(root) );
} else {
throw new IllegalArgumentException("Unexpected type " + lhsType.getName());
}
}
// Necessary so that unicode chars that are in the negative integer range
// don't cause an IndexOutOfBoundsException e.g., Character.valueOf( char )
// chokes on negative chars
public static Character valueOf( char c )
{
if( c >= 0 && c <= 127 )
{
return c;
}
//noinspection UnnecessaryBoxing
return new Character( c );
}
public IRExpression boxValue( IRType lhsType, IRExpression root )
{
// If it's not a primitive, or the type is void (likely because it's a null expression), just return the expression back out
if( !lhsType.isPrimitive() || lhsType.isVoid() )
{
return root;
}
if( lhsType.isBoolean() )
{
return callStaticMethod( Boolean.class, "valueOf", new Class[]{boolean.class}, Collections.singletonList(root) );
}
if( lhsType.isByte() )
{
return callStaticMethod( Byte.class, "valueOf", new Class[]{byte.class}, Collections.singletonList(root) );
}
if( lhsType.isChar() )
{
return callStaticMethod( AbstractElementTransformer.class, "valueOf", new Class[]{char.class}, Collections.singletonList(root) );
}
if( lhsType.isShort() )
{
return callStaticMethod( Short.class, "valueOf", new Class[]{short.class}, Collections.singletonList(root) );
}
if( lhsType.isInt() )
{
return callStaticMethod( Integer.class, "valueOf", new Class[]{int.class}, Collections.singletonList(root) );
}
if( lhsType.isLong() )
{
return callStaticMethod( Long.class, "valueOf", new Class[]{long.class}, Collections.singletonList(root) );
}
if( lhsType.isFloat() )
{
return callStaticMethod( Float.class, "valueOf", new Class[]{float.class}, Collections.singletonList(root) );
}
if( lhsType.isDouble() )
{
return callStaticMethod( Double.class, "valueOf", new Class[]{double.class}, Collections.singletonList(root) );
}
throw new IllegalArgumentException("Unexpected type " + lhsType.getName());
}
public IRExpression boxValueToType( IType toType, IRExpression root )
{
if( toType.isPrimitive() )
{
return boxValue( toType, root );
}
String VALUE_OF = "valueOf";
if( toType == JavaTypes.BOOLEAN() )
{
return callStaticMethod( Boolean.class, VALUE_OF, new Class[]{boolean.class}, Collections.singletonList(root) );
}
if( toType == JavaTypes.BYTE() )
{
return callStaticMethod( Byte.class, VALUE_OF, new Class[]{byte.class}, Collections.singletonList(root) );
}
if( toType == JavaTypes.CHARACTER() )
{
return callStaticMethod( Character.class, VALUE_OF, new Class[]{char.class}, Collections.singletonList(root) );
}
if( toType == JavaTypes.SHORT() )
{
return callStaticMethod( Short.class, VALUE_OF, new Class[]{short.class}, Collections.singletonList(root) );
}
if( toType == JavaTypes.INTEGER() )
{
return callStaticMethod( Integer.class, VALUE_OF, new Class[]{int.class}, Collections.singletonList(root) );
}
else if( toType == JavaTypes.LONG() )
{
return callStaticMethod( Long.class, VALUE_OF, new Class[]{long.class}, Collections.singletonList(root) );
}
else if( toType == JavaTypes.FLOAT() )
{
return callStaticMethod( Float.class, VALUE_OF, new Class[]{float.class}, Collections.singletonList(root) );
}
else if( toType == JavaTypes.DOUBLE() )
{
return callStaticMethod( Double.class, VALUE_OF, new Class[]{double.class}, Collections.singletonList(root) );
}
else
{
// It's not a boxed type, so just return the expression back out
return root;
}
}
public IRExpression unboxValueToType( IType lhsType, IRExpression expression )
{
if( !lhsType.isPrimitive() )
{
return checkCast( lhsType, expression );
}
if( lhsType == JavaTypes.pBOOLEAN() )
{
return callMethod(Boolean.class, "booleanValue", new Class[]{},
checkCast(Boolean.class, expression), Collections.<IRExpression>emptyList());
}
if( lhsType == JavaTypes.pBYTE() )
{
return callMethod(Byte.class, "byteValue", new Class[]{},
checkCast(Byte.class, expression), Collections.<IRExpression>emptyList());
}
if( lhsType == JavaTypes.pCHAR() )
{
return callMethod(Character.class, "charValue", new Class[]{},
checkCast(Character.class, expression), Collections.<IRExpression>emptyList());
}
if( lhsType == JavaTypes.pSHORT() )
{
return callMethod(Short.class, "shortValue", new Class[]{},
checkCast(Short.class, expression), Collections.<IRExpression>emptyList());
}
if( lhsType == JavaTypes.pINT() )
{
return callMethod(Integer.class, "intValue", new Class[]{},
checkCast(Integer.class, expression), Collections.<IRExpression>emptyList());
}
else if( lhsType == JavaTypes.pLONG() )
{
return callMethod(Long.class, "longValue", new Class[]{},
checkCast(Long.class, expression), Collections.<IRExpression>emptyList());
}
else if( lhsType == JavaTypes.pFLOAT() )
{
return callMethod(Float.class, "floatValue", new Class[]{},
checkCast(Float.class, expression), Collections.<IRExpression>emptyList());
}
else if( lhsType == JavaTypes.pDOUBLE() )
{
return callMethod(Double.class, "doubleValue", new Class[]{},
checkCast(Double.class, expression), Collections.<IRExpression>emptyList());
}
else if (lhsType == JavaTypes.pVOID() )
{
return expression;
}
else {
throw new IllegalArgumentException("Unexpected type " + lhsType.getName());
}
}
public IRExpression unboxValueToType( IRType lhsType, IRExpression expression )
{
if( !lhsType.isPrimitive() )
{
return buildCast( lhsType, expression );
}
if( lhsType.isBoolean() )
{
return callMethod(Boolean.class, "booleanValue", new Class[]{},
checkCast(Boolean.class, expression), Collections.<IRExpression>emptyList());
}
if( lhsType.isByte() )
{
return callMethod(Byte.class, "byteValue", new Class[]{},
checkCast(Byte.class, expression), Collections.<IRExpression>emptyList());
}
if( lhsType.isChar() )
{
return callMethod(Character.class, "charValue", new Class[]{},
checkCast(Character.class, expression), Collections.<IRExpression>emptyList());
}
if( lhsType.isShort() )
{
return callMethod(Short.class, "shortValue", new Class[]{},
checkCast(Short.class, expression), Collections.<IRExpression>emptyList());
}
if( lhsType.isInt() )
{
return callMethod(Integer.class, "intValue", new Class[]{},
checkCast(Integer.class, expression), Collections.<IRExpression>emptyList());
}
else if( lhsType.isLong() )
{
return callMethod(Long.class, "longValue", new Class[]{},
checkCast(Long.class, expression), Collections.<IRExpression>emptyList());
}
else if( lhsType.isFloat() )
{
return callMethod(Float.class, "floatValue", new Class[]{},
checkCast(Float.class, expression), Collections.<IRExpression>emptyList());
}
else if( lhsType.isDouble() )
{
return callMethod(Double.class, "doubleValue", new Class[]{},
checkCast(Double.class, expression), Collections.<IRExpression>emptyList());
}
else if (lhsType.isVoid() )
{
return expression;
}
else {
throw new IllegalArgumentException("Unexpected type " + lhsType.getName());
}
}
public IRExpression unboxValueFromType( IType boxedType, IRExpression root )
{
if( boxedType.isPrimitive() )
{
return root;
}
if( boxedType == JavaTypes.BOOLEAN() )
{
return callMethod( Boolean.class, "booleanValue", new Class[0],
checkCast( Boolean.class, root ), Collections.<IRExpression>emptyList() );
}
if( boxedType == JavaTypes.BYTE() )
{
return callMethod( Byte.class, "byteValue", new Class[0],
checkCast( Byte.class, root ), Collections.<IRExpression>emptyList() );
}
if( boxedType == JavaTypes.CHARACTER() )
{
return callMethod( Character.class, "charValue", new Class[0],
checkCast( Character.class, root ), Collections.<IRExpression>emptyList() );
}
if( boxedType == JavaTypes.SHORT() )
{
return callMethod( Short.class, "shortValue", new Class[0],
checkCast( Short.class, root ), Collections.<IRExpression>emptyList() );
}
if( boxedType == JavaTypes.INTEGER() )
{
return callMethod( Integer.class, "intValue", new Class[0],
checkCast( Integer.class, root ), Collections.<IRExpression>emptyList() );
}
else if( boxedType == JavaTypes.LONG() )
{
return callMethod( Long.class, "longValue", new Class[0],
checkCast( Long.class, root ), Collections.<IRExpression>emptyList() );
}
else if( boxedType == JavaTypes.FLOAT() )
{
return callMethod( Float.class, "floatValue", new Class[0],
checkCast( Float.class, root ), Collections.<IRExpression>emptyList() );
}
else if( boxedType == JavaTypes.DOUBLE() )
{
return callMethod( Double.class, "doubleValue", new Class[0],
checkCast( Double.class, root ), Collections.<IRExpression>emptyList() );
} else {
throw new IllegalArgumentException("Unexpected type " + boxedType.getName());
}
}
public boolean isProgram( IType type )
{
return type instanceof IGosuProgram;
}
public static boolean isEnhancementType( IType type )
{
return type instanceof IGosuEnhancement;
}
public static boolean isBytecodeType( IType type )
{
return TypeSystem.isBytecodeType( type );
}
public static boolean isBytecodeType( IConstructorInfo ci )
{
if( ci instanceof IJavaConstructorInfo )
{
return true;
}
else if( ci instanceof IGosuConstructorInfo )
{
return true;
}
return false;
}
public static IType[] getTypes( IParameterInfo[] parameters )
{
IType[] paramTypes = new IType[parameters.length];
for( int i = 0; i < parameters.length; i++ )
{
paramTypes[i] = parameters[i].getFeatureType();
}
return paramTypes;
}
protected IRExpression pushString( IExpression expr )
{
if( expr.getType() != JavaTypes.STRING() )
{
return callMethod( ICoercionManager.class, "makeStringFrom", new Class[]{Object.class},
callStaticMethod( CommonServices.class, "getCoercionManager", new Class[0], Collections.<IRExpression>emptyList() ),
Collections.singletonList(ExpressionTransformer.compile( expr, _cc() )));
}
else
{
return ExpressionTransformer.compile( expr, _cc() );
}
}
protected IRExpression pushParamTypes( IParameterInfo[] parameters )
{
if( parameters.length == 0 )
{
return pushNull();
}
else
{
return pushArrayOfTypes( getTypes( parameters ) );
}
}
protected IRExpression pushNull()
{
return nullLiteral();
}
protected IRExpression pushConstant( Object constant )
{
if( constant instanceof Boolean )
{
return booleanLiteral( (Boolean)constant );
}
else if( constant instanceof Character )
{
return charLiteral( (Character)constant );
}
else if( constant instanceof Number )
{
return numericLiteral( (Number)constant );
}
else if( constant instanceof String )
{
return stringLiteral( (String)constant );
}
else if( constant instanceof Class )
{
return classLiteral( (Class)constant );
}
else if( constant instanceof IJavaType )
{
return classLiteral( ((IJavaType)constant).getBackingClassInfo() );
}
else if( constant instanceof IJavaClassInfo )
{
return classLiteral( (IJavaClassInfo)constant );
}
else if( constant instanceof IRType )
{
return classLiteral( (IRType)constant );
}
else if( constant == null )
{
return pushNull();
}
else
{
throw new IllegalArgumentException( constant.getClass() + " is not a supported constant type" );
}
}
protected IRExpression convertBoxedNullToPrimitive( IType boxedType )
{
if( boxedType == JavaTypes.BOOLEAN() )
{
return convertNullToPrimitive( JavaTypes.pBOOLEAN() );
}
else if( boxedType == JavaTypes.BYTE() )
{
return convertNullToPrimitive( JavaTypes.pBYTE() );
}
else if( boxedType == JavaTypes.CHARACTER() )
{
return convertNullToPrimitive( JavaTypes.pCHAR() );
}
else if( boxedType == JavaTypes.SHORT() )
{
return convertNullToPrimitive( JavaTypes.pSHORT() );
}
else if( boxedType == JavaTypes.INTEGER() )
{
return convertNullToPrimitive( JavaTypes.pINT() );
}
else if( boxedType == JavaTypes.LONG() )
{
return convertNullToPrimitive( JavaTypes.pLONG() );
}
else if( boxedType == JavaTypes.FLOAT() )
{
return convertNullToPrimitive( JavaTypes.pFLOAT() );
}
else if( boxedType == JavaTypes.DOUBLE() )
{
return convertNullToPrimitive( JavaTypes.pDOUBLE() );
}
else
{
throw new IllegalArgumentException("Unexpected type " + boxedType.getName());
}
}
protected IRExpression convertNullToPrimitive( IType primitive )
{
// Convert null value to primitive constant
if( primitive == JavaTypes.pBOOLEAN() )
{
return booleanLiteral(false);
}
else if ( primitive == JavaTypes.pBYTE() )
{
return numericLiteral( Byte.valueOf( (byte) 0 ) );
}
else if ( primitive == JavaTypes.pSHORT() )
{
return numericLiteral( Short.valueOf( (short) 0 ) );
}
else if (
primitive == JavaTypes.pCHAR() ||
primitive == JavaTypes.pINT() )
{
return numericLiteral( 0 );
}
else if( primitive == JavaTypes.pLONG() )
{
return numericLiteral( Long.valueOf(0) );
}
else if( primitive == JavaTypes.pFLOAT() )
{
return numericLiteral( Float.valueOf(0) );
}
else if( primitive == JavaTypes.pDOUBLE() )
{
return numericLiteral( Double.valueOf(0) );
}
else
{
throw new IllegalStateException( "Unexpected type: " + primitive );
}
}
protected IRExpression checkCast( Class cls, IRExpression expression )
{
return checkCast( TypeSystem.get( cls ), expression );
}
protected IRExpression checkCast( IType type, IRExpression expression )
{
if( type instanceof CompoundType )
{
CompoundType ct = (CompoundType)type;
Set<IType> types = ct.getTypes();
List<IType> ltypes = new ArrayList<IType>( types );
Collections.sort( ltypes,
new Comparator<IType>()
{
public int compare( IType t1, IType t2 )
{
return t1.isInterface() ? 0 : 1;
}
} );
IRExpression last = expression;
// Nest the casts, with the concrete type last
for( IType t : ltypes )
{
last = checkCast(t, last);
}
return last;
}
else
{
return buildCast(getDescriptor(type, true), expression);
}
}
protected IRExpression numberConvert( IType from, IType to, IRExpression root )
{
if (from.equals(to)) {
return root;
} else {
return new IRPrimitiveTypeConversion( root, getDescriptor( from ), getDescriptor( to ) );
}
}
protected IRExpression numberConvert( IRType from, IRType to, IRExpression root )
{
if (from.equals(to)) {
return root;
} else {
return new IRPrimitiveTypeConversion( root, from, to );
}
}
public static boolean isIntType( IType from )
{
return from == JavaTypes.pBYTE() ||
from == JavaTypes.pCHAR() ||
from == JavaTypes.pSHORT() ||
from == JavaTypes.pINT();
}
protected boolean isPrimitiveNumberType( IType type )
{
return type.isPrimitive() && BeanAccess.isNumericType( type );
}
public static boolean isNonBigBoxedNumberType( IType type )
{
return
type == JavaTypes.BYTE() ||
type == JavaTypes.CHARACTER() ||
type == JavaTypes.SHORT() ||
type == JavaTypes.INTEGER() ||
type == JavaTypes.LONG() ||
type == JavaTypes.FLOAT() ||
type == JavaTypes.DOUBLE();
}
public static boolean isBoxedIntType( IType type )
{
return
type == JavaTypes.BYTE() ||
type == JavaTypes.CHARACTER() ||
type == JavaTypes.SHORT() ||
type == JavaTypes.INTEGER();
}
// The symbol is considered to be on an enclosing type if the symbol is defined on a class that encloses this
// class OR is defined on a supertype or interface of an enclosing class
protected ICompilableTypeInternal isMemberOnEnclosingType(IReducedSymbol symbol)
{
if( !_cc().isNonStaticInnerClass() )
{
return null;
}
// If the symbol is on this class, or any ancestors, it's not enclosed
//noinspection SuspiciousMethodCalls
IType symbolClass = maybeUnwrapProxy( symbol.getGosuClass() );
if( getGosuClass().getAllTypesInHierarchy().contains( symbolClass ) )
{
return null;
}
ICompilableTypeInternal enclosingClass = _cc().getEnclosingType();
if( !(TypeLord.getOuterMostEnclosingClass( _cc().getEnclosingType() ) instanceof IGosuEnhancement) &&
symbolClass instanceof IGosuEnhancement )
{
symbolClass = ((IGosuEnhancement)symbolClass).getEnhancedType();
}
while( enclosingClass != null )
{
//noinspection SuspiciousMethodCalls
if( enclosingClass.getAllTypesInHierarchy().contains( symbolClass ) )
{
return enclosingClass;
}
enclosingClass = enclosingClass.getEnclosingType();
}
return null;
}
// The symbol is considered to be on an enclosing type if the symbol is defined on a class that encloses this
// class OR is defined on a supertype or interface of an enclosing class
protected boolean isMemberOnEnhancementOfEnclosingType( AbstractDynamicSymbol symbol )
{
if (!_cc().isNonStaticInnerClass()) {
return false;
}
IType enhancement = symbol.getGosuClass();
if (! ( enhancement instanceof IGosuEnhancement ) ) {
return false;
}
IType enhancedType = ((IGosuEnhancement) enhancement).getEnhancedType();
// If the symbol is on this class, or any ancestors, it's not enclosed
//noinspection SuspiciousMethodCalls
if (getGosuClass().getAllTypesInHierarchy().contains( enhancedType )) {
return false;
}
ICompilableTypeInternal enclosingClass = _cc().getEnclosingType();
while( enclosingClass != null )
{
//noinspection SuspiciousMethodCalls
if( enclosingClass.getAllTypesInHierarchy().contains( enhancedType ) )
{
return true;
}
enclosingClass = enclosingClass.getEnclosingType();
}
return false;
}
private static IType maybeUnwrapProxy( IType type )
{
if( type != null && type.isParameterizedType() )
{
type = type.getGenericType();
}
return type == null ? null : IGosuClass.ProxyUtil.getProxiedType( type );
}
protected IRExpression pushThis()
{
String symbolName = (isCompilingEnhancement() ? GosuClassTransformer.ENHANCEMENT_THIS_REF : Keyword.KW_this.getName() );
return identifier( _cc.getSymbol( symbolName ) );
}
protected IRExpression pushThisOrOuter( IType currentType )
{
if( currentType.isAssignableFrom( getGosuClass() ) )
{
return pushThis();
}
else if( currentType instanceof IGosuEnhancement &&
currentType.isAssignableFrom( ((IGosuEnhancement)currentType).getEnhancedType() ) )
{
return pushThis();
}
else
{
return pushOuter( currentType );
}
}
protected IRExpression pushOuter()
{
if( _cc().getCurrentFunction() != null && _cc().getCurrentFunction().isConstructor() )
{
return identifier( _cc().getSymbol( _cc().getOuterThisParamName() ) );
}
else
{
return getInstanceField( getGosuClass(), _cc().getOuterThisFieldName(), getDescriptor( getRuntimeEnclosingType( getGosuClass() ) ), AccessibilityUtil.forOuter(), pushThis() );
}
}
protected IRExpression pushOuter( IType outerTarget )
{
return pushOuter( maybeUnwrapProxy( outerTarget ), maybeUnwrapProxy( _cc().getEnclosingType() ), pushOuter() );
}
protected IRExpression pushOuter( IType outerTarget, IType currentOuter, IRExpression root )
{
IRExpression result = root;
while( !outerTarget.isAssignableFrom( currentOuter ) )
{
IRMethod irMethod = IRMethodFactory.createIRMethod( currentOuter, OUTER_ACCESS, getRuntimeEnclosingType( currentOuter ), new IType[]{currentOuter}, AccessibilityUtil.forOuter(), true);
result = callMethod( irMethod, null, Collections.singletonList(result));
currentOuter = maybeUnwrapProxy( currentOuter.getEnclosingType() );
}
return result;
}
protected IRExpression getInstanceField( IType owner, String strField, IRType fieldType, IRelativeTypeInfo.Accessibility accessibility, IRExpression root )
{
return getField( owner,
strField,
fieldType,
accessibility,
root);
}
protected IRExpression getStaticField( IType owner, String strField, IRType fieldType, IRelativeTypeInfo.Accessibility accessibility )
{
return getField( owner,
strField,
fieldType,
accessibility,
null);
}
protected IRExpression getField( IRProperty field, IRExpression root )
{
return getField( field.getOwningIType(),
field.getName(),
field.getType(),
field.getAccessibility(),
root);
}
protected IRStatement setField( IRProperty field, IRExpression root, IRExpression value )
{
return setField( field.getOwningIType(),
field.getName(),
field.getType(),
field.getAccessibility(),
root,
value);
}
protected IRStatement setInstanceField( IType owner, String strField, IRType fieldType, IRelativeTypeInfo.Accessibility accessibility,
IRExpression root, IRExpression value)
{
return setField( owner,
strField,
fieldType,
accessibility,
root,
value);
}
protected IRStatement setStaticField( IType owner, String strField, IRType fieldType, IRelativeTypeInfo.Accessibility accessibility,
IRExpression value)
{
return setField( owner,
strField,
fieldType,
accessibility,
null,
value);
}
private IRExpression getField( IType owner, String strField, IRType fieldType, IRelativeTypeInfo.Accessibility accessibility, IRExpression root )
{
if( _cc().shouldUseReflection( owner, accessibility ) )
{
return getFieldReflectively( owner, strField, fieldType, root );
}
return buildFieldGet(getDescriptor( owner ), strField, fieldType, root);
}
private IRExpression getFieldReflectively( IType owner, String strField, IRType fieldType, IRExpression root )
{
// Call getDeclaredField using the owner's Class and the field name as arguments
IRExpression getDeclaredFieldCall = buildMethodCall(AbstractElementTransformer.class, "getDeclaredField", Field.class, new Class[]{Class.class, String.class},
null,
exprList( classLiteral( getDescriptor( owner ) ),
stringLiteral( strField ) ) );
// Call "get" on the result of that call, passing in the root expression or the "null" constant if there is none
IRExpression getCall = buildMethodCall( Field.class, "get", Object.class, new Class[]{Object.class},
getDeclaredFieldCall,
exprList( (root == null ? pushNull() : root) ) );
// Unbox it on the way out if needed
return unboxValueToType( fieldType, getCall );
}
protected IRStatement setField( IType owner, String strField, IRType fieldType, IRelativeTypeInfo.Accessibility accessibility,
IRExpression root, IRExpression value )
{
if( _cc().shouldUseReflection( owner, accessibility ) )
{
return setFieldReflectively( owner, strField, root, value );
}
return buildFieldSet(getDescriptor( owner ),
strField,
fieldType,
root,
value);
}
private IRStatement setFieldReflectively( IType owner, String strField,
IRExpression root, IRExpression value)
{
// Call getDeclaredField using the owner's Class and the field name as arguments
IRExpression getDeclaredFieldCall = buildMethodCall(AbstractElementTransformer.class, "getDeclaredField", Field.class, new Class[]{Class.class, String.class},
null,
exprList( classLiteral( getDescriptor( owner ) ),
stringLiteral( strField ) ) );
// Call "set" on the result of that call, passing in the root expression or the "null" constant if there is none and the value
IRExpression setCall = buildMethodCall( Field.class, "set", void.class, new Class[]{Object.class, Object.class},
getDeclaredFieldCall,
exprList( (root == null ? pushNull() : root),
boxValue( value.getType(), value ) ) );
return new IRMethodCallStatement( setCall );
}
protected static boolean isEvalProgram( IGosuClassInternal gsClass )
{
return gsClass instanceof IGosuProgram &&
gsClass.isAnonymous();
}
public static Field getDeclaredField( Class cls, String strName ) {
Field f = getDeclaredFieldImpl(cls, strName);
if( f == null )
{
throw new IllegalStateException( "Failed to find field: " + strName + " starting from class " + cls);
}
return f;
}
public static Field getDeclaredFieldImpl( Class cls, String strName )
{
if( cls == null )
{
return null;
}
for( Field f : cls.getDeclaredFields() )
{
if( f.getName().equals( strName ) )
{
f.setAccessible( true );
return f;
}
}
// If we didn't find the method, recurse up through the superclass, then any implemented interfaces,
// and lastly check any enclosing classes
Field f = getDeclaredFieldImpl( cls.getSuperclass(), strName );
if (f != null) {
return f;
}
for (Class iface : cls.getInterfaces()) {
f = getDeclaredFieldImpl( iface, strName );
if (f != null) {
return f;
}
}
return null;
}
public static Method getDeclaredMethod( Class cls, String strName, Class... params ) {
Method m = getDeclaredMethodImpl(cls, strName, params);
if( m == null )
{
throw new IllegalStateException( "Failed to find method: " + strName + "(" + Arrays.asList(params) + ") starting from class " + cls);
}
return m;
}
public static IJavaClassMethod getDeclaredMethod(IJavaClassInfo cls, String strName, Class... params) {
IJavaClassMethod m = getDeclaredMethodImpl(cls, strName, params);
if( m == null )
{
throw new IllegalStateException( "Failed to find method: " + strName + "(" + Arrays.asList(params) + ") starting from class " + cls);
}
return m;
}
private static Method getDeclaredMethodImpl( Class cls, String strName, Class... params )
{
if( cls == null )
{
return null;
}
for( Method m : NewIntrospector.getDeclaredMethods( cls ) )
{
if( m.getName().equals( strName ) )
{
Class<?>[] mParams = m.getParameterTypes();
if( mParams.length == params.length )
{
boolean bFound = true;
for( int i = 0; i < mParams.length; i++ )
{
if( !mParams[i].getName().equals( params[i].getName() ) )
{
bFound = false;
break;
}
}
if( bFound )
{
m.setAccessible( true );
return m;
}
}
}
}
// If we didn't find the method, recurse up through the superclass, then any implemented interfaces,
// and lastly check any enclosing classes
Method m = getDeclaredMethodImpl( cls.getSuperclass(), strName, params );
if (m != null) {
return m;
}
for (Class iface : cls.getInterfaces()) {
m = getDeclaredMethodImpl( iface, strName, params );
if (m != null) {
return m;
}
}
return null;
}
private static IJavaClassMethod getDeclaredMethodImpl(IJavaClassInfo cls, String strName, Class... params)
{
if( cls == null )
{
return null;
}
for( IJavaClassMethod m : cls.getDeclaredMethods() )
{
if( m.getName().equals( strName ) )
{
IJavaClassInfo[] mParams = m.getParameterTypes();
if( mParams.length == params.length )
{
boolean bFound = true;
for( int i = 0; i < mParams.length; i++ )
{
if( !mParams[i].getName().equals( params[i].getName() ) )
{
bFound = false;
break;
}
}
if( bFound )
{
return m;
}
}
}
}
// If we didn't find the method, recurse up through the superclass, then any implemented interfaces,
// and lastly check any enclosing classes
IJavaClassMethod m = getDeclaredMethodImpl(cls.getSuperclass(), strName, params);
if (m != null) {
return m;
}
for (IJavaClassInfo iface : cls.getInterfaces()) {
m = getDeclaredMethodImpl( iface, strName, params );
if (m != null) {
return m;
}
}
return null;
}
protected IRExpression newArray( IRType atomicType, IRExpression sizeExpression )
{
return new IRNewArrayExpression( atomicType, sizeExpression );
}
//
// private String getTypeForNewArray( IType atomicType )
// {
// return atomicType.isArray()
// ? getDescriptor( atomicType )
// : getSlashName( atomicType );
// }
//
protected IRExpression makeArrayViaTypeInfo( IType atomicType, List<Expression> valueExpressions )
{
_cc().pushScope( false );
try
{
// atomicType
// .makeArrayInstance( size )
pushType( atomicType );
pushConstant( valueExpressions.size() );
IRExpression arrayCreation = callMethod( IType.class, "makeArrayInstance", new Class[]{int.class},
pushType( atomicType ),
exprList( pushConstant( valueExpressions.size() ) ) );
if (valueExpressions.isEmpty()) {
// If there are no values, just cast the array after creating it
return checkCast( atomicType.getArrayType(), arrayCreation );
} else {
// If it needs to be initialized, we trap the creation in a temp variable, then assign to it,
// then load it and cast it at the end
IRSymbol temp = _cc().makeAndIndexTempSymbol(IRTypeConstants.OBJECT());
List<IRElement> statements = new ArrayList<IRElement>();
statements.add( buildAssignment( temp, arrayCreation ) );
// For each value, we need to call setArrayComponent on the type, using this value
for( int i = 0; i < valueExpressions.size(); i++ )
{
Expression expression = valueExpressions.get( i );
IRExpression call = callMethod( IType.class, "setArrayComponent", new Class[]{Object.class, int.class, Object.class},
pushType( atomicType ),
exprList( identifier( temp ),
numericLiteral( i ),
boxValue( expression.getType(), ExpressionTransformer.compile( expression, _cc() ) ) ) );
statements.add(new IRMethodCallStatement( call ) );
}
statements.add( checkCast( atomicType.getArrayType(), identifier ( temp ) ) );
return new IRCompositeExpression( statements );
}
}
finally
{
_cc().popScope();
}
}
protected IRExpression makeEmptyArrayViaTypeInfo( IType atomicType, List<Expression> sizeExpressions )
{
_cc().pushScope( false );
try
{
// (atomicType[]) AbstractElementTransformer.initMultiArray(atomicType, atomicType.makeArrayInstance( sizeExpressions.get(0) ),
// sizeExpressions.size(),
// sizeExpressions)
IRExpression arrayCreation = callMethod( IType.class, "makeArrayInstance", new Class[]{int.class},
pushType( atomicType ),
exprList( ExpressionTransformer.compile( sizeExpressions.get( 0 ), _cc() ) ) );
List<IRExpression> sizes = new ArrayList<IRExpression>();
sizes.add(numericLiteral(0)); // First one doesn't matter
for (int i = 1; i < sizeExpressions.size(); i++) {
sizes.add( ExpressionTransformer.compile( sizeExpressions.get(i), _cc() ) );
}
IRExpression sizeArrays = buildInitializedArray(IRTypeConstants.pINT(), sizes);
IRExpression initCall = callStaticMethod( getClass(), "initMultiArray", new Class[]{IType.class, Object.class, int.class, int[].class},
exprList( pushType( atomicType ),
arrayCreation,
numericLiteral( 1 ),
sizeArrays) );
return checkCast( atomicType.getArrayType(), initCall );
}
finally
{
_cc().popScope();
}
}
public static Object initMultiArray( IType componentType, Object instance, int iDimension, int[] sizes )
{
if( sizes.length <= iDimension )
{
return instance;
}
int iLength = componentType.getArrayLength( instance );
componentType = componentType.getComponentType();
for( int i = 0; i < iLength; i++ )
{
Object component = componentType.makeArrayInstance( sizes[iDimension] );
initMultiArray( componentType, component, iDimension + 1, sizes );
componentType.setArrayComponent( instance, i, component );
}
return instance;
}
public IRExpression pushRuntimeTypeOfTypeVar( IType type )
{
IType rtType = type;
if( rtType instanceof TypeVariableType )
{
return getRuntimeTypeParameter( (TypeVariableType)rtType );
}
else if( rtType instanceof TypeVariableArrayType )
{
// TypeSystem.getByFullName( runtimeType.getName().concat(brackets) )
StringBuilder brackets = new StringBuilder();
while( rtType.isArray() )
{
brackets.append( '[' );
brackets.append( ']' );
rtType = rtType.getComponentType();
}
IRExpression typeNameExpression = callMethod( String.class, "concat", new Class[]{String.class},
callMethod( IType.class, "getName", new Class[0],
getRuntimeTypeParameter( (TypeVariableType)rtType ),
Collections.<IRExpression>emptyList()),
Collections.singletonList( pushConstant( brackets.toString() )) );
return callStaticMethod( TypeSystem.class, "getByFullName", new Class[]{String.class},
Collections.singletonList(typeNameExpression) );
}
else
{
throw new IllegalArgumentException( "Only type variable types allowed in this method" );
}
}
private IRExpression getRuntimeTypeParameter( TypeVariableType type )
{
IRSymbol iTypeParamSymbol = _cc().getTypeParamIndex( type );
if( iTypeParamSymbol != null )
{
// The type param is passed as an arg to the enclosing function/constructor, pass directly from local var
return identifier( iTypeParamSymbol );
}
if( type.isFunctionStatement() )
{
// The type-var is defined on a function e.g., function foo<F>()
// In this case the type parameter is passed into the function directly, before the proper args.
IFunctionType funcType = (IFunctionType)type.getEnclosingType();
if( equivalentTypes( funcType.getScriptPart().getContainingType(), getGosuClass() ) )
{
IGenericTypeVariable[] genTypeVars = funcType.getGenericTypeVariables();
for( int i = 0; i < genTypeVars.length; i++ )
{
IGenericTypeVariable gv = genTypeVars[i];
if( gv.getName().equals( type.getName() ) )
{
return identifier( _cc().getSymbol( getTypeVarParamName( gv ) ) );
}
}
}
// May fail to find if the type var does not belong to this immediate function (see Case #2 below)
}
// 1) The type var is defined on 'this' e.g., class Foo<T>.
// In this case the type parameter is stored on the class e.g., typeparam$T
// ...or...
// 2) The type var is on a function *but* the function encloses
// an aonymous class which references the type var.
// In this case the type parameter is stored on the anonymous class just
// like a class type var e.g., typeparam$E
// Case #.5 - enhancement type parameters
IType enclosingType = type.getEnclosingType();
// The type-var is defined on an enhancement
// In this case the type parameter is passed into the function directly, before the function type args.
if( enclosingType instanceof IGosuEnhancement && equivalentTypes( enclosingType, _cc().getGosuClass() ) )
{
IGenericTypeVariable[] typeVariables = enclosingType.getGenericTypeVariables();
for( int i = 0; i < typeVariables.length; i++ )
{
ITypeVariableType variableType = typeVariables[i].getTypeVariableDefinition().getType();
if( type.equals( variableType ) )
{
return identifier( _cc().getSymbol( getTypeVarParamName( typeVariables[i] ) ) );
}
}
}
//
// Case #1
//
String strTypeVarField = TYPE_PARAM_PREFIX + type.getRelativeName();
for( IGenericTypeVariable gv : getGosuClass().getGenericTypeVariables() )
{
if( gv.getName().equals( type.getName() ) )
{
return getInstanceField( getGosuClass(), strTypeVarField, IRTypeConstants.ITYPE(), AccessibilityUtil.forTypeParameter(), pushThis() );
}
}
//
// Case #2
//
IRExpression fromEnclosingFunction = maybeGetFromEnclosingFunction( getGosuClass(), type, strTypeVarField );
if( fromEnclosingFunction != null )
{
return fromEnclosingFunction;
}
//
// Now repeat #1 and #2 for each outer/enclosing class
//
for( ICompilableType gsClass = getGosuClass().getEnclosingType();
gsClass != null;
gsClass = gsClass.getEnclosingType() )
{
//
// Case #1
//
for( IGenericTypeVariable gv : gsClass.getGenericTypeVariables() )
{
if( gv.getName().equals( type.getName() ) )
{
return getInstanceField( gsClass, strTypeVarField, IRTypeConstants.ITYPE(), AccessibilityUtil.forTypeParameter(), pushOuter( gsClass ) );
}
}
//
// Case #2
//
fromEnclosingFunction = maybeGetFromEnclosingFunction( gsClass, type, strTypeVarField );
if( fromEnclosingFunction != null )
{
return fromEnclosingFunction;
}
}
return pushType( type.getBoundingType() );
}
private boolean equivalentTypes( IType type1, IType type2 )
{
return TypeLord.getPureGenericType( type1 ) == TypeLord.getPureGenericType( type2 );
}
/**
* An anonymous class enclosed in a generic function has as a synthetic field the type parameter[s] from the function.
*/
private IRExpression maybeGetFromEnclosingFunction( ICompilableType gsClass, TypeVariableType type, String strTypeVarField )
{
if( gsClass.isAnonymous() )
{
IDynamicFunctionSymbol dfs = getEnclosingDFS( gsClass );
if( dfs != null && dfs != getEnclosingDFS( gsClass.getEnclosingType() ) )
{
for( IGenericTypeVariable gv : getTypeVarsForDFS( dfs ) )
{
if( gv.getName().equals( type.getName() ) )
{
return getInstanceField( gsClass, strTypeVarField, IRTypeConstants.ITYPE(), AccessibilityUtil.forTypeParameter(), pushThisOrOuter( gsClass ) );
}
}
}
}
return null;
}
public static IDynamicFunctionSymbol getEnclosingDFS( ICompilableType gsClass )
{
IParsedElement pe;
if( gsClass instanceof IGosuProgram )
{
pe = ((IGosuProgram)gsClass).getEnclosingEvalExpression();
}
else if( gsClass instanceof IBlockClass )
{
pe = ((IBlockClass)gsClass).getBlock();
}
else
{
pe = gsClass.getClassStatement();
}
// It can be null e.g., if the new expr is a field initializer (not defined in a function)
if( pe == null || pe.getLocation() == null || pe.getLocation().getEnclosingFunctionStatement() == null )
{
return null;
}
else
{
return pe.getLocation().getEnclosingFunctionStatement().getDynamicFunctionSymbol();
}
}
public static List<IGenericTypeVariable> getTypeVarsForDFS( IDynamicFunctionSymbol dfs )
{
ArrayList<IGenericTypeVariable> typeVars = new ArrayList<IGenericTypeVariable>();
if( !dfs.isStatic() && isEnhancementType( dfs.getGosuClass() ) )
{
typeVars.addAll( Arrays.asList( getTypeVarsForEnhancement( dfs ) ) );
}
if( dfs.getType().isGenericType() )
{
typeVars.addAll( Arrays.asList( dfs.getType().getGenericTypeVariables() ) );
}
return typeVars;
}
private static IGenericTypeVariable[] getTypeVarsForEnhancement( IDynamicFunctionSymbol dfs )
{
IGosuClass aClass = dfs.getGosuClass();
if( aClass.isParameterizedType() )
{
aClass = (IGosuClass)aClass.getGenericType();
}
return aClass.getGenericTypeVariables();
}
/**
* Parameters are order like so:
* ctor( [OuterThis,] [This,] [CapturedSymbols,] [TypeParams,] params )
*/
protected IRType[] getConstructorParamTypes( IType[] declaredParams, int iTypeParams, IType type )
{
List<IRType> params = new ArrayList<IRType>();
//
// Insert outer 'this'
//
if( isNonStaticInnerClass( type ) )
{
params.add( getDescriptor( getRuntimeEnclosingType( type ) ) );
}
//
// Insert captured symbols
//
if( type instanceof IGosuClassInternal && type.isValid() ) //&& ((IGosuClassInternal)type).isAnonymous() )
{
Map<String, ICapturedSymbol> capturedSymbols = ((IGosuClassInternal)type).getCapturedSymbols();
if( capturedSymbols != null )
{
for( ICapturedSymbol sym : capturedSymbols.values() )
{
params.add( getDescriptor( sym.getType().getArrayType() ) );
}
}
}
// The external symbols are always treated as captured
if (requiresExternalSymbolCapture( type ) ) {
params.add( getDescriptor( IExternalSymbolMap.class ) );
}
//
// Insert type-parameter types
//
if( iTypeParams > 0 )
{
for( int i = 0; i < iTypeParams; i++ )
{
params.add(IRTypeConstants.ITYPE());
}
}
//
// Enums have name and ordinal arguments implicitly added to their constructors
//
if (type.isEnum()) {
params.add(IRTypeConstants.STRING());
params.add(IRTypeConstants.pINT());
}
//
// Add declared parameters
//
for (IType declaredParam : declaredParams ) {
params.add( getDescriptor( declaredParam ) );
}
return params.toArray(new IRType[params.size()]);
}
protected int pushTypeParametersForConstructor( IExpression expr, IType type, List<IRExpression> args )
{
if( !(type instanceof IGosuClassInternal) )
{
return 0;
}
int iCount = 0;
if( type.isParameterizedType() )
{
for( IType typeParam : type.getTypeParameters() )
{
args.add( pushType( typeParam ) );
iCount++;
}
} else if (type.isGenericType()) {
// We should only be here if it's a this() call, so we grab the type parameters out of the current scope,
// i.e. just pass through whatever was passed to this constructor
for ( IGenericTypeVariable typeVariable : type.getGenericTypeVariables() ) {
args.add( identifier( new IRSymbol( getTypeVarParamName( typeVariable ), getDescriptor( IType.class ), false) ) );
iCount++;
}
}
iCount = pushEnclosingFunctionTypeParams( expr, type, iCount, args );
return iCount;
}
protected void pushEnumSuperConstructorArguments( List<IRExpression> args ) {
if(_cc().compilingEnum()) {
// The name and ordinal arguments are always the next ones on the stack after the "this" pointer,
// since generics can't be non-static inner classes or generified
args.add( identifier( _cc().getSymbol( ENUM_PARAM_PREFIX + "name" ) ) );
args.add( identifier( _cc().getSymbol( ENUM_PARAM_PREFIX + "ordinal" ) ) );
}
}
private int pushEnclosingFunctionTypeParams( IParsedElement pe, IType type, int iCount, List<IRExpression> args )
{
ICompilableTypeInternal gsClass = (ICompilableTypeInternal)type;
while( gsClass.isAnonymous() )
{
// If the class is anonymous and it's constructed within a generic method,
// it may need the type params of the of method.
if( pe != null && pe.getLocation() != null )
{
IFunctionStatement funcStmt = pe.getLocation().getEnclosingFunctionStatement();
if( funcStmt != null ) // can be null e.g., anonymous classes can be constructed as field initializers
{
IDynamicFunctionSymbol dfs = funcStmt.getDynamicFunctionSymbol();
List<IGenericTypeVariable> genTypeVars = getTypeVarsForDFS( dfs );
// unlike "normal" anonymous classes block classes can be nested under the same
// function statement, so we need to roll out to the outermost one under this dfs
while( getEnclosingDFS( gsClass.getEnclosingType() ) == dfs )
{
gsClass = gsClass.getEnclosingType();
}
for( int i = 0; i < genTypeVars.size(); i++ )
{
if( gsClass == type )
{
args.add( identifier( _cc().getSymbol( getTypeVarParamName( genTypeVars.get(i) ) ) ) );
}
else
{
args.add( getInstanceField( gsClass, TYPE_PARAM_PREFIX + genTypeVars.get( i ).getName(),
IRTypeConstants.ITYPE(), AccessibilityUtil.forTypeParameter(),
pushThisOrOuter( gsClass ) ) );
}
iCount++;
}
}
}
gsClass = gsClass.getEnclosingType();
if( gsClass.isAnonymous() )
{
pe = gsClass instanceof IGosuProgram
? ((IGosuProgram)gsClass).getEnclosingEvalExpression()
: gsClass.getClassStatement();
}
}
return iCount;
}
protected IRAssignmentStatement initLocalVarWithDefault( Symbol varSym )
{
return initLocalVar( varSym, getDefaultConstIns( varSym.getType() ) );
}
protected IRAssignmentStatement initLocalVar( Symbol varSym, IRExpression initialValue )
{
IRSymbol symbol = makeIRSymbol( varSym );
IRExpression actualValue;
if( varSym.isValueBoxed() )
{
// We need to get the component because the type of the boxed symbol will already be the array type,
// i.e., int[] instead of just int
actualValue = buildInitializedArray( symbol.getType().getComponentType(), exprList( initialValue ) );
}
else
{
actualValue = initialValue;
}
return buildAssignment( symbol, actualValue );
}
protected IRSymbol makeIRSymbol( Symbol varSym )
{
IType type = varSym.getType();
if( varSym.isValueBoxed() )
{
type = type.getArrayType();
}
// This is pretty brutal. It's here because ForEachStatementTransformer takes in a
// Symbol to its helper methods, and sometimes we really want that to be a temp symbol
IRSymbol symbol = new IRSymbol( varSym.getName(), getDescriptor( type ), varSym.getName().startsWith("*temp") );
_cc().putSymbol( symbol );
return symbol;
}
protected void pushCapturedSymbols( IType type, List<IRExpression> args, boolean ignoreExternalSymbols )
{
if( type instanceof IGosuClassInternal && type.isValid() ) //&& ((IGosuClassInternal)type).isAnonymous() )
{
Map<String, ICapturedSymbol> capturedSymbols = ((IGosuClassInternal)type).getCapturedSymbols();
if( capturedSymbols != null )
{
for( ICapturedSymbol sym : capturedSymbols.values() )
{
if( isCapturedOnEnclosingAnonymousClass( sym, (IGosuClassInternal)type ) )
{
args.add( getInstanceField( getGosuClass(), CAPTURED_VAR_PREFIX + sym.getName(), getDescriptor( sym.getType().getArrayType() ), AccessibilityUtil.forSymbol((IReducedSymbol) sym),
pushThis()));
}
else
{
args.add( identifier( _cc().getSymbol(sym.getName() ) ) );
}
}
}
}
if ( !ignoreExternalSymbols && requiresExternalSymbolCapture( type ) /* && !inStaticContext() */ ) {
args.add( pushExternalSymbolsMap() );
}
}
private boolean isCapturedOnEnclosingAnonymousClass( ICapturedSymbol sym, IGosuClassInternal gsClass )
{
ICompilableTypeInternal enclosingType = gsClass.getEnclosingType();
if( enclosingType.isAnonymous() )
{
return enclosingType.getCapturedSymbols().containsKey( sym.getName() );
}
return false;
}
public IRExpression collectArgsIntoObjArray( List<IRExpression> args )
{
List<IRExpression> values = new ArrayList<IRExpression>();
if( args != null )
{
for( IRExpression arg : args )
{
if( arg.getType().isPrimitive() )
{
arg = boxValue( arg.getType(), arg );
}
values.add( arg );
}
}
return buildInitializedArray(IRTypeConstants.OBJECT(), values);
}
protected IRExpression getScopedSymbolValue( IReducedSymbol symbol )
{
return unboxValueToType(symbol.getType(),
callMethod(IAttributeSource.class, "getAttribute", new Class[]{String.class},
pushAttributeSource( symbol),
Collections.singletonList(pushConstant(((ScopedDynamicSymbol) symbol).getAttributeName()))));
}
protected IRExpression getScopedSymbolValue( IGosuVarPropertyInfo pi )
{
return unboxValueToType(pi.getScopedSymbolType(),
callMethod(IAttributeSource.class, "getAttribute", new Class[]{String.class},
pushAttributeSource(pi),
Collections.singletonList(pushConstant(pi.getSymbolAttributeName()))));
}
protected IRStatement setScopedSymbolValue( ISymbol symbol, IExpression rhsValue )
{
IRExpression methodCall = callMethod( IAttributeSource.class, "setAttribute", new Class[]{String.class, Object.class},
pushAttributeSource( (ScopedDynamicSymbol)symbol ),
exprList(
pushConstant( ((ScopedDynamicSymbol)symbol).getAttributeName() ),
boxValue( rhsValue.getType(), ExpressionTransformer.compile( rhsValue, _cc() ) ) ) );
return new IRMethodCallStatement( methodCall );
}
protected IRStatement setScopedSymbolValue( IGosuVarPropertyInfo pi, IExpression rhsValue )
{
IRExpression methodCall = callMethod( IAttributeSource.class, "setAttribute", new Class[]{String.class, Object.class},
pushAttributeSource( pi ),
exprList(
pushConstant( pi.getSymbolAttributeName() ),
boxValue( rhsValue.getType(), ExpressionTransformer.compile( rhsValue, _cc() ) ) ) );
return new IRMethodCallStatement( methodCall );
}
private IRExpression pushAttributeSource(IGosuVarPropertyInfo pi) {
String globalScope = pi.getSymbolScopeString();
return callMethod( IEntityAccess.class, "getAttributeSource", new Class[]{GlobalScope.class},
callStaticMethod( CommonServices.class, "getEntityAccess", new Class[0],
Collections.<IRExpression>emptyList() ),
Collections.singletonList( callStaticMethod( GlobalScope.class, "getScope", new Class[]{String.class},
Collections.singletonList( pushConstant( globalScope ) ) ) ) );
}
protected IRExpression pushAttributeSource( IReducedSymbol scopedSymbol )
{
GlobalScope globalScope = scopedSymbol.getScope();
return callMethod( IEntityAccess.class, "getAttributeSource", new Class[]{GlobalScope.class},
callStaticMethod( CommonServices.class, "getEntityAccess", new Class[0],
Collections.<IRExpression>emptyList() ),
Collections.singletonList( callStaticMethod( GlobalScope.class, "getScope", new Class[]{String.class},
Collections.singletonList( pushConstant( globalScope.toString() ) ) ) ) );
}
protected boolean isScopedField( IPropertyInfo pi )
{
if( pi instanceof PropertyInfoDelegate )
{
pi = ((PropertyInfoDelegate) pi).getDelegatePI();
}
return pi instanceof IGosuVarPropertyInfo && ((IGosuVarPropertyInfo)pi).isScopedField();
}
protected IGosuVarPropertyInfo getActualPropertyInfo(IPropertyInfo pi)
{
if( pi instanceof PropertyInfoDelegate )
{
pi = ((PropertyInfoDelegate) pi).getDelegatePI();
}
return (IGosuVarPropertyInfo)pi;
}
protected IRExpression castResultingTypeIfNecessary( IRType expectedType, IRType actualReturnType, IRExpression root ) {
if (!expectedType.isVoid() && !expectedType.isPrimitive() && !expectedType.isAssignableFrom(actualReturnType)) {
return buildCast( expectedType, root );
} else {
return root;
}
}
/**
* @param type
* @return the actual runtime enclosing type of this type (handles the case of enhancements, when
* the "enclosing type" at runtime will be the enhanced object, rather than the acutal enclosing type)
*/
public static IType getRuntimeEnclosingType( IType type )
{
IType enclosingType = maybeUnwrapProxy( type.getEnclosingType() );
if( enclosingType instanceof IGosuEnhancement )
{
IGosuEnhancement enhancement = (IGosuEnhancement)enclosingType;
enclosingType = enhancement.getEnhancedType();
}
return enclosingType;
}
public IType maybeUnwrapMetaType( IType rootType )
{
if( rootType instanceof IMetaType )
{
rootType = ((IMetaType)rootType).getType();
}
return rootType;
}
@Deprecated // Use the IJavaClassInfo version -- we should not be loading classes during compilation
protected IRExpression classLiteral( Class value )
{
return classLiteral( getDescriptor( value ) );
}
protected IRExpression classLiteral( IJavaClassInfo value ) {
return classLiteral( JavaClassIRType.get( value ) );
}
protected IRExpression classLiteral( IRType value ) {
IType type;
if (value instanceof JavaClassIRType) {
type = ((JavaClassIRType) value).getType();
} else if (value instanceof GosuClassIRType) {
type = ((GosuClassIRType) value).getType();
} else {
throw new RuntimeException("Unsupported IRType " + value.getClass());
}
return pushClassLiteral( value, type );
}
private IRExpression pushClassLiteral( IRType value, IType type ) {
//## todo: this should be fixed now that Gosu's loader is the app loader i.e., we can probably just use the IRClassLiteral
// Unfortunately, it's possible for a class literal to be illegal in a given context: if that literal refers to an
// internal or private type, for example, and classloader issues result in the caller being in a different package/classloader
// at runtime, the type needs to be looked up reflectively instead. For purposes of this method call, the feature itself is public
if( type != null && RequiresReflectionDeterminer.shouldUseReflection( type, _cc.getGosuClass(), IRelativeTypeInfo.Accessibility.PUBLIC ) ) {
return callMethod( GosuRuntimeMethods.class, "lookUpClass", new Class[]{String.class}, null, exprList( stringLiteral( value.getDescriptor() ) ) );
} else {
return new IRClassLiteral( value );
}
}
public static ICustomExpressionRuntime getCustomRuntime( String id ) {
return CUSTOM_RUNTIMES.get( id );
}
protected IRExpression handleCustomExpressionRuntime( ICustomExpressionRuntime customRuntime, IType expectedType ) {
String customRuntimeId;
synchronized( CUSTOM_RUNTIMES )
{
customRuntimeId = "customRuntime:" + CUSTOM_RUNTIMES.size();
CUSTOM_RUNTIMES.put( customRuntimeId, customRuntime );
}
IRExpression getCustomExpression = callMethod( AbstractElementTransformer.class, "getCustomRuntime", new Class[]{String.class}, null, exprList( pushConstant( customRuntimeId ) ) );
IRExpression result = callMethod( ICustomExpressionRuntime.class, "evaluate", new Class[0], getCustomExpression, exprList() );
return unboxValueToType( expectedType, result );
}
protected IRExpression pushExternalSymbolsMap()
{
// If this class also requires external symbol capture...
if( _cc().hasSymbol( GosuFragmentTransformer.SYMBOLS_PARAM_ARG_NAME ) )
{
// Passed into a constructor, forwarding to super.
// Normally this is handled in MethodCallExpressionTransformer#callSuperOrThisConstructorSymbol, however we have to handle it
// here e.g., for the case where we're building the default ctor body.
return identifier( _cc().getSymbol( GosuFragmentTransformer.SYMBOLS_PARAM_ARG_NAME ) );
}
else if( requiresExternalSymbolCapture( _cc().getGosuClass() ) && !inStaticContext() )
{
// Calling from an inner class enclosed in a program or fragment; pass along the already captured external map
return buildFieldGet( _cc().getIRTypeForCurrentClass(), GosuFragmentTransformer.SYMBOLS_PARAM_NAME, getDescriptor( IExternalSymbolMap.class ), pushThis() );
}
else
{
// Otherwise, the symbol should be in scope in the current function
return identifier( _cc().getSymbol( GosuFragmentTransformer.SYMBOLS_PARAM_NAME ) );
}
}
public static boolean requiresExternalSymbolCapture( IType type ) {
IType enclosingType = type.getEnclosingType();
if ( enclosingType == null ) {
return false;
} else if ( enclosingType instanceof GosuFragment || enclosingType instanceof IGosuProgram ) {
return true;
} else {
return requiresExternalSymbolCapture( enclosingType );
}
}
// ---------------------------------------- Newly-Added helpers
protected IRMethodCallExpression buildMethodCall(IRType ownersType, String name, boolean isInterface, IRType returnType, List<IRType> paramTypes, IRExpression root, List<IRExpression> args) {
return new IRMethodCallExpression(name, ownersType, isInterface, returnType, paramTypes, root, args);
}
protected IRMethodCallExpression buildMethodCall(Class ownersType, String name, Class returnType, Class[] paramTypes, IRExpression root, List<IRExpression> args) {
return new IRMethodCallExpression(name, getDescriptor(ownersType), ownersType.isInterface(), getDescriptor(returnType), getIRTypes(paramTypes), root, args);
}
protected IRExpression buildCast(IRType castType, IRExpression expression) {
if( castType.isPrimitive() && expression.getType().equals( JavaClassIRType.get( Object.class ) ) ) {
return unboxValueToType( castType, expression );
}
return new IRCastExpression(expression, castType);
}
protected IRExpression numericLiteral(Number value) {
return new IRNumericLiteral(value);
}
protected IRExpression charLiteral(char c) {
return new IRCharacterLiteral(c);
}
protected IRExpression booleanLiteral(boolean value) {
return new IRBooleanLiteral(value);
}
protected IRExpression nullLiteral() {
return new IRNullLiteral();
}
protected IRExpression stringLiteral( String value ) {
return new IRStringLiteralExpression(value);
}
protected IRExpression buildArrayLoad(IRExpression root, int index, IRType componentType) {
return new IRArrayLoadExpression(root, numericLiteral(index), componentType);
}
protected IRExpression buildArrayLoad(IRExpression root, IRExpression index, IRType componentType) {
return new IRArrayLoadExpression(root, index, componentType);
}
protected IRExpression buildFieldGet(IRType owner, String fieldName, IRType fieldType, IRExpression root) {
return new IRFieldGetExpression(root, fieldName, fieldType, owner );
}
protected IRStatement buildFieldSet(IRType owner, String fieldName, IRType fieldType, IRExpression root, IRExpression value) {
return new IRFieldSetStatement(root, value, fieldName, fieldType, owner);
}
protected IRAssignmentStatement buildAssignment( IRSymbol symbol, IRExpression value ) {
return new IRAssignmentStatement(symbol, value );
}
protected IRIdentifier identifier( IRSymbol symbol ) {
return new IRIdentifier( symbol );
}
protected IRStatement buildArrayStore( IRExpression lhs, IRExpression index, IRExpression value, IRType componentType ) {
return new IRArrayStoreStatement( lhs, index, value, componentType );
}
protected IRExpression buildInitializedArray( IRType componentType, List<IRExpression> values ) {
List<IRElement> elements = new ArrayList<IRElement>();
IRSymbol tempArray = _cc.makeAndIndexTempSymbol( componentType.getArrayType() );
elements.add( buildAssignment( tempArray, newArray( componentType, numericLiteral( values.size() ) ) ) );
for (int i = 0; i < values.size(); i++) {
IRExpression value = values.get(i);
// Make sure the components of the array are all actually assignable to the component type.
if (!(value instanceof IRNullLiteral) && !componentType.isAssignableFrom( value.getType() ) ) {
value = buildCast( componentType, value );
}
elements.add( buildArrayStore(
identifier( tempArray ),
numericLiteral( i ),
value,
componentType) );
}
elements.add( identifier( tempArray ) );
return new IRCompositeExpression( elements );
}
protected IRExpression buildNewExpression( IRType type, List<IRType> parameterTypes, List<IRExpression> args ) {
return new IRNewExpression( type, parameterTypes, args );
}
protected IRExpression buildNewExpression( Class type, Class[] parameterTypes, List<IRExpression> args ) {
return new IRNewExpression( getDescriptor(type), getIRTypes(parameterTypes), args );
}
protected String getTypeVarParamName( IGenericTypeVariable typeVar ) {
return TYPE_PARAM_PREFIX + typeVar.getName();
}
protected String getCapturedSymbolParameterName( ICapturedSymbol sym ) {
return CAPTURED_VAR_PREFIX + sym.getName();
}
protected static List<IRExpression> exprList(IRExpression... expressions) {
return Arrays.asList(expressions);
}
protected static List<IRType> getIRTypes(Class[] classes) {
List<IRType> types = new ArrayList<IRType>(classes.length);
for (int i = 0; i < classes.length; i++) {
types.add(getDescriptor(classes[i]));
}
return types;
}
protected static List<IRType> getIRTypes(IJavaClassInfo[] iJavaClassInfos) {
List<IRType> types = new ArrayList<IRType>(iJavaClassInfos.length);
for (int i = 0; i < iJavaClassInfos.length; i++) {
types.add(getDescriptor(iJavaClassInfos[i]));
}
return types;
}
protected IRExpression buildNullCheckTernary(IRExpression root, IRExpression ifNull, IRExpression ifNotNull) {
return new IRTernaryExpression(
buildEquals( root, nullLiteral() ),
ifNull,
ifNotNull,
ifNotNull.getType()
);
}
protected IRThrowStatement buildThrow( IRExpression exception ) {
return new IRThrowStatement( exception );
}
protected IRIfStatement buildIf( IRExpression test, IRStatement ifStatement ) {
return new IRIfStatement( test, ifStatement, null);
}
protected IRIfStatement buildIfElse( IRExpression test, IRStatement ifStatement, IRStatement elseStatement) {
return new IRIfStatement( test, ifStatement, elseStatement);
}
protected IRCompositeExpression buildComposite(IRElement... elements) {
return new IRCompositeExpression( elements );
}
protected IRCompositeExpression buildComposite(List<IRElement> elements) {
return new IRCompositeExpression( elements );
}
protected IRMethodCallStatement buildMethodCall( IRExpression methodCall ) {
return new IRMethodCallStatement( methodCall );
}
protected IRNewStatement buildNewExpression( IRNewExpression newExpression ) {
return new IRNewStatement( newExpression );
}
protected IRStatement buildReturn() {
return new IRReturnStatement();
}
protected IREqualityExpression buildEquals(IRExpression lhs, IRExpression rhs) {
return new IREqualityExpression( lhs, rhs, true );
}
protected IREqualityExpression buildNotEquals( IRExpression lhs, IRExpression rhs ) {
return new IREqualityExpression( lhs, rhs, false );
}
protected IRRelationalExpression buildGreaterThan( IRExpression lhs, IRExpression rhs ) {
return new IRRelationalExpression( lhs, rhs, IRRelationalExpression.Operation.GT );
}
protected IRExpression buildTernary( IRExpression test, IRExpression trueValue, IRExpression falseValue, IRType resultType ) {
return new IRTernaryExpression( IRArgConverter.castOrConvertIfNecessary(IRTypeConstants.pBOOLEAN(), test ), trueValue, falseValue, resultType );
}
protected IRExpression buildAddition( IRExpression lhs, IRExpression rhs) {
return buildArithmetic( lhs, rhs, IRArithmeticExpression.Operation.Addition );
}
protected IRExpression buildSubtraction( IRExpression lhs, IRExpression rhs) {
return buildArithmetic( lhs, rhs, IRArithmeticExpression.Operation.Subtraction );
}
protected IRExpression buildArithmetic( IRExpression lhs, IRExpression rhs, IRArithmeticExpression.Operation operation) {
if (!lhs.getType().equals(rhs.getType())) {
throw new IllegalArgumentException("Arithmetic must be between identical types. Found " + lhs.getType().getName() + " and " + rhs.getType().getName());
}
return new IRArithmeticExpression( lhs.getType(), lhs, rhs, operation );
}
protected IRExpression buildNegation( IRExpression root ) {
return new IRNegationExpression( root );
}
protected IRExpression buildArrayLength( IRExpression root ) {
return new IRArrayLengthExpression( root );
}
protected boolean inStaticContext() {
return _cc().isCurrentFunctionStatic();
}
protected List<GosuAnnotationInfo> makeAnnotationInfos( List<IGosuAnnotation> gosuAnnotations, IFeatureInfo fiAnnotated )
{
List<GosuAnnotationInfo> annotationInfos = new ArrayList<GosuAnnotationInfo>();
for( int i = 0; i < gosuAnnotations.size(); i++ )
{
IGosuAnnotation ga = gosuAnnotations.get( i );
annotationInfos.add( new GosuAnnotationInfo( ga, fiAnnotated, (IGosuClassInternal)ga.getOwnersType(), i ) );
}
return annotationInfos;
}
// --------------------- Methods moved from GosuClassTransformer
public static boolean isNonStaticInnerClass( IType type )
{
return (type instanceof IGosuClass) && type.getEnclosingType() != null && !((IGosuClass)type).isStatic();
}
protected IRExpression getField_new( IRProperty irProp, IRExpression root, IRType expectedType )
{
IRType resultType;
IRExpression result = getFieldImpl_new( irProp, root );
if ( irProp.isCaptured() ) {
result = buildArrayLoad( result, 0, irProp.getType().getComponentType() );
resultType = irProp.getType().getComponentType();
} else {
resultType = irProp.getType();
}
// If the owner is parameterized, we only know that the field is of the reified type, so we must checkcast
if( !expectedType.isAssignableFrom( resultType ) )
{
result = buildCast( expectedType, result );
}
return result;
}
private IRExpression getFieldImpl_new( IRProperty irProp, IRExpression root )
{
if ( _cc().shouldUseReflection( irProp.getOwningIType(), irProp.getAccessibility() ) ) {
return getFieldReflectively_new( irProp, root );
} else {
return buildFieldGet(irProp.getOwningIRType(), irProp.getName(), irProp.getType(), root);
}
}
private IRExpression getFieldReflectively_new( IRProperty irProp, IRExpression root )
{
// Call getDeclaredField using the owner's Class and the field name as arguments
IRExpression getDeclaredFieldCall = buildMethodCall(AbstractElementTransformer.class, "getDeclaredField", Field.class, new Class[]{Class.class, String.class},
null,
exprList( classLiteral( irProp.getOwningIRType() ),
stringLiteral( irProp.getName() ) ) );
// Call "get" on the result of that call, passing in the root expression or the "null" constant if there is none
IRExpression getCall = buildMethodCall( Field.class, "get", Object.class, new Class[]{Object.class},
getDeclaredFieldCall,
exprList( (root == null ? pushNull() : root) ) );
// Unbox it on the way out if needed
return unboxValueToType( irProp.getType(), getCall );
}
protected void assignStructuralTypeOwner( IExpression rootExpr, IRExpression irMethodCall )
{
if( rootExpr != null && rootExpr.getType() instanceof IGosuClass && ((IGosuClass)rootExpr.getType()).isStructure() )
{
IRExpression mc = irMethodCall;
while( mc instanceof IRCompositeExpression )
{
List<IRElement> elements = ((IRCompositeExpression)mc).getElements();
mc = (IRExpression)elements.get( elements.size() -1 );
}
((IRMethodCallExpression)mc).setStructuralTypeOwner( GosuClassIRType.get( TypeLord.getPureGenericType( rootExpr.getType() ) ) );
}
}
}