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 import org.eclipse.core.resources.IResource;
36 public class Compiler implements ITypeRequestor, ProblemSeverities {
37 public UnitParser parser;
38 public ICompilerRequestor requestor;
39 public CompilerOptions options;
40 public ProblemReporter problemReporter;
41 // management of unit to be processed
42 //public CompilationUnitResult currentCompilationUnitResult;
43 public CompilationUnitDeclaration[] unitsToProcess;
44 public int totalUnits; // (totalUnits-1) gives the last unit in unitToProcess
46 public LookupEnvironment lookupEnvironment;
47 // ONCE STABILIZED, THESE SHOULD RETURN TO A FINAL FIELD
48 public static boolean DEBUG = false;
49 public int parseThreshold = -1;
50 // number of initial units parsed at once (-1: none)
52 * Static requestor reserved to listening compilation results in debug mode,
53 * so as for example to monitor compiler activity independantly from a
54 * particular builder implementation. It is reset at the end of compilation,
55 * and should not persist any information after having been reset.
57 // public static IDebugRequestor DebugRequestor = null;
59 * Answer a new compiler using the given name environment and compiler
60 * options. The environment and options will be in effect for the lifetime of
61 * the compiler. When the compiler is run, compilation results are sent to
62 * the given requestor.
65 * org.eclipse.jdt.internal.compiler.api.env.INameEnvironment
66 * Environment used by the compiler in order to resolve type and
67 * package names. The name environment implements the actual
68 * connection of the compiler to the outside world (e.g. in batch
69 * mode the name environment is performing pure file accesses,
70 * reuse previous build state or connection to repositories).
71 * Note: the name environment is responsible for implementing the
72 * actual classpath rules.
75 * org.eclipse.jdt.internal.compiler.api.problem.IErrorHandlingPolicy
76 * Configurable part for problem handling, allowing the compiler
77 * client to specify the rules for handling problems (stop on
78 * first error or accumulate them all) and at the same time
79 * perform some actions such as opening a dialog in UI when
80 * compiling interactively.
81 * @see org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies
84 * org.eclipse.jdt.internal.compiler.api.ICompilerRequestor
85 * Component which will receive and persist all compilation
86 * results and is intended to consume them as they are produced.
87 * Typically, in a batch compiler, it is responsible for writing
88 * out the actual .class files to the file system.
89 * @see org.eclipse.jdt.internal.compiler.CompilationResult
91 * @param problemFactory
92 * org.eclipse.jdt.internal.compiler.api.problem.IProblemFactory
93 * Factory used inside the compiler to create problem descriptors.
94 * It allows the compiler client to supply its own representation
95 * of compilation problems in order to avoid object conversions.
96 * Note that the factory is not supposed to accumulate the created
97 * problems, the compiler will gather them all and hand them back
98 * as part of the compilation unit result.
100 public Compiler(INameEnvironment environment, IErrorHandlingPolicy policy,
101 Map settings, final ICompilerRequestor requestor,
102 IProblemFactory problemFactory) {
103 // create a problem handler given a handling policy
104 this.options = new CompilerOptions(settings);
105 // wrap requestor in DebugRequestor if one is specified
106 // if(DebugRequestor == null) {
107 this.requestor = requestor;
109 // this.requestor = new ICompilerRequestor(){
110 // public void acceptResult(CompilationResult result){
111 // if (DebugRequestor.isActive()){
112 // DebugRequestor.acceptDebugResult(result);
114 // requestor.acceptResult(result);
118 this.problemReporter = new ProblemReporter(policy, this.options,
120 this.lookupEnvironment = new LookupEnvironment(this, problemReporter,
121 environment); //options, problemReporter, environment);
122 this.parser = new UnitParser(problemReporter);
123 // this.options.parseLiteralExpressionsAsConstants,
124 // options.sourceLevel >= CompilerOptions.JDK1_4);
127 * Answer a new compiler using the given name environment and compiler
128 * options. The environment and options will be in effect for the lifetime of
129 * the compiler. When the compiler is run, compilation results are sent to
130 * the given requestor.
133 * org.eclipse.jdt.internal.compiler.api.env.INameEnvironment
134 * Environment used by the compiler in order to resolve type and
135 * package names. The name environment implements the actual
136 * connection of the compiler to the outside world (e.g. in batch
137 * mode the name environment is performing pure file accesses,
138 * reuse previous build state or connection to repositories).
139 * Note: the name environment is responsible for implementing the
140 * actual classpath rules.
143 * org.eclipse.jdt.internal.compiler.api.problem.IErrorHandlingPolicy
144 * Configurable part for problem handling, allowing the compiler
145 * client to specify the rules for handling problems (stop on
146 * first error or accumulate them all) and at the same time
147 * perform some actions such as opening a dialog in UI when
148 * compiling interactively.
149 * @see org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies
152 * org.eclipse.jdt.internal.compiler.api.ICompilerRequestor
153 * Component which will receive and persist all compilation
154 * results and is intended to consume them as they are produced.
155 * Typically, in a batch compiler, it is responsible for writing
156 * out the actual .class files to the file system.
157 * @see org.eclipse.jdt.internal.compiler.CompilationResult
159 * @param problemFactory
160 * org.eclipse.jdt.internal.compiler.api.problem.IProblemFactory
161 * Factory used inside the compiler to create problem descriptors.
162 * It allows the compiler client to supply its own representation
163 * of compilation problems in order to avoid object conversions.
164 * Note that the factory is not supposed to accumulate the created
165 * problems, the compiler will gather them all and hand them back
166 * as part of the compilation unit result.
167 * @param parseLiteralExpressionsAsConstants
168 * <code>boolean</code> This parameter is used to optimize the
169 * literals or leave them as they are in the source. If you put
170 * true, "Hello" . " world" will be converted to "Hello world".
172 public Compiler(INameEnvironment environment, IErrorHandlingPolicy policy,
173 Map settings, final ICompilerRequestor requestor,
174 IProblemFactory problemFactory, boolean parseLiteralExpressionsAsConstants) {
175 // create a problem handler given a handling policy
176 this.options = new CompilerOptions(settings);
177 // wrap requestor in DebugRequestor if one is specified
178 // if(DebugRequestor == null) {
179 this.requestor = requestor;
181 // this.requestor = new ICompilerRequestor(){
182 // public void acceptResult(CompilationResult result){
183 // if (DebugRequestor.isActive()){
184 // DebugRequestor.acceptDebugResult(result);
186 // requestor.acceptResult(result);
190 this.problemReporter = new ProblemReporter(policy, this.options,
192 this.lookupEnvironment = new LookupEnvironment(this, problemReporter,
193 environment);//options, problemReporter, environment);
194 this.parser = new UnitParser(problemReporter);
195 // parseLiteralExpressionsAsConstants,
196 // this.options.sourceLevel >= CompilerOptions.JDK1_4);
199 * Add an additional binary type
201 public void accept(IBinaryType binaryType, PackageBinding packageBinding) {
202 lookupEnvironment.createBinaryTypeFrom(binaryType, packageBinding);
205 * Add an additional compilation unit into the loop -> build compilation unit
206 * declarations, their bindings and record their results.
208 public void accept(ICompilationUnit sourceUnit) {
209 // Switch the current policy and compilation result for this unit to the
211 CompilationResult unitResult = new CompilationResult(sourceUnit,
212 totalUnits, totalUnits, this.options.maxProblemsPerUnit);
214 // diet parsing for large collection of unit
215 CompilationUnitDeclaration parsedUnit;
216 if (totalUnits < parseThreshold) {
217 parsedUnit = parser.parse(sourceUnit, unitResult, false);
219 parsedUnit = parser.dietParse(sourceUnit, unitResult);
221 if (options.verbose) {
222 String count = String.valueOf(totalUnits + 1);
223 System.out.println(Util.bind("compilation.request", //$NON-NLS-1$
224 new String[]{count, count, new String(sourceUnit.getFileName())}));
226 // initial type binding creation
227 lookupEnvironment.buildTypeBindings(parsedUnit);
228 this.addCompilationUnit(sourceUnit, parsedUnit);
229 // binding resolution
230 lookupEnvironment.completeTypeBindings(parsedUnit);
231 } catch (AbortCompilationUnit e) {
232 // at this point, currentCompilationUnitResult may not be sourceUnit, but
234 // one requested further along to resolve sourceUnit.
235 if (unitResult.compilationUnit == sourceUnit) { // only report once
236 requestor.acceptResult(unitResult.tagAsAccepted());
238 throw e; // want to abort enclosing request to compile
243 * Add additional source types
245 public void accept(ISourceType[] sourceTypes, PackageBinding packageBinding) {
246 problemReporter.abortDueToInternalError(Util.bind(
247 "abort.againstSourceModel ", //$NON-NLS-1$
248 String.valueOf(sourceTypes[0].getName()), String.valueOf(sourceTypes[0]
251 protected void addCompilationUnit(ICompilationUnit sourceUnit,
252 CompilationUnitDeclaration parsedUnit) {
253 // append the unit to the list of ones to process later on
254 int size = unitsToProcess.length;
255 if (totalUnits == size)
256 // when growing reposition units starting at position 0
257 System.arraycopy(unitsToProcess, 0,
258 (unitsToProcess = new CompilationUnitDeclaration[size * 2]), 0,
260 unitsToProcess[totalUnits++] = parsedUnit;
263 * Add the initial set of compilation units into the loop -> build
264 * compilation unit declarations, their bindings and record their results.
266 protected void beginToCompile(ICompilationUnit[] sourceUnits) {
267 int maxUnits = sourceUnits.length;
269 unitsToProcess = new CompilationUnitDeclaration[maxUnits];
270 // Switch the current policy and compilation result for this unit to the
272 for (int i = 0; i < maxUnits; i++) {
273 CompilationUnitDeclaration parsedUnit;
274 CompilationResult unitResult = new CompilationResult(sourceUnits[i], i,
275 maxUnits, this.options.maxProblemsPerUnit);
277 // diet parsing for large collection of units
278 if (totalUnits < parseThreshold) {
279 parsedUnit = parser.parse(sourceUnits[i], unitResult, false);
281 parsedUnit = parser.dietParse(sourceUnits[i], unitResult);
283 if (options.verbose) {
284 System.out.println(Util.bind("compilation.request", //$NON-NLS-1$
285 new String[]{String.valueOf(i + 1), String.valueOf(maxUnits),
286 new String(sourceUnits[i].getFileName())}));
288 // initial type binding creation
289 // lookupEnvironment.buildTypeBindings(parsedUnit);
290 this.addCompilationUnit(sourceUnits[i], parsedUnit);
291 //} catch (AbortCompilationUnit e) {
292 //requestor.acceptResult(unitResult.tagAsAccepted());
294 sourceUnits[i] = null; // no longer hold onto the unit
297 // binding resolution
298 lookupEnvironment.completeTypeBindings();
301 * General API -> compile each of supplied files -> recompile any required
302 * types for which we have an incomplete principle structure
304 public void compile(ICompilationUnit[] sourceUnits) {
305 CompilationUnitDeclaration unit = null;
308 // build and record parsed units
309 beginToCompile(sourceUnits);
310 // process all units (some more could be injected in the loop by the
311 // lookup environment)
312 for (; i < totalUnits; i++) {
313 unit = unitsToProcess[i];
316 System.out.println(Util.bind("compilation.process", //$NON-NLS-1$
317 new String[]{String.valueOf(i + 1), String.valueOf(totalUnits),
318 new String(unitsToProcess[i].getFileName())}));
321 // cleanup compilation unit result
324 System.out.println(Util.bind("compilation.done", //$NON-NLS-1$
325 new String[]{String.valueOf(i + 1), String.valueOf(totalUnits),
326 new String(unitsToProcess[i].getFileName())}));
328 unitsToProcess[i] = null; // release reference to processed unit
330 requestor.acceptResult(unit.compilationResult.tagAsAccepted());
332 } catch (AbortCompilation e) {
333 this.handleInternalException(e, unit);
335 this.handleInternalException(e, unit, null);
337 } catch (RuntimeException e) {
338 this.handleInternalException(e, unit, null);
343 // if (options.verbose) {
344 // if (totalUnits > 1) {
345 // System.out.println(
346 // ProjectPrefUtil.bind("compilation.units" , String.valueOf(totalUnits)));
349 // System.out.println(
350 // ProjectPrefUtil.bind("compilation.unit" , String.valueOf(totalUnits)));
355 protected void getMethodBodies(CompilationUnitDeclaration unit, int place) {
356 //fill the methods bodies in order for the code to be generated
357 if (unit.ignoreMethodBodies) {
358 unit.ignoreFurtherInvestigation = true;
360 // if initial diet parse did not work, no need to dig into method bodies.
362 if (place < parseThreshold)
363 return; //work already done ...
364 //real parse of the method....
365 parser.scanner.setSource(unit.compilationResult.compilationUnit
367 if (unit.types != null) {
368 for (int i = unit.types.size(); --i >= 0;)
369 if (unit.types.get(i) instanceof TypeDeclaration) {
370 ((TypeDeclaration) unit.types.get(i)).parseMethod(parser, unit);
375 * Compiler crash recovery in case of unexpected runtime exceptions
377 protected void handleInternalException(Throwable internalException,
378 CompilationUnitDeclaration unit, CompilationResult result) {
379 /* dump a stack trace to the console */
380 internalException.printStackTrace();
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 if (result != null) {
388 /* create and record a compilation problem */
389 StringWriter stringWriter = new StringWriter();
390 PrintWriter writer = new PrintWriter(stringWriter);
391 internalException.printStackTrace(writer);
392 StringBuffer buffer = stringWriter.getBuffer();
393 String[] pbArguments = new String[]{Util
394 .bind("compilation.internalError")
397 + buffer.toString()};
398 result.record(problemReporter.createProblem(result.getFileName(),
399 IProblem.Unclassified, pbArguments, pbArguments, Error, // severity
403 unit, result), unit);
404 /* hand back the compilation result */
405 if (!result.hasBeenAccepted) {
406 requestor.acceptResult(result.tagAsAccepted());
411 * Compiler recovery in case of internal AbortCompilation event
413 protected void handleInternalException(AbortCompilation abortException,
414 CompilationUnitDeclaration unit) {
416 * special treatment for SilentAbort: silently cancelling the compilation
419 if (abortException.isSilent) {
420 if (abortException.silentException == null) {
423 throw abortException.silentException;
426 /* uncomment following line to see where the abort came from */
427 // abortException.printStackTrace();
428 // Exception may tell which compilation result it is related, and which
430 CompilationResult result = abortException.compilationResult;
431 if ((result == null) && (unit != null))
432 result = unit.compilationResult; // current unit being processed ?
433 if ((result == null) && (unitsToProcess != null) && (totalUnits > 0))
434 result = unitsToProcess[totalUnits - 1].compilationResult;
435 // last unit in beginToCompile ?
436 if (result != null && !result.hasBeenAccepted) {
437 /* distant problem which could not be reported back there */
438 if (abortException.problemId != 0) {
439 result.record(problemReporter.createProblem(result.getFileName(),
440 abortException.problemId, abortException.problemArguments,
441 abortException.messageArguments, Error, // severity
445 unit, result), unit);
447 /* distant internal exception which could not be reported back there */
448 if (abortException.exception != null) {
449 this.handleInternalException(abortException.exception, null, result);
453 /* hand back the compilation result */
454 if (!result.hasBeenAccepted) {
455 requestor.acceptResult(result.tagAsAccepted());
459 * if (abortException.problemId != 0){ IProblem problem =
460 * problemReporter.createProblem( "???".toCharArray(),
461 * abortException.problemId, abortException.problemArguments, Error, //
462 * severity 0, // source start 0, // source end 0); // line number
463 * System.out.println(problem.getMessage()); }
465 abortException.printStackTrace();
469 * Process a compilation unit already parsed and build.
471 public void process(CompilationUnitDeclaration unit, int i) {
472 getMethodBodies(unit, i);
473 // fault in fields & methods
474 if (unit.scope != null)
475 unit.scope.faultInTypes();
476 // verify inherited methods
477 // if (unit.scope != null)
478 // unit.scope.verifyMethods(lookupEnvironment.methodVerifier());
484 // unit.generateCode();
486 // if (options.produceReferenceInfo && unit.scope != null)
487 // unit.scope.storeDependencyInfo();
488 // refresh the total number of units known at this stage
489 unit.compilationResult.totalUnitsKnown = totalUnits;
491 public void reset() {
492 lookupEnvironment.reset();
493 parser.scanner.source = null;
494 unitsToProcess = null;
495 // if (DebugRequestor != null) DebugRequestor.reset();
498 * Internal API used to resolve a given compilation unit. Can run a subset of the compilation process
500 public CompilationUnitDeclaration resolve(
501 CompilationUnitDeclaration unit,
502 ICompilationUnit sourceUnit,
503 boolean verifyMethods,
504 boolean analyzeCode) {
508 // build and record parsed units
509 parseThreshold = 0; // will request a full parse
510 beginToCompile(new ICompilationUnit[] { sourceUnit });
511 // process all units (some more could be injected in the loop by the lookup environment)
512 unit = unitsToProcess[0];
514 // initial type binding creation
515 lookupEnvironment.buildTypeBindings(unit);
517 // binding resolution
518 lookupEnvironment.completeTypeBindings();
520 // TODO : jsurfer check this
521 // this.parser.getMethodBodies(unit);
522 getMethodBodies(unit, 0);
524 if (unit.scope != null) {
525 // fault in fields & methods
526 unit.scope.faultInTypes();
527 if (unit.scope != null && verifyMethods) {
528 // http://dev.eclipse.org/bugs/show_bug.cgi?id=23117
529 // verify inherited methods
530 unit.scope.verifyMethods(lookupEnvironment.methodVerifier());
536 // if (analyzeCode) unit.analyseCode();
539 // if (generateCode) unit.generateCode();
541 if (unitsToProcess != null) unitsToProcess[0] = null; // release reference to processed unit declaration
542 requestor.acceptResult(unit.compilationResult.tagAsAccepted());
544 } catch (AbortCompilation e) {
545 this.handleInternalException(e, unit);
546 return unit == null ? unitsToProcess[0] : unit;
548 this.handleInternalException(e, unit, null);
550 } catch (RuntimeException e) {
551 this.handleInternalException(e, unit, null);
554 // No reset is performed there anymore since,
555 // within the CodeAssist (or related tools),
556 // the compiler may be called *after* a call
557 // to this resolve(...) method. And such a call
558 // needs to have a compiler with a non-empty
564 * Internal API used to resolve a given compilation unit. Can run a subset of
565 * the compilation process
567 public CompilationUnitDeclaration resolve(ICompilationUnit sourceUnit,
568 boolean verifyMethods, boolean analyzeCode) {
569 // boolean generateCode) {
570 CompilationUnitDeclaration unit = null;
572 // build and record parsed units
573 parseThreshold = 0; // will request a full parse
574 beginToCompile(new ICompilationUnit[]{sourceUnit});
575 // process all units (some more could be injected in the loop by the
576 // lookup environment)
577 unit = unitsToProcess[0];
578 getMethodBodies(unit, 0);
579 if (unit.scope != null) {
580 // // fault in fields & methods
581 // unit.scope.faultInTypes();
582 // if (unit.scope != null && verifyMethods) {
583 // // http://dev.eclipse.org/bugs/show_bug.cgi?id=23117
584 // // verify inherited methods
585 // unit.scope.verifyMethods(lookupEnvironment.methodVerifier());
590 // if (analyzeCode) unit.analyseCode();
592 // if (generateCode) unit.generateCode();
594 unitsToProcess[0] = null; // release reference to processed unit
596 requestor.acceptResult(unit.compilationResult.tagAsAccepted());
598 } catch (AbortCompilation e) {
599 this.handleInternalException(e, unit);
600 return unit == null ? unitsToProcess[0] : unit;
602 this.handleInternalException(e, unit, null);
604 } catch (RuntimeException e) {
605 this.handleInternalException(e, unit, null);
608 // No reset is performed there anymore since,
609 // within the CodeAssist (or related tools),
610 // the compiler may be called *after* a call
611 // to this resolve(...) method. And such a call
612 // needs to have a compiler with a non-empty