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;
33 public class Compiler implements ITypeRequestor, ProblemSeverities {
34 public UnitParser parser;
35 public ICompilerRequestor requestor;
36 public CompilerOptions options;
37 public ProblemReporter problemReporter;
38 // management of unit to be processed
39 //public CompilationUnitResult currentCompilationUnitResult;
40 public CompilationUnitDeclaration[] unitsToProcess;
41 public int totalUnits; // (totalUnits-1) gives the last unit in unitToProcess
43 public LookupEnvironment lookupEnvironment;
44 // ONCE STABILIZED, THESE SHOULD RETURN TO A FINAL FIELD
45 public static boolean DEBUG = false;
46 public int parseThreshold = -1;
47 // number of initial units parsed at once (-1: none)
49 * Static requestor reserved to listening compilation results in debug mode,
50 * so as for example to monitor compiler activity independantly from a
51 * particular builder implementation. It is reset at the end of compilation,
52 * and should not persist any information after having been reset.
54 // public static IDebugRequestor DebugRequestor = null;
56 * Answer a new compiler using the given name environment and compiler
57 * options. The environment and options will be in effect for the lifetime of
58 * the compiler. When the compiler is run, compilation results are sent to
59 * the given requestor.
62 * org.eclipse.jdt.internal.compiler.api.env.INameEnvironment
63 * Environment used by the compiler in order to resolve type and
64 * package names. The name environment implements the actual
65 * connection of the compiler to the outside world (e.g. in batch
66 * mode the name environment is performing pure file accesses,
67 * reuse previous build state or connection to repositories).
68 * Note: the name environment is responsible for implementing the
69 * actual classpath rules.
72 * org.eclipse.jdt.internal.compiler.api.problem.IErrorHandlingPolicy
73 * Configurable part for problem handling, allowing the compiler
74 * client to specify the rules for handling problems (stop on
75 * first error or accumulate them all) and at the same time
76 * perform some actions such as opening a dialog in UI when
77 * compiling interactively.
78 * @see org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies
81 * org.eclipse.jdt.internal.compiler.api.ICompilerRequestor
82 * Component which will receive and persist all compilation
83 * results and is intended to consume them as they are produced.
84 * Typically, in a batch compiler, it is responsible for writing
85 * out the actual .class files to the file system.
86 * @see org.eclipse.jdt.internal.compiler.CompilationResult
88 * @param problemFactory
89 * org.eclipse.jdt.internal.compiler.api.problem.IProblemFactory
90 * Factory used inside the compiler to create problem descriptors.
91 * It allows the compiler client to supply its own representation
92 * of compilation problems in order to avoid object conversions.
93 * Note that the factory is not supposed to accumulate the created
94 * problems, the compiler will gather them all and hand them back
95 * as part of the compilation unit result.
97 public Compiler(INameEnvironment environment, IErrorHandlingPolicy policy,
98 Map settings, final ICompilerRequestor requestor,
99 IProblemFactory problemFactory) {
100 // create a problem handler given a handling policy
101 this.options = new CompilerOptions(settings);
102 // wrap requestor in DebugRequestor if one is specified
103 // if(DebugRequestor == null) {
104 this.requestor = requestor;
106 // this.requestor = new ICompilerRequestor(){
107 // public void acceptResult(CompilationResult result){
108 // if (DebugRequestor.isActive()){
109 // DebugRequestor.acceptDebugResult(result);
111 // requestor.acceptResult(result);
115 this.problemReporter = new ProblemReporter(policy, this.options,
117 this.lookupEnvironment = new LookupEnvironment(this, problemReporter,
118 environment); //options, problemReporter, environment);
119 this.parser = new UnitParser(problemReporter);
120 // this.options.parseLiteralExpressionsAsConstants,
121 // options.sourceLevel >= CompilerOptions.JDK1_4);
124 * Answer a new compiler using the given name environment and compiler
125 * options. The environment and options will be in effect for the lifetime of
126 * the compiler. When the compiler is run, compilation results are sent to
127 * the given requestor.
130 * org.eclipse.jdt.internal.compiler.api.env.INameEnvironment
131 * Environment used by the compiler in order to resolve type and
132 * package names. The name environment implements the actual
133 * connection of the compiler to the outside world (e.g. in batch
134 * mode the name environment is performing pure file accesses,
135 * reuse previous build state or connection to repositories).
136 * Note: the name environment is responsible for implementing the
137 * actual classpath rules.
140 * org.eclipse.jdt.internal.compiler.api.problem.IErrorHandlingPolicy
141 * Configurable part for problem handling, allowing the compiler
142 * client to specify the rules for handling problems (stop on
143 * first error or accumulate them all) and at the same time
144 * perform some actions such as opening a dialog in UI when
145 * compiling interactively.
146 * @see org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies
149 * org.eclipse.jdt.internal.compiler.api.ICompilerRequestor
150 * Component which will receive and persist all compilation
151 * results and is intended to consume them as they are produced.
152 * Typically, in a batch compiler, it is responsible for writing
153 * out the actual .class files to the file system.
154 * @see org.eclipse.jdt.internal.compiler.CompilationResult
156 * @param problemFactory
157 * org.eclipse.jdt.internal.compiler.api.problem.IProblemFactory
158 * Factory used inside the compiler to create problem descriptors.
159 * It allows the compiler client to supply its own representation
160 * of compilation problems in order to avoid object conversions.
161 * Note that the factory is not supposed to accumulate the created
162 * problems, the compiler will gather them all and hand them back
163 * as part of the compilation unit result.
164 * @param parseLiteralExpressionsAsConstants
165 * <code>boolean</code> This parameter is used to optimize the
166 * literals or leave them as they are in the source. If you put
167 * true, "Hello" + " world" will be converted to "Hello world".
169 public Compiler(INameEnvironment environment, IErrorHandlingPolicy policy,
170 Map settings, final ICompilerRequestor requestor,
171 IProblemFactory problemFactory, boolean parseLiteralExpressionsAsConstants) {
172 // create a problem handler given a handling policy
173 this.options = new CompilerOptions(settings);
174 // wrap requestor in DebugRequestor if one is specified
175 // if(DebugRequestor == null) {
176 this.requestor = requestor;
178 // this.requestor = new ICompilerRequestor(){
179 // public void acceptResult(CompilationResult result){
180 // if (DebugRequestor.isActive()){
181 // DebugRequestor.acceptDebugResult(result);
183 // requestor.acceptResult(result);
187 this.problemReporter = new ProblemReporter(policy, this.options,
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, 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(Util.bind("compilation.request", //$NON-NLS-1$
221 new String[]{count, count, new String(sourceUnit.getFileName())}));
223 // initial type binding creation
224 lookupEnvironment.buildTypeBindings(parsedUnit);
225 this.addCompilationUnit(sourceUnit, parsedUnit);
226 // binding resolution
227 lookupEnvironment.completeTypeBindings(parsedUnit);
228 } catch (AbortCompilationUnit e) {
229 // at this point, currentCompilationUnitResult may not be sourceUnit, but
231 // one requested further along to resolve sourceUnit.
232 if (unitResult.compilationUnit == sourceUnit) { // only report once
233 requestor.acceptResult(unitResult.tagAsAccepted());
235 throw e; // want to abort enclosing request to compile
240 * Add additional source types
242 public void accept(ISourceType[] sourceTypes, PackageBinding packageBinding) {
243 problemReporter.abortDueToInternalError(Util.bind(
244 "abort.againstSourceModel ", //$NON-NLS-1$
245 String.valueOf(sourceTypes[0].getName()), String.valueOf(sourceTypes[0]
248 protected void addCompilationUnit(ICompilationUnit sourceUnit,
249 CompilationUnitDeclaration parsedUnit) {
250 // append the unit to the list of ones to process later on
251 int size = unitsToProcess.length;
252 if (totalUnits == size)
253 // when growing reposition units starting at position 0
254 System.arraycopy(unitsToProcess, 0,
255 (unitsToProcess = new CompilationUnitDeclaration[size * 2]), 0,
257 unitsToProcess[totalUnits++] = parsedUnit;
260 * Add the initial set of compilation units into the loop -> build
261 * compilation unit declarations, their bindings and record their results.
263 protected void beginToCompile(ICompilationUnit[] sourceUnits) {
264 int maxUnits = sourceUnits.length;
266 unitsToProcess = new CompilationUnitDeclaration[maxUnits];
267 // Switch the current policy and compilation result for this unit to the
269 for (int i = 0; i < maxUnits; i++) {
270 CompilationUnitDeclaration parsedUnit;
271 CompilationResult unitResult = new CompilationResult(sourceUnits[i], i,
272 maxUnits, this.options.maxProblemsPerUnit);
274 // diet parsing for large collection of units
275 if (totalUnits < parseThreshold) {
276 parsedUnit = parser.parse(sourceUnits[i], unitResult, false);
278 parsedUnit = parser.dietParse(sourceUnits[i], unitResult);
280 if (options.verbose) {
281 System.out.println(Util.bind("compilation.request", //$NON-NLS-1$
282 new String[]{String.valueOf(i + 1), String.valueOf(maxUnits),
283 new String(sourceUnits[i].getFileName())}));
285 // initial type binding creation
286 // lookupEnvironment.buildTypeBindings(parsedUnit);
287 this.addCompilationUnit(sourceUnits[i], parsedUnit);
288 //} catch (AbortCompilationUnit e) {
289 //requestor.acceptResult(unitResult.tagAsAccepted());
291 sourceUnits[i] = null; // no longer hold onto the unit
294 // binding resolution
295 lookupEnvironment.completeTypeBindings();
298 * General API -> compile each of supplied files -> recompile any required
299 * types for which we have an incomplete principle structure
301 public void compile(ICompilationUnit[] sourceUnits) {
302 CompilationUnitDeclaration unit = null;
305 // build and record parsed units
306 beginToCompile(sourceUnits);
307 // process all units (some more could be injected in the loop by the
308 // lookup environment)
309 for (; i < totalUnits; i++) {
310 unit = unitsToProcess[i];
313 System.out.println(Util.bind("compilation.process", //$NON-NLS-1$
314 new String[]{String.valueOf(i + 1), String.valueOf(totalUnits),
315 new String(unitsToProcess[i].getFileName())}));
318 // cleanup compilation unit result
321 System.out.println(Util.bind("compilation.done", //$NON-NLS-1$
322 new String[]{String.valueOf(i + 1), String.valueOf(totalUnits),
323 new String(unitsToProcess[i].getFileName())}));
325 unitsToProcess[i] = null; // release reference to processed unit
327 requestor.acceptResult(unit.compilationResult.tagAsAccepted());
329 } catch (AbortCompilation e) {
330 this.handleInternalException(e, unit);
332 this.handleInternalException(e, unit, null);
334 } catch (RuntimeException e) {
335 this.handleInternalException(e, unit, null);
340 // if (options.verbose) {
341 // if (totalUnits > 1) {
342 // System.out.println(
343 // Util.bind("compilation.units" , String.valueOf(totalUnits)));
346 // System.out.println(
347 // Util.bind("compilation.unit" , String.valueOf(totalUnits)));
352 protected void getMethodBodies(CompilationUnitDeclaration unit, int place) {
353 //fill the methods bodies in order for the code to be generated
354 if (unit.ignoreMethodBodies) {
355 unit.ignoreFurtherInvestigation = true;
357 // if initial diet parse did not work, no need to dig into method bodies.
359 if (place < parseThreshold)
360 return; //work already done ...
361 //real parse of the method....
362 parser.scanner.setSource(unit.compilationResult.compilationUnit
364 if (unit.types != null) {
365 for (int i = unit.types.size(); --i >= 0;)
366 if (unit.types.get(i) instanceof TypeDeclaration) {
367 ((TypeDeclaration) unit.types.get(i)).parseMethod(parser, unit);
372 * Compiler crash recovery in case of unexpected runtime exceptions
374 protected void handleInternalException(Throwable internalException,
375 CompilationUnitDeclaration unit, CompilationResult result) {
376 /* dump a stack trace to the console */
377 internalException.printStackTrace();
378 /* find a compilation result */
379 if ((unit != null)) // basing result upon the current unit if available
380 result = unit.compilationResult; // current unit being processed ?
381 if ((result == null) && (unitsToProcess != null) && (totalUnits > 0))
382 result = unitsToProcess[totalUnits - 1].compilationResult;
383 // last unit in beginToCompile ?
384 if (result != null) {
385 /* create and record a compilation problem */
386 StringWriter stringWriter = new StringWriter();
387 PrintWriter writer = new PrintWriter(stringWriter);
388 internalException.printStackTrace(writer);
389 StringBuffer buffer = stringWriter.getBuffer();
390 String[] pbArguments = new String[]{Util
391 .bind("compilation.internalError")
394 + buffer.toString()};
395 result.record(problemReporter.createProblem(result.getFileName(),
396 IProblem.Unclassified, pbArguments, pbArguments, Error, // severity
400 unit, result), unit);
401 /* hand back the compilation result */
402 if (!result.hasBeenAccepted) {
403 requestor.acceptResult(result.tagAsAccepted());
408 * Compiler recovery in case of internal AbortCompilation event
410 protected void handleInternalException(AbortCompilation abortException,
411 CompilationUnitDeclaration unit) {
413 * special treatment for SilentAbort: silently cancelling the compilation
416 if (abortException.isSilent) {
417 if (abortException.silentException == null) {
420 throw abortException.silentException;
423 /* uncomment following line to see where the abort came from */
424 // abortException.printStackTrace();
425 // Exception may tell which compilation result it is related, and which
427 CompilationResult result = abortException.compilationResult;
428 if ((result == null) && (unit != null))
429 result = unit.compilationResult; // current unit being processed ?
430 if ((result == null) && (unitsToProcess != null) && (totalUnits > 0))
431 result = unitsToProcess[totalUnits - 1].compilationResult;
432 // last unit in beginToCompile ?
433 if (result != null && !result.hasBeenAccepted) {
434 /* distant problem which could not be reported back there */
435 if (abortException.problemId != 0) {
436 result.record(problemReporter.createProblem(result.getFileName(),
437 abortException.problemId, abortException.problemArguments,
438 abortException.messageArguments, Error, // severity
442 unit, result), unit);
444 /* distant internal exception which could not be reported back there */
445 if (abortException.exception != null) {
446 this.handleInternalException(abortException.exception, null, result);
450 /* hand back the compilation result */
451 if (!result.hasBeenAccepted) {
452 requestor.acceptResult(result.tagAsAccepted());
456 * if (abortException.problemId != 0){ IProblem problem =
457 * problemReporter.createProblem( "???".toCharArray(),
458 * abortException.problemId, abortException.problemArguments, Error, //
459 * severity 0, // source start 0, // source end 0); // line number
460 * System.out.println(problem.getMessage()); }
462 abortException.printStackTrace();
466 * Process a compilation unit already parsed and build.
468 public void process(CompilationUnitDeclaration unit, int i) {
469 getMethodBodies(unit, i);
470 // fault in fields & methods
471 if (unit.scope != null)
472 unit.scope.faultInTypes();
473 // verify inherited methods
474 // if (unit.scope != null)
475 // unit.scope.verifyMethods(lookupEnvironment.methodVerifier());
481 // unit.generateCode();
483 // if (options.produceReferenceInfo && unit.scope != null)
484 // unit.scope.storeDependencyInfo();
485 // refresh the total number of units known at this stage
486 unit.compilationResult.totalUnitsKnown = totalUnits;
488 public void reset() {
489 lookupEnvironment.reset();
490 parser.scanner.source = null;
491 unitsToProcess = null;
492 // if (DebugRequestor != null) DebugRequestor.reset();
495 * Internal API used to resolve a given compilation unit. Can run a subset of the compilation process
497 public CompilationUnitDeclaration resolve(
498 CompilationUnitDeclaration unit,
499 ICompilationUnit sourceUnit,
500 boolean verifyMethods,
501 boolean analyzeCode) {
505 // build and record parsed units
506 parseThreshold = 0; // will request a full parse
507 beginToCompile(new ICompilationUnit[] { sourceUnit });
508 // process all units (some more could be injected in the loop by the lookup environment)
509 unit = unitsToProcess[0];
511 // initial type binding creation
512 lookupEnvironment.buildTypeBindings(unit);
514 // binding resolution
515 lookupEnvironment.completeTypeBindings();
517 // TODO : jsurfer check this
518 // this.parser.getMethodBodies(unit);
519 getMethodBodies(unit, 0);
521 if (unit.scope != null) {
522 // fault in fields & methods
523 unit.scope.faultInTypes();
524 if (unit.scope != null && verifyMethods) {
525 // http://dev.eclipse.org/bugs/show_bug.cgi?id=23117
526 // verify inherited methods
527 unit.scope.verifyMethods(lookupEnvironment.methodVerifier());
533 // if (analyzeCode) unit.analyseCode();
536 // if (generateCode) unit.generateCode();
538 if (unitsToProcess != null) unitsToProcess[0] = null; // release reference to processed unit declaration
539 requestor.acceptResult(unit.compilationResult.tagAsAccepted());
541 } catch (AbortCompilation e) {
542 this.handleInternalException(e, unit);
543 return unit == null ? unitsToProcess[0] : unit;
545 this.handleInternalException(e, unit, null);
547 } catch (RuntimeException e) {
548 this.handleInternalException(e, unit, null);
551 // No reset is performed there anymore since,
552 // within the CodeAssist (or related tools),
553 // the compiler may be called *after* a call
554 // to this resolve(...) method. And such a call
555 // needs to have a compiler with a non-empty
561 * Internal API used to resolve a given compilation unit. Can run a subset of
562 * the compilation process
564 public CompilationUnitDeclaration resolve(ICompilationUnit sourceUnit,
565 boolean verifyMethods, boolean analyzeCode) {
566 // boolean generateCode) {
567 CompilationUnitDeclaration unit = null;
569 // build and record parsed units
570 parseThreshold = 0; // will request a full parse
571 beginToCompile(new ICompilationUnit[]{sourceUnit});
572 // process all units (some more could be injected in the loop by the
573 // lookup environment)
574 unit = unitsToProcess[0];
575 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
582 // unit.scope.verifyMethods(lookupEnvironment.methodVerifier());
587 // if (analyzeCode) unit.analyseCode();
589 // if (generateCode) unit.generateCode();
591 unitsToProcess[0] = null; // release reference to processed unit
593 requestor.acceptResult(unit.compilationResult.tagAsAccepted());
595 } catch (AbortCompilation e) {
596 this.handleInternalException(e, unit);
597 return unit == null ? unitsToProcess[0] : unit;
599 this.handleInternalException(e, unit, null);
601 } catch (RuntimeException e) {
602 this.handleInternalException(e, unit, null);
605 // No reset is performed there anymore since,
606 // within the CodeAssist (or related tools),
607 // the compiler may be called *after* a call
608 // to this resolve(...) method. And such a call
609 // needs to have a compiler with a non-empty