Makefile fixup
[org.ibex.tool.git] / repo / org.ibex.tool / src / 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 }