1 /*******************************************************************************
2 * Copyright (c) 2000, 2003 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Common Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/cpl-v10.html
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11 package net.sourceforge.phpdt.internal.compiler;
13 import java.io.PrintWriter;
14 import java.io.StringWriter;
17 import net.sourceforge.phpdt.core.compiler.IProblem;
18 import net.sourceforge.phpdt.internal.compiler.ast.CompilationUnitDeclaration;
19 import net.sourceforge.phpdt.internal.compiler.ast.TypeDeclaration;
20 import net.sourceforge.phpdt.internal.compiler.env.IBinaryType;
21 import net.sourceforge.phpdt.internal.compiler.env.ICompilationUnit;
22 import net.sourceforge.phpdt.internal.compiler.env.INameEnvironment;
23 import net.sourceforge.phpdt.internal.compiler.env.ISourceType;
24 import net.sourceforge.phpdt.internal.compiler.impl.CompilerOptions;
25 import net.sourceforge.phpdt.internal.compiler.impl.ITypeRequestor;
26 import net.sourceforge.phpdt.internal.compiler.lookup.LookupEnvironment;
27 import net.sourceforge.phpdt.internal.compiler.lookup.PackageBinding;
28 import net.sourceforge.phpdt.internal.compiler.parser.UnitParser;
29 import net.sourceforge.phpdt.internal.compiler.problem.AbortCompilation;
30 import net.sourceforge.phpdt.internal.compiler.problem.AbortCompilationUnit;
31 import net.sourceforge.phpdt.internal.compiler.problem.ProblemReporter;
32 import net.sourceforge.phpdt.internal.compiler.problem.ProblemSeverities;
33 import net.sourceforge.phpdt.internal.compiler.util.Util;
35 public class Compiler implements ITypeRequestor, ProblemSeverities {
36 public UnitParser parser;
38 public ICompilerRequestor requestor;
40 public CompilerOptions options;
42 public ProblemReporter problemReporter;
44 // management of unit to be processed
45 // public CompilationUnitResult currentCompilationUnitResult;
46 public CompilationUnitDeclaration[] unitsToProcess;
48 public int totalUnits; // (totalUnits-1) gives the last unit in
52 public LookupEnvironment lookupEnvironment;
54 // ONCE STABILIZED, THESE SHOULD RETURN TO A FINAL FIELD
55 public static boolean DEBUG = false;
57 public int parseThreshold = -1;
59 // number of initial units parsed at once (-1: none)
61 * Static requestor reserved to listening compilation results in debug mode,
62 * so as for example to monitor compiler activity independantly from a
63 * particular builder implementation. It is reset at the end of compilation,
64 * and should not persist any information after having been reset.
66 // public static IDebugRequestor DebugRequestor = null;
68 * Answer a new compiler using the given name environment and compiler
69 * options. The environment and options will be in effect for the lifetime
70 * of the compiler. When the compiler is run, compilation results are sent
71 * to the given requestor.
74 * org.eclipse.jdt.internal.compiler.api.env.INameEnvironment
75 * Environment used by the compiler in order to resolve type and
76 * package names. The name environment implements the actual
77 * connection of the compiler to the outside world (e.g. in batch
78 * mode the name environment is performing pure file accesses,
79 * reuse previous build state or connection to repositories).
80 * Note: the name environment is responsible for implementing the
81 * actual classpath rules.
84 * org.eclipse.jdt.internal.compiler.api.problem.IErrorHandlingPolicy
85 * Configurable part for problem handling, allowing the compiler
86 * client to specify the rules for handling problems (stop on
87 * first error or accumulate them all) and at the same time
88 * perform some actions such as opening a dialog in UI when
89 * compiling interactively.
90 * @see org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies
93 * org.eclipse.jdt.internal.compiler.api.ICompilerRequestor
94 * Component which will receive and persist all compilation
95 * results and is intended to consume them as they are produced.
96 * Typically, in a batch compiler, it is responsible for writing
97 * out the actual .class files to the file system.
98 * @see org.eclipse.jdt.internal.compiler.CompilationResult
100 * @param problemFactory
101 * org.eclipse.jdt.internal.compiler.api.problem.IProblemFactory
102 * Factory used inside the compiler to create problem
103 * descriptors. It allows the compiler client to supply its own
104 * representation of compilation problems in order to avoid
105 * object conversions. Note that the factory is not supposed to
106 * accumulate the created problems, the compiler will gather them
107 * all and hand them back as part of the compilation unit result.
109 public Compiler(INameEnvironment environment, IErrorHandlingPolicy policy,
110 Map settings, final ICompilerRequestor requestor,
111 IProblemFactory problemFactory) {
112 // create a problem handler given a handling policy
113 this.options = new CompilerOptions(settings);
114 // wrap requestor in DebugRequestor if one is specified
115 // if(DebugRequestor == null) {
116 this.requestor = requestor;
118 // this.requestor = new ICompilerRequestor(){
119 // public void acceptResult(CompilationResult result){
120 // if (DebugRequestor.isActive()){
121 // DebugRequestor.acceptDebugResult(result);
123 // requestor.acceptResult(result);
127 this.problemReporter = new ProblemReporter(policy, this.options,
129 this.lookupEnvironment = new LookupEnvironment(this, problemReporter,
130 environment); // options, problemReporter, environment);
131 this.parser = new UnitParser(problemReporter);
132 // this.options.parseLiteralExpressionsAsConstants,
133 // options.sourceLevel >= CompilerOptions.JDK1_4);
137 * Answer a new compiler using the given name environment and compiler
138 * options. The environment and options will be in effect for the lifetime
139 * of the compiler. When the compiler is run, compilation results are sent
140 * to the given requestor.
143 * org.eclipse.jdt.internal.compiler.api.env.INameEnvironment
144 * Environment used by the compiler in order to resolve type and
145 * package names. The name environment implements the actual
146 * connection of the compiler to the outside world (e.g. in batch
147 * mode the name environment is performing pure file accesses,
148 * reuse previous build state or connection to repositories).
149 * Note: the name environment is responsible for implementing the
150 * actual classpath rules.
153 * org.eclipse.jdt.internal.compiler.api.problem.IErrorHandlingPolicy
154 * Configurable part for problem handling, allowing the compiler
155 * client to specify the rules for handling problems (stop on
156 * first error or accumulate them all) and at the same time
157 * perform some actions such as opening a dialog in UI when
158 * compiling interactively.
159 * @see org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies
162 * org.eclipse.jdt.internal.compiler.api.ICompilerRequestor
163 * Component which will receive and persist all compilation
164 * results and is intended to consume them as they are produced.
165 * Typically, in a batch compiler, it is responsible for writing
166 * out the actual .class files to the file system.
167 * @see org.eclipse.jdt.internal.compiler.CompilationResult
169 * @param problemFactory
170 * org.eclipse.jdt.internal.compiler.api.problem.IProblemFactory
171 * Factory used inside the compiler to create problem
172 * descriptors. It allows the compiler client to supply its own
173 * representation of compilation problems in order to avoid
174 * object conversions. Note that the factory is not supposed to
175 * accumulate the created problems, the compiler will gather them
176 * all and hand them back as part of the compilation unit result.
177 * @param parseLiteralExpressionsAsConstants
178 * <code>boolean</code> This parameter is used to optimize the
179 * literals or leave them as they are in the source. If you put
180 * true, "Hello" . " world" will be converted to "Hello world".
182 public Compiler(INameEnvironment environment, IErrorHandlingPolicy policy,
183 Map settings, final ICompilerRequestor requestor,
184 IProblemFactory problemFactory,
185 boolean parseLiteralExpressionsAsConstants) {
186 // create a problem handler given a handling policy
187 this.options = new CompilerOptions(settings);
188 // wrap requestor in DebugRequestor if one is specified
189 // if(DebugRequestor == null) {
190 this.requestor = requestor;
192 // this.requestor = new ICompilerRequestor(){
193 // public void acceptResult(CompilationResult result){
194 // if (DebugRequestor.isActive()){
195 // DebugRequestor.acceptDebugResult(result);
197 // requestor.acceptResult(result);
201 this.problemReporter = new ProblemReporter(policy, this.options,
203 this.lookupEnvironment = new LookupEnvironment(this, problemReporter,
204 environment);// options, problemReporter, environment);
205 this.parser = new UnitParser(problemReporter);
206 // parseLiteralExpressionsAsConstants,
207 // this.options.sourceLevel >= CompilerOptions.JDK1_4);
211 * Add an additional binary type
213 public void accept(IBinaryType binaryType, PackageBinding packageBinding) {
214 lookupEnvironment.createBinaryTypeFrom(binaryType, packageBinding);
218 * Add an additional compilation unit into the loop -> build compilation
219 * unit declarations, their bindings and record their results.
221 public void accept(ICompilationUnit sourceUnit) {
222 // Switch the current policy and compilation result for this unit to the
224 CompilationResult unitResult = new CompilationResult(sourceUnit,
225 totalUnits, totalUnits, this.options.maxProblemsPerUnit);
227 // diet parsing for large collection of unit
228 CompilationUnitDeclaration parsedUnit;
229 if (totalUnits < parseThreshold) {
230 parsedUnit = parser.parse(sourceUnit, unitResult, false);
232 parsedUnit = parser.dietParse(sourceUnit, unitResult);
234 if (options.verbose) {
235 String count = String.valueOf(totalUnits + 1);
236 System.out.println(Util.bind("compilation.request", //$NON-NLS-1$
237 new String[] { count, count,
238 new String(sourceUnit.getFileName()) }));
240 // initial type binding creation
241 lookupEnvironment.buildTypeBindings(parsedUnit);
242 this.addCompilationUnit(sourceUnit, parsedUnit);
243 // binding resolution
244 lookupEnvironment.completeTypeBindings(parsedUnit);
245 } catch (AbortCompilationUnit e) {
246 // at this point, currentCompilationUnitResult may not be
249 // one requested further along to resolve sourceUnit.
250 if (unitResult.compilationUnit == sourceUnit) { // only report once
251 requestor.acceptResult(unitResult.tagAsAccepted());
253 throw e; // want to abort enclosing request to compile
259 * Add additional source types
261 public void accept(ISourceType[] sourceTypes, PackageBinding packageBinding) {
262 problemReporter.abortDueToInternalError(Util.bind(
263 "abort.againstSourceModel ", //$NON-NLS-1$
264 String.valueOf(sourceTypes[0].getName()), String
265 .valueOf(sourceTypes[0].getFileName())));
268 protected void addCompilationUnit(ICompilationUnit sourceUnit,
269 CompilationUnitDeclaration parsedUnit) {
270 // append the unit to the list of ones to process later on
271 int size = unitsToProcess.length;
272 if (totalUnits == size)
273 // when growing reposition units starting at position 0
278 (unitsToProcess = new CompilationUnitDeclaration[size * 2]),
280 unitsToProcess[totalUnits++] = parsedUnit;
284 * Add the initial set of compilation units into the loop -> build
285 * compilation unit declarations, their bindings and record their results.
287 protected void beginToCompile(ICompilationUnit[] sourceUnits) {
288 int maxUnits = sourceUnits.length;
290 unitsToProcess = new CompilationUnitDeclaration[maxUnits];
291 // Switch the current policy and compilation result for this unit to the
293 for (int i = 0; i < maxUnits; i++) {
294 CompilationUnitDeclaration parsedUnit;
295 CompilationResult unitResult = new CompilationResult(
296 sourceUnits[i], i, maxUnits,
297 this.options.maxProblemsPerUnit);
299 // diet parsing for large collection of units
300 if (totalUnits < parseThreshold) {
302 .parse(sourceUnits[i], unitResult, false);
304 parsedUnit = parser.dietParse(sourceUnits[i], unitResult);
306 if (options.verbose) {
308 .println(Util.bind("compilation.request", //$NON-NLS-1$
310 String.valueOf(i + 1),
311 String.valueOf(maxUnits),
312 new String(sourceUnits[i]
315 // initial type binding creation
316 // lookupEnvironment.buildTypeBindings(parsedUnit);
317 this.addCompilationUnit(sourceUnits[i], parsedUnit);
318 // } catch (AbortCompilationUnit e) {
319 // requestor.acceptResult(unitResult.tagAsAccepted());
321 sourceUnits[i] = null; // no longer hold onto the unit
324 // binding resolution
325 lookupEnvironment.completeTypeBindings();
329 * General API -> compile each of supplied files -> recompile any required
330 * types for which we have an incomplete principle structure
332 public void compile(ICompilationUnit[] sourceUnits) {
333 CompilationUnitDeclaration unit = null;
336 // build and record parsed units
337 beginToCompile(sourceUnits);
338 // process all units (some more could be injected in the loop by the
339 // lookup environment)
340 for (; i < totalUnits; i++) {
341 unit = unitsToProcess[i];
344 System.out.println(Util.bind("compilation.process", //$NON-NLS-1$
346 String.valueOf(i + 1),
347 String.valueOf(totalUnits),
348 new String(unitsToProcess[i]
352 // cleanup compilation unit result
355 System.out.println(Util.bind("compilation.done", //$NON-NLS-1$
357 String.valueOf(i + 1),
358 String.valueOf(totalUnits),
359 new String(unitsToProcess[i]
362 unitsToProcess[i] = null; // release reference to processed
365 requestor.acceptResult(unit.compilationResult.tagAsAccepted());
367 } catch (AbortCompilation e) {
368 this.handleInternalException(e, unit);
370 this.handleInternalException(e, unit, null);
372 } catch (RuntimeException e) {
373 this.handleInternalException(e, unit, null);
378 // if (options.verbose) {
379 // if (totalUnits > 1) {
380 // System.out.println(
381 // ProjectPrefUtil.bind("compilation.units" ,
382 // String.valueOf(totalUnits)));
385 // System.out.println(
386 // ProjectPrefUtil.bind("compilation.unit" ,
387 // String.valueOf(totalUnits)));
393 protected void getMethodBodies(CompilationUnitDeclaration unit, int place) {
394 // fill the methods bodies in order for the code to be generated
395 if (unit.ignoreMethodBodies) {
396 unit.ignoreFurtherInvestigation = true;
398 // if initial diet parse did not work, no need to dig into method
401 if (place < parseThreshold)
402 return; // work already done ...
403 // real parse of the method....
404 parser.scanner.setSource(unit.compilationResult.compilationUnit
406 if (unit.types != null) {
407 for (int i = unit.types.size(); --i >= 0;)
408 if (unit.types.get(i) instanceof TypeDeclaration) {
409 ((TypeDeclaration) unit.types.get(i)).parseMethod(parser,
416 * Compiler crash recovery in case of unexpected runtime exceptions
418 protected void handleInternalException(Throwable internalException,
419 CompilationUnitDeclaration unit, CompilationResult result) {
420 /* dump a stack trace to the console */
421 internalException.printStackTrace();
422 /* find a compilation result */
423 if ((unit != null)) // basing result upon the current unit if available
424 result = unit.compilationResult; // current unit being processed
426 if ((result == null) && (unitsToProcess != null) && (totalUnits > 0))
427 result = unitsToProcess[totalUnits - 1].compilationResult;
428 // last unit in beginToCompile ?
429 if (result != null) {
430 /* create and record a compilation problem */
431 StringWriter stringWriter = new StringWriter();
432 PrintWriter writer = new PrintWriter(stringWriter);
433 internalException.printStackTrace(writer);
434 StringBuffer buffer = stringWriter.getBuffer();
435 String[] pbArguments = new String[] { Util
436 .bind("compilation.internalError")
439 + buffer.toString() };
440 result.record(problemReporter.createProblem(result.getFileName(),
441 IProblem.Unclassified, pbArguments, pbArguments, Error, // severity
445 unit, result), unit);
446 /* hand back the compilation result */
447 if (!result.hasBeenAccepted) {
448 requestor.acceptResult(result.tagAsAccepted());
454 * Compiler recovery in case of internal AbortCompilation event
456 protected void handleInternalException(AbortCompilation abortException,
457 CompilationUnitDeclaration unit) {
459 * special treatment for SilentAbort: silently cancelling the
460 * compilation process
462 if (abortException.isSilent) {
463 if (abortException.silentException == null) {
466 throw abortException.silentException;
469 /* uncomment following line to see where the abort came from */
470 // abortException.printStackTrace();
471 // Exception may tell which compilation result it is related, and which
473 CompilationResult result = abortException.compilationResult;
474 if ((result == null) && (unit != null))
475 result = unit.compilationResult; // current unit being processed
477 if ((result == null) && (unitsToProcess != null) && (totalUnits > 0))
478 result = unitsToProcess[totalUnits - 1].compilationResult;
479 // last unit in beginToCompile ?
480 if (result != null && !result.hasBeenAccepted) {
481 /* distant problem which could not be reported back there */
482 if (abortException.problemId != 0) {
483 result.record(problemReporter.createProblem(result
484 .getFileName(), abortException.problemId,
485 abortException.problemArguments,
486 abortException.messageArguments, Error, // severity
490 unit, result), unit);
493 * distant internal exception which could not be reported back
496 if (abortException.exception != null) {
497 this.handleInternalException(abortException.exception,
502 /* hand back the compilation result */
503 if (!result.hasBeenAccepted) {
504 requestor.acceptResult(result.tagAsAccepted());
508 * if (abortException.problemId != 0){ IProblem problem =
509 * problemReporter.createProblem( "???".toCharArray(),
510 * abortException.problemId, abortException.problemArguments, Error, //
511 * severity 0, // source start 0, // source end 0); // line number
512 * System.out.println(problem.getMessage()); }
514 abortException.printStackTrace();
519 * Process a compilation unit already parsed and build.
521 public void process(CompilationUnitDeclaration unit, int i) {
522 getMethodBodies(unit, i);
523 // fault in fields & methods
524 if (unit.scope != null)
525 unit.scope.faultInTypes();
526 // verify inherited methods
527 // if (unit.scope != null)
528 // unit.scope.verifyMethods(lookupEnvironment.methodVerifier());
534 // unit.generateCode();
536 // if (options.produceReferenceInfo && unit.scope != null)
537 // unit.scope.storeDependencyInfo();
538 // refresh the total number of units known at this stage
539 unit.compilationResult.totalUnitsKnown = totalUnits;
542 public void reset() {
543 lookupEnvironment.reset();
544 parser.scanner.source = null;
545 unitsToProcess = null;
546 // if (DebugRequestor != null) DebugRequestor.reset();
550 * Internal API used to resolve a given compilation unit. Can run a subset
551 * of the compilation process
553 public CompilationUnitDeclaration resolve(CompilationUnitDeclaration unit,
554 ICompilationUnit sourceUnit, boolean verifyMethods,
555 boolean analyzeCode) {
559 // build and record parsed units
560 parseThreshold = 0; // will request a full parse
561 beginToCompile(new ICompilationUnit[] { sourceUnit });
562 // process all units (some more could be injected in the loop by
563 // the lookup environment)
564 unit = unitsToProcess[0];
566 // initial type binding creation
567 lookupEnvironment.buildTypeBindings(unit);
569 // binding resolution
570 lookupEnvironment.completeTypeBindings();
572 // TODO : jsurfer check this
573 // this.parser.getMethodBodies(unit);
574 getMethodBodies(unit, 0);
576 if (unit.scope != null) {
577 // fault in fields & methods
578 unit.scope.faultInTypes();
579 if (unit.scope != null && verifyMethods) {
580 // http://dev.eclipse.org/bugs/show_bug.cgi?id=23117
581 // verify inherited methods
583 .verifyMethods(lookupEnvironment.methodVerifier());
589 // if (analyzeCode) unit.analyseCode();
592 // if (generateCode) unit.generateCode();
594 if (unitsToProcess != null)
595 unitsToProcess[0] = null; // release reference to processed
597 requestor.acceptResult(unit.compilationResult.tagAsAccepted());
599 } catch (AbortCompilation e) {
600 this.handleInternalException(e, unit);
601 return unit == null ? unitsToProcess[0] : unit;
603 this.handleInternalException(e, unit, null);
605 } catch (RuntimeException e) {
606 this.handleInternalException(e, unit, null);
609 // No reset is performed there anymore since,
610 // within the CodeAssist (or related tools),
611 // the compiler may be called *after* a call
612 // to this resolve(...) method. And such a call
613 // needs to have a compiler with a non-empty
620 * Internal API used to resolve a given compilation unit. Can run a subset
621 * of the compilation process
623 public CompilationUnitDeclaration resolve(ICompilationUnit sourceUnit,
624 boolean verifyMethods, boolean analyzeCode) {
625 // boolean generateCode) {
626 CompilationUnitDeclaration unit = null;
628 // build and record parsed units
629 parseThreshold = 0; // will request a full parse
630 beginToCompile(new ICompilationUnit[] { sourceUnit });
631 // process all units (some more could be injected in the loop by the
632 // lookup environment)
633 unit = unitsToProcess[0];
634 getMethodBodies(unit, 0);
635 if (unit.scope != null) {
636 // // fault in fields & methods
637 // unit.scope.faultInTypes();
638 // if (unit.scope != null && verifyMethods) {
639 // // http://dev.eclipse.org/bugs/show_bug.cgi?id=23117
640 // // verify inherited methods
641 // unit.scope.verifyMethods(lookupEnvironment.methodVerifier());
646 // if (analyzeCode) unit.analyseCode();
648 // if (generateCode) unit.generateCode();
650 unitsToProcess[0] = null; // release reference to processed unit
652 requestor.acceptResult(unit.compilationResult.tagAsAccepted());
654 } catch (AbortCompilation e) {
655 this.handleInternalException(e, unit);
656 return unit == null ? unitsToProcess[0] : unit;
658 this.handleInternalException(e, unit, null);
660 } catch (RuntimeException e) {
661 this.handleInternalException(e, unit, null);
664 // No reset is performed there anymore since,
665 // within the CodeAssist (or related tools),
666 // the compiler may be called *after* a call
667 // to this resolve(...) method. And such a call
668 // needs to have a compiler with a non-empty