Source code: org/eclipse/jdt/core/dom/CompilationUnitResolver.java
1 /*******************************************************************************
2 * Copyright (c) 2000, 2004 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Common Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/cpl-v10.html
7 *
8 * Contributors:
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11
12 package org.eclipse.jdt.core.dom;
13
14 import java.util.Map;
15
16 import org.eclipse.core.runtime.IProgressMonitor;
17 import org.eclipse.core.runtime.OperationCanceledException;
18 import org.eclipse.jdt.core.IJavaProject;
19 //import org.eclipse.jdt.core.JavaCore;
20 import org.eclipse.jdt.core.JavaModelException;
21 import org.eclipse.jdt.core.WorkingCopyOwner;
22 import org.eclipse.jdt.core.compiler.IProblem;
23 import org.eclipse.jdt.internal.codeassist.ISearchRequestor;
24 import org.eclipse.jdt.internal.compiler.CompilationResult;
25 import org.eclipse.jdt.internal.compiler.Compiler;
26 import org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies;
27 import org.eclipse.jdt.internal.compiler.ICompilerRequestor;
28 import org.eclipse.jdt.internal.compiler.IErrorHandlingPolicy;
29 import org.eclipse.jdt.internal.compiler.IProblemFactory;
30 import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
31 import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
32 import org.eclipse.jdt.internal.compiler.env.INameEnvironment;
33 import org.eclipse.jdt.internal.compiler.env.ISourceType;
34 import org.eclipse.jdt.internal.compiler.env.NameEnvironmentAnswer;
35 import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
36 import org.eclipse.jdt.internal.compiler.lookup.CompilerModifiers;
37 import org.eclipse.jdt.internal.compiler.lookup.PackageBinding;
38 import org.eclipse.jdt.internal.compiler.parser.Parser;
39 import org.eclipse.jdt.internal.compiler.parser.SourceTypeConverter;
40 import org.eclipse.jdt.internal.compiler.problem.AbortCompilation;
41 import org.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory;
42 import org.eclipse.jdt.internal.compiler.problem.ProblemReporter;
43 import org.eclipse.jdt.internal.core.BasicCompilationUnit;
44 import org.eclipse.jdt.internal.core.JavaProject;
45 import org.eclipse.jdt.internal.core.SearchableEnvironment;
46 import org.eclipse.jdt.internal.core.util.CommentRecorderParser;
47
48 class CompilationUnitResolver extends Compiler {
49
50 static class CancelableNameEnvironment extends SearchableEnvironment {
51 IProgressMonitor monitor;
52
53 CancelableNameEnvironment(JavaProject project, WorkingCopyOwner owner, IProgressMonitor monitor) throws JavaModelException {
54 super(project, owner);
55 this.monitor = monitor;
56 }
57
58 private void checkCanceled() {
59 if (monitor != null && monitor.isCanceled())
60 throw new AbortCompilation(true/*silent*/, new OperationCanceledException());
61 }
62
63 public void findPackages(char[] prefix, ISearchRequestor requestor) {
64 checkCanceled();
65 super.findPackages(prefix, requestor);
66 }
67
68 public NameEnvironmentAnswer findType(char[] name, char[][] packageName) {
69 checkCanceled();
70 return super.findType(name, packageName);
71 }
72
73 public NameEnvironmentAnswer findType(char[][] compoundTypeName) {
74 checkCanceled();
75 return super.findType(compoundTypeName);
76 }
77
78 public void findTypes(char[] prefix, ISearchRequestor storage) {
79 checkCanceled();
80 super.findTypes(prefix, storage);
81 }
82 }
83
84 static class CancelableProblemFactory extends DefaultProblemFactory {
85 IProgressMonitor monitor;
86
87 CancelableProblemFactory(IProgressMonitor monitor) {
88 super();
89 this.monitor = monitor;
90 }
91
92 public IProblem createProblem(char[] originatingFileName, int problemId, String[] problemArguments, String[] messageArguments, int severity, int startPosition, int endPosition, int lineNumber) {
93 if (monitor != null && monitor.isCanceled())
94 throw new AbortCompilation(true/*silent*/, new OperationCanceledException());
95 return super.createProblem(originatingFileName, problemId, problemArguments, messageArguments, severity, startPosition, endPosition, lineNumber);
96 }
97 }
98
99 /**
100 * Answer a new CompilationUnitVisitor using the given name environment and compiler options.
101 * The environment and options will be in effect for the lifetime of the compiler.
102 * When the compiler is run, compilation results are sent to the given requestor.
103 *
104 * @param environment org.eclipse.jdt.internal.compiler.api.env.INameEnvironment
105 * Environment used by the compiler in order to resolve type and package
106 * names. The name environment implements the actual connection of the compiler
107 * to the outside world (for example, in batch mode the name environment is performing
108 * pure file accesses, reuse previous build state or connection to repositories).
109 * Note: the name environment is responsible for implementing the actual classpath
110 * rules.
111 *
112 * @param policy org.eclipse.jdt.internal.compiler.api.problem.IErrorHandlingPolicy
113 * Configurable part for problem handling, allowing the compiler client to
114 * specify the rules for handling problems (stop on first error or accumulate
115 * them all) and at the same time perform some actions such as opening a dialog
116 * in UI when compiling interactively.
117 * @see org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies
118 *
119 * @param settings The settings to use for the resolution.
120 *
121 * @param requestor org.eclipse.jdt.internal.compiler.api.ICompilerRequestor
122 * Component which will receive and persist all compilation results and is intended
123 * to consume them as they are produced. Typically, in a batch compiler, it is
124 * responsible for writing out the actual .class files to the file system.
125 * @see org.eclipse.jdt.internal.compiler.CompilationResult
126 *
127 * @param problemFactory org.eclipse.jdt.internal.compiler.api.problem.IProblemFactory
128 * Factory used inside the compiler to create problem descriptors. It allows the
129 * compiler client to supply its own representation of compilation problems in
130 * order to avoid object conversions. Note that the factory is not supposed
131 * to accumulate the created problems, the compiler will gather them all and hand
132 * them back as part of the compilation unit result.
133 */
134 public CompilationUnitResolver(
135 INameEnvironment environment,
136 IErrorHandlingPolicy policy,
137 Map settings,
138 ICompilerRequestor requestor,
139 IProblemFactory problemFactory) {
140
141 super(environment, policy, settings, requestor, problemFactory, false);
142 }
143
144 /*
145 * Add additional source types
146 */
147 public void accept(ISourceType[] sourceTypes, PackageBinding packageBinding) {
148 CompilationResult result =
149 new CompilationResult(sourceTypes[0].getFileName(), 1, 1, this.options.maxProblemsPerUnit);
150 // need to hold onto this
151 CompilationUnitDeclaration unit =
152 SourceTypeConverter.buildCompilationUnit(
153 sourceTypes,//sourceTypes[0] is always toplevel here
154 SourceTypeConverter.FIELD_AND_METHOD // need field and methods
155 | SourceTypeConverter.MEMBER_TYPE // need member types
156 | SourceTypeConverter.FIELD_INITIALIZATION, // need field initialization: see bug 40476
157 this.lookupEnvironment.problemReporter,
158 result);
159
160 if (unit != null) {
161 this.lookupEnvironment.buildTypeBindings(unit);
162 this.lookupEnvironment.completeTypeBindings(unit, true);
163 }
164 }
165
166 /*
167 * Low-level API performing the actual compilation
168 */
169 protected static IErrorHandlingPolicy getHandlingPolicy() {
170
171 // passes the initial set of files to the batch oracle (to avoid finding more than once the same units when case insensitive match)
172 return new IErrorHandlingPolicy() {
173 public boolean stopOnFirstError() {
174 return false;
175 }
176 public boolean proceedOnErrors() {
177 return false; // stop if there are some errors
178 }
179 };
180 }
181
182 /*
183 * Answer the component to which will be handed back compilation results from the compiler
184 */
185 protected static ICompilerRequestor getRequestor() {
186 return new ICompilerRequestor() {
187 public void acceptResult(CompilationResult compilationResult) {
188 // do nothing
189 }
190 };
191 }
192
193 /* (non-Javadoc)
194 * @see org.eclipse.jdt.internal.compiler.Compiler#initializeParser()
195 */
196 public void initializeParser() {
197 this.parser = new CommentRecorderParser(this.problemReporter, false);
198 }
199 /*
200 * Compiler crash recovery in case of unexpected runtime exceptions
201 */
202 protected void handleInternalException(
203 Throwable internalException,
204 CompilationUnitDeclaration unit,
205 CompilationResult result) {
206 super.handleInternalException(internalException, unit, result);
207 if (unit != null) {
208 removeUnresolvedBindings(unit);
209 }
210 }
211
212 /*
213 * Compiler recovery in case of internal AbortCompilation event
214 */
215 protected void handleInternalException(
216 AbortCompilation abortException,
217 CompilationUnitDeclaration unit) {
218 super.handleInternalException(abortException, unit);
219 if (unit != null) {
220 removeUnresolvedBindings(unit);
221 }
222 }
223
224 public static CompilationUnitDeclaration parse(char[] source, NodeSearcher nodeSearcher, Map settings) {
225 if (source == null) {
226 throw new IllegalArgumentException();
227 }
228 CompilerOptions compilerOptions = new CompilerOptions(settings);
229 Parser parser = new CommentRecorderParser(
230 new ProblemReporter(
231 DefaultErrorHandlingPolicies.proceedWithAllProblems(),
232 compilerOptions,
233 new DefaultProblemFactory()),
234 false);
235 org.eclipse.jdt.internal.compiler.env.ICompilationUnit sourceUnit =
236 new org.eclipse.jdt.internal.compiler.batch.CompilationUnit(
237 source,
238 "", //$NON-NLS-1$
239 compilerOptions.defaultEncoding);
240 CompilationUnitDeclaration compilationUnitDeclaration = parser.dietParse(sourceUnit, new CompilationResult(sourceUnit, 0, 0, compilerOptions.maxProblemsPerUnit));
241
242 if (compilationUnitDeclaration.ignoreMethodBodies) {
243 compilationUnitDeclaration.ignoreFurtherInvestigation = true;
244 // if initial diet parse did not work, no need to dig into method bodies.
245 return null;
246 }
247
248 if (nodeSearcher != null) {
249 int searchPosition = nodeSearcher.position;
250 if (searchPosition < 0 || searchPosition > source.length) {
251 // the position is out of range. There is no need to search for a node.
252 return compilationUnitDeclaration;
253 }
254
255 compilationUnitDeclaration.traverse(nodeSearcher, compilationUnitDeclaration.scope);
256
257 org.eclipse.jdt.internal.compiler.ast.ASTNode node = nodeSearcher.found;
258 if (node == null) {
259 return compilationUnitDeclaration;
260 }
261
262 org.eclipse.jdt.internal.compiler.ast.TypeDeclaration enclosingTypeDeclaration = nodeSearcher.enclosingType;
263
264 if (node instanceof AbstractMethodDeclaration) {
265 ((AbstractMethodDeclaration)node).parseStatements(parser, compilationUnitDeclaration);
266 } else if (enclosingTypeDeclaration != null) {
267 if (node instanceof org.eclipse.jdt.internal.compiler.ast.Initializer) {
268 ((org.eclipse.jdt.internal.compiler.ast.Initializer) node).parseStatements(parser, enclosingTypeDeclaration, compilationUnitDeclaration);
269 } else {
270 ((org.eclipse.jdt.internal.compiler.ast.TypeDeclaration)node).parseMethod(parser, compilationUnitDeclaration);
271 }
272 }
273 } else {
274 //fill the methods bodies in order for the code to be generated
275 //real parse of the method....
276 parser.scanner.setSource(source);
277 org.eclipse.jdt.internal.compiler.ast.TypeDeclaration[] types = compilationUnitDeclaration.types;
278 if (types != null) {
279 for (int i = types.length; --i >= 0;)
280 types[i].parseMethod(parser, compilationUnitDeclaration);
281 }
282 }
283 return compilationUnitDeclaration;
284 }
285
286 public static CompilationUnitDeclaration resolve(
287 char[] source,
288 char[][] packageName,
289 String unitName,
290 IJavaProject javaProject,
291 NodeSearcher nodeSearcher,
292 Map options,
293 boolean cleanUp,
294 WorkingCopyOwner owner,
295 IProgressMonitor monitor)
296 throws JavaModelException {
297
298 CompilationUnitDeclaration unit = null;
299 CancelableNameEnvironment environment = null;
300 CancelableProblemFactory problemFactory = null;
301 try {
302 environment = new CancelableNameEnvironment(((JavaProject)javaProject), owner, monitor);
303 problemFactory = new CancelableProblemFactory(monitor);
304 CompilationUnitResolver resolver =
305 new CompilationUnitResolver(
306 environment,
307 getHandlingPolicy(),
308 options,
309 getRequestor(),
310 problemFactory);
311
312 unit =
313 resolver.resolve(
314 null, // no existing compilation unit declaration
315 new BasicCompilationUnit(
316 source,
317 packageName,
318 unitName,
319 javaProject),
320 nodeSearcher,
321 true, // method verification
322 true, // analyze code
323 true); // generate code
324 return unit;
325 } finally {
326 if (environment != null) {
327 environment.monitor = null; // don't hold a reference to this external object
328 }
329 if (problemFactory != null) {
330 problemFactory.monitor = null; // don't hold a reference to this external object
331 }
332 if (cleanUp && unit != null) {
333 unit.cleanUp();
334 }
335 }
336 }
337 /*
338 * When unit result is about to be accepted, removed back pointers
339 * to unresolved bindings
340 */
341 public void removeUnresolvedBindings(CompilationUnitDeclaration compilationUnitDeclaration) {
342 final org.eclipse.jdt.internal.compiler.ast.TypeDeclaration[] types = compilationUnitDeclaration.types;
343 if (types != null) {
344 for (int i = 0, max = types.length; i < max; i++) {
345 removeUnresolvedBindings(types[i]);
346 }
347 }
348 }
349 private void removeUnresolvedBindings(org.eclipse.jdt.internal.compiler.ast.TypeDeclaration type) {
350 final org.eclipse.jdt.internal.compiler.ast.TypeDeclaration[] memberTypes = type.memberTypes;
351 if (memberTypes != null) {
352 for (int i = 0, max = memberTypes.length; i < max; i++){
353 removeUnresolvedBindings(memberTypes[i]);
354 }
355 }
356 if (type.binding != null && (type.binding.modifiers & CompilerModifiers.AccUnresolved) != 0) {
357 type.binding = null;
358 }
359
360 final org.eclipse.jdt.internal.compiler.ast.FieldDeclaration[] fields = type.fields;
361 if (fields != null) {
362 for (int i = 0, max = fields.length; i < max; i++){
363 if (fields[i].binding != null && (fields[i].binding.modifiers & CompilerModifiers.AccUnresolved) != 0) {
364 fields[i].binding = null;
365 }
366 }
367 }
368
369 final AbstractMethodDeclaration[] methods = type.methods;
370 if (methods != null) {
371 for (int i = 0, max = methods.length; i < max; i++){
372 if (methods[i].binding != null && (methods[i].binding.modifiers & CompilerModifiers.AccUnresolved) != 0) {
373 methods[i].binding = null;
374 }
375 }
376 }
377 }
378
379 private CompilationUnitDeclaration resolve(
380 CompilationUnitDeclaration unit,
381 org.eclipse.jdt.internal.compiler.env.ICompilationUnit sourceUnit,
382 NodeSearcher nodeSearcher,
383 boolean verifyMethods,
384 boolean analyzeCode,
385 boolean generateCode) {
386
387 try {
388
389 if (unit == null) {
390 // build and record parsed units
391 this.parseThreshold = 0; // will request a full parse
392 beginToCompile(new org.eclipse.jdt.internal.compiler.env.ICompilationUnit[] { sourceUnit });
393 // process all units (some more could be injected in the loop by the lookup environment)
394 unit = this.unitsToProcess[0];
395 } else {
396 // initial type binding creation
397 this.lookupEnvironment.buildTypeBindings(unit);
398
399 // binding resolution
400 this.lookupEnvironment.completeTypeBindings();
401 }
402
403 if (nodeSearcher == null) {
404 this.parser.getMethodBodies(unit); // no-op if method bodies have already been parsed
405 } else {
406 int searchPosition = nodeSearcher.position;
407 if (searchPosition >= 0 && searchPosition <= sourceUnit.getContents().length) {
408 unit.traverse(nodeSearcher, unit.scope);
409
410 org.eclipse.jdt.internal.compiler.ast.ASTNode node = nodeSearcher.found;
411
412 if (node != null) {
413 org.eclipse.jdt.internal.compiler.ast.TypeDeclaration enclosingTypeDeclaration = nodeSearcher.enclosingType;
414 if (node instanceof AbstractMethodDeclaration) {
415 ((AbstractMethodDeclaration)node).parseStatements(this.parser, unit);
416 } else if (enclosingTypeDeclaration != null) {
417 if (node instanceof org.eclipse.jdt.internal.compiler.ast.Initializer) {
418 ((org.eclipse.jdt.internal.compiler.ast.Initializer) node).parseStatements(this.parser, enclosingTypeDeclaration, unit);
419 } else if (node instanceof org.eclipse.jdt.internal.compiler.ast.TypeDeclaration) {
420 ((org.eclipse.jdt.internal.compiler.ast.TypeDeclaration)node).parseMethod(this.parser, unit);
421 }
422 }
423 }
424 }
425 }
426
427 if (unit.scope != null) {
428 // fault in fields & methods
429 unit.scope.faultInTypes();
430 if (unit.scope != null && verifyMethods) {
431 // http://dev.eclipse.org/bugs/show_bug.cgi?id=23117
432 // verify inherited methods
433 unit.scope.verifyMethods(this.lookupEnvironment.methodVerifier());
434 }
435 // type checking
436 unit.resolve();
437
438 // flow analysis
439 if (analyzeCode) unit.analyseCode();
440
441 // code generation
442 if (generateCode) unit.generateCode();
443 }
444 if (this.unitsToProcess != null) this.unitsToProcess[0] = null; // release reference to processed unit declaration
445 this.requestor.acceptResult(unit.compilationResult.tagAsAccepted());
446 return unit;
447 } catch (AbortCompilation e) {
448 this.handleInternalException(e, unit);
449 return unit == null ? this.unitsToProcess[0] : unit;
450 } catch (Error e) {
451 this.handleInternalException(e, unit, null);
452 throw e; // rethrow
453 } catch (RuntimeException e) {
454 this.handleInternalException(e, unit, null);
455 throw e; // rethrow
456 } finally {
457 // No reset is performed there anymore since,
458 // within the CodeAssist (or related tools),
459 // the compiler may be called *after* a call
460 // to this resolve(...) method. And such a call
461 // needs to have a compiler with a non-empty
462 // environment.
463 // this.reset();
464 }
465 }
466 /*
467 * Internal API used to resolve a given compilation unit. Can run a subset of the compilation process
468 */
469 public CompilationUnitDeclaration resolve(
470 org.eclipse.jdt.internal.compiler.env.ICompilationUnit sourceUnit,
471 boolean verifyMethods,
472 boolean analyzeCode,
473 boolean generateCode) {
474
475 return resolve(
476 null, /* no existing compilation unit declaration*/
477 sourceUnit,
478 null/*no node searcher*/,
479 verifyMethods,
480 analyzeCode,
481 generateCode);
482 }
483
484 /*
485 * Internal API used to resolve a given compilation unit. Can run a subset of the compilation process
486 */
487 public CompilationUnitDeclaration resolve(
488 CompilationUnitDeclaration unit,
489 org.eclipse.jdt.internal.compiler.env.ICompilationUnit sourceUnit,
490 boolean verifyMethods,
491 boolean analyzeCode,
492 boolean generateCode) {
493
494 return resolve(
495 unit,
496 sourceUnit,
497 null/*no node searcher*/,
498 verifyMethods,
499 analyzeCode,
500 generateCode);
501 }
502 }