/******************************************************************************* * Copyright (c) 2000, 2003 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Common Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/cpl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package net.sourceforge.phpdt.internal.compiler; import java.io.PrintWriter; import java.io.StringWriter; import java.util.Map; import net.sourceforge.phpdt.core.compiler.IProblem; import net.sourceforge.phpdt.internal.compiler.ast.CompilationUnitDeclaration; import net.sourceforge.phpdt.internal.compiler.ast.TypeDeclaration; import net.sourceforge.phpdt.internal.compiler.env.IBinaryType; import net.sourceforge.phpdt.internal.compiler.env.ICompilationUnit; import net.sourceforge.phpdt.internal.compiler.env.INameEnvironment; import net.sourceforge.phpdt.internal.compiler.env.ISourceType; import net.sourceforge.phpdt.internal.compiler.impl.CompilerOptions; import net.sourceforge.phpdt.internal.compiler.impl.ITypeRequestor; import net.sourceforge.phpdt.internal.compiler.lookup.LookupEnvironment; import net.sourceforge.phpdt.internal.compiler.lookup.PackageBinding; import net.sourceforge.phpdt.internal.compiler.parser.UnitParser; import net.sourceforge.phpdt.internal.compiler.problem.AbortCompilation; import net.sourceforge.phpdt.internal.compiler.problem.AbortCompilationUnit; import net.sourceforge.phpdt.internal.compiler.problem.ProblemReporter; import net.sourceforge.phpdt.internal.compiler.problem.ProblemSeverities; import net.sourceforge.phpdt.internal.compiler.util.Util; public class Compiler implements ITypeRequestor, ProblemSeverities { public UnitParser parser; public ICompilerRequestor requestor; public CompilerOptions options; public ProblemReporter problemReporter; // management of unit to be processed // public CompilationUnitResult currentCompilationUnitResult; public CompilationUnitDeclaration[] unitsToProcess; public int totalUnits; // (totalUnits-1) gives the last unit in // unitToProcess // name lookup public LookupEnvironment lookupEnvironment; // ONCE STABILIZED, THESE SHOULD RETURN TO A FINAL FIELD public static boolean DEBUG = false; public int parseThreshold = -1; // number of initial units parsed at once (-1: none) /* * Static requestor reserved to listening compilation results in debug mode, * so as for example to monitor compiler activity independantly from a * particular builder implementation. It is reset at the end of compilation, * and should not persist any information after having been reset. */ // public static IDebugRequestor DebugRequestor = null; /** * Answer a new compiler using the given name environment and compiler * options. The environment and options will be in effect for the lifetime * of the compiler. When the compiler is run, compilation results are sent * to the given requestor. * * @param environment * org.eclipse.jdt.internal.compiler.api.env.INameEnvironment * Environment used by the compiler in order to resolve type and * package names. The name environment implements the actual * connection of the compiler to the outside world (e.g. in batch * mode the name environment is performing pure file accesses, * reuse previous build state or connection to repositories). * Note: the name environment is responsible for implementing the * actual classpath rules. * * @param policy * org.eclipse.jdt.internal.compiler.api.problem.IErrorHandlingPolicy * Configurable part for problem handling, allowing the compiler * client to specify the rules for handling problems (stop on * first error or accumulate them all) and at the same time * perform some actions such as opening a dialog in UI when * compiling interactively. * @see org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies * * @param requestor * org.eclipse.jdt.internal.compiler.api.ICompilerRequestor * Component which will receive and persist all compilation * results and is intended to consume them as they are produced. * Typically, in a batch compiler, it is responsible for writing * out the actual .class files to the file system. * @see org.eclipse.jdt.internal.compiler.CompilationResult * * @param problemFactory * org.eclipse.jdt.internal.compiler.api.problem.IProblemFactory * Factory used inside the compiler to create problem * descriptors. It allows the compiler client to supply its own * representation of compilation problems in order to avoid * object conversions. Note that the factory is not supposed to * accumulate the created problems, the compiler will gather them * all and hand them back as part of the compilation unit result. */ public Compiler(INameEnvironment environment, IErrorHandlingPolicy policy, Map settings, final ICompilerRequestor requestor, IProblemFactory problemFactory) { // create a problem handler given a handling policy this.options = new CompilerOptions(settings); // wrap requestor in DebugRequestor if one is specified // if(DebugRequestor == null) { this.requestor = requestor; // } else { // this.requestor = new ICompilerRequestor(){ // public void acceptResult(CompilationResult result){ // if (DebugRequestor.isActive()){ // DebugRequestor.acceptDebugResult(result); // } // requestor.acceptResult(result); // } // }; // } this.problemReporter = new ProblemReporter(policy, this.options, problemFactory); this.lookupEnvironment = new LookupEnvironment(this, problemReporter, environment); // options, problemReporter, environment); this.parser = new UnitParser(problemReporter); // this.options.parseLiteralExpressionsAsConstants, // options.sourceLevel >= CompilerOptions.JDK1_4); } /** * Answer a new compiler using the given name environment and compiler * options. The environment and options will be in effect for the lifetime * of the compiler. When the compiler is run, compilation results are sent * to the given requestor. * * @param environment * org.eclipse.jdt.internal.compiler.api.env.INameEnvironment * Environment used by the compiler in order to resolve type and * package names. The name environment implements the actual * connection of the compiler to the outside world (e.g. in batch * mode the name environment is performing pure file accesses, * reuse previous build state or connection to repositories). * Note: the name environment is responsible for implementing the * actual classpath rules. * * @param policy * org.eclipse.jdt.internal.compiler.api.problem.IErrorHandlingPolicy * Configurable part for problem handling, allowing the compiler * client to specify the rules for handling problems (stop on * first error or accumulate them all) and at the same time * perform some actions such as opening a dialog in UI when * compiling interactively. * @see org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies * * @param requestor * org.eclipse.jdt.internal.compiler.api.ICompilerRequestor * Component which will receive and persist all compilation * results and is intended to consume them as they are produced. * Typically, in a batch compiler, it is responsible for writing * out the actual .class files to the file system. * @see org.eclipse.jdt.internal.compiler.CompilationResult * * @param problemFactory * org.eclipse.jdt.internal.compiler.api.problem.IProblemFactory * Factory used inside the compiler to create problem * descriptors. It allows the compiler client to supply its own * representation of compilation problems in order to avoid * object conversions. Note that the factory is not supposed to * accumulate the created problems, the compiler will gather them * all and hand them back as part of the compilation unit result. * @param parseLiteralExpressionsAsConstants * boolean This parameter is used to optimize the * literals or leave them as they are in the source. If you put * true, "Hello" . " world" will be converted to "Hello world". */ public Compiler(INameEnvironment environment, IErrorHandlingPolicy policy, Map settings, final ICompilerRequestor requestor, IProblemFactory problemFactory, boolean parseLiteralExpressionsAsConstants) { // create a problem handler given a handling policy this.options = new CompilerOptions(settings); // wrap requestor in DebugRequestor if one is specified // if(DebugRequestor == null) { this.requestor = requestor; // } else { // this.requestor = new ICompilerRequestor(){ // public void acceptResult(CompilationResult result){ // if (DebugRequestor.isActive()){ // DebugRequestor.acceptDebugResult(result); // } // requestor.acceptResult(result); // } // }; // } this.problemReporter = new ProblemReporter(policy, this.options, problemFactory); this.lookupEnvironment = new LookupEnvironment(this, problemReporter, environment);// options, problemReporter, environment); this.parser = new UnitParser(problemReporter); // parseLiteralExpressionsAsConstants, // this.options.sourceLevel >= CompilerOptions.JDK1_4); } /** * Add an additional binary type */ public void accept(IBinaryType binaryType, PackageBinding packageBinding) { lookupEnvironment.createBinaryTypeFrom(binaryType, packageBinding); } /** * Add an additional compilation unit into the loop -> build compilation * unit declarations, their bindings and record their results. */ public void accept(ICompilationUnit sourceUnit) { // Switch the current policy and compilation result for this unit to the // requested one. CompilationResult unitResult = new CompilationResult(sourceUnit, totalUnits, totalUnits, this.options.maxProblemsPerUnit); try { // diet parsing for large collection of unit CompilationUnitDeclaration parsedUnit; if (totalUnits < parseThreshold) { parsedUnit = parser.parse(sourceUnit, unitResult, false); } else { parsedUnit = parser.dietParse(sourceUnit, unitResult); } if (options.verbose) { String count = String.valueOf(totalUnits + 1); System.out.println(Util.bind("compilation.request", //$NON-NLS-1$ new String[] { count, count, new String(sourceUnit.getFileName()) })); } // initial type binding creation lookupEnvironment.buildTypeBindings(parsedUnit); this.addCompilationUnit(sourceUnit, parsedUnit); // binding resolution lookupEnvironment.completeTypeBindings(parsedUnit); } catch (AbortCompilationUnit e) { // at this point, currentCompilationUnitResult may not be // sourceUnit, but // some other // one requested further along to resolve sourceUnit. if (unitResult.compilationUnit == sourceUnit) { // only report once requestor.acceptResult(unitResult.tagAsAccepted()); } else { throw e; // want to abort enclosing request to compile } } } /** * Add additional source types */ public void accept(ISourceType[] sourceTypes, PackageBinding packageBinding) { problemReporter.abortDueToInternalError(Util.bind( "abort.againstSourceModel ", //$NON-NLS-1$ String.valueOf(sourceTypes[0].getName()), String .valueOf(sourceTypes[0].getFileName()))); } protected void addCompilationUnit(ICompilationUnit sourceUnit, CompilationUnitDeclaration parsedUnit) { // append the unit to the list of ones to process later on int size = unitsToProcess.length; if (totalUnits == size) // when growing reposition units starting at position 0 System .arraycopy( unitsToProcess, 0, (unitsToProcess = new CompilationUnitDeclaration[size * 2]), 0, totalUnits); unitsToProcess[totalUnits++] = parsedUnit; } /** * Add the initial set of compilation units into the loop -> build * compilation unit declarations, their bindings and record their results. */ protected void beginToCompile(ICompilationUnit[] sourceUnits) { int maxUnits = sourceUnits.length; totalUnits = 0; unitsToProcess = new CompilationUnitDeclaration[maxUnits]; // Switch the current policy and compilation result for this unit to the // requested one. for (int i = 0; i < maxUnits; i++) { CompilationUnitDeclaration parsedUnit; CompilationResult unitResult = new CompilationResult( sourceUnits[i], i, maxUnits, this.options.maxProblemsPerUnit); try { // diet parsing for large collection of units if (totalUnits < parseThreshold) { parsedUnit = parser .parse(sourceUnits[i], unitResult, false); } else { parsedUnit = parser.dietParse(sourceUnits[i], unitResult); } if (options.verbose) { System.out .println(Util.bind("compilation.request", //$NON-NLS-1$ new String[] { String.valueOf(i + 1), String.valueOf(maxUnits), new String(sourceUnits[i] .getFileName()) })); } // initial type binding creation // lookupEnvironment.buildTypeBindings(parsedUnit); this.addCompilationUnit(sourceUnits[i], parsedUnit); // } catch (AbortCompilationUnit e) { // requestor.acceptResult(unitResult.tagAsAccepted()); } finally { sourceUnits[i] = null; // no longer hold onto the unit } } // binding resolution lookupEnvironment.completeTypeBindings(); } /** * General API -> compile each of supplied files -> recompile any required * types for which we have an incomplete principle structure */ public void compile(ICompilationUnit[] sourceUnits) { CompilationUnitDeclaration unit = null; int i = 0; try { // build and record parsed units beginToCompile(sourceUnits); // process all units (some more could be injected in the loop by the // lookup environment) for (; i < totalUnits; i++) { unit = unitsToProcess[i]; try { if (options.verbose) System.out.println(Util.bind("compilation.process", //$NON-NLS-1$ new String[] { String.valueOf(i + 1), String.valueOf(totalUnits), new String(unitsToProcess[i] .getFileName()) })); process(unit, i); } finally { // cleanup compilation unit result unit.cleanUp(); if (options.verbose) System.out.println(Util.bind("compilation.done", //$NON-NLS-1$ new String[] { String.valueOf(i + 1), String.valueOf(totalUnits), new String(unitsToProcess[i] .getFileName()) })); } unitsToProcess[i] = null; // release reference to processed // unit // declaration requestor.acceptResult(unit.compilationResult.tagAsAccepted()); } } catch (AbortCompilation e) { this.handleInternalException(e, unit); } catch (Error e) { this.handleInternalException(e, unit, null); throw e; // rethrow } catch (RuntimeException e) { this.handleInternalException(e, unit, null); throw e; // rethrow } finally { this.reset(); } // if (options.verbose) { // if (totalUnits > 1) { // System.out.println( // ProjectPrefUtil.bind("compilation.units" , // String.valueOf(totalUnits))); // //$NON-NLS-1$ // } else { // System.out.println( // ProjectPrefUtil.bind("compilation.unit" , // String.valueOf(totalUnits))); // //$NON-NLS-1$ // } // } } protected void getMethodBodies(CompilationUnitDeclaration unit, int place) { // fill the methods bodies in order for the code to be generated if (unit.ignoreMethodBodies) { unit.ignoreFurtherInvestigation = true; return; // if initial diet parse did not work, no need to dig into method // bodies. } if (place < parseThreshold) return; // work already done ... // real parse of the method.... parser.scanner.setSource(unit.compilationResult.compilationUnit .getContents()); if (unit.types != null) { for (int i = unit.types.size(); --i >= 0;) if (unit.types.get(i) instanceof TypeDeclaration) { ((TypeDeclaration) unit.types.get(i)).parseMethod(parser, unit); } } } /* * Compiler crash recovery in case of unexpected runtime exceptions */ protected void handleInternalException(Throwable internalException, CompilationUnitDeclaration unit, CompilationResult result) { /* dump a stack trace to the console */ internalException.printStackTrace(); /* find a compilation result */ if ((unit != null)) // basing result upon the current unit if available result = unit.compilationResult; // current unit being processed // ? if ((result == null) && (unitsToProcess != null) && (totalUnits > 0)) result = unitsToProcess[totalUnits - 1].compilationResult; // last unit in beginToCompile ? if (result != null) { /* create and record a compilation problem */ StringWriter stringWriter = new StringWriter(); PrintWriter writer = new PrintWriter(stringWriter); internalException.printStackTrace(writer); StringBuffer buffer = stringWriter.getBuffer(); String[] pbArguments = new String[] { Util .bind("compilation.internalError") //$NON-NLS-1$ + "\n" //$NON-NLS-1$ + buffer.toString() }; result.record(problemReporter.createProblem(result.getFileName(), IProblem.Unclassified, pbArguments, pbArguments, Error, // severity 0, // source start 0, // source end 0, // line number unit, result), unit); /* hand back the compilation result */ if (!result.hasBeenAccepted) { requestor.acceptResult(result.tagAsAccepted()); } } } /* * Compiler recovery in case of internal AbortCompilation event */ protected void handleInternalException(AbortCompilation abortException, CompilationUnitDeclaration unit) { /* * special treatment for SilentAbort: silently cancelling the * compilation process */ if (abortException.isSilent) { if (abortException.silentException == null) { return; } else { throw abortException.silentException; } } /* uncomment following line to see where the abort came from */ // abortException.printStackTrace(); // Exception may tell which compilation result it is related, and which // problem caused it CompilationResult result = abortException.compilationResult; if ((result == null) && (unit != null)) result = unit.compilationResult; // current unit being processed // ? if ((result == null) && (unitsToProcess != null) && (totalUnits > 0)) result = unitsToProcess[totalUnits - 1].compilationResult; // last unit in beginToCompile ? if (result != null && !result.hasBeenAccepted) { /* distant problem which could not be reported back there */ if (abortException.problemId != 0) { result.record(problemReporter.createProblem(result .getFileName(), abortException.problemId, abortException.problemArguments, abortException.messageArguments, Error, // severity 0, // source start 0, // source end 0, // line number unit, result), unit); } else { /* * distant internal exception which could not be reported back * there */ if (abortException.exception != null) { this.handleInternalException(abortException.exception, null, result); return; } } /* hand back the compilation result */ if (!result.hasBeenAccepted) { requestor.acceptResult(result.tagAsAccepted()); } } else { /* * if (abortException.problemId != 0){ IProblem problem = * problemReporter.createProblem( "???".toCharArray(), * abortException.problemId, abortException.problemArguments, Error, // * severity 0, // source start 0, // source end 0); // line number * System.out.println(problem.getMessage()); } */ abortException.printStackTrace(); } } /** * Process a compilation unit already parsed and build. */ public void process(CompilationUnitDeclaration unit, int i) { getMethodBodies(unit, i); // fault in fields & methods if (unit.scope != null) unit.scope.faultInTypes(); // verify inherited methods // if (unit.scope != null) // unit.scope.verifyMethods(lookupEnvironment.methodVerifier()); // type checking unit.resolve(); // flow analysis unit.analyseCode(); // code generation // unit.generateCode(); // reference info // if (options.produceReferenceInfo && unit.scope != null) // unit.scope.storeDependencyInfo(); // refresh the total number of units known at this stage unit.compilationResult.totalUnitsKnown = totalUnits; } public void reset() { lookupEnvironment.reset(); parser.scanner.source = null; unitsToProcess = null; // if (DebugRequestor != null) DebugRequestor.reset(); } /** * Internal API used to resolve a given compilation unit. Can run a subset * of the compilation process */ public CompilationUnitDeclaration resolve(CompilationUnitDeclaration unit, ICompilationUnit sourceUnit, boolean verifyMethods, boolean analyzeCode) { try { if (unit == null) { // build and record parsed units parseThreshold = 0; // will request a full parse beginToCompile(new ICompilationUnit[] { sourceUnit }); // process all units (some more could be injected in the loop by // the lookup environment) unit = unitsToProcess[0]; } else { // initial type binding creation lookupEnvironment.buildTypeBindings(unit); // binding resolution lookupEnvironment.completeTypeBindings(); } // TODO : jsurfer check this // this.parser.getMethodBodies(unit); getMethodBodies(unit, 0); if (unit.scope != null) { // fault in fields & methods unit.scope.faultInTypes(); if (unit.scope != null && verifyMethods) { // http://dev.eclipse.org/bugs/show_bug.cgi?id=23117 // verify inherited methods unit.scope .verifyMethods(lookupEnvironment.methodVerifier()); } // type checking unit.resolve(); // flow analysis // if (analyzeCode) unit.analyseCode(); // code generation // if (generateCode) unit.generateCode(); } if (unitsToProcess != null) unitsToProcess[0] = null; // release reference to processed // unit declaration requestor.acceptResult(unit.compilationResult.tagAsAccepted()); return unit; } catch (AbortCompilation e) { this.handleInternalException(e, unit); return unit == null ? unitsToProcess[0] : unit; } catch (Error e) { this.handleInternalException(e, unit, null); throw e; // rethrow } catch (RuntimeException e) { this.handleInternalException(e, unit, null); throw e; // rethrow } finally { // No reset is performed there anymore since, // within the CodeAssist (or related tools), // the compiler may be called *after* a call // to this resolve(...) method. And such a call // needs to have a compiler with a non-empty // environment. // this.reset(); } } /** * Internal API used to resolve a given compilation unit. Can run a subset * of the compilation process */ public CompilationUnitDeclaration resolve(ICompilationUnit sourceUnit, boolean verifyMethods, boolean analyzeCode) { // boolean generateCode) { CompilationUnitDeclaration unit = null; try { // build and record parsed units parseThreshold = 0; // will request a full parse beginToCompile(new ICompilationUnit[] { sourceUnit }); // process all units (some more could be injected in the loop by the // lookup environment) unit = unitsToProcess[0]; getMethodBodies(unit, 0); if (unit.scope != null) { // // fault in fields & methods // unit.scope.faultInTypes(); // if (unit.scope != null && verifyMethods) { // // http://dev.eclipse.org/bugs/show_bug.cgi?id=23117 // // verify inherited methods // unit.scope.verifyMethods(lookupEnvironment.methodVerifier()); // } // // type checking // unit.resolve(); // flow analysis // if (analyzeCode) unit.analyseCode(); // code generation // if (generateCode) unit.generateCode(); } unitsToProcess[0] = null; // release reference to processed unit // declaration requestor.acceptResult(unit.compilationResult.tagAsAccepted()); return unit; } catch (AbortCompilation e) { this.handleInternalException(e, unit); return unit == null ? unitsToProcess[0] : unit; } catch (Error e) { this.handleInternalException(e, unit, null); throw e; // rethrow } catch (RuntimeException e) { this.handleInternalException(e, unit, null); throw e; // rethrow } finally { // No reset is performed there anymore since, // within the CodeAssist (or related tools), // the compiler may be called *after* a call // to this resolve(...) method. And such a call // needs to have a compiler with a non-empty // environment. // this.reset(); } } }