Java tutorial
/******************************************************************************* * Copyright (c) 2000, 2017 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 * which accompanies this distribution, and is available at * https://www.eclipse.org/legal/epl-2.0/ * * SPDX-License-Identifier: EPL-2.0 * * Contributors: * IBM Corporation - initial API and implementation * Stephan Herrmann - contributions for * bug 337868 - [compiler][model] incomplete support for package-info.java when using SearchableEnvironment * bug 186342 - [compiler][null] Using annotations for null checking *******************************************************************************/ package org.eclipse.jdt.internal.compiler; import org.eclipse.jdt.core.compiler.*; import org.eclipse.jdt.internal.compiler.env.*; import org.eclipse.jdt.internal.compiler.impl.*; import org.eclipse.jdt.internal.compiler.ast.*; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.lookup.*; import org.eclipse.jdt.internal.compiler.parser.*; import org.eclipse.jdt.internal.compiler.problem.*; import org.eclipse.jdt.internal.compiler.util.*; import java.io.*; import java.util.*; @SuppressWarnings("rawtypes") public class Compiler implements ITypeRequestor, ProblemSeverities { public Parser parser; public ICompilerRequestor requestor; public CompilerOptions options; public ProblemReporter problemReporter; protected PrintWriter out; // output for messages that are not sent to problemReporter public CompilerStats stats; public CompilationProgress progress; public int remainingIterations = 1; // management of unit to be processed //public CompilationUnitResult currentCompilationUnitResult; public CompilationUnitDeclaration[] unitsToProcess; public int totalUnits; // (totalUnits-1) gives the last unit in unitToProcess private Map<String, APTProblem[]> aptProblems; // name lookup public LookupEnvironment lookupEnvironment; // ONCE STABILIZED, THESE SHOULD RETURN TO A FINAL FIELD public static boolean DEBUG = false; public int parseThreshold = -1; public AbstractAnnotationProcessorManager annotationProcessorManager; public int annotationProcessorStartIndex = 0; public ReferenceBinding[] referenceBindings; public boolean useSingleThread = true; // by default the compiler will not use worker threads to read/process/write // 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 settings java.util.Map * The settings that control the compiler behavior. * * @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. * * @deprecated this constructor is kept to preserve 3.1 and 3.2M4 compatibility */ public Compiler(INameEnvironment environment, IErrorHandlingPolicy policy, Map<String, String> settings, final ICompilerRequestor requestor, IProblemFactory problemFactory) { this(environment, policy, new CompilerOptions(settings), requestor, problemFactory, null /* printwriter */, null /* progress */); } /** * 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 settings java.util.Map * The settings that control the compiler behavior. * * @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 <code>boolean</code> * 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". * * @deprecated this constructor is kept to preserve 3.1 and 3.2M4 compatibility */ public Compiler(INameEnvironment environment, IErrorHandlingPolicy policy, Map settings, final ICompilerRequestor requestor, IProblemFactory problemFactory, boolean parseLiteralExpressionsAsConstants) { this(environment, policy, new CompilerOptions(settings, parseLiteralExpressionsAsConstants), requestor, problemFactory, null /* printwriter */, null /* progress */); } /** * 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 options org.eclipse.jdt.internal.compiler.impl.CompilerOptions * The options that control the compiler behavior. * * @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, CompilerOptions options, final ICompilerRequestor requestor, IProblemFactory problemFactory) { this(environment, policy, options, requestor, problemFactory, null /* printwriter */, null /* progress */); } /** * 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 options org.eclipse.jdt.internal.compiler.impl.CompilerOptions * The options that control the compiler behavior. * * @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. * @deprecated */ public Compiler(INameEnvironment environment, IErrorHandlingPolicy policy, CompilerOptions options, final ICompilerRequestor requestor, IProblemFactory problemFactory, PrintWriter out) { this(environment, policy, options, requestor, problemFactory, out, null /* progress */); } public Compiler(INameEnvironment environment, IErrorHandlingPolicy policy, CompilerOptions options, final ICompilerRequestor requestor, IProblemFactory problemFactory, PrintWriter out, CompilationProgress progress) { this.options = options; this.progress = progress; // wrap requestor in DebugRequestor if one is specified if (DebugRequestor == null) { this.requestor = requestor; } else { this.requestor = new ICompilerRequestor() { @Override 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, this.options, this.problemReporter, environment); this.out = out == null ? new PrintWriter(System.out, true) : out; this.stats = new CompilerStats(); initializeParser(); } /** * Add an additional binary type */ @Override public void accept(IBinaryType binaryType, PackageBinding packageBinding, AccessRestriction accessRestriction) { if (this.options.verbose) { this.out.println(Messages.bind(Messages.compilation_loadBinary, new String(binaryType.getName()))); // new Exception("TRACE BINARY").printStackTrace(System.out); // System.out.println(); } LookupEnvironment env = packageBinding.environment; env.createBinaryTypeFrom(binaryType, packageBinding, accessRestriction); } /** * Add an additional compilation unit into the loop * -> build compilation unit declarations, their bindings and record their results. */ @Override public void accept(ICompilationUnit sourceUnit, AccessRestriction accessRestriction) { // Switch the current policy and compilation result for this unit to the requested one. CompilationResult unitResult = new CompilationResult(sourceUnit, this.totalUnits, this.totalUnits, this.options.maxProblemsPerUnit); unitResult.checkSecondaryTypes = true; try { if (this.options.verbose) { String count = String.valueOf(this.totalUnits + 1); this.out.println(Messages.bind(Messages.compilation_request, new String[] { count, count, new String(sourceUnit.getFileName()) })); } // diet parsing for large collection of unit CompilationUnitDeclaration parsedUnit; if (this.totalUnits < this.parseThreshold) { parsedUnit = this.parser.parse(sourceUnit, unitResult); } else { parsedUnit = this.parser.dietParse(sourceUnit, unitResult); } // initial type binding creation this.lookupEnvironment.buildTypeBindings(parsedUnit, accessRestriction); addCompilationUnit(sourceUnit, parsedUnit); // binding resolution this.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 this.requestor.acceptResult(unitResult.tagAsAccepted()); } else { throw e; // want to abort enclosing request to compile } } } /** * Add additional source types */ @Override public void accept(ISourceType[] sourceTypes, PackageBinding packageBinding, AccessRestriction accessRestriction) { this.problemReporter.abortDueToInternalError(Messages.bind(Messages.abort_againstSourceModel, new String[] { String.valueOf(sourceTypes[0].getName()), String.valueOf(sourceTypes[0].getFileName()) })); } protected synchronized void addCompilationUnit(ICompilationUnit sourceUnit, CompilationUnitDeclaration parsedUnit) { if (this.unitsToProcess == null) return; // not collecting units // append the unit to the list of ones to process later on int size = this.unitsToProcess.length; if (this.totalUnits == size) // when growing reposition units starting at position 0 System.arraycopy(this.unitsToProcess, 0, (this.unitsToProcess = new CompilationUnitDeclaration[size * 2]), 0, this.totalUnits); this.unitsToProcess[this.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; this.totalUnits = 0; this.unitsToProcess = new CompilationUnitDeclaration[maxUnits]; internalBeginToCompile(sourceUnits, maxUnits); } /** * Checks whether the compilation has been canceled and reports the given progress to the compiler progress. */ protected void reportProgress(String taskDecription) { if (this.progress != null) { if (this.progress.isCanceled()) { // Only AbortCompilation can stop the compiler cleanly. // We check cancellation again following the call to compile. throw new AbortCompilation(true, null); } this.progress.setTaskName(taskDecription); } } /** * Checks whether the compilation has been canceled and reports the given work increment to the compiler progress. */ protected void reportWorked(int workIncrement, int currentUnitIndex) { if (this.progress != null) { if (this.progress.isCanceled()) { // Only AbortCompilation can stop the compiler cleanly. // We check cancellation again following the call to compile. throw new AbortCompilation(true, null); } this.progress.worked(workIncrement, (this.totalUnits * this.remainingIterations) - currentUnitIndex - 1); } } public void compile(ICompilationUnit[] sourceUnits) { compile(sourceUnits, false); } /** * General API * -> compile each of supplied files * -> recompile any required types for which we have an incomplete principle structure */ private void compile(ICompilationUnit[] sourceUnits, boolean lastRound) { this.stats.startTime = System.currentTimeMillis(); try { // build and record parsed units reportProgress(Messages.compilation_beginningToCompile); if (this.options.complianceLevel >= ClassFileConstants.JDK9) { // in Java 9 the compiler must never ask the oracle for a module that is contained in the input units: sortModuleDeclarationsFirst(sourceUnits); } if (this.annotationProcessorManager == null) { beginToCompile(sourceUnits); } else { ICompilationUnit[] originalUnits = sourceUnits.clone(); // remember source units in case a source type collision occurs try { beginToCompile(sourceUnits); if (!lastRound) { processAnnotations(); } if (!this.options.generateClassFiles) { // -proc:only was set on the command line return; } } catch (SourceTypeCollisionException e) { backupAptProblems(); reset(); // a generated type was referenced before it was created // the compiler either created a MissingType or found a BinaryType for it // so add the processor's generated files & start over, // but remember to only pass the generated files to the annotation processor int originalLength = originalUnits.length; int newProcessedLength = e.newAnnotationProcessorUnits.length; ICompilationUnit[] combinedUnits = new ICompilationUnit[originalLength + newProcessedLength]; System.arraycopy(originalUnits, 0, combinedUnits, 0, originalLength); System.arraycopy(e.newAnnotationProcessorUnits, 0, combinedUnits, originalLength, newProcessedLength); this.annotationProcessorStartIndex = originalLength; compile(combinedUnits, e.isLastRound); return; } } // Restore the problems before the results are processed and cleaned up. restoreAptProblems(); processCompiledUnits(0, lastRound); } catch (AbortCompilation e) { this.handleInternalException(e, null); } if (this.options.verbose) { if (this.totalUnits > 1) { this.out.println(Messages.bind(Messages.compilation_units, String.valueOf(this.totalUnits))); } else { this.out.println(Messages.bind(Messages.compilation_unit, String.valueOf(this.totalUnits))); } } } private void sortModuleDeclarationsFirst(ICompilationUnit[] sourceUnits) { Arrays.sort(sourceUnits, (u1, u2) -> { char[] fn1 = u1.getFileName(); char[] fn2 = u2.getFileName(); boolean isMod1 = CharOperation.endsWith(fn1, TypeConstants.MODULE_INFO_FILE_NAME) || CharOperation.endsWith(fn1, TypeConstants.MODULE_INFO_CLASS_NAME); boolean isMod2 = CharOperation.endsWith(fn2, TypeConstants.MODULE_INFO_FILE_NAME) || CharOperation.endsWith(fn2, TypeConstants.MODULE_INFO_CLASS_NAME); if (isMod1 == isMod2) return 0; return isMod1 ? -1 : 1; }); } class APTProblem { CategorizedProblem problem; ReferenceContext context; APTProblem(CategorizedProblem problem, ReferenceContext context) { this.problem = problem; this.context = context; } } protected void backupAptProblems() { if (this.unitsToProcess == null) return; for (int i = 0; i < this.totalUnits; i++) { CompilationUnitDeclaration unitDecl = this.unitsToProcess[i]; CompilationResult result = unitDecl.compilationResult; if (result != null && result.hasErrors()) { CategorizedProblem[] errors = result.getErrors(); for (CategorizedProblem problem : errors) { if (problem.getCategoryID() == CategorizedProblem.CAT_UNSPECIFIED) { if (this.aptProblems == null) { this.aptProblems = new HashMap<>(); } APTProblem[] problems = this.aptProblems.get(new String(unitDecl.getFileName())); if (problems == null) { this.aptProblems.put(new String(unitDecl.getFileName()), new APTProblem[] { new APTProblem(problem, result.getContext(problem)) }); } else { APTProblem[] temp = new APTProblem[problems.length + 1]; System.arraycopy(problems, 0, temp, 0, problems.length); temp[problems.length] = new APTProblem(problem, result.getContext(problem)); this.aptProblems.put(new String(unitDecl.getFileName()), temp); } } } } } } protected void restoreAptProblems() { if (this.unitsToProcess != null && this.aptProblems != null) { for (int i = 0; i < this.totalUnits; i++) { CompilationUnitDeclaration unitDecl = this.unitsToProcess[i]; APTProblem[] problems = this.aptProblems.get(new String(unitDecl.getFileName())); if (problems != null) { for (APTProblem problem : problems) { unitDecl.compilationResult.record(problem.problem, problem.context); } } } } this.aptProblems = null; // No need for this. } protected void processCompiledUnits(int startingIndex, boolean lastRound) throws java.lang.Error { CompilationUnitDeclaration unit = null; ProcessTaskManager processingTask = null; try { if (this.useSingleThread) { // process all units (some more could be injected in the loop by the lookup environment) for (int i = startingIndex; i < this.totalUnits; i++) { unit = this.unitsToProcess[i]; if (unit.compilationResult != null && unit.compilationResult.hasBeenAccepted) continue; reportProgress(Messages.bind(Messages.compilation_processing, new String(unit.getFileName()))); try { if (this.options.verbose) this.out.println(Messages.bind(Messages.compilation_process, new String[] { String.valueOf(i + 1), String.valueOf(this.totalUnits), new String(this.unitsToProcess[i].getFileName()) })); process(unit, i); } finally { // cleanup compilation unit result, but only if not annotation processed. if (this.annotationProcessorManager == null || shouldCleanup(i)) unit.cleanUp(); } if (this.annotationProcessorManager == null) { this.unitsToProcess[i] = null; // release reference to processed unit declaration } reportWorked(1, i); this.stats.lineCount += unit.compilationResult.lineSeparatorPositions.length; long acceptStart = System.currentTimeMillis(); this.requestor.acceptResult(unit.compilationResult.tagAsAccepted()); this.stats.generateTime += System.currentTimeMillis() - acceptStart; // record accept time as part of generation if (this.options.verbose) this.out.println( Messages.bind(Messages.compilation_done, new String[] { String.valueOf(i + 1), String.valueOf(this.totalUnits), new String(unit.getFileName()) })); } } else { processingTask = new ProcessTaskManager(this, startingIndex); int acceptedCount = 0; // process all units (some more could be injected in the loop by the lookup environment) // the processTask can continue to process units until its fixed sized cache is full then it must wait // for this this thread to accept the units as they appear (it only waits if no units are available) while (true) { try { unit = processingTask.removeNextUnit(); // waits if no units are in the processed queue } catch (Error | RuntimeException e) { unit = processingTask.unitToProcess; throw e; } if (unit == null) break; reportWorked(1, acceptedCount++); this.stats.lineCount += unit.compilationResult.lineSeparatorPositions.length; this.requestor.acceptResult(unit.compilationResult.tagAsAccepted()); if (this.options.verbose) this.out.println(Messages.bind(Messages.compilation_done, new String[] { String.valueOf(acceptedCount), String.valueOf(this.totalUnits), new String(unit.getFileName()) })); } } if (!lastRound) { if (this.annotationProcessorManager != null && this.totalUnits > this.annotationProcessorStartIndex) { int backup = this.annotationProcessorStartIndex; int prevUnits = this.totalUnits; processAnnotations(); // Clean up the units that were left out previously for annotation processing. for (int i = backup; i < prevUnits; i++) { this.unitsToProcess[i].cleanUp(); } processCompiledUnits(backup, lastRound); } } } catch (AbortCompilation e) { this.handleInternalException(e, unit); } catch (Error | RuntimeException e) { this.handleInternalException(e, unit, null); throw e; // rethrow } finally { if (processingTask != null) { processingTask.shutdown(); processingTask = null; } reset(); this.annotationProcessorStartIndex = 0; this.stats.endTime = System.currentTimeMillis(); } } public synchronized CompilationUnitDeclaration getUnitToProcess(int next) { if (next < this.totalUnits) { CompilationUnitDeclaration unit = this.unitsToProcess[next]; if (this.annotationProcessorManager == null || next < this.annotationProcessorStartIndex) { this.unitsToProcess[next] = null; // release reference to processed unit declaration } return unit; } return null; } /* * Returns whether the compilation unit at the given index should be * cleaned up after processing. This basically means whether or not * the unit is still required for annotation processing. */ public boolean shouldCleanup(int index) { return index < this.annotationProcessorStartIndex; } public void setBinaryTypes(ReferenceBinding[] binaryTypes) { this.referenceBindings = binaryTypes; } /* * Compiler crash recovery in case of unexpected runtime exceptions */ protected void handleInternalException(Throwable internalException, CompilationUnitDeclaration unit, CompilationResult result) { if (result == null && unit != null) { result = unit.compilationResult; // current unit being processed ? } // Lookup environment may be in middle of connecting types if (result == null && this.lookupEnvironment.unitBeingCompleted != null) { result = this.lookupEnvironment.unitBeingCompleted.compilationResult; } if (result == null) { synchronized (this) { if (this.unitsToProcess != null && this.totalUnits > 0) result = this.unitsToProcess[this.totalUnits - 1].compilationResult; } } // last unit in beginToCompile ? boolean needToPrint = true; if (result != null) { /* create and record a compilation problem */ // only keep leading portion of the trace String[] pbArguments = new String[] { Messages.bind(Messages.compilation_internalError, Util.getExceptionSummary(internalException)), }; result.record( this.problemReporter.createProblem(result.getFileName(), IProblem.Unclassified, pbArguments, pbArguments, Error, // severity 0, // source start 0, // source end 0, // line number 0), // column number unit, true); /* hand back the compilation result */ if (!result.hasBeenAccepted) { this.requestor.acceptResult(result.tagAsAccepted()); needToPrint = false; } } if (needToPrint) { /* dump a stack trace to the console */ internalException.printStackTrace(); } } /* * 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; } 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 ? } // Lookup environment may be in middle of connecting types if (result == null && this.lookupEnvironment.unitBeingCompleted != null) { result = this.lookupEnvironment.unitBeingCompleted.compilationResult; } if (result == null) { synchronized (this) { if (this.unitsToProcess != null && this.totalUnits > 0) result = this.unitsToProcess[this.totalUnits - 1].compilationResult; } } // last unit in beginToCompile ? if (result != null && !result.hasBeenAccepted) { /* distant problem which could not be reported back there? */ if (abortException.problem != null) { recordDistantProblem: { CategorizedProblem distantProblem = abortException.problem; CategorizedProblem[] knownProblems = result.problems; for (int i = 0; i < result.problemCount; i++) { if (knownProblems[i] == distantProblem) { // already recorded break recordDistantProblem; } } if (distantProblem instanceof DefaultProblem) { // fixup filename TODO (philippe) should improve API to make this official ((DefaultProblem) distantProblem).setOriginatingFileName(result.getFileName()); } result.record(distantProblem, unit, true); } } 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) { this.requestor.acceptResult(result.tagAsAccepted()); } } else { abortException.printStackTrace(); } } public void initializeParser() { this.parser = new Parser(this.problemReporter, this.options.parseLiteralExpressionsAsConstants); } private void abortIfPreviewNotAllowed(ICompilationUnit[] sourceUnits, int maxUnits) { if (!this.options.enablePreviewFeatures) return; try { if (this.options.sourceLevel != ClassFileConstants.getLatestJDKLevel()) { this.problemReporter.abortDueToPreviewEnablingNotAllowed( CompilerOptions.versionFromJdkLevel(this.options.sourceLevel), CompilerOptions.getLatestVersion()); } } catch (AbortCompilation a) { // best effort to find a way for reporting this problem: report on the first source if (a.compilationResult == null) { a.compilationResult = new CompilationResult(sourceUnits[0], 0, maxUnits, this.options.maxProblemsPerUnit); } throw a; } } /** * Add the initial set of compilation units into the loop * -> build compilation unit declarations, their bindings and record their results. */ protected void internalBeginToCompile(ICompilationUnit[] sourceUnits, int maxUnits) { abortIfPreviewNotAllowed(sourceUnits, maxUnits); if (!this.useSingleThread && maxUnits >= ReadManager.THRESHOLD) this.parser.readManager = new ReadManager(sourceUnits, maxUnits); // Switch the current policy and compilation result for this unit to the requested one. for (int i = 0; i < maxUnits; i++) { CompilationResult unitResult = null; try { if (this.options.verbose) { this.out.println( Messages.bind(Messages.compilation_request, new String[] { String.valueOf(i + 1), String.valueOf(maxUnits), new String(sourceUnits[i].getFileName()) })); } // diet parsing for large collection of units CompilationUnitDeclaration parsedUnit; unitResult = new CompilationResult(sourceUnits[i], i, maxUnits, this.options.maxProblemsPerUnit); long parseStart = System.currentTimeMillis(); if (this.totalUnits < this.parseThreshold) { parsedUnit = this.parser.parse(sourceUnits[i], unitResult); } else { parsedUnit = this.parser.dietParse(sourceUnits[i], unitResult); } long resolveStart = System.currentTimeMillis(); this.stats.parseTime += resolveStart - parseStart; // initial type binding creation this.lookupEnvironment.buildTypeBindings(parsedUnit, null /*no access restriction*/); this.stats.resolveTime += System.currentTimeMillis() - resolveStart; addCompilationUnit(sourceUnits[i], parsedUnit); ImportReference currentPackage = parsedUnit.currentPackage; if (currentPackage != null) { unitResult.recordPackageName(currentPackage.tokens); } //} catch (AbortCompilationUnit e) { // requestor.acceptResult(unitResult.tagAsAccepted()); } catch (AbortCompilation a) { // best effort to find a way for reporting this problem: if (a.compilationResult == null) a.compilationResult = unitResult; throw a; } finally { sourceUnits[i] = null; // no longer hold onto the unit } } if (this.parser.readManager != null) { this.parser.readManager.shutdown(); this.parser.readManager = null; } // binding resolution this.lookupEnvironment.completeTypeBindings(); } /** * Process a compilation unit already parsed and build. */ public void process(CompilationUnitDeclaration unit, int i) { this.lookupEnvironment.unitBeingCompleted = unit; long parseStart = System.currentTimeMillis(); this.parser.getMethodBodies(unit); long resolveStart = System.currentTimeMillis(); this.stats.parseTime += resolveStart - parseStart; // fault in fields & methods if (unit.scope != null) unit.scope.faultInTypes(); // verify inherited methods if (unit.scope != null) unit.scope.verifyMethods(this.lookupEnvironment.methodVerifier()); // type checking unit.resolve(); long analyzeStart = System.currentTimeMillis(); this.stats.resolveTime += analyzeStart - resolveStart; //No need of analysis or generation of code if statements are not required if (!this.options.ignoreMethodBodies) unit.analyseCode(); // flow analysis long generateStart = System.currentTimeMillis(); this.stats.analyzeTime += generateStart - analyzeStart; if (!this.options.ignoreMethodBodies) unit.generateCode(); // code generation // reference info if (this.options.produceReferenceInfo && unit.scope != null) unit.scope.storeDependencyInfo(); // finalize problems (suppressWarnings) unit.finalizeProblems(); this.stats.generateTime += System.currentTimeMillis() - generateStart; // refresh the total number of units known at this stage unit.compilationResult.totalUnitsKnown = this.totalUnits; this.lookupEnvironment.unitBeingCompleted = null; } protected void processAnnotations() { try { processAnnotationsInternal(); } finally { this.annotationProcessorManager.cleanUp(); } } private void processAnnotationsInternal() { int newUnitSize = 0; int newClassFilesSize = 0; int bottom = this.annotationProcessorStartIndex; int top = this.totalUnits; ReferenceBinding[] binaryTypeBindingsTemp = this.referenceBindings; if (top == 0 && binaryTypeBindingsTemp == null) return; this.referenceBindings = null; do { // extract units to process int length = top - bottom; CompilationUnitDeclaration[] currentUnits = new CompilationUnitDeclaration[length]; int index = 0; for (int i = bottom; i < top; i++) { CompilationUnitDeclaration currentUnit = this.unitsToProcess[i]; currentUnits[index++] = currentUnit; } if (index != length) { System.arraycopy(currentUnits, 0, (currentUnits = new CompilationUnitDeclaration[index]), 0, index); } this.annotationProcessorManager.processAnnotations(currentUnits, binaryTypeBindingsTemp, false); // https://bugs.eclipse.org/bugs/show_bug.cgi?id=407841 // It is possible that during the #processAnnotations() call, some units in the next batch would have been // brought forward and compiled already. If there are any such, process them for annotations then and there. // This would avoid the complications of marking some units as compiled but not-annotation-processed-yet. if (top < this.totalUnits) { length = this.totalUnits - top; // NOTE: Reuse the same variable, but make sure it's not used after this point CompilationUnitDeclaration[] addedUnits = new CompilationUnitDeclaration[length]; System.arraycopy(this.unitsToProcess, top, addedUnits, 0, length); this.annotationProcessorManager.processAnnotations(addedUnits, binaryTypeBindingsTemp, false); } this.annotationProcessorStartIndex = top; ICompilationUnit[] newUnits = this.annotationProcessorManager.getNewUnits(); newUnitSize = newUnits.length; ReferenceBinding[] newClassFiles = this.annotationProcessorManager.getNewClassFiles(); binaryTypeBindingsTemp = newClassFiles; newClassFilesSize = newClassFiles.length; if (newUnitSize != 0) { ICompilationUnit[] newProcessedUnits = newUnits.clone(); // remember new units in case a source type collision occurs try { this.lookupEnvironment.isProcessingAnnotations = true; internalBeginToCompile(newUnits, newUnitSize); } catch (SourceTypeCollisionException e) { e.newAnnotationProcessorUnits = newProcessedUnits; throw e; } finally { this.lookupEnvironment.isProcessingAnnotations = false; this.annotationProcessorManager.reset(); } bottom = top; top = this.totalUnits; // last unit added this.annotationProcessorStartIndex = top; } else { bottom = top; this.annotationProcessorManager.reset(); } } while (newUnitSize != 0 || newClassFilesSize != 0); this.annotationProcessorManager.processAnnotations(null, null, true); // process potential units added in the final round see 329156 ICompilationUnit[] newUnits = this.annotationProcessorManager.getNewUnits(); newUnitSize = newUnits.length; if (newUnitSize != 0) { ICompilationUnit[] newProcessedUnits = newUnits.clone(); // remember new units in case a source type collision occurs try { this.lookupEnvironment.isProcessingAnnotations = true; internalBeginToCompile(newUnits, newUnitSize); } catch (SourceTypeCollisionException e) { e.isLastRound = true; e.newAnnotationProcessorUnits = newProcessedUnits; throw e; } finally { this.lookupEnvironment.isProcessingAnnotations = false; this.annotationProcessorManager.reset(); } } else { this.annotationProcessorManager.reset(); } // Units added in final round don't get annotation processed this.annotationProcessorStartIndex = this.totalUnits; } public void reset() { this.lookupEnvironment.reset(); this.parser.scanner.source = null; this.unitsToProcess = null; if (DebugRequestor != null) DebugRequestor.reset(); this.problemReporter.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, boolean generateCode) { try { if (unit == null) { // build and record parsed units this.parseThreshold = 0; // will request a full parse beginToCompile(new ICompilationUnit[] { sourceUnit }); // find the right unit from what was injected via accept(ICompilationUnit,..): for (int i = 0; i < this.totalUnits; i++) { if (this.unitsToProcess[i] != null && this.unitsToProcess[i].compilationResult.compilationUnit == sourceUnit) { unit = this.unitsToProcess[i]; break; } } if (unit == null) unit = this.unitsToProcess[0]; // fall back to old behavior } else { // initial type binding creation this.lookupEnvironment.buildTypeBindings(unit, null /*no access restriction*/); // binding resolution this.lookupEnvironment.completeTypeBindings(); } this.lookupEnvironment.unitBeingCompleted = unit; this.parser.getMethodBodies(unit); 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(this.lookupEnvironment.methodVerifier()); } // type checking unit.resolve(); // flow analysis if (analyzeCode) unit.analyseCode(); // code generation if (generateCode) unit.generateCode(); // finalize problems (suppressWarnings) unit.finalizeProblems(); } if (this.unitsToProcess != null) this.unitsToProcess[0] = null; // release reference to processed unit declaration this.requestor.acceptResult(unit.compilationResult.tagAsAccepted()); return unit; } catch (AbortCompilation e) { this.handleInternalException(e, unit); return unit == null ? this.unitsToProcess[0] : unit; } catch (Error | RuntimeException e) { this.handleInternalException(e, unit, null); throw e; // rethrow } finally { // leave this.lookupEnvironment.unitBeingCompleted set to the unit, until another unit is resolved // other calls to dom can cause classpath errors to be detected, resulting in AbortCompilation exceptions // 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) { return resolve(null, sourceUnit, verifyMethods, analyzeCode, generateCode); } }