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;
16 import net.sourceforge.phpdt.core.compiler.IProblem;
17 import net.sourceforge.phpdt.internal.compiler.env.IBinaryType;
18 import net.sourceforge.phpdt.internal.compiler.env.ICompilationUnit;
19 import net.sourceforge.phpdt.internal.compiler.env.INameEnvironment;
20 import net.sourceforge.phpdt.internal.compiler.env.ISourceType;
21 import net.sourceforge.phpdt.internal.compiler.impl.CompilerOptions;
22 import net.sourceforge.phpdt.internal.compiler.impl.ITypeRequestor;
23 import net.sourceforge.phpdt.internal.compiler.lookup.LookupEnvironment;
24 import net.sourceforge.phpdt.internal.compiler.lookup.PackageBinding;
25 import net.sourceforge.phpdt.internal.compiler.parser.UnitParser;
26 import net.sourceforge.phpdt.internal.compiler.problem.AbortCompilation;
27 import net.sourceforge.phpdt.internal.compiler.problem.AbortCompilationUnit;
28 import net.sourceforge.phpdt.internal.compiler.problem.ProblemReporter;
29 import net.sourceforge.phpdt.internal.compiler.problem.ProblemSeverities;
30 import net.sourceforge.phpdt.internal.compiler.util.Util;
31 import net.sourceforge.phpeclipse.internal.compiler.ast.CompilationUnitDeclaration;
32 import net.sourceforge.phpeclipse.internal.compiler.ast.TypeDeclaration;
34 public class Compiler implements ITypeRequestor, ProblemSeverities {
35 public UnitParser parser;
36 public ICompilerRequestor requestor;
37 public CompilerOptions options;
38 public ProblemReporter problemReporter;
39 // management of unit to be processed
40 //public CompilationUnitResult currentCompilationUnitResult;
41 public CompilationUnitDeclaration[] unitsToProcess;
42 public int totalUnits; // (totalUnits-1) gives the last unit in unitToProcess
44 public LookupEnvironment lookupEnvironment;
45 // ONCE STABILIZED, THESE SHOULD RETURN TO A FINAL FIELD
46 public static boolean DEBUG = false;
47 public int parseThreshold = -1;
48 // number of initial units parsed at once (-1: none)
50 * Static requestor reserved to listening compilation results in debug mode,
51 * so as for example to monitor compiler activity independantly from a
52 * particular builder implementation. It is reset at the end of compilation,
53 * and should not persist any information after having been reset.
55 // public static IDebugRequestor DebugRequestor = null;
57 * Answer a new compiler using the given name environment and compiler
58 * options. The environment and options will be in effect for the lifetime of
59 * the compiler. When the compiler is run, compilation results are sent to
60 * the given requestor.
63 * org.eclipse.jdt.internal.compiler.api.env.INameEnvironment
64 * Environment used by the compiler in order to resolve type and
65 * package names. The name environment implements the actual
66 * connection of the compiler to the outside world (e.g. in batch
67 * mode the name environment is performing pure file accesses,
68 * reuse previous build state or connection to repositories).
69 * Note: the name environment is responsible for implementing the
70 * actual classpath rules.
73 * org.eclipse.jdt.internal.compiler.api.problem.IErrorHandlingPolicy
74 * Configurable part for problem handling, allowing the compiler
75 * client to specify the rules for handling problems (stop on
76 * first error or accumulate them all) and at the same time
77 * perform some actions such as opening a dialog in UI when
78 * compiling interactively.
79 * @see org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies
82 * org.eclipse.jdt.internal.compiler.api.ICompilerRequestor
83 * Component which will receive and persist all compilation
84 * results and is intended to consume them as they are produced.
85 * Typically, in a batch compiler, it is responsible for writing
86 * out the actual .class files to the file system.
87 * @see org.eclipse.jdt.internal.compiler.CompilationResult
89 * @param problemFactory
90 * org.eclipse.jdt.internal.compiler.api.problem.IProblemFactory
91 * Factory used inside the compiler to create problem descriptors.
92 * It allows the compiler client to supply its own representation
93 * of compilation problems in order to avoid object conversions.
94 * Note that the factory is not supposed to accumulate the created
95 * problems, the compiler will gather them all and hand them back
96 * as part of the compilation unit result.
98 public Compiler(INameEnvironment environment, IErrorHandlingPolicy policy,
99 Map settings, final ICompilerRequestor requestor,
100 IProblemFactory problemFactory) {
101 // create a problem handler given a handling policy
102 this.options = new CompilerOptions(settings);
103 // wrap requestor in DebugRequestor if one is specified
104 // if(DebugRequestor == null) {
105 this.requestor = requestor;
107 // this.requestor = new ICompilerRequestor(){
108 // public void acceptResult(CompilationResult result){
109 // if (DebugRequestor.isActive()){
110 // DebugRequestor.acceptDebugResult(result);
112 // requestor.acceptResult(result);
116 this.problemReporter = new ProblemReporter(policy, this.options,
118 this.lookupEnvironment = new LookupEnvironment(this, problemReporter,
119 environment); //options, problemReporter, environment);
120 this.parser = new UnitParser(problemReporter);
121 // this.options.parseLiteralExpressionsAsConstants,
122 // options.sourceLevel >= CompilerOptions.JDK1_4);
125 * Answer a new compiler using the given name environment and compiler
126 * options. The environment and options will be in effect for the lifetime of
127 * the compiler. When the compiler is run, compilation results are sent to
128 * the given requestor.
131 * org.eclipse.jdt.internal.compiler.api.env.INameEnvironment
132 * Environment used by the compiler in order to resolve type and
133 * package names. The name environment implements the actual
134 * connection of the compiler to the outside world (e.g. in batch
135 * mode the name environment is performing pure file accesses,
136 * reuse previous build state or connection to repositories).
137 * Note: the name environment is responsible for implementing the
138 * actual classpath rules.
141 * org.eclipse.jdt.internal.compiler.api.problem.IErrorHandlingPolicy
142 * Configurable part for problem handling, allowing the compiler
143 * client to specify the rules for handling problems (stop on
144 * first error or accumulate them all) and at the same time
145 * perform some actions such as opening a dialog in UI when
146 * compiling interactively.
147 * @see org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies
150 * org.eclipse.jdt.internal.compiler.api.ICompilerRequestor
151 * Component which will receive and persist all compilation
152 * results and is intended to consume them as they are produced.
153 * Typically, in a batch compiler, it is responsible for writing
154 * out the actual .class files to the file system.
155 * @see org.eclipse.jdt.internal.compiler.CompilationResult
157 * @param problemFactory
158 * org.eclipse.jdt.internal.compiler.api.problem.IProblemFactory
159 * Factory used inside the compiler to create problem descriptors.
160 * It allows the compiler client to supply its own representation
161 * of compilation problems in order to avoid object conversions.
162 * Note that the factory is not supposed to accumulate the created
163 * problems, the compiler will gather them all and hand them back
164 * as part of the compilation unit result.
165 * @param parseLiteralExpressionsAsConstants
166 * <code>boolean</code> This parameter is used to optimize the
167 * literals or leave them as they are in the source. If you put
168 * true, "Hello" + " world" will be converted to "Hello world".
170 public Compiler(INameEnvironment environment, IErrorHandlingPolicy policy,
171 Map settings, final ICompilerRequestor requestor,
172 IProblemFactory problemFactory, boolean parseLiteralExpressionsAsConstants) {
173 // create a problem handler given a handling policy
174 this.options = new CompilerOptions(settings);
175 // wrap requestor in DebugRequestor if one is specified
176 // if(DebugRequestor == null) {
177 this.requestor = requestor;
179 // this.requestor = new ICompilerRequestor(){
180 // public void acceptResult(CompilationResult result){
181 // if (DebugRequestor.isActive()){
182 // DebugRequestor.acceptDebugResult(result);
184 // requestor.acceptResult(result);
188 this.problemReporter = new ProblemReporter(policy, this.options,
190 this.lookupEnvironment = new LookupEnvironment(this, problemReporter,
191 environment);//options, problemReporter, environment);
192 this.parser = new UnitParser(problemReporter);
193 // parseLiteralExpressionsAsConstants,
194 // this.options.sourceLevel >= CompilerOptions.JDK1_4);
197 * Add an additional binary type
199 public void accept(IBinaryType binaryType, PackageBinding packageBinding) {
200 lookupEnvironment.createBinaryTypeFrom(binaryType, packageBinding);
203 * Add an additional compilation unit into the loop -> build compilation unit
204 * declarations, their bindings and record their results.
206 public void accept(ICompilationUnit sourceUnit) {
207 // Switch the current policy and compilation result for this unit to the
209 CompilationResult unitResult = new CompilationResult(sourceUnit,
210 totalUnits, totalUnits, this.options.maxProblemsPerUnit);
212 // diet parsing for large collection of unit
213 CompilationUnitDeclaration parsedUnit;
214 if (totalUnits < parseThreshold) {
215 parsedUnit = parser.parse(sourceUnit, unitResult, false);
217 parsedUnit = parser.dietParse(sourceUnit, unitResult);
219 if (options.verbose) {
220 String count = String.valueOf(totalUnits + 1);
221 System.out.println(Util.bind("compilation.request", //$NON-NLS-1$
222 new String[]{count, count, new String(sourceUnit.getFileName())}));
224 // initial type binding creation
225 lookupEnvironment.buildTypeBindings(parsedUnit);
226 this.addCompilationUnit(sourceUnit, parsedUnit);
227 // binding resolution
228 lookupEnvironment.completeTypeBindings(parsedUnit);
229 } catch (AbortCompilationUnit e) {
230 // at this point, currentCompilationUnitResult may not be sourceUnit, but
232 // one requested further along to resolve sourceUnit.
233 if (unitResult.compilationUnit == sourceUnit) { // only report once
234 requestor.acceptResult(unitResult.tagAsAccepted());
236 throw e; // want to abort enclosing request to compile
241 * Add additional source types
243 public void accept(ISourceType[] sourceTypes, PackageBinding packageBinding) {
244 problemReporter.abortDueToInternalError(Util.bind(
245 "abort.againstSourceModel ", //$NON-NLS-1$
246 String.valueOf(sourceTypes[0].getName()), String.valueOf(sourceTypes[0]
249 protected void addCompilationUnit(ICompilationUnit sourceUnit,
250 CompilationUnitDeclaration parsedUnit) {
251 // append the unit to the list of ones to process later on
252 int size = unitsToProcess.length;
253 if (totalUnits == size)
254 // when growing reposition units starting at position 0
255 System.arraycopy(unitsToProcess, 0,
256 (unitsToProcess = new CompilationUnitDeclaration[size * 2]), 0,
258 unitsToProcess[totalUnits++] = parsedUnit;
261 * Add the initial set of compilation units into the loop -> build
262 * compilation unit declarations, their bindings and record their results.
264 protected void beginToCompile(ICompilationUnit[] sourceUnits) {
265 int maxUnits = sourceUnits.length;
267 unitsToProcess = new CompilationUnitDeclaration[maxUnits];
268 // Switch the current policy and compilation result for this unit to the
270 for (int i = 0; i < maxUnits; i++) {
271 CompilationUnitDeclaration parsedUnit;
272 CompilationResult unitResult = new CompilationResult(sourceUnits[i], i,
273 maxUnits, this.options.maxProblemsPerUnit);
275 // diet parsing for large collection of units
276 if (totalUnits < parseThreshold) {
277 parsedUnit = parser.parse(sourceUnits[i], unitResult, false);
279 parsedUnit = parser.dietParse(sourceUnits[i], unitResult);
281 if (options.verbose) {
282 System.out.println(Util.bind("compilation.request", //$NON-NLS-1$
283 new String[]{String.valueOf(i + 1), String.valueOf(maxUnits),
284 new String(sourceUnits[i].getFileName())}));
286 // initial type binding creation
287 // lookupEnvironment.buildTypeBindings(parsedUnit);
288 this.addCompilationUnit(sourceUnits[i], parsedUnit);
289 //} catch (AbortCompilationUnit e) {
290 //requestor.acceptResult(unitResult.tagAsAccepted());
292 sourceUnits[i] = null; // no longer hold onto the unit
295 // binding resolution
296 lookupEnvironment.completeTypeBindings();
299 * General API -> compile each of supplied files -> recompile any required
300 * types for which we have an incomplete principle structure
302 public void compile(ICompilationUnit[] sourceUnits) {
303 CompilationUnitDeclaration unit = null;
306 // build and record parsed units
307 beginToCompile(sourceUnits);
308 // process all units (some more could be injected in the loop by the
309 // lookup environment)
310 for (; i < totalUnits; i++) {
311 unit = unitsToProcess[i];
314 System.out.println(Util.bind("compilation.process", //$NON-NLS-1$
315 new String[]{String.valueOf(i + 1), String.valueOf(totalUnits),
316 new String(unitsToProcess[i].getFileName())}));
319 // cleanup compilation unit result
322 System.out.println(Util.bind("compilation.done", //$NON-NLS-1$
323 new String[]{String.valueOf(i + 1), String.valueOf(totalUnits),
324 new String(unitsToProcess[i].getFileName())}));
326 unitsToProcess[i] = null; // release reference to processed unit
328 requestor.acceptResult(unit.compilationResult.tagAsAccepted());
330 } catch (AbortCompilation e) {
331 this.handleInternalException(e, unit);
333 this.handleInternalException(e, unit, null);
335 } catch (RuntimeException e) {
336 this.handleInternalException(e, unit, null);
341 // if (options.verbose) {
342 // if (totalUnits > 1) {
343 // System.out.println(
344 // Util.bind("compilation.units" , String.valueOf(totalUnits)));
347 // System.out.println(
348 // Util.bind("compilation.unit" , String.valueOf(totalUnits)));
353 protected void getMethodBodies(CompilationUnitDeclaration unit, int place) {
354 //fill the methods bodies in order for the code to be generated
355 if (unit.ignoreMethodBodies) {
356 unit.ignoreFurtherInvestigation = true;
358 // if initial diet parse did not work, no need to dig into method bodies.
360 if (place < parseThreshold)
361 return; //work already done ...
362 //real parse of the method....
363 parser.scanner.setSource(unit.compilationResult.compilationUnit
365 if (unit.types != null) {
366 for (int i = unit.types.size(); --i >= 0;)
367 if (unit.types.get(i) instanceof TypeDeclaration) {
368 ((TypeDeclaration) unit.types.get(i)).parseMethod(parser, unit);
373 * Compiler crash recovery in case of unexpected runtime exceptions
375 protected void handleInternalException(Throwable internalException,
376 CompilationUnitDeclaration unit, CompilationResult result) {
377 /* dump a stack trace to the console */
378 internalException.printStackTrace();
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 if (result != null) {
386 /* create and record a compilation problem */
387 StringWriter stringWriter = new StringWriter();
388 PrintWriter writer = new PrintWriter(stringWriter);
389 internalException.printStackTrace(writer);
390 StringBuffer buffer = stringWriter.getBuffer();
391 String[] pbArguments = new String[]{Util
392 .bind("compilation.internalError")
395 + buffer.toString()};
396 result.record(problemReporter.createProblem(result.getFileName(),
397 IProblem.Unclassified, pbArguments, pbArguments, Error, // severity
401 unit, result), unit);
402 /* hand back the compilation result */
403 if (!result.hasBeenAccepted) {
404 requestor.acceptResult(result.tagAsAccepted());
409 * Compiler recovery in case of internal AbortCompilation event
411 protected void handleInternalException(AbortCompilation abortException,
412 CompilationUnitDeclaration unit) {
414 * special treatment for SilentAbort: silently cancelling the compilation
417 if (abortException.isSilent) {
418 if (abortException.silentException == null) {
421 throw abortException.silentException;
424 /* uncomment following line to see where the abort came from */
425 // abortException.printStackTrace();
426 // Exception may tell which compilation result it is related, and which
428 CompilationResult result = abortException.compilationResult;
429 if ((result == null) && (unit != null))
430 result = unit.compilationResult; // current unit being processed ?
431 if ((result == null) && (unitsToProcess != null) && (totalUnits > 0))
432 result = unitsToProcess[totalUnits - 1].compilationResult;
433 // last unit in beginToCompile ?
434 if (result != null && !result.hasBeenAccepted) {
435 /* distant problem which could not be reported back there */
436 if (abortException.problemId != 0) {
437 result.record(problemReporter.createProblem(result.getFileName(),
438 abortException.problemId, abortException.problemArguments,
439 abortException.messageArguments, Error, // severity
443 unit, result), unit);
445 /* distant internal exception which could not be reported back there */
446 if (abortException.exception != null) {
447 this.handleInternalException(abortException.exception, null, result);
451 /* hand back the compilation result */
452 if (!result.hasBeenAccepted) {
453 requestor.acceptResult(result.tagAsAccepted());
457 * if (abortException.problemId != 0){ IProblem problem =
458 * problemReporter.createProblem( "???".toCharArray(),
459 * abortException.problemId, abortException.problemArguments, Error, //
460 * severity 0, // source start 0, // source end 0); // line number
461 * System.out.println(problem.getMessage()); }
463 abortException.printStackTrace();
467 * Process a compilation unit already parsed and build.
469 public void process(CompilationUnitDeclaration unit, int i) {
470 getMethodBodies(unit, i);
471 // fault in fields & methods
472 if (unit.scope != null)
473 unit.scope.faultInTypes();
474 // verify inherited methods
475 // if (unit.scope != null)
476 // unit.scope.verifyMethods(lookupEnvironment.methodVerifier());
482 // unit.generateCode();
484 // if (options.produceReferenceInfo && unit.scope != null)
485 // unit.scope.storeDependencyInfo();
486 // refresh the total number of units known at this stage
487 unit.compilationResult.totalUnitsKnown = totalUnits;
489 public void reset() {
490 lookupEnvironment.reset();
491 parser.scanner.source = null;
492 unitsToProcess = null;
493 // if (DebugRequestor != null) DebugRequestor.reset();
496 * Internal API used to resolve a given compilation unit. Can run a subset of the compilation process
498 public CompilationUnitDeclaration resolve(
499 CompilationUnitDeclaration unit,
500 ICompilationUnit sourceUnit,
501 boolean verifyMethods,
502 boolean analyzeCode) {
506 // build and record parsed units
507 parseThreshold = 0; // will request a full parse
508 beginToCompile(new ICompilationUnit[] { sourceUnit });
509 // process all units (some more could be injected in the loop by the lookup environment)
510 unit = unitsToProcess[0];
512 // initial type binding creation
513 lookupEnvironment.buildTypeBindings(unit);
515 // binding resolution
516 lookupEnvironment.completeTypeBindings();
518 // TODO : jsurfer check this
519 // this.parser.getMethodBodies(unit);
520 getMethodBodies(unit, 0);
522 if (unit.scope != null) {
523 // fault in fields & methods
524 unit.scope.faultInTypes();
525 if (unit.scope != null && verifyMethods) {
526 // http://dev.eclipse.org/bugs/show_bug.cgi?id=23117
527 // verify inherited methods
528 unit.scope.verifyMethods(lookupEnvironment.methodVerifier());
534 // if (analyzeCode) unit.analyseCode();
537 // if (generateCode) unit.generateCode();
539 if (unitsToProcess != null) unitsToProcess[0] = null; // release reference to processed unit declaration
540 requestor.acceptResult(unit.compilationResult.tagAsAccepted());
542 } catch (AbortCompilation e) {
543 this.handleInternalException(e, unit);
544 return unit == null ? unitsToProcess[0] : unit;
546 this.handleInternalException(e, unit, null);
548 } catch (RuntimeException e) {
549 this.handleInternalException(e, unit, null);
552 // No reset is performed there anymore since,
553 // within the CodeAssist (or related tools),
554 // the compiler may be called *after* a call
555 // to this resolve(...) method. And such a call
556 // needs to have a compiler with a non-empty
562 * Internal API used to resolve a given compilation unit. Can run a subset of
563 * the compilation process
565 public CompilationUnitDeclaration resolve(ICompilationUnit sourceUnit,
566 boolean verifyMethods, boolean analyzeCode) {
567 // boolean generateCode) {
568 CompilationUnitDeclaration unit = null;
570 // build and record parsed units
571 parseThreshold = 0; // will request a full parse
572 beginToCompile(new ICompilationUnit[]{sourceUnit});
573 // process all units (some more could be injected in the loop by the
574 // lookup environment)
575 unit = unitsToProcess[0];
576 getMethodBodies(unit, 0);
577 if (unit.scope != null) {
578 // // fault in fields & methods
579 // unit.scope.faultInTypes();
580 // if (unit.scope != null && verifyMethods) {
581 // // http://dev.eclipse.org/bugs/show_bug.cgi?id=23117
582 // // verify inherited methods
583 // unit.scope.verifyMethods(lookupEnvironment.methodVerifier());
588 // if (analyzeCode) unit.analyseCode();
590 // if (generateCode) unit.generateCode();
592 unitsToProcess[0] = null; // release reference to processed unit
594 requestor.acceptResult(unit.compilationResult.tagAsAccepted());
596 } catch (AbortCompilation e) {
597 this.handleInternalException(e, unit);
598 return unit == null ? unitsToProcess[0] : unit;
600 this.handleInternalException(e, unit, null);
602 } catch (RuntimeException e) {
603 this.handleInternalException(e, unit, null);
606 // No reset is performed there anymore since,
607 // within the CodeAssist (or related tools),
608 // the compiler may be called *after* a call
609 // to this resolve(...) method. And such a call
610 // needs to have a compiler with a non-empty