/*
* Copyright 2013 Guidewire Software, Inc.
*/
package gw.internal.gosu.ir.transform.expression;
import gw.config.CommonServices;
import gw.internal.gosu.parser.expressions.ArrayAccess;
import gw.internal.gosu.ir.transform.ExpressionTransformer;
import gw.internal.gosu.ir.transform.TopLevelTransformationContext;
import gw.lang.ir.IRExpression;
import gw.lang.ir.IRSymbol;
import gw.lang.ir.expression.IRCompositeExpression;
import gw.lang.reflect.IType;
import gw.lang.reflect.IConstructorInfo;
import java.util.Arrays;
import java.util.List;
/**
*/
public class ArrayAccessTransformer extends AbstractExpressionTransformer<ArrayAccess>
{
public static IRExpression compile( TopLevelTransformationContext cc, ArrayAccess expr )
{
ArrayAccessTransformer compiler = new ArrayAccessTransformer( cc, expr );
return compiler.compile();
}
private ArrayAccessTransformer( TopLevelTransformationContext cc, ArrayAccess expr )
{
super( cc, expr );
}
protected IRExpression compile_impl()
{
// Transform the root expression
IRExpression originalRoot = ExpressionTransformer.compile( _expr().getRootExpression(), _cc() );
// Short-circuit null check (only on non-primitives)
// In that case, we'll need to store the root off and use an identifier expression as the root
IRSymbol tempRoot = null;
IRExpression root;
boolean needsAutoinsert = ArrayAccess.needsAutoinsert( getParsedElement() );
boolean bStandardGosu = CommonServices.getEntityAccess().getLanguageLevel().isStandard();
boolean bNullSafe = _expr().isNullSafe() ||
(!bStandardGosu && !_expr().getType().isPrimitive()) ||
(needsAutoinsert && !_expr().getType().isPrimitive());
if( bNullSafe || needsAutoinsert )
{
tempRoot = _cc().makeAndIndexTempSymbol( originalRoot.getType() );
root = identifier( tempRoot );
}
else
{
root = originalRoot;
}
// For auto-insert logic we need to reference the index without reevaluating it, so we need to store
// it to a temp variable, much like with null-checking
IRExpression originalIndex = ExpressionTransformer.compile( _expr().getMemberExpression(), _cc() );
IRSymbol tempIndex = null;
IRExpression index;
if( needsAutoinsert )
{
tempIndex = _cc().makeAndIndexTempSymbol( originalIndex.getType() );
index = identifier( tempIndex );
}
else
{
index = originalIndex;
}
IRExpression arrayAccess;
// Non-null
IType rootType = _expr().getRootExpression().getType();
if( rootType.isArray() && isBytecodeType( rootType ) )
{
// Normal array access
arrayAccess = buildArrayLoad( root, index, getDescriptor( rootType.getComponentType() ) );
}
else
{
// Ordered List, Custom Array, etc.
arrayAccess = callStaticMethod( ArrayAccess.class, "getArrayElement", new Class[]{Object.class, int.class, boolean.class},
exprList( root, index, pushConstant( bNullSafe ) ) );
if( _expr().getType().isPrimitive() )
{
arrayAccess = unboxValueToType( _expr().getType(), arrayAccess );
}
else
{
arrayAccess = checkCast( _expr().getType(), arrayAccess );
}
if( needsAutoinsert )
{
IType typeToAutoInsert = ArrayAccess.getTypeToAutoInsert( getParsedElement().getRootExpression() );
// add an maybeAutoInsert before the array access
arrayAccess = buildComposite( buildMethodCall( callStaticMethod( ArrayAccessTransformer.class, "maybeAutoInsert",
new Class[]{Object.class, int.class, IType.class},
Arrays.asList( identifier( tempRoot ),
identifier( tempIndex ),
pushType( typeToAutoInsert ) ) ) ),
arrayAccess );
}
}
if( tempRoot != null )
{
// Null short-circuit: create a composite expression that looks like:
// temp = root
// (temp == null ? (ExpectedType) null : temp[member])
IRCompositeExpression composite = new IRCompositeExpression();
composite.addElement( buildAssignment( tempRoot, originalRoot ) );
if( tempIndex != null )
{
composite.addElement( buildAssignment( tempIndex, originalIndex ) );
}
if( _expr().getType().isPrimitive() )
{
composite.addElement( buildNullCheckTernary( identifier( tempRoot ),
shortCircuitValue( arrayAccess.getType() ),
arrayAccess ) );
}
else
{
composite.addElement( buildNullCheckTernary( identifier( tempRoot ),
checkCast( _expr().getType(), nullLiteral() ),
checkCast( _expr().getType(), arrayAccess ) ) );
}
return composite;
}
else
{
// We're not short-circuiting, so no need to wrap the access expression
return arrayAccess;
}
}
@SuppressWarnings({"UnusedDeclaration"})
public static void maybeAutoInsert( Object obj, int index, IType typeToAutoCreate )
{
List l = (List)obj;
if( l.size() == index )
{
if( typeToAutoCreate.isAbstract() || typeToAutoCreate.isInterface() )
{
throw new IllegalStateException( "Type " + typeToAutoCreate.getName() + " is abstract or an interface and has no default implementation class to use" );
}
final IConstructorInfo constructor = typeToAutoCreate.getTypeInfo().getConstructor();
if( constructor == null )
{
throw new IllegalStateException( "Type " + typeToAutoCreate.getName() + " has no default constructor" );
}
//noinspection unchecked
l.add( constructor.getConstructor().newInstance() );
}
}
}