/*
* Copyright 2013 Guidewire Software, Inc.
*/
package gw.internal.gosu.parser;
import gw.internal.gosu.parser.expressions.BlockExpression;
import gw.internal.gosu.parser.expressions.Identifier;
import gw.internal.gosu.parser.expressions.MethodCallExpression;
import gw.internal.gosu.parser.statements.ClassFileStatement;
import gw.internal.gosu.parser.statements.ClassStatement;
import gw.internal.gosu.parser.statements.MethodCallStatement;
import gw.internal.gosu.parser.statements.NoOpStatement;
import gw.internal.gosu.parser.statements.VarStatement;
import gw.lang.parser.GosuParserTypes;
import gw.lang.parser.ICapturedSymbol;
import gw.lang.parser.IReducedSymbol;
import gw.lang.parser.ISymbol;
import gw.lang.parser.ISymbolTable;
import gw.lang.parser.Keyword;
import gw.lang.parser.ScriptPartId;
import gw.lang.parser.exceptions.ParseResultsException;
import gw.lang.parser.statements.IUsesStatement;
import gw.lang.reflect.FunctionType;
import gw.lang.reflect.IParameterInfo;
import gw.lang.reflect.IType;
import gw.lang.reflect.TypeSystem;
import gw.lang.reflect.gs.IGosuConstructorInfo;
import gw.lang.reflect.java.GosuTypes;
import gw.lang.reflect.java.JavaTypes;
import gw.util.fingerprint.FP64;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
*/
public class GosuClassParseInfo {
transient private IGosuClassInternal _gosuClass;
transient private ClassStatement _classStmt;
transient private ParseResultsException _pe;
transient private List<DynamicFunctionSymbol> _listStaticFunctions;
transient private Map<String, DynamicFunctionSymbol> _mapMemberFunctions;
transient private Map<String, DynamicFunctionSymbol> _mapConstructorFunctions;
transient private List<DynamicPropertySymbol> _listStaticProperties;
transient private Map<String, DynamicPropertySymbol> _mapMemberProperties;
transient private Map<String, VarStatement> _mapStaticFields;
transient private Map<String, VarStatement> _mapMemberFields;
transient private Map<CharSequence, ISymbol> _memberFieldIndexByName;
transient private Symbol _thisSymbol;
transient private Map<String, ICapturedSymbol> _capturedSymbols;
transient private long _sourceFingerprint;
transient private BlockExpression _block;
public GosuClassParseInfo(IGosuClassInternal gosuClass) {
_gosuClass = gosuClass;
_classStmt = new ClassStatement(gosuClass);
_listStaticFunctions = Collections.emptyList();
_mapMemberFunctions = Collections.emptyMap();
_mapConstructorFunctions = Collections.emptyMap();
_listStaticProperties = Collections.emptyList();
_mapMemberProperties = Collections.emptyMap();
_mapStaticFields = Collections.emptyMap();
_mapMemberFields = Collections.emptyMap();
_memberFieldIndexByName = Collections.emptyMap();
_capturedSymbols = Collections.emptyMap();
}
public ClassStatement getClassStatement() {
return _classStmt;
}
public ClassFileStatement getClassFileStatement() {
return (ClassFileStatement) _classStmt.getParent();
}
public void setParseResultsException(ParseResultsException pe) {
_pe = pe;
}
public ParseResultsException getParseResultsException() {
return _pe;
}
public void addStaticFunction( DynamicFunctionSymbol function )
{
assert function.isClassMember();
if( _listStaticFunctions == Collections.EMPTY_LIST )
{
_listStaticFunctions = new ArrayList<DynamicFunctionSymbol>( 2 );
}
else
{
// Remove duplicates. Remain consistent with how non-static functions are maintained (i.e., Map.put() replaces duplicate)
_listStaticFunctions.remove( function );
}
_listStaticFunctions.add( function );
}
private void clearDebugInfoOnFunctions( Collection<DynamicFunctionSymbol> mapFunctions )
{
for( DynamicFunctionSymbol function : mapFunctions )
{
function.clearDebugInfo();
}
}
private void clearDebugInfoOnProperties( Collection<DynamicPropertySymbol> mapProperties )
{
for( DynamicPropertySymbol property : mapProperties )
{
property.clearDebugInfo();
}
}
private void clearDebugInfoOnAnnotations( List<IGosuAnnotation> annotations )
{
if( annotations != null )
{
for( IGosuAnnotation annotation : annotations )
{
if( annotation instanceof GosuAnnotation)
{
((GosuAnnotation)annotation).clearDebugInfo();
}
}
}
}
private void clearDebugInfoOnFields( Collection<VarStatement> fields )
{
for( VarStatement field : fields )
{
field.clearParseTreeInformation();
}
}
public List<DynamicFunctionSymbol> getStaticFunctions() {
return _listStaticFunctions;
}
public Map<String, DynamicFunctionSymbol> getMemberFunctions() {
return _mapMemberFunctions;
}
public void addMemberFunction( DynamicFunctionSymbol function )
{
assert function.isClassMember();
if( function.isStatic() )
{
addStaticFunction( function );
return;
}
if( _mapMemberFunctions == Collections.EMPTY_MAP )
{
//noinspection unchecked
_mapMemberFunctions = new LinkedHashMap( 2 );
}
_mapMemberFunctions.put( function.getName(), function );
}
public Map<String, DynamicFunctionSymbol> getConstructorFunctions() {
return _mapConstructorFunctions;
}
public void addConstructorFunction( DynamicFunctionSymbol function )
{
if( _mapConstructorFunctions == Collections.EMPTY_MAP )
{
//noinspection unchecked
_mapConstructorFunctions = new LinkedHashMap(2);
}
_mapConstructorFunctions.put( function.getName(), function);
}
protected boolean addDefaultConstructor( ISymbolTable symbolTable )
{
NoOpStatement value = new NoOpStatement();
value.initEmptyParseTree();
DynamicFunctionSymbol dfsCtor =
new DynamicFunctionSymbol( symbolTable, _gosuClass.getRelativeName(), GosuTypes.DEF_CTOR_TYPE(), null, value );
dfsCtor.setScriptPart( new ScriptPartId(_gosuClass, null ) );
if( _gosuClass.getSuperClass() != null )
{
// If the supertype is Enum, we need to look for a super constructor that takes (String, int) instead of a no-arg one
// The appropriate arguments to super() will be generated by the compiler and won't appear in the parse tree
DynamicFunctionSymbol defCtorFromSuper;
if( _gosuClass.getSupertype().getGenericType() == JavaTypes.ENUM() )
{
defCtorFromSuper = _gosuClass.getSuperClass().getConstructorFunction( "Enum(java.lang.String, int)" );
}
else
{
defCtorFromSuper = _gosuClass.getSuperClass().getDefaultConstructor();
}
if( defCtorFromSuper == null || !_gosuClass.getSuperClass().isAccessible( _gosuClass, defCtorFromSuper ) )
{
return false;
}
MethodCallExpression e = new MethodCallExpression();
e.setFunctionSymbol( new SuperConstructorFunctionSymbol( defCtorFromSuper ) );
e.setArgs( null );
e.setType( GosuParserTypes.NULL_TYPE() );
MethodCallStatement initializer = new MethodCallStatement();
initializer.setMethodCall( e );
dfsCtor.setInitializer( initializer );
}
else
{
MethodCallExpression e = new MethodCallExpression();
e.setFunctionSymbol( new InitConstructorFunctionSymbol( symbolTable ) );
e.setArgs( null );
e.setType( GosuParserTypes.NULL_TYPE() );
MethodCallStatement initializer = new MethodCallStatement();
initializer.setMethodCall( e );
dfsCtor.setInitializer( initializer );
}
if( _mapConstructorFunctions == Collections.EMPTY_MAP )
{
//noinspection unchecked
_mapConstructorFunctions = new LinkedHashMap(2);
}
if( _gosuClass.isEnum() )
{
dfsCtor.setPrivate( true );
}
_mapConstructorFunctions.put( dfsCtor.getName(), dfsCtor );
return true;
}
boolean addAnonymousConstructor( ISymbolTable symTable, GosuConstructorInfo superCtor )
{
symTable.pushIsolatedScope(new GosuClassTransparentActivationContext(_gosuClass, true));
try
{
NoOpStatement value = new NoOpStatement();
value.initEmptyParseTree();
List<ISymbol> argSymbols = makeArgSymbols( superCtor, symTable );
DynamicFunctionSymbol dfsCtor =
new DynamicFunctionSymbol( symTable, _gosuClass.getRelativeName(),
new FunctionType( GosuTypes.DEF_CTOR_TYPE().getDisplayName(), JavaTypes.pVOID(), typesFromSymbols( argSymbols ) ),
argSymbols, value );
dfsCtor.setScriptPart( new ScriptPartId(_gosuClass, null ) );
IGosuConstructorInfo ctorFromSuper = superCtor;
while( ctorFromSuper instanceof ParameterizedGosuConstructorInfo )
{
ctorFromSuper = ctorFromSuper.getBackingConstructorInfo();
}
MethodCallExpression e = new MethodCallExpression();
DynamicFunctionSymbol dfsCtorFromSuper;
if( ctorFromSuper == null || !_gosuClass.getSuperClass().isAccessible( _gosuClass, dfsCtorFromSuper = getSuperDfsFromSuperCtor( ctorFromSuper ) ) )
{
return false;
}
e.setFunctionSymbol( new SuperConstructorFunctionSymbol( dfsCtorFromSuper ) );
e.setArgs( makeArgs( argSymbols, symTable ) );
e.setType( GosuParserTypes.NULL_TYPE() );
MethodCallStatement initializer = new MethodCallStatement();
initializer.setMethodCall( e );
dfsCtor.setInitializer( initializer );
if( _mapConstructorFunctions == Collections.EMPTY_MAP )
{
//noinspection unchecked
_mapConstructorFunctions = new LinkedHashMap(2);
}
_mapConstructorFunctions.put( dfsCtor.getName(), dfsCtor );
return true;
}
finally
{
symTable.popScope();
}
}
private DynamicFunctionSymbol getSuperDfsFromSuperCtor(IGosuConstructorInfo ctorFromSuper) {
IGosuClassInternal gsClass = (IGosuClassInternal) ctorFromSuper.getOwnersType();
List<DynamicFunctionSymbol> constructors = gsClass.getConstructorFunctions();
for (DynamicFunctionSymbol constructor : constructors) {
if (equals(constructor, ctorFromSuper)) {
return constructor;
}
}
return null;
}
private boolean equals(DynamicFunctionSymbol constructor, IGosuConstructorInfo ctorFromSuper) {
IType[] args1 = constructor.getArgTypes();
List<IReducedSymbol> args2 = ctorFromSuper.getArgs();
if (args1.length != args2.size()) {
return false;
}
for (int i = 0; i < args1.length; i++) {
if (!args1[i].equals(args2.get(i).getType())) {
return false;
}
}
return true;
}
private Identifier[] makeArgs( List<ISymbol> argSymbols, ISymbolTable symTable )
{
Identifier[] args = new Identifier[argSymbols.size()];
for( int i = 0; i < args.length; i++ )
{
ISymbol sym = argSymbols.get( i );
Identifier id = new Identifier();
id.setSymbol( sym, symTable );
id.setType( sym.getType() );
args[i] = id;
}
return args;
}
private List<ISymbol> makeArgSymbols( GosuConstructorInfo ci, ISymbolTable symTable )
{
if(ci == null)
{
return Collections.emptyList();
}
int i = 0;
List<ISymbol> args = new ArrayList<ISymbol>( ci.getParameters().length );
for( IParameterInfo pi : ci.getParameters() )
{
Symbol sym = new Symbol( "p" + i++, pi.getFeatureType(), symTable, null );
symTable.putSymbol( sym );
args.add( sym );
}
return args;
}
private IType[] typesFromSymbols( List<ISymbol> argSymbols )
{
if( argSymbols.size() == 0 )
{
return IType.EMPTY_ARRAY;
}
IType[] types = new IType[argSymbols.size()];
for( int i = 0; i < types.length; i++ )
{
types[i] = argSymbols.get( i ).getType();
}
return types;
}
public void addStaticProperty( DynamicPropertySymbol property )
{
if( _listStaticProperties == Collections.EMPTY_LIST )
{
_listStaticProperties = new ArrayList<DynamicPropertySymbol>( 2 );
}
_listStaticProperties.add( property );
}
public List<DynamicPropertySymbol> getStaticProperties() {
return _listStaticProperties;
}
public Map<String, VarStatement> getMemberFields() {
return _mapMemberFields;
}
public void addMemberProperty( DynamicPropertySymbol property )
{
if( property.isStatic() )
{
addStaticProperty(property);
return;
}
if( _mapMemberProperties == Collections.EMPTY_MAP )
{
//noinspection unchecked
_mapMemberProperties = new LinkedHashMap(2);
}
_mapMemberProperties.put( property.getName(), property );
}
private void addStaticField( VarStatement varStmt )
{
// Static vars must be maintained in the order declared. They must evaluate in that order.
if( _mapStaticFields == Collections.EMPTY_MAP )
{
//noinspection unchecked
_mapStaticFields = new LinkedHashMap(2);
}
_mapStaticFields.put( varStmt.getIdentifierName(), varStmt );
}
public Map<String, DynamicPropertySymbol> getMemberProperties() {
return _mapMemberProperties;
}
public Map<String, VarStatement> getStaticFields() {
return _mapStaticFields;
}
public void addMemberField( VarStatement varStmt )
{
if( varStmt.isStatic() )
{
addStaticField( varStmt );
return;
}
if( _mapMemberFields == Collections.EMPTY_MAP )
{
//noinspection unchecked
_mapMemberFields = new LinkedHashMap(2);
}
// Static vars must be maintained in the order declared. They must evaluate in that order.
String varName = varStmt.getIdentifierName();
_mapMemberFields.put( varName, varStmt );
if( !_memberFieldIndexByName.containsKey( varName ) )
{
int iIndex = _memberFieldIndexByName.size();
if( _memberFieldIndexByName == Collections.EMPTY_MAP )
{
_memberFieldIndexByName = new HashMap<CharSequence, ISymbol>( 4 );
}
_memberFieldIndexByName.put( varName, new MemberFieldSymbol( iIndex, varName ) );
}
if (_gosuClass instanceof GosuProgram) {
// Remove initializers, fields are assigned in the programs entry point function
varStmt.setAsExpression( null );
}
}
public Map<CharSequence, ISymbol> getMemberFieldIndexByName() {
return _memberFieldIndexByName;
}
public Symbol getStaticThisSymbol()
{
if( _thisSymbol == null )
{
if( _gosuClass.isParameterizedType() )
{
GosuClass genericType = (GosuClass) _gosuClass.getGenericType();
_thisSymbol = ((GosuClass) genericType.dontEverCallThis()).getStaticThisSymbol();
}
else
{
_thisSymbol = makeThisSymbol();
}
}
return _thisSymbol;
}
private Symbol makeThisSymbol()
{
return new ReadOnlySymbol( Keyword.KW_this.getName(), TypeLord.getConcreteType( _gosuClass ), Symbol.MEMBER_STACK_PROVIDER, null );
}
public Map<String, ICapturedSymbol> getCapturedSymbols() {
return _capturedSymbols;
}
public void addCapturedSymbolSilent( ICapturedSymbol sym )
{
if( _capturedSymbols.isEmpty() )
{
_capturedSymbols = new HashMap<String, ICapturedSymbol>( 2 );
}
_capturedSymbols.put( sym.getName(), sym );
}
public static void clear() {
// int n = 0;
// for (GosuClassParseInfo info : new ArrayList<GosuClassParseInfo>(_allParseInfos)) {
// IGosuClassInternal gosuClass = info._gosuClass;
// for (IGosuClassInternal parameterizedType : gosuClass.getParameterizedTypes()) {
// parameterizedType.clearParseInfo();
// n++;
// }
// gosuClass.clearParseInfo();
// n++;
// }
// System.out.println("Dropped " + n);
// _allParseInfos = null;
}
public void setBlock(BlockExpression blk) {
_block = blk;
}
public BlockExpression getBlock() {
return _block;
}
public void maybeClearDebugInfo() {
if (_gosuClass.getTypeLoader().getModule().getExecutionEnvironment().isSingleModuleMode()) {
TypeSystem.lock();
try {
if (!_gosuClass.getTypeLoader().shouldKeepDebugInfo(_gosuClass)) {
clearDebugInfoOnFields(_mapStaticFields.values());
clearDebugInfoOnFields(_mapMemberFields.values());
clearDebugInfoOnProperties(_listStaticProperties);
clearDebugInfoOnProperties(_mapMemberProperties.values());
clearDebugInfoOnFunctions(_mapMemberFunctions.values());
clearDebugInfoOnFunctions(_listStaticFunctions);
clearDebugInfoOnFunctions(_mapConstructorFunctions.values());
clearDebugInfoOnAnnotations(_gosuClass.getModifierInfo().getAnnotations());
getClassStatement().clearParseTreeInformation();
Set<IUsesStatement> usesStatements = _gosuClass.getTypeUsesMap() == null ? null : _gosuClass.getTypeUsesMap().getUsesStatements();
if (usesStatements != null) {
for (IUsesStatement usesStatement : usesStatements) {
usesStatement.clearParseTreeInformation();
}
}
}
} finally {
TypeSystem.unlock();
}
}
}
public void updateSource(String source) {
_sourceFingerprint = new FP64(source).getRawFingerprint();
}
public long getSourceFingerprint() {
return _sourceFingerprint;
}
}