1 /*******************************************************************************
2 * Copyright (c) 2000, 2001, 2002 International Business Machines Corp. and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Common Public License v0.5
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/cpl-v05.html
9 * IBM Corporation - initial API and implementation
10 ******************************************************************************/
11 package net.sourceforge.phpdt.internal.compiler;
13 import java.io.PrintWriter;
14 import java.io.StringWriter;
17 import net.sourceforge.phpdt.core.compiler.IProblem;
18 import net.sourceforge.phpdt.internal.compiler.ast.CompilationUnitDeclaration;
19 import net.sourceforge.phpdt.internal.compiler.env.IBinaryType;
20 import net.sourceforge.phpdt.internal.compiler.env.ICompilationUnit;
21 import net.sourceforge.phpdt.internal.compiler.env.INameEnvironment;
22 import net.sourceforge.phpdt.internal.compiler.env.ISourceType;
23 import net.sourceforge.phpdt.internal.compiler.impl.CompilerOptions;
24 import net.sourceforge.phpdt.internal.compiler.impl.ITypeRequestor;
25 import net.sourceforge.phpdt.internal.compiler.lookup.LookupEnvironment;
26 import net.sourceforge.phpdt.internal.compiler.lookup.PackageBinding;
27 import net.sourceforge.phpdt.internal.compiler.parser.Parser;
28 import net.sourceforge.phpdt.internal.compiler.problem.AbortCompilation;
29 import net.sourceforge.phpdt.internal.compiler.problem.AbortCompilationUnit;
30 import net.sourceforge.phpdt.internal.compiler.problem.ProblemReporter;
31 import net.sourceforge.phpdt.internal.compiler.problem.ProblemSeverities;
32 import net.sourceforge.phpdt.internal.compiler.util.Util;
34 public class Compiler implements ITypeRequestor, ProblemSeverities {
36 ICompilerRequestor requestor;
37 public CompilerOptions options;
38 public ProblemReporter problemReporter;
40 // management of unit to be processed
41 //public CompilationUnitResult currentCompilationUnitResult;
42 CompilationUnitDeclaration[] unitsToProcess;
43 int totalUnits; // (totalUnits-1) gives the last unit in unitToProcess
46 public LookupEnvironment lookupEnvironment;
48 // ONCE STABILIZED, THESE SHOULD RETURN TO A FINAL FIELD
49 public static boolean DEBUG = false;
50 public int parseThreshold = -1;
51 // number of initial units parsed at once (-1: none)
54 * Static requestor reserved to listening compilation results in debug mode,
55 * so as for example to monitor compiler activity independantly from a particular
56 * builder implementation. It is reset at the end of compilation, and should not
57 * persist any information after having been reset.
59 public static IDebugRequestor DebugRequestor = null;
62 * Answer a new compiler using the given name environment and compiler options.
63 * The environment and options will be in effect for the lifetime of the compiler.
64 * When the compiler is run, compilation results are sent to the given requestor.
66 * @param environment org.eclipse.jdt.internal.compiler.api.env.INameEnvironment
67 * Environment used by the compiler in order to resolve type and package
68 * names. The name environment implements the actual connection of the compiler
69 * to the outside world (e.g. in batch mode the name environment is performing
70 * pure file accesses, reuse previous build state or connection to repositories).
71 * Note: the name environment is responsible for implementing the actual classpath
74 * @param policy org.eclipse.jdt.internal.compiler.api.problem.IErrorHandlingPolicy
75 * Configurable part for problem handling, allowing the compiler client to
76 * specify the rules for handling problems (stop on first error or accumulate
77 * them all) and at the same time perform some actions such as opening a dialog
78 * in UI when compiling interactively.
79 * @see org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies
81 * @param requestor org.eclipse.jdt.internal.compiler.api.ICompilerRequestor
82 * Component which will receive and persist all compilation results and is intended
83 * to consume them as they are produced. Typically, in a batch compiler, it is
84 * responsible for writing out the actual .class files to the file system.
85 * @see org.eclipse.jdt.internal.compiler.CompilationResult
87 * @param problemFactory org.eclipse.jdt.internal.compiler.api.problem.IProblemFactory
88 * Factory used inside the compiler to create problem descriptors. It allows the
89 * compiler client to supply its own representation of compilation problems in
90 * order to avoid object conversions. Note that the factory is not supposed
91 * to accumulate the created problems, the compiler will gather them all and hand
92 * them back as part of the compilation unit result.
95 INameEnvironment environment,
96 IErrorHandlingPolicy policy,
98 final ICompilerRequestor requestor,
99 IProblemFactory problemFactory) {
101 // create a problem handler given a handling policy
102 this.options = new CompilerOptions(settings);
104 // wrap requestor in DebugRequestor if one is specified
105 if(DebugRequestor == null) {
106 this.requestor = requestor;
108 this.requestor = new ICompilerRequestor(){
109 public void acceptResult(CompilationResult result){
110 if (DebugRequestor.isActive()){
111 DebugRequestor.acceptDebugResult(result);
113 requestor.acceptResult(result);
117 this.problemReporter =
118 new ProblemReporter(policy, this.options, problemFactory);
119 this.lookupEnvironment =
120 new LookupEnvironment(this, options, problemReporter, environment);
124 this.options.parseLiteralExpressionsAsConstants,
125 this.options.assertMode);
129 * Answer a new compiler using the given name environment and compiler options.
130 * The environment and options will be in effect for the lifetime of the compiler.
131 * When the compiler is run, compilation results are sent to the given requestor.
133 * @param environment org.eclipse.jdt.internal.compiler.api.env.INameEnvironment
134 * Environment used by the compiler in order to resolve type and package
135 * names. The name environment implements the actual connection of the compiler
136 * to the outside world (e.g. in batch mode the name environment is performing
137 * pure file accesses, reuse previous build state or connection to repositories).
138 * Note: the name environment is responsible for implementing the actual classpath
141 * @param policy org.eclipse.jdt.internal.compiler.api.problem.IErrorHandlingPolicy
142 * Configurable part for problem handling, allowing the compiler client to
143 * specify the rules for handling problems (stop on first error or accumulate
144 * them all) and at the same time perform some actions such as opening a dialog
145 * in UI when compiling interactively.
146 * @see org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies
148 * @param requestor org.eclipse.jdt.internal.compiler.api.ICompilerRequestor
149 * Component which will receive and persist all compilation results and is intended
150 * to consume them as they are produced. Typically, in a batch compiler, it is
151 * responsible for writing out the actual .class files to the file system.
152 * @see org.eclipse.jdt.internal.compiler.CompilationResult
154 * @param problemFactory org.eclipse.jdt.internal.compiler.api.problem.IProblemFactory
155 * Factory used inside the compiler to create problem descriptors. It allows the
156 * compiler client to supply its own representation of compilation problems in
157 * order to avoid object conversions. Note that the factory is not supposed
158 * to accumulate the created problems, the compiler will gather them all and hand
159 * them back as part of the compilation unit result.
160 * @param parseLiteralExpressionsAsConstants <code>boolean</code>
161 * This parameter is used to optimize the literals or leave them as they are in the source.
162 * If you put true, "Hello" + " world" will be converted to "Hello world".
165 INameEnvironment environment,
166 IErrorHandlingPolicy policy,
168 final ICompilerRequestor requestor,
169 IProblemFactory problemFactory,
170 boolean parseLiteralExpressionsAsConstants) {
172 // create a problem handler given a handling policy
173 this.options = new CompilerOptions(settings);
175 // wrap requestor in DebugRequestor if one is specified
176 if(DebugRequestor == null) {
177 this.requestor = requestor;
179 this.requestor = new ICompilerRequestor(){
180 public void acceptResult(CompilationResult result){
181 if (DebugRequestor.isActive()){
182 DebugRequestor.acceptDebugResult(result);
184 requestor.acceptResult(result);
188 this.problemReporter =
189 new ProblemReporter(policy, this.options, problemFactory);
190 this.lookupEnvironment =
191 new LookupEnvironment(this, options, problemReporter, environment);
195 parseLiteralExpressionsAsConstants,
196 this.options.assertMode);
200 * Add an additional binary type
202 public void accept(IBinaryType binaryType, PackageBinding packageBinding) {
203 lookupEnvironment.createBinaryTypeFrom(binaryType, packageBinding);
207 * Add an additional compilation unit into the loop
208 * -> build compilation unit declarations, their bindings and record their results.
210 public void accept(ICompilationUnit sourceUnit) {
211 // Switch the current policy and compilation result for this unit to the requested one.
212 CompilationResult unitResult =
213 new CompilationResult(sourceUnit, totalUnits, totalUnits, this.options.maxProblemsPerUnit);
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);
223 if (options.verbose) {
226 "compilation.request" , //$NON-NLS-1$
228 String.valueOf(totalUnits + 1),
229 String.valueOf(totalUnits + 1),
230 new String(sourceUnit.getFileName())}));
233 // initial type binding creation
234 lookupEnvironment.buildTypeBindings(parsedUnit);
235 this.addCompilationUnit(sourceUnit, parsedUnit);
237 // binding resolution
238 lookupEnvironment.completeTypeBindings(parsedUnit);
239 } catch (AbortCompilationUnit e) {
240 // at this point, currentCompilationUnitResult may not be sourceUnit, but some other
241 // one requested further along to resolve sourceUnit.
242 if (unitResult.compilationUnit == sourceUnit) { // only report once
243 requestor.acceptResult(unitResult.tagAsAccepted());
245 throw e; // want to abort enclosing request to compile
251 * Add additional source types
253 public void accept(ISourceType[] sourceTypes, PackageBinding packageBinding) {
254 problemReporter.abortDueToInternalError(
256 "abort.againstSourceModel " , //$NON-NLS-1$
257 String.valueOf(sourceTypes[0].getName()),
258 String.valueOf(sourceTypes[0].getFileName())));
261 protected void addCompilationUnit(
262 ICompilationUnit sourceUnit,
263 CompilationUnitDeclaration parsedUnit) {
265 // append the unit to the list of ones to process later on
266 int size = unitsToProcess.length;
267 if (totalUnits == size)
268 // when growing reposition units starting at position 0
272 (unitsToProcess = new CompilationUnitDeclaration[size * 2]),
275 unitsToProcess[totalUnits++] = parsedUnit;
279 * Add the initial set of compilation units into the loop
280 * -> build compilation unit declarations, their bindings and record their results.
282 protected void beginToCompile(ICompilationUnit[] sourceUnits) {
283 int maxUnits = sourceUnits.length;
285 unitsToProcess = new CompilationUnitDeclaration[maxUnits];
287 // Switch the current policy and compilation result for this unit to the requested one.
288 for (int i = 0; i < maxUnits; i++) {
289 CompilationUnitDeclaration parsedUnit;
290 CompilationResult unitResult =
291 new CompilationResult(sourceUnits[i], i, maxUnits, this.options.maxProblemsPerUnit);
293 // diet parsing for large collection of units
294 if (totalUnits < parseThreshold) {
295 parsedUnit = parser.parse(sourceUnits[i], unitResult);
297 parsedUnit = parser.dietParse(sourceUnits[i], unitResult);
299 if (options.verbose) {
302 "compilation.request" , //$NON-NLS-1$
304 String.valueOf(i + 1),
305 String.valueOf(maxUnits),
306 new String(sourceUnits[i].getFileName())}));
308 // initial type binding creation
309 lookupEnvironment.buildTypeBindings(parsedUnit);
310 this.addCompilationUnit(sourceUnits[i], parsedUnit);
311 //} catch (AbortCompilationUnit e) {
312 // requestor.acceptResult(unitResult.tagAsAccepted());
314 sourceUnits[i] = null; // no longer hold onto the unit
317 // binding resolution
318 lookupEnvironment.completeTypeBindings();
323 * -> compile each of supplied files
324 * -> recompile any required types for which we have an incomplete principle structure
326 public void compile(ICompilationUnit[] sourceUnits) {
327 CompilationUnitDeclaration unit = null;
330 // build and record parsed units
332 beginToCompile(sourceUnits);
334 // process all units (some more could be injected in the loop by the lookup environment)
335 for (; i < totalUnits; i++) {
336 unit = unitsToProcess[i];
341 "compilation.process" , //$NON-NLS-1$
343 String.valueOf(i + 1),
344 String.valueOf(totalUnits),
345 new String(unitsToProcess[i].getFileName())}));
348 // cleanup compilation unit result
351 System.out.println(Util.bind("compilation.done", //$NON-NLS-1$
353 String.valueOf(i + 1),
354 String.valueOf(totalUnits),
355 new String(unitsToProcess[i].getFileName())}));
357 unitsToProcess[i] = null; // release reference to processed unit declaration
358 requestor.acceptResult(unit.compilationResult.tagAsAccepted());
360 } catch (AbortCompilation e) {
361 this.handleInternalException(e, unit);
363 this.handleInternalException(e, unit, null);
365 } catch (RuntimeException e) {
366 this.handleInternalException(e, unit, null);
371 if (options.verbose) {
372 if (totalUnits > 1) {
374 Util.bind("compilation.units" , String.valueOf(totalUnits))); //$NON-NLS-1$
377 Util.bind("compilation.unit" , String.valueOf(totalUnits))); //$NON-NLS-1$
382 protected void getMethodBodies(CompilationUnitDeclaration unit, int place) {
383 //fill the methods bodies in order for the code to be generated
385 if (unit.ignoreMethodBodies) {
386 unit.ignoreFurtherInvestigation = true;
388 // if initial diet parse did not work, no need to dig into method bodies.
391 if (place < parseThreshold)
392 return; //work already done ...
394 //real parse of the method....
395 parser.scanner.setSource(
396 unit.compilationResult.compilationUnit.getContents());
397 if (unit.types != null) {
398 for (int i = unit.types.length; --i >= 0;)
399 unit.types[i].parseMethod(parser, unit);
404 * Compiler crash recovery in case of unexpected runtime exceptions
406 protected void handleInternalException(
407 Throwable internalException,
408 CompilationUnitDeclaration unit,
409 CompilationResult result) {
411 /* dump a stack trace to the console */
412 internalException.printStackTrace();
414 /* find a compilation result */
415 if ((unit != null)) // basing result upon the current unit if available
416 result = unit.compilationResult; // current unit being processed ?
417 if ((result == null) && (unitsToProcess != null) && (totalUnits > 0))
418 result = unitsToProcess[totalUnits - 1].compilationResult;
419 // last unit in beginToCompile ?
421 if (result != null) {
422 /* create and record a compilation problem */
423 StringWriter stringWriter = new StringWriter();
424 PrintWriter writer = new PrintWriter(stringWriter);
425 internalException.printStackTrace(writer);
426 StringBuffer buffer = stringWriter.getBuffer();
432 result.getFileName(),
433 IProblem.Unclassified,
435 Util.bind("compilation.internalError" ) //$NON-NLS-1$
437 + buffer.toString()},
446 /* hand back the compilation result */
447 if (!result.hasBeenAccepted) {
448 requestor.acceptResult(result.tagAsAccepted());
454 * Compiler recovery in case of internal AbortCompilation event
456 protected void handleInternalException(
457 AbortCompilation abortException,
458 CompilationUnitDeclaration unit) {
460 /* special treatment for SilentAbort: silently cancelling the compilation process */
461 if (abortException.isSilent) {
462 if (abortException.silentException == null) {
465 throw abortException.silentException;
469 /* uncomment following line to see where the abort came from */
470 // abortException.printStackTrace();
472 // Exception may tell which compilation result it is related, and which problem caused it
473 CompilationResult result = abortException.compilationResult;
474 if ((result == null) && (unit != null))
475 result = unit.compilationResult; // current unit being processed ?
476 if ((result == null) && (unitsToProcess != null) && (totalUnits > 0))
477 result = unitsToProcess[totalUnits - 1].compilationResult;
478 // last unit in beginToCompile ?
479 if (result != null && !result.hasBeenAccepted) {
480 /* distant problem which could not be reported back there */
481 if (abortException.problemId != 0) {
486 result.getFileName(),
487 abortException.problemId,
488 abortException.problemArguments,
497 /* distant internal exception which could not be reported back there */
498 if (abortException.exception != null) {
499 this.handleInternalException(abortException.exception, null, result);
503 /* hand back the compilation result */
504 if (!result.hasBeenAccepted) {
505 requestor.acceptResult(result.tagAsAccepted());
509 if (abortException.problemId != 0){
511 problemReporter.createProblem(
513 abortException.problemId,
514 abortException.problemArguments,
519 System.out.println(problem.getMessage());
522 abortException.printStackTrace();
527 * Process a compilation unit already parsed and build.
529 private void process(CompilationUnitDeclaration unit, int i) {
531 getMethodBodies(unit, i);
533 // fault in fields & methods
534 if (unit.scope != null)
535 unit.scope.faultInTypes();
537 // verify inherited methods
538 if (unit.scope != null)
539 unit.scope.verifyMethods(lookupEnvironment.methodVerifier());
551 if (options.produceReferenceInfo && unit.scope != null)
552 unit.scope.storeDependencyInfo();
554 // refresh the total number of units known at this stage
555 unit.compilationResult.totalUnitsKnown = totalUnits;
557 public void reset() {
558 lookupEnvironment.reset();
559 parser.scanner.source = null;
560 unitsToProcess = null;
561 if (DebugRequestor != null) DebugRequestor.reset();
565 * Internal API used to resolve a compilation unit minimally for code assist engine
567 public CompilationUnitDeclaration resolve(ICompilationUnit sourceUnit) {
568 CompilationUnitDeclaration unit = null;
570 // build and record parsed units
571 parseThreshold = 0; // will request a full parse
572 beginToCompile(new ICompilationUnit[] { sourceUnit });
573 // process all units (some more could be injected in the loop by the lookup environment)
574 unit = unitsToProcess[0];
575 getMethodBodies(unit, 0);
576 if (unit.scope != null) {
577 // fault in fields & methods
578 unit.scope.faultInTypes();
582 unitsToProcess[0] = null; // release reference to processed unit declaration
583 requestor.acceptResult(unit.compilationResult.tagAsAccepted());
585 } catch (AbortCompilation e) {
586 this.handleInternalException(e, unit);
587 return unit == null ? unitsToProcess[0] : unit;
589 this.handleInternalException(e, unit, null);
591 } catch (RuntimeException e) {
592 this.handleInternalException(e, unit, null);
595 // No reset is performed there anymore since,
596 // within the CodeAssist (or related tools),
597 // the compiler may be called *after* a call
598 // to this resolve(...) method. And such a call
599 // needs to have a compiler with a non-empty