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
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11 package org.eclipse.jdt.internal.compiler;
13 import org.eclipse.jdt.core.compiler.*;
14 import org.eclipse.jdt.internal.compiler.env.*;
15 import org.eclipse.jdt.internal.compiler.impl.*;
16 import org.eclipse.jdt.internal.compiler.ast.*;
17 import org.eclipse.jdt.internal.compiler.lookup.*;
18 import org.eclipse.jdt.internal.compiler.parser.*;
19 import org.eclipse.jdt.internal.compiler.problem.*;
20 import org.eclipse.jdt.internal.compiler.util.*;
25 public class Compiler implements ITypeRequestor, ProblemSeverities {
27 public ICompilerRequestor requestor;
28 public CompilerOptions options;
29 public ProblemReporter problemReporter;
31 // management of unit to be processed
32 //public CompilationUnitResult currentCompilationUnitResult;
33 public CompilationUnitDeclaration[] unitsToProcess;
34 public int totalUnits; // (totalUnits-1) gives the last unit in unitToProcess
37 public LookupEnvironment lookupEnvironment;
39 // ONCE STABILIZED, THESE SHOULD RETURN TO A FINAL FIELD
40 public static boolean DEBUG = false;
41 public int parseThreshold = -1;
42 // number of initial units parsed at once (-1: none)
45 * Static requestor reserved to listening compilation results in debug mode,
46 * so as for example to monitor compiler activity independantly from a particular
47 * builder implementation. It is reset at the end of compilation, and should not
48 * persist any information after having been reset.
50 public static IDebugRequestor DebugRequestor = null;
53 * Answer a new compiler using the given name environment and compiler options.
54 * The environment and options will be in effect for the lifetime of the compiler.
55 * When the compiler is run, compilation results are sent to the given requestor.
57 * @param environment org.eclipse.jdt.internal.compiler.api.env.INameEnvironment
58 * Environment used by the compiler in order to resolve type and package
59 * names. The name environment implements the actual connection of the compiler
60 * to the outside world (e.g. in batch mode the name environment is performing
61 * pure file accesses, reuse previous build state or connection to repositories).
62 * Note: the name environment is responsible for implementing the actual classpath
65 * @param policy org.eclipse.jdt.internal.compiler.api.problem.IErrorHandlingPolicy
66 * Configurable part for problem handling, allowing the compiler client to
67 * specify the rules for handling problems (stop on first error or accumulate
68 * them all) and at the same time perform some actions such as opening a dialog
69 * in UI when compiling interactively.
70 * @see org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies
72 * @param requestor org.eclipse.jdt.internal.compiler.api.ICompilerRequestor
73 * Component which will receive and persist all compilation results and is intended
74 * to consume them as they are produced. Typically, in a batch compiler, it is
75 * responsible for writing out the actual .class files to the file system.
76 * @see org.eclipse.jdt.internal.compiler.CompilationResult
78 * @param problemFactory org.eclipse.jdt.internal.compiler.api.problem.IProblemFactory
79 * Factory used inside the compiler to create problem descriptors. It allows the
80 * compiler client to supply its own representation of compilation problems in
81 * order to avoid object conversions. Note that the factory is not supposed
82 * to accumulate the created problems, the compiler will gather them all and hand
83 * them back as part of the compilation unit result.
86 INameEnvironment environment,
87 IErrorHandlingPolicy policy,
89 final ICompilerRequestor requestor,
90 IProblemFactory problemFactory) {
92 // create a problem handler given a handling policy
93 this.options = new CompilerOptions(settings);
95 // wrap requestor in DebugRequestor if one is specified
96 if(DebugRequestor == null) {
97 this.requestor = requestor;
99 this.requestor = new ICompilerRequestor(){
100 public void acceptResult(CompilationResult result){
101 if (DebugRequestor.isActive()){
102 DebugRequestor.acceptDebugResult(result);
104 requestor.acceptResult(result);
108 this.problemReporter =
109 new ProblemReporter(policy, this.options, problemFactory);
110 this.lookupEnvironment =
111 new LookupEnvironment(this, options, problemReporter, environment);
116 * Answer a new compiler using the given name environment and compiler options.
117 * The environment and options will be in effect for the lifetime of the compiler.
118 * When the compiler is run, compilation results are sent to the given requestor.
120 * @param environment org.eclipse.jdt.internal.compiler.api.env.INameEnvironment
121 * Environment used by the compiler in order to resolve type and package
122 * names. The name environment implements the actual connection of the compiler
123 * to the outside world (e.g. in batch mode the name environment is performing
124 * pure file accesses, reuse previous build state or connection to repositories).
125 * Note: the name environment is responsible for implementing the actual classpath
128 * @param policy org.eclipse.jdt.internal.compiler.api.problem.IErrorHandlingPolicy
129 * Configurable part for problem handling, allowing the compiler client to
130 * specify the rules for handling problems (stop on first error or accumulate
131 * them all) and at the same time perform some actions such as opening a dialog
132 * in UI when compiling interactively.
133 * @see org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies
135 * @param requestor org.eclipse.jdt.internal.compiler.api.ICompilerRequestor
136 * Component which will receive and persist all compilation results and is intended
137 * to consume them as they are produced. Typically, in a batch compiler, it is
138 * responsible for writing out the actual .class files to the file system.
139 * @see org.eclipse.jdt.internal.compiler.CompilationResult
141 * @param problemFactory org.eclipse.jdt.internal.compiler.api.problem.IProblemFactory
142 * Factory used inside the compiler to create problem descriptors. It allows the
143 * compiler client to supply its own representation of compilation problems in
144 * order to avoid object conversions. Note that the factory is not supposed
145 * to accumulate the created problems, the compiler will gather them all and hand
146 * them back as part of the compilation unit result.
147 * @param parseLiteralExpressionsAsConstants <code>boolean</code>
148 * This parameter is used to optimize the literals or leave them as they are in the source.
149 * If you put true, "Hello" + " world" will be converted to "Hello world".
152 INameEnvironment environment,
153 IErrorHandlingPolicy policy,
155 final ICompilerRequestor requestor,
156 IProblemFactory problemFactory,
157 boolean parseLiteralExpressionsAsConstants) {
159 // create a problem handler given a handling policy
160 this.options = new CompilerOptions(settings);
162 // wrap requestor in DebugRequestor if one is specified
163 if(DebugRequestor == null) {
164 this.requestor = requestor;
166 this.requestor = new ICompilerRequestor(){
167 public void acceptResult(CompilationResult result){
168 if (DebugRequestor.isActive()){
169 DebugRequestor.acceptDebugResult(result);
171 requestor.acceptResult(result);
175 this.problemReporter = new ProblemReporter(policy, this.options, problemFactory);
176 this.lookupEnvironment = new LookupEnvironment(this, options, problemReporter, environment);
181 * Add an additional binary type
183 public void accept(IBinaryType binaryType, PackageBinding packageBinding) {
184 if (options.verbose) {
187 "compilation.loadBinary" , //$NON-NLS-1$
189 new String(binaryType.getName())}));
190 // new Exception("TRACE BINARY").printStackTrace(System.out);
191 // System.out.println();
193 lookupEnvironment.createBinaryTypeFrom(binaryType, packageBinding);
197 * Add an additional compilation unit into the loop
198 * -> build compilation unit declarations, their bindings and record their results.
200 public void accept(ICompilationUnit sourceUnit) {
201 // Switch the current policy and compilation result for this unit to the requested one.
202 CompilationResult unitResult =
203 new CompilationResult(sourceUnit, totalUnits, totalUnits, this.options.maxProblemsPerUnit);
205 if (options.verbose) {
206 String count = String.valueOf(totalUnits + 1);
209 "compilation.request" , //$NON-NLS-1$
213 new String(sourceUnit.getFileName())}));
215 // diet parsing for large collection of unit
216 CompilationUnitDeclaration parsedUnit;
217 if (totalUnits < parseThreshold) {
218 parsedUnit = parser.parse(sourceUnit, unitResult);
220 parsedUnit = parser.dietParse(sourceUnit, unitResult);
222 // initial type binding creation
223 lookupEnvironment.buildTypeBindings(parsedUnit);
224 this.addCompilationUnit(sourceUnit, parsedUnit);
226 // binding resolution
227 lookupEnvironment.completeTypeBindings(parsedUnit);
228 } catch (AbortCompilationUnit e) {
229 // at this point, currentCompilationUnitResult may not be sourceUnit, but some other
230 // one requested further along to resolve sourceUnit.
231 if (unitResult.compilationUnit == sourceUnit) { // only report once
232 requestor.acceptResult(unitResult.tagAsAccepted());
234 throw e; // want to abort enclosing request to compile
240 * Add additional source types
242 public void accept(ISourceType[] sourceTypes, PackageBinding packageBinding) {
243 problemReporter.abortDueToInternalError(
245 "abort.againstSourceModel" , //$NON-NLS-1$
246 String.valueOf(sourceTypes[0].getName()),
247 String.valueOf(sourceTypes[0].getFileName())));
250 protected void addCompilationUnit(
251 ICompilationUnit sourceUnit,
252 CompilationUnitDeclaration parsedUnit) {
254 // append the unit to the list of ones to process later on
255 int size = unitsToProcess.length;
256 if (totalUnits == size)
257 // when growing reposition units starting at position 0
261 (unitsToProcess = new CompilationUnitDeclaration[size * 2]),
264 unitsToProcess[totalUnits++] = parsedUnit;
268 * Add the initial set of compilation units into the loop
269 * -> build compilation unit declarations, their bindings and record their results.
271 protected void beginToCompile(ICompilationUnit[] sourceUnits) {
272 int maxUnits = sourceUnits.length;
274 unitsToProcess = new CompilationUnitDeclaration[maxUnits];
276 // Switch the current policy and compilation result for this unit to the requested one.
277 for (int i = 0; i < maxUnits; i++) {
278 CompilationUnitDeclaration parsedUnit;
279 CompilationResult unitResult =
280 new CompilationResult(sourceUnits[i], i, maxUnits, this.options.maxProblemsPerUnit);
282 if (options.verbose) {
285 "compilation.request" , //$NON-NLS-1$
287 String.valueOf(i + 1),
288 String.valueOf(maxUnits),
289 new String(sourceUnits[i].getFileName())}));
291 // diet parsing for large collection of units
292 if (totalUnits < parseThreshold) {
293 parsedUnit = parser.parse(sourceUnits[i], unitResult);
295 parsedUnit = parser.dietParse(sourceUnits[i], unitResult);
297 // initial type binding creation
298 lookupEnvironment.buildTypeBindings(parsedUnit);
299 this.addCompilationUnit(sourceUnits[i], parsedUnit);
300 //} catch (AbortCompilationUnit e) {
301 // requestor.acceptResult(unitResult.tagAsAccepted());
303 sourceUnits[i] = null; // no longer hold onto the unit
306 // binding resolution
307 lookupEnvironment.completeTypeBindings();
312 * -> compile each of supplied files
313 * -> recompile any required types for which we have an incomplete principle structure
315 public void compile(ICompilationUnit[] sourceUnits) {
316 CompilationUnitDeclaration unit = null;
319 // build and record parsed units
321 beginToCompile(sourceUnits);
323 // process all units (some more could be injected in the loop by the lookup environment)
324 for (; i < totalUnits; i++) {
325 unit = unitsToProcess[i];
330 "compilation.process" , //$NON-NLS-1$
332 String.valueOf(i + 1),
333 String.valueOf(totalUnits),
334 new String(unitsToProcess[i].getFileName())}));
337 // cleanup compilation unit result
340 unitsToProcess[i] = null; // release reference to processed unit declaration
341 requestor.acceptResult(unit.compilationResult.tagAsAccepted());
343 System.out.println(Util.bind("compilation.done", //$NON-NLS-1$
345 String.valueOf(i + 1),
346 String.valueOf(totalUnits),
347 new String(unit.getFileName())}));
349 } catch (AbortCompilation e) {
350 this.handleInternalException(e, unit);
352 this.handleInternalException(e, unit, null);
354 } catch (RuntimeException e) {
355 this.handleInternalException(e, unit, null);
360 if (options.verbose) {
361 if (totalUnits > 1) {
363 Util.bind("compilation.units" , String.valueOf(totalUnits))); //$NON-NLS-1$
366 Util.bind("compilation.unit" , String.valueOf(totalUnits))); //$NON-NLS-1$
372 * Compiler crash recovery in case of unexpected runtime exceptions
374 protected void handleInternalException(
375 Throwable internalException,
376 CompilationUnitDeclaration unit,
377 CompilationResult result) {
379 /* find a compilation result */
380 if ((unit != null)) // basing result upon the current unit if available
381 result = unit.compilationResult; // current unit being processed ?
382 if ((result == null) && (unitsToProcess != null) && (totalUnits > 0))
383 result = unitsToProcess[totalUnits - 1].compilationResult;
384 // last unit in beginToCompile ?
386 boolean needToPrint = true;
387 if (result != null) {
388 /* create and record a compilation problem */
389 StringWriter stringWriter = new StringWriter();
390 PrintWriter writer = new PrintWriter(stringWriter);
391 internalException.printStackTrace(writer);
392 StringBuffer buffer = stringWriter.getBuffer();
394 String[] pbArguments = new String[] {
395 Util.bind("compilation.internalError" ) //$NON-NLS-1$
397 + buffer.toString()};
403 result.getFileName(),
404 IProblem.Unclassified,
413 /* hand back the compilation result */
414 if (!result.hasBeenAccepted) {
415 requestor.acceptResult(result.tagAsAccepted());
420 /* dump a stack trace to the console */
421 internalException.printStackTrace();
426 * Compiler recovery in case of internal AbortCompilation event
428 protected void handleInternalException(
429 AbortCompilation abortException,
430 CompilationUnitDeclaration unit) {
432 /* special treatment for SilentAbort: silently cancelling the compilation process */
433 if (abortException.isSilent) {
434 if (abortException.silentException == null) {
437 throw abortException.silentException;
440 /* uncomment following line to see where the abort came from */
441 // abortException.printStackTrace();
443 // Exception may tell which compilation result it is related, and which problem caused it
444 CompilationResult result = abortException.compilationResult;
445 if ((result == null) && (unit != null)) {
446 result = unit.compilationResult; // current unit being processed ?
448 // Lookup environment may be in middle of connecting types
449 if ((result == null) && lookupEnvironment.unitBeingCompleted != null) {
450 result = lookupEnvironment.unitBeingCompleted.compilationResult;
452 if ((result == null) && (unitsToProcess != null) && (totalUnits > 0))
453 result = unitsToProcess[totalUnits - 1].compilationResult;
454 // last unit in beginToCompile ?
455 if (result != null && !result.hasBeenAccepted) {
456 /* distant problem which could not be reported back there? */
457 if (abortException.problem != null) {
458 recordDistantProblem: {
459 IProblem distantProblem = abortException.problem;
460 IProblem[] knownProblems = result.problems;
461 for (int i = 0; i < result.problemCount; i++) {
462 if (knownProblems[i] == distantProblem) { // already recorded
463 break recordDistantProblem;
466 if (distantProblem instanceof DefaultProblem) { // fixup filename TODO (philippe) should improve API to make this official
467 ((DefaultProblem) distantProblem).setOriginatingFileName(result.getFileName());
469 result .record(distantProblem, unit);
472 /* distant internal exception which could not be reported back there */
473 if (abortException.exception != null) {
474 this.handleInternalException(abortException.exception, null, result);
478 /* hand back the compilation result */
479 if (!result.hasBeenAccepted) {
480 requestor.acceptResult(result.tagAsAccepted());
483 abortException.printStackTrace();
487 public void initializeParser() {
489 this.parser = new Parser(this.problemReporter, this.options.parseLiteralExpressionsAsConstants);
493 * Process a compilation unit already parsed and build.
495 public void process(CompilationUnitDeclaration unit, int i) {
497 this.parser.getMethodBodies(unit);
499 // fault in fields & methods
500 if (unit.scope != null)
501 unit.scope.faultInTypes();
503 // verify inherited methods
504 if (unit.scope != null)
505 unit.scope.verifyMethods(lookupEnvironment.methodVerifier());
517 if (options.produceReferenceInfo && unit.scope != null)
518 unit.scope.storeDependencyInfo();
520 // refresh the total number of units known at this stage
521 unit.compilationResult.totalUnitsKnown = totalUnits;
523 public void reset() {
524 lookupEnvironment.reset();
525 parser.scanner.source = null;
526 unitsToProcess = null;
527 if (DebugRequestor != null) DebugRequestor.reset();
531 * Internal API used to resolve a given compilation unit. Can run a subset of the compilation process
533 public CompilationUnitDeclaration resolve(
534 CompilationUnitDeclaration unit,
535 ICompilationUnit sourceUnit,
536 boolean verifyMethods,
538 boolean generateCode) {
542 // build and record parsed units
543 parseThreshold = 0; // will request a full parse
544 beginToCompile(new ICompilationUnit[] { sourceUnit });
545 // process all units (some more could be injected in the loop by the lookup environment)
546 unit = unitsToProcess[0];
548 // initial type binding creation
549 lookupEnvironment.buildTypeBindings(unit);
551 // binding resolution
552 lookupEnvironment.completeTypeBindings();
554 this.parser.getMethodBodies(unit);
555 if (unit.scope != null) {
556 // fault in fields & methods
557 unit.scope.faultInTypes();
558 if (unit.scope != null && verifyMethods) {
559 // http://dev.eclipse.org/bugs/show_bug.cgi?id=23117
560 // verify inherited methods
561 unit.scope.verifyMethods(lookupEnvironment.methodVerifier());
567 if (analyzeCode) unit.analyseCode();
570 if (generateCode) unit.generateCode();
572 if (unitsToProcess != null) unitsToProcess[0] = null; // release reference to processed unit declaration
573 requestor.acceptResult(unit.compilationResult.tagAsAccepted());
575 } catch (AbortCompilation e) {
576 this.handleInternalException(e, unit);
577 return unit == null ? unitsToProcess[0] : unit;
579 this.handleInternalException(e, unit, null);
581 } catch (RuntimeException e) {
582 this.handleInternalException(e, unit, null);
585 // No reset is performed there anymore since,
586 // within the CodeAssist (or related tools),
587 // the compiler may be called *after* a call
588 // to this resolve(...) method. And such a call
589 // needs to have a compiler with a non-empty
595 * Internal API used to resolve a given compilation unit. Can run a subset of the compilation process
597 public CompilationUnitDeclaration resolve(
598 ICompilationUnit sourceUnit,
599 boolean verifyMethods,
601 boolean generateCode) {