added textArea as a temporary report area. will be replaced by a tree at some point...
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / compiler / Compiler.java
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
7  * 
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *******************************************************************************/
11 package net.sourceforge.phpdt.internal.compiler;
12 import java.io.PrintWriter;
13 import java.io.StringWriter;
14 import java.util.Map;
15 import net.sourceforge.phpdt.core.compiler.IProblem;
16 import net.sourceforge.phpdt.internal.compiler.env.IBinaryType;
17 import net.sourceforge.phpdt.internal.compiler.env.ICompilationUnit;
18 import net.sourceforge.phpdt.internal.compiler.env.INameEnvironment;
19 import net.sourceforge.phpdt.internal.compiler.env.ISourceType;
20 import net.sourceforge.phpdt.internal.compiler.impl.CompilerOptions;
21 import net.sourceforge.phpdt.internal.compiler.impl.ITypeRequestor;
22 import net.sourceforge.phpdt.internal.compiler.lookup.LookupEnvironment;
23 import net.sourceforge.phpdt.internal.compiler.lookup.PackageBinding;
24 import net.sourceforge.phpdt.internal.compiler.parser.UnitParser;
25 import net.sourceforge.phpdt.internal.compiler.problem.AbortCompilation;
26 import net.sourceforge.phpdt.internal.compiler.problem.AbortCompilationUnit;
27 import net.sourceforge.phpdt.internal.compiler.problem.ProblemReporter;
28 import net.sourceforge.phpdt.internal.compiler.problem.ProblemSeverities;
29 import net.sourceforge.phpdt.internal.compiler.util.Util;
30 import net.sourceforge.phpeclipse.internal.compiler.ast.CompilationUnitDeclaration;
31 import net.sourceforge.phpeclipse.internal.compiler.ast.TypeDeclaration;
32 public class Compiler implements ITypeRequestor, ProblemSeverities {
33   public UnitParser parser;
34   public ICompilerRequestor requestor;
35   public CompilerOptions options;
36   public ProblemReporter problemReporter;
37   // management of unit to be processed
38   //public CompilationUnitResult currentCompilationUnitResult;
39   public CompilationUnitDeclaration[] unitsToProcess;
40   public int totalUnits; // (totalUnits-1) gives the last unit in unitToProcess
41   // name lookup
42   public LookupEnvironment lookupEnvironment;
43   // ONCE STABILIZED, THESE SHOULD RETURN TO A FINAL FIELD
44   public static boolean DEBUG = true;
45   public int parseThreshold = -1;
46   // number of initial units parsed at once (-1: none)
47   /*
48    * Static requestor reserved to listening compilation results in debug mode,
49    * so as for example to monitor compiler activity independantly from a
50    * particular builder implementation. It is reset at the end of compilation,
51    * and should not persist any information after having been reset.
52    */
53   //    public static IDebugRequestor DebugRequestor = null;
54   /**
55    * Answer a new compiler using the given name environment and compiler
56    * options. The environment and options will be in effect for the lifetime of
57    * the compiler. When the compiler is run, compilation results are sent to
58    * the given requestor.
59    * 
60    * @param environment
61    *            org.eclipse.jdt.internal.compiler.api.env.INameEnvironment
62    *            Environment used by the compiler in order to resolve type and
63    *            package names. The name environment implements the actual
64    *            connection of the compiler to the outside world (e.g. in batch
65    *            mode the name environment is performing pure file accesses,
66    *            reuse previous build state or connection to repositories).
67    *            Note: the name environment is responsible for implementing the
68    *            actual classpath rules.
69    * 
70    * @param policy
71    *            org.eclipse.jdt.internal.compiler.api.problem.IErrorHandlingPolicy
72    *            Configurable part for problem handling, allowing the compiler
73    *            client to specify the rules for handling problems (stop on
74    *            first error or accumulate them all) and at the same time
75    *            perform some actions such as opening a dialog in UI when
76    *            compiling interactively.
77    * @see org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies
78    * 
79    * @param requestor
80    *            org.eclipse.jdt.internal.compiler.api.ICompilerRequestor
81    *            Component which will receive and persist all compilation
82    *            results and is intended to consume them as they are produced.
83    *            Typically, in a batch compiler, it is responsible for writing
84    *            out the actual .class files to the file system.
85    * @see org.eclipse.jdt.internal.compiler.CompilationResult
86    * 
87    * @param problemFactory
88    *            org.eclipse.jdt.internal.compiler.api.problem.IProblemFactory
89    *            Factory used inside the compiler to create problem descriptors.
90    *            It allows the compiler client to supply its own representation
91    *            of compilation problems in order to avoid object conversions.
92    *            Note that the factory is not supposed to accumulate the created
93    *            problems, the compiler will gather them all and hand them back
94    *            as part of the compilation unit result.
95    */
96   public Compiler(INameEnvironment environment, IErrorHandlingPolicy policy,
97       Map settings, final ICompilerRequestor requestor,
98       IProblemFactory problemFactory) {
99     // create a problem handler given a handling policy
100     this.options = new CompilerOptions(settings);
101     // wrap requestor in DebugRequestor if one is specified
102     //          if(DebugRequestor == null) {
103     this.requestor = requestor;
104     //          } else {
105     //                  this.requestor = new ICompilerRequestor(){
106     //                          public void acceptResult(CompilationResult result){
107     //                                  if (DebugRequestor.isActive()){
108     //                                          DebugRequestor.acceptDebugResult(result);
109     //                                  }
110     //                                  requestor.acceptResult(result);
111     //                          }
112     //                  };
113     //          }
114     this.problemReporter = new ProblemReporter(policy, this.options,
115         problemFactory);
116     this.lookupEnvironment = new LookupEnvironment(this, problemReporter,
117         environment); //options, problemReporter, environment);
118     this.parser = new UnitParser(problemReporter);
119     //                          this.options.parseLiteralExpressionsAsConstants,
120     //                          options.sourceLevel >= CompilerOptions.JDK1_4);
121   }
122   /**
123    * Answer a new compiler using the given name environment and compiler
124    * options. The environment and options will be in effect for the lifetime of
125    * the compiler. When the compiler is run, compilation results are sent to
126    * the given requestor.
127    * 
128    * @param environment
129    *            org.eclipse.jdt.internal.compiler.api.env.INameEnvironment
130    *            Environment used by the compiler in order to resolve type and
131    *            package names. The name environment implements the actual
132    *            connection of the compiler to the outside world (e.g. in batch
133    *            mode the name environment is performing pure file accesses,
134    *            reuse previous build state or connection to repositories).
135    *            Note: the name environment is responsible for implementing the
136    *            actual classpath rules.
137    * 
138    * @param policy
139    *            org.eclipse.jdt.internal.compiler.api.problem.IErrorHandlingPolicy
140    *            Configurable part for problem handling, allowing the compiler
141    *            client to specify the rules for handling problems (stop on
142    *            first error or accumulate them all) and at the same time
143    *            perform some actions such as opening a dialog in UI when
144    *            compiling interactively.
145    * @see org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies
146    * 
147    * @param requestor
148    *            org.eclipse.jdt.internal.compiler.api.ICompilerRequestor
149    *            Component which will receive and persist all compilation
150    *            results and is intended to consume them as they are produced.
151    *            Typically, in a batch compiler, it is responsible for writing
152    *            out the actual .class files to the file system.
153    * @see org.eclipse.jdt.internal.compiler.CompilationResult
154    * 
155    * @param problemFactory
156    *            org.eclipse.jdt.internal.compiler.api.problem.IProblemFactory
157    *            Factory used inside the compiler to create problem descriptors.
158    *            It allows the compiler client to supply its own representation
159    *            of compilation problems in order to avoid object conversions.
160    *            Note that the factory is not supposed to accumulate the created
161    *            problems, the compiler will gather them all and hand them back
162    *            as part of the compilation unit result.
163    * @param parseLiteralExpressionsAsConstants
164    *            <code>boolean</code> This parameter is used to optimize the
165    *            literals or leave them as they are in the source. If you put
166    *            true, "Hello" + " world" will be converted to "Hello world".
167    */
168   public Compiler(INameEnvironment environment, IErrorHandlingPolicy policy,
169       Map settings, final ICompilerRequestor requestor,
170       IProblemFactory problemFactory, boolean parseLiteralExpressionsAsConstants) {
171     // create a problem handler given a handling policy
172     this.options = new CompilerOptions(settings);
173     // wrap requestor in DebugRequestor if one is specified
174     //          if(DebugRequestor == null) {
175     this.requestor = requestor;
176     //          } else {
177     //                  this.requestor = new ICompilerRequestor(){
178     //                          public void acceptResult(CompilationResult result){
179     //                                  if (DebugRequestor.isActive()){
180     //                                          DebugRequestor.acceptDebugResult(result);
181     //                                  }
182     //                                  requestor.acceptResult(result);
183     //                          }
184     //                  };
185     //          }
186     this.problemReporter = new ProblemReporter(policy, this.options,
187         problemFactory);
188     this.lookupEnvironment = new LookupEnvironment(this, problemReporter,
189         environment);//options, problemReporter, environment);
190     this.parser = new UnitParser(problemReporter);
191     //                          parseLiteralExpressionsAsConstants,
192     //                          this.options.sourceLevel >= CompilerOptions.JDK1_4);
193   }
194   /**
195    * Add an additional binary type
196    */
197   public void accept(IBinaryType binaryType, PackageBinding packageBinding) {
198     lookupEnvironment.createBinaryTypeFrom(binaryType, packageBinding);
199   }
200   /**
201    * Add an additional compilation unit into the loop -> build compilation unit
202    * declarations, their bindings and record their results.
203    */
204   public void accept(ICompilationUnit sourceUnit) {
205     // Switch the current policy and compilation result for this unit to the
206     // requested one.
207     CompilationResult unitResult = new CompilationResult(sourceUnit,
208         totalUnits, totalUnits, this.options.maxProblemsPerUnit);
209     try {
210       // diet parsing for large collection of unit
211       CompilationUnitDeclaration parsedUnit;
212       if (totalUnits < parseThreshold) {
213         parsedUnit = parser.parse(sourceUnit, unitResult, false);
214       } else {
215         parsedUnit = parser.dietParse(sourceUnit, unitResult);
216       }
217       if (options.verbose) {
218         String count = String.valueOf(totalUnits + 1);
219         System.out.println(Util.bind("compilation.request", //$NON-NLS-1$
220             new String[]{count, count, new String(sourceUnit.getFileName())}));
221       }
222       // initial type binding creation
223       lookupEnvironment.buildTypeBindings(parsedUnit);
224       this.addCompilationUnit(sourceUnit, parsedUnit);
225       // binding resolution
226       lookupEnvironment.completeTypeBindings(parsedUnit);
227     } catch (AbortCompilationUnit e) {
228       // at this point, currentCompilationUnitResult may not be sourceUnit, but
229       // 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    * Add additional source types
240    */
241   public void accept(ISourceType[] sourceTypes, PackageBinding packageBinding) {
242     problemReporter.abortDueToInternalError(Util.bind(
243         "abort.againstSourceModel ", //$NON-NLS-1$
244         String.valueOf(sourceTypes[0].getName()), String.valueOf(sourceTypes[0]
245             .getFileName())));
246   }
247   protected void addCompilationUnit(ICompilationUnit sourceUnit,
248       CompilationUnitDeclaration parsedUnit) {
249     // append the unit to the list of ones to process later on
250     int size = unitsToProcess.length;
251     if (totalUnits == size)
252       // when growing reposition units starting at position 0
253       System.arraycopy(unitsToProcess, 0,
254           (unitsToProcess = new CompilationUnitDeclaration[size * 2]), 0,
255           totalUnits);
256     unitsToProcess[totalUnits++] = parsedUnit;
257   }
258   /**
259    * Add the initial set of compilation units into the loop -> build
260    * compilation unit declarations, their bindings and record their results.
261    */
262   protected void beginToCompile(ICompilationUnit[] sourceUnits) {
263     int maxUnits = sourceUnits.length;
264     totalUnits = 0;
265     unitsToProcess = new CompilationUnitDeclaration[maxUnits];
266     // Switch the current policy and compilation result for this unit to the
267     // requested one.
268     for (int i = 0; i < maxUnits; i++) {
269       CompilationUnitDeclaration parsedUnit;
270       CompilationResult unitResult = new CompilationResult(sourceUnits[i], i,
271           maxUnits, this.options.maxProblemsPerUnit);
272       try {
273         // diet parsing for large collection of units
274         if (totalUnits < parseThreshold) {
275           parsedUnit = parser.parse(sourceUnits[i], unitResult, false);
276         } else {
277           parsedUnit = parser.dietParse(sourceUnits[i], unitResult);
278         }
279         if (options.verbose) {
280           System.out.println(Util.bind("compilation.request", //$NON-NLS-1$
281               new String[]{String.valueOf(i + 1), String.valueOf(maxUnits),
282                   new String(sourceUnits[i].getFileName())}));
283         }
284         // initial type binding creation
285         //        lookupEnvironment.buildTypeBindings(parsedUnit);
286         this.addCompilationUnit(sourceUnits[i], parsedUnit);
287         //} catch (AbortCompilationUnit e) {
288         //requestor.acceptResult(unitResult.tagAsAccepted());
289       } finally {
290         sourceUnits[i] = null; // no longer hold onto the unit
291       }
292     }
293     // binding resolution
294     lookupEnvironment.completeTypeBindings();
295   }
296   /**
297    * General API -> compile each of supplied files -> recompile any required
298    * types for which we have an incomplete principle structure
299    */
300   public void compile(ICompilationUnit[] sourceUnits) {
301     CompilationUnitDeclaration unit = null;
302     int i = 0;
303     try {
304       // build and record parsed units
305       beginToCompile(sourceUnits);
306       // process all units (some more could be injected in the loop by the
307       // lookup environment)
308       for (; i < totalUnits; i++) {
309         unit = unitsToProcess[i];
310         try {
311           if (options.verbose)
312             System.out.println(Util.bind("compilation.process", //$NON-NLS-1$
313                 new String[]{String.valueOf(i + 1), String.valueOf(totalUnits),
314                     new String(unitsToProcess[i].getFileName())}));
315           process(unit, i);
316         } finally {
317           // cleanup compilation unit result
318           unit.cleanUp();
319           if (options.verbose)
320             System.out.println(Util.bind("compilation.done", //$NON-NLS-1$
321                 new String[]{String.valueOf(i + 1), String.valueOf(totalUnits),
322                     new String(unitsToProcess[i].getFileName())}));
323         }
324         unitsToProcess[i] = null; // release reference to processed unit
325         // declaration
326         requestor.acceptResult(unit.compilationResult.tagAsAccepted());
327       }
328     } catch (AbortCompilation e) { 
329       this.handleInternalException(e, unit);
330     } catch (Error e) {
331       this.handleInternalException(e, unit, null);
332       throw e; // rethrow
333     } catch (RuntimeException e) {
334       this.handleInternalException(e, unit, null);
335       throw e; // rethrow
336     } finally {
337       this.reset();
338     }
339     //          if (options.verbose) {
340     //                  if (totalUnits > 1) {
341     //                          System.out.println(
342     //                                  Util.bind("compilation.units" , String.valueOf(totalUnits)));
343     // //$NON-NLS-1$
344     //                  } else {
345     //                          System.out.println(
346     //                                  Util.bind("compilation.unit" , String.valueOf(totalUnits)));
347     // //$NON-NLS-1$
348     //                  }
349     //          }
350   }
351   protected void getMethodBodies(CompilationUnitDeclaration unit, int place) {
352     //fill the methods bodies in order for the code to be generated
353     if (unit.ignoreMethodBodies) {
354       unit.ignoreFurtherInvestigation = true;
355       return;
356       // if initial diet parse did not work, no need to dig into method bodies.
357     }
358     if (place < parseThreshold)
359       return; //work already done ...
360     //real parse of the method....
361     parser.scanner.setSource(unit.compilationResult.compilationUnit
362         .getContents());
363     if (unit.types != null) {
364       for (int i = unit.types.size(); --i >= 0;)
365         if (unit.types.get(i) instanceof TypeDeclaration) {
366           ((TypeDeclaration) unit.types.get(i)).parseMethod(parser, unit);
367         }
368     }
369   }
370   /*
371    * Compiler crash recovery in case of unexpected runtime exceptions
372    */
373   protected void handleInternalException(Throwable internalException,
374       CompilationUnitDeclaration unit, CompilationResult result) {
375     /* dump a stack trace to the console */
376     internalException.printStackTrace();
377     /* find a compilation result */
378     if ((unit != null)) // basing result upon the current unit if available
379       result = unit.compilationResult; // current unit being processed ?
380     if ((result == null) && (unitsToProcess != null) && (totalUnits > 0))
381       result = unitsToProcess[totalUnits - 1].compilationResult;
382     // last unit in beginToCompile ?
383     if (result != null) {
384       /* create and record a compilation problem */
385       StringWriter stringWriter = new StringWriter();
386       PrintWriter writer = new PrintWriter(stringWriter);
387       internalException.printStackTrace(writer);
388       StringBuffer buffer = stringWriter.getBuffer();
389       String[] pbArguments = new String[]{Util
390           .bind("compilation.internalError")
391           //$NON-NLS-1$
392           + "\n" //$NON-NLS-1$
393           + buffer.toString()};
394       result.record(problemReporter.createProblem(result.getFileName(),
395           IProblem.Unclassified, pbArguments, pbArguments, Error, // severity
396           0, // source start
397           0, // source end
398           0, // line number
399           unit, result), unit);
400       /* hand back the compilation result */
401       if (!result.hasBeenAccepted) {
402         requestor.acceptResult(result.tagAsAccepted());
403       }
404     }
405   }
406   /*
407    * Compiler recovery in case of internal AbortCompilation event
408    */
409   protected void handleInternalException(AbortCompilation abortException,
410       CompilationUnitDeclaration unit) {
411     /*
412      * special treatment for SilentAbort: silently cancelling the compilation
413      * process
414      */
415     if (abortException.isSilent) {
416       if (abortException.silentException == null) {
417         return;
418       } else {
419         throw abortException.silentException;
420       }
421     }
422     /* uncomment following line to see where the abort came from */
423     // abortException.printStackTrace();
424     // Exception may tell which compilation result it is related, and which
425     // problem caused it
426     CompilationResult result = abortException.compilationResult;
427     if ((result == null) && (unit != null))
428       result = unit.compilationResult; // current unit being processed ?
429     if ((result == null) && (unitsToProcess != null) && (totalUnits > 0))
430       result = unitsToProcess[totalUnits - 1].compilationResult;
431     // last unit in beginToCompile ?
432     if (result != null && !result.hasBeenAccepted) {
433       /* distant problem which could not be reported back there */
434       if (abortException.problemId != 0) {
435         result.record(problemReporter.createProblem(result.getFileName(),
436             abortException.problemId, abortException.problemArguments,
437             abortException.messageArguments, Error, // severity
438             0, // source start
439             0, // source end
440             0, // line number
441             unit, result), unit);
442       } else {
443         /* distant internal exception which could not be reported back there */
444         if (abortException.exception != null) {
445           this.handleInternalException(abortException.exception, null, result);
446           return;
447         }
448       }
449       /* hand back the compilation result */
450       if (!result.hasBeenAccepted) {
451         requestor.acceptResult(result.tagAsAccepted());
452       }
453     } else {
454       /*
455        * if (abortException.problemId != 0){ IProblem problem =
456        * problemReporter.createProblem( "???".toCharArray(),
457        * abortException.problemId, abortException.problemArguments, Error, //
458        * severity 0, // source start 0, // source end 0); // line number
459        * System.out.println(problem.getMessage()); }
460        */
461       abortException.printStackTrace();
462     }
463   }
464   /**
465    * Process a compilation unit already parsed and build.
466    */
467   public void process(CompilationUnitDeclaration unit, int i) {
468     getMethodBodies(unit, i);
469     // fault in fields & methods
470     if (unit.scope != null)
471       unit.scope.faultInTypes();
472     // verify inherited methods
473     //    if (unit.scope != null)
474     //      unit.scope.verifyMethods(lookupEnvironment.methodVerifier());
475     // type checking
476     unit.resolve();
477     // flow analysis
478     unit.analyseCode();
479     // code generation
480     //          unit.generateCode();
481     // reference info
482     //          if (options.produceReferenceInfo && unit.scope != null)
483     //                  unit.scope.storeDependencyInfo();
484     // refresh the total number of units known at this stage
485     unit.compilationResult.totalUnitsKnown = totalUnits;
486   }
487   public void reset() {
488     lookupEnvironment.reset();
489     parser.scanner.source = null;
490     unitsToProcess = null;
491     //          if (DebugRequestor != null) DebugRequestor.reset();
492   }
493   /**
494    * Internal API used to resolve a given compilation unit. Can run a subset of
495    * the compilation process
496    */
497   public CompilationUnitDeclaration resolve(ICompilationUnit sourceUnit,
498       boolean verifyMethods, boolean analyzeCode) {
499     //                  boolean generateCode) {
500     CompilationUnitDeclaration unit = null;
501     try {
502       // build and record parsed units
503       parseThreshold = 0; // will request a full parse
504       beginToCompile(new ICompilationUnit[]{sourceUnit});
505       // process all units (some more could be injected in the loop by the
506       // lookup environment)
507       unit = unitsToProcess[0];
508       getMethodBodies(unit, 0);
509       if (unit.scope != null) {
510         //                              // fault in fields & methods
511         //                              unit.scope.faultInTypes();
512         //                              if (unit.scope != null && verifyMethods) {
513         //                                      // http://dev.eclipse.org/bugs/show_bug.cgi?id=23117
514         //                                      // verify inherited methods
515         //                                      unit.scope.verifyMethods(lookupEnvironment.methodVerifier());
516         //                              }
517         //                              // type checking
518         //                              unit.resolve();
519         // flow analysis
520         //                              if (analyzeCode) unit.analyseCode();
521         // code generation
522         //                              if (generateCode) unit.generateCode();
523       }
524       unitsToProcess[0] = null; // release reference to processed unit
525       // declaration
526       requestor.acceptResult(unit.compilationResult.tagAsAccepted());
527       return unit;
528     } catch (AbortCompilation e) {
529       this.handleInternalException(e, unit);
530       return unit == null ? unitsToProcess[0] : unit;
531     } catch (Error e) {
532       this.handleInternalException(e, unit, null);
533       throw e; // rethrow
534     } catch (RuntimeException e) {
535       this.handleInternalException(e, unit, null);
536       throw e; // rethrow
537     } finally {
538       // No reset is performed there anymore since,
539       // within the CodeAssist (or related tools),
540       // the compiler may be called *after* a call
541       // to this resolve(...) method. And such a call
542       // needs to have a compiler with a non-empty
543       // environment.
544       // this.reset();
545     }
546   }
547 }