/*
* Copyright 2013 Guidewire Software, Inc.
*/
package gw.internal.gosu.parser;
import gw.internal.gosu.parser.statements.FunctionStatement;
import gw.internal.gosu.parser.statements.MethodCallStatement;
import gw.lang.parser.*;
import gw.lang.reflect.*;
import gw.lang.reflect.gs.*;
import gw.util.GosuExceptionUtil;
import java.util.Collections;
import java.util.List;
/**
* Represents a parsed function as specified in the Gosu spec.
*/
public class DynamicFunctionSymbol extends AbstractDynamicSymbol implements IDynamicFunctionSymbol
{
private static final Object DEFINITION_CLEARED = new Object() {public String toString() {return "<cleared>";}};
private String _strDisplayName;
private List<ISymbol> _args;
private MethodCallStatement _initializer;
private FunctionStatement _functionStmt;
private String _superGenericName;
private DynamicFunctionSymbol _superDfs;
private boolean _bLoopImplicitReturn;
/**
* Constructs a DynamicFunctionSymbol for use with an IGosuParser's ISymbolTable.
*
* @param symTable The symbol table.
* @param strName The symbol name.
* @param type The IGosuParser specific type.
* @param args The symbols for the function's arguments (or null).
* @param value A statement for the body of the function (or null).
*/
public DynamicFunctionSymbol( ISymbolTable symTable, CharSequence strName, IFunctionType type, List<ISymbol> args, IStatement value )
{
this( symTable, strName, type, args, (Object)value );
}
/**
* Constructs a DynamicFunctionSymbol for use with an IGosuParser's ISymbolTable.
*
* @param symTable The symbol table.
* @param strName The symbol name.
* @param type The IGosuParser specific type.
* @param args The symbols for the function's arguments (or null).
* @param value An expression for the body of the function (or null).
*/
public DynamicFunctionSymbol( ISymbolTable symTable, CharSequence strName, IFunctionType type, List<ISymbol> args, IExpression value )
{
this(symTable, strName, type, args, (Object) value);
}
protected DynamicFunctionSymbol( ISymbolTable symTable, CharSequence name, IFunctionType type, List<ISymbol> args, Object value )
{
super( symTable, name, type );
_value = value;
assert value instanceof Statement || value instanceof Symbol || value == null || value instanceof Expression:
"Invalid type for DFS value: " + value.getClass();
if( args == null || args.isEmpty() )
{
_args = Collections.emptyList();
}
else
{
_args = args;
}
_strDisplayName = name.toString();
setName( getSignatureName( name ) );
}
public DynamicFunctionSymbol( DynamicFunctionSymbol dfs )
{
super( dfs._symTable, dfs.getName(), dfs.getType() );
_value = dfs._value;
_args = dfs._args;
_initializer = dfs._initializer;
setName( (String)dfs.getName() );
_scriptPartId = dfs._scriptPartId;
replaceModifierInfo(dfs.getModifierInfo());
}
public void renameAsErrantDuplicate( int iIndex )
{
setName( getSignatureName( iIndex + "_duplicate_" + _strDisplayName ) );
}
public String getDisplayName()
{
return _strDisplayName;
}
protected void setDisplayName( String strDisplayName )
{
_strDisplayName = strDisplayName;
}
/**
* @return The Symbols for the arguments to this function.
*/
public List<ISymbol> getArgs()
{
return _args;
}
public IType[] getArgTypes()
{
IType[] types = new IType[_args == null ? 0 : _args.size()];
for( int i = 0; i < types.length; i++ )
{
assert _args != null;
types[i] = _args.get( i ).getType();
}
return types;
}
public IType getReturnType()
{
return ((FunctionType)getType()).getReturnType();
}
/**
* Invokes the dynamic function.
*/
public Object invoke( Object[] args )
{
return invokeFromBytecode( args );
}
private Object invokeFromBytecode( Object[] args )
{
//## todo: maybe cache the method info?
IParsedElement stmts = (IParsedElement)getValueDirectly();
if( stmts == null )
{
ensureDeclaringClassIsCompiled();
stmts = (IParsedElement)getValueDirectly();
}
IGosuClass gsClass = findGosuClassOrProgram( stmts );
if( gsClass == null )
{
throw new IllegalStateException( "Did not find Gosu Class/Program" );
}
Class<?> javaClass = gsClass.getBackingClass();
IProgramInstance instance = null;
if( gsClass instanceof IGosuProgram )
{
try
{
instance = (IProgramInstance)javaClass.newInstance();
instance.evaluate(null);
}
catch( Exception e )
{
throw GosuExceptionUtil.forceThrow( e );
}
}
IMethodInfo mi = gsClass.getTypeInfo().getMethod( gsClass, getDisplayName(), getArgTypes() );
return mi.getCallHandler().handleCall( instance, args );
}
private void ensureDeclaringClassIsCompiled()
{
IGosuClassInternal gsClass = getGosuClass();
if( gsClass != null )
{
gsClass.isValid();
}
}
private IGosuClass findGosuClassOrProgram( IParsedElement pe )
{
if( pe == null )
{
return null;
}
IGosuClass gsClass = pe.getGosuProgram();
if( gsClass != null )
{
return gsClass;
}
gsClass = getGosuClass();
if( gsClass != null )
{
return gsClass;
}
return findGosuClassOrProgram( pe.getParent() );
}
/**
* @return the canonical, generic name of this function
*/
protected String getCannonicalName()
{
return getName();
}
public boolean isClassMember()
{
return super.isClassMember() && !isConstructor();
}
@Override
public void clearDebugInfo()
{
super.clearDebugInfo();
if (_functionStmt != null) {
_functionStmt.clearParseTreeInformation();
}
}
public boolean isConstructor()
{
return getInitializer() != null;
}
public String getSignatureDescription()
{
return getMethodSignature();
}
public ISymbol getLightWeightReference()
{
return this;
}
public String getMethodSignature()
{
return getDisplayName() + getParameterDisplay( true ) + " : " + TypeInfoUtil.getTypeName( ((FunctionType)getType()).getReturnType() );
}
public static String getSignatureName( CharSequence strName, List<ISymbol> args )
{
return strName + getUniqueNameForParameters( args );
}
protected String getSignatureName( CharSequence strName )
{
return getSignatureName( strName, getArgs() );
}
public String getParameterDisplay( boolean bRelative )
{
IType[] paramTypes = ((FunctionType)getType()).getParameterTypes();
if( paramTypes == null || paramTypes.length == 0 )
{
return "()";
}
String strParams = "(";
for( int i = 0; i < paramTypes.length; i++ )
{
strParams += (i == 0 ? "" : ", " ) + (bRelative ? paramTypes[i].getRelativeName() : paramTypes[i].getName());
}
strParams += ")";
return strParams;
}
private static String getUniqueNameForParameters( List<ISymbol> args )
{
if( args == null || args.size() == 0 )
{
return "()";
}
String strParams = "(";
for( int i = 0; i < args.size(); i++ )
{
strParams += (i == 0 ? "" : ", " ) + args.get( i ).getType().getName();
}
strParams += ")";
return strParams;
}
protected DynamicFunctionSymbol getFunctionSymbol()
{
ISymbol symbol = _symTable.getSymbol( getName() );
if( symbol != null )
{
return (DynamicFunctionSymbol)symbol;
}
return this;
}
public void setInitializer( MethodCallStatement initializer )
{
_initializer = initializer;
}
public MethodCallStatement getInitializer()
{
return _initializer;
}
public boolean isAbstract()
{
return Modifier.isAbstract( getModifiers() );
}
public void setAbstract( boolean bAbstract )
{
setModifiers( Modifier.setAbstract( getModifiers(), bAbstract ) );
}
public boolean isFinal()
{
return Modifier.isFinal( getModifiers() );
}
public void setFinal( boolean bFinal )
{
setModifiers( Modifier.setFinal(getModifiers(), bFinal) );
}
public IDynamicFunctionSymbol getBackingDfs()
{
return this;
}
public IAttributedFeatureInfo getMethodOrConstructorInfo( boolean acceptNone )
{
IAttributedFeatureInfo methodOrConstructorInfo = getMethodOrConstructorInfo();
if( (methodOrConstructorInfo == null) && !acceptNone )
{
throw new IllegalStateException( "Should have found method/ctor info for " + getName() );
}
else
{
return methodOrConstructorInfo;
}
}
public IAttributedFeatureInfo getMethodOrConstructorInfo()
{
IScriptPartId scriptPart = getScriptPart();
IType declaringType = scriptPart == null ? null : scriptPart.getContainingType();
if( declaringType == null )
{
return null;
}
ITypeInfo typeInfo = declaringType.getTypeInfo();
List<? extends IMethodInfo> methods;
if (typeInfo instanceof IRelativeTypeInfo) {
methods = ((IRelativeTypeInfo) typeInfo).getMethods( declaringType );
} else {
methods = typeInfo.getMethods();
}
IReducedDynamicFunctionSymbol thisRS = createReducedSymbol();
for( IMethodInfo mi : methods ) {
if (mi instanceof IDFSBackedFeatureInfo) {
IReducedDynamicFunctionSymbol dfs = ((IDFSBackedFeatureInfo) mi).getDfs();
if (thisRS.equals(dfs) || thisRS.getBackingDfs().equals(dfs)) {
return mi;
}
}
}
List<? extends IConstructorInfo> ctors;
if (typeInfo instanceof IRelativeTypeInfo) {
ctors = ((IRelativeTypeInfo)typeInfo).getConstructors( declaringType );
} else {
ctors = typeInfo.getConstructors();
}
for( IConstructorInfo ci : ctors ) {
if (ci instanceof IDFSBackedFeatureInfo) {
IReducedDynamicFunctionSymbol dfs = ((IDFSBackedFeatureInfo) ci).getDfs();
if (thisRS.equals(dfs) || thisRS.getBackingDfs().equals(dfs)) {
return ci;
} else if (((this instanceof SuperConstructorFunctionSymbol) ||
(this instanceof ThisConstructorFunctionSymbol)) &&
(dfs.getArgs().equals(getArgs()))) {
return ci;
}
}
}
return null;
}
public ITypeInfo getDeclaringTypeInfo()
{
IType type = getScriptPart() == null ? null : getScriptPart().getContainingType();
if( type == null )
{
return null;
}
return type.getTypeInfo();
}
public String getFullDescription()
{
return getModifierInfo() == null ? "" : getModifierInfo().getDescription();
}
public void setValue(Object value) {
assert value == null || (value instanceof Statement || value instanceof Symbol || value instanceof Expression) :
"Invalid type for DFS value: " + value.getClass();
super.setValue(value);
}
public FunctionStatement getDeclFunctionStmt()
{
return _functionStmt;
}
public void setDeclFunctionStmt( FunctionStatement declFunctionStmt )
{
_functionStmt = declFunctionStmt;
}
@Override
public int hashCode()
{
return getName().hashCode();
}
public boolean equals( Object o )
{
if( this == o )
{
return true;
}
if( o == null || !(o instanceof DynamicFunctionSymbol))
{
return false;
}
DynamicFunctionSymbol that = (DynamicFunctionSymbol)o;
String strName = getName();
return !(strName != null ? !strName.equals( that.getName() ) : that.getName() != null);
}
public DynamicFunctionSymbol getParameterizedVersion( IGosuClass gsClass )
{
return new ParameterizedDynamicFunctionSymbol( this, gsClass );
}
/**
* Used to remove definition compiled info from this class.
*/
public void clearDefn()
{
setValueDirectly( DEFINITION_CLEARED );
}
public void setArgs( List<ISymbol> args )
{
_args = args;
}
public void setSuperDfs( DynamicFunctionSymbol superDfs )
{
_superDfs = superDfs;
if( superDfs instanceof ParameterizedDynamicFunctionSymbol &&
superDfs.getBackingDfs() != null &&
!superDfs.getBackingDfs().getName().equals( getName() ) )
{
_superGenericName = superDfs.getBackingDfs().getName();
}
}
public DynamicFunctionSymbol getSuperDfs()
{
return _superDfs;
}
public String getSuperGenericName()
{
return _superGenericName;
}
public boolean hasTypeVariables()
{
IGenericTypeVariable[] tvs = getType().getGenericTypeVariables();
return tvs != null && tvs.length != 0;
}
public boolean hasOptionalParameters()
{
List<ISymbol> args = getArgs();
if( args != null )
{
for( ISymbol arg : args )
{
if( arg.getDefaultValueExpression() != null )
{
return true;
}
}
}
return false;
}
public IReducedDynamicFunctionSymbol createReducedSymbol()
{
return new ReducedDynamicFunctionSymbol( this );
}
public void setLoopImplicitReturn( boolean bLoopImplicitReturn )
{
_bLoopImplicitReturn = bLoopImplicitReturn;
}
public boolean isLoopImplicitReturn()
{
return _bLoopImplicitReturn;
}
}