/******************************************************************************* * Copyright (c) 2000, 2010 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.jdt.core.dom; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.WorkingCopyOwner; import org.eclipse.jdt.core.compiler.CategorizedProblem; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.internal.compiler.CompilationResult; import org.eclipse.jdt.internal.compiler.Compiler; import org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies; import org.eclipse.jdt.internal.compiler.ICompilerRequestor; import org.eclipse.jdt.internal.compiler.IErrorHandlingPolicy; import org.eclipse.jdt.internal.compiler.IProblemFactory; import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; import org.eclipse.jdt.internal.compiler.batch.FileSystem.Classpath; import org.eclipse.jdt.internal.compiler.env.AccessRestriction; import org.eclipse.jdt.internal.compiler.env.INameEnvironment; import org.eclipse.jdt.internal.compiler.env.ISourceType; import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; import org.eclipse.jdt.internal.compiler.lookup.AnnotationBinding; import org.eclipse.jdt.internal.compiler.lookup.Binding; import org.eclipse.jdt.internal.compiler.lookup.ExtraCompilerModifiers; import org.eclipse.jdt.internal.compiler.lookup.PackageBinding; import org.eclipse.jdt.internal.compiler.parser.Parser; import org.eclipse.jdt.internal.compiler.problem.AbortCompilation; import org.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory; import org.eclipse.jdt.internal.compiler.problem.ProblemReporter; import org.eclipse.jdt.internal.compiler.util.HashtableOfObject; import org.eclipse.jdt.internal.compiler.util.HashtableOfObjectToInt; import org.eclipse.jdt.internal.compiler.util.Messages; import org.eclipse.jdt.internal.compiler.util.Util; import org.eclipse.jdt.internal.core.BinaryMember; import org.eclipse.jdt.internal.core.CancelableNameEnvironment; import org.eclipse.jdt.internal.core.CancelableProblemFactory; import org.eclipse.jdt.internal.core.INameEnviromentWithProgress; import org.eclipse.jdt.internal.core.JavaProject; import org.eclipse.jdt.internal.core.NameLookup; import org.eclipse.jdt.internal.core.SourceRefElement; import org.eclipse.jdt.internal.core.SourceTypeElementInfo; import org.eclipse.jdt.internal.core.util.BindingKeyResolver; import org.eclipse.jdt.internal.core.util.CommentRecorderParser; import org.eclipse.jdt.internal.core.util.DOMFinder; class CompilationUnitResolver extends Compiler { public static final int RESOLVE_BINDING= 0x1; public static final int PARTIAL= 0x2; public static final int STATEMENT_RECOVERY= 0x4; public static final int IGNORE_METHOD_BODIES= 0x8; public static final int BINDING_RECOVERY= 0x10; public static final int INCLUDE_RUNNING_VM_BOOTCLASSPATH= 0x20; /* A list of int */ static class IntArrayList { public int[] list= new int[5]; public int length= 0; public void add(int i) { if (this.list.length == this.length) { System.arraycopy(this.list, 0, this.list= new int[this.length * 2], 0, this.length); } this.list[this.length++]= i; } } /* * The sources that were requested. * Map from file name (char[]) to org.eclipse.jdt.internal.compiler.env.ICompilationUnit. */ HashtableOfObject requestedSources; /* * The binding keys that were requested. * Map from file name (char[]) to BindingKey (or ArrayList if multiple keys in the same file). */ HashtableOfObject requestedKeys; DefaultBindingResolver.BindingTables bindingTables; boolean hasCompilationAborted; private IProgressMonitor monitor; /** * Set to <code>true</code> if the receiver was initialized using a java project name * environment */ boolean fromJavaProject; /** * Answer a new CompilationUnitVisitor using the given name environment and compiler options. * The environment and options will be in effect for the lifetime of the compiler. When the * compiler is run, compilation results are sent to the given requestor. * * @param environment org.eclipse.jdt.internal.compiler.api.env.INameEnvironment Environment * used by the compiler in order to resolve type and package names. The name * environment implements the actual connection of the compiler to the outside world * (for example, in batch mode the name environment is performing pure file accesses, * reuse previous build state or connection to repositories). Note: the name * environment is responsible for implementing the actual classpath rules. * * @param policy org.eclipse.jdt.internal.compiler.api.problem.IErrorHandlingPolicy Configurable * part for problem handling, allowing the compiler client to specify the rules for * handling problems (stop on first error or accumulate them all) and at the same * time perform some actions such as opening a dialog in UI when compiling * interactively. * @see org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies * * @param compilerOptions The compiler options to use for the resolution. * * @param requestor org.eclipse.jdt.internal.compiler.api.ICompilerRequestor Component which * will receive and persist all compilation results and is intended to consume them * as they are produced. Typically, in a batch compiler, it is responsible for * writing out the actual .class files to the file system. * @see org.eclipse.jdt.internal.compiler.CompilationResult * * @param problemFactory org.eclipse.jdt.internal.compiler.api.problem.IProblemFactory Factory * used inside the compiler to create problem descriptors. It allows the compiler * client to supply its own representation of compilation problems in order to avoid * object conversions. Note that the factory is not supposed to accumulate the * created problems, the compiler will gather them all and hand them back as part of * the compilation unit result. */ public CompilationUnitResolver( INameEnvironment environment, IErrorHandlingPolicy policy, CompilerOptions compilerOptions, ICompilerRequestor requestor, IProblemFactory problemFactory, IProgressMonitor monitor, boolean fromJavaProject) { super(environment, policy, compilerOptions, requestor, problemFactory); this.hasCompilationAborted= false; this.monitor= monitor; this.fromJavaProject= fromJavaProject; } /* * Add additional source types */ public void accept(ISourceType[] sourceTypes, PackageBinding packageBinding, AccessRestriction accessRestriction) { // Need to reparse the entire source of the compilation unit so as to get source positions // (case of processing a source that was not known by beginToCompile (e.g. when asking to createBinding)) SourceTypeElementInfo sourceType= (SourceTypeElementInfo)sourceTypes[0]; accept((org.eclipse.jdt.internal.compiler.env.ICompilationUnit)sourceType.getHandle().getCompilationUnit(), accessRestriction); } public synchronized void accept(org.eclipse.jdt.internal.compiler.env.ICompilationUnit sourceUnit, AccessRestriction accessRestriction) { super.accept(sourceUnit, accessRestriction); } /** * Add the initial set of compilation units into the loop -> build compilation unit * declarations, their bindings and record their results. */ protected void beginToCompile(org.eclipse.jdt.internal.compiler.env.ICompilationUnit[] sourceUnits, String[] bindingKeys) { int sourceLength= sourceUnits.length; int keyLength= bindingKeys.length; int maxUnits= sourceLength + keyLength; this.totalUnits= 0; this.unitsToProcess= new CompilationUnitDeclaration[maxUnits]; int index= 0; // walks the source units this.requestedSources= new HashtableOfObject(); for (int i= 0; i < sourceLength; i++) { org.eclipse.jdt.internal.compiler.env.ICompilationUnit sourceUnit= sourceUnits[i]; CompilationUnitDeclaration parsedUnit; CompilationResult unitResult= new CompilationResult(sourceUnit, index++, maxUnits, this.options.maxProblemsPerUnit); try { if (this.options.verbose) { this.out.println( Messages.bind(Messages.compilation_request, new String[] { String.valueOf(index++ + 1), String.valueOf(maxUnits), new String(sourceUnit.getFileName()) })); } // diet parsing for large collection of units if (this.totalUnits < this.parseThreshold) { parsedUnit= this.parser.parse(sourceUnit, unitResult); } else { parsedUnit= this.parser.dietParse(sourceUnit, unitResult); } // initial type binding creation this.lookupEnvironment.buildTypeBindings(parsedUnit, null /*no access restriction*/); addCompilationUnit(sourceUnit, parsedUnit); this.requestedSources.put(unitResult.getFileName(), sourceUnit); worked(1); } finally { sourceUnits[i]= null; // no longer hold onto the unit } } // walk the binding keys this.requestedKeys= new HashtableOfObject(); for (int i= 0; i < keyLength; i++) { BindingKeyResolver resolver= new BindingKeyResolver(bindingKeys[i], this, this.lookupEnvironment); resolver.parse(true/*pause after fully qualified name*/); // If it doesn't have a type name, then it is either an array type, package or base type, which will definitely not have a compilation unit. // Skipping it will speed up performance because the call will open jars. (theodora) CompilationUnitDeclaration parsedUnit= resolver.hasTypeName() ? resolver.getCompilationUnitDeclaration() : null; if (parsedUnit != null) { char[] fileName= parsedUnit.compilationResult.getFileName(); Object existing= this.requestedKeys.get(fileName); if (existing == null) this.requestedKeys.put(fileName, resolver); else if (existing instanceof ArrayList) ((ArrayList)existing).add(resolver); else { ArrayList list= new ArrayList(); list.add(existing); list.add(resolver); this.requestedKeys.put(fileName, list); } } else { char[] key= resolver.hasTypeName() ? resolver.getKey().toCharArray() // binary binding : CharOperation.concatWith(resolver.compoundName(), '.'); // package binding or base type binding this.requestedKeys.put(key, resolver); } worked(1); } // binding resolution this.lookupEnvironment.completeTypeBindings(); } IBinding createBinding(String key) { if (this.bindingTables == null) throw new RuntimeException("Cannot be called outside ASTParser#createASTs(...)"); //$NON-NLS-1$ BindingKeyResolver keyResolver= new BindingKeyResolver(key, this, this.lookupEnvironment); Binding compilerBinding= keyResolver.getCompilerBinding(); if (compilerBinding == null) return null; DefaultBindingResolver resolver= new DefaultBindingResolver(this.lookupEnvironment, null/*no owner*/, this.bindingTables, false, this.fromJavaProject); return resolver.getBinding(compilerBinding); } public static CompilationUnit convert( CompilationUnitDeclaration compilationUnitDeclaration, char[] source, int apiLevel, Map options, boolean needToResolveBindings, WorkingCopyOwner owner, DefaultBindingResolver.BindingTables bindingTables, int flags, IProgressMonitor monitor, boolean fromJavaProject) { BindingResolver resolver= null; AST ast= AST.newAST(apiLevel); ast.setDefaultNodeFlag(ASTNode.ORIGINAL); CompilationUnit compilationUnit= null; ASTConverter converter= new ASTConverter(options, needToResolveBindings, monitor); if (needToResolveBindings) { resolver= new DefaultBindingResolver(compilationUnitDeclaration.scope, owner, bindingTables, (flags & ICompilationUnit.ENABLE_BINDINGS_RECOVERY) != 0, fromJavaProject); ast.setFlag(flags | AST.RESOLVED_BINDINGS); } else { resolver= new BindingResolver(); ast.setFlag(flags); } ast.setBindingResolver(resolver); converter.setAST(ast); compilationUnit= converter.convert(compilationUnitDeclaration, source); compilationUnit.setLineEndTable(compilationUnitDeclaration.compilationResult.getLineSeparatorPositions()); ast.setDefaultNodeFlag(0); ast.setOriginalModificationCount(ast.modificationCount()); return compilationUnit; } protected static CompilerOptions getCompilerOptions(Map options, boolean statementsRecovery) { CompilerOptions compilerOptions= new CompilerOptions(options); compilerOptions.performMethodsFullRecovery= statementsRecovery; compilerOptions.performStatementsRecovery= statementsRecovery; compilerOptions.parseLiteralExpressionsAsConstants= false; compilerOptions.storeAnnotations= true /*store annotations in the bindings*/; return compilerOptions; } /* * Low-level API performing the actual compilation */ protected static IErrorHandlingPolicy getHandlingPolicy() { // passes the initial set of files to the batch oracle (to avoid finding more than once the same units when case insensitive match) return new IErrorHandlingPolicy() { public boolean stopOnFirstError() { return false; } public boolean proceedOnErrors() { return false; // stop if there are some errors } }; } /* * Answer the component to which will be handed back compilation results from the compiler */ protected static ICompilerRequestor getRequestor() { return new ICompilerRequestor() { public void acceptResult(CompilationResult compilationResult) { // do nothing } }; } /* (non-Javadoc) * @see org.eclipse.jdt.internal.compiler.Compiler#initializeParser() */ public void initializeParser() { this.parser= new CommentRecorderParser(this.problemReporter, false); } public void process(CompilationUnitDeclaration unit, int i) { // don't resolve a second time the same unit (this would create the same binding twice) char[] fileName= unit.compilationResult.getFileName(); if (this.requestedKeys.get(fileName) == null && this.requestedSources.get(fileName) == null) super.process(unit, i); } /* * Compiler crash recovery in case of unexpected runtime exceptions */ protected void handleInternalException( Throwable internalException, CompilationUnitDeclaration unit, CompilationResult result) { super.handleInternalException(internalException, unit, result); if (unit != null) { removeUnresolvedBindings(unit); } } /* * Compiler recovery in case of internal AbortCompilation event */ protected void handleInternalException( AbortCompilation abortException, CompilationUnitDeclaration unit) { super.handleInternalException(abortException, unit); if (unit != null) { removeUnresolvedBindings(unit); } this.hasCompilationAborted= true; } public static void parse(ICompilationUnit[] compilationUnits, ASTRequestor astRequestor, int apiLevel, Map options, int flags, IProgressMonitor monitor) { try { CompilerOptions compilerOptions= new CompilerOptions(options); compilerOptions.ignoreMethodBodies= (flags & ICompilationUnit.IGNORE_METHOD_BODIES) != 0; Parser parser= new CommentRecorderParser( new ProblemReporter( DefaultErrorHandlingPolicies.proceedWithAllProblems(), compilerOptions, new DefaultProblemFactory()), false); int unitLength= compilationUnits.length; if (monitor != null) monitor.beginTask("", unitLength); //$NON-NLS-1$ for (int i= 0; i < unitLength; i++) { org.eclipse.jdt.internal.compiler.env.ICompilationUnit sourceUnit= (org.eclipse.jdt.internal.compiler.env.ICompilationUnit)compilationUnits[i]; CompilationResult compilationResult= new CompilationResult(sourceUnit, 0, 0, compilerOptions.maxProblemsPerUnit); CompilationUnitDeclaration compilationUnitDeclaration= parser.dietParse(sourceUnit, compilationResult); if (compilationUnitDeclaration.ignoreMethodBodies) { compilationUnitDeclaration.ignoreFurtherInvestigation= true; // if initial diet parse did not work, no need to dig into method bodies. continue; } //fill the methods bodies in order for the code to be generated //real parse of the method.... org.eclipse.jdt.internal.compiler.ast.TypeDeclaration[] types= compilationUnitDeclaration.types; if (types != null) { for (int j= 0, typeLength= types.length; j < typeLength; j++) { types[j].parseMethods(parser, compilationUnitDeclaration); } } // convert AST CompilationUnit node= convert(compilationUnitDeclaration, parser.scanner.getSource(), apiLevel, options, false/*don't resolve binding*/, null/*no owner needed*/, null/*no binding table needed*/, flags /* flags */, monitor, true); node.setTypeRoot(compilationUnits[i]); // accept AST astRequestor.acceptAST(compilationUnits[i], node); if (monitor != null) monitor.worked(1); } } finally { if (monitor != null) monitor.done(); } } public static void parse( String[] sourceUnits, String[] encodings, FileASTRequestor astRequestor, int apiLevel, Map options, int flags, IProgressMonitor monitor) { try { CompilerOptions compilerOptions= new CompilerOptions(options); compilerOptions.ignoreMethodBodies= (flags & ICompilationUnit.IGNORE_METHOD_BODIES) != 0; Parser parser= new CommentRecorderParser( new ProblemReporter( DefaultErrorHandlingPolicies.proceedWithAllProblems(), compilerOptions, new DefaultProblemFactory()), false); int unitLength= sourceUnits.length; if (monitor != null) monitor.beginTask("", unitLength); //$NON-NLS-1$ for (int i= 0; i < unitLength; i++) { char[] contents= null; String encoding= encodings != null ? encodings[i] : null; try { contents= Util.getFileCharContent(new File(sourceUnits[i]), encoding); } catch (IOException e) { // go to the next unit continue; } if (contents == null) { // go to the next unit continue; } org.eclipse.jdt.internal.compiler.batch.CompilationUnit compilationUnit= new org.eclipse.jdt.internal.compiler.batch.CompilationUnit(contents, sourceUnits[i], encoding); org.eclipse.jdt.internal.compiler.env.ICompilationUnit sourceUnit= compilationUnit; CompilationResult compilationResult= new CompilationResult(sourceUnit, 0, 0, compilerOptions.maxProblemsPerUnit); CompilationUnitDeclaration compilationUnitDeclaration= parser.dietParse(sourceUnit, compilationResult); if (compilationUnitDeclaration.ignoreMethodBodies) { compilationUnitDeclaration.ignoreFurtherInvestigation= true; // if initial diet parse did not work, no need to dig into method bodies. continue; } //fill the methods bodies in order for the code to be generated //real parse of the method.... org.eclipse.jdt.internal.compiler.ast.TypeDeclaration[] types= compilationUnitDeclaration.types; if (types != null) { for (int j= 0, typeLength= types.length; j < typeLength; j++) { types[j].parseMethods(parser, compilationUnitDeclaration); } } // convert AST CompilationUnit node= convert(compilationUnitDeclaration, parser.scanner.getSource(), apiLevel, options, false/*don't resolve binding*/, null/*no owner needed*/, null/*no binding table needed*/, flags /* flags */, monitor, true); node.setTypeRoot(null); // accept AST astRequestor.acceptAST(sourceUnits[i], node); if (monitor != null) monitor.worked(1); } } finally { if (monitor != null) monitor.done(); } } public static CompilationUnitDeclaration parse( org.eclipse.jdt.internal.compiler.env.ICompilationUnit sourceUnit, NodeSearcher nodeSearcher, Map settings, int flags) { if (sourceUnit == null) { throw new IllegalStateException(); } CompilerOptions compilerOptions= new CompilerOptions(settings); boolean statementsRecovery= (flags & ICompilationUnit.ENABLE_STATEMENTS_RECOVERY) != 0; compilerOptions.performMethodsFullRecovery= statementsRecovery; compilerOptions.performStatementsRecovery= statementsRecovery; compilerOptions.ignoreMethodBodies= (flags & ICompilationUnit.IGNORE_METHOD_BODIES) != 0; Parser parser= new CommentRecorderParser( new ProblemReporter( DefaultErrorHandlingPolicies.proceedWithAllProblems(), compilerOptions, new DefaultProblemFactory()), false); CompilationResult compilationResult= new CompilationResult(sourceUnit, 0, 0, compilerOptions.maxProblemsPerUnit); CompilationUnitDeclaration compilationUnitDeclaration= parser.dietParse(sourceUnit, compilationResult); if (compilationUnitDeclaration.ignoreMethodBodies) { compilationUnitDeclaration.ignoreFurtherInvestigation= true; // if initial diet parse did not work, no need to dig into method bodies. return compilationUnitDeclaration; } if (nodeSearcher != null) { char[] source= parser.scanner.getSource(); int searchPosition= nodeSearcher.position; if (searchPosition < 0 || searchPosition > source.length) { // the position is out of range. There is no need to search for a node. return compilationUnitDeclaration; } compilationUnitDeclaration.traverse(nodeSearcher, compilationUnitDeclaration.scope); org.eclipse.jdt.internal.compiler.ast.ASTNode node= nodeSearcher.found; if (node == null) { return compilationUnitDeclaration; } org.eclipse.jdt.internal.compiler.ast.TypeDeclaration enclosingTypeDeclaration= nodeSearcher.enclosingType; if (node instanceof AbstractMethodDeclaration) { ((AbstractMethodDeclaration)node).parseStatements(parser, compilationUnitDeclaration); } else if (enclosingTypeDeclaration != null) { if (node instanceof org.eclipse.jdt.internal.compiler.ast.Initializer) { ((org.eclipse.jdt.internal.compiler.ast.Initializer)node).parseStatements(parser, enclosingTypeDeclaration, compilationUnitDeclaration); } else if (node instanceof org.eclipse.jdt.internal.compiler.ast.TypeDeclaration) { ((org.eclipse.jdt.internal.compiler.ast.TypeDeclaration)node).parseMethods(parser, compilationUnitDeclaration); } } } else { //fill the methods bodies in order for the code to be generated //real parse of the method.... org.eclipse.jdt.internal.compiler.ast.TypeDeclaration[] types= compilationUnitDeclaration.types; if (types != null) { for (int j= 0, typeLength= types.length; j < typeLength; j++) { types[j].parseMethods(parser, compilationUnitDeclaration); } } } return compilationUnitDeclaration; } public static void resolve( ICompilationUnit[] compilationUnits, String[] bindingKeys, ASTRequestor requestor, int apiLevel, Map options, IJavaProject javaProject, WorkingCopyOwner owner, int flags, IProgressMonitor monitor) { CancelableNameEnvironment environment= null; CancelableProblemFactory problemFactory= null; try { if (monitor != null) { int amountOfWork= (compilationUnits.length + bindingKeys.length) * 2; // 1 for beginToCompile, 1 for resolve monitor.beginTask("", amountOfWork); //$NON-NLS-1$ } environment= new CancelableNameEnvironment(((JavaProject)javaProject), owner, monitor); problemFactory= new CancelableProblemFactory(monitor); CompilerOptions compilerOptions= getCompilerOptions(options, (flags & ICompilationUnit.ENABLE_STATEMENTS_RECOVERY) != 0); compilerOptions.ignoreMethodBodies= (flags & ICompilationUnit.IGNORE_METHOD_BODIES) != 0; CompilationUnitResolver resolver= new CompilationUnitResolver( environment, getHandlingPolicy(), compilerOptions, getRequestor(), problemFactory, monitor, javaProject != null); resolver.resolve(compilationUnits, bindingKeys, requestor, apiLevel, options, owner, flags); if (NameLookup.VERBOSE) { System.out.println(Thread.currentThread() + " TIME SPENT in NameLoopkup#seekTypesInSourcePackage: " + environment.nameLookup.timeSpentInSeekTypesInSourcePackage + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ System.out.println(Thread.currentThread() + " TIME SPENT in NameLoopkup#seekTypesInBinaryPackage: " + environment.nameLookup.timeSpentInSeekTypesInBinaryPackage + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ } } catch (JavaModelException e) { // project doesn't exist -> simple parse without resolving parse(compilationUnits, requestor, apiLevel, options, flags, monitor); } finally { if (monitor != null) monitor.done(); if (environment != null) { environment.setMonitor(null); // don't hold a reference to this external object } if (problemFactory != null) { problemFactory.monitor= null; // don't hold a reference to this external object } } } public static void resolve( String[] sourceUnits, String[] encodings, String[] bindingKeys, FileASTRequestor requestor, int apiLevel, Map options, List classpaths, int flags, IProgressMonitor monitor) { INameEnviromentWithProgress environment= null; CancelableProblemFactory problemFactory= null; try { if (monitor != null) { int amountOfWork= (sourceUnits.length + bindingKeys.length) * 2; // 1 for beginToCompile, 1 for resolve monitor.beginTask("", amountOfWork); //$NON-NLS-1$ } Classpath[] allEntries= new Classpath[classpaths.size()]; classpaths.toArray(allEntries); environment= new NameEnviromentWithProgress(allEntries, null, monitor); problemFactory= new CancelableProblemFactory(monitor); CompilerOptions compilerOptions= getCompilerOptions(options, (flags & ICompilationUnit.ENABLE_STATEMENTS_RECOVERY) != 0); compilerOptions.ignoreMethodBodies= (flags & ICompilationUnit.IGNORE_METHOD_BODIES) != 0; CompilationUnitResolver resolver= new CompilationUnitResolver( environment, getHandlingPolicy(), compilerOptions, getRequestor(), problemFactory, monitor, false); resolver.resolve(sourceUnits, encodings, bindingKeys, requestor, apiLevel, options, flags); if (NameLookup.VERBOSE && (environment instanceof CancelableNameEnvironment)) { CancelableNameEnvironment cancelableNameEnvironment= (CancelableNameEnvironment)environment; System.out.println(Thread.currentThread() + " TIME SPENT in NameLoopkup#seekTypesInSourcePackage: " + cancelableNameEnvironment.nameLookup.timeSpentInSeekTypesInSourcePackage + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ System.out.println(Thread.currentThread() + " TIME SPENT in NameLoopkup#seekTypesInBinaryPackage: " + cancelableNameEnvironment.nameLookup.timeSpentInSeekTypesInBinaryPackage + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ } } finally { if (monitor != null) monitor.done(); if (environment != null) { environment.setMonitor(null); // don't hold a reference to this external object } if (problemFactory != null) { problemFactory.monitor= null; // don't hold a reference to this external object } } } public static CompilationUnitDeclaration resolve( org.eclipse.jdt.internal.compiler.env.ICompilationUnit sourceUnit, IJavaProject javaProject, List classpaths, NodeSearcher nodeSearcher, Map options, WorkingCopyOwner owner, int flags, IProgressMonitor monitor) throws JavaModelException { CompilationUnitDeclaration unit= null; INameEnviromentWithProgress environment= null; CancelableProblemFactory problemFactory= null; CompilationUnitResolver resolver= null; try { if (javaProject == null) { Classpath[] allEntries= new Classpath[classpaths.size()]; classpaths.toArray(allEntries); environment= new NameEnviromentWithProgress(allEntries, null, monitor); } else { environment= new CancelableNameEnvironment((JavaProject)javaProject, owner, monitor); } problemFactory= new CancelableProblemFactory(monitor); CompilerOptions compilerOptions= getCompilerOptions(options, (flags & ICompilationUnit.ENABLE_STATEMENTS_RECOVERY) != 0); boolean ignoreMethodBodies= (flags & ICompilationUnit.IGNORE_METHOD_BODIES) != 0; compilerOptions.ignoreMethodBodies= ignoreMethodBodies; resolver= new CompilationUnitResolver( environment, getHandlingPolicy(), compilerOptions, getRequestor(), problemFactory, monitor, javaProject != null); boolean analyzeAndGenerateCode= !ignoreMethodBodies; unit= resolver.resolve( null, // no existing compilation unit declaration sourceUnit, nodeSearcher, true, // method verification analyzeAndGenerateCode, // analyze code analyzeAndGenerateCode); // generate code if (resolver.hasCompilationAborted) { // the bindings could not be resolved due to missing types in name environment // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=86541 CompilationUnitDeclaration unitDeclaration= parse(sourceUnit, nodeSearcher, options, flags); final int problemCount= unit.compilationResult.problemCount; if (problemCount != 0) { unitDeclaration.compilationResult.problems= new CategorizedProblem[problemCount]; System.arraycopy(unit.compilationResult.problems, 0, unitDeclaration.compilationResult.problems, 0, problemCount); unitDeclaration.compilationResult.problemCount= problemCount; } return unitDeclaration; } if (NameLookup.VERBOSE && environment instanceof CancelableNameEnvironment) { CancelableNameEnvironment cancelableNameEnvironment= (CancelableNameEnvironment)environment; System.out.println(Thread.currentThread() + " TIME SPENT in NameLoopkup#seekTypesInSourcePackage: " + cancelableNameEnvironment.nameLookup.timeSpentInSeekTypesInSourcePackage + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ System.out.println(Thread.currentThread() + " TIME SPENT in NameLoopkup#seekTypesInBinaryPackage: " + cancelableNameEnvironment.nameLookup.timeSpentInSeekTypesInBinaryPackage + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ } return unit; } finally { if (environment != null) { // don't hold a reference to this external object environment.setMonitor(null); } if (problemFactory != null) { problemFactory.monitor= null; // don't hold a reference to this external object } } } public static IBinding[] resolve( final IJavaElement[] elements, int apiLevel, Map compilerOptions, IJavaProject javaProject, WorkingCopyOwner owner, int flags, IProgressMonitor monitor) { final int length= elements.length; final HashMap sourceElementPositions= new HashMap(); // a map from ICompilationUnit to int[] (positions in elements) int cuNumber= 0; final HashtableOfObjectToInt binaryElementPositions= new HashtableOfObjectToInt(); // a map from String (binding key) to int (position in elements) for (int i= 0; i < length; i++) { IJavaElement element= elements[i]; if (!(element instanceof SourceRefElement)) throw new IllegalStateException(element + " is not part of a compilation unit or class file"); //$NON-NLS-1$ Object cu= element.getAncestor(IJavaElement.COMPILATION_UNIT); if (cu != null) { // source member IntArrayList intList= (IntArrayList)sourceElementPositions.get(cu); if (intList == null) { sourceElementPositions.put(cu, intList= new IntArrayList()); cuNumber++; } intList.add(i); } else { // binary member try { String key= ((BinaryMember)element).getKey(true/*open to get resolved info*/); binaryElementPositions.put(key, i); } catch (JavaModelException e) { throw new IllegalArgumentException(element + " does not exist"); //$NON-NLS-1$ } } } ICompilationUnit[] cus= new ICompilationUnit[cuNumber]; sourceElementPositions.keySet().toArray(cus); int bindingKeyNumber= binaryElementPositions.size(); String[] bindingKeys= new String[bindingKeyNumber]; binaryElementPositions.keysToArray(bindingKeys); class Requestor extends ASTRequestor { IBinding[] bindings= new IBinding[length]; public void acceptAST(ICompilationUnit source, CompilationUnit ast) { // TODO (jerome) optimize to visit the AST only once IntArrayList intList= (IntArrayList)sourceElementPositions.get(source); for (int i= 0; i < intList.length; i++) { final int index= intList.list[i]; SourceRefElement element= (SourceRefElement)elements[index]; DOMFinder finder= new DOMFinder(ast, element, true/*resolve binding*/); try { finder.search(); } catch (JavaModelException e) { throw new IllegalArgumentException(element + " does not exist"); //$NON-NLS-1$ } this.bindings[index]= finder.foundBinding; } } public void acceptBinding(String bindingKey, IBinding binding) { int index= binaryElementPositions.get(bindingKey); this.bindings[index]= binding; } } Requestor requestor= new Requestor(); resolve(cus, bindingKeys, requestor, apiLevel, compilerOptions, javaProject, owner, flags, monitor); return requestor.bindings; } /* * When unit result is about to be accepted, removed back pointers * to unresolved bindings */ public void removeUnresolvedBindings(CompilationUnitDeclaration compilationUnitDeclaration) { final org.eclipse.jdt.internal.compiler.ast.TypeDeclaration[] types= compilationUnitDeclaration.types; if (types != null) { for (int i= 0, max= types.length; i < max; i++) { removeUnresolvedBindings(types[i]); } } } private void removeUnresolvedBindings(org.eclipse.jdt.internal.compiler.ast.TypeDeclaration type) { final org.eclipse.jdt.internal.compiler.ast.TypeDeclaration[] memberTypes= type.memberTypes; if (memberTypes != null) { for (int i= 0, max= memberTypes.length; i < max; i++) { removeUnresolvedBindings(memberTypes[i]); } } if (type.binding != null && (type.binding.modifiers & ExtraCompilerModifiers.AccUnresolved) != 0) { type.binding= null; } final org.eclipse.jdt.internal.compiler.ast.FieldDeclaration[] fields= type.fields; if (fields != null) { for (int i= 0, max= fields.length; i < max; i++) { if (fields[i].binding != null && (fields[i].binding.modifiers & ExtraCompilerModifiers.AccUnresolved) != 0) { fields[i].binding= null; } } } final AbstractMethodDeclaration[] methods= type.methods; if (methods != null) { for (int i= 0, max= methods.length; i < max; i++) { if (methods[i].binding != null && (methods[i].binding.modifiers & ExtraCompilerModifiers.AccUnresolved) != 0) { methods[i].binding= null; } } } } private void resolve( ICompilationUnit[] compilationUnits, String[] bindingKeys, ASTRequestor astRequestor, int apiLevel, Map compilerOptions, WorkingCopyOwner owner, int flags) { // temporarily connect ourselves to the ASTResolver - must disconnect when done astRequestor.compilationUnitResolver= this; this.bindingTables= new DefaultBindingResolver.BindingTables(); CompilationUnitDeclaration unit= null; try { int length= compilationUnits.length; org.eclipse.jdt.internal.compiler.env.ICompilationUnit[] sourceUnits= new org.eclipse.jdt.internal.compiler.env.ICompilationUnit[length]; System.arraycopy(compilationUnits, 0, sourceUnits, 0, length); beginToCompile(sourceUnits, bindingKeys); // process all units (some more could be injected in the loop by the lookup environment) for (int i= 0; i < this.totalUnits; i++) { if (resolvedRequestedSourcesAndKeys(i)) { // no need to keep resolving if no more ASTs and no more binding keys are needed // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=114935 // cleanup remaining units for (; i < this.totalUnits; i++) { this.unitsToProcess[i].cleanUp(); this.unitsToProcess[i]= null; } break; } unit= this.unitsToProcess[i]; try { super.process(unit, i); // this.process(...) is optimized to not process already known units // requested AST char[] fileName= unit.compilationResult.getFileName(); ICompilationUnit source= (ICompilationUnit)this.requestedSources.get(fileName); if (source != null) { // convert AST CompilationResult compilationResult= unit.compilationResult; org.eclipse.jdt.internal.compiler.env.ICompilationUnit sourceUnit= compilationResult.compilationUnit; char[] contents= sourceUnit.getContents(); AST ast= AST.newAST(apiLevel); ast.setFlag(flags | AST.RESOLVED_BINDINGS); ast.setDefaultNodeFlag(ASTNode.ORIGINAL); ASTConverter converter= new ASTConverter(compilerOptions, true/*need to resolve bindings*/, this.monitor); BindingResolver resolver= new DefaultBindingResolver(unit.scope, owner, this.bindingTables, (flags & ICompilationUnit.ENABLE_BINDINGS_RECOVERY) != 0, this.fromJavaProject); ast.setBindingResolver(resolver); converter.setAST(ast); CompilationUnit compilationUnit= converter.convert(unit, contents); compilationUnit.setTypeRoot(source); compilationUnit.setLineEndTable(compilationResult.getLineSeparatorPositions()); ast.setDefaultNodeFlag(0); ast.setOriginalModificationCount(ast.modificationCount()); // pass it to requestor astRequestor.acceptAST(source, compilationUnit); worked(1); // remove at the end so that we don't resolve twice if a source and a key for the same file name have been requested this.requestedSources.put(fileName, null); // mark it as removed } // requested binding Object key= this.requestedKeys.get(fileName); if (key != null) { if (key instanceof BindingKeyResolver) { reportBinding(key, astRequestor, owner, unit); worked(1); } else if (key instanceof ArrayList) { Iterator iterator= ((ArrayList)key).iterator(); while (iterator.hasNext()) { reportBinding(iterator.next(), astRequestor, owner, unit); worked(1); } } // remove at the end so that we don't resolve twice if a source and a key for the same file name have been requested this.requestedKeys.put(fileName, null); // mark it as removed } } finally { // cleanup compilation unit result unit.cleanUp(); } this.unitsToProcess[i]= null; // release reference to processed unit declaration this.requestor.acceptResult(unit.compilationResult.tagAsAccepted()); } // remaining binding keys DefaultBindingResolver resolver= new DefaultBindingResolver(this.lookupEnvironment, owner, this.bindingTables, (flags & ICompilationUnit.ENABLE_BINDINGS_RECOVERY) != 0, true); Object[] keys= this.requestedKeys.valueTable; for (int j= 0, keysLength= keys.length; j < keysLength; j++) { BindingKeyResolver keyResolver= (BindingKeyResolver)keys[j]; if (keyResolver == null) continue; Binding compilerBinding= keyResolver.getCompilerBinding(); IBinding binding= compilerBinding == null ? null : resolver.getBinding(compilerBinding); // pass it to requestor astRequestor.acceptBinding(((BindingKeyResolver)this.requestedKeys.valueTable[j]).getKey(), binding); worked(1); } } catch (OperationCanceledException e) { throw e; } catch (AbortCompilation e) { this.handleInternalException(e, unit); } catch (Error e) { this.handleInternalException(e, unit, null); throw e; // rethrow } catch (RuntimeException e) { this.handleInternalException(e, unit, null); throw e; // rethrow } finally { // disconnect ourselves from ast requestor astRequestor.compilationUnitResolver= null; } } private void resolve( String[] sourceCompilationUnits, String[] encodings, String[] bindingKeys, FileASTRequestor astRequestor, int apiLevel, Map compilerOptions, int flags) { // temporarily connect ourselves to the ASTResolver - must disconnect when done astRequestor.compilationUnitResolver= this; this.bindingTables= new DefaultBindingResolver.BindingTables(); CompilationUnitDeclaration unit= null; try { int length= sourceCompilationUnits.length; org.eclipse.jdt.internal.compiler.env.ICompilationUnit[] sourceUnits= new org.eclipse.jdt.internal.compiler.env.ICompilationUnit[length]; int count= 0; for (int i= 0; i < length; i++) { char[] contents= null; String encoding= encodings != null ? encodings[i] : null; String sourceUnitPath= sourceCompilationUnits[i]; try { contents= Util.getFileCharContent(new File(sourceUnitPath), encoding); } catch (IOException e) { // go to the next unit continue; } if (contents == null) { // go to the next unit continue; } sourceUnits[count++]= new org.eclipse.jdt.internal.compiler.batch.CompilationUnit(contents, sourceUnitPath, encoding); } beginToCompile(sourceUnits, bindingKeys); // process all units (some more could be injected in the loop by the lookup environment) for (int i= 0; i < this.totalUnits; i++) { if (resolvedRequestedSourcesAndKeys(i)) { // no need to keep resolving if no more ASTs and no more binding keys are needed // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=114935 // cleanup remaining units for (; i < this.totalUnits; i++) { this.unitsToProcess[i].cleanUp(); this.unitsToProcess[i]= null; } break; } unit= this.unitsToProcess[i]; try { super.process(unit, i); // this.process(...) is optimized to not process already known units // requested AST char[] fileName= unit.compilationResult.getFileName(); org.eclipse.jdt.internal.compiler.env.ICompilationUnit source= (org.eclipse.jdt.internal.compiler.env.ICompilationUnit)this.requestedSources.get(fileName); if (source != null) { // convert AST CompilationResult compilationResult= unit.compilationResult; org.eclipse.jdt.internal.compiler.env.ICompilationUnit sourceUnit= compilationResult.compilationUnit; char[] contents= sourceUnit.getContents(); AST ast= AST.newAST(apiLevel); ast.setFlag(flags | AST.RESOLVED_BINDINGS); ast.setDefaultNodeFlag(ASTNode.ORIGINAL); ASTConverter converter= new ASTConverter(compilerOptions, true/*need to resolve bindings*/, this.monitor); BindingResolver resolver= new DefaultBindingResolver(unit.scope, null, this.bindingTables, (flags & ICompilationUnit.ENABLE_BINDINGS_RECOVERY) != 0, this.fromJavaProject); ast.setBindingResolver(resolver); converter.setAST(ast); CompilationUnit compilationUnit= converter.convert(unit, contents); compilationUnit.setTypeRoot(null); compilationUnit.setLineEndTable(compilationResult.getLineSeparatorPositions()); ast.setDefaultNodeFlag(0); ast.setOriginalModificationCount(ast.modificationCount()); // pass it to requestor astRequestor.acceptAST(new String(source.getFileName()), compilationUnit); worked(1); // remove at the end so that we don't resolve twice if a source and a key for the same file name have been requested this.requestedSources.put(fileName, null); // mark it as removed } // requested binding Object key= this.requestedKeys.get(fileName); if (key != null) { if (key instanceof BindingKeyResolver) { reportBinding(key, astRequestor, unit); worked(1); } else if (key instanceof ArrayList) { Iterator iterator= ((ArrayList)key).iterator(); while (iterator.hasNext()) { reportBinding(iterator.next(), astRequestor, unit); worked(1); } } // remove at the end so that we don't resolve twice if a source and a key for the same file name have been requested this.requestedKeys.put(fileName, null); // mark it as removed } } finally { // cleanup compilation unit result unit.cleanUp(); } this.unitsToProcess[i]= null; // release reference to processed unit declaration this.requestor.acceptResult(unit.compilationResult.tagAsAccepted()); } // remaining binding keys DefaultBindingResolver resolver= new DefaultBindingResolver(this.lookupEnvironment, null, this.bindingTables, (flags & ICompilationUnit.ENABLE_BINDINGS_RECOVERY) != 0, true); Object[] keys= this.requestedKeys.valueTable; for (int j= 0, keysLength= keys.length; j < keysLength; j++) { BindingKeyResolver keyResolver= (BindingKeyResolver)keys[j]; if (keyResolver == null) continue; Binding compilerBinding= keyResolver.getCompilerBinding(); IBinding binding= compilerBinding == null ? null : resolver.getBinding(compilerBinding); // pass it to requestor astRequestor.acceptBinding(((BindingKeyResolver)this.requestedKeys.valueTable[j]).getKey(), binding); worked(1); } } catch (OperationCanceledException e) { throw e; } catch (AbortCompilation e) { this.handleInternalException(e, unit); } catch (Error e) { this.handleInternalException(e, unit, null); throw e; // rethrow } catch (RuntimeException e) { this.handleInternalException(e, unit, null); throw e; // rethrow } finally { // disconnect ourselves from ast requestor astRequestor.compilationUnitResolver= null; } } private void reportBinding(Object key, ASTRequestor astRequestor, WorkingCopyOwner owner, CompilationUnitDeclaration unit) { BindingKeyResolver keyResolver= (BindingKeyResolver)key; Binding compilerBinding= keyResolver.getCompilerBinding(); if (compilerBinding != null) { DefaultBindingResolver resolver= new DefaultBindingResolver(unit.scope, owner, this.bindingTables, false, this.fromJavaProject); AnnotationBinding annotationBinding= keyResolver.getAnnotationBinding(); IBinding binding; if (annotationBinding != null) { binding= resolver.getAnnotationInstance(annotationBinding); } else { binding= resolver.getBinding(compilerBinding); } if (binding != null) astRequestor.acceptBinding(keyResolver.getKey(), binding); } } private void reportBinding(Object key, FileASTRequestor astRequestor, CompilationUnitDeclaration unit) { BindingKeyResolver keyResolver= (BindingKeyResolver)key; Binding compilerBinding= keyResolver.getCompilerBinding(); if (compilerBinding != null) { DefaultBindingResolver resolver= new DefaultBindingResolver(unit.scope, null, this.bindingTables, false, this.fromJavaProject); AnnotationBinding annotationBinding= keyResolver.getAnnotationBinding(); IBinding binding; if (annotationBinding != null) { binding= resolver.getAnnotationInstance(annotationBinding); } else { binding= resolver.getBinding(compilerBinding); } if (binding != null) astRequestor.acceptBinding(keyResolver.getKey(), binding); } } private CompilationUnitDeclaration resolve( CompilationUnitDeclaration unit, org.eclipse.jdt.internal.compiler.env.ICompilationUnit sourceUnit, NodeSearcher nodeSearcher, boolean verifyMethods, boolean analyzeCode, boolean generateCode) { try { if (unit == null) { // build and record parsed units this.parseThreshold= 0; // will request a full parse beginToCompile(new org.eclipse.jdt.internal.compiler.env.ICompilationUnit[] { sourceUnit }); // process all units (some more could be injected in the loop by the lookup environment) unit= this.unitsToProcess[0]; } else { // initial type binding creation this.lookupEnvironment.buildTypeBindings(unit, null /*no access restriction*/); // binding resolution this.lookupEnvironment.completeTypeBindings(); } if (nodeSearcher == null) { this.parser.getMethodBodies(unit); // no-op if method bodies have already been parsed } else { int searchPosition= nodeSearcher.position; char[] source= sourceUnit.getContents(); int length= source.length; if (searchPosition >= 0 && searchPosition <= length) { unit.traverse(nodeSearcher, unit.scope); org.eclipse.jdt.internal.compiler.ast.ASTNode node= nodeSearcher.found; if (node != null) { // save existing values to restore them at the end of the parsing process // see bug 47079 for more details int[] oldLineEnds= this.parser.scanner.lineEnds; int oldLinePtr= this.parser.scanner.linePtr; this.parser.scanner.setSource(source, unit.compilationResult); org.eclipse.jdt.internal.compiler.ast.TypeDeclaration enclosingTypeDeclaration= nodeSearcher.enclosingType; if (node instanceof AbstractMethodDeclaration) { ((AbstractMethodDeclaration)node).parseStatements(this.parser, unit); } else if (enclosingTypeDeclaration != null) { if (node instanceof org.eclipse.jdt.internal.compiler.ast.Initializer) { ((org.eclipse.jdt.internal.compiler.ast.Initializer)node).parseStatements(this.parser, enclosingTypeDeclaration, unit); } else if (node instanceof org.eclipse.jdt.internal.compiler.ast.TypeDeclaration) { ((org.eclipse.jdt.internal.compiler.ast.TypeDeclaration)node).parseMethods(this.parser, unit); } } // this is done to prevent any side effects on the compilation unit result // line separator positions array. this.parser.scanner.lineEnds= oldLineEnds; this.parser.scanner.linePtr= oldLinePtr; } } } if (unit.scope != null) { // fault in fields & methods unit.scope.faultInTypes(); if (unit.scope != null && verifyMethods) { // http://dev.eclipse.org/bugs/show_bug.cgi?id=23117 // verify inherited methods unit.scope.verifyMethods(this.lookupEnvironment.methodVerifier()); } // type checking unit.resolve(); // flow analysis if (analyzeCode) unit.analyseCode(); // code generation if (generateCode) unit.generateCode(); // finalize problems (suppressWarnings) unit.finalizeProblems(); } if (this.unitsToProcess != null) this.unitsToProcess[0]= null; // release reference to processed unit declaration this.requestor.acceptResult(unit.compilationResult.tagAsAccepted()); return unit; } catch (AbortCompilation e) { this.handleInternalException(e, unit); return unit == null ? this.unitsToProcess[0] : unit; } catch (Error e) { this.handleInternalException(e, unit, null); throw e; // rethrow } catch (RuntimeException e) { this.handleInternalException(e, unit, null); throw e; // rethrow } finally { // No reset is performed there anymore since, // within the CodeAssist (or related tools), // the compiler may be called *after* a call // to this resolve(...) method. And such a call // needs to have a compiler with a non-empty // environment. // this.reset(); } } /* * Internal API used to resolve a given compilation unit. Can run a subset of the compilation process */ public CompilationUnitDeclaration resolve( org.eclipse.jdt.internal.compiler.env.ICompilationUnit sourceUnit, boolean verifyMethods, boolean analyzeCode, boolean generateCode) { return resolve( null, /* no existing compilation unit declaration*/ sourceUnit, null/*no node searcher*/, verifyMethods, analyzeCode, generateCode); } boolean resolvedRequestedSourcesAndKeys(int unitIndexToProcess) { if (unitIndexToProcess < this.requestedSources.size() && unitIndexToProcess < this.requestedKeys.size()) return false; // must process at least this many units before checking to see if all are done Object[] sources= this.requestedSources.valueTable; for (int i= 0, l= sources.length; i < l; i++) if (sources[i] != null) return false; Object[] keys= this.requestedKeys.valueTable; for (int i= 0, l= keys.length; i < l; i++) if (keys[i] != null) return false; return true; } /* * Internal API used to resolve a given compilation unit. Can run a subset of the compilation process */ public CompilationUnitDeclaration resolve( CompilationUnitDeclaration unit, org.eclipse.jdt.internal.compiler.env.ICompilationUnit sourceUnit, boolean verifyMethods, boolean analyzeCode, boolean generateCode) { return resolve( unit, sourceUnit, null/*no node searcher*/, verifyMethods, analyzeCode, generateCode); } private void worked(int work) { if (this.monitor != null) { if (this.monitor.isCanceled()) throw new OperationCanceledException(); this.monitor.worked(work); } } }