/*
* Copyright 2013 Guidewire Software, Inc.
*/
package gw.internal.gosu.parser;
import gw.config.CommonServices;
import gw.internal.gosu.parser.expressions.BlockExpression;
import gw.internal.gosu.parser.expressions.ClassDeclaration;
import gw.internal.gosu.parser.expressions.InterfacesClause;
import gw.internal.gosu.parser.expressions.MethodCallExpression;
import gw.internal.gosu.parser.expressions.NameInDeclaration;
import gw.internal.gosu.parser.expressions.NullExpression;
import gw.internal.gosu.parser.expressions.ParameterListClause;
import gw.internal.gosu.parser.expressions.SuperTypeClause;
import gw.internal.gosu.parser.expressions.TypeLiteral;
import gw.internal.gosu.parser.expressions.TypeVariableDefinitionImpl;
import gw.internal.gosu.parser.statements.ClassStatement;
import gw.internal.gosu.parser.statements.ConstructorStatement;
import gw.internal.gosu.parser.statements.DelegateStatement;
import gw.internal.gosu.parser.statements.FunctionStatement;
import gw.internal.gosu.parser.statements.MethodCallStatement;
import gw.internal.gosu.parser.statements.NamespaceStatement;
import gw.internal.gosu.parser.statements.NoOpStatement;
import gw.internal.gosu.parser.statements.NotAStatement;
import gw.internal.gosu.parser.statements.PropertyStatement;
import gw.internal.gosu.parser.statements.ReturnStatement;
import gw.internal.gosu.parser.statements.StatementList;
import gw.internal.gosu.parser.statements.UsesStatement;
import gw.internal.gosu.parser.statements.VarInitializationVerifier;
import gw.internal.gosu.parser.statements.VarStatement;
import gw.lang.annotation.UsageTarget;
import gw.lang.parser.GlobalScope;
import gw.lang.parser.GosuParserTypes;
import gw.lang.parser.IBlockClass;
import gw.lang.parser.IDynamicFunctionSymbol;
import gw.lang.parser.IFullParserState;
import gw.lang.parser.IFunctionSymbol;
import gw.lang.parser.ILanguageLevel;
import gw.lang.parser.IParseIssue;
import gw.lang.parser.IParseTree;
import gw.lang.parser.IScope;
import gw.lang.parser.ISymbol;
import gw.lang.parser.ISymbolTable;
import gw.lang.parser.IToken;
import gw.lang.parser.ITokenizerOffsetMarker;
import gw.lang.parser.ITypeUsesMap;
import gw.lang.parser.Keyword;
import gw.lang.parser.ScriptPartId;
import gw.lang.parser.exceptions.NotImplementedParseException;
import gw.lang.parser.exceptions.ObsoleteConstructorWarning;
import gw.lang.parser.exceptions.ParseException;
import gw.lang.parser.exceptions.ParseIssue;
import gw.lang.parser.exceptions.ParseResultsException;
import gw.lang.parser.expressions.IMemberAccessExpression;
import gw.lang.parser.expressions.IModifierListClause;
import gw.lang.parser.expressions.IParameterDeclaration;
import gw.lang.parser.expressions.ITypeVariableDefinition;
import gw.lang.parser.expressions.ITypeVariableDefinitionExpression;
import gw.lang.parser.resources.Res;
import gw.lang.parser.statements.IFunctionStatement;
import gw.lang.parser.statements.ITerminalStatement;
import gw.lang.parser.statements.IUsesStatementList;
import gw.lang.reflect.FunctionType;
import gw.lang.reflect.IEnhanceableType;
import gw.lang.reflect.IFeatureInfo;
import gw.lang.reflect.IFunctionType;
import gw.lang.reflect.IInvocableType;
import gw.lang.reflect.IMethodInfo;
import gw.lang.reflect.IPropertyInfo;
import gw.lang.reflect.IRelativeTypeInfo;
import gw.lang.reflect.IType;
import gw.lang.reflect.ITypeInfo;
import gw.lang.reflect.ITypeVariableType;
import gw.lang.reflect.Modifier;
import gw.lang.reflect.TypeSystem;
import gw.lang.reflect.gs.ClassType;
import gw.lang.reflect.gs.IGenericTypeVariable;
import gw.lang.reflect.gs.IGosuClass;
import gw.lang.reflect.gs.IGosuClassParser;
import gw.lang.reflect.gs.IGosuProgram;
import gw.lang.reflect.gs.ISourceFileHandle;
import gw.lang.reflect.gs.StringSourceFileHandle;
import gw.lang.reflect.java.GosuTypes;
import gw.lang.reflect.java.IJavaType;
import gw.lang.reflect.java.JavaTypes;
import gw.util.GosuExceptionUtil;
import gw.util.GosuObjectUtil;
import gw.util.GosuStringUtil;
import gw.util.Stack;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
*/
@SuppressWarnings({"ThrowableInstanceNeverThrown"})
public class GosuClassParser extends ParserBase implements IGosuClassParser, ITokenizerOffsetMarker
{
private int _iClassOffset;
private int _iClassLineNum;
private int _iClassColumn;
private ClassStatement _classStmt;
private Stack<IGosuClassInternal> _innerClasses;
private int _innerClassOffset;
public GosuClassParser( GosuParser owner )
{
super( owner );
_innerClasses = new Stack<IGosuClassInternal>();
}
//## todo: maybe ctors should set the class here so that subsequent calls to parseXxx() don't need to take a IGosuClass
private GosuClassParser( GosuParser owner, IGosuClassInternal innerClass )
{
super( owner );
int mark = ((InnerClassFileSystemSourceFileHandle)innerClass.getSourceFileHandle()).getMark();
if( mark >= 0 )
{
getTokenizer().restoreToMark( mark );
}
else
{
goToPosition( innerClass.getSourceFileHandle().getOffset() );
}
_innerClassOffset = getTokenizer().mark();
_innerClasses = new Stack<IGosuClassInternal>();
}
public static void parseAnonymousInnerClass( GosuParser gosuParser, IGosuClassInternal innerGsClass )
{
Stack<BlockExpression> enclosingBlocks = gosuParser._blocks;
gosuParser.setBlocks( null );
Map<String, Set<IFunctionSymbol>> restoreDfsDecls = gosuParser.getDfsDecls();
try
{
new GosuClassParser( gosuParser, innerGsClass ).parseHeader(innerGsClass, false, true, true );
new GosuClassParser( gosuParser, innerGsClass ).parseDeclarations( innerGsClass );
new GosuClassParser( gosuParser, innerGsClass ).parseDefinitions( innerGsClass );
}
finally
{
gosuParser.setDfsDeclInSetByName( restoreDfsDecls );
gosuParser.setBlocks( enclosingBlocks );
}
}
@Override
protected String getScript()
{
return getOwner().getScript();
}
@Override
public int getLineNumShift()
{
return getOwner().getLineNumShift();
}
@Override
public int getOffsetShift()
{
return getOwner().getOffsetShift();
}
@Override
public int getOffsetMark()
{
if( isInnerClass( getGosuClass() ) )
{
return _innerClassOffset;
}
return -1;
}
@Override
public ClassStatement getClassStatement()
{
return _classStmt;
}
private void setClassStatement( ClassStatement classStmt )
{
if( classStmt == null )
{
throw new IllegalArgumentException( "Class stmt is null" );
}
_classStmt = classStmt;
}
private IGosuClassInternal getCurrentInnerClass()
{
return _innerClasses.isEmpty() ? null : _innerClasses.peek();
}
private void pushInnerClass( IGosuClassInternal gsInnerClass )
{
_innerClasses.push( gsInnerClass );
}
private IGosuClassInternal popInnerClass( IGosuClassInternal gsInnerClass )
{
IGosuClassInternal top = _innerClasses.pop();
if( top != gsInnerClass )
{
throw new IllegalStateException( "Unbalanced push/pop for inner classes" );
}
return top;
}
private boolean isInnerClassesEmpty()
{
return _innerClasses.isEmpty();
}
/**
* Parses all declarations including:<br>
* <ul>
* <li> Fields
* <li> Methods
* <li> Properties
* <li> Inner types, recursively
* </ul>
*/
public void parseDeclarations( IGosuClass gsCls )
{
IGosuClassInternal gsClass = (IGosuClassInternal)gsCls;
if( gsClass.isDeclarationsCompiled() )
{
if( !gsClass.isInnerDeclarationsCompiled() )
{
if( parseDeclarationsOfLeftOverInnerClasses( gsClass ) )
{
gsClass.setInnerDeclarationsCompiled();
}
}
return;
}
boolean bPushedScope = pushScopeIfNeeded( gsClass );
getTokenizer().pushOffsetMarker( this );
ScriptPartId scriptPartId = new ScriptPartId( gsClass, null );
getOwner().pushScriptPart( scriptPartId );
GosuClassCompilingStack.pushCompilingType( gsClass );
gsClass.setCompilingDeclarations( true );
try
{
ClassStatement classStmt = (ClassStatement)gsClass.getClassStatement();
try
{
setClassStatement( classStmt );
}
catch( Exception e )
{
throw GosuExceptionUtil.forceThrow( e, gsClass.getName() );
}
if( isTopLevelClass( gsClass ) || TypeLord.isEvalProgram( gsClass ) )
{
classStmt.getClassFileStatement().clearParseTreeInformation();
}
// Don't need an isolated scope here because class members are all dynamic
// and, therefore, don't have to be indexed wrt an isolated scope.
getSymbolTable().pushScope();
try
{
//## todo: reparsing header with annotations this time, any chance we can do that the first time we parse the header, so we can avoid doing it twice?
String strClassName = parseHeader(gsClass, false, false, true);
if( gsClass instanceof IGosuEnhancementInternal )
{
parseEnhancementBodyDecl( gsClass );
}
else
{
parseClassBodyDecl( strClassName, gsClass );
}
}
finally
{
getSymbolTable().popScope();
pushStatement( classStmt );
setLocation( _iClassOffset, _iClassLineNum, _iClassColumn, true );
popStatement();
if( isTopLevelClass( gsClass ) || TypeLord.isEvalProgram( gsClass ) )
{
pushStatement( classStmt.getClassFileStatement() );
setLocation( 0, 1, _iClassColumn, true );
popStatement();
}
}
classStmt.compactParseTree();
}
finally
{
gsClass.setCompilingDeclarations( false );
// Do not set decls compiled; we do that in parseClassBodyDecl(). Also the decls may not have actually been compiled
//gsClass.setDeclarationsCompiled();
GosuClassCompilingStack.popCompilingType();
getOwner().popScriptPart( scriptPartId );
popScopeIfNeeded( bPushedScope, gsClass );
getTokenizer().popOffsetMarker( this );
removeTypeVarsFromParserMap( gsClass );
}
}
private boolean isTopLevelClass( IGosuClassInternal gsClass )
{
return gsClass.getEnclosingType() == null;
}
// /**
// * Extend the bounds of the enclosing ClassFileStatement if need be. Note this is only necessary when
// * the enclosing class has errors and, therefore, may not have parsed elements with the
// */
// private void extendEnclosingClassFileBounds( IParsedElement enclosingClassFileStmt )
// {
// if( enclosingClassFileStmt.getLocation() != null )
// {
// int iExtentDelta = enclosingClassFileStmt.getLocation().getExtent() - getClassStatement().getClassFileStatement().getLocation().getExtent();
// if( iExtentDelta < 0 )
// {
// enclosingClassFileStmt.getLocation().setLength( enclosingClassFileStmt.getLocation().getLength() + -iExtentDelta );
// }
// }
// }
public void parseDefinitions( IGosuClass gsCls )
{
IGosuClassInternal gsClass = (IGosuClassInternal)gsCls;
getTokenizer().pushOffsetMarker( this );
boolean bPushedScope = pushScopeIfNeeded( gsClass );
gsClass.setCompilingDefinitions( true );
GosuClassParseInfo parseInfo = gsClass.getParseInfo();
ClassStatement classStmt = parseInfo.getClassStatement();
setClassStatement( classStmt );
clearParseTree( gsClass );
ScriptPartId scriptPartId = new ScriptPartId( gsClass, null );
getOwner().pushScriptPart( scriptPartId );
GosuClassCompilingStack.pushCompilingType( gsClass );
getOwner()._iReturnOk++;
if( isDeprecated( (ModifierInfo)gsCls.getModifierInfo() ) )
{
getOwner().pushIgnoreTypeDeprecation();
}
try
{
try
{
if( !gsClass.isDefinitionsCompiled() )
{
// Don't need an isolated scope here because class members are all dynamic
// and, therefore, don't have to be indexed wrt an isolated scope.
getSymbolTable().pushScope();
try
{
//
// Reset the tokenizer to prepare for secon.. er third pass
//
getTokenizer().reset();
if( isTopLevelClass( gsClass ) || TypeLord.isEvalProgram( gsClass ) )
{
getLocationsList().clear();
}
else
{
removeInnerClassDelcarationsFromLocationsList( gsClass );
}
//
// Parse the whole class, including inner types
//
// Note function definitions are parsed as no-op statements, but are
// pushed onto the dynamic function symobl stack.
//## todo: do we really need to parse the header *again* (maybe for annotations?)
parseHeader(gsClass, false, false, true );
if( gsClass instanceof IGosuEnhancementInternal )
{
parseClassStatementAsEnhancement( gsClass );
}
else
{
parseClassStatement();
}
}
finally
{
getSymbolTable().popScope();
if( gsClass instanceof IGosuProgramInternal )
{
((IGosuProgramInternal)gsClass).setParsingExecutableProgramStatements( true );
try
{
FunctionStatement fs = parseExecutableProgramStatements( (IGosuProgramInternal)gsClass );
makeExprRootFunction( (IGosuProgramInternal)gsClass, fs );
}
finally
{
((IGosuProgramInternal)gsClass).setParsingExecutableProgramStatements( false );
}
}
boolean b = isInnerClass( gsClass ) || match( null, SourceCodeTokenizer.TT_EOF );
if( !verify( classStmt, b, Res.MSG_END_OF_STMT ) )
{
consumeTrailingTokens();
}
gsClass.setDefinitionsCompiled();
}
}
if( isTopLevelClass( gsClass ) || TypeLord.isEvalProgram( gsClass ) )
{
getOwner().setParsed( true );
}
}
finally
{
pushStatement( classStmt );
setLocation( _iClassOffset, _iClassLineNum, _iClassColumn, true );
if( isTopLevelClass( gsClass ) || TypeLord.isEvalProgram( gsClass ) )
{
popStatement();
pushStatement( classStmt.getClassFileStatement() );
setLocation( 0, 1, _iClassColumn, true );
popStatement();
}
assignTokens( classStmt );
}
if( !ILanguageLevel.Util.STANDARD_GOSU() && !CommonServices.getPlatformHelper().isInIDE() )
{
// Only support this insanity in gw gosu
CompileTimeAnnotationHandler.postDefinitionVerification( classStmt );
}
try
{
verifyParsedElement( isInnerClass( gsClass ) && !TypeLord.isEvalProgram( gsClass ) ? classStmt : classStmt.getClassFileStatement() );
}
catch( ParseResultsException pre )
{
gsClass.setParseResultsException( pre );
}
}
finally
{
try
{
gsClass.setCompilingDefinitions( false );
gsClass.setDefinitionsCompiled();
getOwner().popScriptPart( scriptPartId );
}
finally
{
GosuClassCompilingStack.popCompilingType();
}
popScopeIfNeeded( bPushedScope, gsClass );
getTokenizer().popOffsetMarker( this );
removeTypeVarsFromParserMap( gsClass );
getOwner()._iReturnOk--;
pushStatement( _classStmt.getClassFileStatement() );
setLocation( 0, 1, _iClassColumn, true );
popStatement();
if( isDeprecated( (ModifierInfo)gsCls.getModifierInfo() ) )
{
getOwner().popIgnoreTypeDeprecation();
}
gsClass.syncGenericAndParameterizedClasses();
getOwner().clearDfsStack();
_classStmt = null;
VarInitializationVerifier.verifyFinalFields( gsClass );
VarInitializationVerifier.verifyLocalVars( gsClass, true );
}
}
private void removeInnerClassDelcarationsFromLocationsList( IGosuClassInternal gsClass )
{
List<ParseTree> locations = getLocationsList();
for( int i = locations.size()-1; i >= 0; i-- )
{
ParseTree csr = locations.get( i );
if( csr.getScriptPartId().getContainingType() == gsClass )
{
IParseTree parent = csr.getParent();
if( parent != null )
{
parent.removeChild( csr );
}
locations.remove( csr );
}
else
{
break;
}
}
}
private void consumeTrailingTokens()
{
while( !match( null, SourceCodeTokenizer.TT_EOF ) )
{
getTokenizer().nextToken();
}
}
private void assignTokens( ClassStatement classStmt )
{
if( !getOwner().isEditorParser() )
{
return;
}
if( !isTopLevelClass( classStmt.getGosuClass() ) )
{
return;
}
List<IToken> tokens = getOwner().getTokenizer().getTokens().toList();
classStmt.getClassFileStatement().assignTokens( tokens );
//## todo: handle programs (see GosuAstTransformer)
// String strSource = getGosuClass().getSource();
// String strTextFromParseTree = classStmt.getClassFileStatement().getLocation().getTextFromTokens();
// if( !strSource.equals( strTextFromParseTree ) )
// {
// int[] diff = getDiffOffset( strSource, strTextFromParseTree );
//
// throw new IllegalStateException( buildInconsistentParseErrorMessage( strSource, strTextFromParseTree, diff ) );
// }
//noinspection LoopStatementThatDoesntLoop
for( IToken token : tokens )
{
throw new IllegalStateException( "One or more tokens were not assigned: " + token );
}
}
private String buildInconsistentParseErrorMessage( String strSource, String strTextFromParseTree, int[] diff )
{
return
"Parsed class, " + getGosuClass().getName() + ", inconsistent with source.\n" +
"Line: " + diff[1] + " Offset: " + diff[0] + "\n" +
"*** Parsed Version ***\n" +
ParseIssue.makeContextString( diff[1], strTextFromParseTree, diff[2] ) + "\n" +
"*** Source Version ***\n" +
ParseIssue.makeContextString( diff[1], strSource, diff[2] ) + "\n";
}
private int[] getDiffOffset( String strSource, String strTextFromParseTree )
{
if( strSource == null || strTextFromParseTree == null )
{
return null;
}
int i;
int iLineOffset = 0;
int iLine = 0;
for( i = 0; i < strSource.length(); i++ )
{
if( i >= strTextFromParseTree.length() )
{
return new int[] {i, iLine, iLineOffset};
}
char sourceChar = strSource.charAt( i );
char parserChar = strTextFromParseTree.charAt( i );
if( sourceChar != parserChar )
{
return new int[] {i, iLine, iLineOffset};
}
if( parserChar == '\n' )
{
iLine++;
iLineOffset = i;
}
}
return new int[] {i, iLine, iLineOffset};
}
private void clearParseTree( IGosuClassInternal gsClass )
{
if( (!(gsClass instanceof IGosuProgram) && isTopLevelClass( gsClass )) ||
TypeLord.isEvalProgram( gsClass ) )
{
gsClass.getClassStatement().getClassFileStatement().clearParseTreeInformation();
}
else
{
gsClass.getClassStatement().clearParseTreeInformation();
if( gsClass.isAnonymous() )
{
//noinspection SuspiciousMethodCalls
if( !getLocationsList().isEmpty() )
{
ParseTree last = getLocationsList().get( getLocationsList().size() - 1 );
if( last.getParsedElement() == null )
{
// Remove abandoned class-stmt parse tree from decl parse
getLocationsList().remove( last );
}
}
}
}
}
private boolean isInnerClass( IGosuClassInternal gsClass )
{
return gsClass.getEnclosingType() != null;
}
private FunctionStatement parseExecutableProgramStatements( IGosuProgramInternal gsClass )
{
List savedLocations = getOwner().getLocations();
getTokenizer().resetButKeepTokens();
getLocationsList().clear();
getOwner().setLocationsFromProgramClassParser( savedLocations );
parseHeader( gsClass, false, false, true );
gsClass.addCapturedProgramSymbols( getSymbolTable() );
FunctionStatement fs = parseProgramAsFunctionStatement( gsClass );
List newLocations = getOwner().getLocations();
removeRedundantUsesStatementList( newLocations );
getOwner().getLocationsList().clear();
getOwner().setLocationsFromProgramClassParser( null );
getOwner().getLocationsList().addAll( savedLocations );
getOwner().getLocationsList().addAll( newLocations );
return fs;
}
private void removeRedundantUsesStatementList( List newLocations )
{
for( int i = 0; i < newLocations.size(); i++ )
{
IParseTree pt = (IParseTree)newLocations.get( i );
if( pt.getParsedElement() instanceof IUsesStatementList )
{
newLocations.remove( i-- );
}
}
}
private void makeExprRootFunction( IGosuProgramInternal gsClass, FunctionStatement callableStmt )
{
DynamicFunctionSymbol dfsDecl = getProgramRootExprValueDfs();
if (dfsDecl != null) {
getOwner().putDfsDeclInSetByName( dfsDecl );
StatementList stmtList = makeReturnStatementWithExprRoot( gsClass, callableStmt );
if( stmtList != null )
{
FunctionStatement fs = new FunctionStatement();
fs.setDynamicFunctionSymbol( dfsDecl );
dfsDecl.setValueDirectly( stmtList );
getOwner().pushDynamicFunctionSymbol( dfsDecl );
fs.setDynamicFunctionSymbol( dfsDecl );
dfsDecl.setClassMember( true );
gsClass.getParseInfo().addMemberFunction(dfsDecl);
}
}
}
private StatementList makeReturnStatementWithExprRoot( IGosuProgramInternal gsClass, FunctionStatement callableStmt )
{
Statement statement = (Statement)callableStmt.getDynamicFunctionSymbol().getValueDirectly();
if( statement != null )
{
boolean[] bAbsolute = {false};
ITerminalStatement significantTerminalStatement = statement.getLeastSignificantTerminalStatement( bAbsolute );
if( gsClass.isGenRootExprAccess() &&
bAbsolute[0] &&
significantTerminalStatement instanceof ReturnStatement &&
significantTerminalStatement.getParent() != null &&
significantTerminalStatement.getParent().getParent() == callableStmt )
{
ReturnStatement rs = (ReturnStatement)significantTerminalStatement;
Expression expr = rs.getValue();
if( expr instanceof IMemberAccessExpression )
{
Expression rootExpr = (Expression)((IMemberAccessExpression)expr).getRootExpression();
ReturnStatement defaultReturnStmt = new ReturnStatement();
defaultReturnStmt.setValue( rootExpr );
List<Statement> stmts = new ArrayList<Statement>( 2 );
stmts.add( defaultReturnStmt );
StatementList stmtList = new StatementList( getSymbolTable() );
stmtList.setStatements( stmts );
return stmtList;
}
}
}
ReturnStatement defaultReturnStmt = new ReturnStatement();
NullExpression nullExpr = new NullExpression();
nullExpr.setType( JavaTypes.OBJECT() );
defaultReturnStmt.setValue( nullExpr );
List<Statement> stmts = new ArrayList<Statement>( 2 );
stmts.add( defaultReturnStmt );
StatementList stmtList = new StatementList( getSymbolTable() );
stmtList.setStatements( stmts );
return stmtList;
}
private DynamicFunctionSymbol getProgramRootExprValueDfs()
{
for( IDynamicFunctionSymbol dfs : getGosuClass().getMemberFunctions() )
{
if( dfs.getName().contains( "evaluateRootExpr" ) )
{
return (DynamicFunctionSymbol)dfs;
}
}
return null;
}
private FunctionStatement parseProgramAsFunctionStatement( IGosuClassInternal gsClass )
{
// Copy the Non-Static Scope so we can reuse it for each member
//
IScope nonstaticScope;
Map<String, Set<IFunctionSymbol>> nonstaticDfsMap;
getSymbolTable().pushScope();
try
{
getOwner().newDfsDeclInSetByName();
gsClass.putClassMembers( getOwner(), getSymbolTable(), getGosuClass(), false );
nonstaticDfsMap = getOwner().getDfsDecls();
getOwner().newDfsDeclInSetByName();
}
finally
{
nonstaticScope = getSymbolTable().popScope();
}
getSymbolTable().pushScope();
getOwner().newDfsDeclInSetByName();
FunctionStatement functionStmt;
int iOffset = getTokenizer().getTokenStart();
int iLineNum = getTokenizer().getLineNumber();
int iColumn = getTokenizer().getTokenColumn();
try
{
getOwner().setDfsDeclInSetByName( nonstaticDfsMap );
getOwner().putDfsDeclsInTable( ((IGosuProgramInternal)getGosuClass()).getSymbolTable() );
getSymbolTable().pushScope( nonstaticScope );
getOwner().pushParsingStaticMember( false );
try
{
functionStmt = getOwner().parseProgramEntryPointBody();
}
finally
{
getSymbolTable().popScope();
getOwner().popParsingStaticMember();
}
DynamicFunctionSymbol dfs = functionStmt == null ? null : functionStmt.getDynamicFunctionSymbol();
if( dfs != null )
{
dfs.setClassMember( true );
if( dfs.getDisplayName().equals( gsClass.getRelativeName() ) )
{
gsClass.getParseInfo().addConstructorFunction(dfs);
}
else
{
gsClass.getParseInfo().addMemberFunction(dfs);
}
}
}
finally
{
getOwner().newDfsDeclInSetByName();
getSymbolTable().popScope();
}
setLocation( iOffset, iLineNum, iColumn, true );
if( getTokenizer().getTokenStart() == iOffset )
{
getLocationsList().remove( getLocationsList().size() - 1 );
}
functionStmt = (FunctionStatement)popStatement();
return functionStmt;
}
private void parseClassBodyDecl( String strClassName, IGosuClassInternal gsClass )
{
try
{
if( strClassName != null )
{
IType type = TypeLoaderAccess.instance().getIntrinsicTypeByFullName( strClassName );
if( TypeSystem.getOrCreateTypeReference( gsClass ) != type && !(gsClass instanceof IGosuClassFragment) )
{
getClassStatement().addParseException( new ParseException( makeFullParserState(), Res.MSG_DUPLICATE_CLASS_FOUND, type.getName() ) );
}
}
}
catch( ClassNotFoundException e )
{
// ignore
}
verify( getClassStatement(), gsClass instanceof IGosuProgram || match( null, '{' ), Res.MSG_EXPECTING_OPEN_BRACE_FOR_CLASS_DEF );
if( !putClassMembersOfSuperAndInterfaces( gsClass ) )
{
return;
}
if( isInnerClass( gsClass ) && !gsClass.isStatic() )
{
addOuterMember( gsClass );
}
addAutomaticEnumMethodsAndProperties( gsClass );
processEnumConstants( gsClass );
for( Object member = parseFunctionOrConstructorOrFieldDeclaration( gsClass );
member != null;
member = parseFunctionOrConstructorOrFieldDeclaration( gsClass ) )
{
popStatement();
if( member instanceof DynamicFunctionSymbol )
{
processFunctionSymbol( (DynamicFunctionSymbol)member, gsClass );
}
else if( member instanceof DynamicPropertySymbol )
{
processPropertySymbol( (DynamicPropertySymbol)member, gsClass );
}
else
{
processVarStmt( gsClass, (VarStatement)member );
}
}
if( !gsClass.isInterface() )
{
if( !gsClass.ensureDefaultConstructor( getSymbolTable() ) )
{
getClassStatement().addParseException( new ParseException( makeFullParserState(),
Res.MSG_NO_DEFAULT_CTOR_IN,
gsClass.getSupertype().getName() ) );
}
}
boolean b = isInnerClass( gsClass ) || match( null, SourceCodeTokenizer.TT_EOF );
verify( getClassStatement(), b, Res.MSG_END_OF_STMT );
gsClass.addDelegateImpls( getSymbolTable(), this );
if( gsClass instanceof IGosuProgramInternal )
{
((IGosuProgramInternal)gsClass).addProgramEntryPoint( getSymbolTable(), this );
}
if( gsClass instanceof IGosuTemplateInternal )
{
((IGosuTemplateInternal)gsClass).addTemplateEntryPoints( getSymbolTable(), this );
}
gsClass.syncGenericAndParameterizedClasses();
gsClass.setDeclarationsCompiled();
if( parseDeclarationsOfLeftOverInnerClasses( gsClass ) )
{
gsClass.setInnerDeclarationsCompiled();
}
}
private boolean putClassMembersOfSuperAndInterfaces( IGosuClassInternal gsClass )
{
for( IType type : gsClass.getInterfaces() )
{
if( !(type instanceof ErrorType) )
{
if( !putClassMembersOfSuperOrInterface( type ) )
{
return false;
}
}
}
return putClassMembersOfSuperOrInterface( gsClass.getSuperClass() );
}
private boolean putClassMembersOfSuperOrInterface( IType type )
{
IGosuClassInternal gsType = IGosuClassInternal.Util.getGosuClassFrom( type );
if( gsType != null )
{
gsType.compileDeclarationsIfNeeded();
if( !gsType.isDeclarationsCompiled() )
{
advanceToClassBodyEnd();
// Try again after enclosing class finishes
return false;
}
gsType.putClassMembers( getOwner(), getSymbolTable(), getGosuClass(), false );
}
return true;
}
private boolean parseDeclarationsOfLeftOverInnerClasses( IGosuClassInternal gsClass )
{
int iCount = 0;
int iPriorCount;
Collection<? extends IGosuClass> innerClasses = gsClass.getKnownInnerClassesWithoutCompiling().values();
do
{
iPriorCount = iCount;
iCount = 0;
for( IGosuClass c : innerClasses )
{
IGosuClassInternal innerClass = (IGosuClassInternal)c;
if( !innerClass.isDeclarationsCompiled() )
{
int state = getTokenizer().mark();
new GosuClassParser( getOwner(), innerClass ).parseDeclarations( innerClass );
getTokenizer().restoreToMark( state );
iCount += (innerClass.isDeclarationsCompiled() && innerClass.isInnerDeclarationsCompiled()) ? 0 : 1;
}
}
if( iPriorCount > 0 && iPriorCount == iCount )
{
// Could not decl parse one or more inner classes, must be a cycle; will reparse later
return false;
}
} while( iCount > 0 );
return true;
}
private void addAutomaticEnumMethodsAndProperties( IGosuClassInternal gsClass )
{
if( gsClass.isEnum() )
{
addEnumProperty( gsClass, new EnumCodePropertySymbol( gsClass, TypeSystem.getCompiledGosuClassSymbolTable() ) );
addEnumProperty( gsClass, new EnumDisplayNamePropertySymbol( gsClass, TypeSystem.getCompiledGosuClassSymbolTable() ) );
addEnumProperty( gsClass, new EnumNamePropertySymbol( gsClass, TypeSystem.getCompiledGosuClassSymbolTable() ) );
addEnumProperty( gsClass, new EnumOrdinalPropertySymbol( gsClass, TypeSystem.getCompiledGosuClassSymbolTable() ) );
addEnumProperty( gsClass, new EnumValuePropertySymbol( gsClass, TypeSystem.getCompiledGosuClassSymbolTable() ) );
addEnumProperty( gsClass, new EnumAllValuesPropertySymbol( gsClass, TypeSystem.getCompiledGosuClassSymbolTable() ) );
DynamicFunctionSymbol dfs = new EnumValueOfFunctionSymbol( gsClass, TypeSystem.getCompiledGosuClassSymbolTable() );
gsClass.getParseInfo().addMemberFunction( dfs );
getOwner().putDfsDeclInSetByName( dfs );
dfs = new EnumValuesFunctionSymbol( gsClass, TypeSystem.getCompiledGosuClassSymbolTable() );
gsClass.getParseInfo().addMemberFunction( dfs );
getOwner().putDfsDeclInSetByName( dfs );
}
}
private void addEnumProperty( IGosuClassInternal gsClass, DynamicPropertySymbol dps )
{
gsClass.getParseInfo().addMemberProperty( dps );
getOwner().putDfsDeclInSetByName( dps.getGetterDfs() ); // put in dfs map to prevent overriding by enum impl class
}
private void processEnumConstants( IGosuClassInternal gsClass )
{
boolean bEnum = gsClass != null && gsClass.isEnum();
if( !bEnum )
{
return;
}
Token t = new Token();
int state = getTokenizer().mark();
boolean bAtLeastOneConst = false;
do
{
int iOffset = getTokenizer().getTokenStart();
int iLineNum = getTokenizer().getLineNumber();
int iColumn = getTokenizer().getTokenColumn();
if( match( t, null, SourceCodeTokenizer.TT_WORD, true ) &&
!Keyword.isKeyword( t._strValue ) &&
match( t, SourceCodeTokenizer.TT_WORD ) )
{
VarStatement varStmt = parseEnumConstantDecl( t._strValue );
varStmt.setNameOffset( t.getTokenStart(), t._strValue );
setLocation( iOffset, iLineNum, iColumn );
popStatement();
processVarStmt( gsClass, varStmt );
bAtLeastOneConst = true;
}
if( match( null, ';' ) )
{
break;
}
} while( match( null, ',' ) );
if( !bAtLeastOneConst )
{
getTokenizer().restoreToMark( state );
}
}
private VarStatement parseEnumConstantDecl( String strIdentifier )
{
VarStatement varStmt = new VarStatement();
ModifierInfo modifiers = new ModifierInfo( Modifier.PUBLIC | Modifier.STATIC | Modifier.FINAL );
varStmt.setModifierInfo( modifiers );
verify( varStmt, getSymbolTable().getSymbol( strIdentifier ) == null, Res.MSG_VARIABLE_ALREADY_DEFINED, strIdentifier );
if( match( null, null, '(', true ) )
{
eatParenthesized( varStmt, Res.MSG_EXPECTING_RIGHTPAREN_FUNCTION_DEF );
if( match( null, null, '{', true ) )
{
eatStatementBlock( varStmt, Res.MSG_EXPECTING_RIGHTBRACE_STMTBLOCK );
}
}
IType type = getGosuClass();
varStmt.setScope( GlobalScope.EXECUTION );
AbstractDynamicSymbol symbol = new DynamicSymbol( getGosuClass(), getSymbolTable(), strIdentifier, type, null );
modifiers.addAll( symbol.getModifierInfo() );
symbol.setModifierInfo( modifiers );
varStmt.setSymbol( symbol );
varStmt.setEnumConstant( true );
getSymbolTable().putSymbol( symbol );
pushStatement( varStmt );
return varStmt;
}
private void processVarStmt( IGosuClassInternal gsClass, VarStatement varStmt )
{
gsClass.getParseInfo().addMemberField(varStmt);
}
public void processFunctionSymbol( DynamicFunctionSymbol dfs, IGosuClassInternal gsClass )
{
getSymbolTable().putSymbol( dfs );
if( dfs.getDisplayName().equals( gsClass.getRelativeName() ) )
{
gsClass.getParseInfo().addConstructorFunction(dfs);
}
else
{
gsClass.getParseInfo().addMemberFunction(dfs);
}
}
void processPropertySymbol( DynamicPropertySymbol dps, ICompilableTypeInternal gsClass )
{
getSymbolTable().putSymbol( dps );
dps.addMemberSymbols( gsClass );
}
private void addOuterMember( ICompilableTypeInternal gsClass )
{
while( gsClass instanceof IBlockClass )
{
// blocks should never be considered part of the outer hierarchy
gsClass = gsClass.getEnclosingType();
}
DynamicFunctionSymbol dfs = new OuterFunctionSymbol( getSymbolTable(), gsClass );
dfs.setClassMember( true );
DynamicPropertySymbol dps = getOrCreateDynamicPropertySymbol( getClassStatement(), gsClass, dfs, true );
processPropertySymbol( dps, gsClass );
}
private void parseEnhancementBodyDecl( IGosuClassInternal gsClass )
{
try
{
IType type = TypeLoaderAccess.instance().getIntrinsicTypeByFullName( gsClass.getName() );
if( gsClass != type )
{
getClassStatement().addParseException( new ParseException( makeFullParserState(), Res.MSG_DUPLICATE_ENHANCEMENT_FOUND, type.getName() ) );
}
}
catch( ClassNotFoundException e )
{
// ignore
}
verify( getClassStatement(), match( null, '{' ), Res.MSG_EXPECTING_OPEN_BRACE_FOR_CLASS_DEF );
for( Object result = parseFunctionDeclForEnhancement( gsClass );
result != null;
result = parseFunctionDeclForEnhancement( gsClass ) )
{
if( !result.equals( Boolean.FALSE ) )
{
popStatement();
if( result instanceof DynamicFunctionSymbol )
{
DynamicFunctionSymbol dfs = (DynamicFunctionSymbol)result;
getSymbolTable().putSymbol( dfs );
gsClass.getParseInfo().addMemberFunction(dfs);
}
else if( result instanceof DynamicPropertySymbol )
{
getSymbolTable().putSymbol( (DynamicPropertySymbol)result );
((DynamicPropertySymbol)result).addMemberSymbols( gsClass );
}
}
}
verify( getClassStatement(), isInnerClass( gsClass ) || match( null, SourceCodeTokenizer.TT_EOF ), Res.MSG_END_OF_STMT );
gsClass.syncGenericAndParameterizedClasses();
gsClass.setDeclarationsCompiled();
gsClass.setInnerDeclarationsCompiled();
}
public List<ParseException> resolveFunctionAndPropertyDecls( ISymbolTable table )
{
for( Object member = parseFunctionOrConstructorOrFieldDeclaration( null );
member != null; member = parseFunctionOrConstructorOrFieldDeclaration( null ) )
{
popStatement();
if( member instanceof DynamicFunctionSymbol )
{
table.putSymbol( (DynamicFunctionSymbol)member );
}
else if( member instanceof DynamicPropertySymbol )
{
table.putSymbol( (DynamicPropertySymbol)member );
}
}
pushStatement( getClassStatement() );
setLocation( _iClassOffset, _iClassLineNum, _iClassColumn );
popStatement();
//noinspection RedundantCast,unchecked
return (List<ParseException>)(List)getClassStatement().getParseExceptions();
}
private Object parseFunctionDeclForEnhancement( IGosuClassInternal gsClass )
{
int[] location = new int[3];
Object rtn = _parseFunctionDeclForEnhancement( gsClass, location );
if( rtn != null && !Boolean.FALSE.equals( rtn ) )
{
setLocation( location[0], location[1], location[2] );
}
return rtn;
}
private Object _parseFunctionDeclForEnhancement( IGosuClassInternal gsClass, int[] location )
{
Token T = new Token();
int iOffset = getTokenizer().getTokenStart();
int iLineNum = getTokenizer().getLineNumber();
int iColumn = getTokenizer().getTokenColumn();
ModifierInfo modifiers;
modifiers = parseUntilMemberKeyword( T, false, location );
if( modifiers.getModifiers() == -1 )
{
return null;
}
if( T._strValue != null && T._strValue.equals( Keyword.KW_function.toString() ) )
{
FunctionStatement fs = new FunctionStatement();
DynamicFunctionSymbol dfs = getOwner().parseFunctionDecl( fs, false, false, modifiers );
fs.setDynamicFunctionSymbol( dfs );
pushStatement( fs );
if( dfs != null )
{
dfs.setClassMember( true );
}
if( verify( getClassStatement(), !Modifier.isAbstract( modifiers.getModifiers() ), Res.MSG_MODIFIER_ABSTRACT_NOT_ALLOWED_HERE ) )
{
if( !Modifier.isNative( modifiers.getModifiers() ) )
{
eatStatementBlock( fs, Res.MSG_EXPECTING_OPEN_BRACE_FOR_FUNCTION_DEF );
}
}
return dfs;
}
else if( T._strValue != null && T._strValue.equals( Keyword.KW_property.toString() ) )
{
boolean bGetter = match( null, Keyword.KW_get );
verify( getClassStatement(), bGetter || match( null, Keyword.KW_set ), Res.MSG_EXPECTING_PROPERTY_GET_OR_SET_MODIFIER );
FunctionStatement fs = new FunctionStatement();
DynamicFunctionSymbol dfs = getOwner().parseFunctionDecl( fs, true, bGetter, modifiers );
fs.setDynamicFunctionSymbol( dfs );
pushStatement( fs );
setLocation( iOffset, iLineNum, iColumn );
popStatement();
if( dfs != null )
{
dfs.setClassMember( true );
}
if( verify( getClassStatement(), !Modifier.isAbstract( modifiers.getModifiers() ), Res.MSG_MODIFIER_ABSTRACT_NOT_ALLOWED_HERE ) )
{
if( !Modifier.isNative( modifiers.getModifiers() ) )
{
eatStatementBlock( fs, Res.MSG_EXPECTING_OPEN_BRACE_FOR_FUNCTION_DEF );
}
}
DynamicPropertySymbol dps = dfs == null ? null : getOrCreateDynamicPropertySymbol( getClassStatement(), gsClass, dfs, bGetter );
PropertyStatement statement = new PropertyStatement( fs, dps );
verifyPropertiesAreSymmetric( bGetter, dfs, dps, statement );
pushStatement( statement );
return dps;
}
else if( T._strValue != null && T._strValue.equals( Keyword.KW_var.toString() ) )
{
return Boolean.FALSE;
}
return null;
}
private void parseClassStatementAsEnhancement( IGosuClassInternal gsClass )
{
//## todo: remove this scope?
IGosuEnhancementInternal enhancement = (IGosuEnhancementInternal)gsClass;
getSymbolTable().pushScope();
try
{
verify( getClassStatement(), match( null, '{' ), Res.MSG_EXPECTING_OPEN_BRACE_FOR_CLASS_DEF );
parseClassMembers( gsClass );
for( Statement stmt = peekStatement(); stmt != null; stmt = peekStatement() )
{
stmt = popStatement();
IType enhancedType = enhancement.getEnhancedType();
if( stmt instanceof FunctionStatement )
{
FunctionStatement func = (FunctionStatement)stmt;
if( func.getDynamicFunctionSymbol() != null && !(enhancedType instanceof ErrorType) )
{
ITypeInfo typeInfo = enhancedType.getTypeInfo();
if( typeInfo != null )
{
IMethodInfo mi = typeInfo instanceof IRelativeTypeInfo
? ((IRelativeTypeInfo)typeInfo).getMethod( enhancement, func.getFunctionName(), func.getDynamicFunctionSymbol().getArgTypes() )
: typeInfo.getMethod( func.getFunctionName(), func.getDynamicFunctionSymbol().getArgTypes() );
if( overridesMethodWithDefaultParams(func, typeInfo) )
{
ParseException parseException = new ParseException( stmt.getLineNum(), 1, stmt.getLocation().getColumn(), stmt.getLocation().getOffset(), stmt.getLocation().getExtent(),
getSymbolTable(), Res.MSG_OVERLOADING_NOT_ALLOWED_WITH_OPTIONAL_PARAMS, mi.getDisplayName(), enhancedType.getRelativeName() );
stmt.addParseException( parseException );
}
else if( (mi != null) && (!featureIsOwnedByEnhancement( enhancement, mi ) || (enhancedType != JavaTypes.OBJECT() && GosuClass.isObjectMethod( mi ))) )
{
ParseException parseException = new ParseException( stmt.getLineNum(), 1, stmt.getLocation().getColumn(), stmt.getLocation().getOffset(), stmt.getLocation().getExtent(),
getSymbolTable(), Res.MSG_CANNOT_OVERRIDE_FUNCTIONS_IN_ENHANCEMENTS, mi.getDisplayName(), enhancedType.getRelativeName() );
stmt.addParseException( parseException );
}
}
}
}
else if( stmt instanceof PropertyStatement )
{
PropertyStatement prop = (PropertyStatement)stmt;
ITypeInfo typeInfo = enhancedType.getTypeInfo();
if( typeInfo != null && !(enhancedType instanceof ErrorType) )
{
IPropertyInfo pi = typeInfo instanceof IRelativeTypeInfo
? ((IRelativeTypeInfo)typeInfo).getProperty( enhancement, prop.getFunctionName() )
: typeInfo.getProperty( prop.getFunctionName() );
if( pi != null && !featureIsOwnedByEnhancement( enhancement, pi ) )
{
ParseException parseException = new ParseException( stmt.getLineNum(), 1, stmt.getLocation().getColumn(), stmt.getLocation().getOffset(), stmt.getLocation().getExtent(),
getSymbolTable(), Res.MSG_CANNOT_OVERRIDE_PROPERTIES_IN_ENHANCEMENTS, pi.getDisplayName(), enhancedType.getRelativeName() );
stmt.addParseException( parseException );
}
}
}
else if( stmt instanceof NoOpStatement ||
stmt instanceof NamespaceStatement ||
stmt instanceof UsesStatement )
{
//ignore
}
else
{
ParseException parseException = new ParseException( stmt.getLineNum(), 1, stmt.getLocation().getColumn(), stmt.getLocation().getOffset(), stmt.getLocation().getExtent(),
getSymbolTable(), Res.MSG_ENHANCEMENT_DOES_NOT_ACCEPT_THIS_STATEMENT );
stmt.addParseException( parseException );
}
}
verify( getClassStatement(), match( null, '}' ), Res.MSG_EXPECTING_CLOSE_BRACE_FOR_CLASS_DEF );
}
finally
{
getSymbolTable().popScope();
}
}
private boolean overridesMethodWithDefaultParams(FunctionStatement func, ITypeInfo typeInfo) {
if( !(typeInfo instanceof IRelativeTypeInfo) )
{
return false;
}
IRelativeTypeInfo rti = (IRelativeTypeInfo) typeInfo;
for( IMethodInfo mi : rti.getMethods( func.getGosuClass() ) )
{
if( mi.getDisplayName().equals( func.getFunctionName() ) && mi instanceof GosuMethodInfo && !featureIsOwnedByEnhancement( func.getGosuClass(), mi ) )
{
final ReducedDynamicFunctionSymbol dfs0 = ((GosuMethodInfo) mi).getDfs();
final DynamicFunctionSymbol dfs1 = func.getDynamicFunctionSymbol();
return dfs0 != null && dfs1 != null && (((IInvocableType) dfs0.getType()).hasOptionalParams() || dfs1.hasOptionalParameters());
}
}
return false;
}
private boolean featureIsOwnedByEnhancement( IGosuClass enhancement, IFeatureInfo iMethodInfo )
{
if( !(enhancement instanceof IGosuEnhancementInternal) )
{
return false;
}
IType ownerType = iMethodInfo.getOwnersType();
if( ownerType != null && ownerType.isParameterizedType() )
{
ownerType = ownerType.getGenericType();
}
IType enhancementType = enhancement;
if( enhancementType != null && enhancementType.isParameterizedType() )
{
enhancementType = enhancementType.getGenericType();
}
if( enhancementType instanceof IGosuEnhancementInternal &&
ownerType instanceof IGosuEnhancementInternal )
{
return GosuObjectUtil.equals( enhancementType.getName(), ownerType.getName() );
}
else
{
return GosuObjectUtil.equals( enhancementType, ownerType );
}
}
String parseHeader( IGosuClassInternal gsClass, boolean bParseEnhancementOnly, boolean bIsAnonymous, boolean bResolveUsesTypes )
{
boolean bPushedScope = pushScopeIfNeeded( gsClass );
if( gsClass.isHeaderCompiled() )
{
((CompilationState)gsClass.getCompilationState()).setReparsingHeader( true );
}
else
{
gsClass.setCompilingHeader( true );
}
getTokenizer().pushOffsetMarker( this );
gsClass.createNewParseInfo();
setClassStatement( gsClass.getParseInfo().getClassStatement() );
ScriptPartId scriptPartId = new ScriptPartId( gsClass, null );
getOwner().pushScriptPart( scriptPartId );
GosuClassCompilingStack.pushCompilingType( gsClass );
try
{
setTokenizerToClassStart();
if( match( null, SourceCodeTokenizer.TT_EOF ) )
{
if( gsClass instanceof IGosuProgram )
{
// Let empty *program* source parse
//## todo: cache and reuse empty program class
gsClass.setSuperType( JavaTypes.OBJECT() );
}
else if( getClassStatement() != null && getClassStatement().getClassFileStatement() != null )
{
getClassStatement().getClassFileStatement().addParseException( new ParseException( makeFullParserState(), Res.MSG_NO_SOURCE_FOUND ) );
}
return null;
}
getOwner().checkInstruction( true );
if( gsClass instanceof IGosuProgram )
{
getOwner().parseClasspathStatements();
getOwner().parseTypeLoaderStatements();
}
getOwner().checkInstruction( true );
int iOffset = getTokenizer().getTokenStart();
int iLineNum = getTokenizer().getLineNumber();
int iColumn = getTokenizer().getTokenColumn();
Token T = new Token();
if( match( null, Keyword.KW_package ) )
{
getOwner().parseNamespaceStatement( T );
setLocation( iOffset, iLineNum, iColumn );
popStatement();
}
else if( gsClass instanceof IGosuProgram )
{
ISourceFileHandle sfh = gsClass.getSourceFileHandle();
boolean bEval = sfh instanceof StringSourceFileHandle;
if( bEval )
{
ITypeUsesMap typeUsesMap = ((StringSourceFileHandle)sfh).getTypeUsesMap();
if( typeUsesMap != null )
{
getOwner().setTypeUsesMap( typeUsesMap );
}
}
if( gsClass.isAnonymous() )
{
// Anonymous implies Eval program...
gsClass.setEnclosingType( TypeSystem.getByFullNameIfValid( sfh.getParentType() ) );
IType enclosingType = gsClass.getEnclosingTypeReference();
getOwner().setNamespace(enclosingType.getNamespace());
Map<String, ITypeVariableDefinition> capturedTypeVars = bEval ? ((StringSourceFileHandle)sfh).getCapturedTypeVars() : null;
if( capturedTypeVars != null )
{
getOwner().getTypeVariables().putAll( capturedTypeVars );
}
}
else
{
String strNamespace = getGosuClass().getNamespace();
getOwner().setNamespace( strNamespace != null && !strNamespace.isEmpty() ? strNamespace : IGosuProgram.PACKAGE );
}
}
else if( !isInnerClass( gsClass ) )
{
getOwner().setNamespace( "" );
}
getOwner().checkInstruction( true );
getOwner().parseUsesStatementList( bResolveUsesTypes, T );
if( gsClass.getEnclosingType() == null )
{
// Inner classes start parsing right at the class-stmt, so they must
// get at the uses map from the top-level enclosing class
gsClass.setTypeUsesMap(getOwner().getTypeUsesMap());
}
ClassType classType;
if( gsClass.isAnonymous() && !(gsClass instanceof IGosuProgram) )
{
try
{
classType = parseAnonymousClassHeader( gsClass );
}
catch( InnerClassNotFoundException e )
{
classType = ClassType.Class;
}
_iClassOffset = getTokenizer().getTokenStart();
_iClassLineNum = getTokenizer().getLineNumber();
_iClassColumn = getTokenizer().getTokenColumn();
}
else if( gsClass instanceof IGosuProgram )
{
gsClass.setModifierInfo(new ModifierInfo(Modifier.PUBLIC | Modifier.FINAL));
if( gsClass.isAnonymous() ) // generated 'eval' program
{
IParseTree parseTree = ((IGosuProgram)gsClass).getEnclosingEvalExpression().getLocation();
IFunctionStatement fs = (parseTree == null ? null : parseTree.getEnclosingFunctionStatement());
if( fs != null && fs.getDynamicFunctionSymbol().isStatic() )
{
((ModifierInfo)gsClass.getModifierInfo()).addModifiers( Modifier.STATIC );
}
}
classType = ClassType.Class;
}
else
{
getOwner().checkInstruction( true );
_iClassOffset = getTokenizer().getTokenStart();
_iClassLineNum = getTokenizer().getLineNumber();
_iClassColumn = getTokenizer().getTokenColumn();
if( !bIsAnonymous )
{
classType = parseClassType( gsClass, true );
if( classType == ClassType.Interface || classType == ClassType.Structure )
{
((ModifierInfo)gsClass.getModifierInfo()).addModifiers( Modifier.ABSTRACT );
}
else if( classType == ClassType.Enum )
{
((ModifierInfo)gsClass.getModifierInfo()).addModifiers( Modifier.FINAL );
}
}
else
{
classType = parseClassTypeForHeader( gsClass );
}
}
if( classType == null )
{
if( bParseEnhancementOnly )
{
return null;
}
verify( getClassStatement(), false, Res.MSG_EXPECTING_NAME_CLASS_DEF );
}
if( classType == ClassType.Enhancement )
{
if( gsClass instanceof IGosuEnhancementInternal )
{
IGosuEnhancementInternal scriptEnhancement = (IGosuEnhancementInternal)gsClass;
scriptEnhancement.setFoundCorrectHeader();
return parseEnhancementHeaderSuffix( scriptEnhancement, T );
}
else
{
getClassStatement().addParseException( new ParseException( makeFullParserState(), Res.MSG_MUST_BE_DEFINED_AS_CLASS ) );
return null;
}
}
else if( classType != null && !bParseEnhancementOnly )
{
if( classType == ClassType.Enum )
{
gsClass.setEnum();
}
return parseClassOrInterfaceHeaderSuffix( gsClass, classType, T, bResolveUsesTypes );
}
else
{
return null;
}
}
finally
{
boolean bHeaderCompiled;
try
{
bHeaderCompiled = gsClass.isHeaderCompiled();
if( !bHeaderCompiled )
{
parseInnerClassHeaders( gsClass, bResolveUsesTypes );
}
}
finally
{
GosuClassCompilingStack.popCompilingType();
}
getOwner().popScriptPart( scriptPartId );
((CompilationState)gsClass.getCompilationState()).setReparsingHeader( false );
gsClass.setCompilingHeader( false );
gsClass.setHeaderCompiled();
popScopeIfNeeded( bPushedScope, gsClass );
getTokenizer().popOffsetMarker( this );
if( !bHeaderCompiled )
{
removeTypeVarsFromParserMap( gsClass );
}
}
}
private void removeTypeVarsFromParserMap( IGosuClassInternal gsClass )
{
for( IGenericTypeVariable gtv : gsClass.getGenericTypeVariables() )
{
ITypeVariableDefinition typeVarDef = gtv.getTypeVariableDefinition();
Map<String, ITypeVariableDefinition> typeVarMap = getOwner().getTypeVariables();
if( typeVarMap.containsValue( typeVarDef ) )
{
typeVarMap.remove( typeVarDef.getName() );
}
}
}
private boolean pushScopeIfNeeded( final IGosuClassInternal gsClass )
{
ISymbolTable compilingClass = CompiledGosuClassSymbolTable.instance().getSymbolTableForCompilingClass( gsClass );
if( compilingClass != null )
{
return false;
}
// *barf*
if( gsClass.getParser() != null )
{
CompiledGosuClassSymbolTable.instance().pushCompileTimeSymbolTable( gsClass, gsClass.getParser().getSymbolTable() );
}
else
{
CompiledGosuClassSymbolTable.instance().pushCompileTimeSymbolTable( gsClass );
}
getSymbolTable().pushIsolatedScope(new GosuClassTransparentActivationContext(gsClass, false));
return true;
}
private void popScopeIfNeeded( boolean bPop, IGosuClass gsClass )
{
if( bPop )
{
getSymbolTable().popScope();
CompiledGosuClassSymbolTable.instance().popCompileTimeSymbolTable( gsClass );
}
}
private void setTokenizerToClassStart()
{
if( isInnerClass( getGosuClass() ) )
{
getTokenizer().reset();
}
if( !getTokenizer().isPositioned() )
{
getTokenizer().nextToken();
}
}
private ClassType parseAnonymousClassHeader( IGosuClassInternal gsClass )
{
ClassType classType = ClassType.Class;
IType instanceClass;
ParsedElement elem;
if( match( null, null, '(', true ) )
{
// The type name is inferred in this case e.g., var obj : Object = new() {}
instanceClass = gsClass.getSupertype();
elem = getClassStatement();
}
else if( !getOwner().parseTypeLiteral() )
{
throw new InnerClassNotFoundException();
}
else
{
elem = popExpression();
instanceClass = ((TypeLiteral)elem).getType().getType();
}
eatParenthesized( elem, Res.MSG_EXPECTING_FUNCTION_CLOSE );
//getLocationsList().remove( superTypeLiteral.getLocation() ); // rely on the new-expr to keep the type literal *it* parses
instanceClass = TypeLord.makeDefaultParameterizedType( instanceClass );
if( instanceClass.isInterface() )
{
gsClass.addInterface( instanceClass );
}
else
{
gsClass.setSuperType( instanceClass );
if( instanceClass.isEnum() )
{
gsClass.setEnum();
}
}
return classType;
}
private boolean goToPosition( int iOffset )
{
try
{
getTokenizer().goToPosition( iOffset );
return true;
}
catch( IOException e )
{
//noinspection ThrowableResultOfMethodCallIgnored
getClassStatement().addParseException( ParseException.wrap( e, makeFullParserState() ) );
}
return false;
}
private ClassType parseClassTypeForHeader( IGosuClassInternal gsClass )
{
while( true )
{
if( match( null, SourceCodeTokenizer.TT_EOF ) )
{
return null;
}
ClassType classType = parseClassType( gsClass, !gsClass.isDeclarationsCompiled() );
if( classType != null )
{
return classType;
}
getTokenizer().nextToken();
}
}
private ClassType parseClassType( IGosuClassInternal gsClass, boolean bSetModifiers )
{
ModifierInfo modifiers = parseModifiers( !bSetModifiers );
if( !Modifier.isInternal( modifiers.getModifiers() )
&& !Modifier.isProtected( modifiers.getModifiers() )
&& !Modifier.isPrivate( modifiers.getModifiers() ) )
{
modifiers.addModifiers( Modifier.PUBLIC );
}
ClassType classType = null;
if( match( null, Keyword.KW_enhancement ) )
{
classType = ClassType.Enhancement;
if( bSetModifiers )
{
verify( getClassStatement(), !Modifier.isPrivate( modifiers.getModifiers() ), Res.MSG_ILLEGAL_USE_OF_MODIFIER, Keyword.KW_private, classType.name() );
verify( getClassStatement(), !Modifier.isProtected( modifiers.getModifiers() ), Res.MSG_ILLEGAL_USE_OF_MODIFIER, Keyword.KW_protected, classType.name() );
verify( getClassStatement(), !Modifier.isInternal( modifiers.getModifiers() ), Res.MSG_ILLEGAL_USE_OF_MODIFIER, Keyword.KW_internal, classType.name() );
verify( getClassStatement(), !Modifier.isFinal( modifiers.getModifiers() ), Res.MSG_ILLEGAL_USE_OF_MODIFIER, Keyword.KW_final, classType.name() );
verify( getClassStatement(), !Modifier.isAbstract( modifiers.getModifiers() ), Res.MSG_ILLEGAL_USE_OF_MODIFIER, Keyword.KW_abstract, classType.name() );
verify( getClassStatement(), !Modifier.isTransient( modifiers.getModifiers() ), Res.MSG_ILLEGAL_USE_OF_MODIFIER, Keyword.KW_transient, classType.name() );
verifyNoAbstractHideOverrideStaticModifierDefined( getClassStatement(), false, modifiers.getModifiers(), Keyword.KW_enhancement );
gsClass.setModifierInfo(modifiers);
}
}
else if( match( null, Keyword.KW_interface ) )
{
classType = ClassType.Interface;
if( bSetModifiers )
{
verify( getClassStatement(), !Modifier.isHide( modifiers.getModifiers() ), Res.MSG_ILLEGAL_USE_OF_MODIFIER, Keyword.KW_hide, classType.name() );
verify( getClassStatement(), !Modifier.isOverride( modifiers.getModifiers() ), Res.MSG_ILLEGAL_USE_OF_MODIFIER, Keyword.KW_override, classType.name() );
verify( getClassStatement(), !Modifier.isFinal( modifiers.getModifiers() ), Res.MSG_ILLEGAL_USE_OF_MODIFIER, Keyword.KW_final, classType.name() );
verify( getClassStatement(), !Modifier.isTransient( modifiers.getModifiers() ), Res.MSG_ILLEGAL_USE_OF_MODIFIER, Keyword.KW_transient, classType.name() );
gsClass.setModifierInfo(modifiers);
}
}
else if( match( null, Keyword.KW_structure ) )
{
classType = ClassType.Structure;
if( bSetModifiers )
{
verify( getClassStatement(), !Modifier.isHide( modifiers.getModifiers() ), Res.MSG_ILLEGAL_USE_OF_MODIFIER, Keyword.KW_hide, classType.name() );
verify( getClassStatement(), !Modifier.isOverride( modifiers.getModifiers() ), Res.MSG_ILLEGAL_USE_OF_MODIFIER, Keyword.KW_override, classType.name() );
verify( getClassStatement(), !Modifier.isFinal( modifiers.getModifiers() ), Res.MSG_ILLEGAL_USE_OF_MODIFIER, Keyword.KW_final, classType.name() );
verify( getClassStatement(), !Modifier.isTransient( modifiers.getModifiers() ), Res.MSG_ILLEGAL_USE_OF_MODIFIER, Keyword.KW_transient, classType.name() );
gsClass.setModifierInfo(modifiers);
}
}
else if( match( null, Keyword.KW_class ) )
{
classType = ClassType.Class;
if( bSetModifiers )
{
verify( getClassStatement(), !Modifier.isHide( modifiers.getModifiers() ), Res.MSG_ILLEGAL_USE_OF_MODIFIER, Keyword.KW_hide, classType.name() );
verify( getClassStatement(), !Modifier.isOverride( modifiers.getModifiers() ), Res.MSG_ILLEGAL_USE_OF_MODIFIER, Keyword.KW_override, classType.name() );
verify( getClassStatement(), !Modifier.isTransient( modifiers.getModifiers() ), Res.MSG_ILLEGAL_USE_OF_MODIFIER, Keyword.KW_transient, classType.name() );
gsClass.setModifierInfo(modifiers);
}
}
else if( match( null, Keyword.KW_enum ) )
{
classType = ClassType.Enum;
if( bSetModifiers )
{
verifyNoAbstractHideOverrideModifierDefined( getClassStatement(), false, modifiers.getModifiers(), Keyword.KW_final );
verify( getClassStatement(), !Modifier.isTransient( modifiers.getModifiers() ), Res.MSG_ILLEGAL_USE_OF_MODIFIER, Keyword.KW_transient, classType.name() );
gsClass.setModifierInfo(modifiers);
}
}
if( gsClass.shouldFullyCompileAnnotations() )
{
verifyModifiers( getClassStatement(), modifiers, UsageTarget.TypeTarget );
}
gsClass.setFullDescription( modifiers.getDescription() );
if( bSetModifiers && classType != null && gsClass.getEnclosingType() == null )
{
verify( getClassStatement(), !Modifier.isPrivate( modifiers.getModifiers() ), Res.MSG_ILLEGAL_USE_OF_MODIFIER, Keyword.KW_private, classType.name() );
verify( getClassStatement(), !Modifier.isProtected( modifiers.getModifiers() ), Res.MSG_ILLEGAL_USE_OF_MODIFIER, Keyword.KW_protected, classType.name() );
verify( getClassStatement(), !Modifier.isStatic( modifiers.getModifiers() ), Res.MSG_ILLEGAL_USE_OF_MODIFIER, Keyword.KW_static, classType.name() );
}
return classType;
}
private String parseClassOrInterfaceHeaderSuffix( IGosuClassInternal gsClass, ClassType classType, Token t, boolean bResolveTypes )
{
String strClassName;
IGosuClassInternal gosuObjectInterface = getGosuObjectInterface();
if (gosuObjectInterface == null) {
return gsClass.getName();
}
if( gsClass instanceof IGosuProgram )
{
gsClass.addInterface(gosuObjectInterface);
if( !gsClass.isAnonymous() )
{
// if( gsClass.isHeaderCompiled() && !getOwner().isParsingProgram() )
// {
// makeSyntheticClassDeclaration( gsClass.getRelativeName(), true );
// }
IType type = parseEnhancedOrImplementedType( gsClass, false, Collections.<IType>emptyList() );
gsClass.setSuperType( type );
}
strClassName = gsClass.getName();
}
else if( gsClass.isAnonymous() )
{
gsClass.addInterface(gosuObjectInterface);
strClassName = gsClass.getName();
if( gsClass.isHeaderCompiled() )
{
ClassDeclaration classDeclaration = new ClassDeclaration( strClassName );
pushExpression( classDeclaration );
SourceCodeTokenizer tokenizer = getOwner().getTokenizer();
setLocation( tokenizer.getTokenStart(), tokenizer.getLineNumber(), tokenizer.getTokenColumn(), true, true );
popExpression();
getClassStatement().setClassDeclaration( classDeclaration );
// makeSyntheticClassDeclaration( strClassName, false );
}
}
else
{
boolean bStructure = classType.equals( ClassType.Structure );
boolean bInterface = bStructure || classType.equals( ClassType.Interface );
gsClass.setInterface( bInterface || bStructure );
gsClass.setStructure( bStructure );
int iOffset = getTokenizer().getTokenStart();
int iLineNum = getTokenizer().getLineNumber();
int iColumn = getTokenizer().getTokenColumn();
verify( getClassStatement(), match( t, SourceCodeTokenizer.TT_WORD ), Res.MSG_EXPECTING_NAME_CLASS_DEF );
strClassName = t._strValue;
String strNamespace;
if( isTopLevelClass( getGosuClass() ) )
{
strNamespace = getOwner().getNamespace();
}
else
{
strNamespace = getGosuClass().getEnclosingType().getName();
}
strClassName = GosuStringUtil.isEmpty(strNamespace)
? strClassName
: strNamespace + '.' + strClassName;
if( gsClass.getEnclosingTypeReference() == null && strClassName != null && !strClassName.equals( gsClass.getName() ) )
{
verify( getClassStatement(), false, Res.MSG_WRONG_CLASSNAME, strClassName, gsClass.getName() );
}
if( strClassName != null && gsClass.isHeaderCompiled() )
{
ClassDeclaration classDeclaration = new ClassDeclaration( strClassName );
pushExpression( classDeclaration );
setLocation( iOffset, iLineNum, iColumn );
popExpression();
getClassStatement().setClassDeclaration( classDeclaration );
}
List<ITypeVariableDefinitionExpression> typeVarLiteralList = getOwner().parseTypeVariableDefs( getClassStatement(), false, getDeclTypeVars() );
gsClass.setGenericTypeVariables((List)typeVarLiteralList);
if( gsClass.isEnum() )
{
verify( getClassStatement(), typeVarLiteralList.isEmpty(), Res.MSG_ENUM_MAY_NOT_HAVE_TYPEPARAM );
}
iOffset = getTokenizer().getTokenStart();
iLineNum = getTokenizer().getLineNumber();
iColumn = getTokenizer().getTokenColumn();
if( !bInterface && (match( null, Keyword.KW_extends ) || gsClass.isEnum()) )
{
IType superType = parseEnhancedOrImplementedType( gsClass, true, Collections.<IType>emptyList() );
if( superType instanceof IGosuClassInternal )
{
if( bResolveTypes )
{
((IGosuClassInternal)superType).compileDeclarationsIfNeeded();
}
}
gsClass.setSuperType( superType );
if( gsClass.getCompilationState().isCompilingDeclarations() &&
gsClass.isGenericType() )
{
verify( getClassStatement(), !JavaTypes.THROWABLE().isAssignableFrom( superType ) , Res.MSG_INVALID_GENERIC_EXCEPTION );
}
SuperTypeClause extendsClause = new SuperTypeClause( superType );
pushExpression( extendsClause );
setLocation( iOffset, iLineNum, iColumn );
popExpression();
iOffset = getTokenizer().getTokenStart();
iLineNum = getTokenizer().getLineNumber();
iColumn = getTokenizer().getTokenColumn();
}
boolean hasImplements = false;
if( (bInterface && match( null, Keyword.KW_extends )) ||
(hasImplements = match( null, Keyword.KW_implements )) )
{
if( bInterface )
{
verify( getClassStatement(), !hasImplements, Res.MSG_NO_IMPLEMENTS_ALLOWED );
}
List<IType> interfaces = new ArrayList<IType>();
do
{
IType type = parseEnhancedOrImplementedType( gsClass, bInterface, interfaces );
gsClass.addInterface( type );
interfaces.add( type );
} while( match( null, ',' ) );
InterfacesClause interfacesClause = new InterfacesClause( gsClass, interfaces.toArray( new IType[interfaces.size()] ) );
pushExpression( interfacesClause );
setLocation( iOffset, iLineNum, iColumn );
popExpression();
}
if( classType == ClassType.Class || classType == ClassType.Interface || classType == ClassType.Structure )
{
IGosuClassInternal gsObjectInterace = gosuObjectInterface;
if( (!gsClass.isInterface() || !interfaceExtendsGosuObject( gsClass, gsObjectInterace )) && !gsClass.getName().startsWith( IGosuClass.PROXY_PREFIX ) )
{
gsClass.addInterface( gsObjectInterace );
}
}
else if( classType == ClassType.Enum )
{
gsClass.addInterface(gosuObjectInterface);
}
}
if( (isTopLevelClass( gsClass ) ||
gsClass instanceof IGosuProgram ||
// Anonymous classes can have inner classes
gsClass.isAnonymous()) &&
!gsClass.isHeaderCompiled() )
{
// Recursively *load* (no parsing) all nested inner types from the top-level class file
int state = getTokenizer().mark();
loadAllNestedInnerClasses( gsClass );
getTokenizer().restoreToMark( state );
}
return strClassName;
}
private boolean interfaceExtendsGosuObject( IGosuClassInternal gsClass, IGosuClassInternal gsObjectInterace )
{
if( gsClass == gsObjectInterace )
{
return true;
}
for( IType iface: gsClass.getInterfaces() )
{
if( iface instanceof IGosuClass )
{
return true;
}
}
return false;
}
private List<TypeVariableDefinitionImpl> getDeclTypeVars()
{
IGosuClass gsClass = getGosuClass();
if( !gsClass.isDeclarationsCompiled() )
{
return Collections.emptyList();
}
IGenericTypeVariable[] typeVars = gsClass.getGenericTypeVariables();
if( typeVars == null )
{
return Collections.emptyList();
}
List<TypeVariableDefinitionImpl> result = new ArrayList<TypeVariableDefinitionImpl>( typeVars.length );
for( IGenericTypeVariable typeVar : typeVars )
{
result.add( (TypeVariableDefinitionImpl)typeVar.getTypeVariableDefinition() );
}
return result;
}
private void makeSyntheticClassDeclaration( String strClassName, boolean bProgram )
{
ClassDeclaration classDeclaration = new ClassDeclaration( strClassName );
pushExpression( classDeclaration );
SourceCodeTokenizer tokenizer = getOwner().getTokenizer();
setLocation( bProgram ? 0 : tokenizer.getTokenStart(), tokenizer.getLineNumber(), bProgram ? 0 : tokenizer.getTokenColumn(), true, true );
popExpression();
getClassStatement().setClassDeclaration( classDeclaration );
}
private void parseInnerClassHeaders( IGosuClassInternal gsClass, boolean bResolveTypes )
{
Map<CharSequence, ? extends IGosuClass> innerClassesByName = gsClass.getKnownInnerClassesWithoutCompiling();
if( innerClassesByName.isEmpty() )
{
return;
}
int state = getTokenizer().mark();
int iLocationsSize = getLocationsList().size();
try
{
for( CharSequence name : innerClassesByName.keySet() )
{
IGosuClassInternal innerClass = (IGosuClassInternal)innerClassesByName.get( name );
if( !(innerClass instanceof IBlockClass) )
{
innerClass.createNewParseInfo();
new GosuClassParser( getOwner(), innerClass ).parseHeader( innerClass, false, false, bResolveTypes );
}
}
}
finally
{
while( getLocationsList().size() > iLocationsSize )
{
getLocationsList().remove( getLocationsList().size()-1 );
}
getTokenizer().restoreToMark( state );
}
}
private void loadAllNestedInnerClasses( IGosuClassInternal gsClass )
{
Token T = new Token();
if( !(gsClass instanceof IGosuProgram) )
{
advanceToClassBodyStart();
}
ModifierInfo modifiers;
while( true )
{
int[] location = new int[3];
int[] mark = new int[]{-1};
modifiers = parseUntilMemberKeyword( T, true, -1, location, mark );
if( modifiers.getModifiers() == -1 )
{
if( getTokenizer().isEOF() )
{
break;
}
if( !isInnerClassesEmpty() ) // can be empty e.g., errors with unmatching braces
{
IGosuClassInternal innerClass = popInnerClass( getCurrentInnerClass() );
innerClass.getSourceFileHandle().setEnd( location[0] );
}
else if( gsClass.isAnonymous() )
{
break;
}
}
else
{
ClassType classType = getClassType( T._strValue );
if( classType != null )
{
IGosuClassInternal innerClass = loadNextInnerClass( gsClass, classType );
if( innerClass == null )
{
break;
}
innerClass.getSourceFileHandle().setOffset( location[0] );
((InnerClassFileSystemSourceFileHandle)innerClass.getSourceFileHandle()).setMark( mark[0] );
pushInnerClass( innerClass );
}
}
}
}
private ClassType getClassType( String strValue )
{
return
Keyword.KW_class.toString().equals( strValue )
? ClassType.Class
: Keyword.KW_interface.equals( strValue )
? ClassType.Interface
: Keyword.KW_structure.equals( strValue )
? ClassType.Structure
: Keyword.KW_enum.toString().equals( strValue )
? ClassType.Enum
: null;
}
private void advanceToClassBodyStart()
{
while( true )
{
if( match( null, '{' ) )
{
break;
}
if( match( null, SourceCodeTokenizer.TT_EOF ) )
{
break;
}
getTokenizer().nextToken();
}
}
private void advanceToClassBodyEnd()
{
int iEnd = getGosuClass().getSourceFileHandle().getEnd();
if( iEnd <= 0 )
{
//assert isTopLevelClass( getGosuClass() ) || isEvalClass() : "Inner class does not have an 'end' marker";
return;
}
try
{
//## perf: this is very slow, maybe use a tokenizer mark instead
getTokenizer().goToPosition( iEnd );
}
catch( IOException e )
{
throw new RuntimeException( e );
}
verify( getClassStatement(), match( null, '}' ), Res.MSG_EXPECTING_CLOSE_BRACE_FOR_CLASS_DEF );
}
private IGosuClassInternal loadNextInnerClass( IGosuClassInternal gsClass, ClassType classType )
{
Token T = new Token();
IGosuClassInternal enclosingGsClass = getGosuClass();
if( verify( getClassStatement(), match( T, SourceCodeTokenizer.TT_WORD ), Res.MSG_EXPECTING_NAME_CLASS_DEF ) )
{
enclosingGsClass = getCurrentInnerClass() != null ? getCurrentInnerClass() : enclosingGsClass;
String strInnerClass = T._strValue;
IGosuClassInternal innerGsClass;
innerGsClass = (IGosuClassInternal)enclosingGsClass.getKnownInnerClassesWithoutCompiling().get( strInnerClass );
if( innerGsClass != null )
{
// Duplicate inner class name
getClassStatement().addParseException( new ParseException( makeFullParserState(), Res.MSG_DUPLICATE_CLASS_FOUND, strInnerClass ) );
strInnerClass = strInnerClass + "_duplicate_" + nextIndexOfErrantDuplicateInnerClass( enclosingGsClass, innerGsClass );
}
innerGsClass = (IGosuClassInternal)gsClass.getTypeLoader().makeNewClass(
new InnerClassFileSystemSourceFileHandle( classType, enclosingGsClass.getName(), strInnerClass, gsClass.isTestClass() ) );
innerGsClass.setEnclosingType( enclosingGsClass );
innerGsClass.setNamespace( enclosingGsClass.getNamespace() );
enclosingGsClass.addInnerClass( innerGsClass );
advanceToClassBodyStart();
return innerGsClass;
}
return null;
}
public int nextIndexOfErrantDuplicateInnerClass( IGosuClassInternal enclosingGsClass, IGosuClassInternal innerClass )
{
int iMax = -1;
String strName = innerClass.getRelativeName() + "_duplicate_";
while( true )
{
IType existingInnerClass = enclosingGsClass.getKnownInnerClassesWithoutCompiling().get( strName + ++iMax );
if( existingInnerClass == null )
{
return iMax;
}
}
}
private IGosuClassInternal getGosuObjectInterface()
{
return IGosuClassInternal.Util.getGosuClassFrom( JavaTypes.IGOSU_OBJECT() );
}
private String parseEnhancementHeaderSuffix( IGosuEnhancementInternal gsClass, Token t )
{
int iOffset = getTokenizer().getTokenStart();
int iLineNum = getTokenizer().getLineNumber();
int iColumn = getTokenizer().getTokenColumn();
verify( getClassStatement(), match( t, SourceCodeTokenizer.TT_WORD ), Res.MSG_EXPECTING_NAME_CLASS_DEF );
String strClassName = t._strValue;
strClassName = GosuStringUtil.isEmpty(getOwner().getNamespace())
? strClassName
: getOwner().getNamespace() + '.' + strClassName;
ClassDeclaration classDeclaration = new ClassDeclaration( strClassName );
pushExpression( classDeclaration );
setLocation( iOffset, iLineNum, iColumn );
popExpression();
getClassStatement().setClassDeclaration( classDeclaration );
if( gsClass.getEnclosingTypeReference() == null && !strClassName.equals( gsClass.getName() ) )
{
verify( getClassStatement(), false, Res.MSG_WRONG_CLASSNAME, strClassName, gsClass.getName() );
}
List<ITypeVariableDefinitionExpression> typeVarLiteralList = getOwner().parseTypeVariableDefs( getClassStatement(), false, getDeclTypeVars() );
gsClass.setGenericTypeVariables((List)typeVarLiteralList);
verify( getClassStatement(), match( null, ":", SourceCodeTokenizer.TT_OPERATOR ), Res.MSG_EXPECTING_COLON_ENHANCEMENT );
IType enhancedType = parseEnhancedOrImplementedType( gsClass, true, Collections.<IType>emptyList() );
if( !(enhancedType instanceof ErrorType ||
enhancedType instanceof IEnhanceableType) )
{
verify( getClassStatement(), false, Res.MSG_NOT_AN_ENHANCEABLE_TYPE, enhancedType.getName() );
}
gsClass.setEnhancedType( enhancedType );
ensureEnhancedTypeUsesTypeVarsOfEnhancement( typeVarLiteralList, enhancedType );
return strClassName;
}
private void ensureEnhancedTypeUsesTypeVarsOfEnhancement( List<ITypeVariableDefinitionExpression> typeVarLiteralList, IType enhancedType )
{
if( typeVarLiteralList.isEmpty() )
{
return;
}
for( ITypeVariableDefinitionExpression expr: typeVarLiteralList )
{
boolean bReferencedByOtherTypeVar = false;
for( ITypeVariableDefinitionExpression expr2: typeVarLiteralList )
{
if( expr2 != expr )
{
if( hasTypeVar( expr2.getTypeVarDef().getBoundingType(), expr.getTypeVarDef().getType() ) )
{
bReferencedByOtherTypeVar = true;
break;
}
}
}
verify( getClassStatement(), bReferencedByOtherTypeVar || hasTypeVar( enhancedType, expr.getTypeVarDef().getType() ), Res.MSG_ENHANCED_TYPE_MUST_USE_ENHANCEMENT_TYPEVARS );
}
}
private boolean hasTypeVar( IType type, ITypeVariableType typeVar )
{
if( type == null )
{
return false;
}
if( type.isArray() )
{
type = TypeLord.getCoreType( type );
}
if( type == null || type.equals( typeVar ) )
{
return true;
}
if( type.isParameterizedType() )
{
for( IType typeParam: type.getTypeParameters() )
{
if( hasTypeVar( typeParam, typeVar ) )
{
return true;
}
}
}
return false;
}
private IType parseEnhancedOrImplementedType( IGosuClassInternal gsClass, boolean bExtended, List<IType> interfaces )
{
IType extendedType = null;
TypeLiteral extendedTypeExpr = null;
if( gsClass instanceof IGosuProgram )
{
extendedType = gsClass.getSuperClass() != null ? gsClass.getSuperClass() : JavaTypes.OBJECT();
}
else if( !gsClass.isEnum() || !bExtended )
{
getOwner().parseTypeLiteral( !(gsClass instanceof IGosuEnhancementInternal) && (gsClass.isInterface() || !bExtended) );
extendedTypeExpr = (TypeLiteral)popExpression();
extendedType = extendedTypeExpr.getType().getType();
if( !verify( extendedTypeExpr, !extendedType.isCompoundType(), Res.MSG_COMPOUND_TYPE_NOT_ALLOWED_HERE ) )
{
extendedType = ErrorType.getInstance();
}
if( !(extendedType instanceof ErrorType) )
{
if( !(gsClass instanceof IGosuEnhancementInternal) )
{
if( gsClass.isInterface() )
{
verify( extendedTypeExpr, extendedType.isInterface(), Res.MSG_INTERFACE_CANNOT_EXTEND_CLASS );
}
else if( bExtended )
{
if( verify( extendedTypeExpr, !extendedType.isInterface(), Res.MSG_CLASS_CANNOT_EXTEND_INTERFACE ) )
{
verify( extendedTypeExpr, !gsClass.isEnum(), Res.MSG_ENUM_CANNOT_EXTEND_CLASS );
verify( extendedTypeExpr, extendedType != JavaTypes.OBJECT(), Res.MSG_SUBCLASS_OBJECT, gsClass.getRelativeName() );
}
}
else
{
verify( extendedTypeExpr, extendedType.isInterface(), Res.MSG_CLASS_CANNOT_IMPLEMENT_CLASS );
}
verify( extendedTypeExpr, !extendedType.isPrimitive(), Res.MSG_CANNOT_EXTEND_PRIMITIVE_TYPE );
verify( extendedTypeExpr, !extendedType.isFinal(), Res.MSG_CANNOT_EXTEND_FINAL_TYPE, extendedType.getName() );
verify( extendedTypeExpr, !interfaces.contains( extendedType ), Res.MSG_DUPLICATE_CLASS_FOUND, extendedType.getRelativeName() );
if( isCyclicInheritance( extendedType, gsClass ) )
{
extendedType = ErrorType.getInstance( extendedType.getName() );
verify( extendedTypeExpr, false, Res.MSG_CYCLIC_INHERITANCE, extendedType.getName() );
}
}
else
{
if( extendedType instanceof IGosuEnhancementInternal )
{
verify( extendedTypeExpr, false, Res.MSG_ENHANCEMENTS_CANNOT_ENHANCE_OTHER_ENHANCEMENTS, extendedType.getName() );
}
}
}
}
else if( gsClass.isEnum() )
{
extendedType = JavaTypes.ENUM();
extendedType = extendedType.getParameterizedType( gsClass );
}
makeProxy( gsClass, extendedType );
extendedType = TypeLord.makeDefaultParameterizedType( extendedType );
if( !verify( extendedTypeExpr == null ? getClassStatement() : extendedTypeExpr,
(!extendedType.isGenericType() || extendedType instanceof IGosuClass && !((IGosuClass) extendedType).isHeaderCompiled()) ||
extendedType.isParameterizedType() || gsClass instanceof IGosuEnhancementInternal,
Res.MSG_CANNOT_EXTEND_RAW_GENERIC_TYPE, extendedType.getName() ) )
{
// If we are unable to resolve a parameterized type, extend the error type
extendedType = ErrorType.getInstance();
}
if( bExtended && !(gsClass instanceof IGosuEnhancementInternal) )
{
verify( extendedTypeExpr == null ? getClassStatement() : extendedTypeExpr,
Modifier.isStatic( extendedType.getModifiers() ) || extendedType.getEnclosingType() == null ||
TypeLord.enclosingTypeInstanceInScope( extendedType.getEnclosingType(), getGosuClass() ),
Res.MSG_NO_ENCLOSING_INSTANCE_IN_SCOPE, extendedType.getEnclosingType() );
}
return extendedType;
}
private void makeProxy( IGosuClassInternal gsClass, IType extendedType )
{
if( !(gsClass instanceof IGosuEnhancementInternal) && extendedType instanceof IJavaType )
{
// Create a gosu class proxy for the java one.
// It is attached to the JavaType as its adapterClass.
GosuClassProxyFactory.instance().create( extendedType );
}
}
private Object parseFunctionOrConstructorOrFieldDeclaration( IGosuClassInternal gsClass )
{
int[] location = new int[3];
Object rtn = _parseFunctionOrConstructorOrFieldDeclaration( gsClass, location );
if( rtn != null )
{
setLocation( location[0], location[1], location[2] );
}
return rtn;
}
//------------------------------------------------------------------------------
//
// class-member-declaration
// <constructor-declaration>
// <function-declaration>
// <field-declaration>
//
// constructor-declaration
// [modifiers] function <class-name> ( [ <argument-declaration-list> ] )
//
// function-declaration
// [modifiers] function <identifier> ( [ <argument-declaration-list> ] ) [: <type-literal>]
//
// field-declaration
// [modifiers] var <identifier> [ : <type-literal> ] = <expression>
// [modifiers] var <identifier> : <type-literal> [ = <expression> ]
//
private Object _parseFunctionOrConstructorOrFieldDeclaration( IGosuClassInternal gsClass, int[] location )
{
Token T = new Token();
ModifierInfo modifiers;
boolean bInterface = gsClass.isInterface();
while( true )
{
modifiers = parseUntilMemberKeyword( T, false, -1, location );
if( modifiers.getModifiers() == -1 )
{
return null;
}
if( Keyword.KW_class.toString().equals( T._strValue ) ||
Keyword.KW_interface.equals( T._strValue ) ||
Keyword.KW_structure.equals( T._strValue ) ||
Keyword.KW_enum.equals( T._strValue ) )
{
if( bInterface && Keyword.KW_enum.equals( T._strValue ))
{
verify( getClassStatement(), !Modifier.isFinal( modifiers.getModifiers() ), Res.MSG_ILLEGAL_USE_OF_MODIFIER, Keyword.KW_final, Keyword.KW_enum );
}
parseInnerClassDeclartion( T );
}
else
{
break;
}
}
if( bInterface )
{
modifiers.addModifiers( Modifier.PUBLIC );
}
if( T._strValue != null &&
(T._strValue.equals( Keyword.KW_function.toString() ) ||
T._strValue.equals( Keyword.KW_construct.toString() )) )
{
if( bInterface )
{
modifiers.addModifiers( Modifier.ABSTRACT );
}
Token ctorNameToken = null;
boolean bConstructKeyword = false;
if( T._strValue.equals( Keyword.KW_construct.toString() ) )
{
T._strValue = gsClass.getRelativeName();
ctorNameToken = T;
bConstructKeyword = true;
}
else
{
match( T, null, SourceCodeTokenizer.TT_WORD, true );
}
FunctionStatement fs = makeFunctionOrConstructorStatement( gsClass, T, bConstructKeyword );
IFullParserState constructOrFunctionState = makeFullParserState();
verify( fs, !(gsClass instanceof IGosuProgramInternal) || !((IGosuProgramInternal)gsClass).isStatementsOnly(),
Res.MSG_FUNCTIONS_NOT_ALLOWED_IN_THIS_CONTEXT );
DynamicFunctionSymbol dfs = getOwner().parseFunctionDecl( fs, ctorNameToken, false, false, modifiers );
fs.setDynamicFunctionSymbol( dfs );
pushStatement( fs );
if( dfs != null )
{
dfs.setClassMember( true );
boolean bConstructor = dfs.getDisplayName().equals( gsClass.getRelativeName() );
if( bConstructor )
{
verify( fs, !Modifier.isAbstract( modifiers.getModifiers() ), Res.MSG_MODIFIER_ABSTRACT_NOT_ALLOWED_HERE );
verify( fs, !gsClass.isInterface(), Res.MSG_NOT_ALLOWED_IN_INTERFACE );
verify( fs, !(gsClass instanceof IGosuProgramInternal), Res.MSG_CONSTRUCTORS_NOT_ALLOWD_IN_THIS_CONTEXT );
verify( fs, !Modifier.isOverride( modifiers.getModifiers() ), Res.MSG_ILLEGAL_USE_OF_MODIFIER, Keyword.KW_override, Keyword.KW_construct );
verify( fs, !Modifier.isFinal( modifiers.getModifiers() ), Res.MSG_ILLEGAL_USE_OF_MODIFIER, Keyword.KW_final, Keyword.KW_construct );
verify( fs, !Modifier.isStatic( modifiers.getModifiers() ), Res.MSG_NO_STATIC_CONSTRUCTOR );
if( !bConstructKeyword )
{
fs.addParseWarning( new ObsoleteConstructorWarning( constructOrFunctionState, Res.MSG_OBSOLETE_CTOR_SYNTAX ) );
}
}
else
{
verifyNoCombinedFinalStaticModifierDefined( fs, false, modifiers.getModifiers() );
verify( fs, !gsClass.isInterface() || !Modifier.isStatic( modifiers.getModifiers() ),Res.MSG_ILLEGAL_USE_OF_MODIFIER, Keyword.KW_static, Keyword.KW_function );
verify( fs, !gsClass.isInterface() || !Modifier.isFinal( modifiers.getModifiers() ), Res.MSG_ILLEGAL_USE_OF_MODIFIER, Keyword.KW_final, Keyword.KW_function );
}
}
eatOptionalSemiColon( bInterface );
if( !bInterface &&
!Modifier.isNative( modifiers.getModifiers() ) && !Modifier.isAbstract( modifiers.getModifiers() ) )
{
eatStatementBlock( fs, Res.MSG_EXPECTING_OPEN_BRACE_FOR_FUNCTION_DEF );
}
return dfs;
}
else if( T._strValue != null && T._strValue.equals( Keyword.KW_property.toString() ) )
{
boolean bGetter = match( null, Keyword.KW_get );
verify( getClassStatement(), bGetter || match( null, Keyword.KW_set ), Res.MSG_EXPECTING_PROPERTY_GET_OR_SET_MODIFIER );
FunctionStatement fs = new FunctionStatement();
if( bInterface )
{
verify( fs, !Modifier.isStatic( modifiers.getModifiers() ),Res.MSG_ILLEGAL_USE_OF_MODIFIER, Keyword.KW_static, Keyword.KW_property );
verify( fs, !Modifier.isFinal( modifiers.getModifiers() ), Res.MSG_ILLEGAL_USE_OF_MODIFIER, Keyword.KW_final, Keyword.KW_property );
modifiers.setModifiers( Modifier.setAbstract( modifiers.getModifiers(), true ) );
}
verify( fs, !Modifier.isAbstract( modifiers.getModifiers() ) || gsClass.isAbstract(), Res.MSG_ABSTRACT_MEMBER_NOT_IN_ABSTRACT_CLASS );
verifyNoCombinedFinalStaticModifierDefined( fs, false, modifiers.getModifiers() );
verify( fs, !(gsClass instanceof IGosuProgramInternal) || !((IGosuProgramInternal)gsClass).isStatementsOnly(),
Res.MSG_FUNCTIONS_NOT_ALLOWED_IN_THIS_CONTEXT );
DynamicFunctionSymbol dfs = getOwner().parseFunctionDecl( fs, true, bGetter, modifiers );
if( dfs == null )
{
getClassStatement().addParseException( new ParseException( makeFullParserState(), Res.MSG_EXPECTING_DECL ) );
return null;
}
fs.setDynamicFunctionSymbol( dfs );
pushStatement( fs );
setLocation( location[0], location[1], location[2] );
popStatement();
dfs.setClassMember( true );
eatOptionalSemiColon( bInterface );
if( !bInterface &&
!Modifier.isNative( modifiers.getModifiers() ) && !Modifier.isAbstract( modifiers.getModifiers() ) )
{
eatStatementBlock( fs, Res.MSG_EXPECTING_OPEN_BRACE_FOR_FUNCTION_DEF );
}
DynamicPropertySymbol dps = getOrCreateDynamicPropertySymbol( getClassStatement(), gsClass, dfs, bGetter );
PropertyStatement statement = new PropertyStatement( fs, dps );
verifyPropertiesAreSymmetric( bGetter, dfs, dps, statement );
pushStatement( statement );
return dps;
}
else if( T._strValue != null && T._strValue.equals( Keyword.KW_var.toString() ) )
{
if( bInterface )
{
modifiers.setModifiers( Modifier.setStatic( modifiers.getModifiers(), true ) );
modifiers.setModifiers( Modifier.setFinal( modifiers.getModifiers(), true ) );
}
return parseFieldDecl( modifiers );
}
else if( T._strValue != null && T._strValue.equals( Keyword.KW_delegate.toString() ) )
{
return parseDelegateDecl( modifiers, gsClass );
}
else
{
getClassStatement().addParseException( new ParseException( makeFullParserState(), Res.MSG_EXPECTING_DECL ) );
return null;
}
}
private void verifyPropertiesAreSymmetric( boolean bGetter,
DynamicFunctionSymbol newFunction,
DynamicPropertySymbol propertySymbol,
Statement stmt )
{
DynamicFunctionSymbol getter;
DynamicFunctionSymbol setter;
if( bGetter )
{
getter = newFunction;
setter = propertySymbol == null ? null : propertySymbol.getSetterDfs();
}
else
{
getter = propertySymbol == null ? null : propertySymbol.getGetterDfs();
setter = newFunction;
}
if( getter != null && setter != null )
{
if( getter.isStatic() != setter.isStatic() )
{
verify( stmt, false, Res.MSG_PROPERTIES_MUST_AGREE_ON_STATIC_MODIFIERS );
}
if( setter.getArgs().size() == 1 )
{
IType setterType = setter.getArgTypes()[0];
IType returnType = getter.getReturnType();
if( !GosuObjectUtil.equals( returnType, setterType ) ||
!GosuObjectUtil.equals( propertySymbol.getType(), setterType ) ||
!GosuObjectUtil.equals( propertySymbol.getType(), setterType ) )
{
verify( stmt, false, Res.MSG_PROPERTIES_MUST_AGREE_ON_TYPE );
}
}
}
else if( getter != null && propertySymbol != null && newFunction != null &&
getGosuClass() == propertySymbol.getScriptPart().getContainingType() &&
getter.getSuperDfs() == null )
{
verify( stmt, propertySymbol.getType().equals( newFunction.getReturnType() ), Res.MSG_PROPERTIES_MUST_AGREE_ON_TYPE );
}
}
private FunctionStatement makeFunctionOrConstructorStatement(
IGosuClassInternal gsClass, Token T, boolean bConstructKeyword )
{
FunctionStatement fs;
if( gsClass != null &&
(bConstructKeyword || gsClass.getRelativeName().equals( T._strValue )) )
{
fs = new ConstructorStatement( bConstructKeyword );
}
else
{
fs = new FunctionStatement();
}
return fs;
}
private ModifierInfo parseUntilMemberKeyword( Token T, boolean bIgnoreErrors, int[] location )
{
return parseUntilMemberKeyword( T, bIgnoreErrors, -1, location );
}
private ModifierInfo parseUntilMemberKeyword( Token T, boolean bIgnoreErrors, int iEnd, int[] location )
{
return parseUntilMemberKeyword( T, bIgnoreErrors, iEnd, location, null );
}
private ModifierInfo parseUntilMemberKeyword( Token T, boolean bIgnoreErrors, int iEnd, int[] location, int[] mark )
{
boolean bPeek = T == null;
while( true )
{
if( location != null )
{
location[0] = getTokenizer().getTokenStart();
location[1] = getTokenizer().getLineNumber();
location[2] = getTokenizer().getTokenColumn();
}
if( mark != null )
{
mark[0] = getTokenizer().mark();
}
ModifierInfo modifiers = parseModifiers( bIgnoreErrors );
if( matchDeclarationKeyword( T, bPeek, getTokenizer() ) )
{
return modifiers;
}
popModifierList();
boolean bAte = false;
if( getGosuClass() instanceof IGosuProgram )
{
bAte = eatPossibleEnclosedVarInStmt(); // e.g., for( var foo in foos ) {...} we don't want the var foo to be consumed as a field (applies to GosuPrograms).
}
bAte = eatPossibleStatementBlock() || bAte;
if( location != null )
{
// Mark possible end location of member definition
location[0] = getTokenizer().getTokenStart();
}
if( match( null, SourceCodeTokenizer.TT_EOF ) ||
((!(getGosuClass() instanceof IGosuProgram) || !getGosuClass().isHeaderCompiled()) && match( null, '}' )) ||
(iEnd >= 0 && getTokenizer().getTokenStart() >= iEnd) )
{
modifiers.setModifiers( -1 );
return modifiers;
}
if( !bAte )
{
getTokenizer().nextToken();
if( getTokenizer().isEOF() )
{
modifiers.setModifiers( -1 );
return modifiers;
}
}
}
}
private void popModifierList()
{
ParseTree parseTree = getOwner().peekLocation();
if( parseTree == null )
{
return;
}
ParsedElement pe = parseTree.getParsedElement();
if( pe instanceof IModifierListClause )
{
List<ParseTree> locationsList = getLocationsList();
locationsList.remove( locationsList.size()-1 );
}
}
private void parseInnerClassDeclartion( Token t )
{
IGosuClassInternal enclosingGsClass = getClassStatement().getGosuClass();
verify( getClassStatement(), match( t, SourceCodeTokenizer.TT_WORD ), Res.MSG_EXPECTING_NAME_CLASS_DEF );
String strInnerClass = t._strValue;
if( strInnerClass != null )
{
for( IGosuClass c : enclosingGsClass.getKnownInnerClassesWithoutCompiling().values() )
{
IGosuClassInternal innerClass = (IGosuClassInternal)c;
if( innerClass.getRelativeName().equals( strInnerClass ) )
{
int i = 0;
String relativeName = innerClass.getName();
while( innerClass.isDeclarationsCompiled() )
{
// The inner class is already declaration-compiled, maybe this is a duplicate inner class...
String duplicate = relativeName + "_duplicate_" + i++;
innerClass = (IGosuClassInternal)TypeSystem.getByFullNameIfValid( duplicate );
if( innerClass == null )
{
return;
}
}
Map<String, Set<IFunctionSymbol>> restoreDfsDecls = copyDFSDecls();
new GosuClassParser( getOwner(), innerClass ).parseDeclarations( innerClass );
if( innerClass.isInterface() )
{
ModifierInfo mi = (ModifierInfo)innerClass.getModifierInfo();
mi.setModifiers( Modifier.setStatic( mi.getModifiers(), true ));
}
getOwner().setDfsDeclInSetByName( restoreDfsDecls );
break;
}
}
}
}
private Map<String, Set<IFunctionSymbol>> copyDFSDecls()
{
Map<String, Set<IFunctionSymbol>> hashMap = new HashMap<String, Set<IFunctionSymbol>>( getOwner().getDfsDecls() );
for( String name : hashMap.keySet() )
{
hashMap.put( name, new HashSet<IFunctionSymbol>( hashMap.get( name ) ) );
}
return hashMap;
}
private VarStatement parseFieldDecl( ModifierInfo modifiers )
{
Token T = new Token();
VarStatement varStmt = new VarStatement();
verify( varStmt, !Modifier.isAbstract( modifiers.getModifiers() ), Res.MSG_MODIFIER_ABSTRACT_NOT_ALLOWED_HERE );
verify( varStmt, !Modifier.isOverride( modifiers.getModifiers() ), Res.MSG_ILLEGAL_USE_OF_MODIFIER, Keyword.KW_override, Keyword.KW_var );
final int iNameStart = getTokenizer().getTokenStart();
if( !verify( varStmt, match( T, SourceCodeTokenizer.TT_WORD ), Res.MSG_EXPECTING_IDENTIFIER_VAR ) )
{
T._strValue = null;
}
String strIdentifier = T._strValue == null ? "" : T._strValue;
boolean bAlreadyDefined = getSymbolTable().getSymbol( strIdentifier ) != null;
verify( varStmt, !bAlreadyDefined, Res.MSG_VARIABLE_ALREADY_DEFINED, strIdentifier );
checkForEnumConflict( varStmt, strIdentifier );
boolean bStatic = Modifier.isStatic( modifiers.getModifiers() );
GlobalScope scope;
if( match( null, Keyword.KW_application ) )
{
// NOTE: For class parsing APPLICATION == static
bStatic = true;
scope = GlobalScope.EXECUTION;
verifyOrWarn( varStmt, false, true, Res.MSG_APPLICATION_MODIFIER_HAS_BEEN_DEPRECATED );
}
else if( match( null, Keyword.KW_session ) )
{
bStatic = true;
scope = GlobalScope.SESSION;
verifyOrWarn( varStmt, false, true, Res.MSG_SESSION_MODIFIER_HAS_BEEN_DEPRECATED );
}
else if( match( null, Keyword.KW_request ) )
{
bStatic = true;
scope = GlobalScope.REQUEST;
verifyOrWarn( varStmt, false, true, Res.MSG_REQUEST_MODIFIER_HAS_BEEN_DEPRECATED );
}
else
{
// execution keyword may be there
boolean hasExecutionKeyword = match( null, Keyword.KW_execution );
scope = GlobalScope.EXECUTION;
verifyOrWarn( varStmt, !hasExecutionKeyword, true, Res.MSG_EXECUTION_MODIFIER_HAS_BEEN_DEPRECATED );
}
TypeLiteral typeLiteral = null;
if( match( null, ":", SourceCodeTokenizer.TT_OPERATOR ) )
{
getOwner().parseTypeLiteral();
typeLiteral = (TypeLiteral)popExpression();
}
else if( !match( null, "=", SourceCodeTokenizer.TT_OPERATOR, true ) )
{
if( match( null, null, '(', true ) )
{
getOwner().parseBlockLiteral();
typeLiteral = (TypeLiteral)popExpression();
}
}
IType type;
if( typeLiteral != null )
{
type = typeLiteral.getType().getType();
varStmt.setTypeLiteral( typeLiteral );
}
else
{
type = GosuParserTypes.NULL_TYPE();
}
varStmt.setScope( scope );
if( bStatic )
{
modifiers.setModifiers( Modifier.setStatic( modifiers.getModifiers(), true ) );
}
varStmt.setModifierInfo( modifiers );
if( !verify( varStmt, varStmt.isPrivate() || type != GosuParserTypes.NULL_TYPE(), Res.MSG_NON_PRIVATE_MEMBERS_MUST_DECLARE_TYPE ) )
{
type = ErrorType.getInstance();
}
DynamicPropertySymbol dpsVarProperty = getOwner().parseVarPropertyClause( varStmt, strIdentifier, type, false );
if( dpsVarProperty != null )
{
getSymbolTable().putSymbol( dpsVarProperty );
verifyPropertiesAreSymmetric( true, dpsVarProperty.getGetterDfs(), dpsVarProperty, varStmt );
setStatic( bStatic, dpsVarProperty );
dpsVarProperty.addMemberSymbols( getGosuClass() );
}
AbstractDynamicSymbol symbol;
if( varStmt.getScope() == GlobalScope.EXECUTION )
{
symbol = new DynamicSymbol( getGosuClass(), getSymbolTable(), strIdentifier, type, null );
}
else
{
symbol = new ScopedDynamicSymbol( getSymbolTable(), strIdentifier, getGosuClass().getName(), type, varStmt.getScope() );
}
modifiers.addAll( symbol.getModifierInfo() );
symbol.setModifierInfo( modifiers );
varStmt.setSymbol( symbol );
varStmt.setNameOffset( iNameStart, T._strValue );
if( varStmt.isPrivate() )
{
// Ensure private bit is explicit
modifiers.setModifiers( Modifier.setPrivate( modifiers.getModifiers(), true ) );
}
if( bAlreadyDefined )
{
int iDupIndex = getOwner().nextIndexOfErrantDuplicateDynamicSymbol( symbol, getSymbolTable().getSymbols().values(), false );
if( iDupIndex >= 0 )
{
symbol.renameAsErrantDuplicate( iDupIndex );
}
}
getSymbolTable().putSymbol( symbol );
pushStatement( varStmt );
return varStmt;
}
private void checkForEnumConflict( VarStatement varStmt, String identifier )
{
if( getGosuClass().isEnum() )
{
ISymbol existingProp = getGosuClass().getMemberProperty( identifier );
verify( varStmt, !(existingProp instanceof DynamicPropertySymbol), Res.MSG_VARIABLE_ALREADY_DEFINED, identifier );
}
}
private VarStatement parseDelegateDecl( ModifierInfo modifiers, IGosuClassInternal gsClass )
{
Token T = new Token();
DelegateStatement delegateStmt = new DelegateStatement();
verify( delegateStmt, gsClass == null || (!gsClass.isInterface() && !gsClass.isEnum()), Res.MSG_DELEGATION_NOT_ALLOWED_HERE );
verify( delegateStmt, !Modifier.isStatic( modifiers.getModifiers() ), Res.MSG_DELEGATES_CANNOT_BE_STATIC );
verify( delegateStmt, !Modifier.isAbstract( modifiers.getModifiers() ), Res.MSG_MODIFIER_ABSTRACT_NOT_ALLOWED_HERE );
verify( delegateStmt, !Modifier.isOverride( modifiers.getModifiers() ), Res.MSG_ILLEGAL_USE_OF_MODIFIER, Keyword.KW_override, Keyword.KW_var );
int iNameOffset = getTokenizer().getTokenStart();
if( verify( delegateStmt, match( T, SourceCodeTokenizer.TT_WORD ), Res.MSG_EXPECTING_IDENTIFIER_VAR ) )
{
delegateStmt.setNameOffset( iNameOffset, null );
}
String strIdentifier = T._strValue == null ? "" : T._strValue;
verify( delegateStmt, getSymbolTable().getSymbol( strIdentifier ) == null, Res.MSG_VARIABLE_ALREADY_DEFINED, strIdentifier );
GlobalScope scope = GlobalScope.EXECUTION;
TypeLiteral typeLiteral = null;
if( match( null, ":", SourceCodeTokenizer.TT_OPERATOR ) )
{
getOwner().parseTypeLiteral();
typeLiteral = (TypeLiteral)popExpression();
}
List<IType> constituents = new ArrayList<IType>();
if( verify( delegateStmt, match( null, Keyword.KW_represents ), Res.MSG_EXPECTING_REPRESENTS ) )
{
do
{
getOwner().parseTypeLiteral();
TypeLiteral ifaceLiteral = (TypeLiteral)popExpression();
IType iface = ifaceLiteral.getType().getType();
if( !(iface instanceof ErrorType) )
{
verify( ifaceLiteral, iface.isInterface(), Res.MSG_DELEGATES_REPRESENT_INTERFACES_ONLY );
verify( ifaceLiteral, iface.isAssignableFrom( gsClass ), Res.MSG_CLASS_DOES_NOT_IMPL, iface );
verify( typeLiteral, typeLiteral == null || iface.isAssignableFrom( typeLiteral.getType().getType() ), Res.MSG_CLASS_DOES_NOT_IMPL, iface );
}
constituents.add( iface );
} while( match( null, ',' ) );
}
delegateStmt.setConstituents( constituents );
IType type;
if( typeLiteral != null )
{
type = typeLiteral.getType().getType();
delegateStmt.setTypeLiteral( typeLiteral );
}
else
{
type = GosuParserTypes.NULL_TYPE();
}
delegateStmt.setScope( scope );
delegateStmt.setModifierInfo( modifiers );
verify( delegateStmt, delegateStmt.isPrivate() || type != GosuParserTypes.NULL_TYPE(), Res.MSG_NON_PRIVATE_MEMBERS_MUST_DECLARE_TYPE );
AbstractDynamicSymbol symbol = new DynamicSymbol( getGosuClass(), getSymbolTable(), strIdentifier, type, null );
modifiers.addAll( symbol.getModifierInfo() );
symbol.setModifierInfo( modifiers );
delegateStmt.setSymbol( symbol );
getSymbolTable().putSymbol( symbol );
pushStatement( delegateStmt );
return delegateStmt;
}
private void setStatic( boolean bStatic, DynamicPropertySymbol dpsVarProperty )
{
dpsVarProperty.setStatic( bStatic );
if( dpsVarProperty.getSetterDfs() != null )
{
dpsVarProperty.getSetterDfs().setStatic( bStatic );
}
if( dpsVarProperty.getGetterDfs() != null )
{
dpsVarProperty.getGetterDfs().setStatic( bStatic );
}
}
//------------------------------------------------------------------------------
//
// class-statement
// [modifiers] class <identifier> [extends <base-class>] [implements <interfaces-list>] { <class-members> }
//
boolean parseClassStatement()
{
IGosuClassInternal gsClass = getGosuClass();
ensureInterfaceMethodsImpled( gsClass );
//## todo: remove this scope?
getSymbolTable().pushScope();
try
{
verify( getClassStatement(), gsClass instanceof IGosuProgram || match( null, '{' ), Res.MSG_EXPECTING_OPEN_BRACE_FOR_CLASS_DEF );
Statement currentStmt = (isTopLevelClass( gsClass ) || TypeLord.isEvalProgram( gsClass )) ? null : peekStatement();
parseClassMembers( gsClass );
for( Statement stmt = peekStatement(); stmt != currentStmt; stmt = peekStatement() )
{
stmt = popStatement();
if( stmt instanceof VarStatement ||
stmt instanceof FunctionStatement ||
stmt instanceof PropertyStatement ||
stmt instanceof NoOpStatement ||
stmt instanceof NamespaceStatement ||
stmt instanceof UsesStatement ||
stmt instanceof ClassStatement )
{
// ignore
}
else
{
throw new IllegalStateException( "Expecting only statements for: package, uses, var, function, or property." );
}
}
verify( getClassStatement(), match( null, '}' ) || gsClass instanceof IGosuProgram, Res.MSG_EXPECTING_CLOSE_BRACE_FOR_CLASS_DEF );
}
finally
{
getSymbolTable().popScope();
}
return true;
}
private void ensureInterfaceMethodsImpled( IGosuClassInternal gsClass )
{
if( !gsClass.isInterface() && !gsClass.isAbstract() )
{
List<IFunctionType> unimpled = gsClass.getUnimplementedMethods();
for( IFunctionType funcType : unimpled )
{
String strClass = funcType.getEnclosingType().getName();
strClass = IGosuClass.ProxyUtil.getNameSansProxy( strClass );
getClassStatement().addParseException( new NotImplementedParseException( makeFullParserState(), gsClass, strClass, funcType ) );
}
}
}
// class-members
// <class-member>
// <class-members> <class-member>
//
// class-member
// <function-definition>
// <var-statement>
//
private void parseClassMembers( IGosuClassInternal gsClass )
{
if( isInnerClass( gsClass ) && !gsClass.isStatic() )
{
addOuterMember( gsClass );
}
ClassScopeCache scopeCache = makeClassScopeCache( gsClass );
parseEnumConstants( gsClass, scopeCache );
do
{
getOwner().checkInstruction( true );
while( match( null, ';' ) )
{
pushStatement( new NoOpStatement() );
}
int iOffset = getTokenizer().getTokenStart();
int iLineNum = getTokenizer().getLineNumber();
int iColumn = getTokenizer().getTokenColumn();
ModifierInfo modifiers;
if( gsClass instanceof IGosuProgram )
{
int[] locations = new int[3];
modifiers = parseUntilMemberKeyword( null, false, locations );
iOffset = locations[0];
iLineNum = locations[1];
iColumn = locations[2];
}
else
{
// push static class symbols for annotations (they are part of modifier parsing)
pushClassSymbols( true, scopeCache );
try
{
modifiers = parseModifiers();
}
finally
{
popClassSymbols();
}
}
boolean bStatic = Modifier.isStatic( modifiers.getModifiers() );
if( gsClass.isInterface() )
{
modifiers.setModifiers( Modifier.setPublic( modifiers.getModifiers(), true ) );
}
boolean bDeprecated = isDeprecated( modifiers );
if( bDeprecated )
{
getOwner().pushIgnoreTypeDeprecation();
}
try
{
boolean bConstructSyntax = false;
Token T = new Token();
if( match( null, Keyword.KW_function ) ||
(bConstructSyntax = match( null, Keyword.KW_construct )) )
{
FunctionStatement functionStmt;
if( bConstructSyntax || isOldStyleConstructor( gsClass, T ) )
{
functionStmt = parseBaseConstructorDefinition( bConstructSyntax, modifiers.getAnnotations(), scopeCache );
verifyModifiers( functionStmt, modifiers, UsageTarget.ConstructorTarget );
}
else
{
pushClassSymbols( bStatic, scopeCache );
try
{
functionStmt = getOwner().parseBaseFunctionDefinition( null, false, false, modifiers );
if( gsClass.isInterface() && !bStatic )
{
eatOptionalSemiColon( true );
pushStatement( functionStmt );
}
verifyModifiers( functionStmt, modifiers, UsageTarget.MethodTarget );
}
finally
{
popClassSymbols();
}
}
DynamicFunctionSymbol dfs = functionStmt == null ? null : functionStmt.getDynamicFunctionSymbol();
if( dfs != null )
{
dfs.setClassMember( true );
if( dfs.getDisplayName().equals( gsClass.getRelativeName() ) )
{
gsClass.getParseInfo().addConstructorFunction(dfs);
}
else
{
gsClass.getParseInfo().addMemberFunction(dfs);
}
}
setLocation( iOffset, iLineNum, iColumn );
}
else if( match( null, Keyword.KW_property ) )
{
pushClassSymbols( bStatic, scopeCache );
try
{
Token t = new Token();
boolean bGetter = match( t, Keyword.KW_get );
boolean bSetter = !bGetter && match( null, Keyword.KW_set );
if( !bGetter && !bSetter )
{
getOwner().maybeEatNonDeclKeyword( false, t._strValue );
}
FunctionStatement functionStmt = getOwner().parseBaseFunctionDefinition( null, true, bGetter, modifiers );
verify( functionStmt, bGetter || bSetter, Res.MSG_EXPECTING_PROPERTY_GET_OR_SET_MODIFIER );
setLocation( iOffset, iLineNum, iColumn );
getOwner().popStatement();
DynamicFunctionSymbol dfs = functionStmt.getDynamicFunctionSymbol();
if( dfs != null )
{
IType returnType = functionStmt.getDynamicFunctionSymbol().getReturnType();
verify( functionStmt, bGetter || returnType == JavaTypes.pVOID(), Res.MSG_PROPERTY_SET_MUST_RETURN_VOID );
if( bGetter && dfs.getArgTypes() != null && dfs.getArgTypes().length > 0 )
{
List<IParameterDeclaration> parameters = functionStmt.getParameters();
for( IParameterDeclaration par : parameters )
{
par.addParseException( Res.MSG_GETTER_CANNOT_HAVE_PARAMETERS );
}
}
dfs.setClassMember( true );
DynamicPropertySymbol dps = getOrCreateDynamicPropertySymbol( functionStmt, gsClass, dfs, bGetter );
PropertyStatement stmt = new PropertyStatement( functionStmt, dps );
getOwner().pushStatement( stmt );
setLocation( iOffset, iLineNum, iColumn, true );
verifyPropertiesAreSymmetric( bGetter, dfs, dps, stmt );
dps.addMemberSymbols( gsClass );
}
verifyModifiers( functionStmt, modifiers, UsageTarget.PropertyTarget );
}
finally
{
popClassSymbols();
}
}
else if( match( null, Keyword.KW_var ) )
{
getOwner().pushParsingStaticMember( bStatic );
try
{
VarStatement varStmt = parseFieldDefn( gsClass, bStatic, scopeCache, modifiers );
setLocation( iOffset, iLineNum, iColumn );
removeInitializerIfInProgram( varStmt );
verifyModifiers( varStmt, modifiers, UsageTarget.PropertyTarget );
}
finally
{
getOwner().popParsingStaticMember();
}
}
else if( match( null, Keyword.KW_delegate ) )
{
DelegateStatement ds = parseDelegateDefn( gsClass, scopeCache, modifiers );
verifyModifiers( ds, modifiers, UsageTarget.PropertyTarget );
setLocation( iOffset, iLineNum, iColumn );
}
else if( match( T, Keyword.KW_class ) ||
match( T, Keyword.KW_interface ) ||
match( T, Keyword.KW_structure ) ||
match( T, Keyword.KW_enum ) )
{
// Pop the modifier list from the declaration phase, otherwise we'll have duplicates
popModifierList();
IGosuClassInternal inner = parseInnerClassDefinition( T );
if( inner != null )
{
inner.setAnnotations( modifiers.getAnnotations() );
if( inner.isInterface() )
{
modifiers.setModifiers( Modifier.setStatic( modifiers.getModifiers(), true ) );
ModifierInfo existingMI = (ModifierInfo)inner.getModifierInfo();
existingMI.setModifiers( modifiers.getModifiers() );
}
verifyModifiers( inner.getClassStatement(), modifiers, UsageTarget.TypeTarget );
}
}
else
{
// Pop the trailing modifier list, which doesn't correspond to any member
popModifierList();
if( !match( null, null, '}', true ) &&
!match( null, SourceCodeTokenizer.TT_EOF ) )
{
// Consume token first
boolean openBrace = false;
if( match( null, '{' ) )
{
openBrace = true;
}
else
{
getOwner().getTokenizer().nextToken();
}
NoOpStatement noop = new NoOpStatement();
verify( noop, false, Res.MSG_UNEXPECTED_TOKEN, getOwner().getTokenizer().getTokenAsString() );
pushStatement( noop );
setLocation( iOffset, iLineNum, iColumn );
if( openBrace )
{
eatBlock( '{', '}', false );
}
}
else
{
break;
}
}
}
finally
{
if( bDeprecated )
{
getOwner().popIgnoreTypeDeprecation();
}
}
} while( true );
}
private boolean isDeprecated( ModifierInfo modifiers )
{
List<IGosuAnnotation> annotations = modifiers.getAnnotations();
if( annotations != null )
{
for( IGosuAnnotation an : annotations )
{
if( an.getName().equalsIgnoreCase( "Deprecated" ) )
{
return true;
}
}
}
return false;
}
private boolean isOldStyleConstructor( IGosuClassInternal gsClass, Token t )
{
return match( t, null, SourceCodeTokenizer.TT_WORD, true ) &&
!gsClass.isInterface() &&
t._strValue.equals( gsClass.getRelativeName() );
}
private ClassScopeCache makeClassScopeCache( IGosuClassInternal gsClass )
{
// Copy the Static Scope so we can reuse it for each member
//
IScope staticScope;
Map<String, Set<IFunctionSymbol>> staticDfsMap;
getSymbolTable().pushScope();
try
{
//getOwner().clearDfsDeclInSetByName();
getOwner().newDfsDeclInSetByName();
gsClass.putClassMembers( getOwner(), getSymbolTable(), getGosuClass(), true );
staticDfsMap = getOwner().getDfsDecls();
}
finally
{
staticScope = getSymbolTable().popScope();
}
// Copy the Non-Static Scope so we can reuse it for each member
//
IScope nonstaticScope;
Map<String, Set<IFunctionSymbol>> nonstaticDfsMap;
getSymbolTable().pushScope();
try
{
getOwner().newDfsDeclInSetByName();
gsClass.putClassMembers( getOwner(), getSymbolTable(), getGosuClass(), false );
nonstaticDfsMap = getOwner().getDfsDecls();
getOwner().newDfsDeclInSetByName();
}
finally
{
nonstaticScope = getSymbolTable().popScope();
}
return new ClassScopeCache( staticScope, staticDfsMap, nonstaticScope, nonstaticDfsMap );
}
private void popClassSymbols()
{
getSymbolTable().popScope();
getOwner().popParsingStaticMember();
getOwner().newDfsDeclInSetByName();
}
private void pushClassSymbols( boolean bStatic, ClassScopeCache classScopeCache )
{
getOwner().setDfsDeclInSetByName( bStatic ? classScopeCache.getStaticDfsMap() : classScopeCache.getNonstaticDfsMap() );
getSymbolTable().pushScope( bStatic ? classScopeCache.getStaticScope() : classScopeCache.getNonstaticScope() );
getOwner().pushParsingStaticMember( bStatic );
}
private void removeInitializerIfInProgram( VarStatement varStmt )
{
if( !(getGosuClass() instanceof IGosuProgram) || getOwner().isEditorParser() )
{
return;
}
ParseTree location = varStmt.getLocation();
List<IParseTree> children = location.getChildren();
int iChildCount = children.size();
if( iChildCount > 3 )
{
if( iChildCount > 4 )
{
if( !(children.get( 3 ).getParsedElement() instanceof NameInDeclaration) ) // this is another NameInDeclaration for the Property name, which can be null if the name was not specified after the 'as' clause
{
throw new IllegalStateException( "Expecting children: 1 for NameInDeclaration, 1 for the type, (maybe another NameInDeclaration if an alias property declared), and 1 for the initializer" );
}
}
IParseTree initializerExpr = children.get( iChildCount -1 );
if( initializerExpr != null )
{
location.removeChild( initializerExpr );
}
}
}
private IGosuClassInternal parseInnerClassDefinition( Token t )
{
IGosuClassInternal enclosingGsClass = getClassStatement().getGosuClass();
verify( getClassStatement(), match( t, SourceCodeTokenizer.TT_WORD ), Res.MSG_EXPECTING_NAME_CLASS_DEF );
String strInnerClass = t._strValue;
if( strInnerClass != null )
{
for( IGosuClass c : enclosingGsClass.getKnownInnerClassesWithoutCompiling().values() )
{
IGosuClassInternal innerClass = (IGosuClassInternal)c;
if( innerClass.getRelativeName().equals( strInnerClass ) )
{
int i = 0;
String relativeName = innerClass.getName();
while( innerClass.isDefinitionsCompiled() )
{
// The inner class is already definition-compiled, maybe this is a duplicate inner class...
String duplicate = relativeName + "_duplicate_" + i++;
innerClass = (IGosuClassInternal)TypeSystem.getByFullNameIfValid( duplicate );
if( innerClass == null )
{
return null;
}
}
new GosuClassParser( getOwner(), innerClass ).parseDefinitions( innerClass );
return innerClass;
}
}
}
return null;
}
private void parseEnumConstants( IGosuClassInternal gsClass,
ClassScopeCache scopeCache )
{
boolean bEnum = gsClass != null && gsClass.isEnum();
if( !bEnum )
{
return;
}
Set<String> constants = new HashSet<String>();
Token t = new Token();
do
{
int iOffset = getTokenizer().getTokenStart();
int iLineNum = getTokenizer().getLineNumber();
int iColumn = getTokenizer().getTokenColumn();
if( match( t, null, SourceCodeTokenizer.TT_WORD, true ) &&
!Keyword.isKeyword( t._strValue ) &&
match( t, SourceCodeTokenizer.TT_WORD ) )
{
parseEnumConstant( t._strValue, scopeCache, constants.contains( t._strValue ) );
setLocation(iOffset, iLineNum, iColumn);
constants.add( t._strValue );
popStatement();
}
if( match( null, ';' ) )
{
break;
}
} while( match( null, ',' ) );
}
private void parseEnumConstant( String strIdentifier, ClassScopeCache scopeCache, boolean bIsDuplicate )
{
IGosuClassInternal gsClass = getGosuClass();
VarStatement varStmt = gsClass.getStaticField( strIdentifier );
if( bIsDuplicate )
{
VarStatement dup = new VarStatement();
dup.setSymbol( varStmt.getSymbol() );
dup.setModifierInfo( varStmt.getModifierInfo() );
dup.setParent( varStmt.getParent() );
dup.setScope( varStmt.getScope() );
varStmt = dup;
}
pushClassSymbols( true, scopeCache );
try
{
getOwner().parseNewExpressionOrAnnotation( gsClass, false, !match( null, null, '(', true ), null, -1 );
Expression asExpr = popExpression();
varStmt.setAsExpression( asExpr );
if( asExpr.hasParseExceptions() )
{
for( IParseIssue pe : asExpr.getParseExceptions() )
{
varStmt.addParseException( pe );
//noinspection ThrowableResultOfMethodCallIgnored
asExpr.removeParseException( pe.getMessageKey() );
}
}
varStmt.setScriptPart( getOwner().getScriptPart() );
pushStatement( varStmt );
ISymbol symbol = varStmt.getSymbol();
symbol.setType( gsClass );
varStmt.setType( gsClass );
varStmt.setEnumConstant( true );
varStmt.setDefinitionParsed( true );
//noinspection unchecked
scopeCache.getNonstaticScope().put( varStmt.getSymbol().getName(), varStmt.getSymbol() );
gsClass.getParseInfo().addMemberField(varStmt);
}
finally
{
popClassSymbols();
}
}
private VarStatement parseFieldDefn( IGosuClassInternal gsClass, boolean bStatic, ClassScopeCache scopeCache, ModifierInfo modifiers )
{
if( gsClass.isInterface() )
{
bStatic = true;
}
Token t = new Token();
String strIdentifier = "";
boolean bHasName;
if( bHasName = match( t, SourceCodeTokenizer.TT_WORD ) )
{
strIdentifier = t._strValue;
}
else
{
t._strValue = null;
}
getOwner().maybeEatNonDeclKeyword( bHasName, strIdentifier );
String insensitveIdentifier = strIdentifier;
VarStatement varStmt;
if( !bStatic )
{
varStmt = findMemberField( gsClass, insensitveIdentifier );
if( varStmt == null )
{
// It might not be in the non-static map if it is a scoped variable
varStmt = findStaticMemberField( gsClass, insensitveIdentifier );
if( varStmt != null )
{
bStatic = true;
}
}
}
else
{
varStmt = findStaticMemberField( gsClass, insensitveIdentifier );
}
if( !bStatic && varStmt != null && varStmt.isStatic() )
{
// Force static scope if the var is static. This is for scoped vars
bStatic = true;
}
pushClassSymbols( bStatic, scopeCache );
try
{
if( varStmt == null )
{
// This is for error conditions like vars appearing on enhancements
varStmt = new VarStatement();
getOwner().parseVarStatement( varStmt, t, false );
}
else
{
getOwner().parseVarStatement( varStmt, t, true );
}
if( bStatic )
{
//noinspection unchecked
scopeCache.getNonstaticScope().put( varStmt.getSymbol().getName(), varStmt.getSymbol() );
}
if( getGosuClass() == null || !getGosuClass().isInterface() )
{
DynamicPropertySymbol dps = getOwner().parseVarPropertyClause( varStmt, varStmt.getIdentifierName(), varStmt.getType(), true );
if( dps != null )
{
verifyPropertiesAreSymmetric( true, dps.getGetterDfs(), dps, varStmt );
setStatic( bStatic, dps );
dps.addMemberSymbols( gsClass );
dps.updateAnnotations( modifiers.getAnnotations() );
}
}
// Consume optional trailing semi as part of the statement
match( null, ';' );
varStmt.getModifierInfo().setAnnotations( modifiers.getAnnotations() );
gsClass.getParseInfo().addMemberField(varStmt);
return varStmt;
}
finally
{
popClassSymbols();
}
}
private VarStatement findMemberField( IGosuClassInternal gsClass, String name )
{
gsClass.compileDeclarationsIfNeeded();
return assignPossibleDuplicateField( name, gsClass.getParseInfo().getMemberFields() );
}
private VarStatement findStaticMemberField( IGosuClassInternal gsClass, String name )
{
gsClass.compileDeclarationsIfNeeded();
return assignPossibleDuplicateField( name, gsClass.getParseInfo().getStaticFields() );
}
private VarStatement assignPossibleDuplicateField( String name, Map<String, VarStatement> fields )
{
VarStatement varStmt = fields.get( name );
varStmt = assignPossibleDuplicateField( name, varStmt, fields );
return varStmt;
}
VarStatement assignPossibleDuplicateField( String name, VarStatement varStmt, Map<String, VarStatement> map )
{
VarStatement result = varStmt;
if( varStmt == null || varStmt.isDefinitionParsed() )
{
int iMin = Integer.MAX_VALUE;
for( String nameCsr : map.keySet() )
{
String strName = nameCsr.toString();
if( strName.toLowerCase().contains( "_duplicate_" + name.toString().toLowerCase() ) )
{
VarStatement stmtCsr = map.get( nameCsr );
if( !stmtCsr.isDefinitionParsed() )
{
int iIndex = Integer.parseInt( strName.substring( 0, strName.indexOf( '_' ) ) );
if( iIndex < iMin )
{
iMin = iIndex;
result = stmtCsr;
}
}
}
}
}
return result;
}
private DelegateStatement parseDelegateDefn( IGosuClassInternal gsClass, ClassScopeCache scopeCache, ModifierInfo modifiers )
{
Token t = new Token();
int iNameOffset = getTokenizer().getTokenStart();
boolean bHasName = match( t, SourceCodeTokenizer.TT_WORD );
String strIdentifier = t._strValue == null ? "" : t._strValue;
getOwner().maybeEatNonDeclKeyword( bHasName, strIdentifier );
String insensitveIdentifier = strIdentifier;
VarStatement varStmt = gsClass.getMemberField( insensitveIdentifier );
if( varStmt != null )
{
varStmt.setNameOffset( iNameOffset, strIdentifier );
}
pushClassSymbols( false, scopeCache );
try
{
//Need to ensure that the varStmt is indeed a delegate statement, because it might be a conflicting var stmt
DelegateStatement delStmt;
if( varStmt instanceof DelegateStatement )
{
delStmt = (DelegateStatement)varStmt;
}
else
{
delStmt = new DelegateStatement();
delStmt.setModifierInfo( modifiers );
}
if( varStmt == null )
{
// This is for error conditions like delegates appearing on enhancements
varStmt = new DelegateStatement();
varStmt.setModifierInfo( modifiers );
varStmt.setSymbol( new Symbol( strIdentifier, JavaTypes.OBJECT(), null ) );
verify( delStmt, !Modifier.isStatic( modifiers.getModifiers() ), Res.MSG_DELEGATES_CANNOT_BE_STATIC );
getOwner().parseDelegateStatement( delStmt, strIdentifier );
}
else
{
getOwner().parseDelegateStatement( delStmt, strIdentifier );
}
gsClass.getParseInfo().addMemberField(varStmt);
return delStmt;
}
finally
{
popClassSymbols();
}
}
DynamicPropertySymbol getOrCreateDynamicPropertySymbol(
ParsedElement parsedElement, ICompilableTypeInternal gsClass, DynamicFunctionSymbol dfs, boolean bGetter )
{
String strPropertyName = dfs.getDisplayName().substring( 1 );
ISymbol symbol = getSymbolTable().getSymbol( strPropertyName );
if( symbol != null && !dfs.getDisplayName().contains( symbol.getDisplayName() ) )
{
// Force case sensitivity, mainly to make overrides consistent
symbol = null;
}
DynamicPropertySymbol dps;
if( !(gsClass instanceof IGosuClass && ((IGosuClass)gsClass).isCompilingDefinitions()) &&
!verify( parsedElement, symbol == null || symbol instanceof DynamicPropertySymbol, Res.MSG_VARIABLE_ALREADY_DEFINED, strPropertyName ) )
{
return new DynamicPropertySymbol( dfs, bGetter );
}
if( symbol == null ||
(gsClass != null &&
gsClass.getMemberProperty( strPropertyName ) == null &&
gsClass.getStaticProperty( strPropertyName ) == null) )
{
dps = new DynamicPropertySymbol( dfs, bGetter );
dps.setClassMember( true );
if( symbol != null )
{
assert symbol instanceof DynamicPropertySymbol;
dps.setParent( (DynamicPropertySymbol)symbol );
}
return dps;
}
else if( !(symbol instanceof DynamicPropertySymbol) )
{
// Error already applied from declaration phase
return new DynamicPropertySymbol( dfs, bGetter );
}
assert symbol instanceof DynamicPropertySymbol;
dps = (DynamicPropertySymbol)symbol;
if( bGetter )
{
verify( parsedElement,
strPropertyName.equals( Keyword.KW_outer.getName() ) ||
dps.getImmediateGetterDfs() == null ||
dps.getImmediateGetterDfs() instanceof VarPropertyGetFunctionSymbol ||
dps.getImmediateGetterDfs().getValueDirectly() != null ||
dps.getImmediateGetterDfs() == dfs ||
(dps.getImmediateGetterDfs().isAbstract() && !dfs.isAbstract()) ||
(gsClass != null && gsClass.isInterface()),
Res.MSG_GETTER_FOR_PROPERTY_ALREADY_DEFINED,
strPropertyName );
if( parsedElement.hasParseException( Res.MSG_FUNCTION_ALREADY_DEFINED ) &&
parsedElement.hasParseException( Res.MSG_GETTER_FOR_PROPERTY_ALREADY_DEFINED ) )
{
//noinspection ThrowableResultOfMethodCallIgnored
parsedElement.removeParseException( Res.MSG_FUNCTION_ALREADY_DEFINED );
}
dps.setGetterDfs( dfs );
}
else
{
verify( parsedElement,
dps.getImmediateSetterDfs() == null ||
dps.getImmediateSetterDfs() instanceof VarPropertySetFunctionSymbol ||
dps.getImmediateSetterDfs().getValueDirectly() != null ||
dps.getImmediateSetterDfs() == dfs ||
(dps.getImmediateSetterDfs().isAbstract() && !dfs.isAbstract()) ||
(gsClass != null && gsClass.isInterface()),
Res.MSG_SETTER_FOR_PROPERTY_ALREADY_DEFINED,
strPropertyName );
if( parsedElement.hasParseException( Res.MSG_FUNCTION_ALREADY_DEFINED ) &&
parsedElement.hasParseException( Res.MSG_SETTER_FOR_PROPERTY_ALREADY_DEFINED ) )
{
//noinspection ThrowableResultOfMethodCallIgnored
parsedElement.removeParseException( Res.MSG_FUNCTION_ALREADY_DEFINED );
}
dps.setSetterDfs( dfs );
}
return dps;
}
@SuppressWarnings({"ConstantConditions"})
private FunctionStatement parseBaseConstructorDefinition( boolean bConstructor, List<IGosuAnnotation> defnAnnotations, ClassScopeCache scopeCache )
{
final IGosuClassInternal gsClass = getGosuClass();
Token T = new Token();
getSymbolTable().pushScope();
try
{
String strFunctionName;
if( bConstructor )
{
strFunctionName = gsClass.getRelativeName();
}
else
{
match( T, SourceCodeTokenizer.TT_WORD );
strFunctionName = T._strValue;
}
// String strNameInSource = T._strValue == null ? "" : T._strValue;
// getOwner().addNameInDeclaration( strFunctionName, iOffsetName-9, iLineName, iColumnName, true );
// Since we're going with a two-pass approach the symbols will already be in the table, but w/o values.
// So we don't want to check for already-defined functions here -- we're going to overwrite them with
// these identical symbols, but with values.
//verify( _symTable.getSymbol( strFunctionName ) == null, strFunctionName + Res.MSG_VARIABLE_ALREADY_DEFINED ) );
match( null, '(' );
List<ISymbol> args;
IType[] argTypes;
FunctionStatement functionStmt = new ConstructorStatement( bConstructor );
int iOffsetParamList = getTokenizer().getTokenStart();
int iColumnParamList = getTokenizer().getTokenColumn();
int iLineParamList = getTokenizer().getLineNumber();
if( !match( null, null, ')', true ) )
{
pushClassSymbols( false, scopeCache );
try
{
args = getOwner().parseParameterDeclarationList( functionStmt, false, null );
}
finally
{
popClassSymbols();
}
argTypes = new IType[args.size()];
for( int i = 0; i < args.size(); i++ )
{
getSymbolTable().putSymbol( args.get( i ) );
argTypes[i] = args.get( i ).getType();
}
}
else
{
argTypes = IType.EMPTY_ARRAY;
args = Collections.emptyList();
pushExpression( new ParameterListClause() );
setLocation( iOffsetParamList, iLineParamList, iColumnParamList, getTokenizer().getTokenStart() <= iOffsetParamList, true );
popExpression();
}
match( null, ')' );
if( match( null, ":", SourceCodeTokenizer.TT_OPERATOR ) )
{
getOwner().parseTypeLiteral();
Expression expression = popExpression();
verify( expression, false, Res.MSG_NO_TYPE_AFTER_CONSTRUCTOR );
}
StatementList stmtList;
int iOffset = getOwner().getTokenizer().getTokenStart();
int iLineNum = getOwner().getTokenizer().getLineNumber();
int iColumn = getOwner().getTokenizer().getTokenColumn();
FunctionType ft = new FunctionType( gsClass.getRelativeName(), gsClass, argTypes );
ft.setScriptPart( getOwner().getScriptPart() );
getOwner().pushParsingFunction( ft );
DynamicFunctionSymbol dfsDecl = findConstructorFunction( gsClass, DynamicFunctionSymbol.getSignatureName( strFunctionName, args ) );
dfsDecl = (dfsDecl == null || dfsDecl.getType() == GosuTypes.DEF_CTOR_TYPE()) ? null : dfsDecl;
functionStmt = dfsDecl == null ? functionStmt : dfsDecl.getDeclFunctionStmt();
verify( functionStmt, dfsDecl != null, Res.MSG_EXPECTING_NAME_FUNCTION_DEF );
if( verify( functionStmt, match( null, '{' ), Res.MSG_EXPECTING_OPEN_BRACE_FOR_CONSTRUCTOR_DEF ) )
{
IGosuClassInternal superClass = gsClass.getSuperClass();
if( superClass != null )
{
// If it's an enum, there's no default super constructor: the enum class extends the Enum java class
// which requires a String and an int. Those arguments are automatically generated by the compiler.
if( gsClass.getSupertype().getGenericType() != JavaTypes.ENUM() )
{
DynamicFunctionSymbol superDefaultConstructor = superClass.getDefaultConstructor();
verify( functionStmt,
match( T, Keyword.KW_super, true ) ||
match( T, Keyword.KW_this, true ) ||
(superDefaultConstructor != null && superClass.isAccessible( getGosuClass(), superDefaultConstructor )),
Res.MSG_NO_DEFAULT_CTOR_IN, superClass.getName() );
}
}
// No need to push an isolated scope here because there are no indexed
// symbol involved. This scope is only to resolve relative constructor
// calls from within a constructor e.g., this( foo ), super( foo ), etc.
boolean bMoreStatements = true;
MethodCallStatement initializer = null;
boolean bSuperOrThisCall = (match( T, Keyword.KW_super, true ) || match( T, Keyword.KW_this, true )) && getTokenizer().lookaheadType( 1, true ) == '(';
if( bSuperOrThisCall )
{
// Has to be static scope here since the JVM verifier prevents explicitly passing 'this' to super()
pushClassSymbols( true, scopeCache );
try
{
putSuperAndThisConstructorSymbols();
// Push static class members in case they are referenced as args in super( xxx ) or this( xxx )
bMoreStatements = getOwner().parseStatement();
initializer = (MethodCallStatement)popStatement();
}
finally
{
popClassSymbols();
}
}
else if( superClass != null )
{
MethodCallExpression e = new MethodCallExpression();
e.setParent( getClassStatement() );
DynamicFunctionSymbol defaultSuperConstructor;
// Enums implicitly call a super function that takes a String and an int, not a no-arg method
if( gsClass.getSupertype().getGenericType() == JavaTypes.ENUM() )
{
defaultSuperConstructor = superClass.getConstructorFunction( "Enum(java.lang.String, int)" );
}
else
{
defaultSuperConstructor = superClass.getDefaultConstructor();
}
if( defaultSuperConstructor != null )
{
e.setFunctionSymbol( new SuperConstructorFunctionSymbol( defaultSuperConstructor ) );
e.setArgs( null );
e.setType( GosuParserTypes.NULL_TYPE() );
initializer = new MethodCallStatement();
initializer.setMethodCall( e );
e.setParent( initializer );
initializer.setParent( functionStmt );
}
}
else
{
MethodCallExpression e = new MethodCallExpression();
e.setParent( getClassStatement() );
e.setFunctionSymbol( new InitConstructorFunctionSymbol( getSymbolTable() ) );
e.setArgs( null );
e.setType( GosuParserTypes.NULL_TYPE() );
initializer = new MethodCallStatement();
initializer.setMethodCall( e );
e.setParent( initializer );
initializer.setParent( functionStmt );
}
ArrayList<Statement> statements = new ArrayList<Statement>( 8 );
if( bMoreStatements )
{
pushClassSymbols( false, scopeCache );
getOwner().pushParsingAbstractConstructor( getClassStatement().getGosuClass().isAbstract() );
getSymbolTable().pushScope();
try
{
getSymbolTable().putSymbol( new Symbol( Keyword.KW_this.getName(), TypeLord.getConcreteType( gsClass ), getSymbolTable(), null ) );
getSymbolTable().putSymbol( new Symbol( Keyword.KW_super.getName(),
superClass == null ? IGosuClassInternal.Util.getGosuClassFrom( JavaTypes.OBJECT() ) :
superClass, getSymbolTable(), null ) );
getOwner().parseStatementsAndDetectUnreachable( statements );
}
finally
{
getSymbolTable().popScope();
getOwner().popParsingAbstractConstructor();
popClassSymbols();
}
}
verify( functionStmt, match( null, '}' ), Res.MSG_EXPECTING_CLOSE_BRACE_FOR_CONSTRUCTOR_DEF );
stmtList = new StatementList( getSymbolTable() );
stmtList.setStatements( statements );
Statement statement = isDontOptimizeStatementLists() ? stmtList : stmtList.getSelfOrSingleStatement();
if( statement == stmtList )
{
pushStatement( statement );
setLocation( iOffset, iLineNum, iColumn );
popStatement();
}
if( dfsDecl != null )
{
dfsDecl.setArgs( args );
dfsDecl.setValueDirectly( statement );
dfsDecl.setInitializer( initializer );
dfsDecl.getModifierInfo().setAnnotations( defnAnnotations );
}
}
else
{
eatStatementBlock( functionStmt, Res.MSG_EXPECTING_OPEN_BRACE_FOR_FUNCTION_DEF );
NotAStatement nas = new NotAStatement();
pushStatement( nas );
setLocation( iOffset, iLineNum, iColumn );
popStatement();
if( dfsDecl != null )
{
dfsDecl.setArgs( args );
dfsDecl.setValueDirectly( nas);
dfsDecl.getModifierInfo().setAnnotations( defnAnnotations );
}
}
getOwner().pushDynamicFunctionSymbol( dfsDecl );
if( functionStmt != null )
{
functionStmt.setDynamicFunctionSymbol( dfsDecl );
pushStatement( functionStmt );
}
return functionStmt;
}
finally
{
getSymbolTable().popScope();
if( getOwner().isParsingFunction() )
{
getOwner().popParsingFunction();
}
}
}
private DynamicFunctionSymbol findConstructorFunction( IGosuClassInternal gsClass, String signatureName )
{
gsClass.compileDeclarationsIfNeeded();
DynamicFunctionSymbol dfs = gsClass.getParseInfo().getConstructorFunctions().get( signatureName );
if( dfs != null && dfs.getValueDirectly() != null )
{
dfs = GosuParser.assignPossibleDuplicateDfs( dfs, gsClass.getParseInfo().getConstructorFunctions().values() );
}
return dfs;
}
/**
* Alias super's ctors and this class's ctors as super(xxx) and this(xxx).
*/
private void putSuperAndThisConstructorSymbols()
{
IGosuClassInternal thisClass = getGosuClass();
IGosuClassInternal superClass = thisClass.getSuperClass();
if( superClass != null )
{
for( DynamicFunctionSymbol dfs : superClass.getConstructorFunctions() )
{
if( superClass.isAccessible( getGosuClass(), dfs ) )
{
dfs = new SuperConstructorFunctionSymbol( superClass.isParameterizedType()
? dfs.getParameterizedVersion( superClass )
: dfs );
getSymbolTable().putSymbol( dfs );
getOwner().putDfsDeclInSetByName( dfs );
}
}
}
for( DynamicFunctionSymbol dfs : thisClass.getConstructorFunctions() )
{
dfs = new ThisConstructorFunctionSymbol( dfs );
getSymbolTable().putSymbol( dfs );
getOwner().putDfsDeclInSetByName( dfs );
}
}
private boolean isCyclicInheritance( IType superType, IGosuClassInternal gsClass )
{
if( TypeLord.getPureGenericType( superType ) == gsClass )
{
return true;
}
if( superType != null && superType instanceof IGosuClassInternal )
{
if( isCyclicInheritance( ((IGosuClassInternal)superType).getSuperClass(), gsClass ) )
{
return true;
}
if( isCyclicInheritance( ((IGosuClassInternal)superType).getEnclosingType(), gsClass ) )
{
return true;
}
}
return superType instanceof IGosuClassInternal &&
isCyclicInterfaceInheritance( (IGosuClassInternal)superType, gsClass );
}
private boolean isCyclicInterfaceInheritance( IGosuClassInternal gsExtendee, IGosuClass gsExtendor )
{
if( gsExtendee == gsExtendor )
{
return true;
}
IType[] interfaces = gsExtendee.getInterfaces();
for( int i = 0; i < interfaces.length; i++ )
{
IType type = interfaces[i];
if( type instanceof ErrorType )
{
return false;
}
IGosuClassInternal gsClass = IGosuClassInternal.Util.getGosuClassFrom( type );
if( isCyclicInterfaceInheritance( gsClass, gsExtendor ) )
{
return true;
}
}
return false;
}
@Override
IGosuClassInternal getGosuClass()
{
return (IGosuClassInternal)super.getGosuClass();
}
@Override
public String toString()
{
IGosuClassInternal gosuClass = getGosuClass();
return "Parsing Class: " + (gosuClass == null ? "null" : gosuClass.getName());
}
}