Source code: org/eclipse/jdt/internal/compiler/Compiler.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 package org.eclipse.jdt.internal.compiler;
12
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.*;
21
22 import java.io.*;
23 import java.util.*;
24
25 public class Compiler implements ITypeRequestor, ProblemSeverities {
26 public Parser parser;
27 public ICompilerRequestor requestor;
28 public CompilerOptions options;
29 public ProblemReporter problemReporter;
30
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
35
36 // name lookup
37 public LookupEnvironment lookupEnvironment;
38
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)
43
44 /*
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.
49 */
50 public static IDebugRequestor DebugRequestor = null;
51
52 /**
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.
56 *
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
63 * rules.
64 *
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
71 *
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
77 *
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.
84 */
85 public Compiler(
86 INameEnvironment environment,
87 IErrorHandlingPolicy policy,
88 Map settings,
89 final ICompilerRequestor requestor,
90 IProblemFactory problemFactory) {
91
92 // create a problem handler given a handling policy
93 this.options = new CompilerOptions(settings);
94
95 // wrap requestor in DebugRequestor if one is specified
96 if(DebugRequestor == null) {
97 this.requestor = requestor;
98 } else {
99 this.requestor = new ICompilerRequestor(){
100 public void acceptResult(CompilationResult result){
101 if (DebugRequestor.isActive()){
102 DebugRequestor.acceptDebugResult(result);
103 }
104 requestor.acceptResult(result);
105 }
106 };
107 }
108 this.problemReporter =
109 new ProblemReporter(policy, this.options, problemFactory);
110 this.lookupEnvironment =
111 new LookupEnvironment(this, options, problemReporter, environment);
112 initializeParser();
113 }
114
115 /**
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.
119 *
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
126 * rules.
127 *
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
134 *
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
140 *
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".
150 */
151 public Compiler(
152 INameEnvironment environment,
153 IErrorHandlingPolicy policy,
154 Map settings,
155 final ICompilerRequestor requestor,
156 IProblemFactory problemFactory,
157 boolean parseLiteralExpressionsAsConstants) {
158
159 // create a problem handler given a handling policy
160 this.options = new CompilerOptions(settings);
161
162 // wrap requestor in DebugRequestor if one is specified
163 if(DebugRequestor == null) {
164 this.requestor = requestor;
165 } else {
166 this.requestor = new ICompilerRequestor(){
167 public void acceptResult(CompilationResult result){
168 if (DebugRequestor.isActive()){
169 DebugRequestor.acceptDebugResult(result);
170 }
171 requestor.acceptResult(result);
172 }
173 };
174 }
175 this.problemReporter = new ProblemReporter(policy, this.options, problemFactory);
176 this.lookupEnvironment = new LookupEnvironment(this, options, problemReporter, environment);
177 initializeParser();
178 }
179
180 /**
181 * Add an additional binary type
182 */
183 public void accept(IBinaryType binaryType, PackageBinding packageBinding) {
184 if (options.verbose) {
185 System.out.println(
186 Util.bind(
187 "compilation.loadBinary" , //$NON-NLS-1$
188 new String[] {
189 new String(binaryType.getName())}));
190 // new Exception("TRACE BINARY").printStackTrace(System.out);
191 // System.out.println();
192 }
193 lookupEnvironment.createBinaryTypeFrom(binaryType, packageBinding);
194 }
195
196 /**
197 * Add an additional compilation unit into the loop
198 * -> build compilation unit declarations, their bindings and record their results.
199 */
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);
204 try {
205 if (options.verbose) {
206 String count = String.valueOf(totalUnits + 1);
207 System.out.println(
208 Util.bind(
209 "compilation.request" , //$NON-NLS-1$
210 new String[] {
211 count,
212 count,
213 new String(sourceUnit.getFileName())}));
214 }
215 // diet parsing for large collection of unit
216 CompilationUnitDeclaration parsedUnit;
217 if (totalUnits < parseThreshold) {
218 parsedUnit = parser.parse(sourceUnit, unitResult);
219 } else {
220 parsedUnit = parser.dietParse(sourceUnit, unitResult);
221 }
222 // initial type binding creation
223 lookupEnvironment.buildTypeBindings(parsedUnit);
224 this.addCompilationUnit(sourceUnit, parsedUnit);
225
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());
233 } else {
234 throw e; // want to abort enclosing request to compile
235 }
236 }
237 }
238
239 /**
240 * Add additional source types
241 */
242 public void accept(ISourceType[] sourceTypes, PackageBinding packageBinding) {
243 problemReporter.abortDueToInternalError(
244 Util.bind(
245 "abort.againstSourceModel" , //$NON-NLS-1$
246 String.valueOf(sourceTypes[0].getName()),
247 String.valueOf(sourceTypes[0].getFileName())));
248 }
249
250 protected void addCompilationUnit(
251 ICompilationUnit sourceUnit,
252 CompilationUnitDeclaration parsedUnit) {
253
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
258 System.arraycopy(
259 unitsToProcess,
260 0,
261 (unitsToProcess = new CompilationUnitDeclaration[size * 2]),
262 0,
263 totalUnits);
264 unitsToProcess[totalUnits++] = parsedUnit;
265 }
266
267 /**
268 * Add the initial set of compilation units into the loop
269 * -> build compilation unit declarations, their bindings and record their results.
270 */
271 protected void beginToCompile(ICompilationUnit[] sourceUnits) {
272 int maxUnits = sourceUnits.length;
273 totalUnits = 0;
274 unitsToProcess = new CompilationUnitDeclaration[maxUnits];
275
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);
281 try {
282 if (options.verbose) {
283 System.out.println(
284 Util.bind(
285 "compilation.request" , //$NON-NLS-1$
286 new String[] {
287 String.valueOf(i + 1),
288 String.valueOf(maxUnits),
289 new String(sourceUnits[i].getFileName())}));
290 }
291 // diet parsing for large collection of units
292 if (totalUnits < parseThreshold) {
293 parsedUnit = parser.parse(sourceUnits[i], unitResult);
294 } else {
295 parsedUnit = parser.dietParse(sourceUnits[i], unitResult);
296 }
297 // initial type binding creation
298 lookupEnvironment.buildTypeBindings(parsedUnit);
299 this.addCompilationUnit(sourceUnits[i], parsedUnit);
300 //} catch (AbortCompilationUnit e) {
301 // requestor.acceptResult(unitResult.tagAsAccepted());
302 } finally {
303 sourceUnits[i] = null; // no longer hold onto the unit
304 }
305 }
306 // binding resolution
307 lookupEnvironment.completeTypeBindings();
308 }
309
310 /**
311 * General API
312 * -> compile each of supplied files
313 * -> recompile any required types for which we have an incomplete principle structure
314 */
315 public void compile(ICompilationUnit[] sourceUnits) {
316 CompilationUnitDeclaration unit = null;
317 int i = 0;
318 try {
319 // build and record parsed units
320
321 beginToCompile(sourceUnits);
322
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];
326 try {
327 if (options.verbose)
328 System.out.println(
329 Util.bind(
330 "compilation.process" , //$NON-NLS-1$
331 new String[] {
332 String.valueOf(i + 1),
333 String.valueOf(totalUnits),
334 new String(unitsToProcess[i].getFileName())}));
335 process(unit, i);
336 } finally {
337 // cleanup compilation unit result
338 unit.cleanUp();
339 }
340 unitsToProcess[i] = null; // release reference to processed unit declaration
341 requestor.acceptResult(unit.compilationResult.tagAsAccepted());
342 if (options.verbose)
343 System.out.println(Util.bind("compilation.done", //$NON-NLS-1$
344 new String[] {
345 String.valueOf(i + 1),
346 String.valueOf(totalUnits),
347 new String(unit.getFileName())}));
348 }
349 } catch (AbortCompilation e) {
350 this.handleInternalException(e, unit);
351 } catch (Error e) {
352 this.handleInternalException(e, unit, null);
353 throw e; // rethrow
354 } catch (RuntimeException e) {
355 this.handleInternalException(e, unit, null);
356 throw e; // rethrow
357 } finally {
358 this.reset();
359 }
360 if (options.verbose) {
361 if (totalUnits > 1) {
362 System.out.println(
363 Util.bind("compilation.units" , String.valueOf(totalUnits))); //$NON-NLS-1$
364 } else {
365 System.out.println(
366 Util.bind("compilation.unit" , String.valueOf(totalUnits))); //$NON-NLS-1$
367 }
368 }
369 }
370
371 /*
372 * Compiler crash recovery in case of unexpected runtime exceptions
373 */
374 protected void handleInternalException(
375 Throwable internalException,
376 CompilationUnitDeclaration unit,
377 CompilationResult result) {
378
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 ?
385
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();
393
394 String[] pbArguments = new String[] {
395 Util.bind("compilation.internalError" ) //$NON-NLS-1$
396 + "\n" //$NON-NLS-1$
397 + buffer.toString()};
398
399 result
400 .record(
401 problemReporter
402 .createProblem(
403 result.getFileName(),
404 IProblem.Unclassified,
405 pbArguments,
406 pbArguments,
407 Error, // severity
408 0, // source start
409 0, // source end
410 0), // line number
411 unit);
412
413 /* hand back the compilation result */
414 if (!result.hasBeenAccepted) {
415 requestor.acceptResult(result.tagAsAccepted());
416 needToPrint = false;
417 }
418 }
419 if (needToPrint) {
420 /* dump a stack trace to the console */
421 internalException.printStackTrace();
422 }
423 }
424
425 /*
426 * Compiler recovery in case of internal AbortCompilation event
427 */
428 protected void handleInternalException(
429 AbortCompilation abortException,
430 CompilationUnitDeclaration unit) {
431
432 /* special treatment for SilentAbort: silently cancelling the compilation process */
433 if (abortException.isSilent) {
434 if (abortException.silentException == null) {
435 return;
436 }
437 throw abortException.silentException;
438 }
439
440 /* uncomment following line to see where the abort came from */
441 // abortException.printStackTrace();
442
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 ?
447 }
448 // Lookup environment may be in middle of connecting types
449 if ((result == null) && lookupEnvironment.unitBeingCompleted != null) {
450 result = lookupEnvironment.unitBeingCompleted.compilationResult;
451 }
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;
464 }
465 }
466 if (distantProblem instanceof DefaultProblem) { // fixup filename TODO (philippe) should improve API to make this official
467 ((DefaultProblem) distantProblem).setOriginatingFileName(result.getFileName());
468 }
469 result .record(distantProblem, unit);
470 }
471 } else {
472 /* distant internal exception which could not be reported back there */
473 if (abortException.exception != null) {
474 this.handleInternalException(abortException.exception, null, result);
475 return;
476 }
477 }
478 /* hand back the compilation result */
479 if (!result.hasBeenAccepted) {
480 requestor.acceptResult(result.tagAsAccepted());
481 }
482 } else {
483 abortException.printStackTrace();
484 }
485 }
486
487 public void initializeParser() {
488
489 this.parser = new Parser(this.problemReporter, this.options.parseLiteralExpressionsAsConstants);
490 }
491
492 /**
493 * Process a compilation unit already parsed and build.
494 */
495 public void process(CompilationUnitDeclaration unit, int i) {
496
497 this.parser.getMethodBodies(unit);
498
499 // fault in fields & methods
500 if (unit.scope != null)
501 unit.scope.faultInTypes();
502
503 // verify inherited methods
504 if (unit.scope != null)
505 unit.scope.verifyMethods(lookupEnvironment.methodVerifier());
506
507 // type checking
508 unit.resolve();
509
510 // flow analysis
511 unit.analyseCode();
512
513 // code generation
514 unit.generateCode();
515
516 // reference info
517 if (options.produceReferenceInfo && unit.scope != null)
518 unit.scope.storeDependencyInfo();
519
520 // refresh the total number of units known at this stage
521 unit.compilationResult.totalUnitsKnown = totalUnits;
522 }
523 public void reset() {
524 lookupEnvironment.reset();
525 parser.scanner.source = null;
526 unitsToProcess = null;
527 if (DebugRequestor != null) DebugRequestor.reset();
528 }
529
530 /**
531 * Internal API used to resolve a given compilation unit. Can run a subset of the compilation process
532 */
533 public CompilationUnitDeclaration resolve(
534 CompilationUnitDeclaration unit,
535 ICompilationUnit sourceUnit,
536 boolean verifyMethods,
537 boolean analyzeCode,
538 boolean generateCode) {
539
540 try {
541 if (unit == null) {
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];
547 } else {
548 // initial type binding creation
549 lookupEnvironment.buildTypeBindings(unit);
550
551 // binding resolution
552 lookupEnvironment.completeTypeBindings();
553 }
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());
562 }
563 // type checking
564 unit.resolve();
565
566 // flow analysis
567 if (analyzeCode) unit.analyseCode();
568
569 // code generation
570 if (generateCode) unit.generateCode();
571 }
572 if (unitsToProcess != null) unitsToProcess[0] = null; // release reference to processed unit declaration
573 requestor.acceptResult(unit.compilationResult.tagAsAccepted());
574 return unit;
575 } catch (AbortCompilation e) {
576 this.handleInternalException(e, unit);
577 return unit == null ? unitsToProcess[0] : unit;
578 } catch (Error e) {
579 this.handleInternalException(e, unit, null);
580 throw e; // rethrow
581 } catch (RuntimeException e) {
582 this.handleInternalException(e, unit, null);
583 throw e; // rethrow
584 } finally {
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
590 // environment.
591 // this.reset();
592 }
593 }
594 /**
595 * Internal API used to resolve a given compilation unit. Can run a subset of the compilation process
596 */
597 public CompilationUnitDeclaration resolve(
598 ICompilationUnit sourceUnit,
599 boolean verifyMethods,
600 boolean analyzeCode,
601 boolean generateCode) {
602
603 return resolve(
604 null,
605 sourceUnit,
606 verifyMethods,
607 analyzeCode,
608 generateCode);
609 }
610 }