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;
12 import java.io.PrintWriter;
13 import java.io.StringWriter;
15 import net.sourceforge.phpdt.core.compiler.IProblem;
16 import net.sourceforge.phpdt.internal.compiler.env.IBinaryType;
17 import net.sourceforge.phpdt.internal.compiler.env.ICompilationUnit;
18 import net.sourceforge.phpdt.internal.compiler.env.INameEnvironment;
19 import net.sourceforge.phpdt.internal.compiler.env.ISourceType;
20 import net.sourceforge.phpdt.internal.compiler.impl.CompilerOptions;
21 import net.sourceforge.phpdt.internal.compiler.impl.ITypeRequestor;
22 import net.sourceforge.phpdt.internal.compiler.lookup.LookupEnvironment;
23 import net.sourceforge.phpdt.internal.compiler.lookup.PackageBinding;
24 import net.sourceforge.phpdt.internal.compiler.parser.UnitParser;
25 import net.sourceforge.phpdt.internal.compiler.problem.AbortCompilation;
26 import net.sourceforge.phpdt.internal.compiler.problem.AbortCompilationUnit;
27 import net.sourceforge.phpdt.internal.compiler.problem.ProblemReporter;
28 import net.sourceforge.phpdt.internal.compiler.problem.ProblemSeverities;
29 import net.sourceforge.phpdt.internal.compiler.util.Util;
30 import net.sourceforge.phpeclipse.internal.compiler.ast.CompilationUnitDeclaration;
31 import net.sourceforge.phpeclipse.internal.compiler.ast.TypeDeclaration;
32 public class Compiler implements ITypeRequestor, ProblemSeverities {
33 public UnitParser parser;
34 public ICompilerRequestor requestor;
35 public CompilerOptions options;
36 public ProblemReporter problemReporter;
37 // management of unit to be processed
38 //public CompilationUnitResult currentCompilationUnitResult;
39 public CompilationUnitDeclaration[] unitsToProcess;
40 public int totalUnits; // (totalUnits-1) gives the last unit in unitToProcess
42 public LookupEnvironment lookupEnvironment;
43 // ONCE STABILIZED, THESE SHOULD RETURN TO A FINAL FIELD
44 public static boolean DEBUG = true;
45 public int parseThreshold = -1;
46 // number of initial units parsed at once (-1: none)
48 * Static requestor reserved to listening compilation results in debug mode,
49 * so as for example to monitor compiler activity independantly from a
50 * particular builder implementation. It is reset at the end of compilation,
51 * and should not persist any information after having been reset.
53 // public static IDebugRequestor DebugRequestor = null;
55 * Answer a new compiler using the given name environment and compiler
56 * options. The environment and options will be in effect for the lifetime of
57 * the compiler. When the compiler is run, compilation results are sent to
58 * the given requestor.
61 * org.eclipse.jdt.internal.compiler.api.env.INameEnvironment
62 * Environment used by the compiler in order to resolve type and
63 * package names. The name environment implements the actual
64 * connection of the compiler to the outside world (e.g. in batch
65 * mode the name environment is performing pure file accesses,
66 * reuse previous build state or connection to repositories).
67 * Note: the name environment is responsible for implementing the
68 * actual classpath rules.
71 * org.eclipse.jdt.internal.compiler.api.problem.IErrorHandlingPolicy
72 * Configurable part for problem handling, allowing the compiler
73 * client to specify the rules for handling problems (stop on
74 * first error or accumulate them all) and at the same time
75 * perform some actions such as opening a dialog in UI when
76 * compiling interactively.
77 * @see org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies
80 * org.eclipse.jdt.internal.compiler.api.ICompilerRequestor
81 * Component which will receive and persist all compilation
82 * results and is intended to consume them as they are produced.
83 * Typically, in a batch compiler, it is responsible for writing
84 * out the actual .class files to the file system.
85 * @see org.eclipse.jdt.internal.compiler.CompilationResult
87 * @param problemFactory
88 * org.eclipse.jdt.internal.compiler.api.problem.IProblemFactory
89 * Factory used inside the compiler to create problem descriptors.
90 * It allows the compiler client to supply its own representation
91 * of compilation problems in order to avoid object conversions.
92 * Note that the factory is not supposed to accumulate the created
93 * problems, the compiler will gather them all and hand them back
94 * as part of the compilation unit result.
96 public Compiler(INameEnvironment environment, IErrorHandlingPolicy policy,
97 Map settings, final ICompilerRequestor requestor,
98 IProblemFactory problemFactory) {
99 // create a problem handler given a handling policy
100 this.options = new CompilerOptions(settings);
101 // wrap requestor in DebugRequestor if one is specified
102 // if(DebugRequestor == null) {
103 this.requestor = requestor;
105 // this.requestor = new ICompilerRequestor(){
106 // public void acceptResult(CompilationResult result){
107 // if (DebugRequestor.isActive()){
108 // DebugRequestor.acceptDebugResult(result);
110 // requestor.acceptResult(result);
114 this.problemReporter = new ProblemReporter(policy, this.options,
116 this.lookupEnvironment = new LookupEnvironment(this, problemReporter,
117 environment); //options, problemReporter, environment);
118 this.parser = new UnitParser(problemReporter);
119 // this.options.parseLiteralExpressionsAsConstants,
120 // options.sourceLevel >= CompilerOptions.JDK1_4);
123 * Answer a new compiler using the given name environment and compiler
124 * options. The environment and options will be in effect for the lifetime of
125 * the compiler. When the compiler is run, compilation results are sent to
126 * the given requestor.
129 * org.eclipse.jdt.internal.compiler.api.env.INameEnvironment
130 * Environment used by the compiler in order to resolve type and
131 * package names. The name environment implements the actual
132 * connection of the compiler to the outside world (e.g. in batch
133 * mode the name environment is performing pure file accesses,
134 * reuse previous build state or connection to repositories).
135 * Note: the name environment is responsible for implementing the
136 * actual classpath rules.
139 * org.eclipse.jdt.internal.compiler.api.problem.IErrorHandlingPolicy
140 * Configurable part for problem handling, allowing the compiler
141 * client to specify the rules for handling problems (stop on
142 * first error or accumulate them all) and at the same time
143 * perform some actions such as opening a dialog in UI when
144 * compiling interactively.
145 * @see org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies
148 * org.eclipse.jdt.internal.compiler.api.ICompilerRequestor
149 * Component which will receive and persist all compilation
150 * results and is intended to consume them as they are produced.
151 * Typically, in a batch compiler, it is responsible for writing
152 * out the actual .class files to the file system.
153 * @see org.eclipse.jdt.internal.compiler.CompilationResult
155 * @param problemFactory
156 * org.eclipse.jdt.internal.compiler.api.problem.IProblemFactory
157 * Factory used inside the compiler to create problem descriptors.
158 * It allows the compiler client to supply its own representation
159 * of compilation problems in order to avoid object conversions.
160 * Note that the factory is not supposed to accumulate the created
161 * problems, the compiler will gather them all and hand them back
162 * as part of the compilation unit result.
163 * @param parseLiteralExpressionsAsConstants
164 * <code>boolean</code> This parameter is used to optimize the
165 * literals or leave them as they are in the source. If you put
166 * true, "Hello" + " world" will be converted to "Hello world".
168 public Compiler(INameEnvironment environment, IErrorHandlingPolicy policy,
169 Map settings, final ICompilerRequestor requestor,
170 IProblemFactory problemFactory, boolean parseLiteralExpressionsAsConstants) {
171 // create a problem handler given a handling policy
172 this.options = new CompilerOptions(settings);
173 // wrap requestor in DebugRequestor if one is specified
174 // if(DebugRequestor == null) {
175 this.requestor = requestor;
177 // this.requestor = new ICompilerRequestor(){
178 // public void acceptResult(CompilationResult result){
179 // if (DebugRequestor.isActive()){
180 // DebugRequestor.acceptDebugResult(result);
182 // requestor.acceptResult(result);
186 this.problemReporter = new ProblemReporter(policy, this.options,
188 this.lookupEnvironment = new LookupEnvironment(this, problemReporter,
189 environment);//options, problemReporter, environment);
190 this.parser = new UnitParser(problemReporter);
191 // parseLiteralExpressionsAsConstants,
192 // this.options.sourceLevel >= CompilerOptions.JDK1_4);
195 * Add an additional binary type
197 public void accept(IBinaryType binaryType, PackageBinding packageBinding) {
198 lookupEnvironment.createBinaryTypeFrom(binaryType, packageBinding);
201 * Add an additional compilation unit into the loop -> build compilation unit
202 * declarations, their bindings and record their results.
204 public void accept(ICompilationUnit sourceUnit) {
205 // Switch the current policy and compilation result for this unit to the
207 CompilationResult unitResult = new CompilationResult(sourceUnit,
208 totalUnits, totalUnits, this.options.maxProblemsPerUnit);
210 // diet parsing for large collection of unit
211 CompilationUnitDeclaration parsedUnit;
212 if (totalUnits < parseThreshold) {
213 parsedUnit = parser.parse(sourceUnit, unitResult, false);
215 parsedUnit = parser.dietParse(sourceUnit, unitResult);
217 if (options.verbose) {
218 String count = String.valueOf(totalUnits + 1);
219 System.out.println(Util.bind("compilation.request", //$NON-NLS-1$
220 new String[]{count, count, new String(sourceUnit.getFileName())}));
222 // initial type binding creation
223 lookupEnvironment.buildTypeBindings(parsedUnit);
224 this.addCompilationUnit(sourceUnit, parsedUnit);
225 // binding resolution
226 lookupEnvironment.completeTypeBindings(parsedUnit);
227 } catch (AbortCompilationUnit e) {
228 // at this point, currentCompilationUnitResult may not be sourceUnit, but
230 // one requested further along to resolve sourceUnit.
231 if (unitResult.compilationUnit == sourceUnit) { // only report once
232 requestor.acceptResult(unitResult.tagAsAccepted());
234 throw e; // want to abort enclosing request to compile
239 * Add additional source types
241 public void accept(ISourceType[] sourceTypes, PackageBinding packageBinding) {
242 problemReporter.abortDueToInternalError(Util.bind(
243 "abort.againstSourceModel ", //$NON-NLS-1$
244 String.valueOf(sourceTypes[0].getName()), String.valueOf(sourceTypes[0]
247 protected void addCompilationUnit(ICompilationUnit sourceUnit,
248 CompilationUnitDeclaration parsedUnit) {
249 // append the unit to the list of ones to process later on
250 int size = unitsToProcess.length;
251 if (totalUnits == size)
252 // when growing reposition units starting at position 0
253 System.arraycopy(unitsToProcess, 0,
254 (unitsToProcess = new CompilationUnitDeclaration[size * 2]), 0,
256 unitsToProcess[totalUnits++] = parsedUnit;
259 * Add the initial set of compilation units into the loop -> build
260 * compilation unit declarations, their bindings and record their results.
262 protected void beginToCompile(ICompilationUnit[] sourceUnits) {
263 int maxUnits = sourceUnits.length;
265 unitsToProcess = new CompilationUnitDeclaration[maxUnits];
266 // Switch the current policy and compilation result for this unit to the
268 for (int i = 0; i < maxUnits; i++) {
269 CompilationUnitDeclaration parsedUnit;
270 CompilationResult unitResult = new CompilationResult(sourceUnits[i], i,
271 maxUnits, this.options.maxProblemsPerUnit);
273 // diet parsing for large collection of units
274 if (totalUnits < parseThreshold) {
275 parsedUnit = parser.parse(sourceUnits[i], unitResult, false);
277 parsedUnit = parser.dietParse(sourceUnits[i], unitResult);
279 if (options.verbose) {
280 System.out.println(Util.bind("compilation.request", //$NON-NLS-1$
281 new String[]{String.valueOf(i + 1), String.valueOf(maxUnits),
282 new String(sourceUnits[i].getFileName())}));
284 // initial type binding creation
285 // lookupEnvironment.buildTypeBindings(parsedUnit);
286 this.addCompilationUnit(sourceUnits[i], parsedUnit);
287 //} catch (AbortCompilationUnit e) {
288 //requestor.acceptResult(unitResult.tagAsAccepted());
290 sourceUnits[i] = null; // no longer hold onto the unit
293 // binding resolution
294 lookupEnvironment.completeTypeBindings();
297 * General API -> compile each of supplied files -> recompile any required
298 * types for which we have an incomplete principle structure
300 public void compile(ICompilationUnit[] sourceUnits) {
301 CompilationUnitDeclaration unit = null;
304 // build and record parsed units
305 beginToCompile(sourceUnits);
306 // process all units (some more could be injected in the loop by the
307 // lookup environment)
308 for (; i < totalUnits; i++) {
309 unit = unitsToProcess[i];
312 System.out.println(Util.bind("compilation.process", //$NON-NLS-1$
313 new String[]{String.valueOf(i + 1), String.valueOf(totalUnits),
314 new String(unitsToProcess[i].getFileName())}));
317 // cleanup compilation unit result
320 System.out.println(Util.bind("compilation.done", //$NON-NLS-1$
321 new String[]{String.valueOf(i + 1), String.valueOf(totalUnits),
322 new String(unitsToProcess[i].getFileName())}));
324 unitsToProcess[i] = null; // release reference to processed unit
326 requestor.acceptResult(unit.compilationResult.tagAsAccepted());
328 } catch (AbortCompilation e) {
329 this.handleInternalException(e, unit);
331 this.handleInternalException(e, unit, null);
333 } catch (RuntimeException e) {
334 this.handleInternalException(e, unit, null);
339 // if (options.verbose) {
340 // if (totalUnits > 1) {
341 // System.out.println(
342 // Util.bind("compilation.units" , String.valueOf(totalUnits)));
345 // System.out.println(
346 // Util.bind("compilation.unit" , String.valueOf(totalUnits)));
351 protected void getMethodBodies(CompilationUnitDeclaration unit, int place) {
352 //fill the methods bodies in order for the code to be generated
353 if (unit.ignoreMethodBodies) {
354 unit.ignoreFurtherInvestigation = true;
356 // if initial diet parse did not work, no need to dig into method bodies.
358 if (place < parseThreshold)
359 return; //work already done ...
360 //real parse of the method....
361 parser.scanner.setSource(unit.compilationResult.compilationUnit
363 if (unit.types != null) {
364 for (int i = unit.types.size(); --i >= 0;)
365 if (unit.types.get(i) instanceof TypeDeclaration) {
366 ((TypeDeclaration) unit.types.get(i)).parseMethod(parser, unit);
371 * Compiler crash recovery in case of unexpected runtime exceptions
373 protected void handleInternalException(Throwable internalException,
374 CompilationUnitDeclaration unit, CompilationResult result) {
375 /* dump a stack trace to the console */
376 internalException.printStackTrace();
377 /* find a compilation result */
378 if ((unit != null)) // basing result upon the current unit if available
379 result = unit.compilationResult; // current unit being processed ?
380 if ((result == null) && (unitsToProcess != null) && (totalUnits > 0))
381 result = unitsToProcess[totalUnits - 1].compilationResult;
382 // last unit in beginToCompile ?
383 if (result != null) {
384 /* create and record a compilation problem */
385 StringWriter stringWriter = new StringWriter();
386 PrintWriter writer = new PrintWriter(stringWriter);
387 internalException.printStackTrace(writer);
388 StringBuffer buffer = stringWriter.getBuffer();
389 String[] pbArguments = new String[]{Util
390 .bind("compilation.internalError")
393 + buffer.toString()};
394 result.record(problemReporter.createProblem(result.getFileName(),
395 IProblem.Unclassified, pbArguments, pbArguments, Error, // severity
399 unit, result), unit);
400 /* hand back the compilation result */
401 if (!result.hasBeenAccepted) {
402 requestor.acceptResult(result.tagAsAccepted());
407 * Compiler recovery in case of internal AbortCompilation event
409 protected void handleInternalException(AbortCompilation abortException,
410 CompilationUnitDeclaration unit) {
412 * special treatment for SilentAbort: silently cancelling the compilation
415 if (abortException.isSilent) {
416 if (abortException.silentException == null) {
419 throw abortException.silentException;
422 /* uncomment following line to see where the abort came from */
423 // abortException.printStackTrace();
424 // Exception may tell which compilation result it is related, and which
426 CompilationResult result = abortException.compilationResult;
427 if ((result == null) && (unit != null))
428 result = unit.compilationResult; // current unit being processed ?
429 if ((result == null) && (unitsToProcess != null) && (totalUnits > 0))
430 result = unitsToProcess[totalUnits - 1].compilationResult;
431 // last unit in beginToCompile ?
432 if (result != null && !result.hasBeenAccepted) {
433 /* distant problem which could not be reported back there */
434 if (abortException.problemId != 0) {
435 result.record(problemReporter.createProblem(result.getFileName(),
436 abortException.problemId, abortException.problemArguments,
437 abortException.messageArguments, Error, // severity
441 unit, result), unit);
443 /* distant internal exception which could not be reported back there */
444 if (abortException.exception != null) {
445 this.handleInternalException(abortException.exception, null, result);
449 /* hand back the compilation result */
450 if (!result.hasBeenAccepted) {
451 requestor.acceptResult(result.tagAsAccepted());
455 * if (abortException.problemId != 0){ IProblem problem =
456 * problemReporter.createProblem( "???".toCharArray(),
457 * abortException.problemId, abortException.problemArguments, Error, //
458 * severity 0, // source start 0, // source end 0); // line number
459 * System.out.println(problem.getMessage()); }
461 abortException.printStackTrace();
465 * Process a compilation unit already parsed and build.
467 public void process(CompilationUnitDeclaration unit, int i) {
468 getMethodBodies(unit, i);
469 // fault in fields & methods
470 if (unit.scope != null)
471 unit.scope.faultInTypes();
472 // verify inherited methods
473 // if (unit.scope != null)
474 // unit.scope.verifyMethods(lookupEnvironment.methodVerifier());
480 // unit.generateCode();
482 // if (options.produceReferenceInfo && unit.scope != null)
483 // unit.scope.storeDependencyInfo();
484 // refresh the total number of units known at this stage
485 unit.compilationResult.totalUnitsKnown = totalUnits;
487 public void reset() {
488 lookupEnvironment.reset();
489 parser.scanner.source = null;
490 unitsToProcess = null;
491 // if (DebugRequestor != null) DebugRequestor.reset();
494 * Internal API used to resolve a given compilation unit. Can run a subset of the compilation process
496 public CompilationUnitDeclaration resolve(
497 CompilationUnitDeclaration unit,
498 ICompilationUnit sourceUnit,
499 boolean verifyMethods,
500 boolean analyzeCode) {
504 // build and record parsed units
505 parseThreshold = 0; // will request a full parse
506 beginToCompile(new ICompilationUnit[] { sourceUnit });
507 // process all units (some more could be injected in the loop by the lookup environment)
508 unit = unitsToProcess[0];
510 // initial type binding creation
511 lookupEnvironment.buildTypeBindings(unit);
513 // binding resolution
514 lookupEnvironment.completeTypeBindings();
516 // TODO : jsurfer check this
517 // this.parser.getMethodBodies(unit);
518 getMethodBodies(unit, 0);
520 if (unit.scope != null) {
521 // fault in fields & methods
522 unit.scope.faultInTypes();
523 if (unit.scope != null && verifyMethods) {
524 // http://dev.eclipse.org/bugs/show_bug.cgi?id=23117
525 // verify inherited methods
526 unit.scope.verifyMethods(lookupEnvironment.methodVerifier());
532 // if (analyzeCode) unit.analyseCode();
535 // if (generateCode) unit.generateCode();
537 if (unitsToProcess != null) unitsToProcess[0] = null; // release reference to processed unit declaration
538 requestor.acceptResult(unit.compilationResult.tagAsAccepted());
540 } catch (AbortCompilation e) {
541 this.handleInternalException(e, unit);
542 return unit == null ? unitsToProcess[0] : unit;
544 this.handleInternalException(e, unit, null);
546 } catch (RuntimeException e) {
547 this.handleInternalException(e, unit, null);
550 // No reset is performed there anymore since,
551 // within the CodeAssist (or related tools),
552 // the compiler may be called *after* a call
553 // to this resolve(...) method. And such a call
554 // needs to have a compiler with a non-empty
560 * Internal API used to resolve a given compilation unit. Can run a subset of
561 * the compilation process
563 public CompilationUnitDeclaration resolve(ICompilationUnit sourceUnit,
564 boolean verifyMethods, boolean analyzeCode) {
565 // boolean generateCode) {
566 CompilationUnitDeclaration unit = null;
568 // build and record parsed units
569 parseThreshold = 0; // will request a full parse
570 beginToCompile(new ICompilationUnit[]{sourceUnit});
571 // process all units (some more could be injected in the loop by the
572 // lookup environment)
573 unit = unitsToProcess[0];
574 getMethodBodies(unit, 0);
575 if (unit.scope != null) {
576 // // fault in fields & methods
577 // unit.scope.faultInTypes();
578 // if (unit.scope != null && verifyMethods) {
579 // // http://dev.eclipse.org/bugs/show_bug.cgi?id=23117
580 // // verify inherited methods
581 // unit.scope.verifyMethods(lookupEnvironment.methodVerifier());
586 // if (analyzeCode) unit.analyseCode();
588 // if (generateCode) unit.generateCode();
590 unitsToProcess[0] = null; // release reference to processed unit
592 requestor.acceptResult(unit.compilationResult.tagAsAccepted());
594 } catch (AbortCompilation e) {
595 this.handleInternalException(e, unit);
596 return unit == null ? unitsToProcess[0] : unit;
598 this.handleInternalException(e, unit, null);
600 } catch (RuntimeException e) {
601 this.handleInternalException(e, unit, null);
604 // No reset is performed there anymore since,
605 // within the CodeAssist (or related tools),
606 // the compiler may be called *after* a call
607 // to this resolve(...) method. And such a call
608 // needs to have a compiler with a non-empty