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 net.sourceforge.phpdt.core.compiler.*;
14 import net.sourceforge.phpdt.internal.compiler.env.*;
15 import net.sourceforge.phpdt.internal.compiler.impl.*;
16 import net.sourceforge.phpdt.internal.compiler.ast.*;
17 import net.sourceforge.phpdt.internal.compiler.lookup.*;
18 import net.sourceforge.phpdt.internal.compiler.parser.*;
19 import net.sourceforge.phpdt.internal.compiler.problem.*;
20 import net.sourceforge.phpdt.internal.compiler.util.*;
25 public class Compiler implements ITypeRequestor, ProblemSeverities {
27 ICompilerRequestor requestor;
28 public CompilerOptions options;
29 public ProblemReporter problemReporter;
31 // management of unit to be processed
32 //public CompilationUnitResult currentCompilationUnitResult;
33 CompilationUnitDeclaration[] unitsToProcess;
34 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);
115 this.options.parseLiteralExpressionsAsConstants,
116 this.options.assertMode);
120 * Answer a new compiler using the given name environment and compiler options.
121 * The environment and options will be in effect for the lifetime of the compiler.
122 * When the compiler is run, compilation results are sent to the given requestor.
124 * @param environment org.eclipse.jdt.internal.compiler.api.env.INameEnvironment
125 * Environment used by the compiler in order to resolve type and package
126 * names. The name environment implements the actual connection of the compiler
127 * to the outside world (e.g. in batch mode the name environment is performing
128 * pure file accesses, reuse previous build state or connection to repositories).
129 * Note: the name environment is responsible for implementing the actual classpath
132 * @param policy org.eclipse.jdt.internal.compiler.api.problem.IErrorHandlingPolicy
133 * Configurable part for problem handling, allowing the compiler client to
134 * specify the rules for handling problems (stop on first error or accumulate
135 * them all) and at the same time perform some actions such as opening a dialog
136 * in UI when compiling interactively.
137 * @see org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies
139 * @param requestor org.eclipse.jdt.internal.compiler.api.ICompilerRequestor
140 * Component which will receive and persist all compilation results and is intended
141 * to consume them as they are produced. Typically, in a batch compiler, it is
142 * responsible for writing out the actual .class files to the file system.
143 * @see org.eclipse.jdt.internal.compiler.CompilationResult
145 * @param problemFactory org.eclipse.jdt.internal.compiler.api.problem.IProblemFactory
146 * Factory used inside the compiler to create problem descriptors. It allows the
147 * compiler client to supply its own representation of compilation problems in
148 * order to avoid object conversions. Note that the factory is not supposed
149 * to accumulate the created problems, the compiler will gather them all and hand
150 * them back as part of the compilation unit result.
151 * @param parseLiteralExpressionsAsConstants <code>boolean</code>
152 * This parameter is used to optimize the literals or leave them as they are in the source.
153 * If you put true, "Hello" + " world" will be converted to "Hello world".
156 INameEnvironment environment,
157 IErrorHandlingPolicy policy,
159 final ICompilerRequestor requestor,
160 IProblemFactory problemFactory,
161 boolean parseLiteralExpressionsAsConstants) {
163 // create a problem handler given a handling policy
164 this.options = new CompilerOptions(settings);
166 // wrap requestor in DebugRequestor if one is specified
167 if(DebugRequestor == null) {
168 this.requestor = requestor;
170 this.requestor = new ICompilerRequestor(){
171 public void acceptResult(CompilationResult result){
172 if (DebugRequestor.isActive()){
173 DebugRequestor.acceptDebugResult(result);
175 requestor.acceptResult(result);
179 this.problemReporter =
180 new ProblemReporter(policy, this.options, problemFactory);
181 this.lookupEnvironment =
182 new LookupEnvironment(this, options, problemReporter, environment);
186 parseLiteralExpressionsAsConstants,
187 this.options.assertMode);
191 * Add an additional binary type
193 public void accept(IBinaryType binaryType, PackageBinding packageBinding) {
194 lookupEnvironment.createBinaryTypeFrom(binaryType, packageBinding);
198 * Add an additional compilation unit into the loop
199 * -> build compilation unit declarations, their bindings and record their results.
201 public void accept(ICompilationUnit sourceUnit) {
202 // Switch the current policy and compilation result for this unit to the requested one.
203 CompilationResult unitResult =
204 new CompilationResult(sourceUnit, totalUnits, totalUnits, this.options.maxProblemsPerUnit);
206 // diet parsing for large collection of unit
207 CompilationUnitDeclaration parsedUnit;
208 if (totalUnits < parseThreshold) {
209 parsedUnit = parser.parse(sourceUnit, unitResult);
211 parsedUnit = parser.dietParse(sourceUnit, unitResult);
214 if (options.verbose) {
217 "compilation.request" , //$NON-NLS-1$
219 String.valueOf(totalUnits + 1),
220 String.valueOf(totalUnits + 1),
221 new String(sourceUnit.getFileName())}));
224 // initial type binding creation
225 lookupEnvironment.buildTypeBindings(parsedUnit);
226 this.addCompilationUnit(sourceUnit, parsedUnit);
228 // binding resolution
229 lookupEnvironment.completeTypeBindings(parsedUnit);
230 } catch (AbortCompilationUnit e) {
231 // at this point, currentCompilationUnitResult may not be sourceUnit, but some other
232 // one requested further along to resolve sourceUnit.
233 if (unitResult.compilationUnit == sourceUnit) { // only report once
234 requestor.acceptResult(unitResult.tagAsAccepted());
236 throw e; // want to abort enclosing request to compile
242 * Add additional source types
244 public void accept(ISourceType[] sourceTypes, PackageBinding packageBinding) {
245 problemReporter.abortDueToInternalError(
247 "abort.againstSourceModel " , //$NON-NLS-1$
248 String.valueOf(sourceTypes[0].getName()),
249 String.valueOf(sourceTypes[0].getFileName())));
252 protected void addCompilationUnit(
253 ICompilationUnit sourceUnit,
254 CompilationUnitDeclaration parsedUnit) {
256 // append the unit to the list of ones to process later on
257 int size = unitsToProcess.length;
258 if (totalUnits == size)
259 // when growing reposition units starting at position 0
263 (unitsToProcess = new CompilationUnitDeclaration[size * 2]),
266 unitsToProcess[totalUnits++] = parsedUnit;
270 * Add the initial set of compilation units into the loop
271 * -> build compilation unit declarations, their bindings and record their results.
273 protected void beginToCompile(ICompilationUnit[] sourceUnits) {
274 int maxUnits = sourceUnits.length;
276 unitsToProcess = new CompilationUnitDeclaration[maxUnits];
278 // Switch the current policy and compilation result for this unit to the requested one.
279 for (int i = 0; i < maxUnits; i++) {
280 CompilationUnitDeclaration parsedUnit;
281 CompilationResult unitResult =
282 new CompilationResult(sourceUnits[i], i, maxUnits, this.options.maxProblemsPerUnit);
284 // diet parsing for large collection of units
285 if (totalUnits < parseThreshold) {
286 parsedUnit = parser.parse(sourceUnits[i], unitResult);
288 parsedUnit = parser.dietParse(sourceUnits[i], unitResult);
290 if (options.verbose) {
293 "compilation.request" , //$NON-NLS-1$
295 String.valueOf(i + 1),
296 String.valueOf(maxUnits),
297 new String(sourceUnits[i].getFileName())}));
299 // initial type binding creation
300 lookupEnvironment.buildTypeBindings(parsedUnit);
301 this.addCompilationUnit(sourceUnits[i], parsedUnit);
302 //} catch (AbortCompilationUnit e) {
303 // requestor.acceptResult(unitResult.tagAsAccepted());
305 sourceUnits[i] = null; // no longer hold onto the unit
308 // binding resolution
309 lookupEnvironment.completeTypeBindings();
314 * -> compile each of supplied files
315 * -> recompile any required types for which we have an incomplete principle structure
317 public void compile(ICompilationUnit[] sourceUnits) {
318 CompilationUnitDeclaration unit = null;
321 // build and record parsed units
323 beginToCompile(sourceUnits);
325 // process all units (some more could be injected in the loop by the lookup environment)
326 for (; i < totalUnits; i++) {
327 unit = unitsToProcess[i];
332 "compilation.process" , //$NON-NLS-1$
334 String.valueOf(i + 1),
335 String.valueOf(totalUnits),
336 new String(unitsToProcess[i].getFileName())}));
339 // cleanup compilation unit result
342 System.out.println(Util.bind("compilation.done", //$NON-NLS-1$
344 String.valueOf(i + 1),
345 String.valueOf(totalUnits),
346 new String(unitsToProcess[i].getFileName())}));
348 unitsToProcess[i] = null; // release reference to processed unit declaration
349 requestor.acceptResult(unit.compilationResult.tagAsAccepted());
351 } catch (AbortCompilation e) {
352 this.handleInternalException(e, unit);
354 this.handleInternalException(e, unit, null);
356 } catch (RuntimeException e) {
357 this.handleInternalException(e, unit, null);
362 if (options.verbose) {
363 if (totalUnits > 1) {
365 Util.bind("compilation.units" , String.valueOf(totalUnits))); //$NON-NLS-1$
368 Util.bind("compilation.unit" , String.valueOf(totalUnits))); //$NON-NLS-1$
373 protected void getMethodBodies(CompilationUnitDeclaration unit, int place) {
374 //fill the methods bodies in order for the code to be generated
376 if (unit.ignoreMethodBodies) {
377 unit.ignoreFurtherInvestigation = true;
379 // if initial diet parse did not work, no need to dig into method bodies.
382 if (place < parseThreshold)
383 return; //work already done ...
385 //real parse of the method....
386 parser.scanner.setSource(
387 unit.compilationResult.compilationUnit.getContents());
388 if (unit.types != null) {
389 for (int i = unit.types.length; --i >= 0;)
390 unit.types[i].parseMethod(parser, unit);
395 * Compiler crash recovery in case of unexpected runtime exceptions
397 protected void handleInternalException(
398 Throwable internalException,
399 CompilationUnitDeclaration unit,
400 CompilationResult result) {
402 /* dump a stack trace to the console */
403 internalException.printStackTrace();
405 /* find a compilation result */
406 if ((unit != null)) // basing result upon the current unit if available
407 result = unit.compilationResult; // current unit being processed ?
408 if ((result == null) && (unitsToProcess != null) && (totalUnits > 0))
409 result = unitsToProcess[totalUnits - 1].compilationResult;
410 // last unit in beginToCompile ?
412 if (result != null) {
413 /* create and record a compilation problem */
414 StringWriter stringWriter = new StringWriter();
415 PrintWriter writer = new PrintWriter(stringWriter);
416 internalException.printStackTrace(writer);
417 StringBuffer buffer = stringWriter.getBuffer();
423 result.getFileName(),
424 IProblem.Unclassified,
426 Util.bind("compilation.internalError" ) //$NON-NLS-1$
428 + buffer.toString()},
437 /* hand back the compilation result */
438 if (!result.hasBeenAccepted) {
439 requestor.acceptResult(result.tagAsAccepted());
445 * Compiler recovery in case of internal AbortCompilation event
447 protected void handleInternalException(
448 AbortCompilation abortException,
449 CompilationUnitDeclaration unit) {
451 /* special treatment for SilentAbort: silently cancelling the compilation process */
452 if (abortException.isSilent) {
453 if (abortException.silentException == null) {
456 throw abortException.silentException;
460 /* uncomment following line to see where the abort came from */
461 // abortException.printStackTrace();
463 // Exception may tell which compilation result it is related, and which problem caused it
464 CompilationResult result = abortException.compilationResult;
465 if ((result == null) && (unit != null))
466 result = unit.compilationResult; // current unit being processed ?
467 if ((result == null) && (unitsToProcess != null) && (totalUnits > 0))
468 result = unitsToProcess[totalUnits - 1].compilationResult;
469 // last unit in beginToCompile ?
470 if (result != null && !result.hasBeenAccepted) {
471 /* distant problem which could not be reported back there */
472 if (abortException.problemId != 0) {
477 result.getFileName(),
478 abortException.problemId,
479 abortException.problemArguments,
488 /* distant internal exception which could not be reported back there */
489 if (abortException.exception != null) {
490 this.handleInternalException(abortException.exception, null, result);
494 /* hand back the compilation result */
495 if (!result.hasBeenAccepted) {
496 requestor.acceptResult(result.tagAsAccepted());
500 if (abortException.problemId != 0){
502 problemReporter.createProblem(
504 abortException.problemId,
505 abortException.problemArguments,
510 System.out.println(problem.getMessage());
513 abortException.printStackTrace();
518 * Process a compilation unit already parsed and build.
520 private void process(CompilationUnitDeclaration unit, int i) {
522 getMethodBodies(unit, i);
524 // fault in fields & methods
525 if (unit.scope != null)
526 unit.scope.faultInTypes();
528 // verify inherited methods
529 if (unit.scope != null)
530 unit.scope.verifyMethods(lookupEnvironment.methodVerifier());
542 if (options.produceReferenceInfo && unit.scope != null)
543 unit.scope.storeDependencyInfo();
545 // refresh the total number of units known at this stage
546 unit.compilationResult.totalUnitsKnown = totalUnits;
548 public void reset() {
549 lookupEnvironment.reset();
550 parser.scanner.source = null;
551 unitsToProcess = null;
552 if (DebugRequestor != null) DebugRequestor.reset();
556 * Internal API used to resolve a compilation unit minimally for code assist engine
558 public CompilationUnitDeclaration resolve(ICompilationUnit sourceUnit) {
559 CompilationUnitDeclaration unit = null;
561 // build and record parsed units
562 parseThreshold = 0; // will request a full parse
563 beginToCompile(new ICompilationUnit[] { sourceUnit });
564 // process all units (some more could be injected in the loop by the lookup environment)
565 unit = unitsToProcess[0];
566 getMethodBodies(unit, 0);
567 if (unit.scope != null) {
568 // fault in fields & methods
569 unit.scope.faultInTypes();
573 unitsToProcess[0] = null; // release reference to processed unit declaration
574 requestor.acceptResult(unit.compilationResult.tagAsAccepted());
576 } catch (AbortCompilation e) {
577 this.handleInternalException(e, unit);
578 return unit == null ? unitsToProcess[0] : unit;
580 this.handleInternalException(e, unit, null);
582 } catch (RuntimeException e) {
583 this.handleInternalException(e, unit, null);
586 // No reset is performed there anymore since,
587 // within the CodeAssist (or related tools),
588 // the compiler may be called *after* a call
589 // to this resolve(...) method. And such a call
590 // needs to have a compiler with a non-empty