1 | /******************************************************************************* |
---|---|
2 | * Copyright (c) 2000, 2022 IBM Corporation and others. |
3 | * |
4 | * This program and the accompanying materials |
5 | * are made available under the terms of the Eclipse Public License 2.0 |
6 | * which accompanies this distribution, and is available at |
7 | * https://www.eclipse.org/legal/epl-2.0/ |
8 | * |
9 | * SPDX-License-Identifier: EPL-2.0 |
10 | * |
11 | * Contributors: |
12 | * IBM Corporation - initial API and implementation |
13 | * Stephan Herrmann - Contribution for |
14 | * bug 363858 - [dom] early throwing of AbortCompilation causes NPE in CompilationUnitResolver |
15 | * Bug 466279 - [hovering] IAE on hover when annotation-based null analysis is enabled |
16 | *******************************************************************************/ |
17 | package org.eclipse.jdt.core.dom; |
18 | |
19 | import java.io.File; |
20 | import java.io.IOException; |
21 | import java.util.ArrayList; |
22 | import java.util.HashMap; |
23 | import java.util.Iterator; |
24 | import java.util.List; |
25 | import java.util.Map; |
26 | |
27 | import org.eclipse.core.resources.IProject; |
28 | import org.eclipse.core.runtime.IPath; |
29 | import org.eclipse.core.runtime.IProgressMonitor; |
30 | import org.eclipse.core.runtime.OperationCanceledException; |
31 | import org.eclipse.core.runtime.SubMonitor; |
32 | import org.eclipse.jdt.core.IClasspathEntry; |
33 | import org.eclipse.jdt.core.ICompilationUnit; |
34 | import org.eclipse.jdt.core.IJavaElement; |
35 | import org.eclipse.jdt.core.IJavaProject; |
36 | import org.eclipse.jdt.core.JavaCore; |
37 | import org.eclipse.jdt.core.JavaModelException; |
38 | import org.eclipse.jdt.core.WorkingCopyOwner; |
39 | import org.eclipse.jdt.core.compiler.CategorizedProblem; |
40 | import org.eclipse.jdt.core.compiler.CharOperation; |
41 | import org.eclipse.jdt.internal.compiler.CompilationResult; |
42 | import org.eclipse.jdt.internal.compiler.Compiler; |
43 | import org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies; |
44 | import org.eclipse.jdt.internal.compiler.ICompilerRequestor; |
45 | import org.eclipse.jdt.internal.compiler.IErrorHandlingPolicy; |
46 | import org.eclipse.jdt.internal.compiler.IProblemFactory; |
47 | import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; |
48 | import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; |
49 | import org.eclipse.jdt.internal.compiler.batch.FileSystem.Classpath; |
50 | import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; |
51 | import org.eclipse.jdt.internal.compiler.env.AccessRestriction; |
52 | import org.eclipse.jdt.internal.compiler.env.INameEnvironment; |
53 | import org.eclipse.jdt.internal.compiler.env.ISourceType; |
54 | import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; |
55 | import org.eclipse.jdt.internal.compiler.lookup.AnnotationBinding; |
56 | import org.eclipse.jdt.internal.compiler.lookup.Binding; |
57 | import org.eclipse.jdt.internal.compiler.lookup.ExtraCompilerModifiers; |
58 | import org.eclipse.jdt.internal.compiler.lookup.PackageBinding; |
59 | import org.eclipse.jdt.internal.compiler.parser.Parser; |
60 | import org.eclipse.jdt.internal.compiler.problem.AbortCompilation; |
61 | import org.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory; |
62 | import org.eclipse.jdt.internal.compiler.problem.ProblemReporter; |
63 | import org.eclipse.jdt.internal.compiler.util.HashtableOfObject; |
64 | import org.eclipse.jdt.internal.compiler.util.HashtableOfObjectToInt; |
65 | import org.eclipse.jdt.internal.compiler.util.Messages; |
66 | import org.eclipse.jdt.internal.compiler.util.Util; |
67 | import org.eclipse.jdt.internal.core.BinaryMember; |
68 | import org.eclipse.jdt.internal.core.BinaryModule; |
69 | import org.eclipse.jdt.internal.core.CancelableNameEnvironment; |
70 | import org.eclipse.jdt.internal.core.CancelableProblemFactory; |
71 | import org.eclipse.jdt.internal.core.ClasspathEntry; |
72 | import org.eclipse.jdt.internal.core.INameEnvironmentWithProgress; |
73 | import org.eclipse.jdt.internal.core.JavaProject; |
74 | import org.eclipse.jdt.internal.core.LocalVariable; |
75 | import org.eclipse.jdt.internal.core.NameLookup; |
76 | import org.eclipse.jdt.internal.core.SourceRefElement; |
77 | import org.eclipse.jdt.internal.core.SourceTypeElementInfo; |
78 | import org.eclipse.jdt.internal.core.util.BindingKeyResolver; |
79 | import org.eclipse.jdt.internal.core.util.CommentRecorderParser; |
80 | import org.eclipse.jdt.internal.core.util.DOMFinder; |
81 | |
82 | @SuppressWarnings({ "rawtypes", "unchecked" }) |
83 | class CompilationUnitResolver extends Compiler { |
84 | public static final int RESOLVE_BINDING = 0x1; |
85 | public static final int PARTIAL = 0x2; |
86 | public static final int STATEMENT_RECOVERY = 0x4; |
87 | public static final int IGNORE_METHOD_BODIES = 0x8; |
88 | public static final int BINDING_RECOVERY = 0x10; |
89 | public static final int INCLUDE_RUNNING_VM_BOOTCLASSPATH = 0x20; |
90 | |
91 | /* A list of int */ |
92 | static class IntArrayList { |
93 | public int[] list = new int[5]; |
94 | public int length = 0; |
95 | public void add(int i) { |
96 | if (this.list.length == this.length) { |
97 | System.arraycopy(this.list, 0, this.list = new int[this.length*2], 0, this.length); |
98 | } |
99 | this.list[this.length++] = i; |
100 | } |
101 | } |
102 | |
103 | /* |
104 | * The sources that were requested. |
105 | * Map from file name (char[]) to org.eclipse.jdt.internal.compiler.env.ICompilationUnit. |
106 | */ |
107 | HashtableOfObject requestedSources; |
108 | |
109 | /* |
110 | * The binding keys that were requested. |
111 | * Map from file name (char[]) to BindingKey (or ArrayList if multiple keys in the same file). |
112 | */ |
113 | HashtableOfObject requestedKeys; |
114 | |
115 | DefaultBindingResolver.BindingTables bindingTables; |
116 | |
117 | boolean hasCompilationAborted; |
118 | CategorizedProblem abortProblem; |
119 | |
120 | private IProgressMonitor monitor; |
121 | |
122 | /** |
123 | * Set to <code>true</code> if the receiver was initialized using a java project name environment |
124 | */ |
125 | boolean fromJavaProject; |
126 | |
127 | /** |
128 | * Answer a new CompilationUnitVisitor using the given name environment and compiler options. |
129 | * The environment and options will be in effect for the lifetime of the compiler. |
130 | * When the compiler is run, compilation results are sent to the given requestor. |
131 | * |
132 | * @param environment org.eclipse.jdt.internal.compiler.api.env.INameEnvironment |
133 | * Environment used by the compiler in order to resolve type and package |
134 | * names. The name environment implements the actual connection of the compiler |
135 | * to the outside world (for example, in batch mode the name environment is performing |
136 | * pure file accesses, reuse previous build state or connection to repositories). |
137 | * Note: the name environment is responsible for implementing the actual classpath |
138 | * rules. |
139 | * |
140 | * @param policy org.eclipse.jdt.internal.compiler.api.problem.IErrorHandlingPolicy |
141 | * Configurable part for problem handling, allowing the compiler client to |
142 | * specify the rules for handling problems (stop on first error or accumulate |
143 | * them all) and at the same time perform some actions such as opening a dialog |
144 | * in UI when compiling interactively. |
145 | * @see org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies |
146 | * |
147 | * @param compilerOptions The compiler options to use for the resolution. |
148 | * |
149 | * @param requestor org.eclipse.jdt.internal.compiler.api.ICompilerRequestor |
150 | * Component which will receive and persist all compilation results and is intended |
151 | * to consume them as they are produced. Typically, in a batch compiler, it is |
152 | * responsible for writing out the actual .class files to the file system. |
153 | * @see org.eclipse.jdt.internal.compiler.CompilationResult |
154 | * |
155 | * @param problemFactory org.eclipse.jdt.internal.compiler.api.problem.IProblemFactory |
156 | * Factory used inside the compiler to create problem descriptors. It allows the |
157 | * compiler client to supply its own representation of compilation problems in |
158 | * order to avoid object conversions. Note that the factory is not supposed |
159 | * to accumulate the created problems, the compiler will gather them all and hand |
160 | * them back as part of the compilation unit result. |
161 | */ |
162 | public CompilationUnitResolver( |
163 | INameEnvironment environment, |
164 | IErrorHandlingPolicy policy, |
165 | CompilerOptions compilerOptions, |
166 | ICompilerRequestor requestor, |
167 | IProblemFactory problemFactory, |
168 | IProgressMonitor monitor, |
169 | boolean fromJavaProject) { |
170 | |
171 | super(environment, policy, compilerOptions, requestor, problemFactory); |
172 | this.hasCompilationAborted = false; |
173 | this.monitor =monitor; |
174 | this.fromJavaProject = fromJavaProject; |
175 | } |
176 | |
177 | /* |
178 | * Add additional source types |
179 | */ |
180 | @Override |
181 | public void accept(ISourceType[] sourceTypes, PackageBinding packageBinding, AccessRestriction accessRestriction) { |
182 | // Need to reparse the entire source of the compilation unit so as to get source positions |
183 | // (case of processing a source that was not known by beginToCompile (e.g. when asking to createBinding)) |
184 | SourceTypeElementInfo sourceType = (SourceTypeElementInfo) sourceTypes[0]; |
185 | accept((org.eclipse.jdt.internal.compiler.env.ICompilationUnit) sourceType.getHandle().getCompilationUnit(), accessRestriction); |
186 | } |
187 | |
188 | @Override |
189 | public synchronized void accept(org.eclipse.jdt.internal.compiler.env.ICompilationUnit sourceUnit, AccessRestriction accessRestriction) { |
190 | super.accept(sourceUnit, accessRestriction); |
191 | } |
192 | |
193 | /** |
194 | * Add the initial set of compilation units into the loop |
195 | * -> build compilation unit declarations, their bindings and record their results. |
196 | */ |
197 | protected void beginToCompile(org.eclipse.jdt.internal.compiler.env.ICompilationUnit[] sourceUnits, String[] bindingKeys) { |
198 | int sourceLength = sourceUnits.length; |
199 | int keyLength = bindingKeys.length; |
200 | int maxUnits = sourceLength + keyLength; |
201 | this.totalUnits = 0; |
202 | this.unitsToProcess = new CompilationUnitDeclaration[maxUnits]; |
203 | int index = 0; |
204 | |
205 | // walks the source units |
206 | this.requestedSources = new HashtableOfObject(); |
207 | for (int i = 0; i < sourceLength; i++) { |
208 | org.eclipse.jdt.internal.compiler.env.ICompilationUnit sourceUnit = sourceUnits[i]; |
209 | CompilationUnitDeclaration parsedUnit; |
210 | CompilationResult unitResult = |
211 | new CompilationResult(sourceUnit, index++, maxUnits, this.options.maxProblemsPerUnit); |
212 | try { |
213 | if (this.options.verbose) { |
214 | this.out.println( |
215 | Messages.bind(Messages.compilation_request, |
216 | new String[] { |
217 | String.valueOf(index++ + 1), |
218 | String.valueOf(maxUnits), |
219 | new String(sourceUnit.getFileName()) |
220 | })); |
221 | } |
222 | if (this.parser instanceof CommentRecorderParser) { |
223 | ((CommentRecorderParser) this.parser).resetComments(); |
224 | } |
225 | // diet parsing for large collection of units |
226 | if (this.totalUnits < this.parseThreshold) { |
227 | parsedUnit = this.parser.parse(sourceUnit, unitResult); |
228 | } else { |
229 | parsedUnit = this.parser.dietParse(sourceUnit, unitResult); |
230 | } |
231 | // initial type binding creation |
232 | this.lookupEnvironment.buildTypeBindings(parsedUnit, null /*no access restriction*/); |
233 | addCompilationUnit(sourceUnit, parsedUnit); |
234 | this.requestedSources.put(unitResult.getFileName(), sourceUnit); |
235 | worked(1); |
236 | } finally { |
237 | sourceUnits[i] = null; // no longer hold onto the unit |
238 | } |
239 | } |
240 | |
241 | // walk the binding keys |
242 | this.requestedKeys = new HashtableOfObject(); |
243 | for (int i = 0; i < keyLength; i++) { |
244 | BindingKeyResolver resolver = new BindingKeyResolver(bindingKeys[i], this, this.lookupEnvironment); |
245 | resolver.parse(true/*pause after fully qualified name*/); |
246 | // 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. |
247 | // Skipping it will speed up performance because the call will open jars. (theodora) |
248 | CompilationUnitDeclaration parsedUnit = resolver.hasTypeName() ? resolver.getCompilationUnitDeclaration() : null; |
249 | if (parsedUnit != null) { |
250 | char[] fileName = parsedUnit.compilationResult.getFileName(); |
251 | Object existing = this.requestedKeys.get(fileName); |
252 | if (existing == null) |
253 | this.requestedKeys.put(fileName, resolver); |
254 | else if (existing instanceof ArrayList) |
255 | ((ArrayList) existing).add(resolver); |
256 | else { |
257 | ArrayList list = new ArrayList(); |
258 | list.add(existing); |
259 | list.add(resolver); |
260 | this.requestedKeys.put(fileName, list); |
261 | } |
262 | |
263 | } else { |
264 | char[] key = resolver.hasTypeName() |
265 | ? resolver.getKey().toCharArray() // binary binding |
266 | : resolver.hasModuleName() |
267 | ? resolver.moduleName() |
268 | : CharOperation.concatWith(resolver.compoundName(), '.'); // package binding or base type binding |
269 | this.requestedKeys.put(key, resolver); |
270 | } |
271 | worked(1); |
272 | } |
273 | |
274 | // binding resolution |
275 | this.lookupEnvironment.completeTypeBindings(); |
276 | } |
277 | |
278 | IBinding createBinding(String key) { |
279 | if (this.bindingTables == null) |
280 | throw new RuntimeException("Cannot be called outside ASTParser#createASTs(...)"); //$NON-NLS-1$ |
281 | BindingKeyResolver keyResolver = new BindingKeyResolver(key, this, this.lookupEnvironment); |
282 | Binding compilerBinding = keyResolver.getCompilerBinding(); |
283 | if (compilerBinding == null) return null; |
284 | DefaultBindingResolver resolver = new DefaultBindingResolver(this.lookupEnvironment, null/*no owner*/, this.bindingTables, false, this.fromJavaProject); |
285 | return resolver.getBinding(compilerBinding); |
286 | } |
287 | |
288 | public static CompilationUnit convert( |
289 | CompilationUnitDeclaration compilationUnitDeclaration, |
290 | char[] source, |
291 | int apiLevel, |
292 | Map options, |
293 | boolean needToResolveBindings, |
294 | WorkingCopyOwner owner, |
295 | DefaultBindingResolver.BindingTables bindingTables, |
296 | int flags, |
297 | IProgressMonitor monitor, |
298 | boolean fromJavaProject) { |
299 | return convert(compilationUnitDeclaration, source,apiLevel, options, needToResolveBindings, owner, bindingTables, |
300 | flags, monitor, fromJavaProject, null); |
301 | } |
302 | public static CompilationUnit convert( |
303 | CompilationUnitDeclaration compilationUnitDeclaration, |
304 | char[] source, |
305 | int apiLevel, |
306 | Map options, |
307 | boolean needToResolveBindings, |
308 | WorkingCopyOwner owner, |
309 | DefaultBindingResolver.BindingTables bindingTables, |
310 | int flags, |
311 | IProgressMonitor monitor, |
312 | boolean fromJavaProject, |
313 | IJavaProject project) { |
314 | BindingResolver resolver = null; |
315 | AST ast = AST.newAST(apiLevel, JavaCore.ENABLED.equals(options.get(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES))); |
316 | String sourceModeSetting = (String) options.get(JavaCore.COMPILER_SOURCE); |
317 | long sourceLevel = CompilerOptions.versionToJdkLevel(sourceModeSetting); |
318 | if (sourceLevel == 0) { |
319 | // unknown sourceModeSetting |
320 | sourceLevel = ClassFileConstants.JDK1_3; |
321 | } |
322 | ast.scanner.sourceLevel = sourceLevel; |
323 | String compliance = (String) options.get(JavaCore.COMPILER_COMPLIANCE); |
324 | long complianceLevel = CompilerOptions.versionToJdkLevel(compliance); |
325 | if (complianceLevel == 0) { |
326 | // unknown sourceModeSetting |
327 | complianceLevel = sourceLevel; |
328 | } |
329 | ast.scanner.complianceLevel = complianceLevel; |
330 | ast.setDefaultNodeFlag(ASTNode.ORIGINAL); |
331 | CompilationUnit compilationUnit = null; |
332 | ASTConverter converter = new ASTConverter(options, needToResolveBindings, monitor); |
333 | if (needToResolveBindings) { |
334 | resolver = new DefaultBindingResolver(compilationUnitDeclaration.scope, owner, bindingTables, (flags & ICompilationUnit.ENABLE_BINDINGS_RECOVERY) != 0, fromJavaProject); |
335 | ast.setFlag(flags | AST.RESOLVED_BINDINGS); |
336 | } else { |
337 | resolver = new BindingResolver(); |
338 | ast.setFlag(flags); |
339 | } |
340 | ast.setBindingResolver(resolver); |
341 | converter.setAST(ast); |
342 | converter.docParser.setProjectPath(CompilationUnitResolver.getProjectPath(project)); |
343 | converter.docParser.setProjectSrcClasspath(CompilationUnitResolver.getSourceClassPaths(project)); |
344 | compilationUnit = converter.convert(compilationUnitDeclaration, source); |
345 | compilationUnit.setLineEndTable(compilationUnitDeclaration.compilationResult.getLineSeparatorPositions()); |
346 | ast.setDefaultNodeFlag(0); |
347 | ast.setOriginalModificationCount(ast.modificationCount()); |
348 | return compilationUnit; |
349 | } |
350 | |
351 | /** |
352 | * @return absolute path in local file system, may return {@code null} |
353 | */ |
354 | private static String getProjectPath(IJavaProject project) { |
355 | if(project == null) { |
356 | return null; |
357 | } |
358 | IProject rp = project.getProject(); |
359 | if (rp == null) { |
360 | return null; |
361 | } |
362 | IPath location = rp.getLocation(); |
363 | if(location == null) { |
364 | return null; |
365 | } |
366 | return location.toOSString(); |
367 | } |
368 | private static ArrayList<String> getSourceClassPaths(IJavaProject project) { |
369 | ArrayList<String> srcClassPath = new ArrayList<>(); |
370 | if(project == null) |
371 | return srcClassPath; |
372 | if(project.getProject() == null) |
373 | return srcClassPath; |
374 | IClasspathEntry[] resolvedClasspath = null; |
375 | try { |
376 | resolvedClasspath = project.getResolvedClasspath(true); |
377 | } catch (JavaModelException e) { |
378 | //do nothing |
379 | } |
380 | |
381 | for (IClasspathEntry entry : resolvedClasspath) { |
382 | if (entry.getEntryKind() == IClasspathEntry.CPE_SOURCE) { |
383 | if(entry instanceof ClasspathEntry) { |
384 | IPath path = ((ClasspathEntry)entry).getPath(); |
385 | srcClassPath.add(path.removeFirstSegments(1).toString()); |
386 | } |
387 | } |
388 | } |
389 | return srcClassPath; |
390 | } |
391 | |
392 | |
393 | protected static CompilerOptions getCompilerOptions(Map options, boolean statementsRecovery) { |
394 | CompilerOptions compilerOptions = new CompilerOptions(options); |
395 | compilerOptions.performMethodsFullRecovery = statementsRecovery; |
396 | compilerOptions.performStatementsRecovery = statementsRecovery; |
397 | compilerOptions.parseLiteralExpressionsAsConstants = false; |
398 | compilerOptions.storeAnnotations = true /*store annotations in the bindings*/; |
399 | compilerOptions.ignoreSourceFolderWarningOption = true; |
400 | return compilerOptions; |
401 | } |
402 | /* |
403 | * Low-level API performing the actual compilation |
404 | */ |
405 | protected static IErrorHandlingPolicy getHandlingPolicy() { |
406 | |
407 | // passes the initial set of files to the batch oracle (to avoid finding more than once the same units when case insensitive match) |
408 | return new IErrorHandlingPolicy() { |
409 | @Override |
410 | public boolean stopOnFirstError() { |
411 | return false; |
412 | } |
413 | @Override |
414 | public boolean proceedOnErrors() { |
415 | return false; // stop if there are some errors |
416 | } |
417 | @Override |
418 | public boolean ignoreAllErrors() { |
419 | return false; |
420 | } |
421 | }; |
422 | } |
423 | |
424 | /* |
425 | * Answer the component to which will be handed back compilation results from the compiler |
426 | */ |
427 | protected static ICompilerRequestor getRequestor() { |
428 | return new ICompilerRequestor() { |
429 | @Override |
430 | public void acceptResult(CompilationResult compilationResult) { |
431 | // do nothing |
432 | } |
433 | }; |
434 | } |
435 | |
436 | @Override |
437 | public void initializeParser() { |
438 | this.parser = new CommentRecorderParser(this.problemReporter, false); |
439 | } |
440 | @Override |
441 | public void process(CompilationUnitDeclaration unit, int i) { |
442 | // don't resolve a second time the same unit (this would create the same binding twice) |
443 | char[] fileName = unit.compilationResult.getFileName(); |
444 | if (this.requestedKeys.get(fileName) == null && this.requestedSources.get(fileName) == null) |
445 | super.process(unit, i); |
446 | } |
447 | /* |
448 | * Compiler crash recovery in case of unexpected runtime exceptions |
449 | */ |
450 | @Override |
451 | protected void handleInternalException( |
452 | Throwable internalException, |
453 | CompilationUnitDeclaration unit, |
454 | CompilationResult result) { |
455 | super.handleInternalException(internalException, unit, result); |
456 | if (unit != null) { |
457 | removeUnresolvedBindings(unit); |
458 | } |
459 | } |
460 | |
461 | /* |
462 | * Compiler recovery in case of internal AbortCompilation event |
463 | */ |
464 | @Override |
465 | protected void handleInternalException( |
466 | AbortCompilation abortException, |
467 | CompilationUnitDeclaration unit) { |
468 | super.handleInternalException(abortException, unit); |
469 | if (unit != null) { |
470 | removeUnresolvedBindings(unit); |
471 | } |
472 | this.hasCompilationAborted = true; |
473 | this.abortProblem = abortException.problem; |
474 | } |
475 | |
476 | public static void parse(ICompilationUnit[] compilationUnits, ASTRequestor astRequestor, int apiLevel, Map options, int flags, IProgressMonitor monitor) { |
477 | CompilerOptions compilerOptions = new CompilerOptions(options); |
478 | compilerOptions.ignoreMethodBodies = (flags & ICompilationUnit.IGNORE_METHOD_BODIES) != 0; |
479 | Parser parser = new CommentRecorderParser( |
480 | new ProblemReporter( |
481 | DefaultErrorHandlingPolicies.proceedWithAllProblems(), |
482 | compilerOptions, |
483 | new DefaultProblemFactory()), |
484 | false); |
485 | int unitLength = compilationUnits.length; |
486 | SubMonitor subMonitor = SubMonitor.convert(monitor); |
487 | for (int i = 0; i < unitLength; i++) { |
488 | subMonitor.setWorkRemaining(unitLength - i); |
489 | org.eclipse.jdt.internal.compiler.env.ICompilationUnit sourceUnit = (org.eclipse.jdt.internal.compiler.env.ICompilationUnit) compilationUnits[i]; |
490 | CompilationResult compilationResult = new CompilationResult(sourceUnit, 0, 0, |
491 | compilerOptions.maxProblemsPerUnit); |
492 | CompilationUnitDeclaration compilationUnitDeclaration = parser.dietParse(sourceUnit, compilationResult); |
493 | |
494 | if (compilationUnitDeclaration.ignoreMethodBodies) { |
495 | compilationUnitDeclaration.ignoreFurtherInvestigation = true; |
496 | // if initial diet parse did not work, no need to dig into method bodies. |
497 | continue; |
498 | } |
499 | |
500 | //fill the methods bodies in order for the code to be generated |
501 | //real parse of the method.... |
502 | org.eclipse.jdt.internal.compiler.ast.TypeDeclaration[] types = compilationUnitDeclaration.types; |
503 | if (types != null) { |
504 | for (int j = 0, typeLength = types.length; j < typeLength; j++) { |
505 | types[j].parseMethods(parser, compilationUnitDeclaration); |
506 | } |
507 | } |
508 | |
509 | // convert AST |
510 | CompilationUnit node = convert(compilationUnitDeclaration, parser.scanner.getSource(), apiLevel, options, |
511 | false/* don't resolve binding */, null/* no owner needed */, null/* no binding table needed */, |
512 | flags /* flags */, subMonitor.split(1), true); |
513 | node.setTypeRoot(compilationUnits[i]); |
514 | |
515 | // accept AST |
516 | astRequestor.acceptAST(compilationUnits[i], node); |
517 | } |
518 | } |
519 | public static void parse( |
520 | String[] sourceUnits, |
521 | String[] encodings, |
522 | FileASTRequestor astRequestor, |
523 | int apiLevel, |
524 | Map options, |
525 | int flags, |
526 | IProgressMonitor monitor) { |
527 | CompilerOptions compilerOptions = new CompilerOptions(options); |
528 | compilerOptions.ignoreMethodBodies = (flags & ICompilationUnit.IGNORE_METHOD_BODIES) != 0; |
529 | Parser parser = new CommentRecorderParser( |
530 | new ProblemReporter( |
531 | DefaultErrorHandlingPolicies.proceedWithAllProblems(), |
532 | compilerOptions, |
533 | new DefaultProblemFactory()), |
534 | false); |
535 | int unitLength = sourceUnits.length; |
536 | SubMonitor subMonitor = SubMonitor.convert(monitor, unitLength); |
537 | for (int i = 0; i < unitLength; i++) { |
538 | SubMonitor iterationMonitor = subMonitor.split(1); |
539 | char[] contents = null; |
540 | String encoding = encodings != null ? encodings[i] : null; |
541 | try { |
542 | contents = Util.getFileCharContent(new File(sourceUnits[i]), encoding); |
543 | } catch(IOException e) { |
544 | // go to the next unit |
545 | continue; |
546 | } |
547 | if (contents == null) { |
548 | // go to the next unit |
549 | continue; |
550 | } |
551 | org.eclipse.jdt.internal.compiler.batch.CompilationUnit compilationUnit = new org.eclipse.jdt.internal.compiler.batch.CompilationUnit(contents, sourceUnits[i], encoding); |
552 | org.eclipse.jdt.internal.compiler.env.ICompilationUnit sourceUnit = compilationUnit; |
553 | CompilationResult compilationResult = new CompilationResult(sourceUnit, 0, 0, compilerOptions.maxProblemsPerUnit); |
554 | CompilationUnitDeclaration compilationUnitDeclaration = parser.dietParse(sourceUnit, compilationResult); |
555 | |
556 | if (compilationUnitDeclaration.ignoreMethodBodies) { |
557 | compilationUnitDeclaration.ignoreFurtherInvestigation = true; |
558 | // if initial diet parse did not work, no need to dig into method bodies. |
559 | continue; |
560 | } |
561 | |
562 | //fill the methods bodies in order for the code to be generated |
563 | //real parse of the method.... |
564 | org.eclipse.jdt.internal.compiler.ast.TypeDeclaration[] types = compilationUnitDeclaration.types; |
565 | if (types != null) { |
566 | for (int j = 0, typeLength = types.length; j < typeLength; j++) { |
567 | types[j].parseMethods(parser, compilationUnitDeclaration); |
568 | } |
569 | } |
570 | |
571 | // convert AST |
572 | CompilationUnit node = convert(compilationUnitDeclaration, parser.scanner.getSource(), apiLevel, options, |
573 | false/* don't resolve binding */, null/* no owner needed */, null/* no binding table needed */, |
574 | flags /* flags */, iterationMonitor, true); |
575 | node.setTypeRoot(null); |
576 | |
577 | // accept AST |
578 | astRequestor.acceptAST(sourceUnits[i], node); |
579 | } |
580 | } |
581 | |
582 | public static CompilationUnitDeclaration parse( |
583 | org.eclipse.jdt.internal.compiler.env.ICompilationUnit sourceUnit, |
584 | NodeSearcher nodeSearcher, |
585 | Map settings, |
586 | int flags) { |
587 | return parse(sourceUnit, nodeSearcher, settings, flags, null); |
588 | } |
589 | public static CompilationUnitDeclaration parse( |
590 | org.eclipse.jdt.internal.compiler.env.ICompilationUnit sourceUnit, |
591 | NodeSearcher nodeSearcher, |
592 | Map settings, |
593 | int flags, IJavaProject project) { |
594 | if (sourceUnit == null) { |
595 | throw new IllegalStateException(); |
596 | } |
597 | CompilerOptions compilerOptions = new CompilerOptions(settings); |
598 | boolean statementsRecovery = (flags & ICompilationUnit.ENABLE_STATEMENTS_RECOVERY) != 0; |
599 | compilerOptions.performMethodsFullRecovery = statementsRecovery; |
600 | compilerOptions.performStatementsRecovery = statementsRecovery; |
601 | compilerOptions.ignoreMethodBodies = (flags & ICompilationUnit.IGNORE_METHOD_BODIES) != 0; |
602 | Parser parser = new CommentRecorderParser( |
603 | new ProblemReporter( |
604 | DefaultErrorHandlingPolicies.proceedWithAllProblems(), |
605 | compilerOptions, |
606 | new DefaultProblemFactory()), |
607 | false); |
608 | if (project != null) { |
609 | parser.javadocParser.setProjectPath(getProjectPath(project)); |
610 | parser.javadocParser.setProjectSrcClasspath(getSourceClassPaths(project)); |
611 | |
612 | } |
613 | CompilationResult compilationResult = new CompilationResult(sourceUnit, 0, 0, compilerOptions.maxProblemsPerUnit); |
614 | CompilationUnitDeclaration compilationUnitDeclaration = parser.dietParse(sourceUnit, compilationResult); |
615 | |
616 | if (compilationUnitDeclaration.ignoreMethodBodies) { |
617 | compilationUnitDeclaration.ignoreFurtherInvestigation = true; |
618 | // if initial diet parse did not work, no need to dig into method bodies. |
619 | return compilationUnitDeclaration; |
620 | } |
621 | |
622 | if (nodeSearcher != null) { |
623 | char[] source = parser.scanner.getSource(); |
624 | int searchPosition = nodeSearcher.position; |
625 | if (searchPosition < 0 || searchPosition > source.length) { |
626 | // the position is out of range. There is no need to search for a node. |
627 | return compilationUnitDeclaration; |
628 | } |
629 | |
630 | compilationUnitDeclaration.traverse(nodeSearcher, compilationUnitDeclaration.scope); |
631 | |
632 | org.eclipse.jdt.internal.compiler.ast.ASTNode node = nodeSearcher.found; |
633 | if (node == null) { |
634 | return compilationUnitDeclaration; |
635 | } |
636 | |
637 | org.eclipse.jdt.internal.compiler.ast.TypeDeclaration enclosingTypeDeclaration = nodeSearcher.enclosingType; |
638 | |
639 | if (node instanceof AbstractMethodDeclaration) { |
640 | ((AbstractMethodDeclaration)node).parseStatements(parser, compilationUnitDeclaration); |
641 | } else if (enclosingTypeDeclaration != null) { |
642 | if (node instanceof org.eclipse.jdt.internal.compiler.ast.Initializer) { |
643 | ((org.eclipse.jdt.internal.compiler.ast.Initializer) node).parseStatements(parser, enclosingTypeDeclaration, compilationUnitDeclaration); |
644 | } else if (node instanceof org.eclipse.jdt.internal.compiler.ast.TypeDeclaration) { |
645 | ((org.eclipse.jdt.internal.compiler.ast.TypeDeclaration)node).parseMethods(parser, compilationUnitDeclaration); |
646 | } |
647 | } |
648 | } else { |
649 | //fill the methods bodies in order for the code to be generated |
650 | //real parse of the method.... |
651 | org.eclipse.jdt.internal.compiler.ast.TypeDeclaration[] types = compilationUnitDeclaration.types; |
652 | if (types != null) { |
653 | for (int j = 0, typeLength = types.length; j < typeLength; j++) { |
654 | types[j].parseMethods(parser, compilationUnitDeclaration); |
655 | } |
656 | } |
657 | } |
658 | return compilationUnitDeclaration; |
659 | } |
660 | |
661 | public static void resolve( |
662 | ICompilationUnit[] compilationUnits, |
663 | String[] bindingKeys, |
664 | ASTRequestor requestor, |
665 | int apiLevel, |
666 | Map options, |
667 | IJavaProject javaProject, |
668 | WorkingCopyOwner owner, |
669 | int flags, |
670 | IProgressMonitor monitor) { |
671 | |
672 | CancelableNameEnvironment environment = null; |
673 | CancelableProblemFactory problemFactory = null; |
674 | try { |
675 | int amountOfWork = (compilationUnits.length + bindingKeys.length) * 2; // 1 for beginToCompile, 1 for resolve |
676 | SubMonitor subMonitor = SubMonitor.convert(monitor, amountOfWork); |
677 | environment = new CancelableNameEnvironment(((JavaProject) javaProject), owner, subMonitor); |
678 | problemFactory = new CancelableProblemFactory(subMonitor); |
679 | CompilerOptions compilerOptions = getCompilerOptions(options, (flags & ICompilationUnit.ENABLE_STATEMENTS_RECOVERY) != 0); |
680 | compilerOptions.ignoreMethodBodies = (flags & ICompilationUnit.IGNORE_METHOD_BODIES) != 0; |
681 | CompilationUnitResolver resolver = |
682 | new CompilationUnitResolver( |
683 | environment, |
684 | getHandlingPolicy(), |
685 | compilerOptions, |
686 | getRequestor(), |
687 | problemFactory, |
688 | subMonitor, |
689 | javaProject != null); |
690 | resolver.resolve(compilationUnits, bindingKeys, requestor, apiLevel, options, owner, flags); |
691 | if (NameLookup.VERBOSE) { |
692 | environment.printTimeSpent(); |
693 | } |
694 | } catch (JavaModelException e) { |
695 | // project doesn't exist -> simple parse without resolving |
696 | parse(compilationUnits, requestor, apiLevel, options, flags, monitor); |
697 | } finally { |
698 | if (environment != null) { |
699 | environment.setMonitor(null); // don't hold a reference to this external object |
700 | } |
701 | if (problemFactory != null) { |
702 | problemFactory.monitor = null; // don't hold a reference to this external object |
703 | } |
704 | } |
705 | } |
706 | public static void resolve( |
707 | String[] sourceUnits, |
708 | String[] encodings, |
709 | String[] bindingKeys, |
710 | FileASTRequestor requestor, |
711 | int apiLevel, |
712 | Map options, |
713 | List classpaths, |
714 | int flags, |
715 | IProgressMonitor monitor) { |
716 | |
717 | INameEnvironmentWithProgress environment = null; |
718 | CancelableProblemFactory problemFactory = null; |
719 | try { |
720 | int amountOfWork = (sourceUnits.length + bindingKeys.length) * 2; // 1 for beginToCompile, 1 for resolve |
721 | SubMonitor subMonitor = SubMonitor.convert(monitor, amountOfWork); |
722 | Classpath[] allEntries = new Classpath[classpaths.size()]; |
723 | classpaths.toArray(allEntries); |
724 | environment = new NameEnvironmentWithProgress(allEntries, null, subMonitor); |
725 | problemFactory = new CancelableProblemFactory(subMonitor); |
726 | CompilerOptions compilerOptions = getCompilerOptions(options, (flags & ICompilationUnit.ENABLE_STATEMENTS_RECOVERY) != 0); |
727 | compilerOptions.ignoreMethodBodies = (flags & ICompilationUnit.IGNORE_METHOD_BODIES) != 0; |
728 | CompilationUnitResolver resolver = |
729 | new CompilationUnitResolver( |
730 | environment, |
731 | getHandlingPolicy(), |
732 | compilerOptions, |
733 | getRequestor(), |
734 | problemFactory, |
735 | subMonitor, |
736 | false); |
737 | resolver.resolve(sourceUnits, encodings, bindingKeys, requestor, apiLevel, options, flags); |
738 | if (NameLookup.VERBOSE && (environment instanceof CancelableNameEnvironment)) { |
739 | CancelableNameEnvironment cancelableNameEnvironment = (CancelableNameEnvironment) environment; |
740 | cancelableNameEnvironment.printTimeSpent(); |
741 | } |
742 | } finally { |
743 | if (environment != null) { |
744 | environment.setMonitor(null); // don't hold a reference to this external object |
745 | } |
746 | if (problemFactory != null) { |
747 | problemFactory.monitor = null; // don't hold a reference to this external object |
748 | } |
749 | } |
750 | } |
751 | public static CompilationUnitDeclaration resolve( |
752 | org.eclipse.jdt.internal.compiler.env.ICompilationUnit sourceUnit, |
753 | IJavaProject javaProject, |
754 | List classpaths, |
755 | NodeSearcher nodeSearcher, |
756 | Map options, |
757 | WorkingCopyOwner owner, |
758 | int flags, |
759 | IProgressMonitor monitor) throws JavaModelException { |
760 | |
761 | CompilationUnitDeclaration unit = null; |
762 | INameEnvironmentWithProgress environment = null; |
763 | CancelableProblemFactory problemFactory = null; |
764 | CompilationUnitResolver resolver = null; |
765 | try { |
766 | if (javaProject == null) { |
767 | Classpath[] allEntries = new Classpath[classpaths.size()]; |
768 | classpaths.toArray(allEntries); |
769 | environment = new NameEnvironmentWithProgress(allEntries, null, monitor); |
770 | } else { |
771 | environment = new CancelableNameEnvironment((JavaProject) javaProject, owner, monitor); |
772 | } |
773 | problemFactory = new CancelableProblemFactory(monitor); |
774 | CompilerOptions compilerOptions = getCompilerOptions(options, (flags & ICompilationUnit.ENABLE_STATEMENTS_RECOVERY) != 0); |
775 | boolean ignoreMethodBodies = (flags & ICompilationUnit.IGNORE_METHOD_BODIES) != 0; |
776 | compilerOptions.ignoreMethodBodies = ignoreMethodBodies; |
777 | resolver = |
778 | new CompilationUnitResolver( |
779 | environment, |
780 | getHandlingPolicy(), |
781 | compilerOptions, |
782 | getRequestor(), |
783 | problemFactory, |
784 | monitor, |
785 | javaProject != null); |
786 | boolean analyzeAndGenerateCode = !ignoreMethodBodies; |
787 | unit = |
788 | resolver.resolve( |
789 | null, // no existing compilation unit declaration |
790 | sourceUnit, |
791 | nodeSearcher, |
792 | true, // method verification |
793 | analyzeAndGenerateCode, // analyze code |
794 | analyzeAndGenerateCode); // generate code |
795 | if (resolver.hasCompilationAborted) { |
796 | // the bindings could not be resolved due to missing types in name environment |
797 | // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=86541 |
798 | CompilationUnitDeclaration unitDeclaration = parse(sourceUnit, nodeSearcher, options, flags); |
799 | if (unit != null) { |
800 | final int problemCount = unit.compilationResult.problemCount; |
801 | if (problemCount != 0) { |
802 | unitDeclaration.compilationResult.problems = new CategorizedProblem[problemCount]; |
803 | System.arraycopy(unit.compilationResult.problems, 0, unitDeclaration.compilationResult.problems, 0, problemCount); |
804 | unitDeclaration.compilationResult.problemCount = problemCount; |
805 | } |
806 | } else if (resolver.abortProblem != null) { |
807 | unitDeclaration.compilationResult.problemCount = 1; |
808 | unitDeclaration.compilationResult.problems = new CategorizedProblem[] { resolver.abortProblem }; |
809 | } |
810 | return unitDeclaration; |
811 | } |
812 | if (NameLookup.VERBOSE && environment instanceof CancelableNameEnvironment) { |
813 | CancelableNameEnvironment cancelableNameEnvironment = (CancelableNameEnvironment) environment; |
814 | cancelableNameEnvironment.printTimeSpent(); |
815 | } |
816 | return unit; |
817 | } finally { |
818 | if (environment != null) { |
819 | // don't hold a reference to this external object |
820 | environment.setMonitor(null); |
821 | } |
822 | if (problemFactory != null) { |
823 | problemFactory.monitor = null; // don't hold a reference to this external object |
824 | } |
825 | } |
826 | } |
827 | public static IBinding[] resolve( |
828 | final IJavaElement[] elements, |
829 | int apiLevel, |
830 | Map compilerOptions, |
831 | IJavaProject javaProject, |
832 | WorkingCopyOwner owner, |
833 | int flags, |
834 | IProgressMonitor monitor) { |
835 | |
836 | final int length = elements.length; |
837 | final HashMap sourceElementPositions = new HashMap(); // a map from ICompilationUnit to int[] (positions in elements) |
838 | int cuNumber = 0; |
839 | final HashtableOfObjectToInt binaryElementPositions = new HashtableOfObjectToInt(); // a map from String (binding key) to int (position in elements) |
840 | for (int i = 0; i < length; i++) { |
841 | IJavaElement element = elements[i]; |
842 | if (!(element instanceof SourceRefElement)) |
843 | throw new IllegalStateException(element + " is not part of a compilation unit or class file"); //$NON-NLS-1$ |
844 | Object cu = element.getAncestor(IJavaElement.COMPILATION_UNIT); |
845 | if (cu != null) { |
846 | // source member |
847 | IntArrayList intList = (IntArrayList) sourceElementPositions.get(cu); |
848 | if (intList == null) { |
849 | sourceElementPositions.put(cu, intList = new IntArrayList()); |
850 | cuNumber++; |
851 | } |
852 | intList.add(i); |
853 | } else { |
854 | // binary member or method argument |
855 | try { |
856 | String key; |
857 | if (element instanceof BinaryMember) |
858 | key = ((BinaryMember) element).getKey(true/*open to get resolved info*/); |
859 | else if (element instanceof LocalVariable) |
860 | key = ((LocalVariable) element).getKey(true/*open to get resolved info*/); |
861 | else if (element instanceof org.eclipse.jdt.internal.core.TypeParameter) |
862 | key = ((org.eclipse.jdt.internal.core.TypeParameter) element).getKey(true/*open to get resolved info*/); |
863 | else if (element instanceof BinaryModule) |
864 | key = ((BinaryModule) element).getKey(true); |
865 | else |
866 | throw new IllegalArgumentException(element + " has an unexpected type"); //$NON-NLS-1$ |
867 | binaryElementPositions.put(key, i); |
868 | } catch (JavaModelException e) { |
869 | throw new IllegalArgumentException(element + " does not exist", e); //$NON-NLS-1$ |
870 | } |
871 | } |
872 | } |
873 | ICompilationUnit[] cus = new ICompilationUnit[cuNumber]; |
874 | sourceElementPositions.keySet().toArray(cus); |
875 | |
876 | int bindingKeyNumber = binaryElementPositions.size(); |
877 | String[] bindingKeys = new String[bindingKeyNumber]; |
878 | binaryElementPositions.keysToArray(bindingKeys); |
879 | |
880 | class Requestor extends ASTRequestor { |
881 | IBinding[] bindings = new IBinding[length]; |
882 | @Override |
883 | public void acceptAST(ICompilationUnit source, CompilationUnit ast) { |
884 | // TODO (jerome) optimize to visit the AST only once |
885 | IntArrayList intList = (IntArrayList) sourceElementPositions.get(source); |
886 | for (int i = 0; i < intList.length; i++) { |
887 | final int index = intList.list[i]; |
888 | SourceRefElement element = (SourceRefElement) elements[index]; |
889 | DOMFinder finder = new DOMFinder(ast, element, true/*resolve binding*/); |
890 | try { |
891 | finder.search(); |
892 | } catch (JavaModelException e) { |
893 | throw new IllegalArgumentException(element + " does not exist", e); //$NON-NLS-1$ |
894 | } |
895 | this.bindings[index] = finder.foundBinding; |
896 | } |
897 | } |
898 | @Override |
899 | public void acceptBinding(String bindingKey, IBinding binding) { |
900 | int index = binaryElementPositions.get(bindingKey); |
901 | this.bindings[index] = binding; |
902 | } |
903 | } |
904 | Requestor requestor = new Requestor(); |
905 | resolve(cus, bindingKeys, requestor, apiLevel, compilerOptions, javaProject, owner, flags, monitor); |
906 | return requestor.bindings; |
907 | } |
908 | /* |
909 | * When unit result is about to be accepted, removed back pointers |
910 | * to unresolved bindings |
911 | */ |
912 | public void removeUnresolvedBindings(CompilationUnitDeclaration compilationUnitDeclaration) { |
913 | final org.eclipse.jdt.internal.compiler.ast.TypeDeclaration[] types = compilationUnitDeclaration.types; |
914 | if (types != null) { |
915 | for (int i = 0, max = types.length; i < max; i++) { |
916 | removeUnresolvedBindings(types[i]); |
917 | } |
918 | } |
919 | } |
920 | private void removeUnresolvedBindings(org.eclipse.jdt.internal.compiler.ast.TypeDeclaration type) { |
921 | final org.eclipse.jdt.internal.compiler.ast.TypeDeclaration[] memberTypes = type.memberTypes; |
922 | if (memberTypes != null) { |
923 | for (int i = 0, max = memberTypes.length; i < max; i++){ |
924 | removeUnresolvedBindings(memberTypes[i]); |
925 | } |
926 | } |
927 | if (type.binding != null && (type.binding.modifiers & ExtraCompilerModifiers.AccUnresolved) != 0) { |
928 | type.binding = null; |
929 | } |
930 | |
931 | final org.eclipse.jdt.internal.compiler.ast.FieldDeclaration[] fields = type.fields; |
932 | if (fields != null) { |
933 | for (int i = 0, max = fields.length; i < max; i++){ |
934 | if (fields[i].binding != null && (fields[i].binding.modifiers & ExtraCompilerModifiers.AccUnresolved) != 0) { |
935 | fields[i].binding = null; |
936 | } |
937 | } |
938 | } |
939 | |
940 | final AbstractMethodDeclaration[] methods = type.methods; |
941 | if (methods != null) { |
942 | for (int i = 0, max = methods.length; i < max; i++){ |
943 | if (methods[i].binding != null && (methods[i].binding.modifiers & ExtraCompilerModifiers.AccUnresolved) != 0) { |
944 | methods[i].binding = null; |
945 | } |
946 | } |
947 | } |
948 | } |
949 | |
950 | private void resolve( |
951 | ICompilationUnit[] compilationUnits, |
952 | String[] bindingKeys, |
953 | ASTRequestor astRequestor, |
954 | int apiLevel, |
955 | Map compilerOptions, |
956 | WorkingCopyOwner owner, |
957 | int flags) { |
958 | |
959 | // temporarily connect ourselves to the ASTResolver - must disconnect when done |
960 | astRequestor.compilationUnitResolver = this; |
961 | this.bindingTables = new DefaultBindingResolver.BindingTables(); |
962 | CompilationUnitDeclaration unit = null; |
963 | try { |
964 | int length = compilationUnits.length; |
965 | org.eclipse.jdt.internal.compiler.env.ICompilationUnit[] sourceUnits = new org.eclipse.jdt.internal.compiler.env.ICompilationUnit[length]; |
966 | System.arraycopy(compilationUnits, 0, sourceUnits, 0, length); |
967 | beginToCompile(sourceUnits, bindingKeys); |
968 | // process all units (some more could be injected in the loop by the lookup environment) |
969 | for (int i = 0; i < this.totalUnits; i++) { |
970 | if (resolvedRequestedSourcesAndKeys(i)) { |
971 | // no need to keep resolving if no more ASTs and no more binding keys are needed |
972 | // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=114935 |
973 | // cleanup remaining units |
974 | for (; i < this.totalUnits; i++) { |
975 | this.unitsToProcess[i].cleanUp(); |
976 | this.unitsToProcess[i] = null; |
977 | } |
978 | break; |
979 | } |
980 | unit = this.unitsToProcess[i]; |
981 | try { |
982 | super.process(unit, i); // this.process(...) is optimized to not process already known units |
983 | |
984 | // requested AST |
985 | char[] fileName = unit.compilationResult.getFileName(); |
986 | ICompilationUnit source = (ICompilationUnit) this.requestedSources.get(fileName); |
987 | if (source != null) { |
988 | // convert AST |
989 | CompilationResult compilationResult = unit.compilationResult; |
990 | org.eclipse.jdt.internal.compiler.env.ICompilationUnit sourceUnit = compilationResult.compilationUnit; |
991 | char[] contents = sourceUnit.getContents(); |
992 | AST ast = AST.newAST(apiLevel, JavaCore.ENABLED.equals(this.options.getMap().get(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES))); |
993 | ast.setFlag(flags | AST.RESOLVED_BINDINGS); |
994 | ast.setDefaultNodeFlag(ASTNode.ORIGINAL); |
995 | ASTConverter converter = new ASTConverter(compilerOptions, true/*need to resolve bindings*/, this.monitor); |
996 | BindingResolver resolver = new DefaultBindingResolver(unit.scope, owner, this.bindingTables, (flags & ICompilationUnit.ENABLE_BINDINGS_RECOVERY) != 0, this.fromJavaProject); |
997 | ast.setBindingResolver(resolver); |
998 | converter.setAST(ast); |
999 | CompilationUnit compilationUnit = converter.convert(unit, contents); |
1000 | compilationUnit.setTypeRoot(source); |
1001 | compilationUnit.setLineEndTable(compilationResult.getLineSeparatorPositions()); |
1002 | ast.setDefaultNodeFlag(0); |
1003 | ast.setOriginalModificationCount(ast.modificationCount()); |
1004 | |
1005 | // pass it to requestor |
1006 | astRequestor.acceptAST(source, compilationUnit); |
1007 | |
1008 | worked(1); |
1009 | |
1010 | // 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 |
1011 | this.requestedSources.put(fileName, null); // mark it as removed |
1012 | } |
1013 | |
1014 | // requested binding |
1015 | Object key = this.requestedKeys.get(fileName); |
1016 | if (key != null) { |
1017 | if (key instanceof BindingKeyResolver) { |
1018 | reportBinding(key, astRequestor, owner, unit); |
1019 | worked(1); |
1020 | } else if (key instanceof ArrayList) { |
1021 | Iterator iterator = ((ArrayList) key).iterator(); |
1022 | while (iterator.hasNext()) { |
1023 | reportBinding(iterator.next(), astRequestor, owner, unit); |
1024 | worked(1); |
1025 | } |
1026 | } |
1027 | |
1028 | // 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 |
1029 | this.requestedKeys.put(fileName, null); // mark it as removed |
1030 | } |
1031 | } finally { |
1032 | // cleanup compilation unit result |
1033 | unit.cleanUp(); |
1034 | } |
1035 | this.unitsToProcess[i] = null; // release reference to processed unit declaration |
1036 | this.requestor.acceptResult(unit.compilationResult.tagAsAccepted()); |
1037 | } |
1038 | |
1039 | // remaining binding keys |
1040 | DefaultBindingResolver resolver = new DefaultBindingResolver(this.lookupEnvironment, owner, this.bindingTables, (flags & ICompilationUnit.ENABLE_BINDINGS_RECOVERY) != 0, true); |
1041 | Object[] keys = this.requestedKeys.valueTable; |
1042 | for (int j = 0, keysLength = keys.length; j < keysLength; j++) { |
1043 | BindingKeyResolver keyResolver = (BindingKeyResolver) keys[j]; |
1044 | if (keyResolver == null) continue; |
1045 | Binding compilerBinding = keyResolver.getCompilerBinding(); |
1046 | IBinding binding = compilerBinding == null ? null : resolver.getBinding(compilerBinding); |
1047 | // pass it to requestor |
1048 | astRequestor.acceptBinding(((BindingKeyResolver) this.requestedKeys.valueTable[j]).getKey(), binding); |
1049 | worked(1); |
1050 | } |
1051 | } catch (OperationCanceledException e) { |
1052 | throw e; |
1053 | } catch (AbortCompilation e) { |
1054 | this.handleInternalException(e, unit); |
1055 | } catch (Error | RuntimeException e) { |
1056 | this.handleInternalException(e, unit, null); |
1057 | throw e; // rethrow |
1058 | } finally { |
1059 | // disconnect ourselves from ast requestor |
1060 | astRequestor.compilationUnitResolver = null; |
1061 | } |
1062 | } |
1063 | |
1064 | private void resolve( |
1065 | String[] sourceCompilationUnits, |
1066 | String[] encodings, |
1067 | String[] bindingKeys, |
1068 | FileASTRequestor astRequestor, |
1069 | int apiLevel, |
1070 | Map compilerOptions, |
1071 | int flags) { |
1072 | |
1073 | // temporarily connect ourselves to the ASTResolver - must disconnect when done |
1074 | astRequestor.compilationUnitResolver = this; |
1075 | this.bindingTables = new DefaultBindingResolver.BindingTables(); |
1076 | CompilationUnitDeclaration unit = null; |
1077 | try { |
1078 | int length = sourceCompilationUnits.length; |
1079 | org.eclipse.jdt.internal.compiler.env.ICompilationUnit[] sourceUnits = new org.eclipse.jdt.internal.compiler.env.ICompilationUnit[length]; |
1080 | int count = 0; |
1081 | for (int i = 0; i < length; i++) { |
1082 | char[] contents = null; |
1083 | String encoding = encodings != null ? encodings[i] : null; |
1084 | String sourceUnitPath = sourceCompilationUnits[i]; |
1085 | try { |
1086 | contents = Util.getFileCharContent(new File(sourceUnitPath), encoding); |
1087 | } catch(IOException e) { |
1088 | // go to the next unit |
1089 | continue; |
1090 | } |
1091 | if (contents == null) { |
1092 | // go to the next unit |
1093 | continue; |
1094 | } |
1095 | sourceUnits[count++] = new org.eclipse.jdt.internal.compiler.batch.CompilationUnit(contents, sourceUnitPath, encoding); |
1096 | } |
1097 | if (count < length) { |
1098 | org.eclipse.jdt.internal.compiler.env.ICompilationUnit[] newArray = new org.eclipse.jdt.internal.compiler.env.ICompilationUnit[count]; |
1099 | System.arraycopy(sourceUnits, 0, newArray, 0, count); |
1100 | sourceUnits = newArray; |
1101 | } |
1102 | beginToCompile(sourceUnits, bindingKeys); |
1103 | // process all units (some more could be injected in the loop by the lookup environment) |
1104 | for (int i = 0; i < this.totalUnits; i++) { |
1105 | if (resolvedRequestedSourcesAndKeys(i)) { |
1106 | // no need to keep resolving if no more ASTs and no more binding keys are needed |
1107 | // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=114935 |
1108 | // cleanup remaining units |
1109 | for (; i < this.totalUnits; i++) { |
1110 | this.unitsToProcess[i].cleanUp(); |
1111 | this.unitsToProcess[i] = null; |
1112 | } |
1113 | break; |
1114 | } |
1115 | unit = this.unitsToProcess[i]; |
1116 | try { |
1117 | super.process(unit, i); // this.process(...) is optimized to not process already known units |
1118 | |
1119 | // requested AST |
1120 | char[] fileName = unit.compilationResult.getFileName(); |
1121 | org.eclipse.jdt.internal.compiler.env.ICompilationUnit source = (org.eclipse.jdt.internal.compiler.env.ICompilationUnit) this.requestedSources.get(fileName); |
1122 | if (source != null) { |
1123 | // convert AST |
1124 | CompilationResult compilationResult = unit.compilationResult; |
1125 | org.eclipse.jdt.internal.compiler.env.ICompilationUnit sourceUnit = compilationResult.compilationUnit; |
1126 | char[] contents = sourceUnit.getContents(); |
1127 | AST ast = AST.newAST(apiLevel, JavaCore.ENABLED.equals(compilerOptions.get(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES))); |
1128 | ast.setFlag(flags | AST.RESOLVED_BINDINGS); |
1129 | ast.setDefaultNodeFlag(ASTNode.ORIGINAL); |
1130 | ASTConverter converter = new ASTConverter(compilerOptions, true/*need to resolve bindings*/, this.monitor); |
1131 | BindingResolver resolver = new DefaultBindingResolver(unit.scope, null, this.bindingTables, (flags & ICompilationUnit.ENABLE_BINDINGS_RECOVERY) != 0, this.fromJavaProject); |
1132 | ast.setBindingResolver(resolver); |
1133 | converter.setAST(ast); |
1134 | CompilationUnit compilationUnit = converter.convert(unit, contents); |
1135 | compilationUnit.setTypeRoot(null); |
1136 | compilationUnit.setLineEndTable(compilationResult.getLineSeparatorPositions()); |
1137 | ast.setDefaultNodeFlag(0); |
1138 | ast.setOriginalModificationCount(ast.modificationCount()); |
1139 | |
1140 | // pass it to requestor |
1141 | astRequestor.acceptAST(new String(source.getFileName()), compilationUnit); |
1142 | |
1143 | worked(1); |
1144 | |
1145 | // 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 |
1146 | this.requestedSources.put(fileName, null); // mark it as removed |
1147 | } |
1148 | |
1149 | // requested binding |
1150 | Object key = this.requestedKeys.get(fileName); |
1151 | if (key != null) { |
1152 | if (key instanceof BindingKeyResolver) { |
1153 | reportBinding(key, astRequestor, unit); |
1154 | worked(1); |
1155 | } else if (key instanceof ArrayList) { |
1156 | Iterator iterator = ((ArrayList) key).iterator(); |
1157 | while (iterator.hasNext()) { |
1158 | reportBinding(iterator.next(), astRequestor, unit); |
1159 | worked(1); |
1160 | } |
1161 | } |
1162 | |
1163 | // 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 |
1164 | this.requestedKeys.put(fileName, null); // mark it as removed |
1165 | } |
1166 | } finally { |
1167 | // cleanup compilation unit result |
1168 | unit.cleanUp(); |
1169 | } |
1170 | this.unitsToProcess[i] = null; // release reference to processed unit declaration |
1171 | this.requestor.acceptResult(unit.compilationResult.tagAsAccepted()); |
1172 | } |
1173 | |
1174 | // remaining binding keys |
1175 | DefaultBindingResolver resolver = new DefaultBindingResolver(this.lookupEnvironment, null, this.bindingTables, (flags & ICompilationUnit.ENABLE_BINDINGS_RECOVERY) != 0, true); |
1176 | Object[] keys = this.requestedKeys.valueTable; |
1177 | for (int j = 0, keysLength = keys.length; j < keysLength; j++) { |
1178 | BindingKeyResolver keyResolver = (BindingKeyResolver) keys[j]; |
1179 | if (keyResolver == null) continue; |
1180 | Binding compilerBinding = keyResolver.getCompilerBinding(); |
1181 | IBinding binding = compilerBinding == null ? null : resolver.getBinding(compilerBinding); |
1182 | // pass it to requestor |
1183 | astRequestor.acceptBinding(((BindingKeyResolver) this.requestedKeys.valueTable[j]).getKey(), binding); |
1184 | worked(1); |
1185 | } |
1186 | } catch (OperationCanceledException e) { |
1187 | throw e; |
1188 | } catch (AbortCompilation e) { |
1189 | this.handleInternalException(e, unit); |
1190 | } catch (Error | RuntimeException e) { |
1191 | this.handleInternalException(e, unit, null); |
1192 | throw e; // rethrow |
1193 | } finally { |
1194 | // disconnect ourselves from ast requestor |
1195 | astRequestor.compilationUnitResolver = null; |
1196 | } |
1197 | } |
1198 | |
1199 | private void reportBinding(Object key, ASTRequestor astRequestor, WorkingCopyOwner owner, CompilationUnitDeclaration unit) { |
1200 | BindingKeyResolver keyResolver = (BindingKeyResolver) key; |
1201 | Binding compilerBinding = keyResolver.getCompilerBinding(); |
1202 | if (compilerBinding != null) { |
1203 | DefaultBindingResolver resolver = new DefaultBindingResolver(unit.scope, owner, this.bindingTables, false, this.fromJavaProject); |
1204 | AnnotationBinding annotationBinding = keyResolver.getAnnotationBinding(); |
1205 | IBinding binding; |
1206 | if (annotationBinding != null) { |
1207 | binding = resolver.getAnnotationInstance(annotationBinding); |
1208 | } else { |
1209 | binding = resolver.getBinding(compilerBinding); |
1210 | } |
1211 | if (binding != null) |
1212 | astRequestor.acceptBinding(keyResolver.getKey(), binding); |
1213 | } |
1214 | } |
1215 | |
1216 | private void reportBinding(Object key, FileASTRequestor astRequestor, CompilationUnitDeclaration unit) { |
1217 | BindingKeyResolver keyResolver = (BindingKeyResolver) key; |
1218 | Binding compilerBinding = keyResolver.getCompilerBinding(); |
1219 | if (compilerBinding != null) { |
1220 | DefaultBindingResolver resolver = new DefaultBindingResolver(unit.scope, null, this.bindingTables, false, this.fromJavaProject); |
1221 | AnnotationBinding annotationBinding = keyResolver.getAnnotationBinding(); |
1222 | IBinding binding; |
1223 | if (annotationBinding != null) { |
1224 | binding = resolver.getAnnotationInstance(annotationBinding); |
1225 | } else { |
1226 | binding = resolver.getBinding(compilerBinding); |
1227 | } |
1228 | if (binding != null) |
1229 | astRequestor.acceptBinding(keyResolver.getKey(), binding); |
1230 | } |
1231 | } |
1232 | |
1233 | private CompilationUnitDeclaration resolve( |
1234 | CompilationUnitDeclaration unit, |
1235 | org.eclipse.jdt.internal.compiler.env.ICompilationUnit sourceUnit, |
1236 | NodeSearcher nodeSearcher, |
1237 | boolean verifyMethods, |
1238 | boolean analyzeCode, |
1239 | boolean generateCode) { |
1240 | |
1241 | try { |
1242 | |
1243 | if (unit == null) { |
1244 | // build and record parsed units |
1245 | this.parseThreshold = 0; // will request a full parse |
1246 | beginToCompile(new org.eclipse.jdt.internal.compiler.env.ICompilationUnit[] { sourceUnit }); |
1247 | // find the right unit from what was injected via accept(ICompilationUnit,..): |
1248 | for (int i=0, max = this.totalUnits; i < max; i++) { |
1249 | CompilationUnitDeclaration currentCompilationUnitDeclaration = this.unitsToProcess[i]; |
1250 | if (currentCompilationUnitDeclaration != null |
1251 | && currentCompilationUnitDeclaration.compilationResult.compilationUnit == sourceUnit) { |
1252 | unit = currentCompilationUnitDeclaration; |
1253 | break; |
1254 | } |
1255 | } |
1256 | if (unit == null) { |
1257 | unit = this.unitsToProcess[0]; // fall back to old behavior |
1258 | } |
1259 | } else { |
1260 | // initial type binding creation |
1261 | this.lookupEnvironment.buildTypeBindings(unit, null /*no access restriction*/); |
1262 | |
1263 | // binding resolution |
1264 | this.lookupEnvironment.completeTypeBindings(); |
1265 | } |
1266 | |
1267 | if (nodeSearcher == null) { |
1268 | this.parser.getMethodBodies(unit); // no-op if method bodies have already been parsed |
1269 | } else { |
1270 | int searchPosition = nodeSearcher.position; |
1271 | char[] source = sourceUnit.getContents(); |
1272 | int length = source.length; |
1273 | if (searchPosition >= 0 && searchPosition <= length) { |
1274 | unit.traverse(nodeSearcher, unit.scope); |
1275 | |
1276 | org.eclipse.jdt.internal.compiler.ast.ASTNode node = nodeSearcher.found; |
1277 | |
1278 | if (node != null) { |
1279 | // save existing values to restore them at the end of the parsing process |
1280 | // see bug 47079 for more details |
1281 | int[] oldLineEnds = this.parser.scanner.lineEnds; |
1282 | int oldLinePtr = this.parser.scanner.linePtr; |
1283 | |
1284 | this.parser.scanner.setSource(source, unit.compilationResult); |
1285 | |
1286 | org.eclipse.jdt.internal.compiler.ast.TypeDeclaration enclosingTypeDeclaration = nodeSearcher.enclosingType; |
1287 | if (node instanceof AbstractMethodDeclaration) { |
1288 | ((AbstractMethodDeclaration)node).parseStatements(this.parser, unit); |
1289 | } else if (enclosingTypeDeclaration != null) { |
1290 | if (node instanceof org.eclipse.jdt.internal.compiler.ast.Initializer) { |
1291 | ((org.eclipse.jdt.internal.compiler.ast.Initializer) node).parseStatements(this.parser, enclosingTypeDeclaration, unit); |
1292 | } else if (node instanceof org.eclipse.jdt.internal.compiler.ast.TypeDeclaration) { |
1293 | ((org.eclipse.jdt.internal.compiler.ast.TypeDeclaration)node).parseMethods(this.parser, unit); |
1294 | } |
1295 | } |
1296 | // this is done to prevent any side effects on the compilation unit result |
1297 | // line separator positions array. |
1298 | this.parser.scanner.lineEnds = oldLineEnds; |
1299 | this.parser.scanner.linePtr = oldLinePtr; |
1300 | } |
1301 | } |
1302 | } |
1303 | |
1304 | if (unit.scope != null) { |
1305 | CompilationUnitDeclaration previousUnit = this.lookupEnvironment.unitBeingCompleted; |
1306 | this.lookupEnvironment.unitBeingCompleted = unit; |
1307 | try { |
1308 | // fault in fields & methods |
1309 | unit.scope.faultInTypes(); |
1310 | if (unit.scope != null && verifyMethods) { |
1311 | // http://dev.eclipse.org/bugs/show_bug.cgi?id=23117 |
1312 | // verify inherited methods |
1313 | unit.scope.verifyMethods(this.lookupEnvironment.methodVerifier()); |
1314 | } |
1315 | // type checking |
1316 | unit.resolve(); |
1317 | |
1318 | // flow analysis |
1319 | if (analyzeCode) unit.analyseCode(); |
1320 | |
1321 | // code generation |
1322 | if (generateCode) unit.generateCode(); |
1323 | |
1324 | // finalize problems (suppressWarnings) |
1325 | unit.finalizeProblems(); |
1326 | } finally { |
1327 | this.lookupEnvironment.unitBeingCompleted = previousUnit; // paranoia, always null in org.eclipse.jdt.core.tests.dom.RunAllTests |
1328 | } |
1329 | } |
1330 | if (this.unitsToProcess != null) this.unitsToProcess[0] = null; // release reference to processed unit declaration |
1331 | this.requestor.acceptResult(unit.compilationResult.tagAsAccepted()); |
1332 | return unit; |
1333 | } catch (AbortCompilation e) { |
1334 | this.handleInternalException(e, unit); |
1335 | return unit == null ? this.unitsToProcess[0] : unit; |
1336 | } catch (Error | RuntimeException e) { |
1337 | this.handleInternalException(e, unit, null); |
1338 | throw e; // rethrow |
1339 | } finally { |
1340 | // No reset is performed there anymore since, |
1341 | // within the CodeAssist (or related tools), |
1342 | // the compiler may be called *after* a call |
1343 | // to this resolve(...) method. And such a call |
1344 | // needs to have a compiler with a non-empty |
1345 | // environment. |
1346 | // this.reset(); |
1347 | } |
1348 | } |
1349 | /* |
1350 | * Internal API used to resolve a given compilation unit. Can run a subset of the compilation process |
1351 | */ |
1352 | @Override |
1353 | public CompilationUnitDeclaration resolve( |
1354 | org.eclipse.jdt.internal.compiler.env.ICompilationUnit sourceUnit, |
1355 | boolean verifyMethods, |
1356 | boolean analyzeCode, |
1357 | boolean generateCode) { |
1358 | |
1359 | return resolve( |
1360 | null, /* no existing compilation unit declaration*/ |
1361 | sourceUnit, |
1362 | null/*no node searcher*/, |
1363 | verifyMethods, |
1364 | analyzeCode, |
1365 | generateCode); |
1366 | } |
1367 | |
1368 | boolean resolvedRequestedSourcesAndKeys(int unitIndexToProcess) { |
1369 | if (unitIndexToProcess < this.requestedSources.size() && unitIndexToProcess < this.requestedKeys.size()) |
1370 | return false; // must process at least this many units before checking to see if all are done |
1371 | |
1372 | Object[] sources = this.requestedSources.valueTable; |
1373 | for (int i = 0, l = sources.length; i < l; i++) |
1374 | if (sources[i] != null) return false; |
1375 | Object[] keys = this.requestedKeys.valueTable; |
1376 | for (int i = 0, l = keys.length; i < l; i++) |
1377 | if (keys[i] != null) return false; |
1378 | return true; |
1379 | } |
1380 | |
1381 | /* |
1382 | * Internal API used to resolve a given compilation unit. Can run a subset of the compilation process |
1383 | */ |
1384 | @Override |
1385 | public CompilationUnitDeclaration resolve( |
1386 | CompilationUnitDeclaration unit, |
1387 | org.eclipse.jdt.internal.compiler.env.ICompilationUnit sourceUnit, |
1388 | boolean verifyMethods, |
1389 | boolean analyzeCode, |
1390 | boolean generateCode) { |
1391 | |
1392 | return resolve( |
1393 | unit, |
1394 | sourceUnit, |
1395 | null/*no node searcher*/, |
1396 | verifyMethods, |
1397 | analyzeCode, |
1398 | generateCode); |
1399 | } |
1400 | |
1401 | private void worked(int work) { |
1402 | if (this.monitor != null) { |
1403 | if (this.monitor.isCanceled()) |
1404 | throw new OperationCanceledException(); |
1405 | this.monitor.worked(work); |
1406 | } |
1407 | } |
1408 | } |
1409 |
Members