added -J option to preserve unmodified files in preexisting jarfile
[org.ibex.tool.git] / 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, AccessRestriction accessRestriction) {
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, accessRestriction);
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, AccessRestriction accessRestriction) {
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, accessRestriction);
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, AccessRestriction accessRestriction) {
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, null /*no access restriction*/);
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(
344                                                 Util.bind(
345                                                         "compilation.done", //$NON-NLS-1$
346                                                         new String[] {
347                                                                 String.valueOf(i + 1),
348                                                                 String.valueOf(totalUnits),
349                                                                 new String(unit.getFileName())}));
350                         }
351                 } catch (AbortCompilation e) {
352                         this.handleInternalException(e, unit);
353                 } catch (Error e) {
354                         this.handleInternalException(e, unit, null);
355                         throw e; // rethrow
356                 } catch (RuntimeException e) {
357                         this.handleInternalException(e, unit, null);
358                         throw e; // rethrow
359                 } finally {
360                         this.reset();
361                 }
362                 if (options.verbose) {
363                         if (totalUnits > 1) {
364                                 System.out.println(
365                                         Util.bind("compilation.units" , String.valueOf(totalUnits))); //$NON-NLS-1$
366                         } else {
367                                 System.out.println(
368                                         Util.bind("compilation.unit" , String.valueOf(totalUnits))); //$NON-NLS-1$
369                         }
370                 }
371         }
372
373         /*
374          * Compiler crash recovery in case of unexpected runtime exceptions
375          */
376         protected void handleInternalException(
377                 Throwable internalException,
378                 CompilationUnitDeclaration unit,
379                 CompilationResult result) {
380
381                 /* find a compilation result */
382                 if ((unit != null)) // basing result upon the current unit if available
383                         result = unit.compilationResult; // current unit being processed ?
384                 if ((result == null) && (unitsToProcess != null) && (totalUnits > 0))
385                         result = unitsToProcess[totalUnits - 1].compilationResult;
386                 // last unit in beginToCompile ?
387
388                 boolean needToPrint = true;
389                 if (result != null) {
390                         /* create and record a compilation problem */
391                         StringWriter stringWriter = new StringWriter();
392                         PrintWriter writer = new PrintWriter(stringWriter);
393                         internalException.printStackTrace(writer);
394                         StringBuffer buffer = stringWriter.getBuffer();
395
396                         String[] pbArguments = new String[] {
397                                 Util.bind("compilation.internalError" ) //$NON-NLS-1$
398                                         + "\n"  //$NON-NLS-1$
399                                         + buffer.toString()};
400
401                         result
402                                 .record(
403                                         problemReporter
404                                         .createProblem(
405                                                 result.getFileName(),
406                                                 IProblem.Unclassified,
407                                                 pbArguments,
408                                                 pbArguments,
409                                                 Error, // severity
410                                                 0, // source start
411                                                 0, // source end
412                                                 0), // line number              
413                                         unit);
414
415                         /* hand back the compilation result */
416                         if (!result.hasBeenAccepted) {
417                                 requestor.acceptResult(result.tagAsAccepted());
418                                 needToPrint = false;
419                         }
420                 }
421                 if (needToPrint) {
422                         /* dump a stack trace to the console */
423                         internalException.printStackTrace();
424                 }
425         }
426
427         /*
428          * Compiler recovery in case of internal AbortCompilation event
429          */
430         protected void handleInternalException(
431                 AbortCompilation abortException,
432                 CompilationUnitDeclaration unit) {
433
434                 /* special treatment for SilentAbort: silently cancelling the compilation process */
435                 if (abortException.isSilent) {
436                         if (abortException.silentException == null) {
437                                 return;
438                         }
439                         throw abortException.silentException;
440                 }
441
442                 /* uncomment following line to see where the abort came from */
443                 // abortException.printStackTrace(); 
444
445                 // Exception may tell which compilation result it is related, and which problem caused it
446                 CompilationResult result = abortException.compilationResult;
447                 if ((result == null) && (unit != null)) {
448                         result = unit.compilationResult; // current unit being processed ?
449                 }
450                 // Lookup environment may be in middle of connecting types
451                 if ((result == null) && lookupEnvironment.unitBeingCompleted != null) {
452                     result = lookupEnvironment.unitBeingCompleted.compilationResult;
453                 }
454                 if ((result == null) && (unitsToProcess != null) && (totalUnits > 0))
455                         result = unitsToProcess[totalUnits - 1].compilationResult;
456                 // last unit in beginToCompile ?
457                 if (result != null && !result.hasBeenAccepted) {
458                         /* distant problem which could not be reported back there? */
459                         if (abortException.problem != null) {
460                                 recordDistantProblem: {
461                                         IProblem distantProblem = abortException.problem;
462                                         IProblem[] knownProblems = result.problems;
463                                         for (int i = 0; i < result.problemCount; i++) {
464                                                 if (knownProblems[i] == distantProblem) { // already recorded
465                                                         break recordDistantProblem;
466                                                 }
467                                         }
468                                         if (distantProblem instanceof DefaultProblem) { // fixup filename TODO (philippe) should improve API to make this official
469                                                 ((DefaultProblem) distantProblem).setOriginatingFileName(result.getFileName());
470                                         }
471                                         result  .record(distantProblem, unit);
472                                 }
473                         } else {
474                                 /* distant internal exception which could not be reported back there */
475                                 if (abortException.exception != null) {
476                                         this.handleInternalException(abortException.exception, null, result);
477                                         return;
478                                 }
479                         }
480                         /* hand back the compilation result */
481                         if (!result.hasBeenAccepted) {
482                                 requestor.acceptResult(result.tagAsAccepted());
483                         }
484                 } else {
485                         abortException.printStackTrace();
486                 }
487         }
488
489         public void initializeParser() {
490
491                 this.parser = new Parser(this.problemReporter, this.options.parseLiteralExpressionsAsConstants);
492         }
493         
494         /**
495          * Process a compilation unit already parsed and build.
496          */
497         public void process(CompilationUnitDeclaration unit, int i) {
498
499                 this.parser.getMethodBodies(unit);
500
501                 // fault in fields & methods
502                 if (unit.scope != null)
503                         unit.scope.faultInTypes();
504
505                 // verify inherited methods
506                 if (unit.scope != null)
507                         unit.scope.verifyMethods(lookupEnvironment.methodVerifier());
508
509                 // type checking
510                 unit.resolve();
511
512                 // flow analysis
513                 unit.analyseCode();
514
515                 // code generation
516                 unit.generateCode();
517
518                 // reference info
519                 if (options.produceReferenceInfo && unit.scope != null)
520                         unit.scope.storeDependencyInfo();
521
522                 // refresh the total number of units known at this stage
523                 unit.compilationResult.totalUnitsKnown = totalUnits;
524         }
525         public void reset() {
526                 lookupEnvironment.reset();
527                 parser.scanner.source = null;
528                 unitsToProcess = null;
529                 if (DebugRequestor != null) DebugRequestor.reset();
530         }
531
532         /**
533          * Internal API used to resolve a given compilation unit. Can run a subset of the compilation process
534          */
535         public CompilationUnitDeclaration resolve(
536                         CompilationUnitDeclaration unit, 
537                         ICompilationUnit sourceUnit, 
538                         boolean verifyMethods,
539                         boolean analyzeCode,
540                         boolean generateCode) {
541                                 
542                 try {
543                         if (unit == null) {
544                                 // build and record parsed units
545                                 parseThreshold = 0; // will request a full parse
546                                 beginToCompile(new ICompilationUnit[] { sourceUnit });
547                                 // process all units (some more could be injected in the loop by the lookup environment)
548                                 unit = unitsToProcess[0];
549                         } else {
550                                 // initial type binding creation
551                                 lookupEnvironment.buildTypeBindings(unit, null /*no access restriction*/);
552
553                                 // binding resolution
554                                 lookupEnvironment.completeTypeBindings();
555                         }
556                         this.parser.getMethodBodies(unit);
557                         if (unit.scope != null) {
558                                 // fault in fields & methods
559                                 unit.scope.faultInTypes();
560                                 if (unit.scope != null && verifyMethods) {
561                                         // http://dev.eclipse.org/bugs/show_bug.cgi?id=23117
562                                         // verify inherited methods
563                                         unit.scope.verifyMethods(lookupEnvironment.methodVerifier());
564                                 }
565                                 // type checking
566                                 unit.resolve();         
567
568                                 // flow analysis
569                                 if (analyzeCode) unit.analyseCode();
570                 
571                                 // code generation
572                                 if (generateCode) unit.generateCode();
573                         }
574                         if (unitsToProcess != null) unitsToProcess[0] = null; // release reference to processed unit declaration
575                         requestor.acceptResult(unit.compilationResult.tagAsAccepted());
576                         return unit;
577                 } catch (AbortCompilation e) {
578                         this.handleInternalException(e, unit);
579                         return unit == null ? unitsToProcess[0] : unit;
580                 } catch (Error e) {
581                         this.handleInternalException(e, unit, null);
582                         throw e; // rethrow
583                 } catch (RuntimeException e) {
584                         this.handleInternalException(e, unit, null);
585                         throw e; // rethrow
586                 } finally {
587                         // No reset is performed there anymore since,
588                         // within the CodeAssist (or related tools),
589                         // the compiler may be called *after* a call
590                         // to this resolve(...) method. And such a call
591                         // needs to have a compiler with a non-empty
592                         // environment.
593                         // this.reset();
594                 }
595         }
596         /**
597          * Internal API used to resolve a given compilation unit. Can run a subset of the compilation process
598          */
599         public CompilationUnitDeclaration resolve(
600                         ICompilationUnit sourceUnit, 
601                         boolean verifyMethods,
602                         boolean analyzeCode,
603                         boolean generateCode) {
604                                 
605                 return resolve(
606                         null,
607                         sourceUnit,
608                         verifyMethods,
609                         analyzeCode,
610                         generateCode);
611         }
612 }