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