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.ITypeRequestor;
21 import net.sourceforge.phpdt.internal.compiler.lookup.LookupEnvironment;
22 import net.sourceforge.phpdt.internal.compiler.lookup.PackageBinding;
23 import net.sourceforge.phpdt.internal.compiler.parser.UnitParser;
24 import net.sourceforge.phpdt.internal.compiler.problem.AbortCompilation;
25 import net.sourceforge.phpdt.internal.compiler.problem.AbortCompilationUnit;
26 import net.sourceforge.phpdt.internal.compiler.problem.ProblemReporter;
27 import net.sourceforge.phpdt.internal.compiler.problem.ProblemSeverities;
28 import net.sourceforge.phpdt.internal.compiler.util.Util;
29 import net.sourceforge.phpeclipse.internal.compiler.ast.CompilationUnitDeclaration;
30 import net.sourceforge.phpeclipse.internal.compiler.ast.TypeDeclaration;
31 public class Compiler implements ITypeRequestor, ProblemSeverities {
32 public UnitParser parser;
33 public ICompilerRequestor requestor;
34 // public CompilerOptions options;
35 public ProblemReporter problemReporter;
36 // management of unit to be processed
37 //public CompilationUnitResult currentCompilationUnitResult;
38 public CompilationUnitDeclaration[] unitsToProcess;
39 public int totalUnits; // (totalUnits-1) gives the last unit in unitToProcess
41 public LookupEnvironment lookupEnvironment;
42 // ONCE STABILIZED, THESE SHOULD RETURN TO A FINAL FIELD
43 public static boolean DEBUG = false;
44 public int parseThreshold = -1;
45 // number of initial units parsed at once (-1: none)
47 * Static requestor reserved to listening compilation results in debug mode,
48 * so as for example to monitor compiler activity independantly from a
49 * particular builder implementation. It is reset at the end of compilation,
50 * and should not persist any information after having been reset.
52 // public static IDebugRequestor DebugRequestor = null;
54 * Answer a new compiler using the given name environment and compiler
55 * options. The environment and options will be in effect for the lifetime of
56 * the compiler. When the compiler is run, compilation results are sent to
57 * the given requestor.
60 * org.eclipse.jdt.internal.compiler.api.env.INameEnvironment
61 * Environment used by the compiler in order to resolve type and
62 * package names. The name environment implements the actual
63 * connection of the compiler to the outside world (e.g. in batch
64 * mode the name environment is performing pure file accesses,
65 * reuse previous build state or connection to repositories).
66 * Note: the name environment is responsible for implementing the
67 * actual classpath rules.
70 * org.eclipse.jdt.internal.compiler.api.problem.IErrorHandlingPolicy
71 * Configurable part for problem handling, allowing the compiler
72 * client to specify the rules for handling problems (stop on
73 * first error or accumulate them all) and at the same time
74 * perform some actions such as opening a dialog in UI when
75 * compiling interactively.
76 * @see org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies
79 * org.eclipse.jdt.internal.compiler.api.ICompilerRequestor
80 * Component which will receive and persist all compilation
81 * results and is intended to consume them as they are produced.
82 * Typically, in a batch compiler, it is responsible for writing
83 * out the actual .class files to the file system.
84 * @see org.eclipse.jdt.internal.compiler.CompilationResult
86 * @param problemFactory
87 * org.eclipse.jdt.internal.compiler.api.problem.IProblemFactory
88 * Factory used inside the compiler to create problem descriptors.
89 * It allows the compiler client to supply its own representation
90 * of compilation problems in order to avoid object conversions.
91 * Note that the factory is not supposed to accumulate the created
92 * problems, the compiler will gather them all and hand them back
93 * as part of the compilation unit result.
95 public Compiler(INameEnvironment environment, IErrorHandlingPolicy policy,
97 final ICompilerRequestor requestor, IProblemFactory problemFactory) {
98 // create a problem handler given a handling policy
99 // this.options = new CompilerOptions(settings);
100 // wrap requestor in DebugRequestor if one is specified
101 // if(DebugRequestor == null) {
102 this.requestor = requestor;
104 // this.requestor = new ICompilerRequestor(){
105 // public void acceptResult(CompilationResult result){
106 // if (DebugRequestor.isActive()){
107 // DebugRequestor.acceptDebugResult(result);
109 // requestor.acceptResult(result);
113 this.problemReporter = new ProblemReporter(policy, problemFactory);//this.options,
115 this.lookupEnvironment = new LookupEnvironment(this, problemReporter,
116 environment); //options, problemReporter, environment);
117 this.parser = new UnitParser(problemReporter);
118 // this.options.parseLiteralExpressionsAsConstants,
119 // options.sourceLevel >= CompilerOptions.JDK1_4);
122 * Answer a new compiler using the given name environment and compiler
123 * options. The environment and options will be in effect for the lifetime of
124 * the compiler. When the compiler is run, compilation results are sent to
125 * the given requestor.
128 * org.eclipse.jdt.internal.compiler.api.env.INameEnvironment
129 * Environment used by the compiler in order to resolve type and
130 * package names. The name environment implements the actual
131 * connection of the compiler to the outside world (e.g. in batch
132 * mode the name environment is performing pure file accesses,
133 * reuse previous build state or connection to repositories).
134 * Note: the name environment is responsible for implementing the
135 * actual classpath rules.
138 * org.eclipse.jdt.internal.compiler.api.problem.IErrorHandlingPolicy
139 * Configurable part for problem handling, allowing the compiler
140 * client to specify the rules for handling problems (stop on
141 * first error or accumulate them all) and at the same time
142 * perform some actions such as opening a dialog in UI when
143 * compiling interactively.
144 * @see org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies
147 * org.eclipse.jdt.internal.compiler.api.ICompilerRequestor
148 * Component which will receive and persist all compilation
149 * results and is intended to consume them as they are produced.
150 * Typically, in a batch compiler, it is responsible for writing
151 * out the actual .class files to the file system.
152 * @see org.eclipse.jdt.internal.compiler.CompilationResult
154 * @param problemFactory
155 * org.eclipse.jdt.internal.compiler.api.problem.IProblemFactory
156 * Factory used inside the compiler to create problem descriptors.
157 * It allows the compiler client to supply its own representation
158 * of compilation problems in order to avoid object conversions.
159 * Note that the factory is not supposed to accumulate the created
160 * problems, the compiler will gather them all and hand them back
161 * as part of the compilation unit result.
162 * @param parseLiteralExpressionsAsConstants
163 * <code>boolean</code> This parameter is used to optimize the
164 * literals or leave them as they are in the source. If you put
165 * true, "Hello" + " world" will be converted to "Hello world".
167 public Compiler(INameEnvironment environment, IErrorHandlingPolicy policy,
169 final ICompilerRequestor requestor, IProblemFactory problemFactory,
170 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, problemFactory);//,
189 this.lookupEnvironment = new LookupEnvironment(this, problemReporter,
190 environment);//options, problemReporter, environment);
191 this.parser = new UnitParser(problemReporter);
192 // parseLiteralExpressionsAsConstants,
193 // this.options.sourceLevel >= CompilerOptions.JDK1_4);
196 * Add an additional binary type
198 public void accept(IBinaryType binaryType, PackageBinding packageBinding) {
199 lookupEnvironment.createBinaryTypeFrom(binaryType, packageBinding);
202 * Add an additional compilation unit into the loop -> build compilation unit
203 * declarations, their bindings and record their results.
205 public void accept(ICompilationUnit sourceUnit) {
206 // Switch the current policy and compilation result for this unit to the
208 CompilationResult unitResult = new CompilationResult(sourceUnit,
209 totalUnits, totalUnits, 10); //this.options.maxProblemsPerUnit);
211 // diet parsing for large collection of unit
212 CompilationUnitDeclaration parsedUnit;
213 if (totalUnits < parseThreshold) {
214 parsedUnit = parser.parse(sourceUnit, unitResult, false);
216 parsedUnit = parser.dietParse(sourceUnit, unitResult);
218 // if (options.verbose) {
219 // String count = String.valueOf(totalUnits + 1);
220 // System.out.println(
222 // "compilation.request" , //$NON-NLS-1$
226 // new String(sourceUnit.getFileName())}));
228 // initial type binding creation
229 lookupEnvironment.buildTypeBindings(parsedUnit);
230 this.addCompilationUnit(sourceUnit, parsedUnit);
231 // binding resolution
232 lookupEnvironment.completeTypeBindings(parsedUnit);
233 } catch (AbortCompilationUnit e) {
234 // at this point, currentCompilationUnitResult may not be sourceUnit, but
236 // one requested further along to resolve sourceUnit.
237 if (unitResult.compilationUnit == sourceUnit) { // only report once
238 requestor.acceptResult(unitResult.tagAsAccepted());
240 throw e; // want to abort enclosing request to compile
245 * Add additional source types
247 public void accept(ISourceType[] sourceTypes, PackageBinding packageBinding) {
248 problemReporter.abortDueToInternalError(Util.bind(
249 "abort.againstSourceModel ", //$NON-NLS-1$
250 String.valueOf(sourceTypes[0].getName()), String.valueOf(sourceTypes[0]
253 protected void addCompilationUnit(ICompilationUnit sourceUnit,
254 CompilationUnitDeclaration parsedUnit) {
255 // append the unit to the list of ones to process later on
256 int size = unitsToProcess.length;
257 if (totalUnits == size)
258 // when growing reposition units starting at position 0
259 System.arraycopy(unitsToProcess, 0,
260 (unitsToProcess = new CompilationUnitDeclaration[size * 2]), 0,
262 unitsToProcess[totalUnits++] = parsedUnit;
265 * Add the initial set of compilation units into the loop -> build
266 * compilation unit declarations, their bindings and record their results.
268 protected void beginToCompile(ICompilationUnit[] sourceUnits) {
269 int maxUnits = sourceUnits.length;
271 unitsToProcess = new CompilationUnitDeclaration[maxUnits];
272 // Switch the current policy and compilation result for this unit to the
274 for (int i = 0; i < maxUnits; i++) {
275 CompilationUnitDeclaration parsedUnit;
276 CompilationResult unitResult = new CompilationResult(sourceUnits[i], i,
277 maxUnits, 10);//, this.options.maxProblemsPerUnit);
279 // diet parsing for large collection of units
280 if (totalUnits < parseThreshold) {
281 parsedUnit = parser.parse(sourceUnits[i], unitResult, false);
283 parsedUnit = parser.dietParse(sourceUnits[i], unitResult);
285 // if (options.verbose) {
286 // System.out.println(
288 // "compilation.request" , //$NON-NLS-1$
290 // String.valueOf(i + 1),
291 // String.valueOf(maxUnits),
292 // new String(sourceUnits[i].getFileName())}));
294 // initial type binding creation
295 lookupEnvironment.buildTypeBindings(parsedUnit);
296 this.addCompilationUnit(sourceUnits[i], parsedUnit);
297 //} catch (AbortCompilationUnit e) {
298 //requestor.acceptResult(unitResult.tagAsAccepted());
300 sourceUnits[i] = null; // no longer hold onto the unit
303 // binding resolution
304 lookupEnvironment.completeTypeBindings();
307 * General API -> compile each of supplied files -> recompile any required
308 * types for which we have an incomplete principle structure
310 public void compile(ICompilationUnit[] sourceUnits) {
311 CompilationUnitDeclaration unit = null;
314 // build and record parsed units
315 beginToCompile(sourceUnits);
316 // process all units (some more could be injected in the loop by the
317 // lookup environment)
318 for (; i < totalUnits; i++) {
319 unit = unitsToProcess[i];
321 // if (options.verbose)
322 // System.out.println(
324 // "compilation.process" , //$NON-NLS-1$
326 // String.valueOf(i + 1),
327 // String.valueOf(totalUnits),
328 // new String(unitsToProcess[i].getFileName())}));
331 // cleanup compilation unit result
333 // if (options.verbose)
334 // System.out.println(Util.bind("compilation.done", //$NON-NLS-1$
336 // String.valueOf(i + 1),
337 // String.valueOf(totalUnits),
338 // new String(unitsToProcess[i].getFileName())}));
340 unitsToProcess[i] = null; // release reference to processed unit
342 requestor.acceptResult(unit.compilationResult.tagAsAccepted());
344 } catch (AbortCompilation e) {
345 this.handleInternalException(e, unit);
347 this.handleInternalException(e, unit, null);
349 } catch (RuntimeException e) {
350 this.handleInternalException(e, unit, null);
355 // if (options.verbose) {
356 // if (totalUnits > 1) {
357 // System.out.println(
358 // Util.bind("compilation.units" , String.valueOf(totalUnits)));
361 // System.out.println(
362 // Util.bind("compilation.unit" , String.valueOf(totalUnits)));
367 protected void getMethodBodies(CompilationUnitDeclaration unit, int place) {
368 //fill the methods bodies in order for the code to be generated
369 if (unit.ignoreMethodBodies) {
370 unit.ignoreFurtherInvestigation = true;
372 // if initial diet parse did not work, no need to dig into method bodies.
374 if (place < parseThreshold)
375 return; //work already done ...
376 //real parse of the method....
377 parser.scanner.setSource(unit.compilationResult.compilationUnit
379 if (unit.types != null) {
380 for (int i = unit.types.size(); --i >= 0;)
381 if (unit.types.get(i) instanceof TypeDeclaration) {
382 ((TypeDeclaration) unit.types.get(i)).parseMethod(parser, unit);
387 * Compiler crash recovery in case of unexpected runtime exceptions
389 protected void handleInternalException(Throwable internalException,
390 CompilationUnitDeclaration unit, CompilationResult result) {
391 /* dump a stack trace to the console */
392 internalException.printStackTrace();
393 /* find a compilation result */
394 if ((unit != null)) // basing result upon the current unit if available
395 result = unit.compilationResult; // current unit being processed ?
396 if ((result == null) && (unitsToProcess != null) && (totalUnits > 0))
397 result = unitsToProcess[totalUnits - 1].compilationResult;
398 // last unit in beginToCompile ?
399 if (result != null) {
400 /* create and record a compilation problem */
401 StringWriter stringWriter = new StringWriter();
402 PrintWriter writer = new PrintWriter(stringWriter);
403 internalException.printStackTrace(writer);
404 StringBuffer buffer = stringWriter.getBuffer();
405 String[] pbArguments = new String[]{Util
406 .bind("compilation.internalError")
409 + buffer.toString()};
410 result.record(problemReporter.createProblem(result.getFileName(),
411 IProblem.Unclassified, pbArguments, pbArguments, Error, // severity
415 unit, result), unit);
416 /* hand back the compilation result */
417 if (!result.hasBeenAccepted) {
418 requestor.acceptResult(result.tagAsAccepted());
423 * Compiler recovery in case of internal AbortCompilation event
425 protected void handleInternalException(AbortCompilation abortException,
426 CompilationUnitDeclaration unit) {
428 * special treatment for SilentAbort: silently cancelling the compilation
431 if (abortException.isSilent) {
432 if (abortException.silentException == null) {
435 throw abortException.silentException;
438 /* uncomment following line to see where the abort came from */
439 // abortException.printStackTrace();
440 // Exception may tell which compilation result it is related, and which
442 CompilationResult result = abortException.compilationResult;
443 if ((result == null) && (unit != null))
444 result = unit.compilationResult; // current unit being processed ?
445 if ((result == null) && (unitsToProcess != null) && (totalUnits > 0))
446 result = unitsToProcess[totalUnits - 1].compilationResult;
447 // last unit in beginToCompile ?
448 if (result != null && !result.hasBeenAccepted) {
449 /* distant problem which could not be reported back there */
450 if (abortException.problemId != 0) {
451 result.record(problemReporter.createProblem(result.getFileName(),
452 abortException.problemId, abortException.problemArguments,
453 abortException.messageArguments, Error, // severity
457 unit, result), unit);
459 /* distant internal exception which could not be reported back there */
460 if (abortException.exception != null) {
461 this.handleInternalException(abortException.exception, null, result);
465 /* hand back the compilation result */
466 if (!result.hasBeenAccepted) {
467 requestor.acceptResult(result.tagAsAccepted());
471 * if (abortException.problemId != 0){ IProblem problem =
472 * problemReporter.createProblem( "???".toCharArray(),
473 * abortException.problemId, abortException.problemArguments, Error, //
474 * severity 0, // source start 0, // source end 0); // line number
475 * System.out.println(problem.getMessage()); }
477 abortException.printStackTrace();
481 * Process a compilation unit already parsed and build.
483 public void process(CompilationUnitDeclaration unit, int i) {
484 getMethodBodies(unit, i);
485 // fault in fields & methods
486 if (unit.scope != null)
487 unit.scope.faultInTypes();
488 // verify inherited methods
489 if (unit.scope != null)
490 unit.scope.verifyMethods(lookupEnvironment.methodVerifier());
496 // unit.generateCode();
498 // if (options.produceReferenceInfo && unit.scope != null)
499 // unit.scope.storeDependencyInfo();
500 // refresh the total number of units known at this stage
501 unit.compilationResult.totalUnitsKnown = totalUnits;
503 public void reset() {
504 lookupEnvironment.reset();
505 parser.scanner.source = null;
506 unitsToProcess = null;
507 // if (DebugRequestor != null) DebugRequestor.reset();
510 * Internal API used to resolve a given compilation unit. Can run a subset of
511 * the compilation process
513 public CompilationUnitDeclaration resolve(ICompilationUnit sourceUnit,
514 boolean verifyMethods, boolean analyzeCode) {
515 // boolean generateCode) {
516 CompilationUnitDeclaration unit = null;
518 // build and record parsed units
519 parseThreshold = 0; // will request a full parse
520 beginToCompile(new ICompilationUnit[]{sourceUnit});
521 // process all units (some more could be injected in the loop by the
522 // lookup environment)
523 unit = unitsToProcess[0];
524 getMethodBodies(unit, 0);
525 if (unit.scope != null) {
526 // // fault in fields & methods
527 // unit.scope.faultInTypes();
528 // if (unit.scope != null && verifyMethods) {
529 // // http://dev.eclipse.org/bugs/show_bug.cgi?id=23117
530 // // verify inherited methods
531 // unit.scope.verifyMethods(lookupEnvironment.methodVerifier());
536 // if (analyzeCode) unit.analyseCode();
538 // if (generateCode) unit.generateCode();
540 unitsToProcess[0] = null; // release reference to processed unit
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