/* * Copyright 2013 Guidewire Software, Inc. */ package gw.internal.gosu.parser; import gw.config.CommonServices; import gw.fs.IFile; import gw.internal.gosu.ir.transform.util.IRTypeResolver; import gw.internal.gosu.parser.expressions.*; import gw.internal.gosu.parser.statements.ArrayAssignmentStatement; import gw.internal.gosu.parser.statements.AssertStatement; import gw.internal.gosu.parser.statements.AssignmentStatement; import gw.internal.gosu.parser.statements.BeanMethodCallStatement; import gw.internal.gosu.parser.statements.BlockInvocationStatement; import gw.internal.gosu.parser.statements.BreakStatement; import gw.internal.gosu.parser.statements.CaseClause; import gw.internal.gosu.parser.statements.CatchClause; import gw.internal.gosu.parser.statements.ClasspathStatement; import gw.internal.gosu.parser.statements.ContinueStatement; import gw.internal.gosu.parser.statements.DelegateStatement; import gw.internal.gosu.parser.statements.DoWhileStatement; import gw.internal.gosu.parser.statements.EvalStatement; import gw.internal.gosu.parser.statements.ForEachStatement; import gw.internal.gosu.parser.statements.FunctionStatement; import gw.internal.gosu.parser.statements.HideFieldNoOpStatement; import gw.internal.gosu.parser.statements.IfStatement; import gw.internal.gosu.parser.statements.LoopStatement; import gw.internal.gosu.parser.statements.MapAssignmentStatement; import gw.internal.gosu.parser.statements.MemberAssignmentStatement; import gw.internal.gosu.parser.statements.MethodCallStatement; import gw.internal.gosu.parser.statements.NamespaceStatement; import gw.internal.gosu.parser.statements.NewStatement; 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.SwitchStatement; import gw.internal.gosu.parser.statements.SyntheticMemberAccessStatement; import gw.internal.gosu.parser.statements.ThrowStatement; import gw.internal.gosu.parser.statements.TryCatchFinallyStatement; import gw.internal.gosu.parser.statements.TypeLoaderStatement; import gw.internal.gosu.parser.statements.UsesStatement; import gw.internal.gosu.parser.statements.UsesStatementList; import gw.internal.gosu.parser.statements.UsingStatement; import gw.internal.gosu.parser.statements.VarStatement; import gw.internal.gosu.parser.statements.WhileStatement; import gw.internal.gosu.parser.types.ConstructorType; import gw.internal.gosu.template.TemplateGenerator; import gw.internal.gosu.util.StringUtil; import gw.lang.IReentrant; import gw.lang.annotation.UsageTarget; import gw.lang.function.IBlock; import gw.lang.ir.IRType; import gw.lang.parser.ExternalSymbolMapForMap; import gw.lang.parser.GlobalScope; import gw.lang.parser.GosuParserFactory; import gw.lang.parser.GosuParserTypes; import gw.lang.parser.IBlockClass; import gw.lang.parser.ICapturedSymbol; import gw.lang.parser.ICoercer; import gw.lang.parser.ICoercionManager; import gw.lang.parser.IDynamicFunctionSymbol; import gw.lang.parser.IDynamicPropertySymbol; import gw.lang.parser.IDynamicSymbol; import gw.lang.parser.IExpression; import gw.lang.parser.IFileContext; import gw.lang.parser.IFunctionSymbol; import gw.lang.parser.IGosuParser; import gw.lang.parser.IGosuValidator; import gw.lang.parser.IHasInnerClass; import gw.lang.parser.IHasType; import gw.lang.parser.IInjectedSymbol; import gw.lang.parser.ILanguageLevel; import gw.lang.parser.IParseIssue; import gw.lang.parser.IParseResult; import gw.lang.parser.IParseTree; import gw.lang.parser.IParsedElement; import gw.lang.parser.IParsedElementWithAtLeastOneDeclaration; import gw.lang.parser.IParserState; import gw.lang.parser.IResolvingCoercer; import gw.lang.parser.IScriptPartId; import gw.lang.parser.ISource; import gw.lang.parser.ISourceCodeTokenizer; import gw.lang.parser.ISymbol; import gw.lang.parser.ISymbolTable; import gw.lang.parser.IToken; import gw.lang.parser.ITokenizerInstructor; import gw.lang.parser.ITypeUsesMap; import gw.lang.parser.Keyword; import gw.lang.parser.MemberAccessKind; import gw.lang.parser.ParserOptions; import gw.lang.parser.PostCompilationAnalysis; import gw.lang.parser.ScriptabilityModifiers; import gw.lang.parser.SourceCodeReader; import gw.lang.parser.StandardCoercionManager; import gw.lang.parser.StandardScope; import gw.lang.parser.SymbolType; import gw.lang.parser.ThreadSafeSymbolTable; import gw.lang.parser.TypeVarToTypeMap; import gw.lang.parser.coercers.FunctionToInterfaceCoercer; import gw.lang.parser.coercers.IdentityCoercer; import gw.lang.parser.exceptions.DoesNotOverrideFunctionException; import gw.lang.parser.exceptions.ErrantGosuClassException; import gw.lang.parser.exceptions.ImplicitCoercionError; import gw.lang.parser.exceptions.NoCtorFoundException; import gw.lang.parser.exceptions.ParseException; import gw.lang.parser.exceptions.ParseIssue; import gw.lang.parser.exceptions.ParseResultsException; import gw.lang.parser.exceptions.ParseWarning; import gw.lang.parser.exceptions.ParseWarningForDeprecatedMember; import gw.lang.parser.exceptions.WrongNumberOfArgsException; import gw.lang.parser.expressions.IArithmeticExpression; import gw.lang.parser.expressions.IBlockInvocation; import gw.lang.parser.expressions.IImplicitTypeAsExpression; import gw.lang.parser.expressions.IInitializerExpression; import gw.lang.parser.expressions.ILiteralExpression; import gw.lang.parser.expressions.IParenthesizedExpression; import gw.lang.parser.expressions.IProgram; import gw.lang.parser.expressions.ISynthesizedMemberAccessExpression; import gw.lang.parser.expressions.ITypeLiteralExpression; import gw.lang.parser.expressions.ITypeVariableDefinition; import gw.lang.parser.expressions.ITypeVariableDefinitionExpression; import gw.lang.parser.expressions.IVarStatement; import gw.lang.parser.resources.Res; import gw.lang.parser.resources.ResourceKey; import gw.lang.parser.statements.IClasspathStatement; import gw.lang.parser.statements.ITerminalStatement; import gw.lang.parser.statements.ITypeLoaderStatement; import gw.lang.parser.statements.IUsesStatement; import gw.lang.parser.statements.IUsesStatementList; import gw.lang.parser.statements.TerminalType; import gw.lang.parser.template.TemplateParseException; import gw.lang.reflect.FeatureManager; import gw.lang.reflect.FunctionType; import gw.lang.reflect.IBlockType; import gw.lang.reflect.IConstructorHandler; import gw.lang.reflect.IConstructorInfo; import gw.lang.reflect.IConstructorType; import gw.lang.reflect.IEnumType; import gw.lang.reflect.IErrorType; import gw.lang.reflect.IFunctionType; import gw.lang.reflect.IInvocableType; import gw.lang.reflect.IMetaType; import gw.lang.reflect.IMethodInfo; import gw.lang.reflect.INamespaceType; import gw.lang.reflect.IOptionalParamCapable; import gw.lang.reflect.IParameterInfo; import gw.lang.reflect.IPlaceholder; import gw.lang.reflect.IPropertyInfo; import gw.lang.reflect.IRelativeTypeInfo; import gw.lang.reflect.IScriptabilityModifier; import gw.lang.reflect.IType; import gw.lang.reflect.ITypeInfo; import gw.lang.reflect.ITypeVariableType; import gw.lang.reflect.MethodList; import gw.lang.reflect.MethodScore; import gw.lang.reflect.Modifier; import gw.lang.reflect.TypeInfoUtil; import gw.lang.reflect.TypeSystem; import gw.lang.reflect.gs.ClassType; import gw.lang.reflect.gs.GosuClassTypeLoader; import gw.lang.reflect.gs.ICompilableType; import gw.lang.reflect.gs.IGenericTypeVariable; import gw.lang.reflect.gs.IGosuArrayClass; import gw.lang.reflect.gs.IGosuClass; import gw.lang.reflect.gs.IGosuEnhancement; import gw.lang.reflect.gs.IGosuFragment; import gw.lang.reflect.gs.IGosuProgram; import gw.lang.reflect.gs.IGosuVarPropertyInfo; 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.lang.reflect.module.IModule; import gw.util.DynamicArray; import gw.util.GosuCollectionUtil; import gw.util.GosuExceptionUtil; import gw.util.GosuObjectUtil; import gw.util.SpaceEfficientHashMap; import java.io.Closeable; import java.io.IOException; import java.io.StringReader; import java.math.BigDecimal; import java.math.BigInteger; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Stack; @SuppressWarnings({"ThrowableInstanceNeverThrown"}) public final class GosuParser extends ParserBase implements IGosuParser { private static final IType AMBIGUOUS_TYPE = ErrorType.getInstance( "Ambiguous Type" ); public static final IType PENDING_BOUNDING_TYPE = ErrorType.getInstance( "Pending Bounding Type"); public static ErrorType notfound = ErrorType.getInstance( "_notfound_" ); private SourceCodeTokenizer _tokenizer; private ISymbolTable _symTable; private boolean _bParsed; private Stack<ParsedElement> _stack; private Stack<DynamicFunctionSymbol> _stackDFS; private List<ParseTree> _locations; private Program _parsingProgram; private ArrayList<FunctionType> _parsingFunctions; private ArrayList<VarStatement> _parsingFieldInitializer; private Map<String, Set<IFunctionSymbol>> _dfsDeclByName; private ITypeUsesMap _typeUsesMap; private String _strNamespace; private ITokenizerInstructor _tokenizerInstructor; private IScriptabilityModifier _scriptabilityConstraint; private int _iBreakOk; private int _iContinueOk; int _iReturnOk; private Stack<IScriptPartId> _scriptPartIdStack; private Map<String, ITypeVariableDefinition> _typeVarsByName; private Stack<List<IType>> _inferredContextStack = new Stack<List<IType>>(); private boolean _bThrowForWarnings; private boolean _bStudioEditorParser; private boolean _bWarnOnCaseIssue; private Stack<Boolean> _parsingAbstractConstructor; private ContextInferenceManager _ctxInferenceMgr = new ContextInferenceManager(); private Stack<IType> _blockReturnTypeStack = new Stack<IType>(); private Stack<Boolean> _parsingStaticFeature; private boolean _bCaptureSymbolsForEval; private boolean _parsingAnnotation; private int _ignoreTypeDeprecation; private boolean _bGenRootExprAccess; private boolean _bProgramCallFunction; private Map<IScriptPartId, Map<String, IType>> _typeCache; private int _iStmtDepth; private List<ParseTree> _savedLocations; private enum JavaAnnotationParsingState { NOT_PARSING_ENUM, PARSING_JAVA_NON_VALUE_ENUM, PARSING_JAVA_VALUE_ENUM } GosuParser( ISymbolTable symTable, IScriptabilityModifier scriptabilityConstraint ) { this(symTable, scriptabilityConstraint, CommonServices.getEntityAccess().getDefaultTypeUses()); } GosuParser( ISymbolTable symTable, IScriptabilityModifier scriptabilityConstraint, ITypeUsesMap tuMap ) { super(); setOwner( this ); setWarnOnCaseIssue( Settings.WARN_ON_CASE_DEFAULT.get() == Boolean.TRUE); setEditorParser( Settings.IDE_EDITOR_PARSER_DEFAULT.get() == Boolean.TRUE); _symTable = symTable; //noinspection unchecked _typeUsesMap = tuMap.copy(); _scriptabilityConstraint = scriptabilityConstraint; _dfsDeclByName = new HashMap<String, Set<IFunctionSymbol>>(); _stack = new Stack<ParsedElement>(); _stackDFS = new Stack<DynamicFunctionSymbol>(); _locations = new ArrayList<ParseTree>(); _parsingFunctions = new ArrayList<FunctionType>(); _parsingFieldInitializer = new ArrayList<VarStatement>(); _typeVarsByName = new HashMap<String, ITypeVariableDefinition>( 2 ); _parsingStaticFeature = new Stack<Boolean>(); _parsingAbstractConstructor = new Stack<Boolean>(); _typeCache = new HashMap<IScriptPartId, Map<String, IType>>(); _bParsed = false; _iReturnOk = 1; } @Override public void setValidator( IGosuValidator validator ) { super.setValidator( validator ); } //------------------------------------------------------------------------------ // -- IGosuParser implementation -- public IScriptPartId getScriptPart() { if( _scriptPartIdStack == null || _scriptPartIdStack.isEmpty() ) { return null; } return _scriptPartIdStack.peek(); } public Stack<IScriptPartId> getScriptPartIdStack() { return _scriptPartIdStack; } public void pushScriptPart( IScriptPartId partId ) { if( _scriptPartIdStack == null ) { _scriptPartIdStack = new Stack<IScriptPartId>(); } _scriptPartIdStack.push( partId ); } void popScriptPart( IScriptPartId partId ) { IScriptPartId top = _scriptPartIdStack.pop(); if( top != partId ) { throw new IllegalStateException( "Unbalanced push/pop script id" ); } } public void setScript( CharSequence source ) { if( source == null ) { source = ""; } setScript( new SourceCodeReader( source ) ); } public void setScript( ISource src ) { if( src == null ) { setScript( (CharSequence)null ); return; } // ISourceCodeTokenizer tokenizer = src.getTokenizer(); // if( tokenizer == null ) { setScript( src.getSource() ); // src.setTokenizer( _tokenizer ); _tokenizer.setInstructor( _tokenizerInstructor ); if( _tokenizerInstructor != null ) { _tokenizerInstructor.setTokenizer( _tokenizer ); } else if( getGosuClass() instanceof IGosuTemplateInternal && ((IGosuTemplateInternal)getGosuClass()).getTokenizerInstructor() != null ) { _tokenizerInstructor = ((IGosuTemplateInternal)getGosuClass()).getTokenizerInstructor(); _tokenizerInstructor.setTokenizer( _tokenizer ); if( _tokenizer.getInstructor() == null ) { _tokenizer.setInstructor( _tokenizerInstructor ); } } } // else // { // _tokenizer = (SourceCodeTokenizer)tokenizer; // _tokenizer.setInstructor( _tokenizerInstructor ); // if( _tokenizerInstructor != null ) // { // _tokenizerInstructor.setTokenizer( _tokenizer ); // } // _tokenizer.reset(); // } reset(); } public void setScript( SourceCodeReader reader ) { if( _tokenizer == null ) { _tokenizer = new SourceCodeTokenizer( reader, _tokenizerInstructor ); // Initialize the tokenizer _tokenizer.wordChars( '_', '_' ); } else { _tokenizer.reset( reader ); } reset(); } public void resetScript() { _tokenizer.reset(); reset(); } private void reset() { _stack.clear(); _stackDFS.clear(); _dfsDeclByName.clear(); _typeUsesMap.clearNonDefaultTypeUses(); _strNamespace = null; _locations.clear(); _parsingFunctions.clear(); _parsingFieldInitializer.clear(); _typeVarsByName.clear(); setParsed( false ); } @Override protected String getScript() { return _tokenizer.getSource(); } @Override public ISymbolTable getSymbolTable() { return _symTable; } public void setSymbolTable( ISymbolTable symTable ) { _symTable = symTable; } public ITypeUsesMap getTypeUsesMap() { return _typeUsesMap; } public void setTypeUsesMap( ITypeUsesMap typeUsesMap ) { _typeUsesMap = typeUsesMap == null ? null : typeUsesMap.copyLocalScope(); } public String getNamespace() { return _strNamespace; } void setNamespace( String strNamespace ) { _strNamespace = strNamespace; if( _strNamespace != null ) { getTypeUsesMap().addToTypeUses( strNamespace + ".*" ); } } public IScriptabilityModifier getVisibilityConstraint() { return _scriptabilityConstraint; } public ITokenizerInstructor getTokenizerInstructor() { return _tokenizerInstructor; } public void setTokenizerInstructor( ITokenizerInstructor tokenizerInstructor ) { _tokenizerInstructor = tokenizerInstructor; if( _tokenizer != null ) { _tokenizer.setInstructor( _tokenizerInstructor ); } } public FunctionType peekParsingFunction() { return _parsingFunctions.get( 0 ); } FunctionType popParsingFunction() { return _parsingFunctions.remove( 0 ); } void pushParsingFunction( FunctionType functionType ) { _parsingFunctions.add( 0, functionType ); } public boolean isParsingFunction() { return _parsingFunctions.size() > 0; } public VarStatement peekParsingFieldInitializer() { return _parsingFieldInitializer.get( 0 ); } VarStatement popParsingFieldInitializer() { return _parsingFieldInitializer.remove( 0 ); } void pushParsingFieldInitializer( VarStatement VarStatement ) { _parsingFieldInitializer.add( 0, VarStatement ); } public boolean isParsingFieldInitializer() { return !isParsingFunction() && !isParsingBlock() && _parsingFieldInitializer.size() > 0; } Program peekParsingProgram() { return _parsingProgram; } boolean isParsingProgram() { return _parsingProgram != null; } public Statement parseStatements( IScriptPartId partId ) throws ParseResultsException { Statement stmt = parseStatements( partId, true, true ); assignRuntime( stmt, true, null, null, partId); return stmt; } private Statement parseStatements( IScriptPartId partId, boolean verify, boolean isolatedScope ) throws ParseResultsException { pushScriptPart( partId ); try { int iOffset = _tokenizer.getTokenStart(); int iLineNum = _tokenizer.getLineNumber(); int iColumn = getTokenizer().getTokenColumn(); _parseStatements( isolatedScope ); Statement stmtList = peekStatement(); verify( stmtList, match( null, SourceCodeTokenizer.TT_EOF ), Res.MSG_END_OF_STMT ); _bParsed = true; if( stmtList instanceof StatementList ) { setLocation( iOffset, iLineNum, iColumn, true ); } if( verify ) { verifyParsedElement( stmtList ); } return stmtList; } finally { popScriptPart( partId ); } } void _parseStatements( boolean isolatedScope ) { StatementList stmtList = new StatementList( _symTable ); _tokenizer.nextToken(); if( _tokenizer.getInstructor() == null ) { parseClasspathStatements(); parseTypeLoaderStatements(); } if ( isolatedScope ) { _symTable.pushScope(); } try { UsesStatementList usesStmtList = parseUsesStatementList( true, new Token() ); List<Statement> statements = new ArrayList<Statement>(); parseStatementsAndDetectUnreachable( statements ); if( usesStmtList != null ) { statements.add( 0, usesStmtList ); } stmtList.setStatements( statements ); pushStatement( isDontOptimizeStatementLists() ? stmtList : stmtList.getSelfOrSingleStatement() ); } finally { if ( isolatedScope ) { _symTable.popScope(); } } } void parseStatementsAndDetectUnreachable( List<Statement> statements ) { for( Statement stmt = null; parseStatement(); ) { stmt = popStatementAndDetectUnreachable( stmt, statements ); } } private Statement popStatementAndDetectUnreachable( Statement previousStatement, List<Statement> statements ) { Statement currentStatement = popStatement(); if( !(previousStatement instanceof ReturnStatement) || !(currentStatement instanceof NotAStatement) ) { boolean[] bAbsolute = {false}; verifyOrWarn( currentStatement, previousStatement == null || currentStatement.isNoOp() || previousStatement.getLeastSignificantTerminalStatement( bAbsolute ) == null || !bAbsolute[0], !CommonServices.getEntityAccess().isUnreachableCodeDetectionOn(), Res.MSG_UNREACHABLE_STMT ); } if( isParsingFunction() && !isParsingBlock() ) { IType returnType = peekParsingFunction().getReturnType(); if( previousStatement instanceof ReturnStatement && returnType == JavaTypes.pVOID() ) { if( currentStatement instanceof NotAStatement || ((currentStatement instanceof BeanMethodCallStatement || currentStatement instanceof MethodCallStatement || currentStatement instanceof MemberAssignmentStatement) && currentStatement.getLineNum() == previousStatement.getLineNum()) || (currentStatement instanceof NoOpStatement && isUnexpectedToken( currentStatement )) ) { if( isUnexpectedToken( currentStatement ) ) { currentStatement.clearParseExceptions(); } verify( currentStatement, false, Res.MSG_RETURN_VAL_FROM_VOID_FUNCTION ); } } } statements.add( currentStatement ); return currentStatement.isNoOp() ? previousStatement : currentStatement; } private boolean isUnexpectedToken( Statement statement ) { for( IParseIssue issue : statement.getParseExceptions() ) { if( issue.getMessageKey() == Res.MSG_UNEXPECTED_TOKEN ) { return true; } } return false; } public IProgram parseProgram( IScriptPartId partId ) throws ParseResultsException { return parseProgram( partId, true, null ); } public IProgram parseProgram( IScriptPartId partId, IType expectedExpressionType ) throws ParseResultsException { return parseProgram( partId, true, expectedExpressionType ); } public IProgram parseProgram( IScriptPartId partId, IType expectedExpressionType, IFileContext ctx, boolean assignRuntime ) throws ParseResultsException { return parseProgram( partId, true, false, expectedExpressionType, ctx, assignRuntime ); } public IProgram parseProgram( IScriptPartId partId, IType expectedExpressionType, IFileContext ctx, boolean assignRuntime, boolean bDoNotThrowParseResultsException ) throws ParseResultsException { return parseProgram( partId, true, false, expectedExpressionType, ctx, assignRuntime, bDoNotThrowParseResultsException ); } public IProgram parseProgram( IScriptPartId partId, boolean isolatedScope, IType expectedExpressionType ) throws ParseResultsException { return parseProgram( partId, true, false, expectedExpressionType, null, true ); } public IProgram parseProgram( IScriptPartId partId, boolean isolatedScope, boolean reallyIsolatedScope, IType expectedExpressionType, IFileContext ctx, boolean assignRuntime ) throws ParseResultsException { return parseProgram( partId, isolatedScope, reallyIsolatedScope, expectedExpressionType, ctx, assignRuntime, false ); } public IProgram parseProgram( IScriptPartId partId, boolean isolatedScope, boolean reallyIsolatedScope, IType expectedExpressionType, IFileContext ctx, boolean assignRuntime, boolean bDoNotThrowParseResultsException ) throws ParseResultsException { return parseProgram( partId, isolatedScope, reallyIsolatedScope, expectedExpressionType, ctx, assignRuntime, bDoNotThrowParseResultsException, null ); } public IProgram parseProgram( IScriptPartId partId, boolean isolatedScope, boolean reallyIsolatedScope, IType expectedExpressionType, IFileContext ctx, boolean assignRuntime, boolean bDoNotThrowParseResultsException, IType superType ) throws ParseResultsException { Program program = new Program(); program.setDeclaredReturnType(expectedExpressionType); _parsingProgram = program; pushScriptPart( partId ); try { try { _tokenizer.nextToken(); if( _tokenizer.getInstructor() == null ) { parseClasspathStatements(); } if (superType != null) { IGosuClassInternal superTypeGosuClass = GosuClass.Util.getGosuClassFrom(superType); superTypeGosuClass.putClassMembers(this, getSymbolTable(), null, false ); } if ( isolatedScope ) { if ( reallyIsolatedScope ) { _symTable.pushIsolatedScope( new GosuParserTransparentActivationContext( partId ) ); } else { _symTable.pushScope(); } } try { // // First just find and parse the function Declarations // for( ISymbol function = parseFunctionOrPropertyDeclaration( program, true, false ); function != null; function = parseFunctionOrPropertyDeclaration( program, true, false ) ) { _symTable.putSymbol( function ); } // // Next we have to reset the tokenizer to the beginning. // _tokenizer.reset(); _locations.clear(); // // Now parse the program as normal.... // // Note function definitions are parsed as no-op statements, but are // pushed onto the dynamic function symobl stack. Statement mainStatement = parseStatements( getScriptPart(), false, false ); // Map the parsed function definitions by name Map<String, DynamicFunctionSymbol> functionMap = new SpaceEfficientHashMap<String, DynamicFunctionSymbol>(); while( peekDynamicFunctionSymbol() != null ) { DynamicFunctionSymbol function = popDynamicFunctionSymbol(); functionMap.put( function.getName(), function ); } mainStatement.addParseIssues( program.getParseIssues() ); program.setMainStatement( mainStatement ); program.setFunctions( functionMap ); program.setLocation( mainStatement.getLocation() ); mainStatement.setParent( program ); verify( program, match( null, SourceCodeTokenizer.TT_EOF ), Res.MSG_END_OF_STMT ); _bParsed = true; PostCompilationAnalysis.maybeAnalyze( program ); if( !bDoNotThrowParseResultsException ) { verifyParsedElement( program ); } CompileTimeAnnotationHandler.postDefinitionVerification( mainStatement ); } finally { if( isolatedScope ) { _symTable.popScope(); } } } finally { _parsingProgram = null; } if( assignRuntime ) { assignRuntime( program, isolatedScope, ctx, superType, partId); } } finally { popScriptPart( partId ); } return program; } @Override public void setGenerateRootExpressionAccessForProgram( boolean bGenRootExprAccess ) { _bGenRootExprAccess = bGenRootExprAccess; } public boolean isGenerateRootExpressionAccessForProgram() { return _bGenRootExprAccess; } private void assignRuntime(ParsedElement elem, boolean bIsolatedScope, IFileContext context, IType superType, IScriptPartId partId) throws ParseResultsException { // if( isForStringLiteralTemplate() ) // { // return; // } if( elem.isCompileTimeConstant() ) { return; } if( bIsolatedScope ) { _symTable.pushScope(); } try { GosuProgramParser programParser = new GosuProgramParser(); ParserOptions options = new ParserOptions().withTypeUsesMap( getTypeUsesMap() ) .withExpectedType( elem.getReturnType() ) .withTokenizerInstructor( getTokenizerInstructor() ) .withSuperType( superType ) .withFileContext( context ) .withCtxInferenceMgr( _ctxInferenceMgr.copy() ) .withGenRootExprAccess( isGenerateRootExpressionAccessForProgram() ) .asThrowawayProgram() .withScriptPartId(partId); IParseResult result = programParser.parseExpressionOrProgram( getScript(), getSymbolTable(), options ); IGosuProgramInternal p = (IGosuProgramInternal)result.getProgram(); if( p == null ) { throw new IllegalStateException(); } elem.setGosuProgram( p ); if( partId != null ) { partId.setRuntimeType( p ); } } catch( Exception t ) { if( t instanceof ParseResultsException ) { if( isForStringLiteralTemplate() ) { //## todo: shouldn't be creating a GosuProgram for this case, but the apps don't start for some reason. // Uncomment code at top of method return; } throw (ParseResultsException)t; } throw (RuntimeException)t; } finally { if( bIsolatedScope ) { _symTable.popScope(); } } } private boolean isForStringLiteralTemplate() { return getScriptPart() != null && getScriptPart().getId() != null && (getScriptPart().getId().equals( TemplateGenerator.GS_TEMPLATE ) || getScriptPart().getId().equals( TemplateGenerator.GS_TEMPLATE_PARSED )) && getScriptPart().getContainingType() instanceof IGosuClass; } List<IClasspathStatement> parseClasspathStatements() { int iOffset = getTokenizer().getTokenStart(); int iLineNum = getTokenizer().getLineNumber(); int iColumn = getTokenizer().getLineOffset(); List<IClasspathStatement> returnList = new ArrayList<IClasspathStatement>(); while( match( null, Keyword.KW_classpath ) ) { ClasspathStatement cpStatement = new ClasspathStatement(); if( parseStringLiteralSeparately() ) { StringLiteral expression = (StringLiteral)popExpression(); String strVal = (String)expression.evaluate(); cpStatement.setClasspath( strVal ); if( strVal.contains( ";" ) ) { verifyOrWarn( cpStatement, false, true, Res.MSG_COMMA_IS_THE_CLASSPATH_SEPARATOR ); } } else { verify( cpStatement, false, Res.MSG_CLASSPATH_STATEMENT_EXPECTS_A_STRING ); } returnList.add( cpStatement ); pushStatement(cpStatement); try { setLocation( iOffset, iLineNum, iColumn ); if( getGosuClass() != null && ((IGosuProgramInternal)getGosuClass()).isParsingExecutableProgramStatements() ) { // Remove unwanted cp stmt resulting from parsing program on second pass for evaluate() method getLocationsList().remove( cpStatement.getLocation() ); } } finally { popStatement(); } iOffset = getTokenizer().getTokenStart(); iLineNum = getTokenizer().getLineNumber(); iColumn = getTokenizer().getLineOffset(); } return returnList; } List<ITypeLoaderStatement> parseTypeLoaderStatements() { int iOffset = getTokenizer().getTokenStart(); int iLineNum = getTokenizer().getLineNumber(); int iColumn = getTokenizer().getLineOffset(); List<ITypeLoaderStatement> returnList = new ArrayList<ITypeLoaderStatement>(); while( match( new Token(), Keyword.KW_typeloader ) ) { TypeLoaderStatement stmt = new TypeLoaderStatement(); parseTypeLiteral(); TypeLiteral expression = (TypeLiteral)popExpression(); stmt.setTypeLoader( expression.getType().getType() ); returnList.add( stmt ); pushStatement( stmt ); try { setLocation( iOffset, iLineNum, iColumn ); } finally { popStatement(); } iOffset = getTokenizer().getTokenStart(); iLineNum = getTokenizer().getLineNumber(); iColumn = getTokenizer().getLineOffset(); } return returnList; } @Override public ISymbol[] parseProgramFunctionsOrPropertyDecls(IScriptPartId partId, boolean bParseProperties, boolean bParseVars) throws ParseResultsException { Program program = new Program(); pushScriptPart( partId ); _tokenizer.nextToken(); _symTable.pushScope(); List<ISymbol> listFunctions; try { listFunctions = new ArrayList<ISymbol>(); // // Just find and parse the function Declarations // for( ISymbol function = parseFunctionOrPropertyDeclaration( program, bParseProperties, bParseVars); function != null; function = parseFunctionOrPropertyDeclaration( program, bParseProperties, bParseVars) ) { if (function instanceof IDynamicPropertySymbol) { IDynamicPropertySymbol property1 = (IDynamicPropertySymbol)function; String propertyName = property1.getName(); IDynamicPropertySymbol property2 = null; for (ISymbol s : listFunctions) { if (s instanceof IDynamicPropertySymbol && propertyName != null && propertyName.equals(s.getName())) { property2 = (IDynamicPropertySymbol)s; } } if (property2 == null) { listFunctions.add( property1 ); } else { property2.setGetterDfs(property1.getGetterDfs() != null ? property1.getGetterDfs() : property2.getGetterDfs()); property2.setSetterDfs(property1.getSetterDfs() != null ? property1.getSetterDfs() : property2.getSetterDfs()); } } else { listFunctions.add( function ); } } } finally { _symTable.popScope(); popScriptPart( partId ); } return listFunctions.toArray( new ISymbol[listFunctions.size()] ); } public Expression parseExp( IScriptPartId partId ) throws ParseResultsException { return parseExp( partId, true, null, true ); } public Expression parseExp( IScriptPartId partId, IType expectedExpressionType ) throws ParseResultsException { return parseExp( partId, true, expectedExpressionType, true ); } public Expression parseExp( IScriptPartId partId, IType expectedExpressionType, IFileContext context, boolean assignRuntime ) throws ParseResultsException { return parseExp( partId, true, expectedExpressionType, context, assignRuntime ); } private Expression parseExp( IScriptPartId partId, boolean isolatedScope, IType expectedExpressionType, boolean assignRuntime ) throws ParseResultsException { return parseExp(partId, isolatedScope, expectedExpressionType, null, assignRuntime); } private Expression parseExp( IScriptPartId partId, boolean isolatedScope, IType expectedExpressionType, IFileContext context, boolean assignRuntime ) throws ParseResultsException { pushScriptPart( partId ); _tokenizer.nextToken(); Expression expression; if ( isolatedScope ) { _symTable.pushScope(); } try { parseExpression(expectedExpressionType); expression = popExpression(); verify( expression, match( null, SourceCodeTokenizer.TT_EOF ), Res.MSG_END_OF_EXPRESSION ); _bParsed = true; verifyParsedElement( expression ); } finally { if ( isolatedScope ) { _symTable.popScope(); } popScriptPart( partId ); } if (assignRuntime) { assignRuntime( expression, isolatedScope, context, null, partId); } CompileTimeAnnotationHandler.postDefinitionVerification( expression ); return expression; } public IExpression parseExpOrProgram( IScriptPartId partId ) throws ParseResultsException { return parseExpOrProgram( partId, true, true ); } public IExpression parseExpOrProgram( IScriptPartId partId, boolean isolatedScope, boolean assignRuntime ) throws ParseResultsException { return parseExpOrProgram( partId, null, isolatedScope, assignRuntime ); } public IExpression parseExpOrProgram( IScriptPartId partId, IType typeExpected, boolean isolatedScope, boolean assignRuntime ) throws ParseResultsException { IExpression exp; try { exp = parseExp( partId, isolatedScope, typeExpected, assignRuntime ); } catch( ParseResultsException expressionParseResultException ) { boolean isProbablyProgram = !getTokenizer().isEOF(); //noinspection CaughtExceptionImmediatelyRethrown try { Map<String, Set<IFunctionSymbol>> dfsDeclByName = new HashMap<String, Set<IFunctionSymbol>>(_dfsDeclByName); resetScript(); _dfsDeclByName = dfsDeclByName; exp = parseProgram( partId, isolatedScope, typeExpected ); } catch( ParseResultsException programParseResultsException ) { if( isProbablyProgram ) { throw programParseResultsException; } else { // Note we can't just rethrow the original exception because we need // the locations etc. in the parser, so we have to reparse and let it throw. Map<String, Set<IFunctionSymbol>> dfsDeclByName = new HashMap<String, Set<IFunctionSymbol>>(_dfsDeclByName); resetScript(); _dfsDeclByName = dfsDeclByName; exp = parseExp( partId, isolatedScope, null, assignRuntime ); } } } return exp; } public TypeLiteral parseTypeLiteral( IScriptPartId partId ) throws ParseResultsException { pushScriptPart( partId ); try { _tokenizer.nextToken(); _symTable.pushScope(); try { parseTypeLiteral(); } finally { _symTable.popScope(); } Expression expression = popExpression(); verify( expression, match( null, SourceCodeTokenizer.TT_EOF ), Res.MSG_END_OF_EXPRESSION ); _bParsed = true; return (TypeLiteral)expression; } finally { popScriptPart( partId ); } } public boolean isParsed() { return _bParsed; } protected void setParsed( boolean bParsed ) { _bParsed = bParsed; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ // GosuParser methods @Override public SourceCodeTokenizer getTokenizer() { return _tokenizer; } @Override List<ParseTree> getLocationsList() { return _locations; } public List<IParseTree> getLocations() { return new ArrayList<IParseTree>( _locations ); } public ParseTree peekLocation() { if( _locations.isEmpty() ) { return null; } return _locations.get( _locations.size()-1 ); } public boolean hasWarnings() { return false; } public boolean isThrowParseResultsExceptionForWarnings() { return _bThrowForWarnings; } public void setThrowParseExceptionForWarnings( boolean bThrowParseExceptionForWarnings ) { _bThrowForWarnings = bThrowParseExceptionForWarnings; } //------------------------------------------------------------------------------ // expression // <conditional-expression> // void parseExpression() { parseExpression( (List<IType>)null ); } void parseExpression( IType contextType ) { parseExpression( contextType, true ); } void parseExpression( IType contextType, boolean bVerify ) { ArrayList<IType> ctxTypes = null; if( contextType != null ) { ctxTypes = new ArrayList<IType>(1); ctxTypes.add( contextType ); } parseExpression( ctxTypes ); if( bVerify && contextType != null ) { Expression expr = popExpression(); verifyComparable( contextType, expr, true ); expr = possiblyWrapWithImplicitCoercion( expr, contextType ); pushExpression( expr ); } } void parseExpression( List<IType> contextTypes ) { push_(contextTypes); try { int iOffset = _tokenizer.getTokenStart(); int iLineNum = _tokenizer.getLineNumber(); int iColumn = _tokenizer.getTokenColumn(); parseConditionalExpression(); convertNumberLiteralsToContextType(contextTypes); convertNullLiterals( contextTypes ); setLocation( iOffset, iLineNum, iColumn ); } finally { _inferredContextStack.pop(); } } private void push_(List<IType> contextTypes) { if (contextTypes != null && contextTypes.size() == 1) { IType contextType = contextTypes.get(0); if (contextType instanceof IBlockType) { IBlockType blockType = (IBlockType) contextType; IType[] blockParameterTypes = blockType.getParameterTypes(); } } _inferredContextStack.push( contextTypes ); } private void convertNullLiterals( List<IType> contextTypes ) { if( !_stack.isEmpty() && contextTypes != null && contextTypes.size() == 1) { Expression expression = peekExpression(); IType type = contextTypes.get( 0 ); if( expression instanceof NullExpression && !type.isPrimitive() ) { expression.setType( type ); } } } @SuppressWarnings({"ThrowableResultOfMethodCallIgnored"}) private void convertNumberLiteralsToContextType( List<IType> contextTypes ) { if( !_stack.isEmpty() ) { Expression expr = peekExpression(); UnaryExpression unary = null; if( expr instanceof UnaryExpression ) { unary = (UnaryExpression)expr; Expression subexpr = unary.getExpression(); if( subexpr instanceof NumericLiteral ) { expr = subexpr; } } if( expr instanceof NumericLiteral ) { // if only a single Number context type exists, consider this number literal to be of that type NumericLiteral literal = (NumericLiteral)expr; IType ctxType = getNumberContextType( contextTypes ); if( ctxType != null && !literal.isExplicitlyTyped() ) { String strValue = literal.getStrValue(); try { if( isPrefixNumericLiteral( strValue ) ) { strValue = parseIntOrLongOrBigInt( strValue ) + ""; } if( JavaTypes.pBYTE().equals( ctxType ) || JavaTypes.BYTE().equals( ctxType ) ) { literal.setValue( Byte.parseByte( strValue ) ); } else if( JavaTypes.pSHORT().equals( ctxType ) || JavaTypes.SHORT().equals( ctxType ) ) { literal.setValue( Short.parseShort( strValue ) ); } else if( JavaTypes.pINT().equals( ctxType ) || JavaTypes.INTEGER().equals( ctxType ) ) { literal.setValue( Integer.parseInt( strValue ) ); } else if( JavaTypes.pLONG().equals( ctxType ) || JavaTypes.LONG().equals( ctxType ) ) { literal.setValue( Long.parseLong( strValue ) ); } else if( JavaTypes.pFLOAT().equals( ctxType ) || JavaTypes.FLOAT().equals( ctxType ) ) { literal.setValue( Float.parseFloat( strValue ) ); } else if( JavaTypes.pDOUBLE().equals( ctxType ) || JavaTypes.DOUBLE().equals( ctxType ) ) { literal.setValue( Double.parseDouble( strValue ) ); } else if( JavaTypes.BIG_INTEGER().equals( ctxType ) ) { literal.setValue( new BigInteger( strValue ) ); } else if( JavaTypes.BIG_DECIMAL().equals( ctxType ) ) { literal.setValue( new BigDecimal( strValue ) ); } else if( literal.getType().isPrimitive() && (JavaTypes.OBJECT().equals( ctxType ) || (JavaTypes.NUMBER().equals( ctxType ) && ctxType.isAssignableFrom( TypeSystem.getBoxType( literal.getType() )))) ) { ctxType = TypeSystem.getBoxType( literal.getType() ); } else { return; } } catch( NumberFormatException ex ) { verify( literal, false, Res.MSG_IMPROPER_VALUE_FOR_NUMERIC_TYPE, strValue, ctxType.getName() ); } literal.setType( ctxType ); if( unary != null ) { unary.setType( ctxType ); } } } } } private IType getNumberContextType( List<IType> contextTypes ) { if( contextTypes == null ) { return null; } IType onlyNumberContextType = null; boolean bNumberCtxTypeFound = false; IType prior = null; for( IType contextType : contextTypes ) { if( prior != null && prior != contextType ) { // Ambiguous context types return null; } prior = contextType; IType compType = contextType.isPrimitive() ? TypeSystem.getBoxType( contextType ) : contextType; if( JavaTypes.NUMBER().isAssignableFrom( compType ) && !JavaTypes.NUMBER().equals( compType ) ) { if( onlyNumberContextType == null ) { onlyNumberContextType = contextType; bNumberCtxTypeFound = true; } else { onlyNumberContextType = null; break; } } } if( !bNumberCtxTypeFound ) { for( IType contextType : contextTypes ) { IType compType = contextType.isPrimitive() ? TypeSystem.getBoxType( contextType ) : contextType; if( JavaTypes.OBJECT().equals( compType ) || JavaTypes.NUMBER().equals( compType ) ) { if( onlyNumberContextType == null ) { onlyNumberContextType = contextType; } else { onlyNumberContextType = null; break; } } } } return onlyNumberContextType; } //------------------------------------------------------------------------------ // conditional-expression // <conditional-or-expression> // <conditional-expression> ? <conditional-expression> : <conditional-expression> // // Left recursion removed is: // // conditional-expression // <conditional-or-expression> <conditional-expression2> // // conditional-expression2 // ? <conditional-expression> : <conditional-expression> // <null> // void parseConditionalExpression() { int iOffset = _tokenizer.getTokenStart(); int iLineNum = _tokenizer.getLineNumber(); int iColumn = _tokenizer.getTokenColumn(); _parseConditionalExpression(); setLocation( iOffset, iLineNum, iColumn ); } void _parseConditionalExpression() { // <conditional-or-expression> _ctxInferenceMgr.pushCtx(); parseConditionalOrExpression(); boolean foundCondExpr = false; // <conditional-or-expression> ? <conditional-expression> : <conditional-expression> Token T = new Token(); if( match( T, "?", SourceCodeTokenizer.TT_OPERATOR ) ) { foundCondExpr = true; ConditionalTernaryExpression cte = new ConditionalTernaryExpression(); Expression condition = popExpression(); if( !JavaTypes.pBOOLEAN().equals( condition.getType() ) && !JavaTypes.BOOLEAN().equals( condition.getType() ) ) { verifyOrWarn( condition, false, CommonServices.getEntityAccess().getLanguageLevel().allowAllImplicitCoercions(), Res.MSG_CONDITIONAL_EXPRESSION_EXPECTS_BOOLEAN ); } condition = possiblyWrapWithImplicitCoercion( condition, JavaTypes.pBOOLEAN() ); verifyComparable( JavaTypes.pBOOLEAN(), condition, true ); parseConditionalExpression(); Expression first = popExpression(); verify( cte, match( T, ":", SourceCodeTokenizer.TT_OPERATOR ), Res.MSG_EXPECTING_COLON_TERNARY ); _ctxInferenceMgr.popCtx( false ); parseConditionalExpression(); Expression second = popExpression(); _ctxInferenceMgr.pushCtx(); List<IType> list = new ArrayList<IType>(); first = wrapExpressionIfNeeded( first, second ); second = wrapExpressionIfNeeded( second, first ); //Do not allow literals that are coercable to the type of the other side to modify the //type of the expression if( !(first instanceof ILiteralExpression) || GosuParserTypes.NULL_TYPE().equals(second.getType()) || second instanceof ILiteralExpression || !CommonServices.getCoercionManager().canCoerce( second.getType(), first.getType() ) ) { list.add( first.getType() ); } if( !(second instanceof ILiteralExpression) || GosuParserTypes.NULL_TYPE().equals(first.getType()) || first instanceof ILiteralExpression || !CommonServices.getCoercionManager().canCoerce( first.getType(), second.getType() ) ) { list.add( second.getType() ); } IType type = TypeLord.findLeastUpperBound( list ); cte.setType( type ); cte.setCondition( condition ); cte.setFirst( possiblyWrapWithImplicitCoercion( first, type ) ); cte.setSecond( possiblyWrapWithImplicitCoercion( second, type ) ); pushExpression( cte ); } else if( match( T, "?:", SourceCodeTokenizer.TT_OPERATOR ) ) { foundCondExpr = true; ConditionalTernaryExpression cte = new ConditionalTernaryExpression(); Expression first = popExpression(); verify( first, !first.getType().isPrimitive(), Res.MSG_EXPECTING_REFERENCE_TYPE ); _ctxInferenceMgr.popCtx( false ); parseConditionalExpression(); Expression second = popExpression(); _ctxInferenceMgr.pushCtx(); List<IType> list = new ArrayList<IType>(); //Do not allow literals that are coercable to the type of the other side to modify the //type of the expression if( !(first instanceof ILiteralExpression) || GosuParserTypes.NULL_TYPE().equals(second.getType()) || second instanceof ILiteralExpression || !CommonServices.getCoercionManager().canCoerce( second.getType(), first.getType() ) ) { list.add( first.getType() ); } if( !(second instanceof ILiteralExpression) || GosuParserTypes.NULL_TYPE().equals(first.getType()) || first instanceof ILiteralExpression || !CommonServices.getCoercionManager().canCoerce( first.getType(), second.getType() ) ) { list.add( second.getType() ); } IType type = TypeLord.findLeastUpperBound( list ); cte.setType( type ); cte.setCondition( null ); cte.setFirst( possiblyWrapWithImplicitCoercion( first, type ) ); cte.setSecond( possiblyWrapWithImplicitCoercion( second, type ) ); pushExpression( cte ); } _ctxInferenceMgr.popCtx( !foundCondExpr ); } private Expression wrapExpressionIfNeeded( Expression first, Expression second ) { if( first.getType() == JavaTypes.pVOID() && second.getType().isPrimitive() ) { return possiblyWrapWithImplicitCoercion( first, TypeSystem.getBoxType( second.getType() ) ); } else if( second.getType() == JavaTypes.pVOID() && first.getType().isPrimitive() ) { return possiblyWrapWithImplicitCoercion( first, TypeSystem.getBoxType( first.getType() ) ); } return first; } //------------------------------------------------------------------------------ // conditional-or-expression // <conditional-and-expression> // <conditional-or-expression> || <conditional-and-expression> // // Left recursion removed is: // // conditional-or-expression // <conditional-and-expression> <conditional-or-expression2> // // conditional-or-expression2 // || <conditional-and-expression> // <null> // void parseConditionalOrExpression() { int iOffset = _tokenizer.getTokenStart(); int iLineNum = _tokenizer.getLineNumber(); int iColumn = getTokenizer().getTokenColumn(); _parseConditionalOrExpression(); setLocation( iOffset, iLineNum, iColumn ); } void _parseConditionalOrExpression() { parseConditionalAndExpression(); // <conditional-or-expression2> boolean foundOr = false; do { Token T = new Token(); if( match( T, "||", SourceCodeTokenizer.TT_OPERATOR ) || match( null, Keyword.KW_or ) ) { _ctxInferenceMgr.clear(); foundOr = true; parseConditionalAndExpression(); ConditionalOrExpression e = new ConditionalOrExpression(); Expression rhs = popExpression(); verifyComparable( JavaTypes.pBOOLEAN(), rhs, true, true ); rhs = possiblyWrapWithImplicitCoercion( rhs, JavaTypes.pBOOLEAN() ); Expression lhs = popExpression(); verifyComparable( JavaTypes.pBOOLEAN(), lhs, true, true ); lhs = possiblyWrapWithImplicitCoercion( lhs, JavaTypes.pBOOLEAN() ); e.setLHS( lhs ); e.setRHS( rhs ); pushExpression( e ); } else { // The <null> case break; } } while( true ); if (foundOr) { _ctxInferenceMgr.clear(); } } //------------------------------------------------------------------------------ // conditional-and-expression // <equality-expression> // <conditional-and-expression> && <equality-expression> // // Left recursion removed is: // // conditional-and-expression // <equality-expression> <conditional-and-expression2> // // conditional-and-expression2 // && <equality-expression> // <null> // void parseConditionalAndExpression() { int iOffset = _tokenizer.getTokenStart(); int iLineNum = _tokenizer.getLineNumber(); int iColumn = getTokenizer().getTokenColumn(); _parseConditionalAndExpression(); setLocation( iOffset, iLineNum, iColumn ); } void _parseConditionalAndExpression() { parseBitwiseOrExpression(); // <conditional-and-expression2> do { Token T = new Token(); if( match( T, "&&", SourceCodeTokenizer.TT_OPERATOR ) || match( null, Keyword.KW_and ) ) { parseBitwiseOrExpression(); ConditionalAndExpression e = new ConditionalAndExpression(); Expression rhs = popExpression(); verifyComparable( JavaTypes.pBOOLEAN(), rhs, true, true ); rhs = possiblyWrapWithImplicitCoercion( rhs, JavaTypes.pBOOLEAN() ); Expression lhs = popExpression(); verifyComparable( JavaTypes.pBOOLEAN(), lhs, true, true ); lhs = possiblyWrapWithImplicitCoercion( lhs, JavaTypes.pBOOLEAN() ); e.setLHS( lhs ); e.setRHS( rhs ); pushExpression( e ); } else { // The <null> case break; } } while( true ); } //------------------------------------------------------------------------------ // bitwise-or-expression // <bitwise-xor-expression> // <bitwise-or-expression> | <bitwise-xor-expression> // // Left recursion removed is: // // bitwise-or-expression // <bitwise-xor-expression> <bitwise-or-expression2> // // bitwise-or-expression2 // | <bitwise-xor-expression> // <null> // void parseBitwiseOrExpression() { int iOffset = _tokenizer.getTokenStart(); int iLineNum = _tokenizer.getLineNumber(); int iColumn = getTokenizer().getTokenColumn(); _parseBitwiseOrExpression(); setLocation( iOffset, iLineNum, iColumn ); } void _parseBitwiseOrExpression() { parseBitwiseXorExpression(); // <bitwise-or-expression2> do { Token T = new Token(); if( match( T, "|", SourceCodeTokenizer.TT_OPERATOR ) ) { BitwiseOrExpression e = new BitwiseOrExpression(); Expression lhs = popExpression(); lhs = ensureOperandIntOrLong( lhs ); parseBitwiseXorExpression(); Expression rhs = popExpression(); verifyComparable( lhs.getType(), rhs, true, true ); rhs = possiblyWrapWithImplicitCoercion( rhs, lhs.getType() ); e.setLHS( lhs ); e.setRHS( rhs ); e.setType( resolveType( e, lhs.getType(), '|', rhs.getType() ) ); pushExpression( e ); } else { // The <null> case break; } } while( true ); } private Expression ensureOperandIntOrLong( Expression lhs ) { IType lhsType = lhs.getType(); if( verify( lhs, lhsType == JavaTypes.LONG() || lhsType == JavaTypes.pLONG() || lhsType == JavaTypes.INTEGER() || lhsType == JavaTypes.pINT() || lhsType == JavaTypes.SHORT() || lhsType == JavaTypes.pSHORT() || lhsType == JavaTypes.BYTE() || lhsType == JavaTypes.pBYTE(), Res.MSG_BITWISE_OPERAND_MUST_BE_INT_OR_LONG ) ) { lhsType = lhsType == JavaTypes.LONG() || lhsType == JavaTypes.pLONG() ? JavaTypes.pLONG() : JavaTypes.pINT(); lhs = possiblyWrapWithImplicitCoercion( lhs, lhsType ); } return lhs; } //------------------------------------------------------------------------------ // bitwise-xor-expression // <bitwise-and-expression> // <bitwise-xor-expression> ^ <bitwise-xor-expression> // // Left recursion removed is: // // bitwise-xor-expression // <bitwise-and-expression> <bitwise-xor-expression2> // // bitwise-xor-expression2 // ^ <bitwise-and-expression> // <null> // void parseBitwiseXorExpression() { int iOffset = _tokenizer.getTokenStart(); int iLineNum = _tokenizer.getLineNumber(); int iColumn = getTokenizer().getTokenColumn(); _parseBitwiseXorExpression(); setLocation( iOffset, iLineNum, iColumn ); } void _parseBitwiseXorExpression() { parseBitwiseAndExpression(); // <bitwise-xor-expression2> do { Token T = new Token(); if( match( T, "^", SourceCodeTokenizer.TT_OPERATOR ) ) { BitwiseXorExpression e = new BitwiseXorExpression(); Expression lhs = popExpression(); lhs = ensureOperandIntOrLong( lhs ); parseBitwiseAndExpression(); Expression rhs = popExpression(); verifyComparable( lhs.getType(), rhs, true, true ); rhs = possiblyWrapWithImplicitCoercion( rhs, lhs.getType() ); e.setLHS( lhs ); e.setRHS( rhs ); e.setType( resolveType( e, lhs.getType(), '^', rhs.getType() ) ); pushExpression( e ); } else { // The <null> case break; } } while( true ); } //------------------------------------------------------------------------------ // bitwise-and-expression // <equality-expression> // <bitwise-and-expression> & <equality-expression> // // Left recursion removed is: // // bitwise-and-expression // <equality-expression> <bitwise-and-expression2> // // bitwise-and-expression2 // ^ <equality-expression> // <null> // void parseBitwiseAndExpression() { int iOffset = _tokenizer.getTokenStart(); int iLineNum = _tokenizer.getLineNumber(); int iColumn = getTokenizer().getTokenColumn(); _parseBitwiseAndExpression(); setLocation( iOffset, iLineNum, iColumn ); } void _parseBitwiseAndExpression() { parseEqualityExpression(); // <bitwise-and-expression2> do { Token T = new Token(); if( match( T, "&", SourceCodeTokenizer.TT_OPERATOR ) ) { BitwiseAndExpression e = new BitwiseAndExpression(); Expression lhs = popExpression(); lhs = ensureOperandIntOrLong( lhs ); parseEqualityExpression(); Expression rhs = popExpression(); verifyComparable( lhs.getType(), rhs, true, true ); rhs = possiblyWrapWithImplicitCoercion( rhs, lhs.getType() ); e.setLHS( lhs ); e.setRHS( rhs ); e.setType( resolveType( e, lhs.getType(), '&', rhs.getType() ) ); pushExpression( e ); } else { // The <null> case break; } } while( true ); } //------------------------------------------------------------------------------ // equality-expression // <relational-expression> // <equality-expression> == <relational-expression> // <equality-expression> != <relational-expression> // // Left recursion removed is: // // equality-expression // <relational-expression> <equality-expression2> // // equality-expression2 // == <relational-expression> // != <relational-expression> // <null> // void parseEqualityExpression() { int iOffset = _tokenizer.getTokenStart(); int iLineNum = _tokenizer.getLineNumber(); int iColumn = getTokenizer().getTokenColumn(); _parseEqualityExpression(); setLocation( iOffset, iLineNum, iColumn ); } void _parseEqualityExpression() { parseRelationalExpression(); // <relational-expression2> do { Token T = new Token(); if( match( T, "===", SourceCodeTokenizer.TT_OPERATOR ) || match( T, "!==", SourceCodeTokenizer.TT_OPERATOR ) ) { IdentityExpression e = new IdentityExpression(); Expression lhs = popExpression(); List<IType> hashSet = new ArrayList<IType>(1); hashSet.add( lhs.getType() ); push_(hashSet); try { parseRelationalExpression(); } finally { _inferredContextStack.pop(); } Expression rhs = popExpression(); verifyComparable( lhs.getType(), rhs, true, true ); // // Wrap in implicit type-as to handle primitives // if( (!rhs.hasParseExceptions() && !lhs.hasParseExceptions()) && // (rhs.getType() != lhs.getType()) && // lhs.getType().isPrimitive() || rhs.getType().isPrimitive() ) // { // IType type = resolveType( lhs, lhs.getType(), '+', rhs.getType() ); // lhs = possiblyWrapWithImplicitCoercion( lhs, type ); // rhs = possiblyWrapWithImplicitCoercion( rhs, type ); // } e.setLHS( lhs ); e.setRHS( rhs ); e.setEquals( T._strValue.equals( "===" ) ); pushExpression( e ); } else if( match( T, "==", SourceCodeTokenizer.TT_OPERATOR ) || match( T, "!=", SourceCodeTokenizer.TT_OPERATOR ) || match( T, "<>", SourceCodeTokenizer.TT_OPERATOR ) ) { EqualityExpression e = new EqualityExpression(); Expression lhs = popExpression(); List<IType> hashSet = new ArrayList<IType>(1); hashSet.add( lhs.getType() ); push_(hashSet); try { parseRelationalExpression(); } finally { _inferredContextStack.pop(); } Expression rhs = popExpression(); rhs = verifyConditionalTypes( lhs, rhs ); //## see PL-9512 verifyCoercionSymmetry( e, lhs, rhs ); e.setLHS( lhs ); e.setRHS( rhs ); e.setEquals( T._strValue.equals( "==" ) ); warnOnSuspiciousEqualsOperator(e); pushExpression( e ); } else { // The <null> case break; } } while( true ); } private void warnOnSuspiciousEqualsOperator( EqualityExpression e ) { Expression lhs = e.getLHS(); Expression rhs = e.getRHS(); if( (isThisPointer( lhs ) && isEqualsArgReference( rhs )) || (isEqualsArgReference( lhs ) && isThisPointer( rhs )) ) { warn( e, false, Res.MSG_WARN_ON_SUSPICIOUS_THIS_COMPARISON ); } } private boolean isThisPointer( Expression expr ) { return expr != null && expr instanceof Identifier && ((Identifier)expr).getSymbol() != null && Keyword.KW_this.getName().equals( ((Identifier)expr).getSymbol().getName() ); } private boolean isEqualsArgReference( Expression expr ) { if( isParsingFunction() && expr != null && expr instanceof Identifier ) { FunctionType functionType = peekParsingFunction(); if( "equals".equals( functionType.getDisplayName() ) ) { if( functionType.getParameterTypes().length == 1 && functionType.getParameterTypes()[0] == JavaTypes.OBJECT() ) { ISymbol symbol = ((Identifier)expr).getSymbol(); if( symbol != null && functionType.getParameterNames()[0].equals( symbol.getName() ) ) { return true; } } } } return false; } private void verifyCoercionSymmetry( EqualityExpression e, Expression lhs, Expression rhs ) { if( !lhs.hasParseExceptions() && !rhs.hasParseExceptions() ) { boolean bHasCoercionWarning = false; for( IParseIssue p: rhs.getParseIssues() ) { if( p.getMessageKey() == Res.MSG_IMPLICIT_COERCION_WARNING ) { bHasCoercionWarning = true; break; } } ICoercionManager cocerionManager = CommonServices.getCoercionManager(); boolean bDontWarn = bHasCoercionWarning || // rhs has coercion warning, or ((lhs.getType() != JavaTypes.OBJECT() && rhs.getType() != JavaTypes.OBJECT()) || // neither side is Object, or (lhs.getType() == JavaTypes.pVOID() || rhs.getType() == JavaTypes.pVOID()) || // one side is "null", or (lhs.getType() != null && BeanAccess.isBeanType( lhs.getType() ) && // both sides are "beans" rhs.getType() != null && BeanAccess.isBeanType( rhs.getType() )) || // ... , or cocerionManager.resolveCoercerStatically( lhs.getType(), rhs.getType() ) == // coercer is symmetric cocerionManager.resolveCoercerStatically( rhs.getType(), lhs.getType() )); verifyOrWarn( e, bDontWarn, true, Res.MSG_ASYMMETRICAL_COMPARISON, lhs.getType(), rhs.getType() ); } } private void verifyRelationalOperandsComparable( BinaryExpression expr ) { boolean bComparable = false; IType lhsType = expr.getLHS().getType(); if( BeanAccess.isNumericType( lhsType ) ) { bComparable = true; } else if( lhsType == GosuParserTypes.DATETIME_TYPE() ) { bComparable = true; } else { IType rhsType = expr.getRHS().getType(); if( BeanAccess.isBeanType( lhsType ) ) { if( BeanAccess.isBeanType( rhsType ) ) { if( lhsType.isAssignableFrom( rhsType ) ) { if( JavaTypes.COMPARABLE().isAssignableFrom( lhsType ) ) { bComparable = true; } } } } if( !bComparable && (JavaTypes.CHAR_SEQUENCE().isAssignableFrom( lhsType ) || JavaTypes.CHAR_SEQUENCE().isAssignableFrom( rhsType )) ) { bComparable = true; } } verify( expr, bComparable, Res.MSG_RELATIONAL_OPERATOR_CANNOT_BE_APPLIED_TO_TYPE, expr.getOperator(), lhsType ); } private Expression verifyConditionalTypes( Expression lhs, Expression rhs ) { IType lhsType = lhs.getType(); IType rhsType = rhs.getType(); IType numberType = ParserBase.resolveType( lhsType, '>', rhsType ); if( numberType instanceof ErrorType || JavaTypes.IDIMENSION().isAssignableFrom( lhsType ) || JavaTypes.IDIMENSION().isAssignableFrom( rhsType ) ) { Expression wrappedRhs = verifyWithComparableDimension( rhs, lhsType ); if( wrappedRhs != null ) { return wrappedRhs; } // Not a number, verify types the general way verifyComparable( lhs.getType(), rhs, true, true ); } else { // Get coercion warnings if any verifyComparable( numberType, rhs, false, true ); verifyComparable( numberType, lhs, false, true ); } if( !(rhs instanceof ImplicitTypeAsExpression) && rhs.hasImmediateParseWarning( Res.MSG_IMPLICIT_COERCION_WARNING ) ) { rhs = possiblyWrapWithImplicitCoercion( rhs, lhs.getType() ); } return rhs; } private Expression verifyWithComparableDimension( Expression rhs, IType lhsType ) { if( JavaTypes.IDIMENSION().isAssignableFrom( lhsType ) && JavaTypes.COMPARABLE().isAssignableFrom( lhsType ) ) { for( IType iface : lhsType.getInterfaces() ) { if( JavaTypes.COMPARABLE().isAssignableFrom( iface.getGenericType() ) ) { IType type = iface.getTypeParameters()[0]; verifyComparable( type, rhs, true, true ); if( !rhs.hasParseExceptions() ) { rhs = possiblyWrapWithImplicitCoercion( rhs, type ); } return rhs; } } } return null; } //------------------------------------------------------------------------------ // relational-expression // <bitshift-expression> // <relational-expression> < <bitshift-expression> // <relational-expression> > <bitshift-expression> // <relational-expression> <= <bitshift-expression> // <relational-expression> >= <bitshift-expression> // <relational-expression> typeis <type-literal> // <relational-expression> as <type-literal> // // Left recursion removed from this *grammar* is: // // relational-expression // <bitshift-expression> <relational-expression2> // // relational-expression2 // < <bitshift-expression> // > <bitshift-expression> // <= <bitshift-expression> // >= <bitshift-expression> // typeis <type-literal> // as <type-literal> // <null> // void parseRelationalExpression() { int iOffset = _tokenizer.getTokenStart(); int iLineNum = _tokenizer.getLineNumber(); int iColumn = getTokenizer().getTokenColumn(); _parseRelationalExpression(); setLocation( iOffset, iLineNum, iColumn ); } void _parseRelationalExpression() { // <bitshift-expression> parseIntervalExpression(); // <relational-expression2> do { Token T = new Token(); if( match( T, "<", SourceCodeTokenizer.TT_OPERATOR ) || match( T, ">", SourceCodeTokenizer.TT_OPERATOR ) || match( T, "<=", SourceCodeTokenizer.TT_OPERATOR ) ) { Token eq = new Token(); if( T._strValue.equals( ">" ) && match( eq, "=", SourceCodeTokenizer.TT_OPERATOR, true ) ) { if( T.getTokenEnd() == eq.getTokenStart() ) { T._strValue = ">="; match( null, "=", SourceCodeTokenizer.TT_OPERATOR ); } } parseIntervalExpression(); RelationalExpression e = new RelationalExpression(); Expression rhs = popExpression(); Expression lhs = popExpression(); rhs = verifyConditionalTypes( lhs, rhs ); e.setLHS( lhs ); e.setRHS( rhs ); e.setOperator( T._strValue ); if( !lhs.hasParseExceptions() && !rhs.hasParseExceptions() ) { verifyRelationalOperandsComparable( e ); } pushExpression( e ); } else if( match( null, Keyword.KW_typeis ) ) { parseTypeLiteral(); TypeIsExpression e = new TypeIsExpression(); Expression rhs = popExpression(); Expression lhs = popExpression(); if( verify( rhs, rhs instanceof TypeLiteral, Res.MSG_EXPECTING_TYPELITERAL ) ) { verify( lhs, !lhs.getType().isPrimitive(), Res.MSG_PRIMITIVES_NOT_ALLOWED_HERE ); IType rhsType = ((TypeLiteral)rhs).getType().getType(); verify( rhs, !rhsType.isPrimitive(), Res.MSG_PRIMITIVES_NOT_ALLOWED_HERE ); warn( rhs, TypeLord.isInstanceOfPossible( lhs.getType(), rhsType ), Res.MSG_INCONVERTIBLE_TYPES, lhs.getType().getName(), rhsType.getName() ); e.setRHS( (TypeLiteral)rhs ); _ctxInferenceMgr.updateType( ContextInferenceManager.getUnwrappedExpression( lhs ), e.getRHS().evaluate() ); } e.setLHS( lhs ); pushExpression( e ); } else { // The <null> case break; } } while( true ); } //------------------------------------------------------------------------------ // interval-expression // <bitshift-expression> // <interval-expression> .. <bitshift-expression> // <interval-expression> |.. <bitshift-expression> // <interval-expression> ..| <bitshift-expression> // <interval-expression> |..| <bitshift-expression> // // Left recursion removed from this *grammar* is: // // interval-expression // <bitshift-expression> <interval-expression2> // // interval-expression2 // .. <bitshift-expression> <interval-expression2> // |.. <bitshift-expression> <interval-expression2> // ..| <bitshift-expression> <interval-expression2> // |..| <bitshift-expression> <interval-expression2> // <null> // void parseIntervalExpression() { int iOffset = _tokenizer.getTokenStart(); int iLineNum = _tokenizer.getLineNumber(); int iColumn = getTokenizer().getTokenColumn(); _parseIntervalExpression(); setLocation( iOffset, iLineNum, iColumn ); } void _parseIntervalExpression() { // <multiplicative-expression> parseBitshiftExpression(); // <additive-expression2> Token t = new Token(); boolean bClosed = match( t, "..", SourceCodeTokenizer.TT_OPERATOR ); boolean bLeftOpen = !bClosed && match( t, "|..", SourceCodeTokenizer.TT_OPERATOR ); boolean bNextTokenIsDotNoWhitespace = false; if( bClosed | bLeftOpen ) { int iNextToken = _tokenizer.getCurrentToken().getTokenStart(); bNextTokenIsDotNoWhitespace = t._iDocPosition + t._strValue.length() == iNextToken && match( null, '.' ); } boolean bRightOpen = !bClosed && !bLeftOpen && match( null, "..|", SourceCodeTokenizer.TT_OPERATOR ); if( !bClosed && !bLeftOpen && !bRightOpen && match( null, "|..|", SourceCodeTokenizer.TT_OPERATOR ) ) { bLeftOpen = bRightOpen = true; } if( bClosed || bLeftOpen || bRightOpen ) { parseBitshiftExpression(); Expression rhs = popExpression(); Expression lhs = popExpression(); rhs = verifyConditionalTypes( lhs, rhs ); IType type = IntervalExpression.getIntervalType( rhs.getType() ); lhs = possiblyWrapWithImplicitCoercion( lhs, rhs.getType() ); IntervalExpression e = new IntervalExpression( bClosed || !bLeftOpen, bClosed || !bRightOpen, lhs, rhs ); verify( e, !bNextTokenIsDotNoWhitespace, Res.MSG_EXTRA_DOT_FOUND_IN_INTERVAL ); //## todo: move to foreach: verify( e, JavaTypes.ITERABLE().isAssignableFrom( type ), Res.MSG_INTERVAL_MUST_BE_ITERABLE_HERE ); e.setType( type ); if( !lhs.hasParseExceptions() && !rhs.hasParseExceptions() ) { verifyRelationalOperandsComparable( e ); } pushExpression( e ); } } //------------------------------------------------------------------------------ // bitshift-expression // <additive-expression> // <bitshift-expression> << <additive-expression> // <bitshift-expression> >> <additive-expression> // <bitshift-expression> >>> <additive-expression> // // Left recursion removed from this *grammar* is: // // bitshift-expression // <addtive-expression> <bitshift-expression2> // // bitshift-expression2 // << <additive-expression> <bitshift-expression2> // >> <additive-expression> <bitshift-expression2> // >>> <additive-expression> <bitshift-expression2> // <null> // void parseBitshiftExpression() { int iOffset = _tokenizer.getTokenStart(); int iLineNum = _tokenizer.getLineNumber(); int iColumn = getTokenizer().getTokenColumn(); _parseBitshiftExpression(); setLocation( iOffset, iLineNum, iColumn ); } void _parseBitshiftExpression() { // <additive-expression> parseAdditiveExpression(); // <bitshift-expression2> do { Token T = new Token(); if( match( T, "<<", SourceCodeTokenizer.TT_OPERATOR ) || matchRightShift() ) { if( T._strValue == null || !T._strValue.equals( "<<" ) ) { T._strValue = ">>"; Token gr = new Token(); int lastEnd = getTokenizer().getPriorToken().getTokenEnd(); if( match( gr, ">", SourceCodeTokenizer.TT_OPERATOR, true ) ) { if( lastEnd == gr.getTokenStart() ) { T._strValue = ">>>"; match( null, ">", SourceCodeTokenizer.TT_OPERATOR ); } } } parseAdditiveExpression(); BitshiftExpression e = new BitshiftExpression(); // Rhs must be an int Expression rhs = popExpression(); verifyTypesComparable( rhs, JavaTypes.pINT(), rhs.getType(), false, true ); rhs = possiblyWrapWithImplicitCoercion( rhs, JavaTypes.pINT() ); // Lhs must be an int or a long Expression lhs = popExpression(); IType lhsType = lhs.getType(); if( verify( lhs, lhsType == JavaTypes.LONG() || lhsType == JavaTypes.pLONG() || lhsType == JavaTypes.INTEGER() || lhsType == JavaTypes.pINT() || lhsType == JavaTypes.SHORT() || lhsType == JavaTypes.pSHORT() || lhsType == JavaTypes.BYTE() || lhsType == JavaTypes.pBYTE(), Res.MSG_BITSHIFT_LHS_MUST_BE_INT_OR_LONG ) ) { lhsType = lhsType == JavaTypes.LONG() || lhsType == JavaTypes.pLONG() ? JavaTypes.pLONG() : JavaTypes.pINT(); lhs = possiblyWrapWithImplicitCoercion( lhs, lhsType ); } e.setLHS( lhs ); e.setRHS( rhs ); e.setOperator( T._strValue ); e.setType( lhsType ); pushExpression( e ); } else { // The <null> case break; } } while( true ); } private boolean matchRightShift() { int iMark = getTokenizer().mark(); Token t0 = new Token(); Token t1 = new Token(); if( match( t0, ">", SourceCodeTokenizer.TT_OPERATOR ) && match( t1, ">", SourceCodeTokenizer.TT_OPERATOR ) ) { if( t0.getTokenEnd() == t1.getTokenStart() ) { return true; } } getTokenizer().restoreToMark( iMark ); return false; } //------------------------------------------------------------------------------ // additive-expression // <multiplicative-expression> // <additive-expression> + <multiplicative-expression> // <additive-expression> - <multiplicative-expression> // // Left recursion removed from this *grammar* is: // // additive-expression // <multiplicative-expression> <additive-expression2> // // additive-expression2 // + <multiplicative-expression> <additive-expression2> // - <multiplicative-expression> <additive-expression2> // <null> // void parseAdditiveExpression() { int iOffset = _tokenizer.getTokenStart(); int iLineNum = _tokenizer.getLineNumber(); int iColumn = getTokenizer().getTokenColumn(); _parseAdditiveExpression(); setLocation( iOffset, iLineNum, iColumn ); } void _parseAdditiveExpression() { // <multiplicative-expression> parseMultiplicativeExpression(); // <additive-expression2> do { Token T = new Token(); boolean bPlus = match( T, "+", SourceCodeTokenizer.TT_OPERATOR ) || match( T, "?+", SourceCodeTokenizer.TT_OPERATOR ); if( bPlus || match( T, "-", SourceCodeTokenizer.TT_OPERATOR ) || match( T, "?-", SourceCodeTokenizer.TT_OPERATOR )) { parseMultiplicativeExpression(); AdditiveExpression e = new AdditiveExpression(); Expression rhs = popExpression(); Expression lhs = popExpression(); e.setLHS( lhs ); e.setRHS( rhs ); e.setOperator( T._strValue ); e.setType( resolveType( e, lhs.getType(), bPlus ? '+' : '-', rhs.getType() ) ); verify( e, !(e.isNullSafe() && e.getType().isPrimitive()), Res.MSG_EXPECTING_REFERENCE_TYPE ); pushExpression( e ); } else { // The <null> case break; } } while( true ); } //------------------------------------------------------------------------------ // multiplicative-expression // <unary-expression> // <multiplicative-expression> * <unary-expression> // <multiplicative-expression> / <unary-expression> // <multiplicative-expression> % <unary-expression> // // NOTE: See parseAdditiveExpression() above for an explanation of left recursion removal. // void parseMultiplicativeExpression() { int iOffset = _tokenizer.getTokenStart(); int iLineNum = _tokenizer.getLineNumber(); int iColumn = getTokenizer().getTokenColumn(); _parseMultiplicativeExpression(); setLocation( iOffset, iLineNum, iColumn ); } void _parseMultiplicativeExpression() { parseTypeAsExpression(); do { Token T = new Token(); if( match( T, "*", SourceCodeTokenizer.TT_OPERATOR ) || match( T, "/", SourceCodeTokenizer.TT_OPERATOR ) || match( T, "%", SourceCodeTokenizer.TT_OPERATOR ) || match( T, "?*", SourceCodeTokenizer.TT_OPERATOR ) || match( T, "?/", SourceCodeTokenizer.TT_OPERATOR ) || match( T, "?%", SourceCodeTokenizer.TT_OPERATOR ) ) { parseTypeAsExpression(); MultiplicativeExpression e = new MultiplicativeExpression(); Expression rhs = popExpression(); Expression lhs = popExpression(); e.setLHS( lhs ); e.setRHS( rhs ); e.setOperator( T._strValue ); e.setType( resolveType( e, lhs.getType(), T._strValue.charAt( 0 ), rhs.getType() ) ); verify( e, !(e.isNullSafe() && e.getType().isPrimitive()), Res.MSG_EXPECTING_REFERENCE_TYPE ); pushExpression( e ); } else { // The <null> case break; } } while( true ); } void parseTypeAsExpression() { int iOffset = _tokenizer.getTokenStart(); int iLineNum = _tokenizer.getLineNumber(); int iColumn = getTokenizer().getTokenColumn(); _parseTypeAsExpression(); setLocation( iOffset, iLineNum, iColumn ); } void _parseTypeAsExpression() { parseUnaryExpression(); do { if( match( null, Keyword.KW_typeas ) || match( null, Keyword.KW_as )) { parseTypeLiteral(); TypeAsExpression e = new TypeAsExpression(); Expression rhs = popExpression(); Expression lhs = popExpression(); if( !(rhs instanceof TypeLiteral) ) { rhs.addParseException( new ParseException( makeFullParserState(), Res.MSG_EXPECTING_TYPELITERAL ) ); } else { IType rhsType = ((TypeLiteral)rhs).getType().getType(); verify( rhs, rhsType != JavaTypes.pVOID(), Res.MSG_VOID_NOT_ALLOWED ); verifyComparable( TypeLord.replaceTypeVariableTypeParametersWithBoundingTypes( rhsType ), lhs, false, false ); if( rhs.hasParseExceptions() || lhs.hasParseExceptions() ) { IType lhsType = lhs.getType(); if (TypeSystem.canCast(lhsType, rhsType)) { //noinspection ThrowableResultOfMethodCallIgnored lhs.removeParseException( Res.MSG_TYPE_MISMATCH ); } } e.setType( rhsType ); e.setCoercer( CommonServices.getCoercionManager().resolveCoercerStatically( rhsType, lhs.getType() ) ); warn( lhs, lhs.getType() instanceof IErrorType || rhs.getType() instanceof IErrorType || !rhsType.isAssignableFrom( lhs.getType() ) || isDynamic( rhsType ), Res.MSG_UNNECESSARY_COERCION, lhs.getType().getRelativeName(), rhsType.getRelativeName() ); } e.setLHS( lhs ); pushExpression( e ); } else { // The <null> case break; } } while( true ); } //------------------------------------------------------------------------------ // unary-expression // + <unary-expression> // - <unary-expression> // <unary-expression-not-plus-minus> // void parseUnaryExpression() { int iOffset = _tokenizer.getTokenStart(); int iLineNum = _tokenizer.getLineNumber(); int iColumn = getTokenizer().getTokenColumn(); _parseUnaryExpression(); setLocation( iOffset, iLineNum, iColumn ); } void _parseUnaryExpression() { Token T = new Token(); if( match( T, "+", SourceCodeTokenizer.TT_OPERATOR ) || match( T, "-", SourceCodeTokenizer.TT_OPERATOR ) ) { boolean negation = T._strValue.charAt( 0 ) == (int)'-'; if( negation && atNumberLiteralStart() ) { parseNumberLiteral(true); } else { parseUnaryExpressionNotPlusMinus(); UnaryExpression ue = new UnaryExpression(); Expression e = popExpression(); verify( e, ue.isSupportedType( e.getType() ), Res.MSG_NUMERIC_TYPE_EXPECTED ); ue.setExpression( e ); ue.setNegated( negation ); ue.setType( e.getType() ); pushExpression( ue ); } } else { parseUnaryExpressionNotPlusMinus(); } } //------------------------------------------------------------------------------ // unary-expression-not-plus-minus // ~ <unary-expression> // ! <unary-expression> // not <unary-expression> // typeof <unary-expression> // eval( <expression> ) // <primary-expression> // void parseUnaryExpressionNotPlusMinus() { int iOffset = _tokenizer.getTokenStart(); int iLineNum = _tokenizer.getLineNumber(); int iColumn = getTokenizer().getTokenColumn(); _parseUnaryExpressionNotPlusMinus(); setLocation( iOffset, iLineNum, iColumn ); checkMemberAccessIsReadable(); } private void checkMemberAccessIsReadable() { Expression expr = peekExpression(); if( expr instanceof MemberAccess ) { IPropertyInfo pi = ((MemberAccess)expr).getPropertyInfoWithoutThrowing(); if( pi != null ) { verify( expr, pi.isReadable(), Res.MSG_CLASS_PROPERTY_NOT_READABLE, pi.getName(), pi.getOwnersType().getName() ); } } else if( (expr instanceof Identifier && ((Identifier)expr).getSymbol() instanceof DynamicPropertySymbol) ) { DynamicPropertySymbol dps = (DynamicPropertySymbol)((Identifier)expr).getSymbol(); if( dps != null && !dps.isReadable() ) { verify( expr, dps.isReadable(), Res.MSG_CLASS_PROPERTY_NOT_READABLE, dps.getName(), dps.getScriptPart() == null ? "" : dps.getScriptPart().getContainingType().getName() ); } } } void _parseUnaryExpressionNotPlusMinus() { Token T = new Token(); if( match( T, "!", SourceCodeTokenizer.TT_OPERATOR ) || match( T, Keyword.KW_not ) ) { _ctxInferenceMgr.pushCtx(); try { parseUnaryExpression(); } finally { _ctxInferenceMgr.popCtx( false ); } UnaryNotPlusMinusExpression ue = new UnaryNotPlusMinusExpression(); Expression e = popExpression(); IType type = e.getType(); verifyTypesComparable( e, JavaTypes.pBOOLEAN(), type, false, true ); e = possiblyWrapWithImplicitCoercion( e, JavaTypes.pBOOLEAN() ); ue.setExpression( e ); ue.setNot( true ); ue.setType( JavaTypes.pBOOLEAN() ); pushExpression( ue ); } else if( match( T, "~", SourceCodeTokenizer.TT_OPERATOR ) ) { parseUnaryExpression(); UnaryNotPlusMinusExpression ue = new UnaryNotPlusMinusExpression(); Expression e = popExpression(); IType type = e.getType(); if( type == JavaTypes.LONG() || type == JavaTypes.pLONG() ) { e = possiblyWrapWithImplicitCoercion( e, JavaTypes.pLONG() ); } else { if( verify( e, !isDynamic( type ), Res.MSG_DYNAMIC_TYPE_NOT_ALLOWED_HERE ) ) { verifyTypesComparable( e, JavaTypes.pINT(), type, false, true ); e = possiblyWrapWithImplicitCoercion( e, JavaTypes.pINT() ); } } ue.setExpression( e ); ue.setBitNot( true ); ue.setType( e.getType() ); pushExpression( ue ); } else if( match( T, Keyword.KW_typeof ) ) { parseUnaryExpression(); TypeOfExpression toe = new TypeOfExpression(); Expression e = popExpression(); toe.setExpression( e ); pushExpression( toe ); } else if( match( T, Keyword.KW_statictypeof ) ) { parseUnaryExpression(); StaticTypeOfExpression toe = new StaticTypeOfExpression(); Expression e = popExpression(); toe.setExpression( e ); pushExpression( toe ); } else if( match( null, "\\", SourceCodeTokenizer.TT_OPERATOR ) ) { parseBlockExpression(); } else if( match( T, Keyword.KW_eval ) ) { parseEvalExpression(); } else { parsePrimaryExpression(); } } private void parseEvalExpression() { EvalExpression evalExpr = new EvalExpression( getTypeUsesMap().copy() ); List<ICapturedSymbol> captured = new ArrayList<ICapturedSymbol>(); captureAllSymbols( null, getCurrentEnclosingGosuClass(), captured ); evalExpr.setCapturedSymbolsForBytecode( captured ); evalExpr.setCapturedTypeVars( new HashMap<String, ITypeVariableDefinition>( getTypeVariables() ) ); verify( evalExpr, match( null, '(' ), Res.MSG_EXPECTING_LEFTPAREN_EVAL ); parseExpression(); verify( evalExpr, match( null, ')' ), Res.MSG_EXPECTING_RIGHTPAREN_EVAL ); Expression e = popExpression(); evalExpr.setExpression( e ); pushExpression( evalExpr ); } public boolean isCaptureSymbolsForEval() { return _bCaptureSymbolsForEval; } public void setCaptureSymbolsForEval( boolean bCaputreSymbolsForEval ) { _bCaptureSymbolsForEval = bCaputreSymbolsForEval; } //------------------------------------------------------------------------------ // primary-expression // null // true // false // NaN // Infinity // <new-expression> // <exists-expression> // <member-access> // <array-access> // <name> // <literal> // <object-literal> // ( <expression) ) // <method-call-expression> // <query-expression> // void parsePrimaryExpression() { int iOffset = _tokenizer.getTokenStart(); int iLineNum = _tokenizer.getLineNumber(); int iColumn = getTokenizer().getTokenColumn(); boolean bForceRedundancy = _parsePrimaryExpression(); setLocation( iOffset, iLineNum, iColumn, bForceRedundancy ); Expression eas = peekExpression(); if( recoverFromJavaStyleCast( eas ) ) { setLocation( iOffset, iLineNum, iColumn, bForceRedundancy ); // re-root the parenthesized expression under the implicit typeas we created Expression implicitTypeAsFromRecovery = peekExpression(); getLocationsList().remove(eas.getLocation()); implicitTypeAsFromRecovery.getLocation().addChild(eas.getLocation()); eas.setParent( implicitTypeAsFromRecovery ); } parseIndirectMemberAccess( iOffset, iLineNum, iColumn ); } boolean _parsePrimaryExpression() { boolean bRet = false; if( match( null, Keyword.KW_block ) ) { _parseBlockLiteral(); } else if( match( null, Keyword.KW_new ) ) { parseNewExpression(); } else if( match( null, Keyword.KW_exists ) ) { parseExistsExpression(); } else if( match( null, Keyword.KW_find, true ) ) { Token T = new Token(); T._strValue = Keyword.KW_find.toString(); parseQueryExpression( T ); } else if( parseNameOrMethodCall() ) { } else if( match( null, '(' ) ) { parseExpression( _inferredContextStack.size() > 0 ? _inferredContextStack.peek() : Collections.<IType>emptyList() ); _ctxInferenceMgr.restoreLastCtx(); Expression e = popExpression(); ParenthesizedExpression expr = new ParenthesizedExpression( e ); pushExpression( expr ); verify( e, match( null, ')' ), Res.MSG_EXPECTING_EXPRESSION_CLOSE ); } else if( parseStandAloneDataStructureInitialization() ) { bRet = true; } else { parseLiteral(); } return bRet; } private boolean parseBooleanLiteral() { if( match( null, Keyword.KW_true ) ) { BooleanLiteral e = new BooleanLiteral( true ); pushExpression( e ); return true; } if( match( null, Keyword.KW_false ) ) { BooleanLiteral e = new BooleanLiteral( false ); pushExpression( e ); return true; } return false; } private boolean parseNullLiteral() { if( match( null, Keyword.KW_null ) ) { pushExpression( new NullExpression() ); return true; } return false; } private boolean parseStandAloneDataStructureInitialization() { IToken startToken = getTokenizer().getCurrentToken(); Token token = new Token(); // infered data constructors if( !match( token, '{' ) ) { return false; } else { IType intrinsicType = getInitializableType(); NewExpression e = new InferredNewExpression(); boolean bPlaceholder = isDynamic( intrinsicType ); if( intrinsicType == null || intrinsicType == AMBIGUOUS_TYPE || bPlaceholder ) { IInitializerExpression initializer; IType type; if( match( token, null, '}', true ) ) { initializer = new CollectionInitializerExpression(); type = JavaTypes.ARRAY_LIST().getParameterizedType( bPlaceholder ? intrinsicType : JavaTypes.OBJECT() ); } else { parseExpression( bPlaceholder ? intrinsicType : JavaTypes.OBJECT() ); Expression initialExpression = popExpression(); Expression actualInitExpr = initialExpression; if( actualInitExpr instanceof ImplicitTypeAsExpression ) { actualInitExpr = ((ImplicitTypeAsExpression)actualInitExpr).getLHS(); } verify( actualInitExpr, actualInitExpr.getType() != JavaTypes.pVOID(), Res.MSG_VOID_NOT_ALLOWED ); if( match( token, ',' ) ) { _parseInitializerExpression( JavaTypes.ARRAY_LIST().getParameterizedType( bPlaceholder ? intrinsicType : JavaTypes.OBJECT() ) ); CollectionInitializerExpression collectionInit = (CollectionInitializerExpression)popExpression(); collectionInit.addFirst( initialExpression ); IType lub = bPlaceholder ? intrinsicType : TypeLord.findLeastUpperBound( getTypes( collectionInit.getValues() ) ); type = JavaTypes.ARRAY_LIST().getParameterizedType( lub ); initializer = collectionInit; } else if( match( token, "->", SourceCodeTokenizer.TT_OPERATOR ) ) { parseExpression( bPlaceholder ? intrinsicType : JavaTypes.OBJECT() ); Expression initialValueExpression = popExpression(); MapInitializerExpression mapInitializer; if( match( token, ',' ) ) { parseMapInitializerList( JavaTypes.HASH_MAP().getParameterizedType( bPlaceholder ? intrinsicType : JavaTypes.OBJECT(), bPlaceholder ? intrinsicType : JavaTypes.OBJECT() )); mapInitializer = (MapInitializerExpression)popExpression(); } else { mapInitializer = new MapInitializerExpression(); } mapInitializer.addFirst( initialExpression, initialValueExpression ); IType keysLub = TypeLord.findLeastUpperBound( getTypes( mapInitializer.getKeys() ) ); IType valuesLub = TypeLord.findLeastUpperBound( getTypes( mapInitializer.getValues() ) ); type = JavaTypes.HASH_MAP().getParameterizedType( keysLub, valuesLub ); initializer = mapInitializer; } else { CollectionInitializerExpression collectionInit= new CollectionInitializerExpression(); collectionInit.addFirst( initialExpression ); IType lub = bPlaceholder ? intrinsicType : TypeLord.findLeastUpperBound( getTypes( collectionInit.getValues() ) ); type = JavaTypes.ARRAY_LIST().getParameterizedType( lub ); initializer = collectionInit; } } verify( e, match( null, '}' ), Res.MSG_EXPECTING_CLOSE_BRACE_FOR_INITIALIZER ); pushExpression( (Expression)initializer ); setLocation( startToken.getTokenStart(), startToken.getLine(), startToken.getTokenColumn(), true ); popExpression(); e.setType( type ); e.setConstructor( getImplicitConstructor(type) ); ((Expression)initializer).setType( type ); e.setInitializer( initializer ); } else { e.setType( intrinsicType ); if( !match( null, '}' ) ) { if( intrinsicType.isArray() ) { List<Expression> valueExpressions = parseArrayValueList( intrinsicType.getComponentType() ); e.setValueExpressions( valueExpressions ); ArrayList<IType> types = new ArrayList<IType>(); for (Object valueExpression : valueExpressions) { types.add(((Expression) valueExpression).getType()); } IType componentLeastUpperBound = TypeLord.findLeastUpperBound( types ); if( componentLeastUpperBound != GosuParserTypes.NULL_TYPE() && !(componentLeastUpperBound instanceof CompoundType) ) { e.setType( componentLeastUpperBound.getArrayType()); } verify( e, match( null, '}' ), Res.MSG_EXPECTING_CLOSE_BRACE_FOR_INITIALIZER ); } else { _parseInitializerExpression( e.getType() ); IInitializerExpression initializerExpression = (IInitializerExpression)popExpression(); e.setInitializer( initializerExpression ); e.setConstructor( intrinsicType.getTypeInfo().getCallableConstructor() ); if( getCurrentInitializableContextType().equals( JavaTypes.MAP() ) && initializerExpression instanceof MapInitializerExpression ) { MapInitializerExpression mapInitializer = (MapInitializerExpression)initializerExpression; IType keysLub = TypeLord.findLeastUpperBound( getTypes( mapInitializer.getKeys() ) ); IType valuesLub = TypeLord.findLeastUpperBound( getTypes( mapInitializer.getValues() ) ); if( keysLub != GosuParserTypes.NULL_TYPE() && valuesLub != GosuParserTypes.NULL_TYPE() ) { e.setType( JavaTypes.MAP().getParameterizedType( keysLub, valuesLub ) ); } } else if( (JavaTypes.COLLECTION().equals(getCurrentInitializableContextType().getGenericType()) || JavaTypes.LIST().equals(getCurrentInitializableContextType().getGenericType()) || JavaTypes.ITERABLE().equals(getCurrentInitializableContextType().getGenericType())) && initializerExpression instanceof CollectionInitializerExpression ) { CollectionInitializerExpression collectionInitializerExpression = (CollectionInitializerExpression)initializerExpression; IType valuesLub = TypeLord.findLeastUpperBound( getTypes( collectionInitializerExpression.getValues() ) ); if( valuesLub != GosuParserTypes.NULL_TYPE() ) { e.setType( e.getType().getGenericType().getParameterizedType( valuesLub ) ); } } verify( e, match( null, '}' ), Res.MSG_EXPECTING_CLOSE_BRACE_FOR_INITIALIZER ); pushExpression( (Expression)initializerExpression ); setLocation( startToken.getTokenStart(), startToken.getLine(), startToken.getTokenColumn(), true ); popExpression(); } } else { e.setConstructor( intrinsicType.getTypeInfo().getCallableConstructor() ); } } pushExpression( e ); return true; } } private List<IType> getTypes( List<? extends IHasType> list ) { if( list == null ) { return Collections.emptyList(); } ArrayList<IType> returnList = new ArrayList<IType>( list.size() ); for( IHasType expression : list ) { returnList.add( expression.getType() ); } return returnList; } private IType getInitializableType() { IType typeToInit = getCurrentInitializableContextType(); if (typeToInit == AMBIGUOUS_TYPE) { return typeToInit; } if( typeToInit == null ) { return null; } else { if( typeToInit.isInterface() ) { typeToInit = findImpl( typeToInit ); } if( typeToInit != null && (typeToInit.isArray() || typeToInit.getTypeInfo().getCallableConstructor() != null) ) { return typeToInit; // we have an unambigous, concrete type with a default constructor } else { return null; } } } private IType getCurrentInitializableContextType() { IType typeToInit = null; List<IType> types = _inferredContextStack.size() > 0 ? _inferredContextStack.peek() : null; if( types != null ) { for( IType type : types ) { if( supportsInitializer( type ) ) { if( typeToInit != null ) { typeToInit = AMBIGUOUS_TYPE; //ambigous case, cannot use infered syntax break; } else { typeToInit = type; } } } } return typeToInit; } private IType getCurrentContextType() { List<IType> types = _inferredContextStack.size() > 0 ? _inferredContextStack.peek() : null; if( types != null && types.size() == 1 ) { return types.get( 0 ); } else { return null; } } public static IType findImpl( IType typeToInit ) { IType genericType; if (typeToInit.isParameterizedType()) { genericType = typeToInit.getGenericType(); } else { genericType = typeToInit; } if( genericType.equals( JavaTypes.LIST() ) || genericType.equals( JavaTypes.COLLECTION() ) || genericType.equals( JavaTypes.ITERABLE() )) { IJavaType arrayListType = JavaTypes.ARRAY_LIST(); if( typeToInit.isParameterizedType() ) { arrayListType = (IJavaType)arrayListType.getParameterizedType( typeToInit.getTypeParameters() ); } return arrayListType; } else if( genericType.equals( JavaTypes.SET() ) ) { IJavaType arrayListType = JavaTypes.HASH_SET(); if( typeToInit.isParameterizedType() ) { arrayListType = (IJavaType)arrayListType.getParameterizedType( typeToInit.getTypeParameters() ); } return arrayListType; } else if( genericType.equals( JavaTypes.MAP() ) ) { IJavaType hashMapType = JavaTypes.HASH_MAP(); if( typeToInit.isParameterizedType() ) { hashMapType = (IJavaType)hashMapType.getParameterizedType( typeToInit.getTypeParameters() ); } return hashMapType; } else { return null; } } private boolean supportsInitializer( IType type ) { return type.isArray() || JavaTypes.MAP().isAssignableFrom( type ) || JavaTypes.SET().isAssignableFrom( type ) || JavaTypes.LIST().isAssignableFrom( type ) || JavaTypes.COLLECTION().equals( type.getGenericType() ) || JavaTypes.ITERABLE().equals( type.getGenericType() ) || isDynamic( type ); } private void parseBlockExpression() { ISymbolTable symTable = getSymbolTable(); boolean pushed = false; int originaliBreakOk = _iBreakOk; _iBreakOk = 0; int originaliContinueOk = _iContinueOk; _iContinueOk = 0; _iReturnOk++; try { List<IType> contextTypes = _inferredContextStack.peek(); IType expectedBlockReturnType = inferReturnTypeForBlockArgument( contextTypes ); _blockReturnTypeStack.push( expectedBlockReturnType ); pushed = true; boolean foundArrow; BlockExpression block = new BlockExpression(); StandardScope blockScope = new StandardScope(); symTable.pushScope( blockScope ); try { block.setScope( blockScope ); //If there are arguments, parse them if( !match( null, "->", SourceCodeTokenizer.TT_OPERATOR ) ) { //Infer the parameter types of the block List<IType> inferredContextTypes = getContextTypesForBlockArgument( contextTypes ); ArrayList<ISymbol> args = parseParameterDeclarationList( block, false, inferredContextTypes ); for (ISymbol arg : args) { _symTable.putSymbol(arg); } foundArrow = verify( block, match( null, "->", SourceCodeTokenizer.TT_OPERATOR ), Res.MSG_EXPECTING_ARROW_AFTER_BLOCK_ARGS ); block.setArgs( args ); } else { foundArrow = true; block.setArgs( Collections.<ISymbol>emptyList() ); } verify( block, block.getArgs().size() <= IBlock.MAX_ARGS, Res.MSG_BLOCKS_CAN_HAVE_A_MOST_SIXTEEN_ARGS ); pushCurrentBlock( block ); try { //parse the expression body if(foundArrow) { int iOffset = _tokenizer.getTokenStart(); int iLineNum = _tokenizer.getLineNumber(); int iColumn = getTokenizer().getTokenColumn(); if( match( null, '{' ) ) { _ctxInferenceMgr.pushLoopCompromised(); try { parseStatementBlock(); } finally { _ctxInferenceMgr.popLoopCompromised(); } if( peekStatement() instanceof StatementList ) { setLocation( iOffset, iLineNum, iColumn ); } Statement blockBody = popStatement(); if( expectedBlockReturnType != null && expectedBlockReturnType != GosuParserTypes.NULL_TYPE() ) { boolean[] bAbsolute = {false}; ITerminalStatement term = blockBody.getLeastSignificantTerminalStatement( bAbsolute ); Statement verifyStmt; if (blockBody instanceof StatementList && ((StatementList) blockBody).getStatements() != null && ((StatementList) blockBody).getStatements().length > 0) { StatementList lst = (StatementList)blockBody; verifyStmt = lst.getStatements()[lst.getStatements().length - 1]; } else { verifyStmt = blockBody; } verify( verifyStmt, term != null && (bAbsolute[0] || term.getTerminalType() == TerminalType.ForeverLoop), Res.MSG_MISSING_RETURN ); } block.setBody( blockBody ); } else { int tokenizerPostion = getTokenizer().getTokenStart(); parseExpression( expectedBlockReturnType ); Expression exprBody = popExpression(); // void functions can work in the body of a block //noinspection ThrowableResultOfMethodCallIgnored exprBody.removeParseException( Res.MSG_VOID_EXPRESSION_NOT_ALLOWED ); //If someone is trying to do a naked assignment, parse it and give a good error message if( matchAssignmentOperator( new Token() ) ) { parseExpression(); Expression assignmentBody = popExpression(); verify( assignmentBody, false, Res.MSG_ASSIGNMENTS_MUST_BE_ENCLOSED_IN_CURLIES_IN_BLOCKS ); } else if( tokenizerPostion == getTokenizer().getTokenStart() ) { //If someone is trying to do a naked return, parse the expression and give a good error message Token token = new Token(); if( match( token, Keyword.KW_return ) ) { parseExpression(); Expression returnBody = popExpression(); verify( returnBody, false, Res.MSG_STATEMENTS_MUST_BE_ENCLOSED_IN_CURLIES_IN_BLOCKS, token._strValue ); } else if( match( token, Keyword.KW_var ) || match( token, Keyword.KW_switch ) || match( token, Keyword.KW_if ) ) { verify( exprBody, false, Res.MSG_STATEMENTS_MUST_BE_ENCLOSED_IN_CURLIES_IN_BLOCKS, token._strValue ); } } // expressions with return values are acceptable when a block is going to discard the value if( JavaTypes.pVOID().equals(expectedBlockReturnType) ) { //noinspection ThrowableResultOfMethodCallIgnored exprBody.removeParseException( Res.MSG_TYPE_MISMATCH ); } block.setBody( exprBody ); } } block.setBlockReturnType( getBlockReturnType( block.getBody(), expectedBlockReturnType ) ); block.setScope( null ); block.updateGosuClass(); pushExpression( block ); } finally { popCurrentBlock(); } } finally { symTable.popScope(); } } finally { _iBreakOk = originaliBreakOk; _iContinueOk = originaliContinueOk; _iReturnOk--; if( pushed ) { _blockReturnTypeStack.pop(); } } } private IType getBlockReturnType( IParsedElement blockBody, IType ctxType ) { if(blockBody == null) { return ErrorType.getInstance(); } if( blockBody instanceof Expression ) { return ((Expression)blockBody).getType(); } else { Statement stmt = (Statement) blockBody; ArrayList<ReturnStatement> returnStatements = new ArrayList<ReturnStatement>(); ArrayList<IType> returnTypes = new ArrayList<IType>(); //noinspection unchecked stmt.getContainedParsedElementsByTypesWithIgnoreSet( (List) returnStatements, new HashSet( Arrays.asList( BlockExpression.class ) ), ReturnStatement.class ); // The statement body must contain only throw statements, so // the return type should just be whatever the context type is if( returnStatements.size() == 0 ) { return ctxType; } else { for( ReturnStatement returnStmt : returnStatements ) { returnTypes.add( returnStmt.getValue().getType() ); } return TypeLord.findLeastUpperBound( returnTypes ); } } } private List<IType> getContextTypesForBlockArgument( List<IType> contextTypes ) { Set<List<IType>> param = new HashSet<List<IType>>(); if( contextTypes != null ) { //first look for function types for( IType inferredArgType : contextTypes ) { if( inferredArgType instanceof FunctionType ) { param.add( Arrays.asList( ((FunctionType)inferredArgType).getParameterTypes() ) ); } } //then look for java interface types if( param.size() == 0 ) { for( IType inferredArgType : contextTypes ) { IFunctionType functionType = FunctionToInterfaceCoercer.getRepresentativeFunctionType( inferredArgType ); if( functionType != null ) { ArrayList<IType> paramTypes = new ArrayList<IType>(); for( IType parameterType : functionType.getParameterTypes() ) { paramTypes.add( TypeLord.getDefaultParameterizedType( parameterType ) ); } param.add( paramTypes ); } } } } //Only return an infered argument type array if there is one and only one possible argument list of // types at this position if( param.size() == 1 ) { return param.iterator().next(); } else if( param.size() > 1 ) { List<IType> returnList = null; for( List<IType> iTypes : param ) { if( returnList == null ) { returnList = iTypes; } else if( GosuCollectionUtil.startsWith( returnList, iTypes ) ) { //noinspection UnnecessaryContinue continue; } else if( GosuCollectionUtil.startsWith( iTypes, returnList ) ) { returnList = iTypes; } else { //We found a disjoint set of parameter types, so we don't do any inference returnList = null; break; } } return returnList; } else { return null; } } private IType inferReturnTypeForBlockArgument( List<IType> contextTypes ) { IType returnType = null; if( contextTypes != null ) { //first look for function types for( IType inferredArgType : contextTypes ) { if( inferredArgType instanceof FunctionType ) { IType iType = ((FunctionType)inferredArgType).getReturnType(); if( returnType == null ) { returnType = iType; } else if( !returnType.equals( iType ) ) { return null; } } } for( IType inferredArgType : contextTypes ) { IFunctionType functionType = FunctionToInterfaceCoercer.getRepresentativeFunctionType( inferredArgType ); if( functionType != null ) { IType iType = functionType.getReturnType(); if( returnType == null ) { returnType = iType; } else if( !returnType.equals( iType ) ) { return null; } } } } // If we are currently infering the return type, use the bounding type to parse // on the way in so that we get the actual type on the way out to infer with returnType = TypeLord.boundTypes( returnType, getCurrentlyInferringTypes() ); return returnType; } //------------------------------------------------------------------------------ // <i>exists-expression</i> // <b>exists</b> <b>(</b> <identifier> <b>in</b> <expression> [ <b>index</b> <identifier> ] <b>where</b> <expression> <b>)</b> // void parseExistsExpression() { ExistsExpression existsExp = new ExistsExpression(); verify( existsExp, match( null, '(' ), Res.MSG_EXPECTING_LEFTPAREN_EXISTS ); match( null, Keyword.KW_var ); Token T = new Token(); int iNameOffset = getTokenizer().getTokenStart(); if( verify( existsExp, match( T, SourceCodeTokenizer.TT_WORD ), Res.MSG_EXPECTING_IDENTIFIER_EXISTS ) ) { existsExp.setNameOffset( iNameOffset, T._strValue ); } String strIdentifier = T._strValue; verify( existsExp, match( null, Keyword.KW_in ), Res.MSG_EXPECTING_IN_EXISTS ); parseExpression(); Expression ein = popExpression(); IType typeIn = ein.getType(); verify( existsExp, LoopStatement.isIteratorType( typeIn ) || typeIn instanceof ErrorType, Res.MSG_EXPECTING_ARRAYTYPE_EXISTS, typeIn.getName() ); Symbol symbol; Symbol symbolIndex = null; _symTable.pushScope(); try { if( match( null, Keyword.KW_index ) ) { Token Tindex = new Token(); verify( existsExp, match( Tindex, SourceCodeTokenizer.TT_WORD ), Res.MSG_EXPECTING_IDENTIFIER_EXISTS_INDEX ); String strIndexIdentifier = Tindex._strValue; verify( existsExp, _symTable.getSymbol( strIndexIdentifier ) == null, Res.MSG_VARIABLE_ALREADY_DEFINED, strIndexIdentifier ); // Create a temporary symbol for the identifier part of the exists statement's index // (so it can be legally referenced in the statement). symbolIndex = new Symbol( strIndexIdentifier, JavaTypes.pINT(), _symTable, null ); _symTable.putSymbol( symbolIndex ); } // Create a temporary symbol for the identifier part of the exists expression IType typeIdentifier = LoopStatement.getArrayComponentType( typeIn ); symbol = new Symbol( strIdentifier, typeIdentifier, _symTable, null ); _symTable.putSymbol( symbol ); verify( existsExp, match( null, Keyword.KW_where ), Res.MSG_EXPECTING_WHERE_EXISTS ); parseExpression( JavaTypes.pBOOLEAN() ); } finally { // Remove the temporary symbols _symTable.popScope(); } Expression ewhere = popExpression(); verify( existsExp, match( null, ')' ), Res.MSG_EXPECTING_RIGHTPAREN_EXISTS ); existsExp.setIdentifier( symbol ); existsExp.setIndexIdentifier( symbolIndex ); existsExp.setInExpression( ein ); existsExp.setWhereExpression( ewhere ); pushExpression( existsExp ); } boolean parseQueryExpression( Token T ) { QueryExpressionParser queryParser = new QueryExpressionParser( this ); return queryParser.parse( T ); } /** * <i>new-expression</i> * <b>new</b> <type-expression> <b>(</b> [<argument-list>] <b>)</b> <b>{</b> [<initialization-expression>] <b>}</b> * <b>new</b> <type-expression> <b>[</b> <expression> <b>]</b> * <b>new</b> <type-expression> <b>[</b><b>]</b> <b>{</b> [<array-value-list>] <b>}</b> */ void parseNewExpression() { parseNewExpressionOrAnnotation( false ); } void parseNewExpressionOrAnnotation( boolean bAnnotation ) { boolean original = isParsingAnnotation(); setParsingAnnotation( bAnnotation ); try { int mark = getTokenizer().mark(); TypeLiteral typeLiteral = null; if( match( null, null, '(', true ) ) { if( _inferredContextStack.size() > 0 && _inferredContextStack.peek() != null ) { List<IType> contextTypes = _inferredContextStack.peek(); if( contextTypes.size() == 1 ) { IType contextType = contextTypes.get( 0 ); typeLiteral = new TypeLiteral( contextType ); pushExpression( typeLiteral ); int iOffset = getTokenizer().getTokenStart(); int iLineNum = getTokenizer().getLineNumber(); int iColumn = getTokenizer().getTokenColumn(); setLocation( iOffset, iLineNum, iColumn, true ); popExpression(); } } } if( typeLiteral == null ) { parseTypeLiteralIgnoreArrayBrackets(); typeLiteral = (TypeLiteral)popExpression(); } verify( typeLiteral, !(typeLiteral instanceof BlockLiteral), Res.MSG_BLOCKS_LITERAL_NOT_ALLOWED_IN_NEW_EXPR ); IType declaringClass = typeLiteral.getType().getType(); declaringClass = handleListType( typeLiteral, declaringClass ); if( isParsingStaticFeature() ) { IType type = typeLiteral.getType().getType(); while( type.getEnclosingType() != null ) { if( type instanceof IGosuClass && verify( typeLiteral, ((IGosuClass)type).isStatic(), Res.MSG_CANNOT_INSTANTIATE_NON_STATIC_CLASSES_HERE ) ) { type = type.getEnclosingType(); } else { break; } } } verify( typeLiteral, !declaringClass.isEnum() || getTokenizer().getCurrentToken().getType() == '[', Res.MSG_ENUM_CONSTRUCTOR_NOT_ACCESSIBLE ); parseNewExpressionOrAnnotation( declaringClass, bAnnotation, false, typeLiteral, mark ); } finally { setParsingAnnotation( original ); } } private IType handleListType( TypeLiteral typeLiteral, IType declaringClass ) { if( !warn( typeLiteral, TypeLord.getPureGenericType( declaringClass ) != GosuParserTypes.LIST_TYPE(), Res.MSG_LIST_TO_ARRAYLIST_WARNING ) ) { // Implicitly coerce our List type to ArrayList if( declaringClass.isParameterizedType() ) { declaringClass = JavaTypes.ARRAY_LIST().getParameterizedType( declaringClass.getTypeParameters() ); } else { declaringClass = JavaTypes.ARRAY_LIST(); } } return declaringClass; } void parseNewExpressionOrAnnotation( IType declaringClass, boolean bAnnotation, boolean bNoArgNoParenthesis, final TypeLiteral typeLiteral, int mark ) { int iParenStart = _tokenizer.getTokenStart(); NewExpression e = bAnnotation ? new AnnotationExpression() : new NewExpression(); e.setType( declaringClass ); e.setTypeLiteral(typeLiteral); verifyCanConstructInnerClassFromCallSite( e, declaringClass ); if( bNoArgNoParenthesis || match( null, '(' ) ) { boolean bAssumeClosingParenMatch = true; e.setArgPosition( iParenStart + 1 ); List<IConstructorType> listConstructorTypes = (!declaringClass.isInterface() || isAnnotation( declaringClass )) ? getPreliminaryConstructorTypes( declaringClass, e ) : Collections.<IConstructorType>emptyList(); scrubAnnotationConstructors(declaringClass, listConstructorTypes); JavaAnnotationParsingState javaAnnotationParsingState = getJavaAnnotationParsingState(declaringClass, listConstructorTypes); boolean bNoArgsProvided = false; if( !bNoArgNoParenthesis && (!(bNoArgsProvided = match( null, null, ')', true )) || listConstructorTypes.size() > 0 && listConstructorTypes.get( 0 ).hasOptionalParams()) ) { MethodScore bestConst = parseArgumentList( e, listConstructorTypes, null, !(declaringClass instanceof ErrorType), bNoArgsProvided, javaAnnotationParsingState ); IConstructorType constructorType = null; if( bestConst.isValid() ) { constructorType = (IConstructorType)bestConst.getRawFunctionType(); List<IExpression> args = bestConst.getArguments(); verifyArgCount( e, args.size(), constructorType ); //noinspection SuspiciousToArrayCall e.setArgs( args.toArray( new Expression[args.size()] ) ); e.setType( declaringClass ); e.setConstructor( constructorType.getConstructor() ); IType[] argTypes = constructorType.getParameterTypes(); e.setArgTypes( argTypes ); e.setNamedArgOrder( bestConst.getNamedArgOrder() ); } else { verify( e, false, Res.MSG_NO_CONSTRUCTOR_FOUND_FOR_CLASS, declaringClass.getName() ); e.setType( ErrorType.getInstance() ); } bAssumeClosingParenMatch = verify( e, match( null, ')' ), Res.MSG_EXPECTING_FUNCTION_CLOSE ); boolean bAnonymous = !isInitializableType( e.getType() ) && match( null, null, '{', true ); verifyConstructorIsAccessible( declaringClass, e, constructorType, bAnonymous ); } else { if( bNoArgsProvided ) { match( null, ')' ); } try { IConstructorType constructorType = getConstructorType( declaringClass, new Expression[0], null, this ); e.setType( declaringClass ); e.setConstructor( constructorType.getConstructor() ); boolean bAnonymous = !isInitializableType( e.getType() ) && match( null, null, '{', true ); verifyConstructorIsAccessible( declaringClass, e, constructorType, bAnonymous ); } catch( ParseException pe ) { boolean possibleAnonymousClassDecl = declaringClass.isInterface() && !isInitializableType( declaringClass ); boolean possibleDataStructDecl = isConcreteInitializableType( declaringClass ); if( (possibleAnonymousClassDecl || possibleDataStructDecl) && match( null, null, '{', true ) ) { // Assume we are constructing an anonymous class on an interface or a data structure } else { e.setType( declaringClass ); IConstructorInfo firstCtor = getConstructor( declaringClass ); e.setConstructor( firstCtor ); e.addParseException( pe ); } } } if( match( null, null, '{', true ) ) { if( isInitializableType( e.getType() ) ) { IToken startToken = getTokenizer().getCurrentToken(); match( null, '{' ); if( !match( null, '}' ) ) { _parseInitializerExpression( e.getType() ); IInitializerExpression initializerExpression = (IInitializerExpression)peekExpression(); e.setInitializer( initializerExpression ); verify( e, match( null, '}' ), Res.MSG_EXPECTING_CLOSE_BRACE_FOR_INITIALIZER ); setLocation( startToken.getTokenStart(), startToken.getLine(), startToken.getTokenColumn() ); popExpression(); } } else if( !(declaringClass instanceof ErrorType) ) { int state = _tokenizer.mark(); //look ahead 2 to see if this is an object initializer if( !declaringClass.isAbstract() && !declaringClass.isInterface() && match( null, '{' ) && match( null, ":", SourceCodeTokenizer.TT_OPERATOR ) ) { _tokenizer.restoreToMark( state ); parseObjectInitializer( typeLiteral.evaluate() ); ObjectInitializerExpression expression = (ObjectInitializerExpression)popExpression(); e.setInitializer( expression ); } else if( bAssumeClosingParenMatch ) { // Assume we are constructing an anonymous class on a gosu or java type. declaringClass = parseAnonymousInnerClass( declaringClass, typeLiteral, e, state, mark ); } } } if( e.getConstructor() != null ) { // java annotations are abstract, technically, but can be instantiated via custom typeinfo constructors boolean isJavaAnnotation = e.getConstructor().getOwnersType() instanceof IJavaType && JavaTypes.ANNOTATION().isAssignableFrom( e.getConstructor().getOwnersType() ); verify( e, isJavaAnnotation || !e.getConstructor().getOwnersType().isAbstract(), Res.MSG_CANNOT_CONSTRUCT_ABSTRACT_CLASS, declaringClass.getName() ); // Prevent recursive types from being constructed directly verify( e, !isRecursiveTypeFromBase( declaringClass ), Res.MSG_CANNOT_CONSTRUCT_RECURSIVE_CLASS, declaringClass.getName() ); } pushExpression( e ); } else if( !bAnnotation ) { if( verify( e, match( null, '[' ), Res.MSG_EXPECTING_NEW_ARRAY_OR_CTOR ) ) { if( match( null, ']' ) ) { int numArrays = 1; while( match( null, '[' ) ) { verify( e, match( null, ']' ), Res.MSG_EXPECTING_ARRAY_BRACKET ); ++numArrays; } for( int i = 0; i < numArrays; i++ ) { declaringClass = declaringClass.getArrayType(); } verify( e, match( null, '{' ), Res.MSG_EXPECTING_OPEN_BRACE_FOR_NEW_ARRAY ); e.setType( declaringClass ); if( match( null, '}' ) ) { e.setValueExpressions( null ); } else { List valueExpressions = parseArrayValueList( declaringClass.getComponentType() ); verify( e, match( null, '}' ), Res.MSG_EXPECTING_CLOSE_BRACE_FOR_NEW_ARRAY ); //noinspection unchecked e.setValueExpressions( valueExpressions ); } } else { parseExpression( JavaTypes.pINT() ); e.addSizeExpression( popExpression() ); IType arrayType = null; boolean bSizelessDimension = false; while( verify( e, match( null, ']' ), Res.MSG_EXPECTING_ARRAY_BRACKET ) ) { declaringClass = declaringClass.getArrayType(); arrayType = declaringClass; if( match( null, '[' ) ) { if( !bSizelessDimension && !match( null, null, ']', true ) ) { parseExpression( JavaTypes.pINT() ); e.addSizeExpression( popExpression() ); } else { bSizelessDimension = true; } } else { break; } } if( arrayType != null ) { declaringClass = arrayType; } else { declaringClass = ErrorType.getInstance(); } e.setType( declaringClass ); } } pushExpression( e ); } else { if( !(declaringClass instanceof ErrorType) ) { ITypeInfo typeInfo = declaringClass.getTypeInfo(); if( typeInfo != null ) { IConstructorInfo iConstructorInfo = typeInfo.getConstructor(); if (iConstructorInfo == null) { outer: for (IConstructorInfo constructorInfo : typeInfo.getConstructors()) { if (constructorInfo instanceof IOptionalParamCapable) { IExpression[] defaultVals = ((IOptionalParamCapable) constructorInfo).getDefaultValueExpressions(); for (IExpression defaultVal : defaultVals) { if (defaultVal == null) { continue outer; } } iConstructorInfo = constructorInfo; break; } } } if( verify( e, iConstructorInfo != null, Res.MSG_NO_DEFAULT_CTOR_IN, declaringClass.getName() ) ) { e.setType( declaringClass ); e.setConstructor( iConstructorInfo ); e.setArgTypes(); e.setType( declaringClass ); } else { e.setType( ErrorType.getInstance() ); } } else { e.setType( ErrorType.getInstance() ); } } else { e.setType( ErrorType.getInstance() ); } pushExpression( e ); } IConstructorInfo constructor = e.getConstructor(); if( (constructor != null) && (constructor.isDeprecated()) ) { IParserState state; if( typeLiteral == null ) { state = null; } else { state = new IParserState() { public int getLineNumber() { return typeLiteral.getLocation().getLineNum(); } public int getTokenColumn() { return typeLiteral.getLocation().getColumn(); } public String getSource() { return getTokenizer().getSource(); } public int getTokenStart() { return typeLiteral.getLocation().getOffset(); } public int getTokenEnd() { return typeLiteral.getLocation().getExtent(); } public int getLineOffset() { return getTokenizer().getLineOffset(); } }; } //noinspection ThrowableInstanceNeverThrown e.addParseWarning( new ParseWarningForDeprecatedMember( state, TypeInfoUtil.getConstructorSignature( constructor ), constructor.getContainer().getName() ) ); } } private JavaAnnotationParsingState getJavaAnnotationParsingState(IType declaringClass, List<IConstructorType> listConstructorTypes) { if (!CommonServices.getEntityAccess().getLanguageLevel().supportHistoricalJavaAnnotationConstructors()) { if (JavaTypes.ANNOTATION().isAssignableFrom(declaringClass)) { if (listConstructorTypes.size() == 1) { IParameterInfo[] parameters = listConstructorTypes.get(0).getConstructor().getParameters(); if (parameters.length == 1 && parameters[0].getName().equals("value")) { throw new RuntimeException("DEPRECATED 0: GosuParser@getJavaAnnotationParsingState"); //return JavaAnnotationParsingState.PARSING_JAVA_VALUE_ENUM; } } throw new RuntimeException("DEPRECATED 0: GosuParser@getJavaAnnotationParsingState"); //return JavaAnnotationParsingState.PARSING_JAVA_NON_VALUE_ENUM; } } return JavaAnnotationParsingState.NOT_PARSING_ENUM; } private void scrubAnnotationConstructors(IType declaringClass, List<IConstructorType> listConstructorTypes) { //## hack: if this is a java annotation and we are parsing named parameters, we remove the // legacy constructors. This allows people to port over to the standard java annotation syntax // while retaining the original legacy constructors if (JavaTypes.ANNOTATION().isAssignableFrom(declaringClass)) { if (match(null, ":", ISourceCodeTokenizer.TT_OPERATOR, true)) { Iterator<IConstructorType> iter = listConstructorTypes.iterator(); while (iter.hasNext()) { IConstructorType next = iter.next(); IConstructorHandler constructor = next.getConstructor().getConstructor(); if (!(constructor instanceof StandardJavaAnnotationConstructor)) { iter.remove(); } } } else { Iterator<IConstructorType> iter = listConstructorTypes.iterator(); while (iter.hasNext()) { IConstructorType next = iter.next(); IConstructorHandler constructor = next.getConstructor().getConstructor(); if (constructor instanceof StandardJavaAnnotationConstructor) { iter.remove(); } } } } } private boolean isRecursiveTypeFromBase( IType declaringClass ) { if( !declaringClass.isGenericType() && !declaringClass.isParameterizedType() ) { return false; } IType genType = TypeLord.getPureGenericType( declaringClass ); if( genType != TypeLord.getDefaultParameterizedType( genType ) ) { return false; } if( declaringClass.isGenericType() && !declaringClass.isParameterizedType() ) { if( declaringClass == TypeLord.getDefaultParameterizedType( declaringClass ) ) { return true; } } else if( declaringClass.isParameterizedType() ) { for( IType typeParam : declaringClass.getTypeParameters() ) { if( isRecursiveTypeFromBase( typeParam ) ) { return true; } } } return false; } private boolean isAnnotation( IType type ) { return JavaTypes.ANNOTATION().isAssignableFrom( type ) || JavaTypes.IANNOTATION().isAssignableFrom( type ); } private ArrayList<IConstructorType> getPreliminaryConstructorTypes( IType declaringClass, NewExpression e ) { // Get a preliminary constructorTypes to check arguments. Note we do this to aid in error feedback and value popup completion. ArrayList<IConstructorType> listConstructorTypes = new ArrayList<IConstructorType>( 2 ); try { getConstructorType( declaringClass, null, listConstructorTypes, this ); } catch( ParseException pe ) { IConstructorInfo firstCtor = getConstructor( declaringClass ); e.setConstructor( firstCtor ); e.addParseException( pe ); } IType[][] argTypesArray = new IType[listConstructorTypes.size()][]; IParameterInfo[][] paramTypesPossible = new IParameterInfo[listConstructorTypes.size()][]; for( int i = 0; i < argTypesArray.length; i++ ) { IConstructorType ctorType = listConstructorTypes.get( i ); paramTypesPossible[i] = ctorType.getConstructor().getParameters(); argTypesArray[i] = ctorType.getParameterTypes(); } return listConstructorTypes; } private IType parseAnonymousInnerClass( IType declaringClass, TypeLiteral typeLiteral, NewExpression newExpr, int state, int mark ) { _tokenizer.restoreToMark( state ); newExpr.setAnonymousClass( true ); IGosuClassInternal gsDeclaringClass = null; if( declaringClass instanceof IJavaType ) { gsDeclaringClass = GosuClassProxyFactory.instance().create( declaringClass ); if( declaringClass.isParameterizedType() ) { gsDeclaringClass = (IGosuClassInternal)gsDeclaringClass.getParameterizedType( declaringClass.getTypeParameters() ); } } else if( declaringClass instanceof IGosuClassInternal ) { gsDeclaringClass = (IGosuClassInternal)declaringClass; } else { verify( newExpr, false, Res.MSG_BAD_ANONYMOUS_CLASS_DECLARATION ); } if( gsDeclaringClass != null && typeLiteral != null ) { declaringClass = TypeLord.makeDefaultParameterizedType( declaringClass ); ICompilableTypeInternal enclosingType = getCurrentEnclosingGosuClass(); // For now anonymous classes must be enclosed in a gsclass. if( verify( newExpr, enclosingType instanceof IGosuClassInternal, Res.MSG_ANONYMOUS_CLASS_NOT_ALLOWED_HERE ) ) { int iNameOffset = typeLiteral.getLocation().getOffset(); int originaliBreakOk = _iBreakOk; _iBreakOk = 0; int originaliContinueOk = _iContinueOk; _iContinueOk = 0; try { _parseAnonymousInnerClass( declaringClass, gsDeclaringClass, enclosingType, iNameOffset, newExpr, mark ); } finally { _iBreakOk = originaliBreakOk; _iContinueOk = originaliContinueOk; } } } return declaringClass; } private void _parseAnonymousInnerClass( IType declaringClass, IGosuClassInternal gsDeclaringClass, ICompilableTypeInternal enclosingType, int iNameOffset, NewExpression newExpr, int mark ) { //##todo: // Note we do NOT case where we are parsing a field initializer since // the anonymous class should have access to all the class' fields. E.g., // // class Foo { // static var Field1 = new Whatever() { // override function bar() : String { // return Field2 // } // } // static var Field2 : String = "hello" // } // String strAnonymousClass = IGosuClassInternal.ANONYMOUS_PREFIX + "_" + enclosingType.getAnonymousInnerClassCount(); boolean bTestClass = enclosingType instanceof IGosuClassInternal && ((IGosuClassInternal)enclosingType).isTestClass(); InnerClassFileSystemSourceFileHandle innerSfh = new InnerClassFileSystemSourceFileHandle(ClassType.Class, enclosingType.getName(), strAnonymousClass, bTestClass ); innerSfh.setOffset( iNameOffset ); innerSfh.setMark( mark ); IGosuClassInternal innerGsClass = (IGosuClassInternal)enclosingType.getTypeLoader().makeNewClass( innerSfh ); ((IGosuClassInternal)enclosingType).addInnerClass(innerGsClass); innerGsClass.setEnclosingType( enclosingType ); innerGsClass.setNamespace( enclosingType.getNamespace() ); innerGsClass.createNewParseInfo(); if( isParsingStaticFeature() ) { innerGsClass.markStatic(); } if( declaringClass.isInterface() ) { innerGsClass.addInterface( declaringClass ); newExpr.setType( innerGsClass ); } else { innerGsClass.setSuperType( declaringClass ); if( declaringClass.isEnum() ) { innerGsClass.setEnum(); } newExpr.setType( innerGsClass ); } if( newExpr.getConstructor() != null ) { GosuConstructorInfo ci = getGsConstructorInfo( newExpr.getConstructor(), gsDeclaringClass ); verify( newExpr, innerGsClass.getParseInfo().addAnonymousConstructor( _symTable, ci ), Res.MSG_NO_CONSTRUCTOR_FOUND_FOR_CLASS, gsDeclaringClass.getName() ); } innerGsClass.setCannotCaptureSymbols( isParsingBlock() || ((IGosuClassInternal)innerGsClass.getEnclosingType()).isCannotCaptureSymbols() ); GosuClassParser.parseAnonymousInnerClass( this, innerGsClass ); if( newExpr.getConstructor() != null && !innerGsClass.getTypeInfo().getConstructors().isEmpty() ) { IConstructorInfo innerCtor = innerGsClass.getTypeInfo().getConstructors().get( 0 ); newExpr.setConstructor( innerCtor ); } popStatement(); if( declaringClass.isInterface() ) { //noinspection unchecked List<IConstructorInfo> ctors = (List<IConstructorInfo>)innerGsClass.getTypeInfo().getConstructors(); if( ctors.size() > 0 ) { newExpr.setConstructor( ctors.get( 0 ) ); } } } public boolean isParsingStaticFeature() { return !_parsingStaticFeature.isEmpty() && _parsingStaticFeature.peek(); } public void pushParsingStaticMember( Boolean bParsingStaticFeature ) { _parsingStaticFeature.push( bParsingStaticFeature ); } public void popParsingStaticMember() { _parsingStaticFeature.pop(); } public boolean isParsingAbstractConstructor() { return !_parsingAbstractConstructor.isEmpty() && _parsingAbstractConstructor.peek(); } public void pushParsingAbstractConstructor( Boolean bParsingAbstractConstructor ) { _parsingAbstractConstructor.push( bParsingAbstractConstructor ); } public void popParsingAbstractConstructor() { _parsingAbstractConstructor.pop(); } private IConstructorInfo getConstructor( IType instanceClass ) { IConstructorInfo firstCtor = null; if( instanceClass.getTypeInfo() instanceof IRelativeTypeInfo) { if( !instanceClass.isInterface() ) { List<? extends IConstructorInfo> ctors = ( (IRelativeTypeInfo) instanceClass.getTypeInfo() ).getConstructors( instanceClass ); if ( ! ctors.isEmpty() ) { firstCtor = ctors.get( 0 ); } } } else { if( !instanceClass.isInterface() ) { ITypeInfo typeInfo = instanceClass.getTypeInfo(); if (typeInfo == null) { firstCtor = null; } else { List<? extends IConstructorInfo> ctors = typeInfo.getConstructors(); if ( ! ctors.isEmpty() ) { firstCtor = ctors.get( 0 ); } } } } return firstCtor; } private void verifyConstructorIsAccessible( IType instanceClass, NewExpression e, IConstructorType constructorType, boolean bAnonymous ) { if( e.getType() instanceof ErrorType ) { return; } if( constructorType != null ) { ITypeInfo typeInfo = instanceClass.getTypeInfo(); List<? extends IConstructorInfo> accessibleCtors = getGosuClass() != null && typeInfo instanceof IRelativeTypeInfo ? ((IRelativeTypeInfo)typeInfo).getConstructors( getGosuClass() ) : typeInfo.getConstructors(); //noinspection SuspiciousMethodCalls verify( e, accessibleCtors.contains( constructorType.getConstructor() ) || (bAnonymous && constructorType.getConstructor().isProtected()), Res.MSG_CTOR_HAS_XXX_ACCESS, getModifierString( constructorType ) ); } } private String getModifierString( IConstructorType constructorType ) { return constructorType.getConstructor().isPrivate() ? "private" : constructorType.getConstructor().isInternal() ? "internal" : constructorType.getConstructor().isProtected() ? "protected" : "public"; } private boolean parseObjectInitializer( IType objectType ) { int iOffset = _tokenizer.getTokenStart(); int iLineNum = _tokenizer.getLineNumber(); int iColumn = getTokenizer().getTokenColumn(); boolean b = _parseObjectInitializer( objectType ); setLocation( iOffset, iLineNum, iColumn ); return b; } private boolean _parseObjectInitializer( IType objectType ) { if( match( null, '{' ) ) { _doParseObjectInitializer(objectType); verify( peekExpression(), match( null, '}' ), Res.MSG_EXPECTING_CLOSE_BRACE_FOR_INITIALIZER ); return true; } else { return false; } } private void _doParseObjectInitializer(IType objectType) { ObjectInitializerExpression oie = new ObjectInitializerExpression(); do { if( parseInitializerAssignment( objectType ) ) { InitializerAssignment newAssignment = (InitializerAssignment)popStatement(); for( InitializerAssignment existing : oie.getInitializers() ) { if( existing.getPropertyName() != null && existing.getPropertyName().equals( newAssignment.getPropertyName() ) ) { newAssignment.addParseException( Res.MSG_REDUNTANT_INITIALIZERS, existing.getPropertyName() ); } } oie.add( newAssignment ); } else { verify( oie, false, Res.MSG_EXPECTING_NAME_VALUE_PAIR, makeLightweightParserState() ); } } while( match( null, ',' ) ); oie.setType( objectType ); pushExpression( oie ); } private boolean parseInitializerAssignment( IType objectType ) { int iOffset = _tokenizer.getTokenStart(); int iLineNum = _tokenizer.getLineNumber(); int iColumn = _tokenizer.getTokenColumn(); if( match( null, ":", SourceCodeTokenizer.TT_OPERATOR ) ) { //handle two colons in a row with error message to preserve parse tree //(helps intellisense) boolean bFoundExtraColon = false; if( match( null, ":", SourceCodeTokenizer.TT_OPERATOR ) ) { bFoundExtraColon = true; } parseInitializerIdentifier( objectType ); Identifier identifier = (Identifier)popExpression(); InitializerAssignment se = new InitializerAssignment( objectType, identifier.getSymbol().getName() ); IPropertyInfo propertyInfo = null; try { if( se.getPropertyName() != null ) { propertyInfo = BeanAccess.getPropertyInfo( objectType, se.getPropertyName(), null, this, getVisibilityConstraint() ); } } catch( ParseException e ) { se.addParseException( e ); } IType type = ErrorType.getInstance(); if( propertyInfo != null ) { verifyCase( se, se.getPropertyName(), propertyInfo.getName(), Res.MSG_PROPERTY_CASE_MISMATCH, false ); if( !JavaTypes.COLLECTION().isAssignableFrom( propertyInfo.getFeatureType() ) && !JavaTypes.MAP().isAssignableFrom( propertyInfo.getFeatureType() ) ) { try { verifyPropertyWritable( objectType, se.getPropertyName(), true ); } catch( Exception ex ) { //noinspection ThrowableResultOfMethodCallIgnored se.addParseException( ParseException.wrap( ex, makeFullParserState() ) ); } } type = propertyInfo.getFeatureType(); } identifier.setType( type ); verify( se, match( null, "=", SourceCodeTokenizer.TT_OPERATOR ), Res.MSG_EQUALS_FOR_INITIALIZER_EXPR ); verify( se, !bFoundExtraColon, Res.MSG_ONLY_ONE_COLON_IN_INITIALIZERS ); parseExpression( type ); Expression value = popExpression(); if( value.hasParseExceptions() ) { value.getParseExceptions().get( 0 ).setExpectedType( type ); } se.setRhs( value ); pushStatement( se ); setLocation( iOffset, iLineNum, iColumn ); return true; } return false; } private void parseInitializerIdentifier( IType objectType ) { int iOffset = _tokenizer.getTokenStart(); int iLineNum = _tokenizer.getLineNumber(); int iColumn = _tokenizer.getTokenColumn(); Token t = new Token(); Identifier i = new Identifier(); verify( i, match( t, SourceCodeTokenizer.TT_WORD ), Res.MSG_EXPECTING_NAME_PROPERTY ); i.setSymbol( new InitializerSymbol( t._strValue, objectType ), null); i.setType( objectType ); pushExpression( i ); setLocation( iOffset, iLineNum, iColumn ); } private GosuConstructorInfo getGsConstructorInfo( IConstructorInfo ci, IGosuClassInternal gsInstanceClass ) { if( ci instanceof GosuConstructorInfo ) { return (GosuConstructorInfo)ci; } List<IType> argTypes = new ArrayList<IType>( 2 ); if (ci != null) { for( IParameterInfo pi : ci.getParameters() ) { argTypes.add( pi.getFeatureType() ); } } return (GosuConstructorInfo) gsInstanceClass.getTypeInfo().getConstructor( gsInstanceClass, argTypes.toArray(new IType[argTypes.size()])); } private boolean isConcreteInitializableType( IType type ) { return isInitializableType( type ) && !type.isAbstract() && !type.isInterface(); } private boolean isInitializableType( IType type ) { return type != null && (JavaTypes.COLLECTION().isAssignableFrom( type ) || JavaTypes.MAP().isAssignableFrom( type )); } private void verifyCanConstructInnerClassFromCallSite( NewExpression e, IType innerClass ) { if( !(innerClass instanceof IGosuClassInternal) && !(innerClass instanceof IJavaType) ) { return; } if( Modifier.isStatic( innerClass.getModifiers() ) ) { return; } IType innersEnclosingClass = innerClass.getEnclosingType(); if( innersEnclosingClass == null ) { // Not an inner class return; } IGosuClassInternal constructingFromClass = getScriptPart() != null && getScriptPart().getContainingType() instanceof IGosuClass ? (IGosuClassInternal)getScriptPart().getContainingType() : null; verify( e, constructingFromClass != null && (innersEnclosingClass.isAssignableFrom( constructingFromClass ) || TypeLord.encloses( innersEnclosingClass, constructingFromClass )), Res.MSG_MUST_BE_IN_OUTER_TO_CONSTRUCT_INNER, innersEnclosingClass.getName(), innerClass.getRelativeName() ); } private void _parseInitializerExpression( IType type ) { if( type != null && JavaTypes.COLLECTION().isAssignableFrom( type ) ) { parseCollectionInitializerList( type ); } else if( type != null && JavaTypes.MAP().isAssignableFrom( type ) ) { parseMapInitializerList( type ); } else if( isDynamic( type ) ) { parseCollectionInitializerList( type ); } else { BadInitializerExpression be = new BadInitializerExpression(); pushExpression(be); } } private IConstructorInfo getImplicitConstructor(IType type) { if (type instanceof IErrorType ) { return null; } IConstructorInfo constructor = null; if (!type.isAbstract()) { ITypeInfo typeInfo = type.getTypeInfo(); if (typeInfo instanceof IRelativeTypeInfo) { constructor = ((IRelativeTypeInfo) typeInfo).getConstructor(getGosuClass(), IType.EMPTY_ARRAY); } else { constructor = typeInfo.getConstructor(); } } return constructor; } /* * <i>collection-init-list</i> * <expression> * <collection-init-list> , <expression> */ private void parseCollectionInitializerList( IType type ) { CollectionInitializerExpression lie = new CollectionInitializerExpression(); IType componentType; if( isDynamic( type ) ) { componentType = type.getComponentType(); } else { IType listType = TypeLord.findParameterizedTypeInHierarchy( type, JavaTypes.COLLECTION() ); if( listType.isParameterizedType() && !listType.isGenericType() ) { componentType = listType.getTypeParameters()[0]; } else { componentType = JavaTypes.OBJECT(); } } do { parseExpression( componentType ); Expression e = popExpression(); lie.add( e ); } while( match( null, ',' ) ); lie.setType( type ); pushExpression( lie ); } /* * <i>map-init-list</i> * <key-expression> <b>-></b> <value-expression> ; * <map-init-list> , <expression> */ private void parseMapInitializerList( IType type ) { MapInitializerExpression mie = new MapInitializerExpression(); IType listType = TypeLord.findParameterizedTypeInHierarchy(type, JavaTypes.MAP()); IType keyType = JavaTypes.OBJECT(); IType valueType = JavaTypes.OBJECT(); if( listType.isParameterizedType() ) { keyType = listType.getTypeParameters()[0]; valueType = listType.getTypeParameters()[1]; } do { parseExpression( keyType ); Expression key = popExpression(); Expression value; if( verify( key, match( null, "->", SourceCodeTokenizer.TT_OPERATOR ), Res.MSG_EXPECTING_ARROW_AFTER_MAP_KEY ) ) { parseExpression( valueType ); value = popExpression(); } else { value = new NullExpression(); } mie.add( key, value ); } while( match( null, ',' ) ); pushExpression( mie ); } /* * <i>array-value-list</i> * <expression> * <array-value-list> , <expression> */ List<Expression> parseArrayValueList( IType componentType ) { while( componentType instanceof TypeVariableType ) { componentType = ((TypeVariableType)componentType).getBoundingType(); } List<Expression> valueExpressions = new ArrayList<Expression>(); do { parseExpression( componentType ); Expression e = popExpression(); valueExpressions.add( e ); } while( match( null, ',' ) ); return valueExpressions; } private void parseIndirectMemberAccess( int iOffset, int iLineNum, int iColumn ) { parseIndirectMemberAccess( iOffset, iLineNum, iColumn, false ); } private void parseIndirectMemberAccess( int iOffset, int iLineNum, int iColumn, boolean bParsingTypeLiteralOnly ) { do { if( !_parseIndirectMemberAccess( bParsingTypeLiteralOnly ) ) { Expression expr = peekExpression(); if( !maybeReplaceErrantPackageExprWithEnumConstEpr( iOffset, iLineNum, iColumn, expr ) ) { maybeReplacePackageExprWithTypeLiteral( iOffset, iLineNum, iColumn, expr ); verify( peekExpression(), !(peekExpression().getType() instanceof NamespaceType), Res.MSG_EXPECTING_TYPE_TO_FOLLOW_PACKAGE_NAME ); } break; } setLocation( iOffset, iLineNum, iColumn ); Expression expr = peekExpression(); if( expr instanceof MemberAccess ) { IType inferType = _ctxInferenceMgr.infer( expr ); if( inferType != null ) { popExpression(); expr = possiblyWrapWithImplicitCoercion( expr, inferType ); pushExpression( expr ); } } } while( true ); } private void maybeReplacePackageExprWithTypeLiteral( int iOffset, int iLineNum, int iColumn, Expression expr ) { if( expr instanceof Identifier && expr.getType() instanceof NamespaceType ) { Token T = new Token(); String strNamespace = expr.getType().getName(); T._strValue = strNamespace; T._iDocPosition = iOffset; T._iDocLength = strNamespace.length(); if( getNamespace() != null ) { popExpression(); // Pop existing expression; we're going to transform it to a type literal tryToMakeTypeLiteral( T, iOffset, iLineNum, iColumn, strNamespace, expr ); } } } private boolean maybeReplaceErrantPackageExprWithEnumConstEpr( int iOffset, int iLineNum, int iColumn, Expression expr ) { if( expr instanceof Identifier && expr.getType() instanceof NamespaceType ) { MemberAccess enumConstExpr = parseEnumeratedConstant( ((Identifier)expr).getSymbol().getName() ); if( enumConstExpr != null ) { // Set the errant expression's location so that its subordinate expressions are not // included in the expression we try next... setLocation( iOffset, iLineNum, iColumn ); enumConstExpr.setStartOffset( iOffset ); getLocationsList().remove( peekExpression().getLocation() ); popExpression(); pushExpression( enumConstExpr ); return true; } } return false; } boolean _parseIndirectMemberAccess( boolean bParseTypeLiteralOnly ) { Expression peekRootExpression = peekExpression(); int operatorLineNumber = getTokenizer().getLineNumber(); Token T = new Token(); if( match( null, '.' ) || match( T, "?.", SourceCodeTokenizer.TT_OPERATOR ) || match( T, "*.", SourceCodeTokenizer.TT_OPERATOR ) ) { MemberAccessKind kind = MemberAccessKind.getForOperator( T._strValue ); Expression expression = popExpression(); verify( expression, kind != MemberAccessKind.EXPANSION || TypeLord.getExpandableComponentType( expression.getType() ) != null, Res.MSG_TYPE_IS_NOT_ITERABLE, expression.getType().getName() ); LightweightParserState state = makeLightweightParserState(); verify( expression, match( T, SourceCodeTokenizer.TT_WORD ) || match( T, SourceCodeTokenizer.TT_KEYWORD ), Res.MSG_EXPECTING_MEMBER_ACCESS_PATH ); parseMemberAccess( expression, kind, T.getTokenStart(), T._strValue == null ? "" : T._strValue, state, bParseTypeLiteralOnly ); verifyNonVoidExpression(peekExpression()); } else if( parseFeatureLiteral( peekRootExpression ) ) { // good } else if( !bParseTypeLiteralOnly && (match( null, '[' ) || match( T, "?[", SourceCodeTokenizer.TT_OPERATOR )) ) { Expression rootExpression = popExpression(); IType rootType = rootExpression.getType(); IType indexType = ArrayAccess.supportsArrayAccess( rootType ) ? JavaTypes.pINT() : MapAccess.supportsMapAccess( rootType ) ? MapAccess.getKeyType( rootType ) : null; parseExpression( indexType ); Expression indexExpression = popExpression(); Expression arrayAccess; // Assume null-safety for backward compatibility in non-open-source versions, otherwise make it explicit with "?[" boolean bNullSafe = !ILanguageLevel.Util.STANDARD_GOSU() || (T._strValue != null && T._strValue.equals( "?[" )); if( ArrayAccess.supportsArrayAccess( rootType ) ) { ArrayAccess aa = new ArrayAccess(); aa.setRootExpression( rootExpression ); if( (indexExpression.getType() != JavaTypes.pINT() || indexExpression.getType() != JavaTypes.INTEGER()) && !(indexExpression.getType() instanceof IErrorType || (indexExpression.getType() instanceof IMetaType) && ((IMetaType)indexExpression.getType()).getType() instanceof IErrorType) ) { aa.setMemberExpression( indexExpression ); } else { verify( indexExpression, false, Res.MSG_ARRAY_INDEX_MUST_BE_INT ); } aa.setNullSafe( bNullSafe ); pushExpression( aa ); arrayAccess = aa; } else if( MapAccess.supportsMapAccess( rootType ) ) { MapAccess ma = new MapAccess(); ma.setRootExpression( rootExpression ); ma.setKeyExpression( indexExpression ); ma.setNullSafe( bNullSafe ); pushExpression( ma ); arrayAccess = ma; } else { verify( rootExpression, BeanAccess.isBeanType( rootExpression.getType() ) || (rootExpression.getType() instanceof MetaType), Res.MSG_EXPECTING_BEAN_TYPE_WITH_REFLECTION_OPERATOR ); verify( indexExpression, JavaTypes.CHAR_SEQUENCE().isAssignableFrom( indexExpression.getType() ), Res.MSG_PROPERTY_REFLECTION_ONLY_WITH_STRINGS ); MemberAccess ma = new MemberAccess(); ma.setRootExpression( rootExpression ); ma.setMemberExpression( indexExpression ); ma.setType( JavaTypes.OBJECT() ); ma.setMemberAccessKind( bNullSafe ? MemberAccessKind.NULL_SAFE : MemberAccessKind.NORMAL ); pushExpression( ma ); arrayAccess = ma; } verify( arrayAccess, match( null, ']' ), Res.MSG_EXPECTING_BRACKET_TO_CLOSE_DYNAMIC_MEMBER_ACCESS ); } else if( peekExpression().getType() instanceof IBlockType && match( null, '(' ) ) { IBlockType iBlockType = (IBlockType)peekExpression().getType(); BlockInvocation bi = new BlockInvocation( popExpression() ); boolean bNoArgsProvided; if( !(bNoArgsProvided = match( null, ')' )) || iBlockType.hasOptionalParams() ) { MethodScore score = parseArgumentList( bi, Collections.singletonList( iBlockType ), IType.EMPTY_ARRAY, true, bNoArgsProvided, null); bi.setNamedArgOrder( score.getNamedArgOrder() ); bi.setArgs( score.getArguments() ); verify( bi, bNoArgsProvided || match( null, ')' ), Res.MSG_EXPECTING_FUNCTION_CLOSE ); } verify( bi, bi.getArgs().size() == iBlockType.getParameterTypes().length, Res.MSG_WRONG_NUM_OF_ARGS, iBlockType.getParameterTypes().length ); bi.setType( iBlockType.getReturnType() ); verifyNonVoidExpression( bi ); pushExpression( bi ); } else { return false; } // Reroot the root expression under the indirect access if( peekRootExpression != null && !peekRootExpression.hasParseExceptions() ) { ParseTree rootLocation = peekRootExpression.getLocation(); if( rootLocation != null ) { getLocationsList().remove( peekRootExpression.getLocation() ); setLocation( rootLocation.getOffset(), rootLocation.getLineNum(), rootLocation.getColumn() ); peekExpression().getLocation().addChild( peekRootExpression.getLocation() ); } } setOperatorLineNumber(peekExpression(), operatorLineNumber); return true; } private boolean parseFeatureLiteral( Expression root ) { Token T = new Token(); if( match( null, "#", SourceCodeTokenizer.TT_OPERATOR ) ) { if( root != popExpression() ) { throw new IllegalStateException(); } FeatureLiteral fle = new FeatureLiteral( root ); boolean foundWord = verify( fle, match( T, SourceCodeTokenizer.TT_WORD ) || match( T, Keyword.KW_construct ), Res.MSG_FL_EXPECTING_FEATURE_NAME ); if( foundWord ) { if( match( null, '<' ) ) { while( verify( fle, parseTypeLiteral(), null ) ) { popExpression(); // TypeLiteral expr if( !match( null, ',' ) ) { break; } } verify( fle, match( null, '>' ), Res.MSG_FL_EXPECTING_RIGHT_CARET ); verify( fle, false, Res.MSG_FL_GENERIC_FUNCTION_REFERENCES_NOT_YET_SUPPORTED ); } if( match( null, '(' ) ) { if( !match( null, ')' ) ) { MethodScore score = parseArgumentList( fle, fle.getFunctionTypes( T._strValue ), IType.EMPTY_ARRAY, true, false, null); boolean allTypeLiterals = true; for( IExpression arg : score.getArguments() ) { if( !(arg instanceof TypeLiteral || (arg instanceof ImplicitTypeAsExpression && ((ImplicitTypeAsExpression)arg).getLHS() instanceof TypeLiteral)) ) { allTypeLiterals = false; } } if( allTypeLiterals ) { List<IType> types = new ArrayList<IType>(); for( IExpression expression : score.getArguments() ) { expression.clearParseExceptions(); expression.clearParseWarnings(); if( expression instanceof ImplicitTypeAsExpression ) { expression = ((ImplicitTypeAsExpression)expression).getLHS(); } types.add( ((TypeLiteral)expression).evaluate() ); } IType[] typesArr = types.toArray( new IType[types.size()] ); if( T._strValue.equals( Keyword.KW_construct.getName() ) ) { verify( fle, fle.resolveConstructor( typesArr ), Res.MSG_FL_CONSTRUCTOR_NOT_FOUND, StringUtil.join( ",", types ) ); } else { verify( fle, fle.resolveMethod( T._strValue, typesArr ), Res.MSG_FL_METHOD_NOT_FOUND, T._strValue, StringUtil.join(",", types) ); } } else { IInvocableType funcType = score.getRawFunctionType(); if( funcType == null ) { fle.setBoundFeature( ErrorType.getInstance().getTypeInfo().getMethod( T._strValue ), score.getArguments() ); } else if( funcType instanceof FunctionType ) { fle.setBoundFeature( ((FunctionType)funcType).getMethodInfo(), score.getArguments() ); } else { fle.setBoundFeature( ((ConstructorType)funcType).getConstructor(), score.getArguments() ); } } verify( fle, match( null, ')' ), Res.MSG_FL_EXPECTING_RIGHT_PAREN ); } else { if( T._strValue.equals( Keyword.KW_construct.getName() ) ) { verify( fle, fle.resolveConstructor(), Res.MSG_FL_CONSTRUCTOR_NOT_FOUND, "" ); } else { verify( fle, fle.resolveMethod( T._strValue ), Res.MSG_FL_METHOD_NOT_FOUND, T._strValue, "" ); } } } else { boolean propResolved = fle.resolveProperty( T._strValue ); if( !propResolved ) { verify( fle, propResolved, Res.MSG_FL_PROPERTY_NOT_FOUND, T._strValue ); } } } if( fle.isStaticish() ) { verify( fle, root instanceof TypeLiteral, Res.MSG_FL_STATIC_FEATURES_MUST_BE_REFERENCED_FROM_THEIR_TYPES ); } fle.resolveType(); pushExpression( fle ); return true; } else { return false; } } private void setOperatorLineNumber( Expression expression, int operatorLineNumber ) { if( expression instanceof IHasOperatorLineNumber ) { ((IHasOperatorLineNumber)expression).setOperatorLineNumber( operatorLineNumber ); } } boolean parseNameOrMethodCall() { if( match( null, Keyword.KW_true, true ) | match( null, Keyword.KW_false, true ) | match( null, Keyword.KW_NaN, true ) | match( null, Keyword.KW_Infinity, true ) | match( null, Keyword.KW_null, true ) ) { return false; } int iOffset = _tokenizer.getTokenStart(); int iLineNum = _tokenizer.getLineNumber(); int iColumn = getTokenizer().getTokenColumn(); boolean bRet = _parseNameOrMethodCall(); if( bRet ) { verifyNonVoidExpression(peekExpression()); setLocation( iOffset, iLineNum, iColumn ); } Expression expr = peekExpression(); if( expr instanceof Identifier ) { IType inferType = _ctxInferenceMgr.infer( expr ); if( inferType != null ) { Identifier ma = (Identifier)popExpression(); expr = possiblyWrapWithImplicitCoercion( ma, inferType ); //ma.setType( inferType ); pushExpression( expr ); } } return bRet; } boolean _parseNameOrMethodCall() { Token T = new Token(); int iOffset = _tokenizer.getTokenStart(); int iLineNum = _tokenizer.getLineNumber(); int iColumn = getTokenizer().getTokenColumn(); IParserState state = makeLightweightParserState(); //capture position of word for error reporting int markBefore = getTokenizer().mark(); if( match( T, SourceCodeTokenizer.TT_WORD ) || match( T, Keyword.KW_super ) || match( T, Keyword.KW_this ) ) { MethodCallExpression e = new MethodCallExpression(); IType[] typeParameters = parsePossibleFunctionParameterization( T, e ); String strFunction = T._strValue; ISymbol functionSymbol = possiblyResolveFunctionSymbol( e, strFunction ); int mark = _tokenizer.mark(); // Only parse the function symbol as a method invocation if it is not a block. Blocks parse as identifiers // and then indirect BlockInvocation expressions if( !isBlockSym( functionSymbol ) && !isInSeparateStringTemplateExpression() && match( null, '(' ) ) { parseMethodCall( T, iOffset, iLineNum, iColumn, state, e, typeParameters, strFunction, functionSymbol, mark ); } else if( !Keyword.KW_super.equals( T._strValue ) || getTokenizer().getCurrentToken().getType() == '.' ) { parseIdentifierOrTypeLiteralOrEnumConstant( T, iOffset, iLineNum, iColumn ); } else { getTokenizer().restoreToMark( markBefore ); return false; } verify( peekExpression(), !GosuObjectUtil.equals( strFunction, Keyword.KW_this.toString() ) || !isParsingStaticFeature() || isParsingConstructor(), Res.MSG_CANNOT_REFERENCE_THIS_IN_STATIC_CONTEXT ); return true; } return false; } private void parseMethodCall( Token t, int iOffset, int iLineNum, int iColumn, IParserState state, MethodCallExpression e, IType[] typeParameters, String strFunction, ISymbol functionSymbol, int mark ) { int iLocationsCount = _locations.size(); parseMethodCall( t, state, e, typeParameters, strFunction, functionSymbol ); Expression expr = peekExpression(); if( expr.hasParseExceptions() ) { maybeParseIdentifierAssumingOpenParenIsForParenthesizedExpr( t, iOffset, iLineNum, iColumn, state, e, typeParameters, strFunction, functionSymbol, mark, iLocationsCount ); } } private void maybeParseIdentifierAssumingOpenParenIsForParenthesizedExpr( Token t, int iOffset, int iLineNum, int iColumn, IParserState state, MethodCallExpression e, IType[] typeParameters, String strFunction, ISymbol functionSymbol, int mark, int iLocationsCount ) { if( !isOpenParenOnNextLine( mark ) ) { return; } backtrack( mark, iLocationsCount ); parseIdentifierOrTypeLiteralOrEnumConstant( t, iOffset, iLineNum, iColumn ); Expression expr = peekExpression(); if( expr.hasParseExceptions() ) { // Failed to parse as Identifier etc., reparse as Method call _tokenizer.restoreToMark( mark ); expr = popExpression(); removeInnerClasses( expr ); removeLocationsFrom( iLocationsCount ); parseMethodCall( t, state, e, typeParameters, strFunction, functionSymbol ); } } private void backtrack( int mark, int iLocationsCount ) { _tokenizer.restoreToMark( mark ); Expression expr = popExpression(); removeInnerClasses( expr ); removeLocationsFrom( iLocationsCount ); } private void parseMethodCall( Token t, IParserState state, MethodCallExpression e, IType[] typeParameters, String strFunction, ISymbol functionSymbol ) { // MethodCall if( functionSymbol == null && getGosuClass() != null ) { functionSymbol = getGosuClass().getExternalSymbol( strFunction ); } if( functionSymbol != null ) { IType symbolType = functionSymbol.getType(); if( symbolType instanceof IFunctionType ) { int mark = getTokenizer().mark(); int iLocationsCount = _locations.size(); parsePlainFunction( (IFunctionSymbol)functionSymbol ); verifyCase( peekExpression(), strFunction, functionSymbol.getName(), state, Res.MSG_FUNCTION_CASE_MISMATCH, false ); if( peekExpression().hasParseException( Res.MSG_WRONG_NUMBER_OF_ARGS_TO_FUNCTION ) ) { // maybe there's a print() method defined locally... backtrack( mark, iLocationsCount ); } else { return; } } if( isDynamic( symbolType ) ) { parseDynamicFunction( (Symbol)functionSymbol ); verifyCase( peekExpression(), strFunction, functionSymbol.getName(), state, Res.MSG_FUNCTION_CASE_MISMATCH, false ); return; } } Expression exp = e; int iParenStart = _tokenizer.getTokenStart(); e.setArgPosition( iParenStart ); List<IFunctionType> listFunctionTypes = null; boolean bNoArgsProvided; if( !(bNoArgsProvided = match( null, ')' )) || ((listFunctionTypes = getFunctionTypesForName( strFunction )).size() == 1 && listFunctionTypes.get( 0 ).hasOptionalParams()) ) { if( listFunctionTypes == null ) { listFunctionTypes = getFunctionTypesForName( strFunction ); } if( typeParameters != null ) { listFunctionTypes = parameterizeFunctionTypes( e, typeParameters, listFunctionTypes ); } pushInferredFunctionTypeVariableTypes( listFunctionTypes ); MethodScore bestMethod; try { bestMethod = parseArgumentList( e, listFunctionTypes, typeParameters, true, bNoArgsProvided, null); } finally { popInferredFunctionTypeVariableTypes(); } //noinspection SuspiciousToArrayCall Expression[] eArgs = bestMethod.getArguments().toArray( new Expression[bestMethod.getArguments().size()] ); e.setArgs( eArgs ); boolean bMatched = bestMethod.isValid() && bestMethod.getRawFunctionType().getParameterTypes().length == eArgs.length; for( Expression arg : eArgs ) { if( arg.hasParseExceptions() ) { bMatched = false; break; } } if( !bMatched ) { IType entityType = resolveTypeName( t._strValue, true ); if( entityType != null && CommonServices.getEntityAccess().isEntityClass( entityType ) ) { ObjectLiteralExpression objE = new ObjectLiteralExpression( entityType ); IType[] argTypes = new IType[]{GosuParserTypes.STRING_TYPE(), GosuParserTypes.STRING_TYPE()}; if( verify( objE, bestMethod.getArguments().size() > 0, Res.MSG_EXPECTING_ARGS, t._strValue ) ) { if( verify( objE, bestMethod.getArguments().size() == 1 || bestMethod.getArguments().size() == argTypes.length, Res.MSG_WRONG_NUM_OF_ARGS, t._strValue + ". Expecting " + argTypes.length + " or 1" ) ) { warn( objE, false, Res.MSG_OBJECT_LITERALS_DEPRECATED ); } } objE.setArgs( eArgs ); exp = objE; } else if( listFunctionTypes.isEmpty() ) { if( staticRefToNonStaticFunc( strFunction, eArgs ) ) { verify( e, false, state, Res.MSG_CANNOT_CALL_NON_STATIC_METHOD_FROM_STATIC_CONTEXT, strFunction ); } else { verify( e, false, state, Res.MSG_NO_SUCH_FUNCTION, strFunction ); } } } if( exp == e ) { if( bestMethod.isValid() ) { // Did not parse as object literal IFunctionType rawFunctionType = (IFunctionType)bestMethod.getRawFunctionType(); verifyArgCount( e, bestMethod.getArguments().size(), rawFunctionType ); if( !(GosuObjectUtil.equals( strFunction, Keyword.KW_super.toString() ) || GosuObjectUtil.equals( strFunction, Keyword.KW_this.toString() )) ) { verifyCase( e, strFunction, rawFunctionType.getName(), state, Res.MSG_FUNCTION_CASE_MISMATCH, false ); } e.setFunctionSymbol( getDFSForFunctionType( strFunction, bestMethod ) ); e.setNamedArgOrder( bestMethod.getNamedArgOrder() ); IFunctionType inferredFunctionType = (IFunctionType)bestMethod.getInferredFunctionType(); if( inferredFunctionType instanceof FunctionType ) { ((FunctionType)inferredFunctionType).setScriptPart( rawFunctionType.getScriptPart() ); } e.setType( inferredFunctionType.getReturnType() ); e.setFunctionType( inferredFunctionType ); } else { e.setType( ErrorType.getInstance() ); } } verify( exp, bNoArgsProvided || match( null, ')' ), Res.MSG_EXPECTING_FUNCTION_CLOSE ); } else { IFunctionSymbol function = (IFunctionSymbol)getSymbolTable().getSymbol( strFunction + "()" ); if( function == null ) { function = (IFunctionSymbol)getSymbolTable().getSymbol( strFunction ); } // check for property match *before* checking for overloading if( function == null || !(function.getType() instanceof IFunctionType)) { String strPropertyName = getPropertyNameFromMethodName( strFunction ); boolean bPossiblePropertyName = getGosuClass() != null && strPropertyName != null; if( bPossiblePropertyName ) { t._strValue = strPropertyName; parseIdentifier( new PropertyAsMethodCallIdentifier( strFunction ), t ); Expression expression = peekExpression(); if( !expression.hasParseExceptions() ) { return; } popExpression(); } } if( function == null ) { listFunctionTypes = getFunctionTypesForName( strFunction ); if( !listFunctionTypes.isEmpty() ) { verifyArgCount( e, 0, listFunctionTypes.get( 0 ) ); } List<IFunctionSymbol> possibleMatches = getDfsDeclsForFunction( strFunction ); if( !possibleMatches.isEmpty() ) { function = possibleMatches.get( 0 ); e.setFunctionSymbol( function ); } } if( function == null || !(function.getType() instanceof IFunctionType) ) { if( staticRefToNonStaticFunc( strFunction, new Expression[0] ) ) { verify( e, false, state, Res.MSG_CANNOT_CALL_NON_STATIC_METHOD_FROM_STATIC_CONTEXT, strFunction ); } else { verify( e, false, state, Res.MSG_NO_SUCH_FUNCTION, strFunction ); } e.setType( ErrorType.getInstance() ); } else { e.setFunctionSymbol( function ); verifyCase( e, strFunction, function.getDisplayName(), state, Res.MSG_FUNCTION_CASE_MISMATCH, false ); IFunctionType funcType = (IFunctionType)function.getType(); if( typeParameters != null ) { if( verifyCanParameterizeType( e, funcType, typeParameters ) ) { IFunctionType parameterizedFuncType = (IFunctionType)funcType.getParameterizedType( typeParameters ); if( verify( e, parameterizedFuncType != null, Res.MSG_COULD_NOT_PARAMETERIZE ) ) { funcType = parameterizedFuncType; } } } assert funcType != null; funcType = maybeParameterizeOnCtxType( funcType ); IFunctionType boundFuncType = boundFunctionType( funcType ); e.setType( boundFuncType.getReturnType() ); e.setFunctionType( boundFuncType ); } e.setArgs( null ); } if( isParsingAbstractConstructor() && e.getFunctionSymbol() instanceof DynamicFunctionSymbol && e.getFunctionSymbol().isAbstract() ) { e.addParseException( new ParseException( state, Res.MSG_NO_ABSTRACT_METHOD_CALL_IN_CONSTR, e.getFunctionSymbol().getDisplayName() ) ); } if( exp instanceof MethodCallExpression ) { verifyNotCallingOverridableFunctionFromCtor( (MethodCallExpression)exp ); if( getGosuClass() != null && getGosuClass().isAnonymous() && getGosuClass().getEnclosingType() instanceof IGosuEnhancement ) { verify( e, false, Res.MSG_CANNOT_REFERENCE_ENCLOSING_METHODS_WITHIN_ENHANCEMENTS ); } } pushExpression( exp ); } private boolean isInSeparateStringTemplateExpression() { // If parsing a template or string template, look to see if we are parsing tokens within a // separate template expression. We want to avoid fusing to expressions together // eg. "${x} ${(x)}" will otherwise try to parse the tokens of the first expr along with the // tokens of the second as a method bad call instead of just stopping after the first. // We can determine if we've crossed an expression boundary by examining the prior token and // checking for the '}' terminal in the whitespace (non-source code content is considered // whitespace while parsing a template). IToken priorToken = getTokenizer().getTokenAt( getTokenizer().getState() - 1 ); return priorToken != null && priorToken.getType() == ISourceCodeTokenizer.TT_WHITESPACE && (priorToken.getStringValue().indexOf( '}' ) >= 0 || priorToken.getStringValue().indexOf( '>' ) >= 0); } private ISymbol possiblyResolveFunctionSymbol( MethodCallExpression e, String strFunction ) { ISymbol functionSymbol = _symTable.getSymbol( strFunction ); if( functionSymbol == null ) { // Try to find a captured closure functionSymbol = resolveSymbol( e, strFunction, false ); //noinspection ThrowableResultOfMethodCallIgnored e.removeParseException( Res.MSG_BAD_IDENTIFIER_NAME ); if( !(functionSymbol instanceof CapturedSymbol) ) { functionSymbol = null; } } return functionSymbol; } private boolean isBlockSym( ISymbol functionSymbol ) { return functionSymbol != null && functionSymbol.getType() instanceof IBlockType; } private boolean staticRefToNonStaticFunc( String stFunction, Expression[] eArgs ) { if( isParsingStaticFeature() && getGosuClass() != null ) { List<? extends IDynamicFunctionSymbol> matchingDFSs = getGosuClass().getMemberFunctions( stFunction ); for( IDynamicFunctionSymbol dfs : matchingDFSs ) { if( !dfs.isStatic() && dfs.getArgs().size() == eArgs.length ) { return true; } } } return false; } private boolean staticRefToNonStaticProp( String name ) { if( isParsingStaticFeature() && getGosuClass() != null ) { IDynamicPropertySymbol dps = getGosuClass().getMemberProperty( name ); if( dps != null && !dps.isStatic() ) { return true; } } return false; } private IFunctionType boundFunctionType( IFunctionType funcType ) { if( funcType.isGenericType() ) { IGenericTypeVariable[] typeVariables = funcType.getGenericTypeVariables(); List<IType> functionTypeVars = new ArrayList<IType>(); for( IGenericTypeVariable typeVariable : typeVariables ) { ITypeVariableDefinition typeVariableDefinition = typeVariable.getTypeVariableDefinition(); if( typeVariableDefinition != null && typeVariableDefinition.getType() != null ) { functionTypeVars.add( typeVariableDefinition.getType() ); } } return (IFunctionType)TypeLord.boundTypes( funcType, functionTypeVars ); } else { return funcType; } } private IDynamicFunctionSymbol getDFSForFunctionType( String strFunction, MethodScore bestMethod ) { List<IFunctionSymbol> list = getDfsDeclsForFunction( strFunction ); IInvocableType rawFunctionType = bestMethod.getRawFunctionType(); for( IFunctionSymbol dfs : list ) { IType dfsType = dfs.getType(); if( dfsType.equals( rawFunctionType ) || dfsType.equals( rawFunctionType.getGenericType() ) ) { return (IDynamicFunctionSymbol) dfs; } } throw new IllegalStateException( "Could not find matching DFS in " + list + " for type " + rawFunctionType ); } private void verifyNotCallingOverridableFunctionFromCtor( MethodCallExpression mce ) { if( !isParsingConstructor() || isParsingBlock() ) { return; } IFunctionSymbol fs = mce.getFunctionSymbol(); if( fs instanceof DynamicFunctionSymbol && !(fs instanceof SuperConstructorFunctionSymbol) && !(fs instanceof ThisConstructorFunctionSymbol) ) { DynamicFunctionSymbol dfs = (DynamicFunctionSymbol)fs; verifyOrWarn( mce, dfs.isPrivate() || dfs.isFinal() || dfs.isStatic(), true, Res.MSG_CALLING_OVERRIDABLE_FROM_CTOR, dfs.getName() ); } } private void verifyNotCallingOverridableFunctionFromCtor( BeanMethodCallExpression mce ) { if( !isParsingConstructor() || isParsingBlock() ) { return; } if( mce.getRootExpression() instanceof Identifier && ((Identifier)mce.getRootExpression()).getSymbol() != null && Keyword.KW_this.equals( ((Identifier)mce.getRootExpression()).getSymbol().getName() ) ) { IMethodInfo mi = mce.getMethodDescriptor(); if( mi instanceof AbstractGenericMethodInfo ) { ReducedDynamicFunctionSymbol dfs = ((AbstractGenericMethodInfo)mi).getDfs(); if (!((AbstractGenericMethodInfo)mi).getDfs().isSuperOrThisConstructor()) { verifyOrWarn( mce, dfs.isPrivate() || dfs.isFinal() || dfs.isStatic(), true, Res.MSG_CALLING_OVERRIDABLE_FROM_CTOR, dfs.getName() ); } } } } private boolean isParsingConstructor() { if( !isParsingFunction() ) { return false; } FunctionType funcType = peekParsingFunction(); return funcType.getReturnType() == getGosuClass() && funcType.getName().equals( funcType.getReturnType().getRelativeName() ); } private IType[] parsePossibleFunctionParameterization( Token T, MethodCallExpression e ) { if( match( null, "<", SourceCodeTokenizer.TT_OPERATOR, true ) ) { List<IFunctionType> listFunctionTypes = getFunctionTypesForName( T._strValue ); for( IFunctionType ftype : listFunctionTypes ) { if( ftype.isGenericType() ) { return parseFunctionParameterization( e ); } } } return null; } private void parseIdentifier( Token T ) { parseIdentifier( new Identifier(), T ); } private void parseIdentifier(Identifier e, Token T) { // Identifier String name = T._strValue; ISymbol s = resolveSymbol( e, name, true ); if( s instanceof DynamicFunctionSymbol ) { // No function symbols allowed as identifiers; use a blocks for that s = resolveNamespaceSymbol( e, name ); } else if( s instanceof DynamicPropertySymbol ) { //wrap in a PropertyAccessIdentifier to distinguish between identifiers that //invoke code versus those that do not e = new PropertyAccessIdentifier( e ); } IType type = s.getType(); e.setSymbol( s, _symTable ); e.setType( type ); if( s instanceof DynamicPropertySymbol ) { if( getGosuClass() != null && !(getGosuClass() instanceof IGosuProgram) && getGosuClass().isAnonymous() && getGosuClass().getEnclosingType() instanceof IGosuEnhancement ) { if( s.getName().equals( Keyword.KW_outer.toString() ) ) { verify( e, false, Res.MSG_CANNOT_REFERENCE_OUTER_SYMBOL_WITHIN_ENHANCEMENTS ); } else { verify( e, false, Res.MSG_CANNOT_REFERENCE_ENCLOSING_PROPERTIES_WITHIN_ENHANCEMENTS ); } } } pushExpression( e ); verify( e, !(getCurrentEnclosingGosuClass() instanceof IBlockClass) || !Keyword.KW_super.equals( s.getName() ), Res.MSG_SUPER_NOT_ACCESSIBLE_FROM_BLOCK ); if( !(type instanceof ErrorType) && !(type instanceof IFunctionType) ) { //resolve a symbol, let's make sure its case is correct verifyCase( e, name, s.getName(), Res.MSG_VAR_CASE_MISMATCH, false ); } } private void parseIdentifierOrTypeLiteralOrEnumConstant( Token T, int iOffset, int iLineNum, int iColumn ) { // Identifier String name = T._strValue; parseIdentifier( T ); Expression identifier = peekExpression(); IType type = identifier.getType(); if( type instanceof ErrorType || (type instanceof IFunctionType && !(type instanceof IBlockType))) { // Couldn't parse an identifer , lets try parsing static member-access or a type literal... // // Note we must parse a type literal here instead of (or in addition to) // parseLiteral() because Identifiers and TypeLiterals have the same // start symbol. // Set the errant expression's location so that its subordinate expressions are not // included in the expression we try next... setLocation( iOffset, iLineNum, iColumn ); getLocationsList().remove( peekExpression().getLocation() ); Expression errantExpression = popExpression(); // See if it can be parsed as an inferred enum expression MemberAccess enumConstExpr = parseEnumeratedConstant( T._strValue ); if( enumConstExpr != null ) { pushExpression( enumConstExpr ); enumConstExpr.setStartOffset( iOffset ); } else { // Try parsing a type literal tryToMakeTypeLiteral( T, iOffset, iLineNum, iColumn, name, errantExpression ); } } } private void tryToMakeTypeLiteral( Token T, int iOffset, int iLineNum, int iColumn, String name, Expression errantExpression ) { TypeLiteral tl = resolveTypeLiteral( T ); boolean bArrayOrParameterzied = resolveArrayOrParameterizationPartOfTypeLiteral( T, false, tl ); if( !bArrayOrParameterzied && ((TypeLiteral)peekExpression()).getType().getType() instanceof ErrorType ) { popExpression(); // All has failed. // Try to determine the most suitable errant expression and push that if( staticRefToNonStaticProp( name ) ) { errantExpression.clearParseExceptions(); verify( errantExpression, false, Res.MSG_CANNOT_REFERENCE_NON_STATIC_PROPERTY_FROM_STATIC_CONTEXT ); } pushExpression( errantExpression ); } else if( match( null, "&", SourceCodeTokenizer.TT_OPERATOR, true ) ) { setLocation( iOffset, iLineNum, iColumn ); // set location of initial type literal (tl) parseAggregateTypeLiteral(false); } } private void parseNamespaceStartOrRelativeType( Token T, boolean bInterface ) { IType type = resolveNamespace( T._strValue ); if( type != null ) { Identifier e = new Identifier(); ISymbol s = new Symbol( T._strValue, type, null ); e.setSymbol( s, _symTable ); e.setType( type ); pushExpression( e ); } else { TypeLiteral tl = resolveTypeLiteral( T, true, bInterface ); resolveArrayOrParameterizationPartOfTypeLiteral( T, true, tl ); } } private MemberAccess parseEnumeratedConstant( String strConstValue ) { if( _inferredContextStack.size() > 0 && _inferredContextStack.peek() != null ) { List<IType> contextTypes = _inferredContextStack.peek(); for( IType contextType : contextTypes ) { if( contextType.isEnum() ) { IEnumType enumAccess = (IEnumType)contextType; try { IPropertyInfo property = enumAccess.getTypeInfo().getProperty( strConstValue ); if( property != null && property.isStatic() ) { MemberAccess ma = new MemberAccess(); TypeLiteral e = new TypeLiteral( MetaType.getLiteral( property.getOwnersType() ) ); ma.setRootExpression( e ); ma.setMemberName( property.getName() ); ma.setType( property.getFeatureType() ); ma.setMemberAccessKind( MemberAccessKind.NORMAL ); return ma; } } catch( IllegalArgumentException iae ) { // skip } } } } return null; } private void verifyArgCount( ParsedElement element, int iArgs, IFunctionType funcType ) { int expectedArgs = funcType.getParameterTypes().length; if( iArgs != expectedArgs ) { ParseException pe = new WrongNumberOfArgsException( makeFullParserState(), Res.MSG_WRONG_NUMBER_OF_ARGS_TO_FUNCTION, funcType.getParamSignature(), expectedArgs, iArgs ); pe.setParamTypesExpected( funcType.getParameterTypes() ); pe.setCausedByArgumentList( true ); element.addParseException( pe ); } } private void verifyOverrideNotOnMethodThatDoesNotExtend( ParsedElement element, DynamicFunctionSymbol dfs ) { if( !dfs.isOverride() ) { return; } ParseException pe = null; ICompilableType gsClass = getGosuClass(); if( gsClass != null ) { String strPotentialProperty = getPropertyNameFromMethodNameIncludingSetter( dfs.getDisplayName() ); if( strPotentialProperty != null ) { pe = new DoesNotOverrideFunctionException( makeFullParserState(), Res.MSG_FUNCTION_NOT_OVERRIDE_PROPERTY, dfs.getName(), strPotentialProperty ); } } if( pe == null ) { pe = new DoesNotOverrideFunctionException( makeFullParserState(), Res.MSG_FUNCTION_NOT_OVERRIDE, dfs.getName() ); } element.addParseException( pe ); } private void verifyArgCount( ParsedElement element, int iArgs, IConstructorType ctorType ) { int expectedArgs = ctorType.getParameterTypes().length; if( iArgs != expectedArgs ) { ParseException pe = new ParseException( makeFullParserState(), Res.MSG_WRONG_NUMBER_OF_ARGS_TO_CONSTRUCTOR, ctorType.getArgSignature(), expectedArgs, iArgs ); pe.setParamTypesExpected( ctorType.getParameterTypes() ); pe.setCausedByArgumentList( true ); element.addParseException( pe ); } } private void parsePlainFunction( IFunctionSymbol functionSymbol ) { MethodCallExpression e = new MethodCallExpression(); e.setFunctionSymbol( functionSymbol ); IFunctionType funcType = (IFunctionType)functionSymbol.getType(); e.setType( funcType.getReturnType() ); IType[] argTypes = funcType.getParameterTypes(); boolean bNoArgsProvided; if( !(bNoArgsProvided = match( null, ')' )) || funcType.hasOptionalParams() ) { verify( e, argTypes != null && argTypes.length > 0, Res.MSG_NO_ARGUMENTS, functionSymbol.getName() ); MethodScore score = parseArgumentList( e, Collections.singletonList( funcType ), null, true, bNoArgsProvided, null); if( score.isValid() ) { List<IExpression> scoreArgs = score.getArguments(); verifyArgCount( e, scoreArgs.size(), funcType ); //noinspection SuspiciousToArrayCall e.setArgs( scoreArgs.toArray( new Expression[scoreArgs.size()] ) ); e.setNamedArgOrder( score.getNamedArgOrder() ); } verify( e, bNoArgsProvided || match( null, ')' ), Res.MSG_EXPECTING_FUNCTION_CLOSE ); } else { verify( e, argTypes == null || argTypes.length == 0, Res.MSG_EXPECTING_ARGS, functionSymbol.getName() ); e.setArgs( null ); } pushExpression( e ); } private void parseDynamicFunction( Symbol dynamcSymbol ) { MethodCallExpression e = new MethodCallExpression(); e.setFunctionSymbol( dynamcSymbol ); e.setType( dynamcSymbol.getType() ); if( !match( null, ')' ) ) { MethodScore score = parseArgumentList( e, Collections.singletonList( new FunctionType( dynamcSymbol.getName(), dynamcSymbol.getType(), IType.EMPTY_ARRAY ) ), null, true, false, null ); List<IExpression> scoreArgs = score.getArguments(); //noinspection SuspiciousToArrayCall e.setArgs( scoreArgs.toArray( new Expression[scoreArgs.size()] ) ); e.setNamedArgOrder( score.getNamedArgOrder() ); verify( e, match( null, ')' ), Res.MSG_EXPECTING_FUNCTION_CLOSE ); } else { e.setArgs( null ); } pushExpression( e ); } private void parseMemberAccess( Expression rootExpression, MemberAccessKind kind, int iTokenStart, String strMemberName, LightweightParserState state, boolean bParseTypeLiteralOnly ) { parseMemberAccess( rootExpression, kind, iTokenStart, strMemberName, state, bParseTypeLiteralOnly, false ); } private void parseMemberAccess( Expression rootExpression, MemberAccessKind kind, final int iTokenStart, final String strMemberName, LightweightParserState state, boolean bParseTypeLiteralOnly, boolean createSynthesizedProperty ) { BeanMethodCallExpression e = new BeanMethodCallExpression(); IType rootType = rootExpression.getType(); rootType = IGosuClass.ProxyUtil.isProxy( rootType ) && rootType instanceof IGosuClass ? ((IGosuClass) rootType).getJavaType() : rootType; if( rootType != null && !rootType.isArray() ) { boolean bAcceptableType = BeanAccess.isBeanType( rootType ) || rootType == GosuParserTypes.BOOLEAN_TYPE() || rootType == GosuParserTypes.STRING_TYPE() || rootType == GosuParserTypes.NUMBER_TYPE() || rootType instanceof MetaType; verify( e, bAcceptableType, Res.MSG_EXPECTING_BEANTYPE, rootType.getName() ); } boolean bExpansion = kind == MemberAccessKind.EXPANSION; IType[] typeParameters = null; try { if( !(bParseTypeLiteralOnly || rootType instanceof ErrorType) ) { List<IFunctionType> list = new ArrayList<IFunctionType>(); // if any function with the specified name is generic, parse parameterization getFunctionType( bExpansion ? TypeLord.getExpandableComponentType( rootType ) : rootType, strMemberName, null, list, this, true ); for( IFunctionType ftype : list ) { if( ftype.isGenericType() ) { typeParameters = parseFunctionParameterization( e ); break; } } } } catch( ParseException pe ) { e.addParseException( pe ); } int iParenStart = _tokenizer.getTokenStart(); int mark = _tokenizer.mark(); if( !bParseTypeLiteralOnly && !isBlockInvoke( rootExpression, strMemberName ) && match( null, '(' ) ) { // Method call parseMethodMember( rootExpression, kind, iTokenStart, strMemberName, state, bParseTypeLiteralOnly, createSynthesizedProperty, e, rootType, bExpansion, typeParameters, iParenStart, mark ); } else { // Property access parsePropertyMember( rootExpression, kind, iTokenStart, strMemberName, state, bParseTypeLiteralOnly, createSynthesizedProperty, rootType, bExpansion ); } } private void parseMethodMember( Expression rootExpression, MemberAccessKind kind, int iTokenStart, String strMemberName, LightweightParserState state, boolean bParseTypeLiteralOnly, boolean createSynthesizedProperty, BeanMethodCallExpression e, IType rootType, boolean bExpansion, IType[] typeParameters, int iParenStart, int mark ) { int iLocationsCount = _locations.size(); parseMethodMember( rootExpression, kind, iTokenStart, strMemberName, state, bParseTypeLiteralOnly, e, rootType, bExpansion, typeParameters, iParenStart ); Expression expr = peekExpression(); if( expr.hasParseExceptions() ) { maybeOpenParenIsForParenthesizedExpr( rootExpression, kind, iTokenStart, strMemberName, state, bParseTypeLiteralOnly, createSynthesizedProperty, e, rootType, bExpansion, typeParameters, iParenStart, mark, iLocationsCount ); } } private void maybeOpenParenIsForParenthesizedExpr( Expression rootExpression, MemberAccessKind kind, int iTokenStart, String strMemberName, LightweightParserState state, boolean bParseTypeLiteralOnly, boolean createSynthesizedProperty, BeanMethodCallExpression e, IType rootType, boolean bExpansion, IType[] typeParameters, int iParenStart, int mark, int iLocationsCount ) { if( !isOpenParenOnNextLine( mark ) ) { return; } backtrack( mark, iLocationsCount ); parsePropertyMember( rootExpression, kind, iTokenStart, strMemberName, state, bParseTypeLiteralOnly, createSynthesizedProperty, rootType, bExpansion ); Expression expr = peekExpression(); if( expr.hasParseExceptions() ) { // Failed to parse as Property, reparse as Method call _tokenizer.restoreToMark( mark ); expr = popExpression(); removeInnerClasses( expr ); removeLocationsFrom( iLocationsCount ); parseMethodMember( rootExpression, kind, iTokenStart, strMemberName, state, bParseTypeLiteralOnly, e, rootType, bExpansion, typeParameters, iParenStart ); } } private boolean isOpenParenOnNextLine( int mark ) { if( mark <= 0 ) { return false; } IToken priorMarkToken = _tokenizer.getTokenAt( mark - 1 ); return priorMarkToken != null && priorMarkToken.getType() == ISourceCodeTokenizer.TT_WHITESPACE && priorMarkToken.getText().indexOf( '\n' ) >= 0; } private void parseMethodMember( Expression rootExpression, MemberAccessKind kind, int iTokenStart, String strMemberName, LightweightParserState state, boolean bParseTypeLiteralOnly, BeanMethodCallExpression e, IType rootType, boolean bExpansion, IType[] typeParameters, int iParenStart ) { e.setArgPosition( iParenStart + 1 ); e.setRootExpression( rootExpression ); IMethodInfo md = null; List<IFunctionType> listFunctionTypes = getPreliminaryFunctionTypes( strMemberName, e, rootType, bExpansion, typeParameters ); boolean bNoArgsProvided; if( !(bNoArgsProvided = match( null, ')' )) || (listFunctionTypes.size() == 1 && listFunctionTypes.get( 0 ).hasOptionalParams()) ) { pushInferredFunctionTypeVariableTypes( listFunctionTypes ); MethodScore methodScore; try { methodScore = parseArgumentList( e, listFunctionTypes, typeParameters, !(rootType instanceof ErrorType), bNoArgsProvided, null); } finally { popInferredFunctionTypeVariableTypes(); } //noinspection SuspiciousToArrayCall Expression[] eArgs = methodScore.getArguments().toArray( new Expression[methodScore.getArguments().size()] ); e.setArgs( eArgs ); if( methodScore.isValid() ) { IFunctionType funcType = (IFunctionType)methodScore.getInferredFunctionType(); verifyArgCount( e, eArgs.length, funcType ); assert funcType != null; e.setType( (!bExpansion || funcType.getReturnType().isArray() || funcType.getReturnType() == JavaTypes.pVOID()) ? funcType.getReturnType() : funcType.getReturnType().getArrayType() ); e.setFunctionType( funcType ); IType[] argTypes = funcType.getParameterTypes(); e.setArgTypes( argTypes ); IFunctionType rawFunctionType = (IFunctionType)methodScore.getRawFunctionType(); md = rawFunctionType.getMethodInfo(); if( !md.isVisible( getVisibilityConstraint() ) ) { verify( e, false, Res.MSG_METHOD_NOT_VISIBLE, strMemberName ); } //verify( md == null || !bMetaType || accessList.size() > 1 || md.isStatic(), strFunction + " cannot call non-static methods here." ); e.setMethodDescriptor( bExpansion ? new ArrayExpansionMethodInfo( md ) : md ); e.setMemberAccessKind( kind ); e.setNamedArgOrder( methodScore.getNamedArgOrder() ); verifyCase( e, strMemberName, funcType.getName(), state, Res.MSG_FUNCTION_CASE_MISMATCH, false ); } else { if( !(rootType instanceof ErrorType) ) { verify( e, false, state, Res.MSG_NO_SUCH_FUNCTION, strMemberName ); } e.setType( ErrorType.getInstance() ); } e.setAccessPath( strMemberName ); verify( e, bNoArgsProvided || match( null, ')' ), Res.MSG_EXPECTING_FUNCTION_CLOSE ); } else { //No parameters were found if( rootType instanceof ErrorType ) { e.setType( ErrorType.getInstance() ); e.setArgTypes( IType.EMPTY_ARRAY ); e.setAccessPath( strMemberName ); md = null; e.setMethodDescriptor( md ); e.setArgs( null ); } else { IFunctionType funcType; try { funcType = getFunctionType( bExpansion ? TypeLord.getExpandableComponentType( rootType ) : rootType, strMemberName, new Expression[0], null, this, true ); } catch( ParseException pe ) { // If this method call is a getXXX() call, we can try to convert it to a XXX property reference... String strPropertyName = getPropertyNameFromMethodName( strMemberName ); if( strPropertyName == null ) { try { funcType = getFunctionType( bExpansion ? TypeLord.getExpandableComponentType( rootType ) : rootType, strMemberName, new Expression[0], null, this, false ); e.addParseException( pe ); } catch( ParseException e1 ) { e.addParseException( pe ); e.setType( ErrorType.getInstance() ); e.setStartOffset( iTokenStart ); pushExpression( e ); return; } } else { parseMemberAccess( rootExpression, kind, iTokenStart, strPropertyName, state, bParseTypeLiteralOnly, true ); Expression expression = peekExpression(); if( expression instanceof MemberAccess ) { MemberAccess ma = (MemberAccess) expression; ma.setMethodNameForSyntheticAccess( strMemberName ); } if( peekExpression().hasParseExceptions() ) { e.addParseException( pe ); e.addParseExceptions( peekExpression().getParseExceptions() ); popExpression(); e.setType( ErrorType.getInstance() ); e.setStartOffset( iTokenStart ); pushExpression( e ); } return; } } if( typeParameters != null ) { if( verifyCanParameterizeType( e, funcType, typeParameters ) ) { IFunctionType parameterizedFuncType = (IFunctionType)funcType.getParameterizedType( typeParameters ); if( verify( e, parameterizedFuncType != null, Res.MSG_COULD_NOT_PARAMETERIZE ) ) { funcType = parameterizedFuncType; } } } assert funcType != null; funcType = maybeParameterizeOnCtxType( funcType ); IFunctionType boundType = boundFunctionType( funcType ); e.setType( (!bExpansion || funcType.getReturnType().isArray() || funcType.getReturnType() == JavaTypes.pVOID()) ? boundType.getReturnType() : boundType.getReturnType().getArrayType() ); e.setFunctionType( funcType ); IType[] argTypes = funcType.getParameterTypes(); e.setArgTypes( argTypes ); e.setAccessPath( strMemberName ); md = funcType.getMethodInfo(); verify( e, md.isVisible( getVisibilityConstraint() ), Res.MSG_METHOD_NOT_VISIBLE, strMemberName ); verify( e, !md.isAbstract() || !(rootExpression instanceof Identifier) || !((Identifier)rootExpression).getSymbol().getName().equals( Keyword.KW_super.toString() ) || GosuClass.isObjectMethod( md ), Res.MSG_ABSTRACT_METHOD_CANNOT_BE_ACCESSED_DIRECTLY, strMemberName ); //verify( md == null || !bMetaType || accessList.size() > 1 || md.isStatic(), strFunction + " cannot call non-static methods here." ); e.setMethodDescriptor( bExpansion ? new ArrayExpansionMethodInfo( md ) : md ); e.setMemberAccessKind( kind ); verifyCase( e, strMemberName, md.getDisplayName(), state, Res.MSG_FUNCTION_CASE_MISMATCH, false ); verifyArgCount( e, 0, funcType ); e.setArgs( null ); } } e.setStartOffset( iTokenStart ); if( md != null && md.isDeprecated() ) { // Add a warning if the method is deprecated e.addParseWarning( new ParseWarningForDeprecatedMember( state.cloneWithNewTokenStartAndTokenEnd( iTokenStart, md.getDisplayName().length() ), TypeInfoUtil.getMethodSignature( md ), e.getRootType().getName() ) ); } if( isParsingAbstractConstructor() ) { handleAbstractCtor( iTokenStart, strMemberName, e, state ); } verifyNotCallingOverridableFunctionFromCtor( e ); pushExpression( e ); } private void parsePropertyMember( Expression rootExpression, MemberAccessKind kind, int iTokenStart, String strMemberName, LightweightParserState state, boolean bParseTypeLiteralOnly, boolean createSynthesizedProperty, IType rootType, boolean bExpansion ) { IPropertyInfo pi = null; MemberAccess ma = bExpansion ? new MemberExpansionAccess() : createSynthesizedProperty ? new SynthesizedMemberAccess() : new MemberAccess(); ma.setRootExpression( rootExpression ); ma.setMemberAccessKind( kind ); IType memberType = null; try { if( rootType instanceof IFunctionType && rootExpression instanceof Identifier ) { // Can't deref functions, convert to namespace if possible (this is to handle a bad // relative namespaace problem e.g., print.foo.SomeTime where print is a namespace in gw.api.print ..yeeeaaah) Identifier identifier = (Identifier)rootExpression; INamespaceType namespaceType = resolveNamespace( identifier.getSymbol().getName() ); if( namespaceType != null ) { Symbol sym = new Symbol( namespaceType.getRelativeName(), namespaceType, null ); identifier.setSymbol( sym, getSymbolTable() ); identifier.setType( namespaceType ); rootType = namespaceType; } } if( rootType instanceof INamespaceType ) { if( !strMemberName.equals( "*" ) ) { String strType = rootType.getName() + '.' + strMemberName; // First, try a sub-namespace memberType = TypeSystem.getNamespace( strType ); if( memberType == null ) { // Now try a fq type name memberType = resolveTypeName( strType, true ); if( memberType == null ) { memberType = resolveNamespace( strType ); } else if( memberType != null ) { Token T = new Token(); T._strValue = strType; TypeLiteral tl = resolveTypeLiteral( T ); resolveArrayOrParameterizationPartOfTypeLiteral( T, bParseTypeLiteralOnly, tl ); tl.setPackageExpression( rootExpression ); return; } } } } else if( rootExpression instanceof TypeLiteral ) { // Try an inner class name IType typeLiteralType = ((MetaType)rootType).getType(); if( typeLiteralType instanceof IHasInnerClass ) { memberType = getInnerClass( strMemberName, memberType, (IHasInnerClass)typeLiteralType ); if( memberType != null ) { if( !shouldParseMemberInstead( strMemberName, rootType, bExpansion, memberType ) ) { Token T = new Token(); T._strValue = memberType.getName(); TypeLiteral tl = resolveTypeLiteral( T ); resolveArrayOrParameterizationPartOfTypeLiteral( T, bParseTypeLiteralOnly, tl ); verifyTypeAccessible( tl, memberType ); tl.setPackageExpression( rootExpression ); return; } } } else if( typeLiteralType instanceof ErrorType ) { memberType = ErrorType.getInstance(); } } if( memberType == null ) { if( bParseTypeLiteralOnly && !(rootType instanceof ErrorType) ) { if( rootType instanceof INamespaceType ) { verify( ma, false, Res.MSG_NO_TYPE_ON_NAMESPACE, strMemberName, rootType == null ? "<no type specified>" : rootType.getName() ); } else { IType errRootType = rootType instanceof IMetaType ? ((IMetaType)rootType).getType() : rootType; addError( ma, Res.MSG_INVALID_INNER_TYPE, strMemberName, TypeLord.getPureGenericType( errRootType ).getRelativeName() ); } memberType = ErrorType.getInstance(); } else { pi = BeanAccess.getPropertyInfo( bExpansion ? TypeLord.getExpandableComponentType( rootType ) : rootType, strMemberName, null, this, getVisibilityConstraint() ); memberType = bExpansion ? new ArrayExpansionPropertyInfo( pi ).getFeatureType() : pi.getFeatureType(); if( pi != null ) { verifyCase( ma, strMemberName, pi.getName(), state, Res.MSG_PROPERTY_CASE_MISMATCH, false ); } if( pi.isStatic() && !JavaTypes.ITYPE().isAssignableFrom( rootType ) ) { IType intrinsicType = rootExpression.getType(); if( rootExpression instanceof Identifier && intrinsicType.getRelativeName().equals( ((Identifier)rootExpression).getSymbol().getName() ) ) { warn( ma, false, Res.MSG_NON_STATIC_ACCESS_WITH_IDENTIFIER_OF_STATIC_MEMBER, pi.getName(), intrinsicType.getName(), ((Identifier)rootExpression).getSymbol().getName(), intrinsicType.getName() ); } else { warn( ma, false, Res.MSG_NON_STATIC_ACCESS_OF_STATIC_MEMBER, pi.getName(), intrinsicType.getName() ); } } } } } catch( ParseException e1 ) { // memberType = ma.getRootType(); if( rootExpression instanceof Identifier && !(rootType instanceof INamespaceType) ) { // This is to handle yet another bad relative namespaace problem e.g., new activity.ActivityDetailHelper(Activity) Identifier identifier = (Identifier)rootExpression; INamespaceType namespaceType = resolveNamespace( identifier.getSymbol().getName().toLowerCase() ); if( namespaceType != null ) { ISymbol oldSymbol = identifier.getSymbol(); Symbol sym = new Symbol( namespaceType.getRelativeName(), namespaceType, null ); identifier.setSymbol( sym, getSymbolTable() ); identifier.setType( namespaceType ); parseMemberAccess( rootExpression, kind, iTokenStart, strMemberName, state, bParseTypeLiteralOnly ); Expression namespaceExpr = peekExpression(); if( namespaceExpr.hasParseExceptions() ) { ((Identifier)rootExpression).setSymbol( oldSymbol, getSymbolTable() ); rootExpression.setType( oldSymbol.getType() ); } else { return; } } } if( rootType instanceof INamespaceType ) { verify( ma, false, Res.MSG_NO_TYPE_ON_NAMESPACE, strMemberName, rootType == null ? "<no type specified>" : rootType.getName() ); } else { ma.addParseException( e1 ); } memberType = ErrorType.getInstance(); } ma.setType( memberType ); ma.setMemberName( strMemberName ); ma.setStartOffset( iTokenStart ); if( pi != null && pi.isDeprecated() ) { // Add a warning if the property is deprecated ma.addParseWarning( new ParseWarningForDeprecatedMember( state.cloneWithNewTokenStartAndTokenEnd( iTokenStart, pi.getName().length() ), pi.getName(), ma.getRootType().getName() ) ); } if( pi != null ) { verify( ma, !pi.isAbstract() || !(rootExpression instanceof Identifier) || !((Identifier)rootExpression).getSymbol().getName().equals( Keyword.KW_super.toString() ), Res.MSG_ABSTRACT_METHOD_CANNOT_BE_ACCESSED_DIRECTLY, strMemberName ); } verifyOrWarn( ma, bExpansion || !(pi instanceof ArrayExpansionPropertyInfo), true, Res.MSG_USE_EXPANSION_OPERATOR ); pushExpression( ma ); } private boolean shouldParseMemberInstead( String strMemberName, IType rootType, boolean bExpansion, IType memberType ) { List<IType> contextTypes = getOwner().getContextTypes(); boolean bMetaTypeInContext = contextTypes.size() == 1 && contextTypes.get( 0 ) instanceof IMetaType; if( !bMetaTypeInContext ) { if( memberType != null ) { try { BeanAccess.getPropertyInfo( bExpansion ? TypeLord.getExpandableComponentType( rootType ) : rootType, strMemberName, null, this, getVisibilityConstraint() ); // The case exists where both an inner class and a member share the same simple name. // Favor the member parse when the context type is not a MetaType return true; } catch( Exception e ) { // eat me } } } return false; } private IType getInnerClass( String strMemberName, IType memberType, IHasInnerClass typeLiteralType ) { try { memberType = typeLiteralType.getInnerClass( strMemberName ); } catch( IllegalStateException e1 ) { //Ignore, JavaType.getInnerClass throws IllegalStateException if unable to get type for inner class } return memberType; } private IFunctionType maybeParameterizeOnCtxType( IFunctionType funcType ) { if( funcType.isGenericType() ) { funcType = funcType.inferParameterizedTypeFromArgTypesAndContextType( IType.EMPTY_ARRAY, getCurrentContextType() ); } return funcType; } private List<IFunctionType> getPreliminaryFunctionTypes( String strMemberName, BeanMethodCallExpression e, IType rootType, boolean bExpansion, IType[] typeParameters ) { // Get a preliminary funcTypes to check arguments. Note we do this to aid in in error feedback and value popup completion. List<IFunctionType> listFunctionTypes = new ArrayList<IFunctionType>( 8 ); try { if( !(rootType instanceof ErrorType) ) { getFunctionType( bExpansion ? TypeLord.getExpandableComponentType( rootType ) : rootType, strMemberName, null, listFunctionTypes, this, true ); } } catch( ParseException pe ) { e.addParseException( pe ); } if( typeParameters != null ) { listFunctionTypes = parameterizeFunctionTypes( e, typeParameters, listFunctionTypes ); } return listFunctionTypes; } private boolean isBlockInvoke( Expression rootExpression, String strMemberName ) { try { IType rootType = rootExpression.getType(); // Don't look up inner classes if( rootExpression instanceof TypeLiteral ) { IType typeLiteralType = ((MetaType)rootType).getType(); if( typeLiteralType instanceof IHasInnerClass ) { if( ((IHasInnerClass)typeLiteralType).getInnerClass( strMemberName ) != null ) { return false; } } } // ignore namepspaces and other functions if( !isErrorType(rootType) && !(rootType instanceof INamespaceType) && !(rootType instanceof IFunctionType) ) { IPropertyInfo pi = BeanAccess.getPropertyInfo( rootExpression.getType(), strMemberName, null, this, getVisibilityConstraint() ); if( pi != null ) { return pi.getFeatureType() instanceof IBlockType; } } } catch( ParseException e ) { //ignore } return false; } private boolean isErrorType(IType rootType) { return rootType instanceof IErrorType || (rootType instanceof IMetaType && ((IMetaType) rootType).getType() instanceof IErrorType); } private ArrayList<IFunctionType> parameterizeFunctionTypes( Expression expression, IType[] typeParameters, List<IFunctionType> listFunctionTypes ) { ArrayList<IFunctionType> parameterizedFunctionTypes = new ArrayList<IFunctionType>( 8 ); for( IFunctionType funcType : listFunctionTypes ) { if( funcType.isGenericType() && verifyCanParameterizeType( expression, funcType, typeParameters ) ) { IFunctionType parameterizedFuncType = (IFunctionType)funcType.getParameterizedType( typeParameters ); if( verify( expression, parameterizedFuncType != null, Res.MSG_COULD_NOT_PARAMETERIZE ) ) { if( parameterizedFuncType != null ) { parameterizedFunctionTypes.add( parameterizedFuncType ); } } } } return parameterizedFunctionTypes; } private void handleAbstractCtor( final int iTokenStart, final String strMemberName, BeanMethodCallExpression e, final IParserState state ) { IMethodInfo methodInfo = e.getMethodDescriptor(); if (methodInfo instanceof GosuMethodInfo ) { // DynamicFunctionSymbol functionSymbol = ((GosuMethodInfo) methodInfo).getDfs(); if (methodInfo.isAbstract()) { //noinspection ThrowableInstanceNeverThrown e.addParseException(new ParseException(new IParserState() { public int getLineNumber() { return state.getLineNumber(); } public int getTokenColumn() { return state.getTokenColumn(); } public String getSource() { return state.getSource(); } public int getTokenStart() { return iTokenStart; } public int getTokenEnd() { return iTokenStart + strMemberName.length(); } public int getLineOffset() { return state.getLineOffset(); } }, Res.MSG_NO_ABSTRACT_METHOD_CALL_IN_CONSTR, methodInfo.getDisplayName())); } } } private boolean verifyCanParameterizeType( ParsedElement elem, IType type, IType[] typeParam ) { if( !verify( elem, type.isGenericType(), Res.MSG_CANNOT_PARAMETERIZE_NONGENERIC ) ) { return false; } IGenericTypeVariable[] typeVars = type.getGenericTypeVariables(); if( verify( elem, typeParam != null && typeParam.length == typeVars.length, Res.MSG_WRONG_NUM_OF_ARGS, "" ) ) { assert typeParam != null; boolean bRet = true; for( int i = 0; i < typeVars.length; i++ ) { IType boundingType = typeVars[i].getBoundingType(); boundingType = TypeLord.isParameterizedType( boundingType ) && TypeLord.isRecursiveType( boundingType, boundingType.getTypeParameters() ) ? TypeLord.getDefaultParameterizedType( boundingType ) : getActualBoundingType( typeVars, typeParam, boundingType ); bRet = bRet && verify( elem, // Hack to support recursive types isTypeParamHeaderCompiling( typeParam[i] ) || isErrorType( typeParam[i] ) || (typeVars[i].getTypeVariableDefinition() != null && typeVars[i].getTypeVariableDefinition().getType().isAssignableFrom( typeParam[i] )) || boundingType.isAssignableFrom( typeParam[i] ), Res.MSG_TYPE_PARAM_NOT_ASSIGNABLE_TO, typeParam[i], boundingType ); } return bRet; } return false; } private IType getActualBoundingType( IGenericTypeVariable[] typeVars, IType[] typeParams, IType boundingType ) { TypeVarToTypeMap typeVarToTypeMap = new TypeVarToTypeMap(); for( int i = 0; i < typeVars.length; i++ ) { IGenericTypeVariable gv = typeVars[i]; if( gv.getTypeVariableDefinition() != null ) { typeVarToTypeMap.put( gv.getTypeVariableDefinition().getType(), typeParams.length > i ? typeParams[i] : gv.getTypeVariableDefinition().getType() ); } } return TypeLord.getActualType( boundingType, typeVarToTypeMap, true ); } private boolean isTypeParamHeaderCompiling( IType typeParam ) { return (typeParam instanceof IGosuClass && ((IGosuClass)typeParam).isCompilingHeader()) || (typeParam instanceof TypeVariableType && isTypeParamHeaderCompiling( ((TypeVariableType)typeParam).getBoundingType() )) || (typeParam instanceof TypeVariableType && ((TypeVariableType)typeParam).getBoundingType() == PENDING_BOUNDING_TYPE); } private IType[] parseFunctionParameterization( Expression e ) { int iOffset = _tokenizer.getTokenStart(); int iLineNum = _tokenizer.getLineNumber(); int iColumn = getTokenizer().getTokenColumn(); if( match( null, "<", SourceCodeTokenizer.TT_OPERATOR ) ) { List<TypeLiteral> paramTypes = parseTypeParameters( null ); verify( e, match( null, ">", SourceCodeTokenizer.TT_OPERATOR ), Res.MSG_EXPECTING_CLOSING_ANGLE_BRACKET_FOR_TYPE ); if( paramTypes.isEmpty() ) { return null; } makeTypeParameterListClause( iOffset, iLineNum, iColumn, paramTypes ); IType[] types = new IType[paramTypes.size()]; for( int i = 0; i < paramTypes.size(); i++ ) { TypeLiteral typeLiteral = paramTypes.get( i ); types[i] = (typeLiteral.getType()).getType(); } return types; } return null; } private MethodScore parseArgumentList(ParsedElement element, List<? extends IInvocableType> listFunctionTypes, IType[] typeParams, boolean bVerifyArgs, boolean bNoArgsProvided, JavaAnnotationParsingState valueAnnotationParsing) { int iArgs = 0; List<Expression> argExpressions = new ArrayList<Expression>( 4 ); List<LightweightParserState> parserStates = new ArrayList<LightweightParserState>( 4 ); List<List<IType>> contextTypesList = extractContextTypes( listFunctionTypes ); int iOffset = _tokenizer.getTokenStart(); int iLineNum = _tokenizer.getLineNumber(); int iColumn = _tokenizer.getTokenColumn(); TypeVarToTypeMap inferenceMap = new TypeVarToTypeMap(); List<Integer> namedArgOrder = null; Set<String> namedArgs = new HashSet<String>(); if( !bNoArgsProvided ) { do { parserStates.add( makeLightweightParserState() ); int iArgPos = parseArgExpression( listFunctionTypes, iArgs, argExpressions, contextTypesList, inferenceMap, parserStates, valueAnnotationParsing, namedArgs ); namedArgOrder = assignArgExprPosition( listFunctionTypes, iArgs, namedArgOrder, iArgPos ); iArgs++; } while( match( null, ',' ) ); } // Extend the args list with default (or empty) values addMisingArgsWithDefaultValues( element, listFunctionTypes, argExpressions, parserStates ); if( iArgs > 0 ) { // Maybe reassign offset for ArgumentListClause... for( Expression argExpr: argExpressions ) { if( argExpr.getLocation() == null ) { continue; } int iExpressionOffset = argExpr.getLocation().getOffset(); if( iExpressionOffset < iOffset ) { // Can happen if first arg is a NotAWordExpression in which case the expr's length is 0 and its offset is the // previous token's ending offset, which will likely be less than the first token in the arg position's offset. iOffset = iExpressionOffset; iLineNum = argExpr.getLocation().getLineNum(); iColumn = argExpr.getLocation().getColumn(); } } ArgumentListClause e = new ArgumentListClause(); pushExpression( e ); setLocation( iOffset, iLineNum, iColumn, true ); popExpression(); } if( !bVerifyArgs ) { MethodScore score = new MethodScore(); score.setValid( false ); //noinspection unchecked score.setArguments( (List)argExpressions ); score.setNamedArgOrder( namedArgOrder ); return score; } else if( isDynamicMethod( listFunctionTypes ) ) { return makeDynamicMethodScore( listFunctionTypes, argExpressions ); } // if there were type parameters, remove any non-parameterized functions if( typeParams != null && typeParams.length > 0 ) { ArrayList<IInvocableType> genericFunctions = new ArrayList<IInvocableType>(); for( IInvocableType type : listFunctionTypes ) { if( type.isGenericType() ) { genericFunctions.add( type ); } } if( !genericFunctions.isEmpty() ) { listFunctionTypes = genericFunctions; } } // score 'em List<MethodScore> scoredMethods = scoreMethods( listFunctionTypes, argExpressions ); boolean isAmbiguousMethodCall = false; // warn on ambiguity if( scoredMethods.size() > 1 && scoredMethods.get( 0 ).getScore() == scoredMethods.get( 1 ).getScore() && scoredMethods.get( 0 ).matchesArgSize( argExpressions ) && !FeatureManager.areInvocableTypesAssignable( scoredMethods.get(0).getRawFunctionType(), scoredMethods.get(1).getRawFunctionType() ) ) { isAmbiguousMethodCall = true; verify( element, false, Res.MSG_AMBIGUOUS_METHOD_INVOCATION); } if( scoredMethods.size() > 0 ) { MethodScore score = scoredMethods.get( 0 ); // infer the function type IInvocableType rawFunctionType = score.getRawFunctionType(); IInvocableType inferredFunctionType = inferFunctionType( rawFunctionType, argExpressions ); // reverify args verifyArgTypes( inferredFunctionType.getParameterTypes(), argExpressions, parserStates, isAmbiguousMethodCall ); if( score.isValid() ) { // if the score is valid, bound infered variables to avoid them getting out as raw type variables inferredFunctionType = maybeBoundFunctionTypeVars( inferredFunctionType ); // wrap up any arguments that need coercions handleImplicitCoercionsInArgs( inferredFunctionType.getParameterTypes(), rawFunctionType.getParameterTypes(), argExpressions ); } //noinspection unchecked score.setArguments( (List)argExpressions ); score.setInferredFunctionType( inferredFunctionType ); score.setNamedArgOrder( namedArgOrder ); return score; } else { MethodScore score = new MethodScore(); score.setValid( false ); //noinspection unchecked score.setArguments( (List)argExpressions ); return score; } } private IInvocableType maybeBoundFunctionTypeVars( IInvocableType inferredFunctionType ) { List<IType> types = new ArrayList<IType>(); for( IType typeVarType : getCurrentlyInferringTypes() ) { IType encType = TypeLord.getPureGenericType( typeVarType.getEnclosingType() ); if( encType != null && TypeLord.getPureGenericType( inferredFunctionType ).isAssignableFrom( typeVarType.getEnclosingType() ) ) { types.add( typeVarType ); } } return (IInvocableType) TypeLord.boundTypes( inferredFunctionType, types ); } private List<Integer> assignArgExprPosition( List<? extends IInvocableType> listFunctionTypes, int iArgs, List<Integer> namedArgOrder, int iArgPos ) { if( (namedArgOrder != null || iArgPos != iArgs) && listFunctionTypes.size() > 0 ) { if( namedArgOrder == null ) { int iSize = listFunctionTypes.get( 0 ).getParameterTypes().length; namedArgOrder = new ArrayList<Integer>( iSize ); for( int i = 0; i < iSize; i++ ) { namedArgOrder.add( i ); } } namedArgOrder.remove( (Integer)iArgPos ); if( namedArgOrder.size() >= iArgs ) { namedArgOrder.add( iArgs, iArgPos ); } else { namedArgOrder.add( iArgPos ); } } return namedArgOrder; } private void addMisingArgsWithDefaultValues( ParsedElement element, List<? extends IInvocableType> listFunctionTypes, List<Expression> argExpressions, List<LightweightParserState> parserStates ) { if( listFunctionTypes.size() == 1 ) { IInvocableType funcType = listFunctionTypes.get( 0 ); if( funcType.hasOptionalParams() ) { for( int i = argExpressions.size(); i < funcType.getParameterTypes().length; i++ ) { parserStates.add( makeLightweightParserState() ); argExpressions.add( i, getDefaultValueOrPlaceHolderForParam( i, funcType ) ); } for( Expression argExpr : argExpressions ) { if( !verify( element, argExpr != DefaultParamValueLiteral.instance(), Res.MSG_MISSING_REQUIRED_ARGUMENTS ) ) { break; } } } } } private int parseArgExpression(List<? extends IInvocableType> listFunctionTypes, int iArgs, List<Expression> argExpressions, List<List<IType>> contextTypesList, TypeVarToTypeMap inferenceMap, List<LightweightParserState> parserStates, JavaAnnotationParsingState javaAnnotationParsingState, Set<String> namedArgs ) { List<IType> allCtxTypes; List<IType> rawContextTypes; List<IType> boundContextTypes; int iArgPos = iArgs; boolean isAlreadyDef = false; if( match( null, ":", ISourceCodeTokenizer.TT_OPERATOR, true ) ) { // Since named params can only be applied to non-overloaded methods, we can assume only one function IInvocableType funcType = listFunctionTypes.size() > 0 ? listFunctionTypes.get( 0 ) : null; iArgPos = parseNamedParamExpression( listFunctionTypes.size() > 0 ? funcType : null ); if( iArgPos == -1) { namedArgs.add( "err" ); } else if ( funcType != null ) { String[] parameterNames = funcType.getParameterNames(); isAlreadyDef = namedArgs.add( parameterNames[iArgPos] ); } allCtxTypes = iArgPos < contextTypesList.size() && iArgPos >= 0 ? contextTypesList.get( iArgPos ) : null; rawContextTypes = inferContextTypes( inferenceMap, allCtxTypes ); boundContextTypes = boundCtxTypes( rawContextTypes ); if( argExpressions.size() < iArgPos+1 ) { // Extend the args list with default (or empty) values up to, but not including, the newly parsed arg for( int i = argExpressions.size(); i < iArgPos; i++ ) { argExpressions.add( i, getDefaultValueOrPlaceHolderForParam( i, funcType ) ); parserStates.add( i, makeLightweightParserState() ); } assert argExpressions.size() == iArgPos; } } else { allCtxTypes = iArgPos < contextTypesList.size() ? contextTypesList.get( iArgPos ) : null; rawContextTypes = inferContextTypes( inferenceMap, allCtxTypes ); boundContextTypes = boundCtxTypes( rawContextTypes ); // Add the component type as a context type, to help w/ type inference for enums if( javaAnnotationParsingState == JavaAnnotationParsingState.PARSING_JAVA_VALUE_ENUM ) { if (boundContextTypes.size() == 1) { IType iType = boundContextTypes.get(0); if (iType.isArray()) { boundContextTypes.add(iType.getComponentType()); } } } parseExpression( boundContextTypes ); // Do not support non java value annotation w/o named args if( javaAnnotationParsingState == JavaAnnotationParsingState.PARSING_JAVA_NON_VALUE_ENUM ) { verify(peekExpression(), false, Res.MSG_ANY, "Named arguments are required for this style of java annotation"); } if( !namedArgs.isEmpty() ) { addError( peekExpression(), Res.MSG_EXPECTING_NAMED_ARG ); } } Expression expression = popExpression(); // Crazy java value enum array wrapping if( javaAnnotationParsingState == JavaAnnotationParsingState.PARSING_JAVA_VALUE_ENUM ) { if (boundContextTypes.size() == 2) { IType iType = boundContextTypes.get(0); if( iType.isArray() ) { if( expression.getType().equals( iType.getComponentType() ) ) { NewExpression newExpression = new NewExpression(); newExpression.setType(iType); newExpression.setValueExpressions(Arrays.asList(expression)); findAndWrapLocation(expression, newExpression); expression = newExpression; } } } } inferFunctionTypeVariables( rawContextTypes, boundContextTypes, expression.getType(), inferenceMap ); iArgPos = iArgPos < 0 ? iArgs : iArgPos; if( iArgPos >= 0 ) { if( iArgPos == argExpressions.size() ) { argExpressions.add( iArgPos, expression ); } else if (iArgPos >=0 && iArgPos < argExpressions.size() && isAlreadyDef ) { Expression existingExpr = argExpressions.set( iArgPos, expression ); verify( expression, existingExpr == DefaultParamValueLiteral.instance() || existingExpr instanceof DefaultArgLiteral || existingExpr instanceof NullExpression, Res.MSG_ARGUMENT_ALREADY_DEFINED ); } } return iArgPos; } private Expression getDefaultValueOrPlaceHolderForParam( int iParam, IInvocableType invType ) { if( invType == null ) { return DefaultParamValueLiteral.instance(); } IExpression[] defValues = invType.getDefaultValueExpressions(); if( defValues == null || defValues.length == 0 ) { return DefaultParamValueLiteral.instance(); } IExpression defValue = defValues[iParam]; if( defValue != null ) { return new DefaultArgLiteral( invType.getParameterTypes()[iParam], defValue ); } return DefaultParamValueLiteral.instance(); } private int parseNamedParamExpression( IInvocableType invType ) { match( null, ":", SourceCodeTokenizer.TT_OPERATOR ); parseNamedParamIdentifier(); Identifier identifier = (Identifier)popExpression(); int[] iPos = {-1}; IType type = getParamTypeFromParamName( invType, identifier.getSymbol().getName(), iPos ); identifier.setType( type ); verify( identifier, !(type instanceof ErrorType), Res.MSG_PARAM_NOT_FOUND ); verify( identifier, match( null, "=", SourceCodeTokenizer.TT_OPERATOR ), Res.MSG_EXPECTING_EQUALS_ASSIGN ); parseExpression( type ); Expression value = popExpression(); if( value.hasParseExceptions() ) { value.getParseExceptions().get( 0 ).setExpectedType( type ); } pushExpression( value ); return iPos[0]; } private IType getParamTypeFromParamName( IInvocableType invType, String strParam, int[] iPos ) { if( invType == null ) { return ErrorType.getInstance(); } String[] names = invType.getParameterNames(); for( int i = 0; i < names.length; i++ ) { if( names[i].equals( strParam ) ) { iPos[0] = i; return invType.getParameterTypes()[i]; } } return ErrorType.getInstance(); } private void parseNamedParamIdentifier() { int iOffset = _tokenizer.getTokenStart(); int iLineNum = _tokenizer.getLineNumber(); int iColumn = _tokenizer.getTokenColumn(); Token t = new Token(); Identifier expr = new Identifier(); verify( expr, match( t, SourceCodeTokenizer.TT_WORD ) || match( t, SourceCodeTokenizer.TT_KEYWORD ), Res.MSG_EXPECTING_NAME_PARAM ); expr.setSymbol( new TypedSymbol( t._strValue, null, null, null, SymbolType.NAMED_PARAMETER ), null ); pushExpression( expr ); setLocation( iOffset, iLineNum, iColumn ); } private boolean isDynamicMethod( List<? extends IInvocableType> listFunctionTypes ) { if( listFunctionTypes == null || listFunctionTypes.isEmpty() ) { return false; } IInvocableType invoType = listFunctionTypes.get( 0 ); if( invoType instanceof FunctionType ) { IMethodInfo mi = ((FunctionType)invoType).getMethodInfo(); if( mi != null && mi.getOwnersType() instanceof IPlaceholder ) { return true; } } return false; } private MethodScore makeDynamicMethodScore( List<? extends IInvocableType> listFunctionTypes, List<Expression> argExpressions ) { MethodScore score = new MethodScore(); score.setValid( true ); //noinspection unchecked score.setArguments( (List)argExpressions ); IMethodInfo mi = ((FunctionType)listFunctionTypes.get( 0 )).getMethodInfo(); mi = ((ITypeInfo)mi.getContainer()).getMethod( mi.getName(), getTypes( argExpressions ).toArray( new IType[argExpressions.size()] ) ); score.setInferredFunctionType( new FunctionType( mi ) ); score.setRawFunctionType( score.getInferredFunctionType() ); score.setScore( 1 ); return score; } private List<MethodScore> scoreMethods( List<? extends IInvocableType> listFunctionTypes, List<Expression> argExpressions ) { List<IType> argTypes = new ArrayList<IType>(argExpressions.size()); for(Expression argExpression : argExpressions) { argTypes.add(argExpression.getType()); } return ITypeInfo.FIND.scoreMethods( listFunctionTypes, argTypes, getCurrentlyInferringTypes() ); } private List<IType> inferContextTypes( TypeVarToTypeMap inferenceMap, List<IType> ctxArgs ) { if( ctxArgs != null ) { ArrayList<IType> returnTypes = new ArrayList<IType>( ); for( IType ctxArg : ctxArgs ) { returnTypes.add( inferArgTypes( ctxArg, inferenceMap ) ); } return returnTypes; } else { return Collections.emptyList(); } } private List<IType> boundCtxTypes( List<IType> allCtxTypes ) { ArrayList<IType> returnList = new ArrayList<IType>(); List<IType> inferringTypes = getCurrentlyInferringTypes(); for( IType allCtxType : allCtxTypes ) { returnList.add( TypeLord.boundTypes( allCtxType, inferringTypes ) ); } return returnList; } private String makeTypesStringForAlternativeMethods( List<MethodScore> scores ) { String str = ""; long bestScore = scores.get( 0 ).getScore(); for( int i = 1; i < scores.size(); i++ ) { MethodScore score = scores.get( i ); if( score.getScore() != bestScore ) { break; } if( i != 1 ) { str += "\n "; } str += makeTypesStringForMethodScore( score ); } return str; } private String makeTypesStringForMethodScore( MethodScore bestScore ) { IType[] types = bestScore.getRawFunctionType().getParameterTypes(); String str = "("; for( int j = 0; j < types.length; j++ ) { if( j != 0 ) { str += ", "; } str += types[j].getRelativeName(); } return str + ")"; } private void inferFunctionTypeVariables( List<IType> rawContextTypes, List<IType> boundContextTypes, IType expressionType, TypeVarToTypeMap inferenceMap ) { // we only infer if there is a single unambiguous type if( rawContextTypes.size() == 1 ) { IType rawContextType = rawContextTypes.get( 0 ); IType boundContextType = boundContextTypes.get( 0 ); if( rawContextType != null && boundContextType != null ) { ICoercer iCoercer = CommonServices.getCoercionManager().resolveCoercerStatically( boundContextType, expressionType ); if( iCoercer instanceof IResolvingCoercer ) { IType resolvedType = ((IResolvingCoercer)iCoercer).resolveType( rawContextType, expressionType ); TypeLord.inferTypeVariableTypesFromGenParamTypeAndConcreteType( rawContextType, resolvedType, inferenceMap ); } else { TypeLord.inferTypeVariableTypesFromGenParamTypeAndConcreteType( rawContextType, expressionType, inferenceMap ); } } } } private IType inferArgTypes( IType contextType, TypeVarToTypeMap inferenceMap ) { TypeLord.addReferencedTypeVarsThatAreNotInMap( contextType, inferenceMap, true ); return TypeLord.getActualType( contextType, inferenceMap, true ); } private void handleImplicitCoercionsInArgs( IType[] argTypes, IType[] rawArgTypes, List<Expression> args ) { for( int i = 0; i < argTypes.length && i < args.size(); i++ ) { IType argType = argTypes[i]; Expression expr = args.get( i ); if( argType != rawArgTypes[i] && (argType instanceof IGosuArrayClass || argType instanceof TypeVariableArrayType) && !(rawArgTypes[i] instanceof IGosuArrayClass) ) { // Special case for GosuArray -> JavaArray since a generic function can be inferred via structural equivalence // e.g., component types are assignable, but different array types on a component type may not be. argType = rawArgTypes[i]; } if( !(expr instanceof DefaultParamValueLiteral) ) { args.set( i, possiblyWrapWithImplicitCoercion( expr, argType ) ); } } } // returns a list of lists of unique types at each argument position, private List<List<IType>> extractContextTypes( List<? extends IInvocableType> funcTypes ) { if( funcTypes != null ) { ArrayList<List<IType>> returnList = new ArrayList<List<IType>>(); for( IInvocableType funcType : funcTypes ) { for( int i = 0; i < funcType.getParameterTypes().length; i++ ) { IType paramType = funcType.getParameterTypes()[i]; if( i >= returnList.size() ) { returnList.add( new ArrayList<IType>() ); } List<IType> paramTypeList = returnList.get( i ); if( !paramTypeList.contains( paramType ) ) { paramTypeList.add( paramType ); } } } return returnList; } else { return Collections.emptyList(); } } private void verifyArgTypes( IType[] argTypes, List<Expression> argExpressions, List<LightweightParserState> parserStates, boolean isAmbiguousMethodCall ) { if( argTypes == null || argTypes.length == 0 || argExpressions == null || argExpressions.size() == 0 ) { return; } for( int i = 0; i < argTypes.length && i < argExpressions.size(); i++ ) { Expression e = argExpressions.get( i ); LightweightParserState state = parserStates.get( i ); if( isAmbiguousMethodCall && argTypes[i] != e.getType() ) { e.addParseException( new ImplicitCoercionError( state, Res.MSG_IMPLICIT_COERCION_ERROR, argTypes[i], e.getType().getDisplayName(), argTypes[i].getDisplayName() ) ); } else { // Adds any parse exceptions that may have been cleared during method scoring verifyComparable( argTypes[i], e, false, true, state ); } //Add a warning if a closure with a void return type is passed to a method expecting //a non-void return value if( argTypes[i] instanceof FunctionType && e.getType() instanceof FunctionType ) { FunctionType expectedFunType = (FunctionType)argTypes[i]; FunctionType foundFunType = (FunctionType)e.getType(); if( expectedFunType.getReturnType() != GosuParserTypes.NULL_TYPE() && foundFunType.getReturnType() == GosuParserTypes.NULL_TYPE() ) { warn( e, false, Res.MSG_VOID_RETURN_IN_CTX_EXPECTING_VALUE ); } } if( e.hasParseExceptions() ) { IParseIssue pe = e.getParseExceptions().get( 0 ); if( pe.getExpectedType() == null ) { pe.setExpectedType( argTypes[i] ); } } } } //------------------------------------------------------------------------------ // literal // number // string // <type-literal> // void parseLiteral() { int iOffset = _tokenizer.getTokenStart(); int iLineNum = _tokenizer.getLineNumber(); int iColumn = getTokenizer().getTokenColumn(); _parseLiteral(); setLocation( iOffset, iLineNum, iColumn ); } void _parseLiteral() { if( !parseNumberLiteral() && !parseRelativeFeatureLiteral() && !parseStringLiteral() && !parseCharLiteral() && !parseBooleanLiteral() && !parseNullLiteral() && !parseTypeLiteral() ) { Expression expr = popExpression(); getLocationsList().remove( expr.getLocation() ); NotAWordExpression notAWord = new NotAWordExpression(); pushExpression( notAWord ); verify( notAWord, false, Res.MSG_SYNTAX_ERROR ); IToken T = getTokenizer().getPriorToken(); setLocation( T.getTokenEnd(), T.getLine(), T.getTokenColumn(), true ); } } private boolean parseRelativeFeatureLiteral() { if( getGosuClass() != null && match( null, "#", SourceCodeTokenizer.TT_OPERATOR, true ) ) { Expression root = new TypeLiteral( getGosuClass() ); pushExpression( root ); if( parseFeatureLiteral( root ) ) { return true; } popExpression(); } return false; } private boolean parseNumberLiteral() { return parseNumberLiteral(false); } private boolean atNumberLiteralStart() { return match( null, null, SourceCodeTokenizer.TT_INTEGER, true ) || match( null, null, '.', true ); } private boolean parseNumberLiteral(boolean negated) { if( match( null, Keyword.KW_NaN ) ) { pushExpression( NumericLiteral.NaN.get().copy() ); return true; } else if( match( null, Keyword.KW_Infinity ) ) { pushExpression( NumericLiteral.INFINITY.get().copy() ); return true; } int mark = getTokenizer().mark(); Token T = new Token(); if( match( T, SourceCodeTokenizer.TT_INTEGER ) ) { String strValue = (negated ? "-" : "") + T._strValue; if( getNumericTypeFrom( strValue ) == null && match( null, '.' ) ) { strValue = maybeStripTypeModifier( strValue, null ); Token tmp = new Token(); if( match( tmp, null, SourceCodeTokenizer.TT_INTEGER, true ) ) { if( !isPrefixNumericLiteral(tmp._strValue) ) { match( T, SourceCodeTokenizer.TT_INTEGER ); strValue += '.'; strValue += T._strValue; } else { strValue += ".0"; } } else { match( T, SourceCodeTokenizer.TT_INTEGER ); strValue += ".0"; } } int lastPos = T.getTokenEnd(); if( match( T, null, SourceCodeTokenizer.TT_WORD, true ) ) { if( (lastPos >= T.getTokenStart()) && (T._strValue.charAt(0) == 'e' || T._strValue.charAt(0) == 'E' ) ) { match( T, SourceCodeTokenizer.TT_WORD ); if( T._strValue.length() == 1 ) { strValue += "e"; if( match( T, "+", SourceCodeTokenizer.TT_OPERATOR ) || match( T, "-", SourceCodeTokenizer.TT_OPERATOR ) ) { strValue += T._strValue; } if( match( T, SourceCodeTokenizer.TT_INTEGER ) ) { strValue += T._strValue; } else { getTokenizer().restoreToMark( mark ); return false; } } else { strValue += T._strValue; } int end = strValue.length() - 1; char suffix = ' '; if ( !Character.isDigit(strValue.charAt(end)) ) { suffix = strValue.charAt(end); strValue = strValue.substring(0, end); } try { BigDecimal bigNum = new BigDecimal( strValue ); strValue = bigNum.toPlainString(); if (bigNum.scale() <= 0) { strValue += ".0"; } } catch( NumberFormatException e ) { getTokenizer().restoreToMark( mark ); return false; } if( suffix != ' ') { strValue += suffix; } } } parseNumericValue( strValue ); return true; } else if( match( null, null, '.' ) ) { String strValue = (negated ? "-" : "") + "."; if( match( T, SourceCodeTokenizer.TT_INTEGER ) ) { strValue += T._strValue; parseNumericValue( strValue ); } else { pushErrorNumberLiteral( Res.MSG_EXPECTING_NUMBER_TO_FOLLOW_DECIMAL ); } return true; } return false; } private void parseNumericValue( String strValue ) { if( isPrefixNumericLiteral( strValue ) && strValue.indexOf( '.' ) != -1 ) { pushErrorNumberLiteral( Res.MSG_IMPROPER_VALUE_FOR_NUMERIC_TYPE, strValue, JavaTypes.pINT() ); } else { IType numericTypeFrom = getNumericTypeFrom( strValue ); if( numericTypeFrom != null ) { parseExplicitlyTypedNumericLiteral( strValue, numericTypeFrom ); } else { IType ctxType = getNumberContextType( _inferredContextStack.isEmpty() ? null : _inferredContextStack.peek() ); NumericLiteral e; if( strValue.indexOf( '.' ) != -1 ) { if( ctxType == JavaTypes.BIG_DECIMAL() ) { e = new NumericLiteral( strValue, new BigDecimal( strValue ), JavaTypes.BIG_DECIMAL()); } else { e = parseDoubleOrBigDec( strValue ); } } else { if( ctxType == JavaTypes.BIG_INTEGER() ) { if( isPrefixNumericLiteral(strValue)) { strValue = stripPrefix( strValue ); } e = new NumericLiteral( strValue, new BigInteger( strValue ), JavaTypes.BIG_INTEGER()); } else { try { e = parseIntOrLongOrBigInt( strValue ); } catch( NumberFormatException ex ) { pushErrorNumberLiteral(Res.MSG_IMPROPER_VALUE_FOR_NUMERIC_TYPE, strValue, JavaTypes.pINT()); return; } } } pushExpression( e ); } } } private NumericLiteral parseIntOrLongOrBigInt( String strValue ) { NumericLiteral e; int radix = 10; String strippedValue = strValue; if( isPrefixNumericLiteral( strValue ) ) { strippedValue = stripPrefix( strValue ); if( isHexLiteral( strValue ) ) { radix = 16; } else if ( isBinLiteral( strValue ) ) { radix = 2; } } try { e = new NumericLiteral( strValue, Integer.parseInt( strippedValue, radix), JavaTypes.pINT() ); } catch( NumberFormatException nfe ) { try { e = new NumericLiteral( strValue, Long.parseLong( strippedValue, radix ), JavaTypes.pLONG() ); } catch( NumberFormatException nfe2 ) { e = new NumericLiteral( strValue, new BigInteger( strippedValue, radix ), JavaTypes.BIG_INTEGER() ); } } return e; } private String stripPrefix( String strValue ) { String strippedValue; if( strValue.startsWith( "-" ) ) { strippedValue = "-" + strValue.substring( 3 ); } else { strippedValue = strValue.substring( 2 ); } return strippedValue; } private NumericLiteral parseDoubleOrBigDec( String strValue ) { double dValue = Double.parseDouble( strValue ); if( dValue == Double.POSITIVE_INFINITY || dValue == Double.NEGATIVE_INFINITY ) { return new NumericLiteral( strValue, new BigDecimal( strValue ), JavaTypes.BIG_DECIMAL() ); } else { return new NumericLiteral( strValue, dValue, JavaTypes.pDOUBLE() ); } } private void parseExplicitlyTypedNumericLiteral( String strValue, IType numericTypeFrom ) { if( isPrefixNumericLiteral( strValue ) ) { parsePrefixNumericLiteral( strValue, numericTypeFrom ); } else { parsePostfixNumericLiteral( strValue, numericTypeFrom, 10 ); } } private boolean isPrefixNumericLiteral( String strValue ) { return !(strValue.equalsIgnoreCase( "0b" ) || strValue.equalsIgnoreCase( "0bi" )|| strValue.equalsIgnoreCase( "0bd" ) || strValue.equalsIgnoreCase( "-0b" ) || strValue.equalsIgnoreCase( "-0bi" )|| strValue.equalsIgnoreCase( "-0bd" )) && (strValue.startsWith( "0x" ) || strValue.startsWith( "0X" ) || strValue.startsWith( "0b" ) || strValue.startsWith( "0B" ) || strValue.startsWith( "-0x" ) || strValue.startsWith( "-0X" ) || strValue.startsWith( "-0b" ) || strValue.startsWith( "-0B" )); } private void parsePrefixNumericLiteral( String strValue, IType numericTypeFrom ) { int radix = 10; String strippedValue = stripPrefix( strValue ); if( isHexLiteral( strValue ) ) { radix = 16; } else if ( isBinLiteral( strValue ) ) { radix = 2; } parsePostfixNumericLiteral( strippedValue, numericTypeFrom, radix ); } private boolean isHexLiteral(String num) { num = num.toLowerCase(); return num.startsWith( "0x" ) || num.startsWith( "-0x" ); } private boolean isBinLiteral(String num) { num = num.toLowerCase(); boolean hasPrefix = num.startsWith("0b") || num.startsWith("-0b"); int b = num.indexOf("b") + 1; boolean hasDigit = ( b < num.length() ) && Character.isDigit( num.charAt( b ) ); return hasPrefix && hasDigit; } private void parsePostfixNumericLiteral( String num, IType numericTypeFrom, int radix ) { String strValue = maybeStripTypeModifier( num, numericTypeFrom ); try { NumericLiteral e; if( JavaTypes.pBYTE().equals( numericTypeFrom ) ) { e = new NumericLiteral( strValue, Byte.parseByte( strValue, radix ), JavaTypes.pBYTE() ); } else if( JavaTypes.pSHORT().equals( numericTypeFrom ) ) { e = new NumericLiteral( strValue, Short.parseShort( strValue, radix ), JavaTypes.pSHORT() ); } else if( JavaTypes.pINT().equals( numericTypeFrom ) ) { e = new NumericLiteral( strValue, Integer.parseInt( strValue, radix ), JavaTypes.pINT() ); } else if( JavaTypes.pLONG().equals( numericTypeFrom ) ) { e = new NumericLiteral( strValue, Long.parseLong( strValue, radix ), JavaTypes.pLONG() ); } else if( JavaTypes.pFLOAT().equals( numericTypeFrom ) ) { e = new NumericLiteral( strValue, Float.parseFloat( strValue ), JavaTypes.pFLOAT() ); } else if( JavaTypes.pDOUBLE().equals( numericTypeFrom ) ) { e = new NumericLiteral( strValue, Double.parseDouble( strValue ), JavaTypes.pDOUBLE() ); } else if( JavaTypes.BIG_INTEGER().equals( numericTypeFrom ) ) { e = new NumericLiteral( strValue, new BigInteger( strValue , radix), JavaTypes.BIG_INTEGER() ); } else if( JavaTypes.BIG_DECIMAL().equals( numericTypeFrom ) ) { e = new NumericLiteral( strValue, new BigDecimal( strValue ), JavaTypes.BIG_DECIMAL() ); } else { throw new IllegalStateException( "Do not know how to parse a numeric type of value " + numericTypeFrom ); } if( hasTypeModifier( num ) ) { e.setExplicitlyTyped( true ); } pushExpression( e ); } catch( NumberFormatException ex ) { pushErrorNumberLiteral( Res.MSG_IMPROPER_VALUE_FOR_NUMERIC_TYPE, strValue, numericTypeFrom.getName() ); } } private String maybeStripTypeModifier( String strValue, IType numericTypeFrom ) { if( hasTypeModifier( strValue ) ) { int modifierLen = JavaTypes.BIG_DECIMAL().equals( numericTypeFrom ) || JavaTypes.BIG_INTEGER().equals( numericTypeFrom ) ? 2 : 1; strValue = strValue.substring( 0, strValue.length() - modifierLen ); } return strValue; } private boolean hasTypeModifier( String strValue ) { boolean hex = isHexLiteral( strValue ); char ch = strValue.toLowerCase().charAt(strValue.length() - 1); if( hex && ( ch == 's' ) || ( ch == 'l' ) ) { return true; } else if ( !hex && !Character.isDigit(ch) ) { return true; } return false; } private void pushErrorNumberLiteral( ResourceKey key, Object... args ) { NumericLiteral error = new NumericLiteral( "0", 0, JavaTypes.pINT() ); addError( error, key, args ); pushExpression( error ); } private IType getNumericTypeFrom( String strValue ) { boolean hex = isHexLiteral( strValue ); boolean bin = isBinLiteral( strValue ); if( !hex && (strValue.endsWith( "b" ) || strValue.endsWith( "B" )) ) { return JavaTypes.pBYTE(); } else if( strValue.endsWith( "s" ) || strValue.endsWith( "S" ) ) { return JavaTypes.pSHORT(); } else if( strValue.endsWith( "l" ) || strValue.endsWith( "L" ) ) { return JavaTypes.pLONG(); } else if( !hex && !bin && (strValue.endsWith( "f" ) || strValue.endsWith( "F" )) ) { return JavaTypes.pFLOAT(); } else if( !hex && (strValue.endsWith( "bi" ) || strValue.endsWith( "BI" )) ) { return JavaTypes.BIG_INTEGER(); } else if( !hex && !bin && (strValue.endsWith( "bd" ) || strValue.endsWith( "BD" )) ) { return JavaTypes.BIG_DECIMAL(); } else if( !hex && !bin && (strValue.endsWith( "d" ) || strValue.endsWith( "D" )) ) { return JavaTypes.pDOUBLE(); } else { return null; } } private boolean parseCharLiteral() { Token T = new Token(); if( match( T, (int)'\'' ) ) { if( T._strValue.length() != 1 ) { _parseStringLiteral( T._bUnterminated, T ); } else { char c = T._strValue.charAt( 0 ); Expression e = new CharLiteral( c ); verify( e, T.getInvalidCharPos() < 0, Res.MSG_INVALID_CHAR_AT, T.getInvalidCharPos() ); verify( e, !T._bUnterminated, Res.MSG_UNTERMINATED_STRING_LITERAL ); pushExpression( e ); } return true; } return false; } private boolean parseStringLiteralSeparately() { int iOffset = _tokenizer.getTokenStart(); int iLineNum = _tokenizer.getLineNumber(); int iColumn = getTokenizer().getTokenColumn(); if( parseStringLiteral() ) { setLocation( iOffset, iLineNum, iColumn ); return true; } return false; } private boolean parseStringLiteral() { Token T = new Token(); if( match( T, (int)'"' ) ) { _parseStringLiteral( T._bUnterminated, T ); return true; } return false; } private void _parseStringLiteral( boolean bUnterminatedLiteral, Token t ) { Expression e; if( t._strValue.contains( TemplateGenerator.SCRIPTLET_BEGIN ) || t._strValue.contains( TemplateGenerator.ALTERNATE_EXPRESSION_BEGIN ) ) { e = parseTemplatizedStringLiteral( t ); } else { String strValue = t._strValue; if( strValue.length() > 0 && strValue.charAt( 0 ) == TemplateGenerator.ESCAPED_SCRIPTLET_MARKER ) { strValue = strValue.substring( 1 ) .replace( TemplateGenerator.ESCAPED_SCRIPTLET_BEGIN_CHAR, '<' ) .replace( TemplateGenerator.ESCAPED_ALTERNATE_EXPRESSION_BEGIN_CHAR, '$' ); } e = new StringLiteral( strValue ); } if( bUnterminatedLiteral ) { e.addParseException(new ParseException(makeFullParserState(), Res.MSG_UNTERMINATED_STRING_LITERAL)); } verify( e, t.getInvalidCharPos() < 0, Res.MSG_INVALID_CHAR_AT, t.getInvalidCharPos() ); pushExpression( e ); } private TemplateStringLiteral parseTemplatizedStringLiteral( Token t ) { TemplateGenerator template = TemplateGenerator.getTemplate( new StringReader( t._strValue ) ); template.setContextInferenceManager(_ctxInferenceMgr); template.setForStringLiteral( true ); TemplateStringLiteral e = new TemplateStringLiteral( template ); GosuParser parser = (GosuParser)GosuParserFactory.createParser( _symTable, ScriptabilityModifiers.SCRIPTABLE ); IScriptPartId scriptPart = getScriptPart(); parser.pushScriptPart( scriptPart ); try { parser.setEditorParser( isEditorParser() ); parser._ctxInferenceMgr = _ctxInferenceMgr; template.setUseStudioEditorParser( isEditorParser() ); copyBlockStackTo( parser ); try { template.verify( parser, _dfsDeclByName, _typeUsesMap ); } catch( ParseResultsException exc ) { // errors are already where they occurred } boolean hasIssues = false; // adjust for open quote for( IParseTree location : parser.getLocations() ) { ((ParseTree)location).adjustOffset( 1, 0, 0 ); for( IParseIssue parseIssue : location.getParsedElement().getParseIssues() ) { ((ParseIssue)parseIssue).setStateSource( _tokenizer.getSource() ); hasIssues = true; } } setSubTree( parser.getLocations() ); try { template.compile( _scriptPartIdStack, _symTable instanceof ThreadSafeSymbolTable ? _symTable : _symTable.copy(), _dfsDeclByName, _typeUsesMap, _blocks, _ctxInferenceMgr ); } catch( TemplateParseException exc ) { if( !hasIssues && exc.getParseException() != null && !(getScriptPart().getContainingType() instanceof IGosuFragment) ) { List<IParseIssue> parseExceptions = exc.getParseException().getParseExceptions(); for( IParseIssue p : parseExceptions ) { e.addParseException( p ); } } } return e; } finally { parser.popScriptPart( scriptPart ); } } //------------------------------------------------------------------------------ // type-literal // name[\<<type-parameter-list>\>][\[\]] // public boolean parseTypeLiteral() { return parseTypeLiteral(false); } boolean parseTypeLiteral( boolean bInterface ) { boolean bContextType = getContextTypes().isEmpty(); if( bContextType ) { // Hint that we are trying to parse a type literal (during indirect member parsing) pushContextType( MetaType.DEFAULT_TYPE_TYPE.get() ); } try { int iOffset = _tokenizer.getTokenStart(); int iLineNum = _tokenizer.getLineNumber(); int iColumn = getTokenizer().getTokenColumn(); boolean bSuccess = _parseTypeLiteralWithAggregateSyntax( false, bInterface ); if( bSuccess ) { Expression e = peekExpression(); if( e instanceof TypeLiteral ) { IType monitorLockType = GosuTypes.IMONITORLOCK(); verify( e, monitorLockType == null || !monitorLockType.equals( ((TypeLiteral)e).getType().getType() ), Res.MSG_IMONITOR_LOCK_SHOULD_ONLY_BE_USED_WITHIN_USING_STMTS ); } setLocation( iOffset, iLineNum, iColumn, true ); } return bSuccess; } finally { if( bContextType ) { popContextType(); } } } void parseTypeLiteralIgnoreArrayBrackets() { int iOffset = _tokenizer.getTokenStart(); int iLineNum = _tokenizer.getLineNumber(); int iColumn = getTokenizer().getTokenColumn(); if( _parseTypeLiteralWithAggregateSyntax(true, false) ) { setLocation( iOffset, iLineNum, iColumn ); } } boolean _parseTypeLiteralWithAggregateSyntax( boolean bIgnoreArrayBrackets, boolean bInterface ) { boolean bRet = _parseTypeLiteral( bIgnoreArrayBrackets, bInterface ); if( match( null, "&", SourceCodeTokenizer.TT_OPERATOR, true ) ) { parseAggregateTypeLiteral( bInterface ); } return bRet; } boolean _parseTypeLiteral( boolean bIgnoreArrayBrackets, boolean bInterface ) { Token T = new Token(); int iOffset = _tokenizer.getTokenStart(); int iLineNum = _tokenizer.getLineNumber(); int iColumn = getTokenizer().getTokenColumn(); if( match( null, Keyword.KW_block ) ) { _parseBlockLiteral(); setLocation( iOffset, iLineNum, iColumn, true ); } else { boolean bNotAWord = !match( T, SourceCodeTokenizer.TT_WORD ); if( bNotAWord ) { TypeLiteral typeLiteral = bInterface ? new InterfaceTypeLiteral( ErrorType.getInstance() ) : new TypeLiteral( ErrorType.getInstance() ); verify( typeLiteral, false, Res.MSG_EXPECTING_TYPE_NAME ); pushExpression( typeLiteral ); IToken priorT = getTokenizer().getPriorToken(); setLocation( priorT.getTokenEnd(), priorT.getLine(), priorT.getTokenColumn(), true, true ); return false; } parseTypeLiteral( T, bIgnoreArrayBrackets, bInterface, iOffset, iLineNum, iColumn ); } return true; } private void parseAggregateTypeLiteral(boolean bInterface) { CompoundTypeLiteral typeLiteral = new CompoundTypeLiteral(); List<IType> types = new ArrayList<IType>(); while( true ) { addToCompoundType( types ); if( !match( null, "&", SourceCodeTokenizer.TT_OPERATOR ) ) { break; } _parseTypeLiteral( false, bInterface ); } verify( typeLiteral, types.size() > 1, Res.MSG_AGGREGATES_MUST_CONTAIN_MORE ); typeLiteral.setType( CompoundType.get( new HashSet<IType>( types ) ) ); pushExpression(typeLiteral); } private void addToCompoundType( List<IType> types ) { TypeLiteral typeLiteralComponent = (TypeLiteral)popExpression(); IType t = typeLiteralComponent.getType().getType(); verify( typeLiteralComponent, t != JavaTypes.pVOID(), Res.MSG_VOID_NOT_ALLOWED ); Set<IType> componentTypes = t.isCompoundType() ? t.getCompoundTypeComponents() : Collections.singleton(t); for( IType componentType : componentTypes ) { boolean bFoundClassAlready = false; for( IType csr : types ) { if( verify( typeLiteralComponent, csr instanceof ErrorType || csr != componentType, Res.MSG_ALREADY_CONTAINS_TYPE, componentType ) ) { verify( typeLiteralComponent, csr instanceof ErrorType || !csr.isAssignableFrom( componentType ), Res.MSG_INTERFACE_REDUNDANT, csr, componentType ); verify( typeLiteralComponent, csr instanceof ErrorType || !componentType.isAssignableFrom( csr ), Res.MSG_INTERFACE_REDUNDANT, componentType, csr ); } if( !csr.isInterface() ) { bFoundClassAlready = true; } verify( typeLiteralComponent, componentType.isInterface() || !bFoundClassAlready, Res.MSG_ONLY_ONE_CLASS_IN_COMPONENT_TYPE ); } types.add( componentType ); } } void parseBlockLiteral() { int iOffset = getTokenizer().getTokenStart(); int iLineNum = getTokenizer().getLineNumber(); int iColumn = getTokenizer().getTokenColumn(); _parseBlockLiteral(); setLocation( iOffset, iLineNum, iColumn ); } void _parseBlockLiteral() { BlockLiteral literal = new BlockLiteral(); verify( literal, match( null, '(' ), Res.MSG_EXPECTING_LEFTPAREN_BLOCK ); ArrayList<IType> argTypes = new ArrayList<IType>(); ArrayList<String> argNames = new ArrayList<String>(); ArrayList<IExpression> defValues = new ArrayList<IExpression>(); if( !match( null, ')' ) ) { do { String result; int state = _tokenizer.mark(); Token t = new Token(); boolean bEquals = false; Expression defExpr = null; TypeLiteral blockLiteral = null; if( match( t, SourceCodeTokenizer.TT_WORD ) ) { if( match( null, ":", SourceCodeTokenizer.TT_OPERATOR ) ) { result = t._strValue; } else if( bEquals = match( null, "=", SourceCodeTokenizer.TT_OPERATOR ) ) { result = t._strValue; parseExpression(); defExpr = popExpression(); } else if( match( null, null, '(', true ) ) { result = t._strValue; parseBlockLiteral(); blockLiteral = (TypeLiteral) popExpression(); } else { _tokenizer.restoreToMark( state ); result = null; } } else { _tokenizer.restoreToMark( state ); result = null; } String name = result; TypeLiteral typeLiteral = null; if( !bEquals ) { if ( blockLiteral == null ) { parseTypeLiteral(); typeLiteral = (TypeLiteral)popExpression(); if( match( null, "=", SourceCodeTokenizer.TT_OPERATOR ) ) { parseExpression( typeLiteral.getType().getType() ); defExpr = popExpression(); } } else { typeLiteral = blockLiteral; } argTypes.add( typeLiteral.getType().getType() ); verifyOrWarn( typeLiteral, name != null, true, Res.MSG_BLOCK_TYPES_SHOULD_HAVE_ARG_NAMES ); verify( typeLiteral, typeLiteral.getType().getType() != JavaTypes.pVOID(), Res.MSG_VOID_NOT_ALLOWED ); } else { argTypes.add( defExpr.getType() ); verifyOrWarn( literal, name != null, true, Res.MSG_BLOCK_TYPES_SHOULD_HAVE_ARG_NAMES ); verify( literal, defExpr.getType() != JavaTypes.pVOID(), Res.MSG_VOID_NOT_ALLOWED ); } if( defExpr != null ) { if( verify( defExpr, defExpr.isCompileTimeConstant(), Res.MSG_COMPILE_TIME_CONSTANT_REQUIRED ) ) { defValues.add( defExpr ); } } else { defValues.add( null ); } if( name != null && argNames.contains( name ) ) { verify( typeLiteral == null ? literal : typeLiteral, false, Res.MSG_VARIABLE_ALREADY_DEFINED, name ); } argNames.add( name == null ? "" : name ); } while( match( null, ',' ) ); verify( literal, match( null, ')' ), Res.MSG_EXPECTING_RIGHTPAREN_BLOCK ); } argNames.trimToSize(); argTypes.trimToSize(); literal.setArgTypes( argTypes ); literal.setArgNames( argNames ); literal.setDefValueExpressions( defValues ); TypeLiteral returnType; if( match( null, ":", SourceCodeTokenizer.TT_OPERATOR ) ) { parseTypeLiteral(); returnType = (TypeLiteral)popExpression(); } else { returnType = new TypeLiteral( JavaTypes.pVOID() ); returnType.setType( JavaTypes.pVOID() ); } verify( literal, argNames.size() <= IBlock.MAX_ARGS, Res.MSG_BLOCKS_CAN_HAVE_A_MOST_SIXTEEN_ARGS ); literal.setReturnType( returnType ); pushExpression( literal ); } void parseTypeLiteral( Token T, boolean bIgnoreArrayBrackets, boolean bInterface, int iOffset, int iLineNum, int iColumn ) { parseCompoundTypeLiteralExpression( T, bInterface, iOffset, iLineNum, iColumn ); Expression expr = popExpression(); if( expr instanceof Identifier && !(expr.getType() instanceof ErrorType) ) { //!! todo: this is a hack so we can resolve types with the same name as identifiers (case insensitively) TypeLiteral e = bInterface ? new InterfaceTypeLiteral( MetaType.getLiteral( expr.getType() ) ) : new TypeLiteral( MetaType.getLiteral( expr.getType() ) ); e.setPackageExpression( expr ); expr = e; } else if( !(expr instanceof TypeLiteral) ) { TypeLiteral e = bInterface ? new InterfaceTypeLiteral( ErrorType.getInstance() ) : new TypeLiteral( ErrorType.getInstance() ); e.setPackageExpression( expr ); e.addParseException( new ParseException( null, Res.MSG_EXPECTING_TYPE_NAME ) ); expr = e; } IType type = ((TypeLiteral)expr).getType().getType(); verifyTypeAccessible( (TypeLiteral)expr, type ); T._strValue = type.getName(); resolveArrayOrParameterizationPartOfTypeLiteral( T, bIgnoreArrayBrackets, (TypeLiteral)expr ); } private void verifyTypeAccessible( TypeLiteral expr, IType type ) { ICompilableType gsClass = getGosuClass(); if( gsClass == null || Modifier.isPublic( type.getModifiers() ) ) { return; } IRelativeTypeInfo.Accessibility acc = FeatureManager.getAccessibilityForClass( type, gsClass ); if( Modifier.isPrivate( type.getModifiers() ) ) { verify( expr, acc == IRelativeTypeInfo.Accessibility.PRIVATE, Res.MSG_TYPE_HAS_XXX_ACCESS, type.getName(), Keyword.KW_private.toString() ); } else if( Modifier.isProtected( type.getModifiers() ) ) { verify( expr, acc == IRelativeTypeInfo.Accessibility.PROTECTED || acc == IRelativeTypeInfo.Accessibility.INTERNAL || acc == IRelativeTypeInfo.Accessibility.PRIVATE, Res.MSG_TYPE_HAS_XXX_ACCESS, type.getName(), Keyword.KW_protected.toString() ); } else if( /* package-protected (or internal) */ !Modifier.isPublic( type.getModifiers() ) && !Modifier.isProtected( type.getModifiers() )) { verify( expr, acc == IRelativeTypeInfo.Accessibility.INTERNAL || acc == IRelativeTypeInfo.Accessibility.PRIVATE, Res.MSG_TYPE_HAS_XXX_ACCESS, type.getName(), Keyword.KW_internal.toString() ); } } private void parseCompoundTypeLiteralExpression( Token T, boolean bInterface, int iOffset, int iLineNum, int iColumn ) { parseNamespaceStartOrRelativeType( T, bInterface ); Expression expr = peekExpression(); if( expr.hasParseExceptions() ) { List<IParseIssue> exceptions = expr.getParseExceptions(); if( exceptions.size() != 1 || (exceptions.get( 0 ).getMessageKey() != Res.MSG_CANNOT_REFERENCE_CLASS_TYPE_VAR_IN_STATIC_CONTEXT && exceptions.get( 0 ).getMessageKey() != Res.MSG_TYPE_PARAM_NOT_ASSIGNABLE_TO && exceptions.get( 0 ).getMessageKey() != Res.MSG_EXPECTING_CLOSING_ANGLE_BRACKET_FOR_TYPE) ) { ParseException pe = expr.removeParseException( null );// Res.MSG_BAD_IDENTIFIER_NAME ); pe.setMessage( Res.MSG_INVALID_TYPE, T._strValue ); expr.addParseException( pe ); } } setLocation( iOffset, iLineNum, iColumn ); parseIndirectMemberAccess( iOffset, iLineNum, iColumn, true ); } private boolean isTypeParameterErrorMsg( Expression expr, List<IParseIssue> exceptions ) { return exceptions.get( 0 ).getMessageKey() == Res.MSG_TYPE_PARAM_NOT_ASSIGNABLE_TO && expr instanceof TypeLiteral && !isErrorType( ((TypeLiteral)expr).getType().getType() ); } /** * @return True if parsed parameterized type. */ private boolean resolveArrayOrParameterizationPartOfTypeLiteral( Token T, boolean bIgnoreArrayBrackets, TypeLiteral e ) { boolean bArrayOrParameterization = false; if( !bIgnoreArrayBrackets ) { bArrayOrParameterization = parseArrayType( e ); } pushExpression(e); if( !T._strValue.endsWith( "[]" ) ) { int iOffset = _tokenizer.getTokenStart(); int iLineNum = _tokenizer.getLineNumber(); int iColumn = getTokenizer().getTokenColumn(); if( match( null, "<", SourceCodeTokenizer.TT_OPERATOR ) ) { TypeLiteral typeLiteral = (TypeLiteral)peekExpression(); IType type = typeLiteral.getType().getType(); List<TypeLiteral> paramTypes = Collections.emptyList(); boolean bRecursiveGosuClass = type instanceof IGosuClassInternal && ((IGosuClassInternal)type).isCompilingHeader(); if( (type instanceof IJavaTypeInternal && ((IJavaTypeInternal)type).isDefiningGenericTypes()) || bRecursiveGosuClass ) { // If defining generic types, we assume a recursive type and, for now, use the raw generic type eatPossibleParametarization( false ); } else { verify( e, type.isGenericType(), Res.MSG_PARAMETERIZATION_NOT_SUPPORTED_FOR_TYPE, type.getName() ); paramTypes = parseTypeParameters( type ); verify( e, match( null, ">", SourceCodeTokenizer.TT_OPERATOR ), Res.MSG_EXPECTING_CLOSING_ANGLE_BRACKET_FOR_TYPE ); makeTypeParameterListClause( iOffset, iLineNum, iColumn, paramTypes ); } int numArrays = 0; while( !bIgnoreArrayBrackets && match( null, '[' ) ) { verify( e, match( null, ']' ), Res.MSG_EXPECTING_ARRAY_BRACKET ); ++numArrays; } if( !(type instanceof ErrorType) ) { IType[] types = new IType[paramTypes.size()]; for( int i = 0; i < paramTypes.size(); i++ ) { TypeLiteral tl = (TypeLiteral)paramTypes.get( i ); types[i] = (tl.getType()).getType(); } if( !bRecursiveGosuClass && verifyCanParameterizeType( e, type, types ) ) { typeLiteral.setParameterTypes( types ); type = typeLiteral.getType().getType(); } if( numArrays > 0 ) { for( int i = 0; i < numArrays; i++ ) { type = type.getArrayType(); } typeLiteral.setType( MetaType.getLiteral( type ) ); } } bArrayOrParameterization = true; } else { TypeLiteral typeLiteral = (TypeLiteral)peekExpression(); IType type = typeLiteral.getType().getType(); try { if( type.isGenericType() ) { // If a generic type, assume the default parameterized version e.g., List => List<Object> type = TypeLord.makeDefaultParameterizedType( type, !type.isParameterizedType() && (!(getGosuClass() instanceof IGosuClass) || !((IGosuClass)getGosuClass()).isCompilingHeader()) ); typeLiteral.setType( MetaType.getLiteral( type ) ); } } catch( Exception ex ) { throw GosuExceptionUtil.forceThrow( ex, type.getName() ); } } } return bArrayOrParameterization; } private boolean parseArrayType( TypeLiteral tl ) { boolean bBalancedBrackets = true; IType baseType = tl.getType().getType(); while( match( null, '[' ) ) { bBalancedBrackets = match( null, ']' ); if( !bBalancedBrackets ) { advanceToNextTokenSilently(); } try { baseType = baseType.getArrayType(); } catch( IllegalArgumentException iae ) { tl.addParseException( Res.MSG_ARRAY_NOT_SUPPORTED, baseType.getName() ); baseType = ErrorType.getInstance(); } } if( baseType != tl.getType().getType() ) { warn( tl, !tl.getType().getType().isParameterizedType() || TypeLord.getDefaultParameterizedType( tl.getType().getType() ) == tl.getType().getType(), Res.MSG_PARAMETERIZED_ARRAY_COMPONENT ); tl.setType( MetaType.getLiteral( baseType ) ); verify( tl, bBalancedBrackets, Res.MSG_EXPECTING_ARRAY_BRACKET ); return true; } return false; } //------------------------------------------------------------------------------ // type-parameter-list // <type-parameter> // <type-parameter-list> , <type-parameter> // List<TypeLiteral> parseTypeParameters( IType enclosingType ) { List<TypeLiteral> paramTypes = new ArrayList<TypeLiteral>(); int i = 0; do { IType boundingType = JavaTypes.OBJECT(); if( enclosingType != null && enclosingType.isGenericType() ) { IGenericTypeVariable[] typeVars = enclosingType.getGenericTypeVariables(); if( typeVars != null && typeVars.length > i ) { boundingType = typeVars[i].getBoundingType(); } } parseParameterType( boundingType ); paramTypes.add( (TypeLiteral)popExpression() ); } while( match( null, ',' ) && ++i > 0 ); return paramTypes; } private void makeTypeParameterListClause( int iOffset, int iLineNum, int iColumn, List<TypeLiteral> paramTypes ) { if( paramTypes.size() > 0 ) { TypeParameterListClause e = new TypeParameterListClause( paramTypes.toArray( new ITypeLiteralExpression[paramTypes.size()] ) ); pushExpression( e ); boolean bZeroLength = _tokenizer.getTokenStart() == iOffset; if( bZeroLength ) { IToken priorToken = getTokenizer().getPriorToken(); iOffset = priorToken.getTokenEnd(); iLineNum = priorToken.getLine(); iColumn = priorToken.getTokenColumn(); } setLocation( iOffset, iLineNum, iColumn, true ); popExpression(); } } //------------------------------------------------------------------------------ // type-parameter // ? [extends <type-literal>] // ? [super <type-literal>] // <type-literal> // boolean parseParameterType( IType boundingType ) { if( match( null, "?", SourceCodeTokenizer.TT_OPERATOR ) ) { if( match( null, Keyword.KW_extends ) ) { if( match( null, "?", SourceCodeTokenizer.TT_OPERATOR, true ) ) { // A strange, but necessary case. E.g., // List<E> has method: addAll( Collection<? extends E> c ) // when we have type List<?> we'll have addAll( Collection<? extends ?> c ) // which essentially needs to be just that - a Collection of something undefined if( !parseParameterType( boundingType ) ) { return false; } } else { parseTypeLiteral(); } } else if( match( null, Keyword.KW_super ) ) { if( match( null, "?", SourceCodeTokenizer.TT_OPERATOR, true ) ) { // A strange, but necessary case. E.g., // List<E> has method: addAll( Collection<? super E> c ) // when we have type List<?> we'll have addAll( Collection<? super ?> c ) // which essentially needs to be just that - a Collection of something undefined if( !parseParameterType( boundingType ) ) { return false; } } else { parseTypeLiteral(); popExpression(); // Eat whatever type literal is here (we punt on contravariance) TypeLiteral typeLiteral = new TypeLiteral( MetaType.getLiteral( boundingType ) ); pushExpression( typeLiteral ); } } else { pushExpression( new TypeLiteral( JavaTypes.OBJECT() ) ); } } else { parseTypeLiteral(); } TypeLiteral tl = (TypeLiteral)peekExpression(); boxTypeLiteralsType( tl ); return true; } private void boxTypeLiteralsType( TypeLiteral tl ) { IType tlType = tl.getType().getType(); if( !warn( tl, !tlType.isPrimitive(), Res.MSG_PRIMITIVE_TYPE_PARAM, tlType.getName(), TypeSystem.getBoxType( tlType ) ) ) { tl.setType(TypeSystem.getBoxType(tlType)); } } //------------------------------------------------------------------------------ // statement // namespace-statement // package namespace-name // // uses-statement // uses type-literal // uses namespace-ref // // block // { [statement-list] } // statement-list // <statement> // <statement-list> <statement> // * Left recursion removed is: * // statement-list // <statement> <statement-list2> // statement-list2 // <statement> // null // // assignment-statement // <identifier> = <expression> // <member-access> = <expression> // <array-access> = <expression> // // method-call-statement // <method-call-expression> // // argument-list // <expression> // <argument-list> , <expression> // // if-statement // if ( <expression> ) <statement> [ else <statement> ] [ unless ( <expression> ) ] // // for...in-statement // for ( <identifier> in <expression> [ index <identifier> ] ) <statement> // // while-statement // while ( <expression> ) <statement> // // do...while-statetment // do <statement> while ( <expression> ) // // var-statement // var <identifier> [ : <type-literal> ] = <expression> // var <identifier> : <type-literal> [ = <expression> ] // // switch-statement // switch ( <expression> ) { [switch-cases] [switch-default] } // // switch-cases // <switch-case> // <switch-cases> <switch-case> // // switch-case // case <expression> : [statement-list] // // switch-default // default : [statement-list] // // continue-statement // continue // // break-statement // break // // return-statement // return <expression> // return ; // // assert-statement // assert <expression> // assert <expression> : <expression> // // class-definition // [modifiers] class <identifier> [extends <base-class>] [implements <interfaces-list>] { <class-members> } // // function-definition // [modifiers] function <identifier> ( [ <argument-declaration-list> ] ) [ : <type-literal> ] <statement-block> // // argument-declaration-list // <argument-declaration> // <argument-declarationlist> , <argument-declaration> // // argument-declaration // <identifier> : <type-literal> // // try-catch-finally-statement // try <statement> [ catch ( <identifier> ) <statement> ] [ finally <statement> ] // boolean parseStatement() { return parseStatement( false ); } boolean parseStatement( boolean bAsStmtBlock ) { incStatementDepth(); try { int iOffset = _tokenizer.getTokenStart(); int iLineNum = _tokenizer.getLineNumber(); int iColumn = getTokenizer().getTokenColumn(); checkInstruction( true ); ParseTree prevLocation = peekLocation(); IParsedElement prevStmt = prevLocation != null && prevLocation.getParsedElement() != null ? prevLocation.getParsedElement() instanceof Statement ? prevLocation.getParsedElement() : null : null; boolean bRet; boolean bSetLocation = true; boolean bMatchedBrace = !bAsStmtBlock && match( null, '{' ); if( bMatchedBrace || bAsStmtBlock ) { parseStatementBlock( bMatchedBrace || !bAsStmtBlock ); bRet = true; bSetLocation = peekStatement() instanceof StatementList; } else { bRet = _parseStatement(); } if( bRet && bSetLocation ) { // Consume optional trailing semi as part of the statement match( null, ';' ); setLocation( iOffset, iLineNum, iColumn, bAsStmtBlock ); ParsedElement currentStmt = peekStatement(); if( !(currentStmt instanceof NoOpStatement) && !(prevStmt instanceof NoOpStatement) ) { warn( currentStmt, prevStmt == null || prevStmt.getLineNum() != currentStmt.getLineNum() || prevStmt.hasParseExceptions() || currentStmt.hasParseExceptions(), Res.MSG_STATEMENT_ON_SAME_LINE ); } } return bRet; } finally { decStatementDepth(); } } boolean parseLoopStatement() { _iBreakOk++; _iContinueOk++; try { return parseStatement(); // if( parseStatement() ) { // Statement stmt = peekStatement(); // warn( stmt, doesLoopCycle( stmt ), Res.MSG_LOOP_DOESNT_LOOP ); // return true; // } // return false; } finally { _iBreakOk--; _iContinueOk--; } } //## todo: doesn't handle this case (break is absolute with help of continue, but info about continue does not exist past the break) //## propbably best to change getLeastSignificantTerminal() to return something that provides more information, yet retains the same //## terminal statement for normal unreachable code detection. // do { // if( cond ) { // break // } // else { // continue // } // } while( true ) // // private boolean doesLoopCycle( Statement stmt ) // { // boolean[] bAbsolute = {false}; // ITerminalStatement termStmt = stmt.getLeastSignificantTerminalStatement( bAbsolute ); // if( termStmt == null || !bAbsolute[0] ) { // return true; // } // return termStmt.getTerminalType() == TerminalType.Continue; // } boolean _parseStatement() { Token T = new Token(); if( areUsingStatementsAllowedInStatementLists() && match( null, Keyword.KW_uses ) ) { parseUsesStatement( T ); } else if( match( null, Keyword.KW_if ) ) { parseIfStatement(); } else if( match( null, Keyword.KW_try ) ) { parseTryCatchFinallyStatement( T ); } else if( match( null, Keyword.KW_throw ) ) { parseThrowStatement(); } else if( match( null, Keyword.KW_continue ) ) { ContinueStatement stmt = new ContinueStatement(); verify( stmt, _iContinueOk > 0, Res.MSG_CONTINUE_OUTSIDE_LOOP ); pushStatement( stmt ); } else if( match( null, Keyword.KW_break ) ) { BreakStatement stmt = new BreakStatement(); verify( stmt, _iBreakOk > 0, Res.MSG_BREAK_OUTSIDE_SWITCH_OR_LOOP ); pushStatement( stmt ); } else if( match( null, Keyword.KW_return ) ) { parseReturnStatement(); } else if( match( null, Keyword.KW_foreach ) || match( null, Keyword.KW_for ) ) { parseForEachStatement( T ); } else if( match( null, Keyword.KW_while ) ) { parseWhileStatement(); } else if( match( null, Keyword.KW_do ) ) { parseDoWhileStatement(); } else if( match( null, Keyword.KW_switch ) ) { parseSwitchStatement(); } else if( match( null, Keyword.KW_using ) ) { parseUsingStatement(); } else if( match( null, Keyword.KW_assert ) ) { parseAssertStatement(); } else if( match( null, Keyword.KW_final ) ) { VarStatement varStmt = new VarStatement(); varStmt.setModifierInfo( new ModifierInfo(0) ); varStmt.setFinal( true ); parseLocalVarStatement( varStmt, T ); } else if( match( null, Keyword.KW_var, true ) ) { VarStatement varStmt = new VarStatement(); parseLocalVarStatement( varStmt, T ); } else if( match( null, ';' ) ) { pushStatement( new NoOpStatement() ); } else if( getGosuClass() instanceof IGosuProgram && getStatementDepth() == 1 && !isParsingBlock() && !((IGosuProgramInternal)getGosuClass()).isStatementsOnly() && (maybeAdvanceTokenizerToEndOfSavedLocation()) ) { pushStatement( new NoOpStatement() ); } else if( !isParsingFunction() && parseFunctionDefinition() ) { return true; } else if( !isParsingFunction() && parsePropertyDefinition() ) { return true; } else if( match( null, Keyword.KW_eval, true ) ) { int iOffset = _tokenizer.getTokenStart(); int iLineNum = _tokenizer.getLineNumber(); int iColumn = getTokenizer().getTokenColumn(); match( null, Keyword.KW_eval ); parseEvalExpression(); setLocation( iOffset, iLineNum, iColumn, true ); pushStatement( new EvalStatement( (EvalExpression)popExpression() ) ); } else if( !parseAssignmentOrMethodCall() ) { if( !match( null, null, SourceCodeTokenizer.TT_EOF, true ) ) { int iOffset = _tokenizer.getTokenStart(); int iLineNum = _tokenizer.getLineNumber(); int iColumn = getTokenizer().getTokenColumn(); if( !match( null, null, '}', true ) && !match( null, null, ';', true ) ) { if( isParsingFunction() ) { Statement noop = new NoOpStatement(); if( maybeAdvanceTokenizerToEndOfSavedLocation() ) { pushStatement( noop ); setLocation( iOffset, iLineNum, iColumn, true, true ); popStatement(); return false; } else if( match( null, Keyword.KW_construct ) || match( null, Keyword.KW_function ) || match( null, Keyword.KW_property ) ) { eatStatementBlock( noop, Res.MSG_SYNTAX_ERROR ); pushStatement( noop ); setLocation( iOffset, iLineNum, iColumn, true, true ); popStatement(); return false; } } String str = _tokenizer.getTokenAsString(); TypeLiteral type = resolveTypeLiteral( str ); _tokenizer.nextToken(); Statement noop = new NoOpStatement(); if( !(type.evaluate() instanceof ErrorType) ) { int state = _tokenizer.mark(); int iLocationsCount = _locations.size(); parsePrimaryExpression(); Expression expression = popExpression(); if( expression instanceof Identifier && match( null, "=", SourceCodeTokenizer.TT_OPERATOR ) ) { expression.clearParseExceptions(); parseExpression(); //bad rhs popExpression(); verify( noop, false, Res.MSG_JAVA_STYLE_VARIABLE_DECLARATION, str ); } else { _tokenizer.restoreToMark( state ); removeLocationsFrom( iLocationsCount ); verify( noop, false, Res.MSG_UNEXPECTED_TOKEN, str ); } } else { verify( noop, false, Res.MSG_UNEXPECTED_TOKEN, str ); } pushStatement( noop ); } else if( getStatementDepth() == 1 && !isParsingBlock() && getGosuClass() instanceof IGosuProgram && ((IGosuProgramInternal)getGosuClass()).isParsingExecutableProgramStatements() ) { Statement noop = new NoOpStatement(); verify( noop, false, Res.MSG_UNEXPECTED_TOKEN, _tokenizer.getTokenAsString() ); pushStatement( noop ); _tokenizer.nextToken(); } else { return false; } } else { return false; } } return true; } private void parseAssertStatement() { AssertStatement assertStmt = new AssertStatement(); if( verify( assertStmt, getGosuClass() instanceof IGosuClassInternal, Res.MSG_ASSERTIONS_NOT_ALLOWED_HERE ) ) { ((IGosuClassInternal)getGosuClass()).setHasAssertions( true ); } parseExpression( JavaTypes.pBOOLEAN() ); Expression condition = popExpression(); if( !verify( condition, !(condition instanceof NotAWordExpression), Res.MSG_EXPECTING_CONDITION_FOR_ASSERT ) ) { //noinspection ThrowableResultOfMethodCallIgnored condition.removeParseException( Res.MSG_SYNTAX_ERROR ); } assertStmt.setCondition( condition ); if( match( null, ":", SourceCodeTokenizer.TT_OPERATOR ) ) { parseExpression( JavaTypes.OBJECT() ); Expression detail = popExpression(); if( !verify( detail, !(detail instanceof NotAWordExpression), Res.MSG_EXPECTING_MESSAGE_FOR_ASSERT ) ) { //noinspection ThrowableResultOfMethodCallIgnored detail.removeParseException( Res.MSG_SYNTAX_ERROR ); } assertStmt.setDetail( detail ); } pushStatement( assertStmt ); } private boolean areUsingStatementsAllowedInStatementLists() { return getGosuClass() == null || (getGosuClass() instanceof IGosuProgramInternal && ((IGosuProgramInternal) getGosuClass()).allowsUses()) || CommonServices.getEntityAccess().areUsesStatementsAllowedInStatementLists(getGosuClass()); } private int getStatementDepth() { return _iStmtDepth; } private void incStatementDepth() { _iStmtDepth++; } private void decStatementDepth() { _iStmtDepth--; } void parseLocalVarStatement( VarStatement varStmt, Token t ) { verify( varStmt, match( t, Keyword.KW_var ), Res.MSG_EXPECTING_VAR_STMT ); int iNameOffset = getTokenizer().getTokenStart(); if( verify( varStmt, match( t, SourceCodeTokenizer.TT_WORD ), Res.MSG_EXPECTING_IDENTIFIER_VAR ) ) { varStmt.setNameOffset( iNameOffset, t._strValue ); warn( varStmt, !Keyword.isKeyword( t._strValue ), Res.MSG_IMPROPER_USE_OF_KEYWORD, t._strValue ); } else { t._strValue = null; } parseVarStatement( varStmt, t, false ); } void parseVarStatement( VarStatement varStmt, Token idToken, boolean bClassMember ) { String strIdentifier = idToken._strValue == null ? "" : idToken._strValue; warn( varStmt, !Keyword.isKeyword( strIdentifier ), Res.MSG_IMPROPER_USE_OF_KEYWORD, strIdentifier ); IToken priorToken = getTokenizer().getPriorToken(); boolean bZeroLength = strIdentifier.length() <= 0; addNameInDeclaration( strIdentifier, bZeroLength ? priorToken.getTokenEnd() : idToken._iDocPosition, priorToken.getLine(), priorToken.getTokenColumn(), !bZeroLength ); ISymbol existingSymbol = _symTable.getSymbol( strIdentifier ); if( (isParsingBlock() || getParsingAnonymousClass() != null) && !isParsingAnnotation() ) { existingSymbol = captureSymbol( getCurrentEnclosingGosuClass(), strIdentifier, null ); } boolean bFieldSymbolFromProgram = false; if( !bClassMember && existingSymbol != null ) { bFieldSymbolFromProgram = getGosuClass() instanceof IGosuProgram && (bClassMember || isLocalVarTopLevelFunctionBodyStmt()) && ((IGosuProgramInternal)getGosuClass()).isParsingExecutableProgramStatements() && (existingSymbol instanceof DynamicSymbol || existingSymbol instanceof ScopedDynamicSymbol); if( verify( varStmt, bFieldSymbolFromProgram, Res.MSG_VARIABLE_ALREADY_DEFINED, strIdentifier ) ) { // Overwrite program var's symbol with class field symbol varStmt.setSymbol( existingSymbol ); } } // NOTE: For class member parsing we don't care about these results. We just want to eat the keywords. // The scope is setup when we decl parse in GosuClassParser boolean bRequest = false; boolean bSession = false; boolean bApplication = false; if( (match( null, Keyword.KW_execution )) || (bRequest = match( null, Keyword.KW_request )) || (bSession = match( null, Keyword.KW_session )) || (bApplication = match( null, Keyword.KW_application )) ) { } if( varStmt.getModifierInfo() == null ) { varStmt.setModifierInfo( new ModifierInfo(0) ); } TypeLiteral typeLiteral = null; if( match( null, ":", SourceCodeTokenizer.TT_OPERATOR ) ) { parseTypeLiteral(); typeLiteral = (TypeLiteral)popExpression(); } else if( !match( null, "=", SourceCodeTokenizer.TT_OPERATOR, true ) ) { if( match( null, null, '(', true ) ) { parseBlockLiteral(); typeLiteral = (TypeLiteral)popExpression(); } } if( !bClassMember && getGosuClass() instanceof IGosuProgram && getGosuClass().getMemberField( strIdentifier ) != null ) { // Eat the property alias if this is a program field (the class parser already made the property) if( match( null, Keyword.KW_as ) ) { match( null, Keyword.KW_readonly ); match( null, SourceCodeTokenizer.TT_WORD ); } } Expression eas = null; if( match( null, "=", SourceCodeTokenizer.TT_OPERATOR ) ) { pushParsingFieldInitializer( varStmt ); putThisAndSuperSymbols( varStmt.getModifierInfo() ); // assume caller must pushes scope try { parseExpression( typeLiteral == null ? null : typeLiteral.getType().getType() ); } finally { popParsingFieldInitializer(); } eas = popExpression(); if( eas.hasParseExceptions() ) { if( typeLiteral != null ) { IType typeCast = typeLiteral.getType().getType(); eas.getParseExceptions().get( 0 ).setExpectedType( typeCast ); } } detectLikelyJavaCast(eas); } verify( varStmt, eas != null || typeLiteral != null, Res.MSG_VARIABLE_TYPE_OR_VALUE_REQUIRED ); if( typeLiteral != null ) { verify( typeLiteral, typeLiteral.getType().getType() != JavaTypes.pVOID(), Res.MSG_VOID_NOT_ALLOWED ); } // Create a local symbol for the identifier part of the 'var' statement // (so it can be legally referenced in the containing scope). IType type = null; if( eas != null ) { type = eas.getType(); if( type == null ) { type = ErrorType.getInstance(); } } if( typeLiteral != null ) { type = typeLiteral.getType().getType(); } else if( bClassMember && eas != null ) { // Assign inferred type to var property corresponding with this field IGosuVarPropertyInfo vpi = (IGosuVarPropertyInfo) getGosuClass().getTypeInfo().getProperty( getGosuClass(), strIdentifier ); vpi.assignActualType( eas.getType() ); vpi.assignSymbolType( eas.getType() ); } verify( varStmt, !JavaTypes.pVOID().equals(type), Res.MSG_VARIABLE_MUST_HAVE_NON_NULL_TYPE ); //if no type was found, we have added an error so give the symbol the error type if( type == null ) { type = ErrorType.getInstance(); } ISymbol symbol; if( bClassMember || bFieldSymbolFromProgram ) { symbol = varStmt.getSymbol(); symbol.setType( type ); varStmt.setType( type ); } else { GlobalScope scope = bApplication ? GlobalScope.APPLICATION : bSession ? GlobalScope.SESSION : bRequest ? GlobalScope.REQUEST : GlobalScope.EXECUTION; Symbol newSym; if( scope == GlobalScope.EXECUTION ) { newSym = new Symbol( strIdentifier, type, _symTable, null ); } else { newSym = new ScopedDynamicSymbol( _symTable, strIdentifier, "", type, scope ); } newSym.setModifierInfo( varStmt.getModifierInfo() ); symbol = newSym; varStmt.setSymbol( newSym ); varStmt.setScope( scope ); } if( existingSymbol == null ) { _symTable.putSymbol( symbol ); } eas = possiblyWrapWithImplicitCoercion( eas, type ); varStmt.setAsExpression( eas ); varStmt.setTypeLiteral( typeLiteral ); varStmt.setScriptPart( getScriptPart() ); varStmt.setDefinitionParsed( true ); boolean bHideFieldForEditorParsing = getGosuClass() != null && getGosuClass().isCreateEditorParser() && bFieldSymbolFromProgram; if( bHideFieldForEditorParsing ) { pushStatement( new HideFieldNoOpStatement( varStmt ) ); } else { pushStatement( varStmt ); } } private boolean isLocalVarTopLevelFunctionBodyStmt() { if( _symTable.getScopeCount() > 1 ) { return _symTable.peekScope( 1 ).getActivationCtx() != null; } return false; } private void detectLikelyJavaCast(Expression eas) { if( eas instanceof IParenthesizedExpression || (eas instanceof IImplicitTypeAsExpression && ((IImplicitTypeAsExpression)eas).getLHS() instanceof IParenthesizedExpression) ) { IParenthesizedExpression parenExpr; if(eas instanceof IParenthesizedExpression) { parenExpr = (IParenthesizedExpression)eas; } else { parenExpr = (IParenthesizedExpression)((IImplicitTypeAsExpression)eas).getLHS(); } if (parenExpr.getExpression() instanceof TypeLiteral) { IType castType = eas.getType(); if(castType instanceof IMetaType) { castType = ((IMetaType)castType).getType(); } eas.addParseWarning( new ParseWarning( makeFullParserState(), Res.MSG_LIKELY_JAVA_CAST, castType.getName() ) ); } } } private boolean recoverFromJavaStyleCast( Expression eas ) { if( eas instanceof IParenthesizedExpression && ((IParenthesizedExpression)eas).getExpression() instanceof TypeLiteral ) { if( getTokenizer().getLineNumber() == eas.getLocation().getLineNum() ) { // Something follows a java-style-cast-looking expression on same line, so assume it's a java-style cast... IType castType = eas.getType(); if( castType instanceof IMetaType ){ castType = ((IMetaType) castType).getType(); } int mark = getTokenizer().mark(); parseUnaryExpression(); Expression maybeRealEas = popExpression(); if( maybeRealEas.hasParseExceptions() ) { _locations.remove( maybeRealEas.getLocation() ); getTokenizer().restoreToMark( mark ); } else { popExpression(); eas.addParseWarning( new ParseWarning( makeFullParserState(), Res.MSG_LIKELY_JAVA_CAST, castType.getName() ) ); eas = possiblyWrapWithImplicitCoercion( maybeRealEas, castType ); pushExpression( eas ); return true; } } } return false; } DynamicPropertySymbol parseVarPropertyClause( VarStatement varStmt, String strVarIdentifier, IType varType, boolean parseInitializer ) { if( !match( null, Keyword.KW_as ) ) { return null; } boolean bReadonly = match( null, Keyword.KW_readonly ) || varStmt.isFinal(); final int iNameStart = getTokenizer().getTokenStart(); Token T = new Token(); varStmt.setHasProperty( true ); String strPropertyName = null; if( verify( varStmt, match( T, SourceCodeTokenizer.TT_WORD ), Res.MSG_EXPECTING_NAME_PROPERTY ) ) { strPropertyName = T._strValue; warn( varStmt, !Keyword.isKeyword( strPropertyName ), Res.MSG_IMPROPER_USE_OF_KEYWORD, strPropertyName ); } String strIdentifier = strPropertyName == null ? "" : strPropertyName; IToken restoreState = getTokenizer().getPriorToken(); addNameInDeclaration( strIdentifier, T._iDocPosition, restoreState.getLine(), restoreState.getTokenColumn(), strIdentifier.length() > 0 ); varStmt.setPropertyName( strPropertyName ); varStmt.setNameOffset(iNameStart, strPropertyName ); ISymbol symbol = getSymbolTable().getSymbol( strPropertyName ); if( symbol != null && !symbol.getDisplayName().equals( strPropertyName ) ) { // Force case sensitivity, mainly to make overrides consistent symbol = null; } if( symbol instanceof DynamicPropertySymbol && (((DynamicPropertySymbol)symbol).getVarIdentifier() != null && !((DynamicPropertySymbol)symbol).getVarIdentifier().equals( strVarIdentifier) ) ) { varStmt.addParseException( new ParseException( makeFullParserState(), Res.MSG_PROPERTY_ALREADY_DEFINED, strPropertyName) ); } ICompilableType gsClass = getGosuClass(); DynamicPropertySymbol dps; if( symbol instanceof DynamicPropertySymbol ) { dps = new DynamicPropertySymbol( (DynamicPropertySymbol)symbol ); if( dps.getGetterDfs() == null || dps.getGetterDfs().getScriptPart().getContainingType() != gsClass ) { VarPropertyGetFunctionSymbol getFunctionSymbol = new VarPropertyGetFunctionSymbol( gsClass, getSymbolTable(), strPropertyName, strVarIdentifier, varType ); getFunctionSymbol.getModifierInfo().setAnnotations( varStmt.getAnnotations() ); getFunctionSymbol.getModifierInfo().setDescription( varStmt.getModifierInfo().getDescription() ); getFunctionSymbol.setClassMember( true ); if (dps.getGetterDfs() != null) { getFunctionSymbol.setOverride(true); getFunctionSymbol.setSuperDfs(dps.getGetterDfs()); } getFunctionSymbol.setStatic( varStmt.isStatic() ); dps.setGetterDfs( getFunctionSymbol ); verifyFunction( getFunctionSymbol, varStmt ); } } else { VarPropertyGetFunctionSymbol getFunctionSymbol = new VarPropertyGetFunctionSymbol( gsClass, getSymbolTable(), strPropertyName, strVarIdentifier, varType ); getFunctionSymbol.getModifierInfo().setAnnotations( varStmt.getAnnotations() ); getFunctionSymbol.getModifierInfo().setDescription( varStmt.getModifierInfo().getDescription() ); getFunctionSymbol.setClassMember( true ); getFunctionSymbol.setStatic( varStmt.isStatic() ); verifyFunction( getFunctionSymbol, varStmt ); dps = new DynamicPropertySymbol( getFunctionSymbol, true ); } if( !bReadonly && (dps.getSetterDfs() == null || dps.getSetterDfs().getScriptPart().getContainingType() != gsClass) ) { VarPropertySetFunctionSymbol setFunctionSymbol = new VarPropertySetFunctionSymbol( gsClass, getSymbolTable(), strPropertyName, strVarIdentifier, varType ); setFunctionSymbol.getModifierInfo().setAnnotations( varStmt.getAnnotations() ); setFunctionSymbol.getModifierInfo().setDescription( varStmt.getModifierInfo().getDescription() ); setFunctionSymbol.setClassMember( true ); setFunctionSymbol.setStatic( varStmt.isStatic() ); if (dps.getSetterDfs() != null) { setFunctionSymbol.setOverride(true); setFunctionSymbol.setSuperDfs(dps.getSetterDfs()); } dps.setSetterDfs( setFunctionSymbol ); verifyFunction( setFunctionSymbol, varStmt ); } dps.setScriptPart( getOwner().getScriptPart() ); dps.setVarIdentifier( strVarIdentifier ); if( parseInitializer && match( null, "=", SourceCodeTokenizer.TT_OPERATOR, false ) ) { pushParsingFieldInitializer( varStmt ); try { parseExpression( varType ); } finally { popParsingFieldInitializer(); } Expression expression = popExpression(); verifyComparable( varType, expression, true ); expression = possiblyWrapWithImplicitCoercion( expression, varType ); varStmt.setAsExpression( expression ); } return dps; } void parseDelegateStatement( DelegateStatement delegateStmt, String strIdentifier ) { if( delegateStmt.getModifierInfo() == null ) { delegateStmt.setModifierInfo( new ModifierInfo(0) ); } TypeLiteral typeLiteral = null; if( match( null, ":", SourceCodeTokenizer.TT_OPERATOR ) ) { parseTypeLiteral(); typeLiteral = (TypeLiteral)popExpression(); } ICompilableType gsClass = getGosuClass(); 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 ); Expression eas = null; if( match( null, "=", SourceCodeTokenizer.TT_OPERATOR ) ) { pushParsingFieldInitializer( delegateStmt ); try { parseExpression( typeLiteral == null ? null : typeLiteral.getType().getType() ); } finally { popParsingFieldInitializer(); } eas = popExpression(); if( eas.hasParseExceptions() ) { if( typeLiteral != null ) { IType typeCast = typeLiteral.getType().getType(); eas.getParseExceptions().get( 0 ).setExpectedType( typeCast ); } } if( eas instanceof IParenthesizedExpression && ((IParenthesizedExpression)eas).getExpression() instanceof TypeLiteral ) { IType castType = eas.getType(); if(castType instanceof IMetaType) { castType = ((IMetaType)castType).getType(); } eas.addParseWarning( new ParseWarning( makeFullParserState(), Res.MSG_LIKELY_JAVA_CAST, castType.getName() ) ); } } // Create a local symbol for the identifier part of the 'var' statement // (so it can be legally referenced in the containing scope). IType type = null; if( eas != null ) { type = eas.getType(); if( type == null ) { type = ErrorType.getInstance(); } } if( typeLiteral != null ) { IType typeCast = typeLiteral.getType().getType(); if( eas != null && type != null ) { verifyComparable( typeCast, eas, false, true ); } type = typeCast; } else if( !(type instanceof ErrorType) ) { if( constituents.isEmpty() ) { type = ErrorType.getInstance(); } else if( constituents.size() == 1 ) { type = constituents.get( 0 ); } else { type = CompoundType.get( new HashSet<IType>( constituents ) ); } } //if no type was found, we have added an error so give the symbol the error type if( type == null ) { type = ErrorType.getInstance(); } ISymbol symbol = delegateStmt.getSymbol(); if( symbol == null ) { symbol = new Symbol( strIdentifier, type, _symTable ); delegateStmt.setSymbol( symbol ); } else { symbol.setType( type ); } _symTable.putSymbol( symbol ); delegateStmt.setType( type ); eas = possiblyWrapWithImplicitCoercion( eas, type ); delegateStmt.setAsExpression( eas ); delegateStmt.setTypeLiteral( typeLiteral ); delegateStmt.setScriptPart( getScriptPart() ); pushStatement( delegateStmt ); } private void parseSwitchStatement() { SwitchStatement switchStmt = new SwitchStatement(); verify( switchStmt, match( null, '(' ), Res.MSG_EXPECTING_LEFTPAREN_SWITCH ); parseExpression(); verify( switchStmt, match( null, ')' ), Res.MSG_EXPECTING_RIGHTPAREN_SWITCH ); verify( switchStmt, match( null, '{' ), Res.MSG_EXPECTING_OPEN_BRACE_FOR_SWITCH ); Expression e = popExpression(); switchStmt.setSwitchExpression( e ); _iBreakOk++; try { parseCaseClauses( switchStmt ); parseDefaultClause( switchStmt, Arrays.asList( switchStmt.getCases() ) ); } finally { _iBreakOk--; } verify( switchStmt, match( null, '}' ), Res.MSG_EXPECTING_CLOSE_BRACE_FOR_SWITCH ); pushStatement( switchStmt ); } private void parseDoWhileStatement() { DoWhileStatement whileStmt = new DoWhileStatement(); _ctxInferenceMgr.pushLoopCompromised(); boolean loopStmtParsed; try { loopStmtParsed = parseLoopStatement(); } finally { _ctxInferenceMgr.popLoopCompromised(); } if( verify( whileStmt, loopStmtParsed, Res.MSG_EXPECTING_STATEMENT ) ) { verify( whileStmt, match( null, Keyword.KW_while ), Res.MSG_EXPECTING_WHILE_DO ); Statement stmt = popStatement(); verify( whileStmt, match( null, '(' ), Res.MSG_EXPECTING_LEFTPAREN_IF ); parseExpression( JavaTypes.pBOOLEAN() ); verify( whileStmt, match( null, ')' ), Res.MSG_EXPECTING_RIGHTPAREN_IF ); Expression e = popExpression(); whileStmt.setExpression( e ); whileStmt.setStatement( stmt ); } pushStatement( whileStmt ); } private void parseWhileStatement() { WhileStatement whileStmt = new WhileStatement(); _ctxInferenceMgr.pushLoopCompromised(); verify( whileStmt, match( null, '(' ), Res.MSG_EXPECTING_LEFTPAREN_WHILE ); parseExpression( JavaTypes.pBOOLEAN() ); verify( whileStmt, match( null, ')' ), Res.MSG_EXPECTING_RIGHTPAREN_WHILE ); Expression e = popExpression(); _ctxInferenceMgr.pushLastCtx(); try { whileStmt.setExpression( e ); if( verify( whileStmt, parseLoopStatement(), Res.MSG_EXPECTING_STATEMENT ) ) { Statement stmt = popStatement(); whileStmt.setStatement( stmt ); } pushStatement( whileStmt ); } finally { _ctxInferenceMgr.popCtx( false ); _ctxInferenceMgr.popLoopCompromised(); } } private void parseForEachStatement( Token t ) { ForEachStatement forEachStmt = new ForEachStatement( _symTable ); verify( forEachStmt, match( null, '(' ), Res.MSG_EXPECTING_LEFTPAREN_FE ); boolean bLoneInterval = false; Expression ein = null; if( !match( null, Keyword.KW_var ) ) { // Handle case where no loop var is provided e.g., for( 1..3 ) ... similar to ruby's 3.times( ... ) int state = _tokenizer.mark(); int iLocationsCount = _locations.size(); parseExpression(); ein = popExpression(); bLoneInterval = JavaTypes.NUMBER_INTERVAL().isAssignableFrom( ein.getType() ); if( !bLoneInterval ) { _tokenizer.restoreToMark( state ); removeLocationsFrom( iLocationsCount ); } } int iOffset = getTokenizer().getTokenStart(); int iLineNum = getTokenizer().getLineNumber(); int iColumn = getTokenizer().getTokenColumn(); if( !bLoneInterval && verify( forEachStmt, match( t, SourceCodeTokenizer.TT_WORD ), Res.MSG_EXPECTING_IDENTIFIER_FOREACH ) ) { forEachStmt.setNameOffset( iOffset, t._strValue ); warn( forEachStmt, !Keyword.isKeyword( t._strValue ), Res.MSG_IMPROPER_USE_OF_KEYWORD, t._strValue ); } else { t._strValue = null; } _symTable.pushScope(); try { String strIdentifier = t._strValue == null ? "" : t._strValue; // var decl *expression* is only for editors LocalVarDeclaration varDecl = null; if( strIdentifier.length() > 0 ) { varDecl = new LocalVarDeclaration( strIdentifier ); verify( forEachStmt, _symTable.getSymbol( strIdentifier ) == null, Res.MSG_VARIABLE_ALREADY_DEFINED, strIdentifier ); varDecl.setType( ErrorType.getInstance() ); // this is updated below pushExpression( varDecl ); setLocation( iOffset, iLineNum, iColumn, strIdentifier == null, true ); popExpression(); } if( !bLoneInterval ) { verify( forEachStmt, match( null, Keyword.KW_in ), Res.MSG_EXPECTING_IN_FOREACH ); parseExpression(); ein = popExpression(); } IType typeIn = ein.getType(); verify( ein, LoopStatement.isIteratorType( typeIn ) || typeIn instanceof ErrorType, Res.MSG_EXPECTING_ARRAYTYPE_FOREACH, typeIn.getName() ); forEachStmt.setInExpression( ein ); forEachStmt.setStructuralIterable( StandardCoercionManager.isStructurallyAssignable_Laxed( JavaTypes.ITERABLE(), typeIn ) ); if( strIdentifier != null ) { // Create a temporary symbol for the identifier part of the foreach statement // (so it can be legally referenced in the statement). IType typeIdentifier = LoopStatement.getArrayComponentType( typeIn ); if( strIdentifier.length() > 0 ) { varDecl.setType( typeIdentifier ); } Symbol symbol = new Symbol( bLoneInterval ? ("_unused_loop_var_" + iOffset) : strIdentifier, typeIdentifier, _symTable, null ); _symTable.putSymbol( symbol ); forEachStmt.setIdentifier( symbol ); } boolean foundIterator = false; if( match( null, Keyword.KW_iterator ) ) { foundIterator = true; parseIteratorVar( forEachStmt, typeIn ); } if( match( null, Keyword.KW_index ) ) { parseIndexVar( forEachStmt ); } if( !foundIterator && match( null, Keyword.KW_iterator ) ) { foundIterator = true; parseIteratorVar( forEachStmt, typeIn ); } if( bLoneInterval && foundIterator ) { addError( forEachStmt, Res.MSG_FOREACH_ITERATOR_NOT_ALLOWED ); } verify( forEachStmt, match( null, ')' ), Res.MSG_EXPECTING_RIGHTPAREN_FE ); if( strIdentifier != null ) { _ctxInferenceMgr.pushLoopCompromised(); boolean bHasBody; try { bHasBody = parseLoopStatement(); } finally { _ctxInferenceMgr.popLoopCompromised(); } verify( forEachStmt, bHasBody, Res.MSG_EXPECTING_STATEMENT ); if( bHasBody ) { Statement stmt = popStatement(); forEachStmt.setStatement( stmt ); } } pushStatement( forEachStmt ); } finally { _symTable.popScope(); } } private void parseIndexVar( ForEachStatement forEachStmt ) { int iOffset; int iLineNum; int iColumn; iOffset = getTokenizer().getTokenStart(); iLineNum = getTokenizer().getLineNumber(); iColumn = getTokenizer().getTokenColumn(); Token Tindex = new Token(); if( verify( forEachStmt, match( Tindex, SourceCodeTokenizer.TT_WORD ), Res.MSG_EXPECTING_IDENTIFIER_FOREACH_INDEX ) ) { warn( forEachStmt, !Keyword.isKeyword( Tindex._strValue ), Res.MSG_IMPROPER_USE_OF_KEYWORD, Tindex._strValue ); } String strIndexIdentifier = Tindex._strValue; // index decl *expression* is only for editors LocalVarDeclaration varIndexDecl = new LocalVarDeclaration( strIndexIdentifier == null ? "#err" : strIndexIdentifier ); varIndexDecl.setType(JavaTypes.pINT()); verify( forEachStmt, _symTable.getSymbol( strIndexIdentifier ) == null, Res.MSG_VARIABLE_ALREADY_DEFINED, strIndexIdentifier ); forEachStmt.setIndexNameOffset( Tindex.getTokenStart() ); // Create a temporary symbol for the identifier part of the foreach statement's index // (so it can be legally referenced in the statement). Symbol indexIdentifier = new TypedSymbol( strIndexIdentifier, JavaTypes.pINT(), _symTable, null, SymbolType.FOREACH_VARIABLE ); indexIdentifier.setFinal( true ); _symTable.putSymbol( indexIdentifier ); forEachStmt.setIndexIdentifier( indexIdentifier ); pushExpression( varIndexDecl ); setLocation( iOffset, iLineNum, iColumn, strIndexIdentifier == null, false ); popExpression(); } private void parseIteratorVar( ForEachStatement forEachStmt, IType type ) { int iOffset; int iLineNum; int iColumn; iOffset = getTokenizer().getTokenStart(); iLineNum = getTokenizer().getLineNumber(); iColumn = getTokenizer().getTokenColumn(); Token Titer = new Token(); if( verify( forEachStmt, match( Titer, SourceCodeTokenizer.TT_WORD ), Res.MSG_EXPECTING_IDENTIFIER_FOREACH_ITERATOR ) ) { warn( forEachStmt, !Keyword.isKeyword( Titer._strValue ), Res.MSG_IMPROPER_USE_OF_KEYWORD, Titer._strValue ); } String strIterIdentifier = Titer._strValue; boolean bIterable = JavaTypes.ITERABLE().isAssignableFrom( type ) || forEachStmt.isStructuralIterable(); verify( forEachStmt, bIterable, Res.MSG_ITERATOR_SYMBOL_ONLY_SUPPORTED_ON_ITERABLE_OBJECTS ); IType iterType; if( bIterable ) { IType iterParam = LoopStatement.getArrayComponentType( type ); iterType = JavaTypes.ITERATOR().getParameterizedType( iterParam ); } else { iterType = ErrorType.getInstance(); } // index decl *expression* is only for editors LocalVarDeclaration varIteratorDecl = new LocalVarDeclaration( strIterIdentifier == null ? "#err" : strIterIdentifier ); varIteratorDecl.setType( iterType ); verify( forEachStmt, _symTable.getSymbol( strIterIdentifier ) == null, Res.MSG_VARIABLE_ALREADY_DEFINED, strIterIdentifier ); forEachStmt.setIndexNameOffset( Titer.getTokenStart() ); Symbol iteratorIdentifier = new TypedSymbol( strIterIdentifier, iterType, _symTable, null, SymbolType.FOREACH_VARIABLE ); iteratorIdentifier.setFinal( true ); _symTable.putSymbol( iteratorIdentifier ); forEachStmt.setIteratorIdentifier( iteratorIdentifier ); pushExpression(varIteratorDecl); setLocation(iOffset, iLineNum, iColumn, strIterIdentifier == null, false); popExpression(); } private void parseReturnStatement() { ReturnStatement returnStmt = new ReturnStatement(); IType returnType = null; if( isParsingBlock() ) { returnType = _blockReturnTypeStack.peek(); } else if( isParsingFunction() ) { if( _bProgramCallFunction && getGosuClass() instanceof IGosuProgram ) { returnType = ((IGosuProgram)getGosuClass()).getExpectedReturnType(); } if( returnType == null ) { returnType = peekParsingFunction().getReturnType(); } } else if ( isParsingProgram() ) { returnType = peekParsingProgram().getDeclaredReturnType(); } verify( returnStmt, _iReturnOk > 0, Res.MSG_RETURN_NOT_ALLOWED_HERRE ); if( match( null, ';' ) || match( null, null, '}', true ) ) { boolean bShouldNotHaveReturnValue = _bProgramCallFunction || returnType == null || returnType == GosuParserTypes.NULL_TYPE(); FunctionType functionType = isParsingFunction() ? peekParsingFunction() : null; boolean bConstructor = !bShouldNotHaveReturnValue && functionType != null && functionType.getMethodOrConstructorInfo() instanceof IConstructorInfo; verify( returnStmt, bShouldNotHaveReturnValue || bConstructor, Res.MSG_MISSING_RETURN_VALUE ); setReturnNullExpr( returnStmt, _bProgramCallFunction ); } else if( returnType != GosuParserTypes.NULL_TYPE() && !inConstructorCtx() ) { parseExpression( returnType ); Expression retValue = popExpression(); if( returnType != null && !isParsingBlock() ) { Expression actualReturnExpr = retValue; if( retValue instanceof ImplicitTypeAsExpression ) { actualReturnExpr = ((ImplicitTypeAsExpression)retValue).getLHS(); } boolean bVoidReturnType = actualReturnExpr.getType() == GosuParserTypes.NULL_TYPE() && !(actualReturnExpr instanceof NullExpression); if( bVoidReturnType ) { //noinspection ThrowableResultOfMethodCallIgnored actualReturnExpr.removeParseWarning( Res.MSG_USING_VOID_RETURN_TYPE_FROM_NON_NULL_EXPR ); verify( actualReturnExpr, !bVoidReturnType, Res.MSG_USING_VOID_RETURN_TYPE_FROM_NON_NULL_EXPR ); } } returnStmt.setValue( retValue ); if( (returnType == null || _bProgramCallFunction) && retValue.getType() == JavaTypes.pVOID() ) { retValue.setType( JavaTypes.OBJECT() ); } } else { setReturnNullExpr( returnStmt, _bProgramCallFunction ); } pushStatement( returnStmt ); } private boolean inConstructorCtx() { if( isParsingConstructor() ) { return _blocks == null || _blocks.isEmpty(); } return false; } private void setReturnNullExpr( ReturnStatement returnStmt, boolean bProgramCallFunction ) { if( bProgramCallFunction ) { NullExpression nullExpr = new NullExpression(); nullExpr.setType( JavaTypes.OBJECT() ); returnStmt.setValue( nullExpr ); } else { returnStmt.setValue( NullExpression.instance() ); } } private void parseThrowStatement() { parseExpression(); Expression e = popExpression(); if( !JavaTypes.THROWABLE().isAssignableFrom( e.getType() ) ) { verifyComparable( JavaTypes.STRING(), e, true ); e = possiblyWrapWithImplicitCoercion( e, JavaTypes.STRING() ); } ThrowStatement throwStmt = new ThrowStatement(); throwStmt.setExpression( e ); pushStatement( throwStmt ); } private void parseTryCatchFinallyStatement( Token t ) { TryCatchFinallyStatement tryCatchFinallyStmt = new TryCatchFinallyStatement(); if( verify( tryCatchFinallyStmt, match( null, null, '{', true ), Res.MSG_EXPECTING_LEFTBRACE_STMTBLOCK ) ) { parseStatement(); Statement tryStmt = popStatement(); tryCatchFinallyStmt.setTryStatement( tryStmt ); } for( int iCatchOffset = _tokenizer.getTokenStart(), iCatchLineNum = _tokenizer.getLineNumber(), iCatchColumn = getTokenizer().getTokenColumn(); match( null, Keyword.KW_catch ); iCatchOffset = _tokenizer.getTokenStart(), iCatchLineNum = _tokenizer.getLineNumber(), iCatchColumn = getTokenizer().getTokenColumn()) { CatchClause catchClause = new CatchClause(); _symTable.pushScope(); try { verify( tryCatchFinallyStmt, match( null, '(' ), Res.MSG_EXPECTING_LEFTPAREN_CATCH ); match( null, Keyword.KW_var ); int iOffset = _tokenizer.getTokenStart(); int iLineNum = _tokenizer.getLineNumber(); int iColumn = getTokenizer().getTokenColumn(); if( verify( tryCatchFinallyStmt, match( t, SourceCodeTokenizer.TT_WORD ), Res.MSG_EXPECTING_IDENTIFIER_CATCH ) ) { catchClause.setNameOffset( iOffset, t._strValue ); } String strIdentifier = t._strValue; LocalVarDeclaration localVarDecl = new LocalVarDeclaration( strIdentifier == null ? "#err" : strIdentifier ); IType iIntrinsicType; if( match( null, ":", SourceCodeTokenizer.TT_OPERATOR ) ) { parseTypeLiteral(); TypeLiteral typeLiteral = (TypeLiteral)popExpression(); iIntrinsicType = typeLiteral.getType().getType(); verify( typeLiteral, (JavaTypes.THROWABLE().isAssignableFrom( iIntrinsicType ) && !(iIntrinsicType instanceof ITypeVariableType)) || iIntrinsicType instanceof IErrorType, Res.MSG_NOT_A_VALID_EXCEPTION_TYPE, iIntrinsicType.getName() ); } else { if( !CommonServices.getEntityAccess().getLanguageLevel().supportsNakedCatchStatements() ) { warn( localVarDecl, false, Res.MSG_EXPLICIT_TYPE_RECOMMENDED_FOR_CATCH_STMTS ); } iIntrinsicType = null; } pushExpression( localVarDecl ); localVarDecl.setType( iIntrinsicType == null ? ErrorType.getInstance() : iIntrinsicType ); setLocation( iOffset, iLineNum, iColumn, strIdentifier == null, false ); popExpression(); verify( tryCatchFinallyStmt, match( null, ')' ), Res.MSG_EXPECTING_RIGHTPAREN_CATCH ); verify( tryCatchFinallyStmt, _symTable.getSymbol( strIdentifier ) == null, Res.MSG_VARIABLE_ALREADY_DEFINED, strIdentifier ); Symbol symbol = new TypedSymbol( strIdentifier, iIntrinsicType != null ? iIntrinsicType : JavaTypes.THROWABLE(), _symTable, null, SymbolType.CATCH_VARIABLE); _symTable.putSymbol( symbol ); if( tryCatchFinallyStmt.getCatchStatements() != null ) { for( CatchClause c : tryCatchFinallyStmt.getCatchStatements() ) { IType earlierCatchType = c.getCatchType() != null ? c.getCatchType() : CatchClause.getNakedCatchExceptionType(); IType currentCatchType = iIntrinsicType != null ? iIntrinsicType : CatchClause.getNakedCatchExceptionType(); if( earlierCatchType.isAssignableFrom( currentCatchType ) ) { verify( catchClause, false, Res.MSG_CATCH_STMT_CANNOT_EXECUTE ); } } } if( verify( tryCatchFinallyStmt, match( null, null, '{', true ), Res.MSG_EXPECTING_LEFTBRACE_STMTBLOCK ) ) { parseStatement(); Statement catchStmt = popStatement(); catchClause.init( iIntrinsicType, catchStmt, symbol ); pushStatement( catchClause ); setLocation( iCatchOffset, iCatchLineNum, iCatchColumn ); popStatement(); tryCatchFinallyStmt.addCatchClause( catchClause ); } } finally { _symTable.popScope(); } } if( match( null, Keyword.KW_finally ) ) { int originaliBreakOk = _iBreakOk; _iBreakOk = 0; int originaliContinueOk = _iContinueOk; _iContinueOk = 0; int originalReturnOk = _iReturnOk; _iReturnOk = 0; try { if( verify( tryCatchFinallyStmt, match( null, null, '{', true ), Res.MSG_EXPECTING_LEFTBRACE_STMTBLOCK ) ) { parseStatement(); Statement finallyStmt = popStatement(); tryCatchFinallyStmt.setFinallyStatement( finallyStmt ); } } finally { _iBreakOk = originaliBreakOk; _iContinueOk = originaliContinueOk; _iReturnOk = originalReturnOk; } } if( tryCatchFinallyStmt.getCatchStatements() == null && tryCatchFinallyStmt.getFinallyStatement() == null ) { tryCatchFinallyStmt.addParseException( new ParseException( makeFullParserState(), Res.MSG_CATCH_OR_FINALLY_REQUIRED ) ); } pushStatement( tryCatchFinallyStmt ); } private void parseIfStatement() { IfStatement ifStmt = new IfStatement(); verify( ifStmt, match( null, '(' ), Res.MSG_EXPECTING_LEFTPAREN_IF ); parseExpression( JavaTypes.pBOOLEAN() ); Expression e = popExpression(); // Bad assignment statement in if clause (mistaken for equality "==") if( match( null, "=", SourceCodeTokenizer.TT_OPERATOR ) ) { parseExpression(); popExpression(); verify( ifStmt, false, Res.MSG_ASSIGNMENT_IN_IF_STATEMENT); } verify( ifStmt, match( null, ')' ), Res.MSG_EXPECTING_RIGHTPAREN_IF ); getSymbolTable().pushScope(); boolean statementParsed = false; try { _ctxInferenceMgr.pushLastCtx(); statementParsed = parseStatement(); } finally { getSymbolTable().popScope(); _ctxInferenceMgr.popCtx( false ); } verify( ifStmt, statementParsed, Res.MSG_EXPECTING_STATEMENT ); if( statementParsed ) { Statement stmt = popStatement(); ifStmt.setExpression( e ); ifStmt.setStatement( stmt ); // Swallow a semicolon if necessary match( null, ';' ); if( match( null, Keyword.KW_else ) ) { getSymbolTable().pushScope(); boolean elseStmtParsed; try { elseStmtParsed = parseStatement(); } finally { getSymbolTable().popScope(); } verify( ifStmt, elseStmtParsed, Res.MSG_EXPECTING_STATEMENT ); if( elseStmtParsed ) { ifStmt.setElseStatement( popStatement() ); } } //## todo: in Diamond remove usage of 'unless' clause in if-statement if( match( null, Keyword.KW_except ) || match( null, Keyword.KW_unless ) ) { verify( ifStmt, match( null, '(' ), Res.MSG_EXPECTING_LEFTPAREN_EXCEPT ); parseExpression(); verify( ifStmt, match( null, ')' ), Res.MSG_EXPECTING_RIGHTPAREN_EXCEPT ); Expression ee = popExpression(); ifStmt.setExcept( ee ); } } pushStatement( ifStmt ); } private void parseUsingStatement() { UsingStatement usingStmt = new UsingStatement(); verify( usingStmt, match( null, '(' ), Res.MSG_EXPECTING_LEFTPAREN_USING ); _symTable.pushScope(); try { parseVarStatementsInUsingStatement( usingStmt ); if( usingStmt.getVarStatements().isEmpty() ) { parseExpression(); Expression expr = popExpression(); usingStmt.setExpression( expr ); verifyTypeForUsingStatementPredicate( expr, expr.getType() ); } verify( usingStmt, match( null, ')' ), Res.MSG_EXPECTING_RIGHTPAREN_USING ); if( verify( usingStmt, match( null, null, '{', true ), Res.MSG_EXPECTING_LEFTBRACE_STMTBLOCK ) ) { parseStatement(); usingStmt.setStatement( popStatement() ); } if( match( null, Keyword.KW_finally )) { if( verify( usingStmt, match( null, null, '{', true ), Res.MSG_EXPECTING_LEFTBRACE_STMTBLOCK ) ) { parseStatement(); Statement finallyStmt = popStatement(); usingStmt.setFinallyStatement( finallyStmt ); } } pushStatement( usingStmt ); } finally { _symTable.popScope(); } } private void parseVarStatementsInUsingStatement( UsingStatement usingStmt ) { Token T = new Token(); List<IVarStatement> varStmts = new ArrayList<IVarStatement>(); int iOffset; int iLineNum; int iColumn; do { iOffset = getTokenizer().getTokenStart(); iLineNum = getTokenizer().getLineNumber(); iColumn = getTokenizer().getTokenColumn(); if( match( null, Keyword.KW_var ) ) { VarStatement varStmt = new VarStatement(); int iNameOffset = getTokenizer().getTokenStart(); if( verify( varStmt, match( T, SourceCodeTokenizer.TT_WORD ), Res.MSG_EXPECTING_IDENTIFIER_VAR ) ) { varStmt.setNameOffset( iNameOffset, null ); } else { T._strValue = null; } parseVarStatement( varStmt, T, false ); setLocation( iOffset, iLineNum, iColumn ); varStmt = (VarStatement)popStatement(); verifyTypeForUsingStatementPredicate( varStmt, varStmt.getType() ); varStmts.add( varStmt ); } else { break; } }while( match( null, ',' ) ); if( varStmts.size() > 0 ) { usingStmt.setVarStatements( varStmts ); // Add a synthetic noop-stmt to avoid "multiple stmts on same line" warning wrt // the using stmt's var stmts and its body e.g., if the stmt body stmt list puts // an opening brace on same line as vars pushStatement( new NoOpStatement() ); setLocation( getTokenizer().getTokenStart(), iLineNum, getTokenizer().getTokenColumn(), true ); popStatement(); } } private void verifyTypeForUsingStatementPredicate( ParsedElement pe, IType type ) { maybeRemoveIMonitorLockError( pe ); boolean bAssignableFromUsingType = isAssignableFrom(JavaTypes.LOCK(), type) || isAssignableFrom(JavaTypes.getJreType(Closeable.class), type ) || isAssignableFrom(JavaTypes.getGosuType( IReentrant.class ), type ) || isAssignableFrom(GosuTypes.IDISPOSABLE(), type ) || type == GosuTypes.IMONITORLOCK() || type.getTypeInfo().getMethod( "dispose" ) != null || type.getTypeInfo().getMethod( "close" ) != null || (type.getTypeInfo().getMethod( "lock" ) != null && type.getTypeInfo().getMethod( "unlock" ) != null); verify( pe, bAssignableFromUsingType, Res.MSG_BAD_TYPE_FOR_USING_STMT ); } private boolean isAssignableFrom(IType type1, IType type2) { return type1 != null && type1.isAssignableFrom( type2 ); } private void maybeRemoveIMonitorLockError( ParsedElement pe ) { if( pe instanceof TypeAsExpression ) { ParseTree after = pe.getLocation().getChildAfter(((TypeAsExpression) pe).getLHS().getLocation()); if( after.getParsedElement() instanceof TypeLiteral ) { //noinspection ThrowableResultOfMethodCallIgnored after.getParsedElement().removeParseException(Res.MSG_IMONITOR_LOCK_SHOULD_ONLY_BE_USED_WITHIN_USING_STMTS); } } else if( pe instanceof VarStatement ) { maybeRemoveIMonitorLockError( ((VarStatement)pe).getAsExpression() ); } else if( pe instanceof ParenthesizedExpression ) { maybeRemoveIMonitorLockError( ((ParenthesizedExpression)pe).getExpression() ); } } private void parseStatementBlock() { parseStatementBlock( true ); } private void parseStatementBlock( boolean bMatchClosingBrace ) { _symTable.pushScope(); if( !bMatchClosingBrace ) { decStatementDepth(); } try { ArrayList<Statement> statements = new ArrayList<Statement>(); parseStatementsAndDetectUnreachable( statements ); StatementList stmtList = new StatementList( _symTable ); verify( stmtList, !bMatchClosingBrace || match( null, '}' ), Res.MSG_EXPECTING_RIGHTBRACE_STMTBLOCK ); stmtList.setStatements( statements ); pushStatement( isDontOptimizeStatementLists() ? stmtList : stmtList.getSelfOrSingleStatement() ); } finally { if( !bMatchClosingBrace ) { incStatementDepth(); } _symTable.popScope(); } } void parseNamespaceStatement( Token t ) { if( isEditorParser() ) { parseNamespaceStatement_editor( t ); } else { parseNamespaceStatement_normal(t); } } void parseNamespaceStatement_editor( Token t ) { NamespaceStatement namespaceStmt = new NamespaceStatement(); parseExpression(); Expression namespace = popExpression(); //noinspection ThrowableResultOfMethodCallIgnored namespace.clearParseExceptions(); //Res.MSG_EXPECTING_TYPE_TO_FOLLOW_PACKAGE_NAME ); IGosuClassInternal gsClass = (IGosuClassInternal)getScriptPart().getContainingType(); String strNamespace = namespace.toString(); verify( namespaceStmt, strNamespace.equals( gsClass.getNamespace() ), Res.MSG_WRONG_NAMESPACE, strNamespace, gsClass.getNamespace() ); setNamespace( strNamespace ); namespaceStmt.setNamespace( strNamespace ); pushStatement( namespaceStmt ); while( match( null, ';' ) ) { //pushStatement( new NoOpStatement() ); } } void parseNamespaceStatement_normal( Token t ) { NamespaceStatement namespaceStmt = new NamespaceStatement(); verify( namespaceStmt, match( t, SourceCodeTokenizer.TT_WORD ), Res.MSG_EXPECTING_TYPELITERAL_OR_NAMESPACE ); parseDotPathWord( t ); String strNamespace = t._strValue; IGosuClassInternal gsClass = (IGosuClassInternal)getScriptPart().getContainingType(); verify( namespaceStmt, strNamespace.equals( gsClass.getNamespace() ), Res.MSG_WRONG_NAMESPACE, strNamespace, gsClass.getNamespace() ); setNamespace( strNamespace ); namespaceStmt.setNamespace( strNamespace ); pushStatement( namespaceStmt ); while( match( null, ';' ) ) { //pushStatement( new NoOpStatement() ); } } @Override public IUsesStatementList parseUsesStatementList( boolean resolveTypes ) { return parseUsesStatementList( resolveTypes, new Token() ); } public UsesStatementList parseUsesStatementList( boolean bResolveUsesTypes, Token t ) { int iOffset = getTokenizer().getTokenStart(); int iLineNum = getTokenizer().getLineNumber(); int iColumn = getTokenizer().getTokenColumn(); int iUsesListOffset = iOffset; int iUsesListLineNum = iLineNum; if( match( null, Keyword.KW_uses, true ) ) { List<IUsesStatement> usesList = new ArrayList<IUsesStatement>(); UsesStatementList stmtList = new UsesStatementList(); stmtList.setUsesStatements( usesList ); while( match( null, Keyword.KW_uses ) ) { getOwner().parseUsesStatement( t, bResolveUsesTypes ); setLocation( iOffset, iLineNum, iColumn ); UsesStatement stmt = (UsesStatement)popStatement(); //noinspection ThrowableResultOfMethodCallIgnored stmt.removeParseWarningRecursively( Res.MSG_DEPRECATED_MEMBER ); // don't show these in uses statements IUsesStatement duplicate = stmtList.conflicts( stmt ); if( duplicate != null ) { if( warn( stmt, !duplicate.getTypeName().equals( stmt.getTypeName() ), Res.MSG_USES_STMT_DUPLICATE ) ) { verify( stmt, false, Res.MSG_USES_STMT_CONFLICT, duplicate.getTypeName() ); } } usesList.add( stmt ); getOwner().checkInstruction( true ); iOffset = getTokenizer().getTokenStart(); iLineNum = getTokenizer().getLineNumber(); } pushStatement( stmtList ); setLocation( iUsesListOffset, iUsesListLineNum, iColumn, true ); popStatement(); return stmtList; } return null; } void parseUsesStatement( Token t ) { parseUsesStatement(t, true); } void parseUsesStatement( Token t, boolean bResolveTypes ) { if( isEditorParser() ) { parseUsesStatement_editor(t, bResolveTypes); } else { parseUsesStatement_normal(t, bResolveTypes); } } void parseUsesStatement_editor( Token t, boolean bResolveTypes ) { UsesStatement usesStmt = new UsesStatement(); parseTypeLiteral(); TypeLiteral typeLiteral = (TypeLiteral)popExpression(); t._strValue = typeLiteral.getType().getType() instanceof ErrorType && typeLiteral.getPackageExpression() != null ? typeLiteral.getPackageExpression().toString() : TypeLord.getPureGenericType( typeLiteral.getType().getType() ).getName(); boolean bForwardRefToInnerClass = getGosuClass() instanceof IGosuClassInternal && t._strValue != null && t._strValue.startsWith( getGosuClass().getName() ); verify( usesStmt, t._strValue == null || !t._strValue.endsWith( "]" ), Res.MSG_BAD_NAMESPACE, t._strValue ); if( !bForwardRefToInnerClass || ((IGosuClassInternal)getGosuClass()).isHeaderCompiled() ) { if( t._strValue.endsWith( "*" ) && match( null, "*", SourceCodeTokenizer.TT_OPERATOR ) ) { typeLiteral.clearParseExceptions(); usesStmt.setTypeName( t._strValue ); if( verify( usesStmt, t._strValue.endsWith( ".*" ), Res.MSG_BAD_NAMESPACE, t._strValue ) ) { String namespace = t._strValue.substring( 0, t._strValue.length() - 2 ); IType type = TypeSystem.getNamespace( namespace ); if( type == null ) { type = TypeSystem.getByFullNameIfValid( namespace ); } verify( usesStmt, type != null, Res.MSG_BAD_NAMESPACE, namespace ); } getTypeUsesMap().addToTypeUses( usesStmt ); } else { if( bResolveTypes ) { String strTypeName = TypeLord.getPureGenericType( typeLiteral.getType().getType() ).getName(); usesStmt.setTypeName( strTypeName ); if( typeLiteral.hasParseExceptions() ) { IParseIssue first = typeLiteral.getParseExceptions().get( 0 ); usesStmt.addParseException( first ); //noinspection ThrowableResultOfMethodCallIgnored typeLiteral.removeParseException( first.getMessageKey() ); } else { getTypeUsesMap().addToTypeUses( usesStmt ); } } else { usesStmt.setTypeName( t._strValue ); getTypeUsesMap().addToTypeUses( usesStmt ); } } } pushStatement( usesStmt ); while( match( null, ';' ) ) { //pushStatement( new NoOpStatement() ); } } void parseUsesStatement_normal( Token t, boolean bResolveTypes ) { UsesStatement usesStmt = new UsesStatement(); int iOffset = _tokenizer.getTokenStart(); int iLineNum = _tokenizer.getLineNumber(); int iColumn = getTokenizer().getTokenColumn(); verify( usesStmt, match( t, SourceCodeTokenizer.TT_WORD ), Res.MSG_EXPECTING_TYPELITERAL_OR_NAMESPACE ); parseDotPathWord( t ); boolean bForwardRefToInnerClass = getGosuClass() instanceof IGosuClassInternal && t._strValue != null && t._strValue.startsWith( getGosuClass().getName() ); if( !bForwardRefToInnerClass || ((IGosuClassInternal)getGosuClass()).isHeaderCompiled() ) { if( match( null, "*", SourceCodeTokenizer.TT_OPERATOR ) ) { usesStmt.setTypeName( t._strValue + "*" ); if( verify( usesStmt, t._strValue.endsWith( "." ), Res.MSG_BAD_NAMESPACE, t._strValue ) ) { String namespace = t._strValue.substring( 0, t._strValue.length() - 1 ); IType type = TypeSystem.getNamespace( namespace ); if( type == null ) { type = TypeSystem.getByFullNameIfValid( namespace ); } verify( usesStmt, type != null, Res.MSG_BAD_NAMESPACE, namespace ); } getTypeUsesMap().addToTypeUses( usesStmt ); } else { if( bResolveTypes ) { TypeLiteral typeLiteral = resolveTypeLiteral( t, false, false ); pushExpression( typeLiteral ); setLocation( iOffset, iLineNum, iColumn ); popExpression(); IType type = TypeLord.getPureGenericType( (typeLiteral.getType()).getType() ); String strTypeName = type.getName(); usesStmt.setTypeName( strTypeName ); if( typeLiteral.hasParseExceptions() ) { IParseIssue first = typeLiteral.getParseExceptions().get( 0 ); usesStmt.addParseException( first ); //noinspection ThrowableResultOfMethodCallIgnored typeLiteral.removeParseException(first.getMessageKey()); } else { getTypeUsesMap().addToTypeUses( usesStmt ); } } else { usesStmt.setTypeName( t._strValue ); getTypeUsesMap().addToTypeUses( usesStmt ); } } } pushStatement( usesStmt ); while( match( null, ';' ) ) { //pushStatement( new NoOpStatement() ); } } void parseCaseClauses( SwitchStatement switchStmt ) { List<CaseClause> cases = new ArrayList<CaseClause>(); while( parseCaseClause( switchStmt, cases ) ) { /* do nothing */ } switchStmt.setCases( cases.toArray( new CaseClause[cases.size()] ) ); } boolean parseCaseClause( SwitchStatement switchStmt, List<CaseClause> cases ) { int iOffset = getTokenizer().getTokenStart(); int iLine = getTokenizer().getLineNumber(); int iColumn = getTokenizer().getTokenColumn(); if( !match( null, Keyword.KW_case ) ) { return false; } verify( switchStmt, switchStmt.getDefaultStatements() == null, Res.MSG_CASE_CLAUSE_MAY_NOT_FOLLOW_DEFAULT_CLAUSE ); if( !cases.isEmpty() ) { warnIfCaseNotTerminated( cases.get( cases.size()-1 ).getStatements() ); } Expression switchExpr = switchStmt.getSwitchExpression(); parseExpression( switchExpr.getType() ); verify( switchStmt, match( null, ":", SourceCodeTokenizer.TT_OPERATOR ), Res.MSG_EXPECTING_CASE_COLON ); Expression e = popExpression(); verifyCaseIsUnique( e, cases ); boolean typeInfered = switchExpr instanceof TypeOfExpression && e instanceof TypeLiteral && isIsolatedCase( cases ); List<Statement> statements = new ArrayList<Statement>(); CaseClause caseClause = new CaseClause( e, statements ); cases.add( caseClause ); if( typeInfered ) { TypeOfExpression toe = (TypeOfExpression)switchExpr; _ctxInferenceMgr.pushCtx(); _ctxInferenceMgr.updateType( toe.getExpression(), (IType)e.evaluate() ); } _symTable.pushScope(); boolean typeInferenceCancelled = false; try { for( Statement stmt = null; true; ) { // must cancel inference before the next case or default clause is parsed if( match( null, Keyword.KW_case, true ) || match( null, Keyword.KW_default, true ) ) { if( typeInfered ) { _ctxInferenceMgr.popCtx( false ); typeInferenceCancelled = true; } break; } if( !parseStatement() ) { break; } stmt = popStatementAndDetectUnreachable( stmt, statements ); } } finally { _symTable.popScope(); if( typeInfered && !typeInferenceCancelled ) { _ctxInferenceMgr.popCtx( false ); } } pushExpression( caseClause ); setLocation( iOffset, iLine, iColumn, true ); popExpression(); return true; } private void verifyCaseIsUnique( Expression e, List<CaseClause> cases ) { if( e.getType() instanceof IErrorType || !e.isCompileTimeConstant() ) { return; // Can't verify this } Object value; try { value = e.evaluate(); if( value == null && e instanceof TypeAsExpression ) { // sometimes a coercer isn't available in a compile-time environment, so bail if null return; } } catch( Exception err ) { return; } for( CaseClause cc: cases ) { Expression expr = cc.getExpression(); if( expr != null && expr.isCompileTimeConstant() ) { Object csr; try { csr = expr.evaluate(); } catch( Exception err ) { // skip over evaluation errors continue; } verify( e, !GosuObjectUtil.equals( csr, value ), Res.MSG_DUPLICATE_CASE_EXPRESSION ); } } } private boolean isIsolatedCase( List<CaseClause> cases ) { if( cases.isEmpty() ) { return true; } else { CaseClause caseClause = cases.get( cases.size() - 1 ); List<Statement> statements = caseClause.getStatements(); int i; for( i = statements.size()-1; i >= 0 && statements.get( i ) instanceof NoOpStatement; i-- ) { /* Loop until we find the index of the last non-noop statement */ } if( i >= 0 ) { boolean[] bAbsolute = {false}; return statements.get( i ).getLeastSignificantTerminalStatement( bAbsolute ) != null && bAbsolute[0]; } } return false; } private void warnIfCaseNotTerminated( List<Statement> statements ) { int i; for( i = statements.size()-1; i >= 0 && statements.get( i ) instanceof NoOpStatement; i-- ) { /* Loop until we find the index of the last non-noop statement */ } if( i >= 0 ) { Statement lastStmt = statements.get( i ); boolean[] bAbsolute = {false}; verifyOrWarn( lastStmt, lastStmt.hasParseExceptions() || lastStmt.getLeastSignificantTerminalStatement( bAbsolute ) != null && bAbsolute[0], true, Res.MSG_NONTERMINAL_CASE_CLAUSE ); } } boolean parseDefaultClause( SwitchStatement switchStmt, List<CaseClause> cases ) { if( !match( null, Keyword.KW_default ) ) { return false; } if( cases.size() > 0 ) { warnIfCaseNotTerminated( cases.get( cases.size()-1 ).getStatements() ); } verify( switchStmt, match( null, ":", SourceCodeTokenizer.TT_OPERATOR ), Res.MSG_EXPECTING_CASE_COLON ); verify( switchStmt, switchStmt.getDefaultStatements() == null, Res.MSG_MULTIPLE_DEFAULT_CLAUSES_NOT_PERMITTED ); List<Statement> defaultStatements = new ArrayList<Statement>(); parseStatementsAndDetectUnreachable( defaultStatements ); switchStmt.setDefaultStatements( defaultStatements ); return true; } void checkInstruction( boolean bProcessDirectives ) { // If the tokenizer has an 'instructor', it may be in a state where // it needs to analyze tokens in a separate context e.g., in a template // an expressoin of the form <%=foo.bar%>. Thus "analyze separately" // means to parse these as expressions and throw them away. Note we can // throw them away because instructors are indicative of pure analysis // of the source; the object code is of no interest e.g., a template // verifier may use an instructor. while( !_tokenizer.isEOF() && (_tokenizer.isAnalyzingSeparately() || _tokenizer.isAnalyzingDirective()) ) { if( _tokenizer.isAnalyzingDirective() ) { parseDirective( bProcessDirectives ); } else if( !(getGosuClass() instanceof IGosuClass) || !((CompilationState)((IGosuClass)getGosuClass()).getCompilationState()).isReparsingHeader() ) { // Only eat <%= xx %> expressions *after* the header is parsed during definition phase. // They need to be parsed within the program evaluate() body parsing phase, which includes class members etc. int iOffset = _tokenizer.getTokenStart(); parseExpression(); Expression e = popExpression(); clearExpressionInTemplateUnlessParsingEvaluateFunctionBody( e ); if( iOffset == _tokenizer.getTokenStart() ) { break; } } else { break; } } } private void clearExpressionInTemplateUnlessParsingEvaluateFunctionBody( Expression e ) { // Retain the parse tree (location) for template expressions when we are parsing the // template as a class and *only* during the phase where we are parsing the program // entry point aka the evaluate() function. Otherwise throw out the parse tree. if( !isParsingProgram() && getGosuClass() instanceof IGosuClass && (((IGosuClass)getGosuClass()).getCompilationState().isDeclarationsCompiled() && !((CompilationState)((IGosuClass)getGosuClass()).getCompilationState()).isReparsingHeader()) ) { if( !isParsingFunction() || (getProgramEntryPointDfs() != null && !peekParsingFunction().equals( getProgramEntryPointDfs().getType() )) ) { e.clearParseExceptions(); removeLocation( e.getLocation() ); removeInnerClasses( e ); } } } private void removeInnerClasses( IParsedElement e ) { if( e == null ) { return; } if( e instanceof BlockExpression ) { IBlockClass blockGosuClass = ((BlockExpression)e).getBlockGosuClass(); if( blockGosuClass != null ) { ((IGosuClassInternal)blockGosuClass.getEnclosingType()).removeBlock( blockGosuClass ); } } if( e instanceof NewExpression && ((NewExpression)e).isAnonymousClass() ) { IGosuClassInternal anon = (IGosuClassInternal)((Expression)e).getType(); if( anon != null ) { ((IGosuClassInternal)anon.getEnclosingType()).removeInnerClass( anon ); } } IParseTree location = e.getLocation(); if( location != null ) { for( IParseTree pt: location.getChildren() ) { removeInnerClasses( pt.getParsedElement() ); } } } private void removeLocation( ParseTree location ) { if( location.getParent() != null ) { location.getParent().removeChild( location ); } getLocationsList().remove( location ); } private void parseDirective( boolean processDirectives ) { int iOffset = _tokenizer.getTokenStart(); int iLineNum = _tokenizer.getLineNumber(); int iColumn = _tokenizer.getTokenColumn(); DirectiveExpression e = new DirectiveExpression(); if( match( null, Keyword.KW_extends ) ) { parseTypeLiteral(); Expression typeLiteral = peekExpression(); if( typeLiteral instanceof TypeLiteral ) { IType extendsType = ((TypeLiteral)typeLiteral).getType().getType(); if( extendsType instanceof IGosuClassInternal ) { IGosuClassInternal supertype = (IGosuClassInternal)extendsType; supertype.putClassMembers( this, _symTable, supertype, true ); List<? extends GosuClassTypeLoader> typeLoaders = TypeSystem.getCurrentModule().getTypeLoaders( GosuClassTypeLoader.class ); for( GosuClassTypeLoader typeLoader : typeLoaders ) { List<? extends IGosuEnhancement> enhancementsForType = typeLoader.getEnhancementIndex().getEnhancementsForType( supertype ); for( IGosuEnhancement enhancement : enhancementsForType ) { if( enhancement instanceof IGosuEnhancementInternal ) { ((IGosuEnhancementInternal)enhancement).putClassMembers( this, _symTable, supertype, true ); } } } for( Object entryObj : _symTable.getSymbols().entrySet() ) { //noinspection unchecked Map.Entry<CharSequence, ISymbol> entry = (Map.Entry<CharSequence, ISymbol>)entryObj; if( entry.getValue().isPrivate() ) { _symTable.removeSymbol( entry.getKey() ); } } } } } else if( match( null, "params" ) ) { verify(e, match(null, '('), Res.MSG_EXPECTING_LEFTPAREN_FUNCTION_DEF); ArrayList<ISymbol> params = parseParameterDeclarationList( e, false, null ); if( processDirectives ) { for( ISymbol param : params ) { getSymbolTable().putSymbol( param ); } } verify( e, match( null, ')' ), Res.MSG_EXPECTING_RIGHTPAREN_FUNCTION_DEF ); } else { advanceToNextTokenSilently(); e.addParseException( new ParseException( makeFullParserState(), Res.MSG_BAD_TEMPLATE_DIRECTIVE ) ); } pushExpression( e ); setLocation( iOffset, iLineNum, iColumn ); popExpression(); } boolean parseAssignmentOrMethodCall() { boolean bRet = true; if( !match( null, null, SourceCodeTokenizer.TT_KEYWORD, true ) && !match( null, null, SourceCodeTokenizer.TT_WORD, true ) && !match( null, null, '(', true ) && !match( null, null, '"', true ) ) { return false; } int initialMark = _tokenizer.mark(); parsePrimaryExpression(); Expression e = popExpression(); if( e instanceof NewExpression ) { // New Statement NewStatement stmt = new NewStatement(); stmt.setNewExpression( (NewExpression)e ); pushStatement( stmt ); } else if( e instanceof MethodCallExpression ) { // Method Call Statement MethodCallStatement stmt = new MethodCallStatement(); stmt.setMethodCall( (MethodCallExpression)e ); pushStatement( stmt ); //noinspection ThrowableResultOfMethodCallIgnored e.removeParseException( Res.MSG_VOID_EXPRESSION_NOT_ALLOWED ); } else if( e instanceof BeanMethodCallExpression ) { // Bean Method Call Statement BeanMethodCallStatement stmt = new BeanMethodCallStatement(); stmt.setBeanMethodCall( (BeanMethodCallExpression)e ); pushStatement( stmt ); //noinspection ThrowableResultOfMethodCallIgnored e.removeParseException(Res.MSG_VOID_EXPRESSION_NOT_ALLOWED); } else if( e instanceof IBlockInvocation ) { pushStatement( new BlockInvocationStatement( (IBlockInvocation)e ) ); //noinspection ThrowableResultOfMethodCallIgnored e.removeParseException(Res.MSG_VOID_EXPRESSION_NOT_ALLOWED); } else if( e instanceof Identifier || (e instanceof ImplicitTypeAsExpression && ((ImplicitTypeAsExpression)e).getLHS() instanceof Identifier) ) { Identifier id; Statement statement; // Assigment Statement Token T = new Token(); if( matchAssignmentOperator( T ) ) { if( e instanceof ImplicitTypeAsExpression ) { id = (Identifier)((ImplicitTypeAsExpression)e).getLHS(); IParsedElement parent = ContextInferenceManager.unwrapImplicitTypeAs( ((ImplicitTypeAsExpression)e).getLHS() ); _locations.remove( e.getLocation() ); if( parent == null ) { _locations.add( id.getLocation() ); } } else { id = (Identifier)e; } AssignmentStatement as = new AssignmentStatement(); statement = as; as.setIdentifier( id ); Expression rhs = parseAssignmentRhs( T, e.getType(), e ); rhs = buildRhsOfCompoundOperator( e, T, rhs ); if( rhs.hasParseExceptions() ) { rhs.getParseExceptions().get( 0 ).setExpectedType( id.getType() ); } ISymbol idSym = id.getSymbol(); verify( as, idSym.isWritable() || (idSym.isFinal() && !idSym.isStatic() && !(idSym instanceof CapturedSymbol) && ((idSym.isLocal() && !((Symbol)idSym).isImplicitlyInitialized()) || (isParsingConstructor() || isParsingProgramEvaluateMethod()) && isSymbolInScopeDirectly( idSym ))), // initializing final var is ok Res.MSG_PROPERTY_NOT_WRITABLE, idSym.getDisplayName() ); if( id instanceof PropertyAccessIdentifier ) { DynamicPropertySymbol symbol = (DynamicPropertySymbol)idSym; DynamicFunctionSymbol setter = symbol.getSetterDfs(); if(setter != null && setter.getScriptPart() != null) { IType settersOwningType = setter.getScriptPart().getContainingType(); if( settersOwningType instanceof IGosuClassInternal ) { IGosuClassInternal settersOwningClass = (IGosuClassInternal) settersOwningType; ICompilableTypeInternal ctxType = getGosuClass(); if( ctxType instanceof IGosuClassInternal ) { IGosuClassInternal ctxClass = (IGosuClassInternal)ctxType; if( !settersOwningClass.isAccessible( ctxClass, setter ) ) { id.addParseException( Res.MSG_PROPERTY_NOT_VISIBLE, idSym.getDisplayName() ); } } } } } if( rhs instanceof Identifier ) { Identifier identifier = (Identifier)rhs; if( idSym != null && idSym.equals( identifier.getSymbol() ) ) { as.addParseWarning( new ParseWarning( makeFullParserState(), Res.MSG_SILLY_ASSIGNMENT, idSym.getName() ) ); } if( identifier.getSymbol() instanceof DynamicPropertySymbol ) { DynamicPropertySymbol dps = (DynamicPropertySymbol) identifier.getSymbol(); if( dps.getVarIdentifier() != null && dps.getVarIdentifier().equals( idSym.getName() ) ) { as.addParseWarning( new ParseWarning( makeFullParserState(), Res.MSG_SILLY_ASSIGNMENT, idSym.getName() ) ); } } } //noinspection ThrowableResultOfMethodCallIgnored e.removeParseException( Res.MSG_CANNOT_READ_A_WRITE_ONLY_PROPERTY ); _ctxInferenceMgr.cancelInferences( id, rhs ); verifyComparable( id.getType(), rhs, true ); rhs = possiblyWrapWithImplicitCoercion( rhs, id.getType() ); as.setExpression( rhs ); } else { NotAStatement nas = new NotAStatement(); statement = nas; nas.setExpression( e ); verify( nas, false, Res.MSG_NOT_A_STATEMENT ); } pushStatement( statement ); } else if( e instanceof ParenthesizedExpression ) { NotAStatement nas = new NotAStatement(); nas.setExpression( e ); verify( nas, false, Res.MSG_EXPECTING_OPERATOR_TO_FOLLOW_EXPRESSION ); pushStatement( nas ); } else if( e instanceof SynthesizedMemberAccess ) { SyntheticMemberAccessStatement stmt = new SyntheticMemberAccessStatement((ISynthesizedMemberAccessExpression)e); pushStatement( stmt ); } else if( e instanceof MemberAccess || (e instanceof ImplicitTypeAsExpression && ((ImplicitTypeAsExpression)e).getLHS() instanceof MemberAccess)) { // Member Assignment Statement MemberAssignmentStatement as = new MemberAssignmentStatement(); MemberAccess ma; if( e instanceof ImplicitTypeAsExpression ) { ma = (MemberAccess)((ImplicitTypeAsExpression)e).getLHS(); IParsedElement parent = ContextInferenceManager.unwrapImplicitTypeAs( ((ImplicitTypeAsExpression)e).getLHS() ); _locations.remove( e.getLocation() ); if( parent == null ) { _locations.add( ma.getLocation() ); } } else { ma = (MemberAccess)e; } // This tests the validity of the access list IType typeExpected = ma.getType(); Token T = new Token(); if( verify( as, matchAssignmentOperator(T), Res.MSG_EXPECTING_EQUALS_ASSIGN ) ) { try { // The only case this can be null is for associative array access. if (ma.getMemberName() != null) { verifyPropertyWritable( ma.getRootType(), ma.getMemberName(), false ); } } catch( Exception ex ) { //noinspection ThrowableResultOfMethodCallIgnored ma.addParseException( ParseException.wrap( ex, makeFullParserState() ) ); } Expression rhs = parseAssignmentRhs( T, typeExpected, e ); _ctxInferenceMgr.cancelInferences( ma, rhs ); typeExpected = ma.getType(); //update type in case an inference was cancelled if( !(typeExpected instanceof ErrorType) ) { IPropertyInfo lhsPi = ma.getPropertyInfo(); if( lhsPi instanceof JavaPropertyInfo && ((JavaPropertyInfo)lhsPi).getWriteMethodInfo() == null && ((JavaPropertyInfo)lhsPi).getPublicField() != null ) { typeExpected = TypeSystem.get(((JavaPropertyInfo) lhsPi).getPublicField().getType()); } } verifyComparable( typeExpected, rhs, true ); rhs = buildRhsOfCompoundOperator( e, T, rhs ); if( rhs.hasParseExceptions() ) { rhs.getParseExceptions().get( 0 ).setExpectedType( typeExpected ); } rhs = possiblyWrapWithImplicitCoercion(rhs, typeExpected); as.setExpression( rhs ); } //noinspection ThrowableResultOfMethodCallIgnored ma.removeParseException( Res.MSG_CANNOT_READ_A_WRITE_ONLY_PROPERTY ); as.setRootExpression( ma.getRootExpression() ); as.setMemberName( ma.getMemberName() ); as.setMemberExpression( ma.getMemberExpression() ); as.setMemberAccess( ma ); pushStatement( as ); } else if( e instanceof ArrayAccess ) { // Array Assignment Statement Statement statement; ArrayAccess aa = (ArrayAccess)e; IType typeExpected = aa.getComponentType(); Token T = new Token(); ArrayAssignmentStatement as = new ArrayAssignmentStatement(); if( verify( as, matchAssignmentOperator(T), Res.MSG_EXPECTING_EQUALS_ASSIGN ) ) { verify( as, aa.getRootExpression().getType() != JavaTypes.STRING(), Res.MSG_STR_IMMUTABLE ); Expression rhs = parseAssignmentRhs( T, typeExpected, aa ); verifyComparable( typeExpected, rhs, true ); rhs = buildRhsOfCompoundOperator( e, T, rhs ); rhs = possiblyWrapWithImplicitCoercion( rhs, typeExpected ); as.setExpression( rhs ); as.setArrayAccessExpression( aa ); statement = as; } else { NotAStatement nas = new NotAStatement(); statement = nas; nas.setExpression( aa ); verify( nas, false, Res.MSG_NOT_A_STATEMENT ); } pushStatement( statement ); } else if( e instanceof MapAccess ) { // Array Assignment Statement Statement statement; MapAccess ma = (MapAccess)e; IType typeExpected = ma.getComponentType(); Token T = new Token(); MapAssignmentStatement as = new MapAssignmentStatement(); if( verify( as, matchAssignmentOperator(T), Res.MSG_EXPECTING_EQUALS_ASSIGN ) ) { Expression rhs = parseAssignmentRhs( T, typeExpected, e ); verifyComparable( typeExpected, rhs, true ); rhs = buildRhsOfCompoundOperator(e, T, rhs); rhs = possiblyWrapWithImplicitCoercion( rhs, ma.getComponentType() ); as.setExpression( rhs ); as.setMapAccessExpression( ma ); statement = as; } else { NotAStatement nas = new NotAStatement(); statement = nas; nas.setExpression( ma ); verify( nas, false, Res.MSG_NOT_A_STATEMENT ); } pushStatement( statement ); } else if (e instanceof FeatureLiteral) { NotAStatement nas = new NotAStatement(); nas.setExpression(e); verify(nas, false, Res.MSG_NOT_A_STATEMENT); pushStatement( nas ); } else { _tokenizer.restoreToMark(initialMark); _locations.remove(e.getLocation()); bRet = false; } return bRet; } private boolean isParsingProgramEvaluateMethod() { return isParsingFunction() && getProgramEntryPointDfs() != null && peekParsingFunction().equals( getProgramEntryPointDfs().getType() ); } private boolean isSymbolInScopeDirectly( ISymbol idSym ) { if( !(idSym instanceof DynamicSymbol) ) { return true; } DynamicSymbol fieldSym = (DynamicSymbol)idSym; IGosuClassInternal declaringClass = fieldSym.getGosuClass(); return getGosuClass() == declaringClass && !isParsingBlock(); } private Expression parseAssignmentRhs( Token operation, IType typeExpected, Expression lhs ) { Expression rhs; if( "++".equals(operation._strValue) || "--".equals( operation._strValue ) ) { AdditiveExpression add = new AdditiveExpression(); add.setLHS( lhs ); NumericLiteral one = new NumericLiteral( "1", 1, JavaTypes.pINT() ); pushExpression( one ); setLocation( lhs.getLocation().getExtent() + 1, lhs.getLineNum(), lhs.getLocation().getColumn() + 1 ); popExpression(); add.setRHS( one ); one.setType( typeExpected ); add.setOperator( "++".equals( operation._strValue ) ? "+" : "-" ); add.setType( resolveType( lhs, lhs.getType(), operation._strValue.charAt( 0 ), lhs.getType() ) ); pushExpression( add ); setLocation( lhs.getLocation().getOffset(), lhs.getLineNum(), lhs.getLocation().getColumn()+1 ); popExpression(); rhs = add; } else { parseExpression( typeExpected, false ); rhs = popExpression(); detectLikelyJavaCast(rhs); } return rhs; } private Expression buildRhsOfCompoundOperator( Expression lhs, Token t, Expression rhs ) { Expression synthetic = null; if( "+=".equals( t._strValue ) || "-=".equals( t._strValue ) ) { AdditiveExpression add = new AdditiveExpression(); add.setLHS( lhs ); add.setRHS( rhs ); add.setOperator( t._strValue.charAt( 0 ) == '+' ? "+" : "-" ); add.setType( resolveType( lhs, lhs.getType(), t._strValue.charAt( 0 ), rhs.getType() ) ); synthetic = add; } else if( "*=".equals( t._strValue ) || "/=".equals( t._strValue ) || "%=".equals( t._strValue ) ) { MultiplicativeExpression mult = new MultiplicativeExpression(); mult.setLHS( lhs ); mult.setRHS( rhs ); mult.setOperator( String.valueOf( t._strValue.charAt( 0 ) ) ); mult.setType( resolveType( lhs, lhs.getType(), t._strValue.charAt( 0 ), rhs.getType() ) ); synthetic = mult; } else if( "&=".equals( t._strValue ) ) { BitwiseAndExpression and = new BitwiseAndExpression(); lhs = ensureOperandIntOrLong( lhs ); rhs = possiblyWrapWithImplicitCoercion( rhs, lhs.getType() ); and.setLHS( lhs ); and.setRHS( rhs ); and.setType( resolveType( lhs, lhs.getType(), t._strValue.charAt( 0 ), rhs.getType() ) ); synthetic = and; } else if( "&&=".equals( t._strValue ) ) { ConditionalAndExpression and = new ConditionalAndExpression(); verifyComparable( JavaTypes.pBOOLEAN(), rhs, true, true ); rhs = possiblyWrapWithImplicitCoercion( rhs, JavaTypes.pBOOLEAN() ); verifyComparable( JavaTypes.pBOOLEAN(), lhs, true, true ); lhs = possiblyWrapWithImplicitCoercion( lhs, JavaTypes.pBOOLEAN() ); and.setLHS( lhs ); and.setRHS( rhs ); synthetic = and; } else if( "^=".equals( t._strValue ) ) { BitwiseXorExpression xor = new BitwiseXorExpression(); lhs = ensureOperandIntOrLong( lhs ); rhs = possiblyWrapWithImplicitCoercion( rhs, lhs.getType() ); xor.setLHS( lhs ); xor.setRHS( rhs ); xor.setType( resolveType( lhs, lhs.getType(), t._strValue.charAt( 0 ), rhs.getType() ) ); synthetic = xor; } else if( "|=".equals( t._strValue ) ) { BitwiseOrExpression or = new BitwiseOrExpression(); lhs = ensureOperandIntOrLong( lhs ); rhs = possiblyWrapWithImplicitCoercion( rhs, lhs.getType() ); or.setLHS( lhs ); or.setRHS( rhs ); or.setType( resolveType( lhs, lhs.getType(), t._strValue.charAt( 0 ), rhs.getType() ) ); synthetic = or; } else if( "||=".equals( t._strValue ) ) { ConditionalOrExpression or = new ConditionalOrExpression(); verifyComparable( JavaTypes.pBOOLEAN(), rhs, true, true ); rhs = possiblyWrapWithImplicitCoercion( rhs, JavaTypes.pBOOLEAN() ); verifyComparable( JavaTypes.pBOOLEAN(), lhs, true, true ); lhs = possiblyWrapWithImplicitCoercion( lhs, JavaTypes.pBOOLEAN() ); or.setLHS( lhs ); or.setRHS( rhs ); synthetic = or; } else if( "<<=".equals( t._strValue ) || ">>=".equals( t._strValue ) || ">>>=".equals( t._strValue ) ) { BitshiftExpression shift = new BitshiftExpression(); verifyTypesComparable( rhs, JavaTypes.pINT(), rhs.getType(), false, true ); rhs = possiblyWrapWithImplicitCoercion( rhs, JavaTypes.pINT() ); // Lhs must be an int or a long IType lhsType = lhs.getType(); if( verify( lhs, lhsType == JavaTypes.LONG() || lhsType == JavaTypes.pLONG() || lhsType == JavaTypes.INTEGER() || lhsType == JavaTypes.pINT() || lhsType == JavaTypes.SHORT() || lhsType == JavaTypes.pSHORT() || lhsType == JavaTypes.BYTE() || lhsType == JavaTypes.pBYTE(), Res.MSG_BITSHIFT_LHS_MUST_BE_INT_OR_LONG ) ) { lhsType = lhsType == JavaTypes.LONG() || lhsType == JavaTypes.pLONG() ? JavaTypes.pLONG() : JavaTypes.pINT(); lhs = possiblyWrapWithImplicitCoercion( lhs, lhsType ); } shift.setLHS( lhs ); shift.setRHS( rhs ); shift.setOperator( t._strValue ); shift.setType( resolveType( lhs, lhs.getType(), t._strValue.charAt( 0 ), rhs.getType() ) ); synthetic = shift; } if( synthetic != null ) { pushExpression( synthetic ); ParseTree rhsLoc = rhs.getLocation(); setLocation( rhsLoc.getOffset(), rhs.getLineNum(), rhsLoc.getColumn(), true ); popExpression(); rhs = synthetic; } return rhs; } private boolean matchAssignmentOperator( Token t ) { return match( t, "=", SourceCodeTokenizer.TT_OPERATOR ) || match( t, "+=", SourceCodeTokenizer.TT_OPERATOR ) || match( t, "-=", SourceCodeTokenizer.TT_OPERATOR ) || match( t, "++", SourceCodeTokenizer.TT_OPERATOR ) || match( t, "--", SourceCodeTokenizer.TT_OPERATOR ) || match( t, "*=", SourceCodeTokenizer.TT_OPERATOR ) || match( t, "%=", SourceCodeTokenizer.TT_OPERATOR ) || match( t, "/=", SourceCodeTokenizer.TT_OPERATOR ) || match( t, "&=", SourceCodeTokenizer.TT_OPERATOR ) || match( t, "&&=", SourceCodeTokenizer.TT_OPERATOR ) || match( t, "^=", SourceCodeTokenizer.TT_OPERATOR ) || match( t, "|=", SourceCodeTokenizer.TT_OPERATOR ) || match( t, "||=", SourceCodeTokenizer.TT_OPERATOR ) || match( t, "<<=", SourceCodeTokenizer.TT_OPERATOR ) || matchRightShiftAssign( t ); } private boolean matchRightShiftAssign( Token t ) { int iMark = getTokenizer().mark(); if( match( null, ">", SourceCodeTokenizer.TT_OPERATOR ) ) { if( match( null, ">", SourceCodeTokenizer.TT_OPERATOR ) ) { if( match( null, ">", SourceCodeTokenizer.TT_OPERATOR ) ) { if( match( null, "=", SourceCodeTokenizer.TT_OPERATOR ) ) { t._strValue = ">>>="; return true; } } else if( match( null, "=", SourceCodeTokenizer.TT_OPERATOR ) ) { t._strValue = ">>="; return true; } } } getTokenizer().restoreToMark( iMark ); return false; } //------------------------------------------------------------------------------ // // function-declaration // [modifiers] function <identifier> ( [ <argument-declaration-list> ] ) : <type-literal> // ISymbol parseFunctionOrPropertyDeclaration( ParsedElement element, boolean bParseProperties, boolean bParseVars ) { ModifierInfo modifiers; do { while( match( null, Keyword.KW_uses ) ) { Token T = new Token(); parseUsesStatement( T ); popStatement(); } modifiers = parseModifiers(); if( match( null, Keyword.KW_function ) ) { DynamicFunctionSymbol symbol = parseFunctionDecl(element, modifiers); eatStatementBlock( symbol != null && symbol.getDeclFunctionStmt() != null ? symbol.getDeclFunctionStmt() : element, Res.MSG_EXPECTING_OPEN_BRACE_FOR_FUNCTION_DEF ); return symbol; } if( bParseProperties && match( null, Keyword.KW_property ) ) { boolean bGetter = match( null, Keyword.KW_get ); boolean bSetter = match( null, Keyword.KW_set ); // We only care about legal properties if (bGetter || bSetter) { FunctionStatement fs = new FunctionStatement(); DynamicFunctionSymbol dfs = getOwner().parseFunctionDecl( fs, true, bGetter, modifiers ); if( dfs == null ) { element.addParseException( new ParseException( makeFullParserState(), Res.MSG_EXPECTING_DECL ) ); return null; } fs.setDynamicFunctionSymbol( dfs ); pushStatement( fs ); dfs.setClassMember( true ); eatStatementBlock( fs, Res.MSG_EXPECTING_OPEN_BRACE_FOR_FUNCTION_DEF ); DynamicPropertySymbol dps = getOrCreateDynamicPropertySymbol( element, null, dfs, bGetter ); dps.setClassMember(false); return dps; } } if( bParseVars && match( null, Keyword.KW_var ) ) { VarStatement var = new VarStatement(); Token T = new Token(); if( !verify( var, match( T, SourceCodeTokenizer.TT_WORD ), Res.MSG_EXPECTING_IDENTIFIER_VAR ) ) { T._strValue = null; } parseVarStatement( var, T, false ); return var.getSymbol(); } if( match( null, SourceCodeTokenizer.TT_EOF ) ) { return null; } _tokenizer.nextToken(); } while( true ); } boolean parsePropertyDefinition() { if( isParsingBlock() ) { return false; } ModifierInfo modifiers = parseModifiers(); if( match( null, Keyword.KW_property ) ) { boolean bGetter = match( null, Keyword.KW_get ); boolean bSetter = !bGetter && match( null, Keyword.KW_set ); int iOffset = getTokenizer().getTokenStart(); int iLineNum = getTokenizer().getLineNumber(); int iColumn = getTokenizer().getTokenColumn(); FunctionStatement functionStmt = 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 ) { // Note, dfs is null only if the function stmt is errant dfs = new DynamicFunctionSymbol( _symTable, "@", new FunctionType( "@", JavaTypes.pVOID(), null, GenericTypeVariable.EMPTY_TYPEVARS ), Collections.<ISymbol>emptyList(), (Statement)null ); functionStmt.setDynamicFunctionSymbol( dfs ); } IType returnType = functionStmt.getDynamicFunctionSymbol().getReturnType(); verify( functionStmt, bGetter || returnType == JavaTypes.pVOID(), Res.MSG_PROPERTY_SET_MUST_RETURN_VOID ); dfs.setClassMember( false ); if( bGetter && dfs.getArgTypes() != null && dfs.getArgTypes().length > 0 ) { List<IParseTree> children = functionStmt.getLocation().getChildren(); for( IParseTree child : children ) { if( child.getParsedElement() instanceof ParameterDeclaration ) { child.getParsedElement().addParseException(Res.MSG_GETTER_CANNOT_HAVE_PARAMETERS); } } } DynamicPropertySymbol dps = getOrCreateDynamicPropertySymbol( functionStmt, null, dfs, bGetter ); dps.setClassMember( false ); getOwner().pushStatement( new PropertyStatement( functionStmt, dps ) ); return true; } return false; } DynamicPropertySymbol getOrCreateDynamicPropertySymbol( ParsedElement parsedElement, IGosuClassInternal gsClass, DynamicFunctionSymbol dfs, boolean bGetter ) { String strPropertyName = dfs.getDisplayName().substring( 1 ); ISymbol symbol = getSymbolTable().getSymbol( strPropertyName ); DynamicPropertySymbol dps; if( !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 ); if( symbol != null ) { assert symbol instanceof DynamicPropertySymbol; dps.setParent( (DynamicPropertySymbol)symbol ); } return dps; } assert symbol instanceof DynamicPropertySymbol; dps = (DynamicPropertySymbol)symbol; if( bGetter ) { verify( parsedElement, dps.getImmediateGetterDfs() == null || dps.getImmediateGetterDfs() instanceof VarPropertyGetFunctionSymbol || dps.getImmediateGetterDfs().getValueDirectly() != null || dps.getImmediateGetterDfs().isAbstract() || (gsClass != null && gsClass.isInterface()), Res.MSG_GETTER_FOR_PROPERTY_ALREADY_DEFINED, strPropertyName ); dps.setGetterDfs( dfs ); } else { verify( parsedElement, dps.getImmediateSetterDfs() == null || dps.getImmediateSetterDfs() instanceof VarPropertySetFunctionSymbol || dps.getImmediateSetterDfs().getValueDirectly() != null || dps.getImmediateSetterDfs().isAbstract() || (gsClass != null && gsClass.isInterface()), Res.MSG_SETTER_FOR_PROPERTY_ALREADY_DEFINED, strPropertyName ); dps.setSetterDfs(dfs); } return dps; } //------------------------------------------------------------------------------ // // function-definition // [modifiers] function <identifier> ( [ <argument-declaration-list> ] ) : <type-literal> statement-block // boolean parseFunctionDefinition() { ModifierInfo modifiers = parseModifiers(); if( !isParsingBlock() && match(null, Keyword.KW_function) ) { parseBaseFunctionDefinition( new FunctionStatement(), false, false, modifiers ); return true; } return false; } FunctionStatement parseBaseFunctionDefinition(FunctionStatement functionStmt, boolean bProperty, boolean bGetter, ModifierInfo modifiers) { boolean bNullFunctionStmt = functionStmt == null; functionStmt = bNullFunctionStmt ? new FunctionStatement() : functionStmt; Token T = new Token(); int iNamedOffset = getTokenizer().getTokenStart(); int iOffsetName = getTokenizer().getTokenStart(); int iLineNumName = getTokenizer().getLineNumber(); int iColumnName = getTokenizer().getTokenColumn(); boolean bHasName; if( bHasName = verify( functionStmt, match( T, SourceCodeTokenizer.TT_WORD ), Res.MSG_EXPECTING_NAME_FUNCTION_DEF ) ) { functionStmt.setNameOffset( iNamedOffset, T._strValue ); } String strFunctionName = T._strValue; addNameInDeclaration( strFunctionName, iOffsetName, iLineNumName, iColumnName, bHasName ); maybeEatNonDeclKeyword( bHasName, strFunctionName ); strFunctionName = strFunctionName == null ? "" : bProperty ? "@" + strFunctionName : strFunctionName; DynamicFunctionSymbol dfsDecl = findCorrespondingDeclDfs( iOffsetName, modifiers.getModifiers() ); List<TypeVariableDefinitionImpl> defsFromDecl = getTypeVarDefsFromDecl( dfsDecl ); List<ITypeVariableDefinitionExpression> typeVarDefs = parseTypeVariableDefs( functionStmt, true, defsFromDecl ); // Must create function type and assign it as the type var's enclosing // type *before* we parse the return type (in case it refs function's type vars) IGenericTypeVariable[] typeVars = TypeVariableDefinition.getTypeVars( typeVarDefs ); if( bProperty && !typeVarDefs.isEmpty() ) { verify( functionStmt, false, Res.MSG_GENERIC_PROPERTIES_NOT_SUPPORTED ); } try { verify( functionStmt, match( null, '(' ), Res.MSG_EXPECTING_LEFTPAREN_FUNCTION_DEF ); ArrayList<ISymbol> args = null; Statement statement = null; boolean functionBodyParsed = true; // Pushing an isolated scope to that the compile-time stack indexes are // aligned with runtime e.g., args must be indexed relative to the // activation record. // We push a scope to parse the function decl, then we pop it to find the dfs, then we push it again to parse the body. _symTable.pushIsolatedScope( new GosuParserTransparentActivationContext( getScriptPart() ) ); try { IType[] argTypes = null; if( !match( null, null, ')', true ) ) { args = parseParameterDeclarationList( functionStmt, Modifier.isStatic( modifiers.getModifiers() ), null, bProperty, bGetter, false ); argTypes = new IType[args.size()]; for( int i = 0; i < args.size(); i++ ) { _symTable.putSymbol( args.get( i ) ); argTypes[i] = args.get( i ).getType(); } } else { parseParameterDeclarationList( functionStmt, Modifier.isStatic( modifiers.getModifiers() ), null, bProperty, bGetter, true ); verify ( functionStmt, ! bProperty || bGetter, Res.MSG_PROPERTY_SET_MUST_HAVE_ONE_PARAMETER ); } verify( functionStmt, match( null, ')' ), Res.MSG_EXPECTING_RIGHTPAREN_FUNCTION_DEF ); TypeLiteral typeLiteral; if( match( null, ":", SourceCodeTokenizer.TT_OPERATOR ) ) { parseTypeLiteral(); typeLiteral = (TypeLiteral)popExpression(); if( bGetter ) { verify( typeLiteral, typeLiteral.getType().getType() != JavaTypes.pVOID(), Res.MSG_VOID_NOT_ALLOWED ); } } else { Token fabricatedT = new Token(); fabricatedT._strValue = Keyword.KW_void.toString(); typeLiteral = resolveTypeLiteral( fabricatedT ); verify( functionStmt, !bGetter, Res.MSG_MISSING_PROPERTY_RETURN ); } // Note dfsDecl *should have* been assigned prior to parsing args etc. However it can be the case where // e.g., crappy pcf types, the symbol can only be resolved from the _symTable. Sigh. if( !bProperty && dfsDecl == null ) { String functionNameWithArgs = DynamicFunctionSymbol.getSignatureName( strFunctionName == null ? "" : strFunctionName, args ); DynamicFunctionSymbol dfsInSymTable = (DynamicFunctionSymbol)_symTable.getSymbol( functionNameWithArgs ); if( dfsInSymTable == null ) { dfsDecl = dfsInSymTable; } else if( dfsInSymTable.getGosuClass() == getGosuClass() ) { dfsDecl = dfsInSymTable; dfsDecl = assignPossibleDuplicateDfs( dfsDecl, _symTable.getSymbols().values() ); } } FunctionType type = dfsDecl == null ? new FunctionType( strFunctionName, JavaTypes.pVOID(), null, typeVars ) : (FunctionType)dfsDecl.getType(); if( !bProperty && dfsDecl != null ) { type = (FunctionType)dfsDecl.getType(); if( typeVarDefs != null ) { for( ITypeVariableDefinitionExpression tvd : typeVarDefs ) { ((ITypeVariableDefinition)tvd).setEnclosingType( type ); } } } else { type.setRetType( typeLiteral.getType().getType() ); type.setArgumentTypes( argTypes ); } type.setScriptPart( getScriptPart() ); type.setModifiers( modifiers.getModifiers() ); putThisAndSuperSymbols( modifiers ); if( !Modifier.isAbstract( modifiers.getModifiers() ) && !Modifier.isNative( modifiers.getModifiers() ) ) { if( getScriptPart() == null || !(getScriptPart().getContainingType() instanceof IGosuClassInternal) || !getScriptPart().getContainingType().isInterface() ) { if( parseFunctionBody( functionStmt, type ) ) { statement = popStatement(); } else { functionBodyParsed = false; } } } } finally { _symTable.popScope(); } ISymbol functionSymbol = _symTable.getSymbol( strFunctionName ); if( functionSymbol instanceof CommonSymbolsScope.LockedDownSymbol ) { functionSymbol = null; } if( bProperty ) { if( functionSymbol instanceof DynamicPropertySymbol ) { DynamicPropertySymbol dps = (DynamicPropertySymbol)functionSymbol; String functionNameWithArgs = DynamicFunctionSymbol.getSignatureName( strFunctionName, args ); dfsDecl = (dps == null || !dps.getName().equals( strFunctionName ) ) ? (DynamicFunctionSymbol)_symTable.getSymbol( functionNameWithArgs ) : dps.getFunction( functionNameWithArgs ); } else if( getGosuClass() == null ) { dfsDecl = findProgramPropertyDfs( strFunctionName, args ); } } if( dfsDecl != null && dfsDecl.getDeclFunctionStmt() != null && bNullFunctionStmt ) { FunctionStatement funcStmtFromDecl = dfsDecl.getDeclFunctionStmt(); if( funcStmtFromDecl.getParent() != null && funcStmtFromDecl.getGosuClass() == getGosuClass() ) { functionStmt = dfsDecl.getDeclFunctionStmt(); } } verify( functionStmt, functionBodyParsed, Res.MSG_EXPECTING_RETURN_TYPE_OR_FUN_BODY ); verify( functionStmt, (getGosuClass() != null && getGosuClass().isAbstract()) || !Modifier.isAbstract( modifiers.getModifiers() ), Res.MSG_ABSTRACT_MEMBER_NOT_IN_ABSTRACT_CLASS ); verify( functionStmt, dfsDecl != null, Res.MSG_EXPECTING_NAME_FUNCTION_DEF ); verify( functionStmt, parsingFunctionsEncloseMyClass(), Res.MSG_EXPECTING_CLOSE_BRACE_FOR_FUNCTION_DEF ); if( dfsDecl != null ) { if( args != null ) { //replace the decl time arg symbols with the impl time arg symbols dfsDecl.getArgs().clear(); dfsDecl.getArgs().addAll( args ); } // Overwrite annotations to use the new-expressions created in dfsDecl.getModifierInfo().setAnnotations( modifiers.getAnnotations() ); dfsDecl.setValueDirectly( statement ); pushDynamicFunctionSymbol( dfsDecl ); functionStmt.setDynamicFunctionSymbol( dfsDecl ); verifyOrWarn( functionStmt, isTerminal( statement, dfsDecl.getReturnType() ), !CommonServices.getEntityAccess().isUnreachableCodeDetectionOn(), Res.MSG_MISSING_RETURN ); } pushStatement( functionStmt ); DynamicFunctionSymbol dynamicFunctionSymbol = functionStmt.getDynamicFunctionSymbol(); if(dynamicFunctionSymbol != null && dynamicFunctionSymbol.getDeclFunctionStmt() == null ) { dynamicFunctionSymbol.setDeclFunctionStmt(functionStmt); } return functionStmt; } finally { for( ITypeVariableDefinitionExpression typeVarDef : typeVarDefs ) { Map<String, ITypeVariableDefinition> typeVarMap = getTypeVariables(); if( typeVarMap.containsValue( typeVarDef ) ) { typeVarMap.remove( ((ITypeVariableDefinition)typeVarDef).getName() ); } } } } private void putThisAndSuperSymbols( ModifierInfo modifiers ) { if( !Modifier.isStatic( modifiers.getModifiers() ) && getScriptPart() != null && (getScriptPart().getContainingType() instanceof IGosuClassInternal) ) { IGosuClassInternal gsClass = (IGosuClassInternal)getScriptPart().getContainingType(); IType thisType = gsClass; if( gsClass instanceof IGosuEnhancementInternal ) { thisType = ((IGosuEnhancementInternal)gsClass).getEnhancedType(); } if( thisType != null ) { thisType = TypeLord.getConcreteType( thisType ); getSymbolTable().putSymbol( new ThisSymbol( thisType, _symTable ) ); if( !(gsClass instanceof IGosuEnhancementInternal) ) { IGosuClassInternal superClass = gsClass.getSuperClass(); if( superClass == null ) { superClass = IGosuClassInternal.Util.getGosuClassFrom( JavaTypes.OBJECT() ); } getSymbolTable().putSymbol( new Symbol( Keyword.KW_super.getName(), superClass, _symTable, null ) ); } } } } private DynamicFunctionSymbol findProgramPropertyDfs( String strFunctionName, ArrayList<ISymbol> args ) { List<IFunctionSymbol> list = getDfsDeclsForFunction( (String)strFunctionName ); String propertyNameWithArgs = DynamicFunctionSymbol.getSignatureName( strFunctionName == null ? "" : strFunctionName, args ); for( IFunctionSymbol func : list ) { if( func instanceof DynamicFunctionSymbol ) { DynamicFunctionSymbol dfs = (DynamicFunctionSymbol)func; String sig = DynamicFunctionSymbol.getSignatureName( dfs.getDisplayName(), dfs.getArgs() ); if( propertyNameWithArgs.equals( sig ) ) { return dfs; } } } return null; } private List<TypeVariableDefinitionImpl> getTypeVarDefsFromDecl( DynamicFunctionSymbol dfsDecl ) { if( dfsDecl == null ) { return Collections.emptyList(); } IGenericTypeVariable[] typeVars = dfsDecl.getType().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 DynamicFunctionSymbol findCorrespondingDeclDfs( int iOffsetName, int iModifiers ) { ICompilableTypeInternal gsClass = getGosuClass(); if( gsClass == null ) { return null; } Collection<DynamicFunctionSymbol> functions = Modifier.isStatic( iModifiers ) ? gsClass.getParseInfo().getStaticFunctions() : gsClass.getParseInfo().getMemberFunctions().values(); for( DynamicFunctionSymbol dfs : functions ) { FunctionStatement funcStmt = dfs.getDeclFunctionStmt(); if( funcStmt != null && funcStmt.getNameOffset( null ) == iOffsetName ) { return dfs; } } return null; } boolean isDeclarationKeyword( String strKeyword ) { return strKeyword != null && (Keyword.KW_function.equals( strKeyword ) || Keyword.KW_construct.equals( strKeyword ) || Keyword.KW_property.equals( strKeyword ) || //Keyword.KW_var.equals( strFunctionName ) || Keyword.KW_delegate.equals( strKeyword ) || Keyword.KW_class.equals( strKeyword ) || Keyword.KW_interface.equals( strKeyword ) || Keyword.KW_structure.equals( strKeyword ) || Keyword.KW_enum.equals( strKeyword )); } static DynamicFunctionSymbol assignPossibleDuplicateDfs( DynamicFunctionSymbol dfsDecl, Iterable symbols ) { DynamicFunctionSymbol result = dfsDecl; if( dfsDecl != null && dfsDecl.getValueDirectly() != null ) { int iMin = Integer.MAX_VALUE; for( Object csr : symbols ) { if( csr instanceof DynamicFunctionSymbol ) { DynamicFunctionSymbol dfsCsr = (DynamicFunctionSymbol)csr; if( dfsCsr.getName().toLowerCase().contains( "_duplicate_" + dfsDecl.getName().toLowerCase() ) && dfsCsr.getGosuClass() == dfsDecl.getGosuClass() ) { String strName = dfsCsr.getName(); if( dfsCsr.getValueDirectly() == null ) { int iIndex = Integer.parseInt( strName.substring( 0, strName.indexOf( '_' ) ) ); if( iIndex < iMin ) { iMin = iIndex; result = (DynamicFunctionSymbol)csr; } } } } } } return result; } void addNameInDeclaration( String strName, int iOffsetName, int iLineNumName, int iColumnName, boolean bHasName ) { NameInDeclaration name = new NameInDeclaration( strName ); pushExpression( name ); setLocation( bHasName ? iOffsetName : getTokenizer().getPriorToken().getTokenEnd(), iLineNumName, iColumnName, !bHasName, true ); popExpression(); } private boolean parsingFunctionsEncloseMyClass() { if( _parsingFunctions.isEmpty() ) { return true; } for( FunctionType ft : _parsingFunctions ) { if( ft.getScriptPart().getContainingType() == getGosuClass() ) { return false; } } return true; } FunctionStatement parseProgramEntryPointBody() { DynamicFunctionSymbol dfsDecl = getProgramEntryPointDfs(); // if( _tokenizer.getInstructor() == null ) // { // parseClasspathStatements( true ); // } _symTable.pushIsolatedScope( new GosuParserTransparentActivationContext( getScriptPart() ) ); boolean bFunctionBodyParsed = true; StatementList stmtList = null; _bProgramCallFunction = true; try { if( dfsDecl != null && parseProgramFunctionBody( (FunctionType)dfsDecl.getType() ) ) { stmtList = (StatementList)popStatement(); } else { bFunctionBodyParsed = false; } } finally { _bProgramCallFunction = false; _symTable.popScope(); } FunctionStatement fs = new FunctionStatement(); fs.setDynamicFunctionSymbol( dfsDecl ); verify( fs, bFunctionBodyParsed, Res.MSG_EXPECTING_RETURN_TYPE_OR_FUN_BODY ); if (dfsDecl != null) { dfsDecl.setValueDirectly( stmtList ); } pushDynamicFunctionSymbol( dfsDecl ); fs.setDynamicFunctionSymbol( dfsDecl ); // if( bFunctionBodyParsed ) // { addDefaultReturnStmt( dfsDecl, stmtList ); // } pushStatement( fs ); return fs; } private DynamicFunctionSymbol getProgramEntryPointDfs() { String functionNameWithArgs = DynamicFunctionSymbol.getSignatureName( "evaluate", Collections.<ISymbol>singletonList( new Symbol( "symbols", JavaTypes.IEXTERNAL_SYMBOL_MAP(), null ) ) ); return (DynamicFunctionSymbol)_symTable.getSymbol( functionNameWithArgs ); } private StatementList handleExpressionStatementList( Expression expr ) { Statement stmt; if( expr.hasParseExceptions() || expr.getType() != JavaTypes.pVOID() || expr instanceof NullExpression ) { stmt = wrapProgramExpressionInReturnStmt( expr ); } else if( expr instanceof MethodCallExpression ) { stmt = new MethodCallStatement(); ((MethodCallStatement)stmt).setMethodCall( (MethodCallExpression)expr ); } else if( expr instanceof BeanMethodCallExpression ) { stmt = new BeanMethodCallStatement(); ((BeanMethodCallStatement)stmt).setBeanMethodCall( (BeanMethodCallExpression)expr ); } else if( expr instanceof BlockInvocation ) { stmt = new BlockInvocationStatement((BlockInvocation)expr); } else { throw new UnsupportedOperationException( "Did not expect expression of type: " + expr.getClass().getName() ); } pushStatement( stmt ); ParseTree exprLoc = expr.getLocation(); setLocation( exprLoc.getOffset(), exprLoc.getLineNum(), exprLoc.getColumn(), true ); popStatement(); StatementList stmtList = new StatementList( _symTable ); List<Statement> stmts = Collections.singletonList( stmt ); stmtList.setStatements( stmts ); pushStatement( stmtList ); setLocation( exprLoc.getOffset(), exprLoc.getLineNum(), exprLoc.getColumn(), true ); return stmtList; } private ReturnStatement wrapProgramExpressionInReturnStmt( Expression e ) { ReturnStatement retStmt = new ReturnStatement(); retStmt.setSynthetic( true ); e = possiblyWrapWithImplicitCoercion( e, JavaTypes.OBJECT() ); if( e.getType() == JavaTypes.pVOID() ) { e.setType( JavaTypes.OBJECT() ); } retStmt.setValue( e ); return retStmt; } private void addDefaultReturnStmt( DynamicFunctionSymbol dfsDecl, StatementList stmtList ) { if( dfsDecl != null && !isTerminal( stmtList, dfsDecl.getReturnType() ) ) { ReturnStatement defaultReturnStmt = new ReturnStatement(); ImplicitTypeAsExpression ta = new ImplicitTypeAsExpression(); ta.setLHS( new NullExpression() ); ta.setType( JavaTypes.OBJECT() ); ta.setCoercer( IdentityCoercer.instance() ); defaultReturnStmt.setValue( ta ); List<Statement> stmts; if( stmtList.getStatements() == null ) { //## todo: Probably short-circuit the condition when a program is an empty expression i.e., don't generate a class for it stmts = new ArrayList<Statement>( 2 ); } else { stmts = new ArrayList<Statement>( Arrays.asList( stmtList.getStatements() ) ); } stmts.add( defaultReturnStmt ); stmtList.setStatements( stmts ); } } private boolean parseProgramFunctionBody( FunctionType type ) { maybeSetExternalSymbols(); pushParsingFunction( type ); try { setDontOptimizeStatementLists( true ); int state = _tokenizer.mark(); int iLocationsCount = _locations.size(); try { parseExpression(); Expression expr = popExpression(); verify( expr, match( null, SourceCodeTokenizer.TT_EOF ), Res.MSG_END_OF_EXPRESSION ); ((IGosuProgramInternal)getGosuClass()).setExpression( expr ); verifyParsedElement( expr, false ); handleExpressionStatementList( expr ); } catch( ParseResultsException exprErr ) { boolean bProbablyProgram = !getTokenizer().isEOF(); _tokenizer.restoreToMark( state ); _stack.clear(); removeLocationsFrom( iLocationsCount ); try { if( !parseStatement( true ) ) { // Couldn't parse a function body at all return false; } verifyParsedElement( peekStatement(), false ); ((IGosuProgramInternal)getGosuClass()).setStatement( peekStatement() ); } catch( ParseResultsException stmtErr ) { if( !bProbablyProgram ) { // Note we can't just rethrow the original exception because we need // the locations etc. in the parser, so we have to reparse and let it throw. _tokenizer.restoreToMark( state ); _stack.clear(); removeLocationsFrom( iLocationsCount ); parseExpression(); Expression expr = popExpression(); verify( expr, match( null, SourceCodeTokenizer.TT_EOF ), Res.MSG_END_OF_EXPRESSION ); final IGosuValidator validator = getValidator(); if( validator != null ) { validator.validate( expr, getScript() ); } handleExpressionStatementList( expr ); } // else // { // int iProgSourceLen = _tokenizer.getReader().getLength(); // int iProgStmtExtent = peekStatement().getLocation().getExtent() + 1; // if( iProgStmtExtent < iProgSourceLen ) // { // ParseTree loc = peekStatement().getLocation(); // loc.setLength( iProgSourceLen - loc.getOffset() ); // } // } } } Statement body = peekStatement(); if( body instanceof StatementList ) { ((StatementList)body).setNoScope(); } return true; } finally { popParsingFunction(); } } private void maybeSetExternalSymbols() { if( getGosuClass() instanceof IGosuProgram ) { ISourceFileHandle sfh = getGosuClass().getSourceFileHandle(); if( sfh instanceof StringSourceFileHandle ) { ISymbolTable extSyms = ((StringSourceFileHandle)sfh).getExternalSymbols(); if( extSyms != null ) { // If extSyms is non-null, it usually means this program is for context-sensitive evaluation e.g., in a debugger HashMap<String, ISymbol> map = new HashMap<String, ISymbol>(); //noinspection unchecked for( Symbol s: (Collection<Symbol>)extSyms.getSymbols().values() ) { map.put( s.getName(), s ); } ExternalSymbolMapForMap extMap = new ExternalSymbolMapForMap( map ); ((GosuProgramParseInfo)getGosuClass().getParseInfo()).setExternalSymbols( extMap ); } } } } private void removeLocationsFrom( int iLocationsCount ) { for( int i = _locations.size(); i > iLocationsCount; i-- ) { _locations.remove( i-1 ); } } private boolean isTerminal( Statement statement, IType returnType ) { boolean[] bAbsolute = {false}; return returnType == JavaTypes.pVOID() || statement == null || statement.getLeastSignificantTerminalStatement( bAbsolute ) != null && bAbsolute[0]; } private boolean parseFunctionBody( FunctionStatement functionStmt, FunctionType type ) { pushParsingFunction( type ); try { if( (!(getGosuClass() instanceof IGosuProgram) || !((IGosuProgramInternal)getGosuClass()).isParsingExecutableProgramStatements()) && !match( null, null, '{', true ) ) { IToken T = getTokenizer().getCurrentToken(); eatStatementBlock( functionStmt, Res.MSG_EXPECTING_OPEN_BRACE_FOR_FUNCTION_DEF ); NotAStatement nas = new NotAStatement(); pushStatement( nas ); setLocation( T.getTokenStart(), T.getLine(), T.getTokenColumn() ); } else if( !parseStatement() ) { return false; } Statement body = peekStatement(); if( body instanceof StatementList ) { ((StatementList)body).setNoScope(); } return true; } finally { popParsingFunction(); } } DynamicFunctionSymbol parseFunctionDecl( ParsedElement element, ModifierInfo modifiers ) { return parseFunctionDecl( element, false, false, modifiers ); } DynamicFunctionSymbol parseFunctionDecl(ParsedElement element, boolean bProperty, boolean bGetter, ModifierInfo modifiers) { return parseFunctionDecl( element, null, bProperty, bGetter, modifiers ); } DynamicFunctionSymbol parseFunctionDecl(ParsedElement element, Token T, boolean bProperty, boolean bGetter, ModifierInfo modifiers) { _symTable.pushIsolatedScope(new FunctionDeclTransparentActivationContext(getScriptPart())); try { boolean bHasName = true; if( T == null ) { T = new Token(); bHasName = verify( element, match( T, SourceCodeTokenizer.TT_WORD ), Res.MSG_EXPECTING_NAME_FUNCTION_DEF ); } if( element instanceof IParsedElementWithAtLeastOneDeclaration ) { ((IParsedElementWithAtLeastOneDeclaration)element).setNameOffset( T.getTokenStart(), T._strValue ); } String strFunctionName = T._strValue; // if( strFunctionName == null ) // { // return null; // } maybeEatNonDeclKeyword( bHasName, strFunctionName ); strFunctionName = strFunctionName == null ? "" : strFunctionName; warn( element, !Keyword.isReservedKeyword( strFunctionName ), Res.MSG_IMPROPER_USE_OF_KEYWORD, strFunctionName ); ICompilableType gsClass = getGosuClass(); if( gsClass != null && strFunctionName.equals( gsClass.getRelativeName() ) && gsClass.isEnum() ) { verify( element, Modifier.isPrivate( modifiers.getModifiers() ), Res.MSG_ENUM_CONSTRUCTOR_MUST_BE_PRIVATE ); } List<ITypeVariableDefinitionExpression> typeVarDefs = parseTypeVariableDefs( element, true, null ); // Must create function type and assign it as the type var's enclosing // type *before* we parse the return type (in case it refs function's type vars) IGenericTypeVariable[] typeVars = TypeVariableDefinition.getTypeVars( typeVarDefs ); strFunctionName = !bProperty ? strFunctionName : ('@' + strFunctionName); FunctionType type = new FunctionType( strFunctionName, JavaTypes.pVOID(), null, typeVars ); if( bProperty && !typeVarDefs.isEmpty() ) { verify( element, false, Res.MSG_GENERIC_PROPERTIES_NOT_SUPPORTED ); } try { verify( element, match( null, '(' ), Res.MSG_EXPECTING_LEFTPAREN_FUNCTION_DEF ); ArrayList<ISymbol> params = null; IType[] paramTypes = null; if( !match( null, ')' ) ) { params = parseParameterDeclarationList( element, Modifier.isStatic( modifiers.getModifiers() ), null, bProperty, bGetter, false ); paramTypes = new IType[params.size()]; for( int i = 0; i < params.size(); i++ ) { _symTable.putSymbol( params.get( i ) ); paramTypes[i] = params.get( i ).getType(); } verify( element, match( null, ')' ), Res.MSG_EXPECTING_RIGHTPAREN_FUNCTION_DEF ); } else { verify ( element, ! bProperty || bGetter, Res.MSG_PROPERTY_SET_MUST_HAVE_ONE_PARAMETER ); } TypeLiteral typeLiteral; if( match( null, ":", SourceCodeTokenizer.TT_OPERATOR ) ) { parseTypeLiteral(); typeLiteral = (TypeLiteral)popExpression(); if( bGetter ) { verify( typeLiteral, typeLiteral.getType().getType() != JavaTypes.pVOID(), Res.MSG_VOID_NOT_ALLOWED ); } } else { Token fakeT = new Token(); fakeT._strValue = Keyword.KW_void.toString(); typeLiteral = resolveTypeLiteral( fakeT ); verify( element, !bGetter, Res.MSG_MISSING_PROPERTY_RETURN ); } type.setArgumentTypes( paramTypes ); type.setRetType( typeLiteral.getType().getType() ); type.setScriptPart( getScriptPart() ); DynamicFunctionSymbol dfs = new DynamicFunctionSymbol( _symTable, strFunctionName, type, params, (Statement)null ); dfs.setScriptPart( getScriptPart() ); dfs.setModifierInfo( modifiers ); if( element instanceof FunctionStatement ) { dfs.setDeclFunctionStmt( (FunctionStatement)element ); } verifyFunction( dfs, element ); int iDupIndex = nextIndexOfErrantDuplicateDynamicSymbol( dfs, _dfsDeclByName.get( dfs.getDisplayName() ), true ); if( iDupIndex >= 0 ) { dfs.renameAsErrantDuplicate( iDupIndex ); } putDfsDeclInSetByName( dfs ); return dfs; } finally { for( ITypeVariableDefinitionExpression typeVarDef : typeVarDefs ) { Map<String, ITypeVariableDefinition> typeVarMap = getTypeVariables(); if( typeVarMap.containsValue( typeVarDef ) ) { typeVarMap.remove( ((ITypeVariableDefinition)typeVarDef).getName() ); } } } } finally { _symTable.popScope(); } } boolean maybeEatNonDeclKeyword( boolean bHasName, String strFunctionName ) { return !bHasName && strFunctionName != null && strFunctionName.length() > 0 && !isDeclarationKeyword( strFunctionName ) && match( null, SourceCodeTokenizer.TT_KEYWORD ); } private void verifyFunction( DynamicFunctionSymbol dfs, ParsedElement element ) { boolean bValidOverrideFound = false; for( IFunctionSymbol existing : getDfsDeclsForFunction( dfs.getDisplayName() ) ) { if( existing instanceof DynamicFunctionSymbol ) { DynamicFunctionSymbol dfsExisting = (DynamicFunctionSymbol)existing; // proxies are ignored and names must match exactly (error if they do not?) if( existing.getDisplayName().equals( dfs.getDisplayName() ) ) { // if the parameters match exactly, if( areParametersEquivalent( dfs, dfsExisting ) ) { if( areDFSsInSameNameSpace( dfs, dfsExisting ) ) { IGosuClass owningTypeForDfs = getOwningTypeForDfs( dfsExisting ); if( owningTypeForDfs instanceof IGosuEnhancement ) { if( dfs.isOverride() || owningTypeForDfs == getGosuClass() ) { verify( element, false, Res.MSG_CANNOT_OVERRIDE_FUNCTION_FROM_ENHANCEMENT ); } else { warn( element, false, Res.MSG_MASKING_ENHANCEMENT_METHODS_MAY_BE_CONFUSING ); } } else { boolean bSameButNotInSameClass = !GosuObjectUtil.equals( dfsExisting.getScriptPart(), dfs.getScriptPart() ); if( !verify( element, bSameButNotInSameClass, Res.MSG_FUNCTION_ALREADY_DEFINED, dfs.getMethodSignature(), getScriptPart() ) ) { return; } boolean bClassAndReturnTypesCompatible = !GosuObjectUtil.equals( dfsExisting.getScriptPart(), dfs.getScriptPart() ) && returnTypesCompatible( dfsExisting, dfs ); if( verify( element, bClassAndReturnTypesCompatible, Res.MSG_FUNCTION_CLASH, dfs.getName(), dfs.getScriptPart(), dfsExisting.getName(), dfsExisting.getScriptPart() ) ) { boolean b = !dfsExisting.isFinal() && (getGosuClass() == null || getGosuClass().getSupertype() == null || !getGosuClass().getSupertype().isFinal()); verify( element, b, Res.MSG_CANNOT_OVERRIDE_FINAL, dfsExisting.getName(), dfsExisting.getScriptPart() ); if( verify( element, !dfs.isStatic() || dfsExisting.isStatic(), Res.MSG_STATIC_METHOD_CANNOT_OVERRIDE, dfs.getName(), dfsExisting.getDeclaringTypeInfo().getName() ) ) { if( !dfs.isStatic() && !dfsExisting.isStatic() ) { if( !dfs.isOverride() ) { boolean bIsConstructorName = getGosuClass() != null && getGosuClass().getRelativeName().equals( dfs.getDisplayName() ); warn( element, bIsConstructorName, Res.MSG_MISSING_OVERRIDE_MODIFIER, dfsExisting.getName(), dfsExisting.getScriptPart().getContainingTypeName() ); if( verify( element, !bIsConstructorName, Res.MSG_SUPER_CLASS_METHOD_NAME_SAME_AS_SUBCLASS, dfsExisting.getName() ) ) { // Set the override modifier when the modifier is missing dfs.setOverride( true ); } } verifyNotWeakerAccess( element, dfs, dfsExisting ); verifySameNumberOfFunctionTypeVars( element, dfs, dfsExisting ); dfs.setSuperDfs( dfsExisting ); bValidOverrideFound = true; } } } } } } else { // if the parameters do not match, but reify to the same IR types, it is an error verify( element, !doParametersReifyToSameBytecodeType( dfs, dfsExisting ), Res.MSG_METHOD_REIFIES_TO_SAME_SIGNATURE_AS_ANOTHER_METHOD ); verify( element, !dfs.hasOptionalParameters() && !dfsExisting.hasOptionalParameters(), Res.MSG_OVERLOADING_NOT_ALLOWED_WITH_OPTIONAL_PARAMS ); } } } } if( !bValidOverrideFound ) { verifyOverrideNotOnMethodThatDoesNotExtend( element, dfs ); } verifyNoImplicitPropertyMethodConflicts( element, dfs ); } private void verifySameNumberOfFunctionTypeVars( ParsedElement element, DynamicFunctionSymbol dfs, DynamicFunctionSymbol dfsExisting ) { FunctionType dfsType = (FunctionType)dfs.getType(); FunctionType existingDfsType = (FunctionType)dfsExisting.getType(); IGenericTypeVariable[] typeVars = dfsType.getTypeVariables(); IGenericTypeVariable[] existingTypeVars = existingDfsType.getTypeVariables(); verify( element, existingTypeVars.length == typeVars.length, Res.MSG_OVERRIDING_FUNCTION_MUST_HAVE_SAME_NUMBER_OF_TYPE_VARS, existingTypeVars.length ); } private boolean areDFSsInSameNameSpace( IDynamicSymbol newDfs, IDynamicSymbol existingDfs ) { IGosuClass newType = getOwningTypeForDfs( newDfs ); IType existingType = getOwningTypeForDfs( existingDfs ); if( newType == null ) { return existingType == null; } else if( newType.isAnonymous() || newType.getEnclosingType() != null ) { if( existingType instanceof IGosuEnhancement ) { existingType = ((IGosuEnhancement)existingType).getEnhancedType(); } if( IGosuClass.ProxyUtil.isProxy( existingType ) ) { IType type = IGosuClass.ProxyUtil.getProxiedType( existingType ); return type.isAssignableFrom( newType ) || JavaTypes.IGOSU_OBJECT().equals( type ); } else { return existingType.isAssignableFrom( newType ); } } else { return true; } } private void verifyNoImplicitPropertyMethodConflicts( ParsedElement element, DynamicFunctionSymbol dfs ) { String name = dfs.getDisplayName(); if( name.startsWith( "@" ) ) { String propName = name.substring( 1 ); if( dfs.getArgs().size() == 0 ) { for( IFunctionSymbol func : getDfsDeclsForFunction( "get" + propName ) ) { if( func instanceof DynamicFunctionSymbol ) { DynamicFunctionSymbol existingDfs = (DynamicFunctionSymbol)func; if( areDFSsInSameNameSpace( dfs, existingDfs ) ) { verify( element, existingDfs.getArgs().size() != 0, Res.MSG_PROPERTY_AND_FUNCTION_CONFLICT, existingDfs.getName(), propName ); } } } } else { for( IFunctionSymbol func : getDfsDeclsForFunction( "set" + propName ) ) { if( func instanceof DynamicFunctionSymbol ) { DynamicFunctionSymbol existingDfs = (DynamicFunctionSymbol)func; if( areDFSsInSameNameSpace( dfs, existingDfs ) ) { if( existingDfs.getArgs().size() == 1 ) { IType argType = dfs.getArgs().get( 0 ).getType(); IType existingArgType = existingDfs.getArgs().get( 0 ).getType(); if( argType.equals( existingArgType ) ) { verify( element, false, Res.MSG_PROPERTY_AND_FUNCTION_CONFLICT, existingDfs.getName(), propName ); } if( doTypesReifyToTheSameBytecodeType( argType, existingArgType ) ) { verify( element, false, Res.MSG_PROPERTY_AND_FUNCTION_CONFLICT_UPON_REIFICATION, existingDfs.getName(), propName ); } } } } } } } else { if( name.startsWith( "set" ) && dfs.getArgs().size() == 1 ) { ISymbol symbol = getSymbolTable().getSymbol( name.substring( 3, name.length() ) ); if( symbol instanceof DynamicPropertySymbol ) { DynamicPropertySymbol dps = (DynamicPropertySymbol)symbol; if( areDFSsInSameNameSpace( dfs, dps ) ) { if( dps.getSetterDfs() != null ) { IType argType = dfs.getArgs().get( 0 ).getType(); if( argType.equals( dps.getType() ) ) { verify( element, false, Res.MSG_PROPERTY_AND_FUNCTION_CONFLICT, dfs.getName(), dps.getName() ); } else if( doTypesReifyToTheSameBytecodeType( argType, dps.getType() ) ) { verify( element, false, Res.MSG_PROPERTY_AND_FUNCTION_CONFLICT_UPON_REIFICATION, dfs.getName(), dps.getName() ); } } } } } else if( name.startsWith( "get" ) && dfs.getArgs().size() == 0 ) { ISymbol symbol = getSymbolTable().getSymbol( name.substring( 3, name.length() ) ); if( symbol instanceof DynamicPropertySymbol ) { DynamicPropertySymbol dps = (DynamicPropertySymbol)symbol; if( areDFSsInSameNameSpace( dfs, dps ) ) { verify( element, dps.getGetterDfs() == null, Res.MSG_PROPERTY_AND_FUNCTION_CONFLICT, dfs.getName(), dps.getName() ); } } } } } private boolean returnTypesCompatible( DynamicFunctionSymbol dfsExisting, DynamicFunctionSymbol dfs ) { IType existingReturnType = maybeResolveFunctionTypeVars( dfsExisting, dfsExisting.getReturnType() ); IType overrideReturnType = maybeResolveFunctionTypeVars( dfs, dfs.getReturnType() ); if( existingReturnType.isAssignableFrom( overrideReturnType ) ) { return true; } else { return dfs.getReturnType() instanceof ErrorType; } } public boolean areParametersEquivalent(IDynamicFunctionSymbol dfs1, IDynamicFunctionSymbol dfs2) { IType[] args = ((FunctionType)dfs1.getType()).getParameterTypes(); IType[] toArgs = ((FunctionType)dfs2.getType()).getParameterTypes(); if( args == null || args.length == 0 ) { return toArgs == null || toArgs.length == 0; } if( toArgs == null ) { return false; } if( args.length != toArgs.length ) { return false; } for( int i = 0; i < args.length; i++ ) { IType argType = maybeResolveFunctionTypeVars( dfs1, args[i] ); IType toArgType = maybeResolveFunctionTypeVars( dfs2, toArgs[i] ); if( !argType.equals( toArgType ) ) { // Note we use equals() check instead of == to handle non-loadable types e.g., TypeVariableTypes return false; } } return true; } private IType maybeResolveFunctionTypeVars( IDynamicFunctionSymbol dfs, IType type ) { if (TypeSystem.isDeleted(type)) { return TypeSystem.getErrorType(); } FunctionType iType = (FunctionType)dfs.getType(); ArrayList<IType> functionTypeVars = new ArrayList<IType>(); for( IGenericTypeVariable typeVariable : iType.getTypeVariables() ) { functionTypeVars.add( typeVariable.getTypeVariableDefinition().getType() ); } return TypeLord.boundTypes( type, functionTypeVars ); } public boolean doParametersReifyToSameBytecodeType( IDynamicFunctionSymbol dfs, IDynamicFunctionSymbol dfsExisting ) { IType[] toArgs = ((FunctionType) dfsExisting.getType()).getParameterTypes(); IType[] args = ((FunctionType)dfs.getType()).getParameterTypes(); if( args == null || args.length == 0 ) { return toArgs == null || toArgs.length == 0; } if( toArgs == null ) { return false; } if( args.length != toArgs.length ) { return false; } for( int i = 0; i < args.length; i++ ) { if( !doTypesReifyToTheSameBytecodeType( toArgs[i], args[i] ) ) { return false; } } return true; } private boolean doTypesReifyToTheSameBytecodeType( IType toArg, IType arg ) { IRType argType = IRTypeResolver.getDescriptor( arg ); argType = eraseIfStructuralType( argType ); IRType toArgType = IRTypeResolver.getDescriptor( toArg ); toArgType = eraseIfStructuralType( toArgType ); return argType.equals( toArgType ); } private IRType eraseIfStructuralType( IRType argType ) { return argType.isStructural() ? IRTypeResolver.getDescriptor( Object.class ) : argType; } private IGosuClass getOwningTypeForDfs( IDynamicSymbol dfs ) { if( dfs.getScriptPart() != null && dfs.getScriptPart().getContainingType() instanceof IGosuClass ) { return (IGosuClass)dfs.getScriptPart().getContainingType(); } else { return null; } } private void verifyNotWeakerAccess( ParsedElement element, DynamicFunctionSymbol dfs, DynamicFunctionSymbol dfsExisting ) { if( dfsExisting.isPublic() ) { verify( element, dfs.isPublic(), Res.MSG_ATTEMPTING_TO_ASSIGN_WEAKER_ACCESS_PRIVILEGES, dfs.getName(), dfs.getScriptPart(), dfsExisting.getName(), dfsExisting.getScriptPart() ); } else if( dfsExisting.isProtected() ) { verify( element, dfs.isPublic() || dfs.isProtected(), Res.MSG_ATTEMPTING_TO_ASSIGN_WEAKER_ACCESS_PRIVILEGES, dfs.getName(), dfs.getScriptPart(), dfsExisting.getName(), dfsExisting.getScriptPart() ); } else if( dfsExisting.isInternal() ) { verify( element, dfs.isPublic() || dfs.isProtected() || dfs.isInternal(), Res.MSG_ATTEMPTING_TO_ASSIGN_WEAKER_ACCESS_PRIVILEGES, dfs.getName(), dfs.getScriptPart(), dfsExisting.getName(), dfsExisting.getScriptPart() ); } } //------------------------------------------------------------------------------ public ArrayList<ISymbol> parseParameterDeclarationList( IParsedElement element, boolean bStatic, List<IType> inferredArgumentTypes ) { return parseParameterDeclarationList( element, bStatic, inferredArgumentTypes, false, false, false ); } public ArrayList<ISymbol> parseParameterDeclarationList( IParsedElement element, boolean bStatic, List<IType> inferredArgumentTypes, boolean bProperty, boolean bGetter, boolean bEmpty ) { ArrayList<ISymbol> params = new ArrayList<ISymbol>(); Token T = new Token(); int iParamPos = -1; boolean bOptionalParamsStarted = false; int iOffsetList = getTokenizer().getTokenStart(); int iLineNumList = getTokenizer().getLineNumber(); int iColumnList = getTokenizer().getTokenColumn(); do { iParamPos++; int iOffsetParam = getTokenizer().getTokenStart(); int iLineNumParam = getTokenizer().getLineNumber(); int iColumnParam = getTokenizer().getTokenColumn(); List<IGosuAnnotation> annotations = parseLocalAnnotations( Collections.<IGosuAnnotation>emptyList() ); boolean bFinal = match( T, Keyword.KW_final ); int iOffsetArgIdentifier = getTokenizer().getTokenStart(); int iColumnArgIdentifier = getTokenizer().getTokenColumn(); int iLineArgIdentifier = getTokenizer().getLineNumber(); IToken tokenBeforeParam = getTokenizer().getCurrentToken(); boolean bMatchColonWithoutName = false; if( bEmpty || !verify( (ParsedElement)element, match( T, SourceCodeTokenizer.TT_WORD ), Res.MSG_EXPECTING_ARGS, "" ) ) { bMatchColonWithoutName = match( null, ":", SourceCodeTokenizer.TT_OPERATOR, true ); if( !bMatchColonWithoutName ) { break; } else { T._strValue = ""; } } ModifierListClause e = new ModifierListClause(); pushExpression( e ); boolean bZeroLength = tokenBeforeParam.getTokenStart() <= iOffsetParam; setLocation( iOffsetParam, iLineNumParam, iColumnParam, bZeroLength, true ); popExpression(); String strArgIdentifier = T._strValue; ParameterDeclaration parameterIdentifier = new ParameterDeclaration( strArgIdentifier ); addNameInDeclaration( strArgIdentifier, iOffsetArgIdentifier, iLineArgIdentifier, iColumnArgIdentifier, strArgIdentifier.length() > 0 || bMatchColonWithoutName ); ISymbol existingSymbol = _symTable.getSymbol( strArgIdentifier ); if( existingSymbol == null ) { for( ISymbol symbol : params ) { if( symbol.getName().equals( strArgIdentifier ) ) { existingSymbol = symbol; } } } boolean bSymbolConflict = existingSymbol != null; if( bStatic && existingSymbol instanceof DynamicSymbol ) { bSymbolConflict = existingSymbol.isStatic(); } verify( parameterIdentifier, !bSymbolConflict, Res.MSG_VARIABLE_ALREADY_DEFINED, strArgIdentifier ); verify( parameterIdentifier, ! bProperty || bGetter || iParamPos == 0, Res.MSG_PROPERTY_SET_MUST_HAVE_ONE_PARAMETER ); IType argType; Expression defExpr = null; boolean bColonFound = match( null, ":", SourceCodeTokenizer.TT_OPERATOR ); boolean bEqualsFound = match( null, "=", SourceCodeTokenizer.TT_OPERATOR ); boolean bParenFound = match( null, null, '(', true ); if( inferredArgumentTypes != null && !bColonFound && !bEqualsFound && !bParenFound ) { if( inferredArgumentTypes.size() > iParamPos ) { argType = inferredArgumentTypes.get(iParamPos); } else { argType = ErrorType.getInstance(); } } else { if( bParenFound ) { parseBlockLiteral(); } else { verify( parameterIdentifier, bColonFound || bEqualsFound, Res.MSG_EXPECTING_TYPE_FUNCTION_DEF ); if( bEqualsFound ) { parseExpression(); } else { parseTypeLiteral(); } } Expression expr = popExpression(); if( bEqualsFound ) { defExpr = expr; argType = defExpr.hasParseExceptions() ? JavaTypes.pVOID() : expr instanceof NullExpression ? JavaTypes.OBJECT() : expr.getType(); } else { argType = ((TypeLiteral)expr).getType().getType(); } verify( expr, bEqualsFound || argType != JavaTypes.pVOID(), Res.MSG_VOID_NOT_ALLOWED ); if( bColonFound && match( null, "=", SourceCodeTokenizer.TT_OPERATOR ) ) { int iOffsetDef = getTokenizer().getTokenStart(); int iLineNumDef = getTokenizer().getLineNumber(); int iColumnDef = getTokenizer().getTokenColumn(); parseExpression( argType ); defExpr = popExpression(); defExpr = possiblyWrapWithImplicitCoercion( defExpr, argType ); pushExpression( defExpr ); setLocation( iOffsetDef, iLineNumDef, iColumnDef ); popExpression(); } } if( strArgIdentifier != null ) { Symbol symbol = new TypedSymbol( strArgIdentifier, argType, _symTable, null, SymbolType.PARAMETER_DECLARATION ); if( defExpr != null ) { verifyComparable( argType, defExpr, true ); verify( defExpr, defExpr.isCompileTimeConstant(), Res.MSG_COMPILE_TIME_CONSTANT_REQUIRED ); verify( (ParsedElement)element, argType != JavaTypes.pVOID() || !defExpr.hasParseExceptions(), Res.MSG_PARAM_TYPE_CANT_BE_INFERRED_FROM_LATE_BOUND_EXPRESSION ); symbol.setDefaultValueExpression( defExpr ); bOptionalParamsStarted = true; } else { verify( parameterIdentifier, !bOptionalParamsStarted, Res.MSG_EXPECTING_DEFAULT_VALUE ); } symbol.setFinal( bFinal ); symbol.getModifierInfo().setAnnotations( annotations ); params.add( symbol ); parameterIdentifier.setType( argType ); pushExpression( parameterIdentifier ); setLocation( iOffsetParam, iLineNumParam, iColumnParam, true ); popExpression(); if( getGosuClass() instanceof IGosuClassInternal && ((IGosuClassInternal)getGosuClass()).isCompilingDefinitions() ) { verifyModifiers( parameterIdentifier, symbol.getModifierInfo(), UsageTarget.ParameterTarget ); } } } while( match( null, ',' ) ); ParameterListClause e = new ParameterListClause(); pushExpression( e ); boolean bZeroLength = getTokenizer().getTokenStart() <= iOffsetList; setLocation( bZeroLength ? getTokenizer().getPriorToken().getTokenEnd() : iOffsetList, iLineNumList, iColumnList, bZeroLength, true ); popExpression(); params.trimToSize(); return params; } private List<IGosuAnnotation> parseLocalAnnotations( List<IGosuAnnotation> annotations ) { while( match( null, null, '@', true ) ) { if( getOwner() == null ) { match( null, '@' ); throw new IllegalStateException( "Found null owning parser" ); } if( annotations.isEmpty() ) { annotations = new ArrayList<IGosuAnnotation>( 2 ); } parseAnnotation( annotations ); } return annotations; } //------------------------------------------------------------------------------ // type-variables // < <type-variable-list> > // List<ITypeVariableDefinitionExpression> parseTypeVariableDefs( ParsedElement parsedElem, boolean bFunction, List<TypeVariableDefinitionImpl> typeVarDefListFromDecl ) { int iOffset = getTokenizer().getTokenStart(); int iLineNum = getTokenizer().getLineNumber(); int iColumn = getTokenizer().getTokenColumn(); if( !match( null, "<", SourceCodeTokenizer.TT_OPERATOR ) ) { return Collections.emptyList(); } List<ITypeVariableDefinitionExpression> typeVarDefList = parseTypeVariableDefList( parsedElem, bFunction, typeVarDefListFromDecl ); verify( parsedElem, match( null, ">", SourceCodeTokenizer.TT_OPERATOR ), Res.MSG_EXPECTING_CLOSING_ANGLE_BRACKET_FOR_TYPE_VAR_LIST ); // For editors only TypeVariableListClause e = new TypeVariableListClause( typeVarDefList ); pushExpression( e ); setLocation( iOffset, iLineNum, iColumn, true ); popExpression(); return typeVarDefList; } //------------------------------------------------------------------------------ // type-variable-list // <type-variable> // <type-variable-list> , <type-variable> // List<ITypeVariableDefinitionExpression> parseTypeVariableDefList( ParsedElement parsedElem, boolean bForFunction, List<TypeVariableDefinitionImpl> typeVarDefListFromDecl ) { List<ITypeVariableDefinitionExpression> typeVarDefList = new ArrayList<ITypeVariableDefinitionExpression>(); int i = 0; do { TypeVariableDefinition typeVarDef = new TypeVariableDefinition( getEnclosingType(), bForFunction ); if( typeVarDefListFromDecl != null && !typeVarDefListFromDecl.isEmpty() ) { typeVarDef.setTypeVarDef( typeVarDefListFromDecl.get( i++ ) ); } parseTypeVariableDefinition( parsedElem, typeVarDef ); typeVarDef = (TypeVariableDefinition)popExpression(); for( ITypeVariableDefinition csr : _typeVarsByName.values() ) { if( !verify( typeVarDef, !csr.getName().equals( typeVarDef.getName() ) || ((TypeVariableDefinition)csr).getLocation().getExtent() == typeVarDef.getLocation().getExtent(), Res.MSG_VARIABLE_ALREADY_DEFINED, typeVarDef.getName() ) ) { break; } } typeVarDefList.add( typeVarDef ); } while( match( null, ',' ) ); return typeVarDefList; } //------------------------------------------------------------------------------ // type-variable // <identifier> [extends <type-literal>] // void parseTypeVariableDefinition( ParsedElement parsedElem, TypeVariableDefinition typeVarDef ) { int iOffset = getTokenizer().getTokenStart(); int iLineNum = getTokenizer().getLineNumber(); int iColumn = getTokenizer().getTokenColumn(); if( _parseTypeVariableDefinition( parsedElem, typeVarDef ) ) { setLocation( iOffset, iLineNum, iColumn ); } } boolean _parseTypeVariableDefinition( ParsedElement parsedElem, TypeVariableDefinition typeVarDef ) { Token T = new Token(); if( verify( parsedElem, match( T, SourceCodeTokenizer.TT_WORD ), Res.MSG_EXPECTING_IDENTIFIER_EXISTS ) ) { typeVarDef.setName( T._strValue ); Map<String, ITypeVariableDefinition> typeVarMap = getTypeVariables(); if( !typeVarMap.containsKey( typeVarDef.getName() ) ) { getTypeVariables().put( typeVarDef.getName(), typeVarDef ); } int iOffset = getTokenizer().getTokenStart(); int iLineNum = getTokenizer().getLineNumber(); int iColumn = getTokenizer().getTokenColumn(); IType boundingType = null; boolean bExtends; if( bExtends = match( null, Keyword.KW_extends ) ) { typeVarDef.setBoundingType( PENDING_BOUNDING_TYPE ); parseTypeLiteral(); boxTypeLiteralsType( (TypeLiteral)peekExpression() ); TypeLiteral typeLiteral = (TypeLiteral)popExpression(); boundingType = typeLiteral.getType().getType(); if( verify( typeLiteral, boundingType != typeVarDef.getType(), Res.MSG_CYCLIC_INHERITANCE, boundingType.getRelativeName() ) ) { typeVarDef.setBoundingType( boundingType ); } } // For editors only TypeVariableExtendsListClause e = new TypeVariableExtendsListClause( boundingType ); pushExpression( e ); // Note for a zero-length extends clause we must shift it to the left one char to avoid having it follow the enclosing TypeVariableListClause setLocation( iOffset - (bExtends ? 0 : 1), iLineNum, iColumn, !bExtends, true ); popExpression(); } else { typeVarDef.setName( "" ); verify( typeVarDef, false, Res.MSG_ERRANT_TYPE_VAR ); Map<String, ITypeVariableDefinition> typeVarMap = getTypeVariables(); if( !typeVarMap.containsKey( typeVarDef.getName() ) ) { getTypeVariables().put( typeVarDef.getName(), typeVarDef ); } pushExpression( typeVarDef ); // Set the location to zero length at the end of the last token IToken priorT = getTokenizer().getPriorToken(); setLocation( priorT.getTokenEnd(), priorT.getLine(), priorT.getTokenColumn() ); return false; } pushExpression( typeVarDef ); return true; } private IType getEnclosingType() { if( isParsingFunction() ) { return peekParsingFunction(); } return getScriptPart() == null ? null : getScriptPart().getContainingType(); } //------------------------------------------------------------------------------ @Override protected void pushExpression( Expression e ) { assert e != null; maybeVerifyDoubleLiterals( e ); _stack.push( e ); } private void maybeVerifyDoubleLiterals( Expression e ) { if( e instanceof AdditiveExpression || e instanceof MultiplicativeExpression ) { IArithmeticExpression ae = (IArithmeticExpression) e; maybeVerifyDoubleLiteral( ae.getLHS(), ae.getRHS() ); maybeVerifyDoubleLiteral( ae.getRHS(), ae.getLHS() ); } } private void maybeVerifyDoubleLiteral( IExpression oneSide, IExpression otherSide ) { if( (JavaTypes.BIG_DECIMAL().equals( oneSide.getType() ) || JavaTypes.BIG_INTEGER().equals( oneSide.getType() )) && JavaTypes.pDOUBLE().equals( otherSide.getType() )) { if( otherSide instanceof UnaryExpression ) { otherSide = ((UnaryExpression)otherSide).getExpression(); } if( otherSide instanceof NumericLiteral ) { NumericLiteral nl = (NumericLiteral)otherSide; boolean repsAreIdentical = new BigDecimal( nl.getStrValue() ).equals( CommonServices.getCoercionManager().makeBigDecimalFrom( nl.getValue() ) ); verify( (ParsedElement)otherSide, repsAreIdentical, Res.MSG_LOSS_OF_PRECISION_IN_NUMERIC_LITERAL, nl.getStrValue() + "bd" ); } } } //------------------------------------------------------------------------------ @Override public Expression popExpression() { return (Expression)_stack.pop(); } @Override public String parseDotPathWord() { Token token = new Token(); if( match(token, ISourceCodeTokenizer.TT_WORD) ) { parseDotPathWord( token ); return token.getStringValue(); } else { return null; } } @Override public void setTokenizer( ISourceCodeTokenizer tokenizer ) { _tokenizer = (SourceCodeTokenizer)tokenizer; } //------------------------------------------------------------------------------ @Override protected Expression peekExpression() { IParsedElement elem = peekParsedElement(); return elem instanceof Expression ? (Expression)elem : null; } //------------------------------------------------------------------------------ protected ParsedElement peekParsedElement() { if( _stack.isEmpty() ) { return null; } return _stack.peek(); } //------------------------------------------------------------------------------ @Override protected void pushStatement( Statement stmt ) { _stack.push( stmt ); } //------------------------------------------------------------------------------ @Override protected Statement popStatement() { ParsedElement stmt = _stack.pop(); return (Statement)stmt; } @Override protected Statement peekStatement() { IParsedElement elem = peekParsedElement(); return elem instanceof Statement ? (Statement)elem : null; } protected void pushDynamicFunctionSymbol( DynamicFunctionSymbol stmt ) { _stackDFS.push( stmt ); } protected DynamicFunctionSymbol popDynamicFunctionSymbol() { return _stackDFS.pop(); } protected DynamicFunctionSymbol peekDynamicFunctionSymbol() { if( _stackDFS.isEmpty() ) { return null; } return _stackDFS.peek(); } protected void clearDfsStack() { _stackDFS.clear(); } public void putDfsDeclsInTable( ISymbolTable table ) { if( table == null ) { return; } for( Iterator iterator = table.getSymbols().values().iterator(); iterator.hasNext(); ) { ISymbol symbol = (ISymbol)iterator.next(); if( symbol instanceof DynamicFunctionSymbol ) { putDfsDeclInSetByName( (DynamicFunctionSymbol)symbol ); } } } public void putDfsDeclInSetByName( IDynamicFunctionSymbol dfs ) { String displayName = dfs.getDisplayName(); Set<IFunctionSymbol> dfsDecls = _dfsDeclByName.get( displayName ); if( dfsDecls == null ) { dfsDecls = new HashSet<IFunctionSymbol>(); try { _dfsDeclByName.put( displayName, dfsDecls ); } catch( Exception e ) { throw new RuntimeException( "Map type: " + _dfsDeclByName.getClass().getName(), e ); } } if( !dfsDecls.add(dfs) ) { // Replace old dfsDecls.remove( dfs ); boolean bAdd = dfsDecls.add(dfs); assert bAdd; } } public int nextIndexOfErrantDuplicateDynamicSymbol( IDynamicSymbol ds, Collection<? extends ISymbol> symbols, boolean bCheckContains ) { if( symbols == null ) { return -1; } int iMax = -1; if( !bCheckContains || symbolIn( ds, symbols ) ) { for( ISymbol csr : symbols ) { boolean bInjected = csr instanceof IInjectedSymbol; if( !(csr instanceof IDynamicSymbol) && !bInjected ) { continue; } String strName = csr.getName(); if( csr.getGosuClass() == ds.getGosuClass() || bInjected ) { if( iMax < 0 && strName.equals( ds.getName() ) ) { iMax = 0; } else if( strName.toLowerCase().contains( "_duplicate_" + ds.getName().toLowerCase() ) ) { int iIndex = Integer.parseInt( strName.substring( 0, strName.indexOf( '_' ) ) ); if( iIndex > iMax ) { iMax = iIndex; } } } } } return iMax < 0 ? iMax : iMax+1; } private boolean symbolIn( IDynamicSymbol ds, Collection<? extends ISymbol> symbols ) { for( ISymbol s : symbols ) { if( s.getName().equals( ds.getName() ) ) { return true; } } return false; } public void setDfsDeclInSetByName( Map<String, Set<IFunctionSymbol>> dfsDecl ) { _dfsDeclByName = dfsDecl; } protected void newDfsDeclInSetByName() { _dfsDeclByName = new HashMap<String, Set<IFunctionSymbol>>(); } public Map<String, Set<IFunctionSymbol>> getDfsDecls() { return _dfsDeclByName; } protected List<IFunctionType> getFunctionTypesForName( String strFunctionName ) { List<IFunctionSymbol> list = getDfsDeclsForFunction( strFunctionName ); List<IFunctionType> listOfTypes = new ArrayList<IFunctionType>( list.size() ); for (IFunctionSymbol dfs : list) { listOfTypes.add((FunctionType) dfs.getType()); } return listOfTypes; } protected TypeLiteral resolveTypeLiteral( Token T ) { return resolveTypeLiteral( T, true, false ); } protected TypeLiteral resolveTypeLiteral( Token T, boolean bRelative, boolean bInterface ) { String strTypeName = T._strValue == null ? "" : T._strValue; return resolveTypeLiteral( strTypeName, bRelative, bInterface ); } protected List<IFunctionSymbol> getDfsDeclsForFunction( String strFunctionName ) { Set<IFunctionSymbol> setOfDfsDecls = _dfsDeclByName.get( strFunctionName ); return setOfDfsDecls == null ? Collections.<IFunctionSymbol>emptyList() : new ArrayList<IFunctionSymbol>(setOfDfsDecls); } /** * Resolves the type literal given by strTypeName. If parentType is non null then strTypeName is assumed relative to * the given parent. * @param strTypeName */ public TypeLiteral resolveTypeLiteral( String strTypeName ) { return resolveTypeLiteral( strTypeName, true, false ); } public TypeLiteral resolveTypeLiteral( String strTypeName, boolean bRelative, boolean bInterface ) { int iArrayDims = 0; if( strTypeName.length() > 0 && strTypeName.charAt( strTypeName.length()-1 ) == ']' ) { int iFirstBracketIndex = strTypeName.indexOf( '[' ); strTypeName = strTypeName.substring( 0, iFirstBracketIndex ); iArrayDims = (strTypeName.length() - iFirstBracketIndex)/2; } IType intrType; boolean bClassTypeVar = false; ITypeVariableDefinition typeVarDef = getTypeVarDef( strTypeName ); if( typeVarDef != null ) { bClassTypeVar = !typeVarDef.getType().isFunctionStatement(); intrType = typeVarDef.getType(); if( intrType == null ) { intrType = resolveInnerClassByRelativeName( strTypeName ); } } else { intrType = resolveInnerClassByRelativeName( strTypeName ); if( intrType == null ) { intrType = resolveTypeName( strTypeName, bRelative ); } } if( intrType == null ) { intrType = ErrorType.getInstance( strTypeName ); } IType finalType = intrType; for( int i = 0; i < iArrayDims; i++ ) { finalType = finalType.getArrayType(); } if (TypeSystem.isDeleted(finalType)) { finalType = TypeSystem.getErrorType(); } TypeLiteral typeLiteral = bInterface ? new InterfaceTypeLiteral( MetaType.getLiteral( finalType ), _ignoreTypeDeprecation > 0 ) : new TypeLiteral( MetaType.getLiteral( finalType ), _ignoreTypeDeprecation > 0 ); verify( typeLiteral, !bClassTypeVar || !isParsingStaticFeature() || isParsingConstructor(), Res.MSG_CANNOT_REFERENCE_CLASS_TYPE_VAR_IN_STATIC_CONTEXT ); if( verify( typeLiteral, !(intrType instanceof ErrorType) || IErrorType.NAME.equals( strTypeName ), Res.MSG_INVALID_TYPE, strTypeName ) ) { verifyCase( typeLiteral, strTypeName, intrType.getName(), Res.MSG_TYPE_CASE_MISMATCH, true ); } return typeLiteral; } private IType resolveTypeName( String strTypeName, boolean bRelative ) { return resolveTypeName_Cache( strTypeName, bRelative ); // return resolveTypeName_NoCache( strTypeName, bRelative ); } private IType resolveTypeName_NoCache( String strTypeName, boolean bRelative ) { IType type; if( bRelative ) { type = TypeLoaderAccess.instance().getTypeByRelativeNameIfValid_NoGenerics(strTypeName, getTypeUsesMap()); } else { type = TypeLoaderAccess.instance().getByFullNameIfValid( strTypeName ); } return type; } private IType resolveTypeName_Cache( String strTypeName, boolean bRelative ) { IType type; Map<String, IType> cache = _typeCache.get( null ); if( cache == null ) { cache = new HashMap<String, IType>(); _typeCache.put( getScriptPart(), cache ); type = null; } else { type = cache.get( strTypeName ); } if( type == null ) { type = resolveTypeName_NoCache( strTypeName, bRelative ); if( type == null ) { type = notfound; } cache.put( strTypeName, type ); } return type == null || type == notfound ? null : type; } private ITypeVariableDefinition getTypeVarDef( String strTypeName ) { ITypeVariableDefinition typeVarDef = getTypeVariables().get( strTypeName ); if( typeVarDef != null && getGosuClass() != null && getGosuClass().isStatic() && TypeLord.encloses( typeVarDef.getEnclosingType(), getGosuClass() ) ) { // Static inner class cannot access enclosing type vars typeVarDef = null; } if( typeVarDef == null ) { typeVarDef = getEnclosingTypeVars().get( strTypeName ); } return typeVarDef; } //## do we still need this method since inner class parsing re-uses the enclosing class' owner/parser private Map<String, TypeVariableDefinition> getEnclosingTypeVars() { ICompilableType gsClass = getGosuClass(); if( gsClass == null ) { return Collections.emptyMap(); } Map<String, TypeVariableDefinition> mapTypeVarDefByName = new HashMap<String, TypeVariableDefinition>( 2 ); while( !gsClass.isStatic() && gsClass.getEnclosingType() != null ) { // Note we don't resolve type vars defined in outer for static inner classes. // This is because we do not maintain separate inner classes for each // parameterization of a generic outer class ICompilableType enclosingType = gsClass.getEnclosingType(); for( IGenericTypeVariable gtv : enclosingType.getGenericTypeVariables() ) { Map<String, ITypeVariableDefinition> typeVarMap = getTypeVariables(); if( !typeVarMap.containsKey( gtv.getTypeVariableDefinition().getName() ) ) { getTypeVariables().put( gtv.getName(), gtv.getTypeVariableDefinition() ); } } gsClass = enclosingType; } return mapTypeVarDefByName; } private IType resolveInnerClassByRelativeName( String strTypeName ) { ICompilableType gsClass = getGosuClass(); if( gsClass == null ) { return null; } return gsClass.resolveRelativeInnerClass( strTypeName, false ); } public Map<String, ITypeVariableDefinition> getTypeVariables() { return _typeVarsByName; } public IGosuClassInternal parseClass( String strQualifiedClassName, ISourceFileHandle sourceFile, boolean bThrowOnWarnings, boolean bFullyCompile ) throws ParseResultsException { GosuClassTypeLoader classLoader; if (!CommonServices.getPlatformHelper().isInIDE()) { classLoader = GosuClassTypeLoader.getDefaultClassLoader(TypeSystem.getGlobalModule()); } else { IFile file = sourceFile.getFile(); IModule module = TypeSystem.getExecutionEnvironment().getModule(file); if (module == null) { // these are files outside of the typesystem (i.e. not in any source root) classLoader = GosuClassTypeLoader.getDefaultClassLoader(TypeSystem.getGlobalModule()); } else { classLoader = module.getModuleTypeLoader().getTypeLoader(GosuClassTypeLoader.class); } } IGosuClassInternal gsClass = (IGosuClassInternal)classLoader.makeNewClass(sourceFile, _symTable); gsClass.setEditorParser(this); gsClass.setCreateEditorParser(isEditorParser()); try { if( bFullyCompile ) { gsClass.compileDefinitionsIfNeeded( true ); } else { gsClass.compileDeclarationsIfNeeded(); } } catch( ErrantGosuClassException e ) { //ignore } //noinspection ThrowableResultOfMethodCallIgnored if( gsClass.getParseResultsException() != null ) { //noinspection ThrowableResultOfMethodCallIgnored if( gsClass.getParseResultsException().hasParseExceptions() || bThrowOnWarnings ) { throw gsClass.getParseResultsException(); } } return gsClass; } public IFunctionType getFunctionType( IType classBean, String functionName, Expression[] eArgs, List<IFunctionType> listAllMatchingMethods, GosuParser parser, boolean bMatchParamTypes ) throws ParseException { if( classBean == null ) { throw new ParseException( parser == null ? null : parser.makeFullParserState(), Res.MSG_BEAN_CLASS_IS_NULL ); } if( functionName == null ) { throw new ParseException( parser == null ? null : parser.makeFullParserState(), Res.MSG_BEAN_MEMBER_PATH_IS_NULL ); } ITypeInfo beanInfo = classBean.getTypeInfo(); if( beanInfo == null ) { throw new ParseException( parser == null ? null : parser.makeFullParserState(), Res.MSG_NO_EXPLICIT_TYPE_INFO_FOUND, classBean.getName() ); } if( ErrorType.shouldHandleAsErrorType( classBean ) ) { return ErrorType.getInstance().getErrorTypeFunctionType( eArgs, functionName, listAllMatchingMethods ); } boolean bFoundMethodWithName = false; MethodList methods = BeanAccess.getMethods( beanInfo, getGosuClass() == null ? JavaTypes.OBJECT() : getGosuClass() ); if( methods != null ) { DynamicArray<? extends IMethodInfo> theMethods = methods.getMethods(functionName); for (int i = 0; i < theMethods.size; i++) { IMethodInfo method = (IMethodInfo) theMethods.data[i]; if( BeanAccess.isDescriptorHidden( method ) ) { continue; } bFoundMethodWithName = true; if( !bMatchParamTypes ) { return new FunctionType(method); } IParameterInfo[] paramTypes = method.getParameters(); if( eArgs == null || paramTypes.length == eArgs.length ) { if( listAllMatchingMethods == null ) { return new FunctionType(method); } listAllMatchingMethods.add(new FunctionType(method)); } } } if( listAllMatchingMethods != null && listAllMatchingMethods.size() > 0 ) { return listAllMatchingMethods.get( 0 ); } if( bFoundMethodWithName ) { throw new ParseException( parser == null ? null : parser.makeFullParserState(), Res.MSG_WRONG_NUMBER_OF_ARGS_FOR_METHOD_ON_CLASS, functionName, TypeSystem.getUnqualifiedClassName( classBean ) ); } else { checkForStaticMethod( classBean, eArgs, functionName, parser ); if( isDynamic( classBean ) ) { IType[] params = null; if( eArgs != null ) { params = new IType[eArgs.length]; for( int i = 0; i < eArgs.length; i++ ) { params[i] = eArgs[i].getType(); } } IMethodInfo mi = classBean.getTypeInfo().getMethod( functionName, params ); if( mi != null ) { FunctionType funcType = new FunctionType( mi ); if( listAllMatchingMethods != null ) { listAllMatchingMethods.add( funcType ); } return funcType; } } throw new ParseException( parser == null ? null : parser.makeFullParserState(), Res.MSG_NO_METHOD_DESCRIPTOR_FOUND_FOR_METHOD, functionName, TypeSystem.getUnqualifiedClassName( classBean ) ); } } private void checkForStaticMethod( IType classBean, Expression[] eArgs, String strMethod, GosuParser parserState ) throws ParseException { if( classBean instanceof MetaType) { IType referredType = ((MetaType)classBean).getType(); IType[] paramTypes = new IType[eArgs == null ? 0 : eArgs.length]; for( int i = 0; i < paramTypes.length; i++ ) { paramTypes[i] = eArgs[i].getType(); } IMethodInfo mi = referredType.getTypeInfo().getCallableMethod( strMethod, paramTypes ); if( mi != null && !mi.isStatic() ) { throw new ParseException( parserState == null ? null : parserState.makeFullParserState(), Res.MSG_METHOD_IS_NOT_STATIC, strMethod, TypeSystem.getUnqualifiedClassName( classBean ) ); } } else { IType referredType = MetaType.get( classBean ); IType[] paramTypes = new IType[eArgs == null ? 0 : eArgs.length]; for( int i = 0; i < paramTypes.length; i++ ) { paramTypes[i] = eArgs[i].getType(); } IMethodInfo mi = referredType.getTypeInfo().getCallableMethod( strMethod, paramTypes ); if( mi != null && !mi.isStatic() ) { throw new ParseException( parserState == null ? null : parserState.makeFullParserState(), Res.MSG_METHOD_IS_STATIC, strMethod, TypeSystem.getUnqualifiedClassName( classBean ) ); } } } private IInvocableType inferFunctionType( IInvocableType funcType, List<Expression> eArgs ) { if( funcType instanceof IFunctionType && funcType.isGenericType() ) { IType[] argTypes = new IType[eArgs.size()]; for( int i = 0; i < eArgs.size(); i++ ) { argTypes[i] = eArgs.get( i ).getType(); } for( int i = 0; i < funcType.getParameterTypes().length; i++ ) { IType paramType = funcType.getParameterTypes()[i]; if( i < argTypes.length ) { IType argType = argTypes[i]; IType boundArgType = TypeLord.boundTypes( paramType, getCurrentlyInferringTypes() ); ICoercer coercer = CommonServices.getCoercionManager().resolveCoercerStatically( boundArgType, argType ); if( coercer instanceof IResolvingCoercer ) { argTypes[i] = ((IResolvingCoercer)coercer).resolveType( paramType, argType ); } } } return ((IFunctionType) funcType).inferParameterizedTypeFromArgTypesAndContextType( argTypes, getCurrentContextType()); } else { return funcType; } } /** * Using some simple pattern matching, get a potential property name from a * method name at the end of an access list. * <p/> * Patterns:<br> * <code>get</code>>mixed-case-name< * <code>is</code>>mixed-case-name< */ private static final String[] METHOD_PREFIX_LIST = {"get", "is"}; private String getPropertyNameFromMethodName( String strMethod ) { if( strMethod == null || strMethod.length() == 0 ) { return null; } for( String strPrefix : METHOD_PREFIX_LIST ) { String strProperty = getPropertyNameFromMethodName( strPrefix, strMethod ); if( strProperty != null ) { return strProperty; } } return null; } private static final String[] METHOD_PREFIX_LIST_WITH_SETTER = {"get", "is", "set" }; private String getPropertyNameFromMethodNameIncludingSetter( String strMethod ) { if( strMethod == null || strMethod.length() == 0 ) { return null; } for( String strPrefix : METHOD_PREFIX_LIST_WITH_SETTER ) { String strProperty = getPropertyNameFromMethodName( strPrefix, strMethod ); if( strProperty != null ) { return strProperty; } } return null; } private String getPropertyNameFromMethodName( String strPrefix, String strMethod ) { int iPropertyOffset = strPrefix.length(); if( strMethod.startsWith( strPrefix ) && strMethod.length() > iPropertyOffset && Character.isUpperCase( strMethod.charAt( iPropertyOffset ) ) ) { return strMethod.substring( iPropertyOffset ); } return null; } private void verifyPropertyWritable( IType classRoot, String strProperty, boolean bFromObjInitializer ) throws ParseException { if( classRoot == null ) { throw new IllegalArgumentException( "Root class is null|" ); } if( strProperty == null ) { throw new IllegalArgumentException( "Bean member path is null!" ); } IPropertyInfo pi = BeanAccess.getPropertyInfo( classRoot, strProperty, null, null, null ); if( pi != null ) { if( !BeanAccess.isDescriptorHidden( pi ) ) { if( !pi.isWritable( getGosuClass() ) ) { if( bFromObjInitializer || !(isParsingConstructor() && pi instanceof IGosuVarPropertyInfo && pi.isFinal() && !pi.isStatic()) ) { throw new ParseException( makeFullParserState(), Res.MSG_CLASS_PROPERTY_NOT_WRITABLE, strProperty, TypeSystem.getUnqualifiedClassName( classRoot )); } } return; } } throw new IllegalArgumentException( "No property descriptor found for property, " + strProperty + ", on class, " + TypeSystem.getUnqualifiedClassName( classRoot ) ); } /** * Get the type of the method specified in the member path. * *@param classBean The declaring class of the constructor. * @param parserState The parserState that may be involved in the process of parsing a constructor. Can be null. * @return A Gosu type for the constructor. */ public IConstructorType getConstructorType( IType classBean, Expression[] eArgs, List<IConstructorType> listAllMatchingMethods, ParserBase parserState ) throws ParseException { if( classBean == null ) { throw new ParseException( parserState == null ? null : parserState.makeFullParserState(), Res.MSG_BEAN_CLASS_IS_NULL ); } if( ErrorType.shouldHandleAsErrorType( classBean ) ) { return ErrorType.getInstance().getErrorTypeConstructorType( eArgs, listAllMatchingMethods ); } ITypeInfo typeInfo = classBean.getTypeInfo(); if( typeInfo != null ) { List<? extends IConstructorInfo> constructors; if( typeInfo instanceof IRelativeTypeInfo ) { while( classBean instanceof ITypeVariableType ) { classBean = ((ITypeVariableType)classBean).getBoundingType(); } constructors = ((IRelativeTypeInfo)typeInfo).getConstructors( classBean ); } else { constructors = typeInfo.getConstructors(); } for( IConstructorInfo constructor : constructors ) { if( typeInfo instanceof JavaTypeInfo ) { if( constructor.isPrivate() ) { continue; } } IParameterInfo[] paramTypes = constructor.getParameters(); if( eArgs == null || paramTypes.length == eArgs.length ) { if( listAllMatchingMethods == null ) { return new ConstructorType( constructor ); } listAllMatchingMethods.add( new ConstructorType( constructor ) ); } } if( listAllMatchingMethods != null && listAllMatchingMethods.size() > 0 ) { return listAllMatchingMethods.get( 0 ); } } throw new NoCtorFoundException( parserState == null ? null : parserState.makeFullParserState(), TypeSystem.getUnqualifiedClassName( classBean ), eArgs == null ? 0 : eArgs.length ); } private void verifyCase( ParsedElement element, String foundName, String actualName, ResourceKey errorKey, boolean isEndsWithMatchOK ) { verifyCase(element, foundName, actualName, null, errorKey, isEndsWithMatchOK); } private void verifyCase( ParsedElement element, String foundName, String actualName, IParserState state, ResourceKey errorKey, boolean isEndsWithMatchOK ) { if( _bWarnOnCaseIssue ) { if( isEndsWithMatchOK ) { if( !actualName.endsWith( foundName ) ) { if( actualName.toUpperCase().endsWith( foundName.toUpperCase() ) ) { CharSequence correctedName = actualName.subSequence( actualName.length() - foundName.length(), actualName.length() ); if(state == null) { warn( element, false, errorKey, foundName, correctedName ); } else { warn( element, false, state, errorKey, foundName, correctedName ); } } } } else if( !GosuObjectUtil.equals( foundName, actualName ) ) { if( actualName.toUpperCase().equals( foundName.toUpperCase() ) ) { if(state == null) { warn( element, false, errorKey, foundName, actualName ); } else { warn( element, false, state, errorKey, foundName, actualName ); } } } } } public void setWarnOnCaseIssue( boolean warnOnCaseIssue ) { _bWarnOnCaseIssue = warnOnCaseIssue; } public void setEditorParser(boolean bStudioEditorParser) { _bStudioEditorParser = bStudioEditorParser; } public boolean isEditorParser() { if (getOwner() != this) { return getOwner().isEditorParser(); } return _bStudioEditorParser; } public IParserState getState() { return makeFullParserState(); } public void setContextInferenceManager(ContextInferenceManager ctxInferenceMgr) { _ctxInferenceMgr = ctxInferenceMgr; } public void pushContextType( IType type ) { ArrayList<IType> typeList = new ArrayList<IType>(1); typeList.add( type ); push_(typeList); } public void popContextType() { _inferredContextStack.pop(); } public List<IType> getContextTypes() { if( _inferredContextStack == null || _inferredContextStack.empty() || _inferredContextStack.peek() == null ) { return Collections.emptyList(); } else { return _inferredContextStack.peek(); } } public boolean isParsingAnnotation() { return _parsingAnnotation; } public void setParsingAnnotation( boolean parsingAnnotation ) { _parsingAnnotation = parsingAnnotation; } public boolean isIgnoreTypeDeprecation() { return _ignoreTypeDeprecation > 0; } public void pushIgnoreTypeDeprecation() { _ignoreTypeDeprecation++; } public void popIgnoreTypeDeprecation() { if( _ignoreTypeDeprecation == 0 ) { throw new IllegalStateException( "Unbalanced calls to push/popIgnoreTypeDeprecation()" ); } _ignoreTypeDeprecation--; } public void setLocationsFromProgramClassParser( List<ParseTree> savedLocations ) { _savedLocations = savedLocations; } boolean maybeAdvanceTokenizerToEndOfSavedLocation() { if( _savedLocations == null ) { return false; } for( ParseTree pt : _savedLocations ) { IToken T = getTokenizer().getCurrentToken(); if( T.getTokenStart() >= pt.getOffset() && T.getTokenEnd() <= pt.getExtent() ) { try { getTokenizer().goToPosition( pt.getOffset() + pt.getLength() ); return true; } catch( IOException e ) { // Eof ok return true; } } } return false; } protected void pushInferredFunctionTypeVariableTypes( List<IFunctionType> listFunctionTypes ) { List<IType> typeVariableTypes = new ArrayList<IType>(); for( IFunctionType functionType : listFunctionTypes ) { if( functionType.isGenericType() && (getEnclosingType() == null || !getEnclosingType().equals( functionType )) ) { IGenericTypeVariable[] typeVariables = functionType.getGenericTypeVariables(); for( IGenericTypeVariable typeVariable : typeVariables ) { ITypeVariableDefinition typeVariableDefinition = typeVariable.getTypeVariableDefinition(); if( typeVariableDefinition != null && typeVariableDefinition.getType() != null ) { typeVariableTypes.add( typeVariableDefinition.getType() ); } } } } pushInferredTypeVars( typeVariableTypes ); } @Override public String toString() { return "Parsing: " + getScriptPart(); } private static class GosuParserTransparentActivationContext extends TransparentActivationContext { public GosuParserTransparentActivationContext( IScriptPartId scriptPart ) { super( scriptPart ); } public String getLabel() { return getContext().toString(); } } private static class FunctionDeclTransparentActivationContext extends TransparentActivationContext { public FunctionDeclTransparentActivationContext( IScriptPartId scriptPart ) { super( scriptPart ); } public String getLabel() { return "parseFunctionDecl"; } } }