1 /*******************************************************************************
2 * Copyright (c) 2000, 2003 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 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.env.IBinaryType;
19 import net.sourceforge.phpdt.internal.compiler.env.ICompilationUnit;
20 import net.sourceforge.phpdt.internal.compiler.env.INameEnvironment;
21 import net.sourceforge.phpdt.internal.compiler.env.ISourceType;
22 import net.sourceforge.phpdt.internal.compiler.impl.ITypeRequestor;
23 import net.sourceforge.phpdt.internal.compiler.lookup.LookupEnvironment;
24 import net.sourceforge.phpdt.internal.compiler.lookup.PackageBinding;
25 import net.sourceforge.phpdt.internal.compiler.parser.UnitParser;
26 import net.sourceforge.phpdt.internal.compiler.problem.AbortCompilation;
27 import net.sourceforge.phpdt.internal.compiler.problem.AbortCompilationUnit;
28 import net.sourceforge.phpdt.internal.compiler.problem.ProblemReporter;
29 import net.sourceforge.phpdt.internal.compiler.problem.ProblemSeverities;
30 import net.sourceforge.phpdt.internal.compiler.util.Util;
31 import net.sourceforge.phpeclipse.internal.compiler.ast.CompilationUnitDeclaration;
32 import net.sourceforge.phpeclipse.internal.compiler.ast.TypeDeclaration;
34 public class Compiler implements ITypeRequestor, ProblemSeverities {
35 public UnitParser parser;
36 public ICompilerRequestor requestor;
37 // public CompilerOptions options;
38 public ProblemReporter problemReporter;
40 // management of unit to be processed
41 //public CompilationUnitResult currentCompilationUnitResult;
42 public CompilationUnitDeclaration[] unitsToProcess;
43 public 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, problemFactory);//this.options, problemFactory);
119 this.lookupEnvironment =
120 new LookupEnvironment(this, problemReporter, environment); //options, problemReporter, environment);
124 // this.options.parseLiteralExpressionsAsConstants,
125 // options.sourceLevel >= CompilerOptions.JDK1_4);
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, problemFactory);//, this.options, problemFactory);
190 this.lookupEnvironment =
191 new LookupEnvironment(this, problemReporter, environment);//options, problemReporter, environment);
195 // parseLiteralExpressionsAsConstants,
196 // this.options.sourceLevel >= CompilerOptions.JDK1_4);
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, 10); //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) {
224 // String count = String.valueOf(totalUnits + 1);
225 // System.out.println(
227 // "compilation.request" , //$NON-NLS-1$
231 // new String(sourceUnit.getFileName())}));
234 // initial type binding creation
235 lookupEnvironment.buildTypeBindings(parsedUnit);
236 this.addCompilationUnit(sourceUnit, parsedUnit);
238 // binding resolution
239 lookupEnvironment.completeTypeBindings(parsedUnit);
240 } catch (AbortCompilationUnit e) {
241 // at this point, currentCompilationUnitResult may not be sourceUnit, but some other
242 // one requested further along to resolve sourceUnit.
243 if (unitResult.compilationUnit == sourceUnit) { // only report once
244 requestor.acceptResult(unitResult.tagAsAccepted());
246 throw e; // want to abort enclosing request to compile
252 * Add additional source types
254 public void accept(ISourceType[] sourceTypes, PackageBinding packageBinding) {
255 problemReporter.abortDueToInternalError(
257 "abort.againstSourceModel " , //$NON-NLS-1$
258 String.valueOf(sourceTypes[0].getName()),
259 String.valueOf(sourceTypes[0].getFileName())));
262 protected void addCompilationUnit(
263 ICompilationUnit sourceUnit,
264 CompilationUnitDeclaration parsedUnit) {
266 // append the unit to the list of ones to process later on
267 int size = unitsToProcess.length;
268 if (totalUnits == size)
269 // when growing reposition units starting at position 0
273 (unitsToProcess = new CompilationUnitDeclaration[size * 2]),
276 unitsToProcess[totalUnits++] = parsedUnit;
280 * Add the initial set of compilation units into the loop
281 * -> build compilation unit declarations, their bindings and record their results.
283 protected void beginToCompile(ICompilationUnit[] sourceUnits) {
284 int maxUnits = sourceUnits.length;
286 unitsToProcess = new CompilationUnitDeclaration[maxUnits];
288 // Switch the current policy and compilation result for this unit to the requested one.
289 for (int i = 0; i < maxUnits; i++) {
290 CompilationUnitDeclaration parsedUnit;
291 CompilationResult unitResult =
292 new CompilationResult(sourceUnits[i], i, maxUnits, 10);//, this.options.maxProblemsPerUnit);
294 // diet parsing for large collection of units
295 if (totalUnits < parseThreshold) {
296 parsedUnit = parser.parse(sourceUnits[i], unitResult);
298 parsedUnit = parser.dietParse(sourceUnits[i], unitResult);
300 // if (options.verbose) {
301 // System.out.println(
303 // "compilation.request" , //$NON-NLS-1$
305 // String.valueOf(i + 1),
306 // String.valueOf(maxUnits),
307 // new String(sourceUnits[i].getFileName())}));
309 // initial type binding creation
310 lookupEnvironment.buildTypeBindings(parsedUnit);
311 this.addCompilationUnit(sourceUnits[i], parsedUnit);
312 //} catch (AbortCompilationUnit e) {
313 // requestor.acceptResult(unitResult.tagAsAccepted());
315 sourceUnits[i] = null; // no longer hold onto the unit
318 // binding resolution
319 lookupEnvironment.completeTypeBindings();
324 * -> compile each of supplied files
325 * -> recompile any required types for which we have an incomplete principle structure
327 public void compile(ICompilationUnit[] sourceUnits) {
328 CompilationUnitDeclaration unit = null;
331 // build and record parsed units
333 beginToCompile(sourceUnits);
335 // process all units (some more could be injected in the loop by the lookup environment)
336 for (; i < totalUnits; i++) {
337 unit = unitsToProcess[i];
339 // if (options.verbose)
340 // System.out.println(
342 // "compilation.process" , //$NON-NLS-1$
344 // String.valueOf(i + 1),
345 // String.valueOf(totalUnits),
346 // new String(unitsToProcess[i].getFileName())}));
349 // cleanup compilation unit result
351 // if (options.verbose)
352 // System.out.println(Util.bind("compilation.done", //$NON-NLS-1$
354 // String.valueOf(i + 1),
355 // String.valueOf(totalUnits),
356 // new String(unitsToProcess[i].getFileName())}));
358 unitsToProcess[i] = null; // release reference to processed unit declaration
359 requestor.acceptResult(unit.compilationResult.tagAsAccepted());
361 } catch (AbortCompilation e) {
362 this.handleInternalException(e, unit);
364 this.handleInternalException(e, unit, null);
366 } catch (RuntimeException e) {
367 this.handleInternalException(e, unit, null);
372 // if (options.verbose) {
373 // if (totalUnits > 1) {
374 // System.out.println(
375 // Util.bind("compilation.units" , String.valueOf(totalUnits))); //$NON-NLS-1$
377 // System.out.println(
378 // Util.bind("compilation.unit" , String.valueOf(totalUnits))); //$NON-NLS-1$
383 protected void getMethodBodies(CompilationUnitDeclaration unit, int place) {
384 //fill the methods bodies in order for the code to be generated
386 if (unit.ignoreMethodBodies) {
387 unit.ignoreFurtherInvestigation = true;
389 // if initial diet parse did not work, no need to dig into method bodies.
392 if (place < parseThreshold)
393 return; //work already done ...
395 //real parse of the method....
396 parser.scanner.setSource(
397 unit.compilationResult.compilationUnit.getContents());
398 if (unit.types != null) {
399 for (int i = unit.types.size(); --i >= 0;)
400 if (unit.types.get(i) instanceof TypeDeclaration) {
401 ((TypeDeclaration)unit.types.get(i)).parseMethod(parser, unit);
407 * Compiler crash recovery in case of unexpected runtime exceptions
409 protected void handleInternalException(
410 Throwable internalException,
411 CompilationUnitDeclaration unit,
412 CompilationResult result) {
414 /* dump a stack trace to the console */
415 internalException.printStackTrace();
417 /* find a compilation result */
418 if ((unit != null)) // basing result upon the current unit if available
419 result = unit.compilationResult; // current unit being processed ?
420 if ((result == null) && (unitsToProcess != null) && (totalUnits > 0))
421 result = unitsToProcess[totalUnits - 1].compilationResult;
422 // last unit in beginToCompile ?
424 if (result != null) {
425 /* create and record a compilation problem */
426 StringWriter stringWriter = new StringWriter();
427 PrintWriter writer = new PrintWriter(stringWriter);
428 internalException.printStackTrace(writer);
429 StringBuffer buffer = stringWriter.getBuffer();
431 String[] pbArguments = new String[] {
432 Util.bind("compilation.internalError" ) //$NON-NLS-1$
434 + buffer.toString()};
440 result.getFileName(),
441 IProblem.Unclassified,
452 /* hand back the compilation result */
453 if (!result.hasBeenAccepted) {
454 requestor.acceptResult(result.tagAsAccepted());
460 * Compiler recovery in case of internal AbortCompilation event
462 protected void handleInternalException(
463 AbortCompilation abortException,
464 CompilationUnitDeclaration unit) {
466 /* special treatment for SilentAbort: silently cancelling the compilation process */
467 if (abortException.isSilent) {
468 if (abortException.silentException == null) {
471 throw abortException.silentException;
475 /* uncomment following line to see where the abort came from */
476 // abortException.printStackTrace();
478 // Exception may tell which compilation result it is related, and which problem caused it
479 CompilationResult result = abortException.compilationResult;
480 if ((result == null) && (unit != null))
481 result = unit.compilationResult; // current unit being processed ?
482 if ((result == null) && (unitsToProcess != null) && (totalUnits > 0))
483 result = unitsToProcess[totalUnits - 1].compilationResult;
484 // last unit in beginToCompile ?
485 if (result != null && !result.hasBeenAccepted) {
486 /* distant problem which could not be reported back there */
487 if (abortException.problemId != 0) {
492 result.getFileName(),
493 abortException.problemId,
494 abortException.problemArguments,
495 abortException.messageArguments,
504 /* distant internal exception which could not be reported back there */
505 if (abortException.exception != null) {
506 this.handleInternalException(abortException.exception, null, result);
510 /* hand back the compilation result */
511 if (!result.hasBeenAccepted) {
512 requestor.acceptResult(result.tagAsAccepted());
516 if (abortException.problemId != 0){
518 problemReporter.createProblem(
520 abortException.problemId,
521 abortException.problemArguments,
526 System.out.println(problem.getMessage());
529 abortException.printStackTrace();
534 * Process a compilation unit already parsed and build.
536 public void process(CompilationUnitDeclaration unit, int i) {
538 getMethodBodies(unit, i);
540 // fault in fields & methods
541 if (unit.scope != null)
542 unit.scope.faultInTypes();
544 // verify inherited methods
545 if (unit.scope != null)
546 unit.scope.verifyMethods(lookupEnvironment.methodVerifier());
555 // unit.generateCode();
558 // if (options.produceReferenceInfo && unit.scope != null)
559 // unit.scope.storeDependencyInfo();
561 // refresh the total number of units known at this stage
562 unit.compilationResult.totalUnitsKnown = totalUnits;
564 public void reset() {
565 lookupEnvironment.reset();
566 parser.scanner.source = null;
567 unitsToProcess = null;
568 // if (DebugRequestor != null) DebugRequestor.reset();
572 * Internal API used to resolve a given compilation unit. Can run a subset of the compilation process
574 public CompilationUnitDeclaration resolve(
575 ICompilationUnit sourceUnit,
576 boolean verifyMethods,
577 boolean analyzeCode) {
578 // boolean generateCode) {
580 CompilationUnitDeclaration unit = null;
582 // build and record parsed units
583 parseThreshold = 0; // will request a full parse
584 beginToCompile(new ICompilationUnit[] { sourceUnit });
585 // process all units (some more could be injected in the loop by the lookup environment)
586 unit = unitsToProcess[0];
587 getMethodBodies(unit, 0);
588 if (unit.scope != null) {
589 // // fault in fields & methods
590 // unit.scope.faultInTypes();
592 // if (unit.scope != null && verifyMethods) {
593 // // http://dev.eclipse.org/bugs/show_bug.cgi?id=23117
594 // // verify inherited methods
595 // unit.scope.verifyMethods(lookupEnvironment.methodVerifier());
601 // if (analyzeCode) unit.analyseCode();
604 // if (generateCode) unit.generateCode();
606 unitsToProcess[0] = null; // release reference to processed unit declaration
607 requestor.acceptResult(unit.compilationResult.tagAsAccepted());
609 } catch (AbortCompilation e) {
610 this.handleInternalException(e, unit);
611 return unit == null ? unitsToProcess[0] : unit;
613 this.handleInternalException(e, unit, null);
615 } catch (RuntimeException e) {
616 this.handleInternalException(e, unit, null);
619 // No reset is performed there anymore since,
620 // within the CodeAssist (or related tools),
621 // the compiler may be called *after* a call
622 // to this resolve(...) method. And such a call
623 // needs to have a compiler with a non-empty