Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/eclipse/jdt/internal/compiler/Compiler.java


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