JPS: support javac inlined constants in incremental java compilation

GitOrigin-RevId: 42977fcc81ef1fcf21dbb46f37e0d5435946ea49
This commit is contained in:
Eugene Zhuravlev
2020-02-10 11:03:00 +00:00
committed by intellij-monorepo-bot
parent 60b4249d3d
commit 425460b641
41 changed files with 514 additions and 453 deletions
@@ -42,7 +42,7 @@
</value>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_6" assert-keyword="true" project-jdk-name="IDEA jdk" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" assert-keyword="true" project-jdk-name="IDEA jdk" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>
@@ -42,7 +42,7 @@
</value>
</option>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_6" assert-keyword="true" project-jdk-name="IDEA jdk" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" assert-keyword="true" project-jdk-name="IDEA jdk" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>
@@ -6,7 +6,7 @@
<component name="ProjectResources">
<default-html-doctype>http://www.w3.org/1999/xhtml</default-html-doctype>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_6" assert-keyword="true" jdk-15="true" project-jdk-name="IDEA jdk" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" assert-keyword="true" jdk-15="true" project-jdk-name="IDEA jdk" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>
@@ -6,7 +6,7 @@
<component name="ProjectResources">
<default-html-doctype>http://www.w3.org/1999/xhtml</default-html-doctype>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_6" assert-keyword="true" jdk-15="true" project-jdk-name="IDEA jdk" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" assert-keyword="true" jdk-15="true" project-jdk-name="IDEA jdk" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>
@@ -3,7 +3,7 @@
<component name="EntryPointsManager">
<entry_points version="2.0" />
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_6" assert-keyword="true" jdk-15="true" project-jdk-name="IDEA jdk" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" assert-keyword="true" jdk-15="true" project-jdk-name="IDEA jdk" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>
@@ -6,7 +6,7 @@
<component name="ProjectResources">
<default-html-doctype>http://www.w3.org/1999/xhtml</default-html-doctype>
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_6" assert-keyword="true" jdk-15="true" project-jdk-name="IDEA jdk" project-jdk-type="JavaSDK">
<component name="ProjectRootManager" version="2" assert-keyword="true" jdk-15="true" project-jdk-name="IDEA jdk" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>
@@ -5,16 +5,14 @@ Compiling files:
src/Server.java
End of files
Cleaning output files:
out/production/ConstantChain/Client.class
out/production/ConstantChain/ServerClient.class
End of files
Compiling files:
src/Client.java
src/ServerClient.java
End of files
Cleaning output files:
out/production/ConstantChain/Server.class
out/production/ConstantChain/Client.class
End of files
Compiling files:
src/Server.java
End of files
src/Client.java
End of files
@@ -5,14 +5,16 @@ Compiling files:
src/A.java
End of files
Cleaning output files:
out/production/ConstantChain1/A.class
out/production/ConstantChain1/B.class
out/production/ConstantChain1/C.class
End of files
Compiling files:
src/B.java
src/C.java
End of files
Cleaning output files:
out/production/ConstantChain1/Client.class
End of files
Compiling files:
src/A.java
src/B.java
src/C.java
src/Client.java
End of files
End of files
@@ -7,12 +7,14 @@ src/Client.java
src/Const1.java
End of files
Cleaning output files:
out/production/ConstantChain2/Client.class
out/production/ConstantChain2/Const1.class
out/production/ConstantChain2/Const2.class
End of files
Compiling files:
src/Client.java
src/Const1.java
src/Const2.java
End of files
Cleaning output files:
out/production/ConstantChain2/Client.class
End of files
Compiling files:
src/Client.java
End of files
@@ -5,26 +5,14 @@ Compiling files:
moduleA/src/a/A.java
End of files
Cleaning output files:
out/production/moduleA/a/A.class
out/production/moduleA/a/AA.class
End of files
Compiling files:
moduleA/src/a/A.java
moduleA/src/a/AA.java
End of files
Cleaning output files:
out/production/moduleB/b/B.class
out/production/moduleB/b/BB.class
End of files
Compiling files:
moduleB/src/b/B.java
moduleB/src/b/BB.java
End of files
Cleaning output files:
out/production/moduleC/c/C.class
out/production/moduleC/c/CC.class
End of files
Compiling files:
moduleC/src/c/C.java
moduleC/src/c/CC.java
End of files
End of files
@@ -7,10 +7,7 @@ src/Server.java
End of files
Cleaning output files:
out/production/InnerConstantChange/Client.class
out/production/InnerConstantChange/Server$Inner.class
out/production/InnerConstantChange/Server.class
End of files
Compiling files:
src/Client.java
src/Server.java
End of files
End of files
@@ -6,11 +6,19 @@ src/constants/B.java
End of files
Cleaning output files:
out/production/MutualConstants/constants/A.class
out/production/MutualConstants/constants/B.class
out/production/MutualConstants/constants/PrintConst.class
End of files
Compiling files:
src/constants/A.java
src/constants/B.java
src/constants/PrintConst.java
End of files
Cleaning output files:
out/production/MutualConstants/constants/B.class
End of files
Compiling files:
src/constants/B.java
End of files
Cleaning output files:
out/production/MutualConstants/constants/PrintConst.class
End of files
Compiling files:
src/constants/PrintConst.java
End of files
@@ -6,9 +6,7 @@ src/Server.java
End of files
Cleaning output files:
out/production/StringConstantLessAccessible/Client.class
out/production/StringConstantLessAccessible/Server.class
End of files
Compiling files:
src/Client.java
src/Server.java
End of files
End of files
@@ -6,9 +6,7 @@ src/Server.java
End of files
Cleaning output files:
out/production/RecompileDependent/Client.class
out/production/RecompileDependent/Server.class
End of files
Compiling files:
src/Client.java
src/Server.java
End of files
End of files
@@ -6,15 +6,13 @@ src/Server.java
End of files
Cleaning output files:
out/production/RecompileDependentTests/Client.class
out/production/RecompileDependentTests/Server.class
End of files
Compiling files:
src/Client.java
src/Server.java
End of files
Cleaning output files:
out/test/RecompileDependentTests/TestClient.class
End of files
Compiling files:
testSrc/TestClient.java
End of files
End of files
@@ -5,12 +5,8 @@ Compiling files:
src/bug/Namespace.java
End of files
Cleaning output files:
out/production/PackageInfoRecompileOnConstantChange/bug/Ann.class
out/production/PackageInfoRecompileOnConstantChange/bug/Namespace.class
out/production/PackageInfoRecompileOnConstantChange/bug/package-info.class
End of files
Compiling files:
src/bug/Ann.java
src/bug/Namespace.java
src/bug/package-info.java
End of files
End of files
@@ -16,6 +16,7 @@ import javax.lang.model.util.Types;
*/
@SuppressWarnings("unused")
public class Javac8RefScanner extends JavacTreeRefScanner {
@Override
public Tree visitLambdaExpression(LambdaExpressionTree node, JavacReferenceCollectorListener.ReferenceCollector refCollector) {
final TypeMirror type = refCollector.getType(node);
@@ -23,7 +24,7 @@ public class Javac8RefScanner extends JavacTreeRefScanner {
if (types != null && type != null) {
final Element element = types.asElement(type);
if (element != null) {
final JavacRef.JavacElementRefBase ref = refCollector.asJavacRef(element);
final JavacRef.JavacElementRefBase ref = refCollector.asJavacRef(getCurrentEnclosingTypeElement(), element);
if (ref != null) {
refCollector.sinkDeclaration(new JavacDef.JavacFunExprDef(ref));
}
@@ -36,14 +37,14 @@ public class Javac8RefScanner extends JavacTreeRefScanner {
public Tree visitMemberReference(MemberReferenceTree node, JavacReferenceCollectorListener.ReferenceCollector refCollector) {
final Element element = refCollector.getReferencedElement(node);
if (element != null) {
final JavacRef.JavacElementRefBase ref = refCollector.asJavacRef(element);
final JavacRef.JavacElementRefBase ref = refCollector.asJavacRef(getCurrentEnclosingTypeElement(), element);
if (ref != null) {
refCollector.sinkReference(ref);
}
}
final TypeMirror type = refCollector.getType(node);
if (type != null) {
final JavacRef.JavacElementRefBase ref = refCollector.asJavacRef(refCollector.getTypeUtility().asElement(type));
final JavacRef.JavacElementRefBase ref = refCollector.asJavacRef(getCurrentEnclosingTypeElement(), refCollector.getTypeUtility().asElement(type));
if (ref != null) {
refCollector.sinkDeclaration(new JavacDef.JavacFunExprDef(ref));
}
@@ -223,6 +223,7 @@ public class JavacMain {
}
else {
diagnosticConsumer.report(new PlainMessageDiagnostic(Diagnostic.Kind.ERROR, buildCompilerErrorMessage(e)));
throw e;
}
}
else {
@@ -22,7 +22,7 @@ import org.jetbrains.jps.javac.DiagnosticOutputConsumer;
import org.jetbrains.jps.javac.JavaCompilerToolExtension;
import org.jetbrains.jps.javac.ast.api.JavacFileData;
import javax.tools.*;
import javax.tools.JavaCompiler;
import java.util.Collection;
public abstract class AbstractRefCollectorCompilerToolExtension extends JavaCompilerToolExtension {
@@ -32,7 +32,7 @@ public abstract class AbstractRefCollectorCompilerToolExtension extends JavaComp
@NotNull Collection<String> options,
@NotNull final DiagnosticOutputConsumer diagnosticConsumer) {
if (compilingTool.isCompilerTreeAPISupported() && isEnabled()) {
JavacReferenceCollector.installOn(task, divideImportsRefs(), new Consumer<JavacFileData>() {
JavacReferenceCollector.installOn(task, new Consumer<JavacFileData>() {
@Override
public void consume(JavacFileData data) {
diagnosticConsumer.registerJavacFileData(data);
@@ -42,6 +42,4 @@ public abstract class AbstractRefCollectorCompilerToolExtension extends JavaComp
}
protected abstract boolean isEnabled();
protected abstract boolean divideImportsRefs();
}
@@ -17,15 +17,10 @@ package org.jetbrains.jps.javac.ast;
public class ExternalRefCollectorCompilerToolExtension extends AbstractRefCollectorCompilerToolExtension {
public static final String ENABLED_PARAM = "external.java.process.ref.collector.enabled";
public static final String DIVIDE_IMPORTS_PARAM = "external.java.process.divide.imports";
@Override
protected boolean isEnabled() {
return "true".equals(System.getProperty(ENABLED_PARAM));
}
@Override
protected boolean divideImportsRefs() {
return "true".equals(System.getProperty(DIVIDE_IMPORTS_PARAM));
}
}
@@ -18,7 +18,7 @@ package org.jetbrains.jps.javac.ast;
import com.intellij.util.Consumer;
import org.jetbrains.jps.javac.ast.api.JavacFileData;
import javax.tools.*;
import javax.tools.JavaCompiler;
/**
* Code here should not depend on any javac private API located in tools.jar if no JavacFileReferencesRegistrar-s will be run.
@@ -26,8 +26,7 @@ import javax.tools.*;
*/
public class JavacReferenceCollector {
public static void installOn(JavaCompiler.CompilationTask task,
boolean divideImportRefs,
Consumer<? super JavacFileData> fileDataConsumer) {
JavacReferenceCollectorListener.installOn(task, divideImportRefs, fileDataConsumer);
JavacReferenceCollectorListener.installOn(task, fileDataConsumer);
}
}
@@ -9,10 +9,7 @@ import gnu.trove.TObjectIntHashMap;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jps.javac.ast.api.*;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.Name;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.*;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
@@ -21,15 +18,9 @@ import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
final class JavacReferenceCollectorListener implements TaskListener {
private final static TObjectIntHashMap<JavacRef> EMPTY_T_OBJ_INT_MAP = new TObjectIntHashMap<JavacRef>(0);
private final boolean myDivideImportRefs;
private final Consumer<? super JavacFileData> myDataConsumer;
private final JavacTask myJavacTask;
private final JavacTreeRefScanner myAstScanner;
@@ -43,9 +34,7 @@ final class JavacReferenceCollectorListener implements TaskListener {
private final Map<String, ReferenceCollector> myIncompletelyProcessedFiles = new HashMap<String, ReferenceCollector>(10);
static void installOn(JavaCompiler.CompilationTask task,
boolean divideImportRefs,
Consumer<? super JavacFileData> dataConsumer) {
static void installOn(JavaCompiler.CompilationTask task, Consumer<? super JavacFileData> dataConsumer) {
JavacTask javacTask = (JavacTask)task;
Method addTaskMethod; // jdk >= 8
try {
@@ -54,10 +43,9 @@ final class JavacReferenceCollectorListener implements TaskListener {
catch (NoSuchMethodException e) {
addTaskMethod = null;
}
final JavacReferenceCollectorListener taskListener = new JavacReferenceCollectorListener(divideImportRefs,
dataConsumer,
javacTask,
addTaskMethod != null);
final JavacReferenceCollectorListener taskListener = new JavacReferenceCollectorListener(
dataConsumer, javacTask, addTaskMethod != null
);
if (addTaskMethod != null) {
try {
addTaskMethod.setAccessible(true);
@@ -75,11 +63,9 @@ final class JavacReferenceCollectorListener implements TaskListener {
}
}
private JavacReferenceCollectorListener(boolean divideImportRefs,
Consumer<? super JavacFileData> dataConsumer,
private JavacReferenceCollectorListener(Consumer<? super JavacFileData> dataConsumer,
JavacTask javacTask,
boolean atLeastJdk8) {
myDivideImportRefs = divideImportRefs;
myDataConsumer = dataConsumer;
myJavacTask = javacTask;
myAtLeastJdk8 = atLeastJdk8;
@@ -133,9 +119,6 @@ final class JavacReferenceCollectorListener implements TaskListener {
if (collectImportsData) {
scanImports(unit, incompletelyProcessedFile.myFileData.getRefs(), incompletelyProcessedFile);
if (myDivideImportRefs) {
scanImports(unit, incompletelyProcessedFile.myFileData.getImportRefs(), incompletelyProcessedFile);
}
}
myAstScanner.scan(declarationToProcess, incompletelyProcessedFile);
@@ -176,7 +159,7 @@ final class JavacReferenceCollectorListener implements TaskListener {
// member import
for (Element memberElement : myElementUtility.getAllMembers((TypeElement)ownerElement)) {
if (memberElement.getSimpleName() == name) {
incrementOrAdd(elements, JavacRef.JavacElementRefBase.fromElement(memberElement, null, myNameTableCache, importProps));
incrementOrAdd(elements, JavacRef.JavacElementRefBase.fromElement(null, memberElement, null, myNameTableCache, importProps));
}
}
}
@@ -193,7 +176,7 @@ final class JavacReferenceCollectorListener implements TaskListener {
for (Element element = baseImport;
element != null && element.getKind() != ElementKind.PACKAGE;
element = element.getEnclosingElement()) {
incrementOrAdd(collector, JavacRef.JavacElementRefBase.fromElement(element, null, myNameTableCache, importProps));
incrementOrAdd(collector, JavacRef.JavacElementRefBase.fromElement(null, element, null, myNameTableCache, importProps));
}
}
@@ -201,18 +184,34 @@ final class JavacReferenceCollectorListener implements TaskListener {
private final JavacFileData myFileData;
private final JavacTreeHelper myTreeHelper;
private int myRemainDeclarations;
private final JavacRef.JavacClass myPackageInfo;
private ReferenceCollector(int remainDeclarations,
String filePath,
CompilationUnitTree unitTree) {
myRemainDeclarations = remainDeclarations;
myFileData = new JavacFileData(filePath,
createReferenceHolder(),
myDivideImportRefs ? createReferenceHolder() : EMPTY_T_OBJ_INT_MAP,
new ArrayList<JavacTypeCast>(),
createDefinitionHolder(),
new THashSet<JavacRef>());
myFileData = new JavacFileData(
filePath, createReferenceHolder(), new ArrayList<JavacTypeCast>(), createDefinitionHolder(), new THashSet<JavacRef>()
);
myTreeHelper = new JavacTreeHelper(unitTree, myTreeUtility);
if (isPackageInfo(filePath)) {
final ExpressionTree packageName = unitTree.getPackageName();
final String pack = packageName != null ? packageName.toString() : "";
myPackageInfo = new JavacRef.JavacClassImpl(false, Collections.<Modifier>emptySet(), pack.isEmpty()? "package-info" : pack + ".package-info");
sinkDeclaration(new JavacDef.JavacClassDef(myPackageInfo, JavacRef.EMPTY_ARRAY));
}
else {
myPackageInfo = null;
}
}
private boolean isPackageInfo(String filePath) {
if (filePath != null && filePath.endsWith("package-info.java")) {
final int idx = filePath.length() - "package-info.java".length() - 1;
return idx >= 0 && (filePath.charAt(idx) == '/' || filePath.charAt(idx) == File.separatorChar);
}
return false;
}
void sinkReference(@Nullable JavacRef.JavacElementRefBase ref) {
@@ -234,21 +233,25 @@ final class JavacReferenceCollectorListener implements TaskListener {
}
@Nullable
JavacRef.JavacElementRefBase asJavacRef(Element element) {
return asJavacRef(element, null);
JavacRef.JavacElementRefBase asJavacRef(final Element containingClass, Element element) {
return asJavacRef(containingClass, element, null);
}
@Nullable
JavacRef.JavacElementRefBase asJavacRef(Element element, Element qualifier) {
return JavacRef.JavacElementRefBase.fromElement(element, qualifier, myNameTableCache);
JavacRef.JavacElementRefBase asJavacRef(final Element containingClass, Element element, Element qualifier) {
return JavacRef.JavacElementRefBase.fromElement(getContainingClassName(containingClass), element, qualifier, myNameTableCache);
}
@Nullable
JavacRef.JavacElementRefBase asJavacRef(TypeMirror typeMirror) {
JavacRef.JavacElementRefBase asJavacRef(final Element containingClass, TypeMirror typeMirror) {
final Element element = getTypeUtility().asElement(typeMirror);
return element == null ? null : JavacRef.JavacElementRefBase.fromElement(element, null, myNameTableCache);
return element == null ? null : JavacRef.JavacElementRefBase.fromElement(getContainingClassName(containingClass), element, null, myNameTableCache);
}
private String getContainingClassName(Element containingClass) {
return containingClass != null? myNameTableCache.parseBinaryName(containingClass) : myPackageInfo != null? myPackageInfo.getName() : null;
}
Element getReferencedElement(Tree tree) {
return myTreeHelper.getReferencedElement(tree);
}
@@ -41,7 +41,7 @@ class JavacTreeRefScanner extends TreeScanner<Tree, JavacReferenceCollectorListe
return null;
}
if (ALLOWED_ELEMENTS.contains(element.getKind())) {
refCollector.sinkReference(refCollector.asJavacRef(element));
refCollector.sinkReference(refCollector.asJavacRef(getCurrentEnclosingTypeElement(), element));
}
return null;
}
@@ -51,7 +51,7 @@ class JavacTreeRefScanner extends TreeScanner<Tree, JavacReferenceCollectorListe
if (node.getClassBody() == null) {
final Element element = collector.getReferencedElement(node);
if (element != null) {
collector.sinkReference(collector.asJavacRef(element));
collector.sinkReference(collector.asJavacRef(getCurrentEnclosingTypeElement(), element));
}
return super.visitNewClass(node, collector);
} else {
@@ -74,7 +74,7 @@ class JavacTreeRefScanner extends TreeScanner<Tree, JavacReferenceCollectorListe
public Tree visitVariable(VariableTree node, JavacReferenceCollectorListener.ReferenceCollector refCollector) {
final Element element = refCollector.getReferencedElement(node);
if (element != null && element.getKind() == ElementKind.FIELD) {
final JavacRef.JavacElementRefBase ref = refCollector.asJavacRef(element);
final JavacRef.JavacElementRefBase ref = refCollector.asJavacRef(getCurrentEnclosingTypeElement(), element);
if (ref != null) {
processMemberDefinition(refCollector, ref, element, element.asType());
}
@@ -92,7 +92,7 @@ class JavacTreeRefScanner extends TreeScanner<Tree, JavacReferenceCollectorListe
if (type instanceof DeclaredType) {
qualifierType = ((DeclaredType)type).asElement();
}
refCollector.sinkReference(refCollector.asJavacRef(element, qualifierType));
refCollector.sinkReference(refCollector.asJavacRef(getCurrentEnclosingTypeElement(), element, qualifierType));
}
return super.visitMemberSelect(node, refCollector);
}
@@ -107,7 +107,7 @@ class JavacTreeRefScanner extends TreeScanner<Tree, JavacReferenceCollectorListe
}
if (element != null) {
final JavacRef.JavacElementRefBase ref = refCollector.asJavacRef(element);
final JavacRef.JavacElementRefBase ref = refCollector.asJavacRef(getCurrentEnclosingTypeElement(), element);
if (ref != null) {
processMemberDefinition(refCollector, ref, element, ((ExecutableElement)element).getReturnType());
}
@@ -124,7 +124,7 @@ class JavacTreeRefScanner extends TreeScanner<Tree, JavacReferenceCollectorListe
Set<TypeElement> typeElements = extractImplicitToStringCalls(lOp, rOp, collector);
if (typeElements != null) {
for (TypeElement element : typeElements) {
JavacRef.JavacElementRefBase ref = collector.asJavacRef(element);
JavacRef.JavacElementRefBase ref = collector.asJavacRef(getCurrentEnclosingTypeElement(), element);
if (ref != null) {
collector.sinkImplicitToString(ref);
}
@@ -151,7 +151,7 @@ class JavacTreeRefScanner extends TreeScanner<Tree, JavacReferenceCollectorListe
retType = typeArguments.get(0);
}
}
final JavacRef.JavacElementRefBase returnType = refCollector.asJavacRef(retType);
final JavacRef.JavacElementRefBase returnType = refCollector.asJavacRef(getCurrentEnclosingTypeElement(), retType);
if (returnType != null) {
refCollector.sinkDeclaration(new JavacDef.JavacMemberDef(ref, returnType, dimension, isStatic(element)));
}
@@ -164,13 +164,13 @@ class JavacTreeRefScanner extends TreeScanner<Tree, JavacReferenceCollectorListe
if (element != null && element.getKind() != ElementKind.CONSTRUCTOR) {
Set<Modifier> modifiers = element.getModifiers();
if (!modifiers.contains(Modifier.STATIC) && !modifiers.contains(Modifier.PRIVATE)) {
TypeElement currentClass = myCurrentEnclosingElement.peek();
final TypeElement currentClass = getCurrentEnclosingTypeElement();
TypeElement actualQualifier = findQualifier(element, currentClass);
//means java.lang.Object's method called from an interface
if (actualQualifier == null) {
actualQualifier = myCurrentEnclosingElement.peek();
actualQualifier = currentClass;
}
collector.sinkReference(collector.asJavacRef(element, actualQualifier));
collector.sinkReference(collector.asJavacRef(currentClass, element, actualQualifier));
scan(node.getTypeArguments(), collector);
scan(node.getArguments(), collector);
return null;
@@ -180,6 +180,10 @@ class JavacTreeRefScanner extends TreeScanner<Tree, JavacReferenceCollectorListe
return super.visitMethodInvocation(node, collector);
}
protected TypeElement getCurrentEnclosingTypeElement() {
return myCurrentEnclosingElement.empty()? null : myCurrentEnclosingElement.peek();
}
private final Stack<TypeElement> myCurrentEnclosingElement = new Stack<TypeElement>(1);
private final Stack<Long> myCurrentEnclosingElementOffset = new Stack<Long>(1);
private final Stack<NewClassTree> myCurrentAnonymousTree = new Stack<NewClassTree>(1);
@@ -199,7 +203,7 @@ class JavacTreeRefScanner extends TreeScanner<Tree, JavacReferenceCollectorListe
final JavacRef[] supers;
if (superclass != refCollector.getTypeUtility().getNoType(TypeKind.NONE)) {
supers = new JavacRef[interfaces.size() + 1];
final JavacRef.JavacElementRefBase ref = refCollector.asJavacRef(superclass);
final JavacRef.JavacElementRefBase ref = refCollector.asJavacRef(getCurrentEnclosingTypeElement(), superclass);
if (ref == null) return null;
supers[interfaces.size()] = ref;
}
@@ -209,11 +213,11 @@ class JavacTreeRefScanner extends TreeScanner<Tree, JavacReferenceCollectorListe
int i = 0;
for (TypeMirror anInterface : interfaces) {
final JavacRef.JavacElementRefBase ref = refCollector.asJavacRef(anInterface);
final JavacRef.JavacElementRefBase ref = refCollector.asJavacRef(getCurrentEnclosingTypeElement(), anInterface);
if (ref == null) return null;
supers[i++] = ref;
}
final JavacRef.JavacElementRefBase aClass = refCollector.asJavacRef(element);
final JavacRef.JavacElementRefBase aClass = refCollector.asJavacRef(null, element);
if (aClass == null) return null;
refCollector.sinkReference(aClass);
refCollector.sinkDeclaration(new JavacDef.JavacClassDef(aClass, supers));
@@ -234,12 +238,12 @@ class JavacTreeRefScanner extends TreeScanner<Tree, JavacReferenceCollectorListe
Element castType = collector.getReferencedElement(node.getType());
if (castType == null) return null;
JavacRef.JavacElementRefBase castTypeElement = collector.asJavacRef(castType);
JavacRef.JavacElementRefBase castTypeElement = collector.asJavacRef(null, castType);
if (!(castTypeElement instanceof JavacRef.JavacClass)) return null;
TypeMirror operandType = collector.getType(node.getExpression());
if (operandType == null) return null;
JavacRef.JavacElementRefBase operandTypeElement = collector.asJavacRef(operandType);
JavacRef.JavacElementRefBase operandTypeElement = collector.asJavacRef(getCurrentEnclosingTypeElement(), operandType);
if (!(operandTypeElement instanceof JavacRef.JavacClass)) return null;
collector.sinkTypeCast(new JavacTypeCast((JavacRef.JavacClass) operandTypeElement, (JavacRef.JavacClass) castTypeElement));
@@ -27,20 +27,17 @@ public class JavacFileData {
private final String myFilePath;
private final TObjectIntHashMap<JavacRef> myRefs;
private final TObjectIntHashMap<JavacRef> myImportRefs;
private final List<JavacTypeCast> myCasts;
private final List<JavacDef> myDefs;
private final Set<JavacRef> myImplicitRefs;
public JavacFileData(@NotNull String path,
@NotNull TObjectIntHashMap<JavacRef> refs,
@NotNull TObjectIntHashMap<JavacRef> importRefs,
@NotNull List<JavacTypeCast> casts,
@NotNull List<JavacDef> defs,
@NotNull Set<JavacRef> implicitRefs) {
myFilePath = path;
myRefs = refs;
myImportRefs = importRefs;
myCasts = casts;
myDefs = defs;
myImplicitRefs = implicitRefs;
@@ -61,11 +58,6 @@ public class JavacFileData {
return myRefs;
}
@NotNull
public TObjectIntHashMap<JavacRef> getImportRefs() {
return myImportRefs;
}
@NotNull
public List<JavacTypeCast> getCasts() {
return myCasts;
@@ -83,7 +75,6 @@ public class JavacFileData {
try {
stream.writeUTF(getFilePath());
saveRefs(stream, getRefs());
saveRefs(stream, getImportRefs());
saveCasts(stream, getCasts());
saveDefs(stream, getDefs());
saveImplicitToString(stream, getImplicitToStringRefs());
@@ -99,12 +90,12 @@ public class JavacFileData {
@SuppressWarnings("IOResourceOpenedButNotSafelyClosed")
final DataInputStream in = new DataInputStream(new ByteArrayInputStream(bytes));
try {
return new JavacFileData(in.readUTF(),
readRefs(in),
readRefs(in),
readCasts(in),
readDefs(in),
readImplicitToString(in));
final String path = in.readUTF();
final TObjectIntHashMap<JavacRef> refs = readRefs(in);
final List<JavacTypeCast> casts = readCasts(in);
final List<JavacDef> defs = readDefs(in);
final Set<JavacRef> implicitRefs = readImplicitToString(in);
return new JavacFileData(path, refs, casts, defs, implicitRefs);
}
catch (IOException e) {
throw new RuntimeException(e);
@@ -213,10 +204,13 @@ public class JavacFileData {
}
else if (ref instanceof JavacRef.JavacField) {
out.writeByte(FIELD_MARKER);
out.writeUTF(((JavacRef.JavacField)ref).getContainingClass());
out.writeUTF(ref.getOwnerName());
out.writeUTF(((JavacRef.JavacField)ref).getDescriptor());
}
else if (ref instanceof JavacRef.JavacMethod) {
out.writeByte(METHOD_MARKER);
out.writeUTF(((JavacRef.JavacMethod)ref).getContainingClass());
out.writeUTF(ref.getOwnerName());
out.write(((JavacRef.JavacMethod)ref).getParamCount());
}
@@ -231,11 +225,27 @@ public class JavacFileData {
final byte marker = in.readByte();
switch (marker) {
case CLASS_MARKER:
return new JavacRef.JavacClassImpl(in.readBoolean(), readModifiers(in), in.readUTF());
final boolean isAnonymous = in.readBoolean();
final Set<Modifier> classModifiers = readModifiers(in);
final String className = in.readUTF();
return new JavacRef.JavacClassImpl(isAnonymous, classModifiers, className);
case METHOD_MARKER:
return new JavacRef.JavacMethodImpl(in.readUTF(), in.readByte(), readModifiers(in), in.readUTF());
final String methodContainingClass = in.readUTF();
final String methodOwnerName = in.readUTF();
final byte methodParamCount = in.readByte();
final Set<Modifier> methodModifiers = readModifiers(in);
final String methodName = in.readUTF();
return new JavacRef.JavacMethodImpl(methodContainingClass, methodOwnerName, methodParamCount, methodModifiers, methodName);
case FIELD_MARKER:
return new JavacRef.JavacFieldImpl(in.readUTF(), readModifiers(in), in.readUTF());
final String fieldContainingClass = in.readUTF();
final String fieldOwnerName = in.readUTF();
final String fieldDescriptor = in.readUTF();
final Set<Modifier> fieldModifiers = readModifiers(in);
final String fieldName = in.readUTF();
return new JavacRef.JavacFieldImpl(fieldContainingClass, fieldOwnerName, fieldModifiers, fieldName, fieldDescriptor);
default:
throw new IllegalStateException("unknown marker " + marker);
}
@@ -19,8 +19,8 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.lang.model.element.*;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.type.*;
import javax.lang.model.util.SimpleTypeVisitor6;
import java.util.Set;
public interface JavacRef {
@@ -61,13 +61,17 @@ public interface JavacRef {
interface JavacClass extends JavacRef {
boolean isAnonymous();
boolean isPackageInfo();
}
interface JavacMethod extends JavacRef {
byte getParamCount();
String getContainingClass();
}
interface JavacField extends JavacRef {
String getDescriptor();
String getContainingClass();
}
abstract class JavacRefBase implements JavacRef {
@@ -115,18 +119,31 @@ public interface JavacRef {
public boolean isAnonymous() {
return myAnonymous;
}
@Override
public boolean isPackageInfo() {
final String name = getName();
return name.endsWith(".package-info") || "package-info".equals(name);
}
}
class JavacMethodImpl extends JavacRefBase implements JavacMethod {
private final String myContainingClassName;
private final String myOwnerName;
private final byte myParamCount;
public JavacMethodImpl(String ownerName, byte paramCount, Set<Modifier> modifiers, String name) {
public JavacMethodImpl(String containingClassName, String ownerName, byte paramCount, Set<Modifier> modifiers, String name) {
super(name, modifiers);
myContainingClassName = containingClassName;
myOwnerName = ownerName;
myParamCount = paramCount;
}
@Override
public String getContainingClass() {
return myContainingClassName;
}
@Override
public byte getParamCount() {
return myParamCount;
@@ -140,11 +157,21 @@ public interface JavacRef {
}
class JavacFieldImpl extends JavacRefBase implements JavacField {
private final String myOwnerName;
public JavacFieldImpl(String ownerName, Set<Modifier> modifiers, String name) {
private final String myContainingClassName;
private final String myOwnerName;
private final String myDescriptor;
public JavacFieldImpl(String containingClassName, String ownerName, Set<Modifier> modifiers, String name, String descriptor) {
super(name, modifiers);
myContainingClassName = containingClassName;
myOwnerName = ownerName;
myDescriptor = descriptor;
}
@Override
public String getContainingClass() {
return myContainingClassName;
}
@NotNull
@@ -152,6 +179,11 @@ public interface JavacRef {
public String getOwnerName() {
return myOwnerName;
}
@Override
public String getDescriptor() {
return myDescriptor;
}
}
abstract class JavacElementRefBase implements JavacRef {
@@ -191,12 +223,12 @@ public interface JavacRef {
}
@Nullable
public static JavacElementRefBase fromElement(Element element, Element qualifier, JavacNameTable nameTableCache) {
return fromElement(element, qualifier, nameTableCache, null);
public static JavacElementRefBase fromElement(final String containingClass, Element element, Element qualifier, JavacNameTable nameTableCache) {
return fromElement(containingClass, element, qualifier, nameTableCache, null);
}
@Nullable
public static JavacElementRefBase fromElement(Element element, Element qualifier, JavacNameTable nameTableCache, @Nullable ImportProperties importProps) {
public static JavacElementRefBase fromElement(final String containigClass, Element element, Element qualifier, JavacNameTable nameTableCache, @Nullable ImportProperties importProps) {
if (qualifier != null) {
TypeMirror type = qualifier.asType();
if (!isValidType(type)) {
@@ -208,11 +240,11 @@ public interface JavacRef {
}
else if (element instanceof VariableElement) {
if (qualifier == null && !checkEnclosingElement(element)) return null;
return new JavacElementFieldImpl(element, qualifier, nameTableCache, importProps);
return new JavacElementFieldImpl(containigClass, element, qualifier, nameTableCache, importProps);
}
else if (element instanceof ExecutableElement) {
if (qualifier == null && !checkEnclosingElement(element)) return null;
return new JavacElementMethodImpl(element, qualifier, nameTableCache, importProps);
return new JavacElementMethodImpl(containigClass, element, qualifier, nameTableCache, importProps);
}
else if (element == null || element.getKind() == ElementKind.OTHER || element.getKind() == ElementKind.TYPE_PARAMETER) {
// javac reserved symbol kind (e.g: com.sun.tools.javac.comp.Resolve.ResolveError)
@@ -270,11 +302,24 @@ public interface JavacRef {
public boolean isAnonymous() {
return myNameTableCache.parseName(myOriginalElement.getSimpleName()).isEmpty();
}
@Override
public boolean isPackageInfo() {
return false;
}
}
class JavacElementMethodImpl extends JavacElementRefBase implements JavacMethod {
public JavacElementMethodImpl(@NotNull Element element, @Nullable Element qualifier, JavacNameTable nameTableCache, final ImportProperties importProps) {
private final String myContainingClass;
public JavacElementMethodImpl(String containingClass, @NotNull Element element, @Nullable Element qualifier, JavacNameTable nameTableCache, final ImportProperties importProps) {
super(element, qualifier, nameTableCache, importProps);
myContainingClass = containingClass;
}
@Override
public String getContainingClass() {
return myContainingClass;
}
@Override
@@ -284,8 +329,51 @@ public interface JavacRef {
}
class JavacElementFieldImpl extends JavacElementRefBase implements JavacField {
public JavacElementFieldImpl(@NotNull Element element, @Nullable Element qualifier, JavacNameTable nameTableCache, final ImportProperties importProps) {
private final String myContainingClass;
public JavacElementFieldImpl(String containingClass, @NotNull Element element, @Nullable Element qualifier, JavacNameTable nameTableCache, final ImportProperties importProps) {
super(element, qualifier, nameTableCache, importProps);
myContainingClass = containingClass;
}
@Override
public String getContainingClass() {
return myContainingClass;
}
@Override
public String getDescriptor() {
return calcDescriptor(myOriginalElement.asType());
}
private String calcDescriptor(TypeMirror type) {
return new SimpleTypeVisitor6<String, Void>(null) {
@Override
public String visitPrimitive(PrimitiveType t, Void aVoid) {
switch(t.getKind()) {
case BYTE: return "B";
case CHAR: return "C";
case DOUBLE: return "D";
case FLOAT: return "F";
case INT: return "I";
case LONG:return "J";
case SHORT: return "S";
case BOOLEAN: return "Z";
}
return null;
}
@Override
public String visitArray(ArrayType t, Void aVoid) {
return "[" + visit(t.getComponentType());
}
@Override
public String visitDeclared(DeclaredType t, Void aVoid) {
return "L" + myNameTableCache.parseBinaryName(t.asElement()).replace('.', '/') + ";";
}
}.visit(type);
}
}
}
@@ -14,4 +14,4 @@
# limitations under the License.
#
org.jetbrains.jps.backwardRefs.JavaBackwardReferenceRegistrar
org.jetbrains.jps.incremental.java.ImportReferencesRegistrar
org.jetbrains.jps.incremental.java.JpsReferenceDependenciesRegistrar
@@ -23,12 +23,8 @@ public class JavaBackwardReferenceRegistrar implements JavacFileReferencesRegist
return JavaBackwardReferenceIndexWriter.isEnabled() && JavaBackwardReferenceIndexWriter.getInstance() != null;
}
@SuppressWarnings("unchecked")
@Override
public boolean onlyImports() {
return false;
}
@Override
public void registerFile(CompileContext context, String filePath,
TObjectIntHashMap<? extends JavacRef> refs,
Collection<? extends JavacDef> defs,
@@ -27,10 +27,36 @@ import java.util.concurrent.Future;
*/
public class Callbacks {
public interface ConstantRef {
String getOwner();
String getName();
String getDescriptor();
}
public interface Backend {
void associate(String classFileName, String sourceFileName, ClassReader cr);
void associate(String classFileName, Collection<String> sources, ClassReader cr);
void registerImports(String className, Collection<String> classImports, Collection<String> staticImports);
void registerConstantReferences(String className, Collection<ConstantRef> cRefs);
}
public static ConstantRef createConstantReference(String ownerClass, String fieldName, String descriptor) {
return new ConstantRef() {
@Override
public String getOwner() {
return ownerClass;
}
@Override
public String getName() {
return fieldName;
}
@Override
public String getDescriptor() {
return descriptor;
}
};
}
public static class ConstantAffection {
@@ -28,6 +28,7 @@ public class ClassRepr extends ClassFileRepr {
private final int myOuterClassName;
private final boolean myIsLocal;
private final boolean myIsAnonymous;
private boolean myHasInlinedConstants;
public Set<MethodRepr> getMethods() {
return myMethods;
@@ -49,6 +50,14 @@ public class ClassRepr extends ClassFileRepr {
return myIsAnonymous;
}
public boolean hasInlinedConstants() {
return myHasInlinedConstants;
}
public void setHasInlinedConstants(boolean hasConstants) {
myHasInlinedConstants = hasConstants;
}
public TypeRepr.ClassType getSuperClass() {
return mySuperClass;
}
@@ -106,10 +115,13 @@ public class ClassRepr extends ClassFileRepr {
if (!mySuperClass.equals(pastClass.mySuperClass)) {
base |= Difference.SUPERCLASS;
}
if (!getUsages().equals(pastClass.getUsages())) {
base |= Difference.USAGES;
}
if (hasInlinedConstants() != pastClass.hasInlinedConstants()) {
base |= Difference.CONSTANT_REFERENCES;
}
final int d = base;
return new Diff(diff) {
@@ -244,6 +256,7 @@ public class ClassRepr extends ClassFileRepr {
int flags = DataInputOutputUtil.readINT(in);
myIsLocal = (flags & LOCAL_MASK) != 0;
myIsAnonymous = (flags & ANONYMOUS_MASK) != 0;
myHasInlinedConstants = (flags & HAS_INLINED_CONSTANTS_MASK) != 0;
}
catch (IOException e) {
throw new BuildDataCorruptedException(e);
@@ -252,6 +265,7 @@ public class ClassRepr extends ClassFileRepr {
private static final int LOCAL_MASK = 1;
private static final int ANONYMOUS_MASK = 2;
private static final int HAS_INLINED_CONSTANTS_MASK = 4;
@Override
public void save(final DataOutput out) {
@@ -264,7 +278,9 @@ public class ClassRepr extends ClassFileRepr {
RW.save(myAnnotationTargets, UsageRepr.AnnotationUsage.elementTypeExternalizer, out);
RW.writeUTF(out, myRetentionPolicy == null ? "" : myRetentionPolicy.toString());
DataInputOutputUtil.writeINT(out, myOuterClassName);
DataInputOutputUtil.writeINT(out, (myIsLocal ? LOCAL_MASK:0) | (myIsAnonymous ? ANONYMOUS_MASK : 0));
DataInputOutputUtil.writeINT(
out, (myIsLocal ? LOCAL_MASK:0) | (myIsAnonymous ? ANONYMOUS_MASK : 0) | (myHasInlinedConstants ? HAS_INLINED_CONSTANTS_MASK : 0)
);
}
catch (IOException e) {
throw new BuildDataCorruptedException(e);
@@ -380,6 +396,8 @@ public class ClassRepr extends ClassFileRepr {
stream.println(myIsLocal);
stream.print(" Anonymous class: ");
stream.println(myIsAnonymous);
stream.print(" Has inlined constants: ");
stream.println(myHasInlinedConstants);
stream.println(" Fields:");
final FieldRepr[] fs = myFields.toArray(new FieldRepr[0]);
@@ -29,6 +29,7 @@ public abstract class Difference {
public static final int SUPERCLASS = 16;
public static final int USAGES = 32;
public static final int ANNOTATIONS = 64;
public static final int CONSTANT_REFERENCES = 128;
public interface Specifier<T, D extends Difference> {
Collection<T> added();
@@ -24,7 +24,6 @@ import java.io.IOException;
import java.io.PrintStream;
import java.lang.annotation.RetentionPolicy;
import java.util.*;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.function.Supplier;
@@ -33,6 +32,8 @@ import java.util.function.Supplier;
*/
public class Mappings {
private final static Logger LOG = Logger.getInstance(Mappings.class);
public static final String PROCESS_CONSTANTS_NON_INCREMENTAL_PROPERTY = "compiler.process.constants.non.incremental";
private final boolean myProcessConstantsIncrementally = !Boolean.valueOf(System.getProperty(PROCESS_CONSTANTS_NON_INCREMENTAL_PROPERTY, "false"));
private final static String CLASS_TO_SUBCLASSES = "classToSubclasses.tab";
private final static String CLASS_TO_CLASS = "classToClass.tab";
@@ -1012,7 +1013,7 @@ public class Mappings {
}
private class Differential {
private static final int DESPERATE_MASK = Opcodes.ACC_FINAL;
private static final int INLINABLE_FIELD_MODIFIERS_MASK = Opcodes.ACC_FINAL;
final Mappings myDelta;
final Collection<? extends File> myFilesToCompile;
@@ -1021,8 +1022,6 @@ public class Mappings {
final Collection<? super File> myAffectedFiles;
@Nullable
final DependentFilesFilter myFilter;
@Nullable final Callbacks.ConstantAffectionResolver myConstantSearch;
final DelayedWorks myDelayedWorks;
final Util myFuture;
final Util myPresent;
@@ -1032,90 +1031,6 @@ public class Mappings {
private final Iterable<AnnotationsChangeTracker> myAnnotationChangeTracker =
JpsServiceManager.getInstance().getExtensions(AnnotationsChangeTracker.class);
private class DelayedWorks {
class Triple {
final int owner;
final FieldRepr field;
@Nullable
final Future<? extends Callbacks.ConstantAffection> affection;
private Triple(final int owner, final FieldRepr field, @Nullable final Future<? extends Callbacks.ConstantAffection> affection) {
this.owner = owner;
this.field = field;
this.affection = affection;
}
Callbacks.ConstantAffection getAffection() {
try {
return affection != null ? affection.get() : Callbacks.ConstantAffection.EMPTY;
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
}
final Collection<Triple> myQueue = new LinkedList<>();
void addConstantWork(final int ownerClass, final FieldRepr changedField, final boolean isRemoved, boolean accessChanged) {
final Future<Callbacks.ConstantAffection> future;
if (myConstantSearch == null) {
future = null;
}
else {
final String className = myContext.getValue(ownerClass);
final String fieldName = myContext.getValue(changedField.name);
future = myConstantSearch.request(className.replace('/', '.'), fieldName, changedField.access, isRemoved, accessChanged);
}
myQueue.add(new Triple(ownerClass, changedField, future));
}
boolean doWork(@NotNull final Collection<? super File> affectedFiles) {
if (!myQueue.isEmpty()) {
debug("Starting delayed works.");
for (final Triple t : myQueue) {
final Callbacks.ConstantAffection affection = t.getAffection();
debug("Class: ", t.owner);
debug("Field: ", t.field.name);
if (!affection.isKnown()) {
if (myConstantSearch != null) {
debug("No external dependency information available.");
}
else {
debug("Constant search service not available.");
}
debug("Trying to soften non-incremental decision.");
if (!incrementalDecision(t.owner, t.field, affectedFiles, myFilesToCompile, myFilter)) {
debug("No luck.");
debug("End of delayed work, returning false.");
return false;
}
}
else {
debug("External dependency information retrieved.");
final Collection<File> files = affection.getAffectedFiles();
if (myFilter == null) {
affectedFiles.addAll(files);
}
else {
for (File file : files) {
if (myFilter.accept(file)) {
affectedFiles.add(file);
}
}
}
}
}
debug("End of delayed work, returning true.");
}
return true;
}
}
private class FileClasses {
final File myFileName;
final Set<ClassRepr> myFileClasses = new THashSet<>();
@@ -1157,31 +1072,20 @@ public class Mappings {
this.myCompiledWithErrors = null;
this.myAffectedFiles = null;
this.myFilter = null;
this.myConstantSearch = null;
myDelayedWorks = null;
myFuture = null;
myPresent = null;
myEasyMode = true;
delta.myIsRebuild = true;
}
private Differential(final Mappings delta, final Collection<String> removed, final Collection<? extends File> filesToCompile) {
delta.myRemovedFiles = removed;
this.myDelta = delta;
this.myFilesToCompile = filesToCompile;
this.myCompiledFiles = null;
this.myCompiledWithErrors = null;
this.myAffectedFiles = null;
this.myFilter = null;
this.myConstantSearch = null;
myDelayedWorks = null;
myFuture = new Util(delta);
myPresent = new Util();
myEasyMode = true;
@@ -1193,8 +1097,7 @@ public class Mappings {
final Collection<? extends File> compiledWithErrors,
final Collection<? extends File> compiledFiles,
final Collection<? super File> affectedFiles,
@NotNull final DependentFilesFilter filter,
@Nullable final Callbacks.ConstantAffectionResolver constantSearch) {
@NotNull final DependentFilesFilter filter) {
delta.myRemovedFiles = removed;
this.myDelta = delta;
@@ -1203,9 +1106,6 @@ public class Mappings {
this.myCompiledWithErrors = compiledWithErrors;
this.myAffectedFiles = affectedFiles;
this.myFilter = filter;
this.myConstantSearch = constantSearch;
myDelayedWorks = new DelayedWorks();
myFuture = new Util(delta);
myPresent = new Util();
@@ -1755,17 +1655,11 @@ public class Mappings {
for (final FieldRepr f : removed) {
debug("Field: ", f.name);
if (!f.isPrivate() && (f.access & DESPERATE_MASK) == DESPERATE_MASK && f.hasValue()) {
if (!myProcessConstantsIncrementally && !f.isPrivate() && (f.access & INLINABLE_FIELD_MODIFIERS_MASK) == INLINABLE_FIELD_MODIFIERS_MASK && f.hasValue()) {
debug("Field had value and was (non-private) final static => a switch to non-incremental mode requested");
if (myConstantSearch != null) {
assert myDelayedWorks != null;
myDelayedWorks.addConstantWork(it.name, f, true, false);
}
else {
if (!incrementalDecision(it.name, f, myAffectedFiles, myFilesToCompile, myFilter)) {
debug("End of Differentiate, returning false");
return false;
}
if (!incrementalDecision(it.name, f, myAffectedFiles, myFilesToCompile, myFilter)) {
debug("End of Differentiate, returning false");
return false;
}
}
@@ -1788,6 +1682,7 @@ public class Mappings {
}
debug("Processing changed fields:");
assert myFuture != null;
assert myPresent != null;
for (final Pair<FieldRepr, Difference> f : changed) {
final Difference d = f.second;
@@ -1795,8 +1690,10 @@ public class Mappings {
debug("Field: ", field.name);
final Supplier<TIntHashSet> propagated = lazy(()-> myFuture.propagateFieldAccess(field.name, it.name));
// only if the field was a compile-time constant
if (!field.isPrivate() && (field.access & DESPERATE_MASK) == DESPERATE_MASK && d.hadValue()) {
if (!field.isPrivate() && (field.access & INLINABLE_FIELD_MODIFIERS_MASK) == INLINABLE_FIELD_MODIFIERS_MASK && d.hadValue()) {
final int changedModifiers = d.addedModifiers() | d.removedModifiers();
final boolean harmful = (changedModifiers & (Opcodes.ACC_STATIC | Opcodes.ACC_FINAL)) != 0;
final boolean accessChanged = (changedModifiers & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED)) != 0;
@@ -1804,12 +1701,12 @@ public class Mappings {
final boolean valueChanged = (d.base() & Difference.VALUE) != 0;
if (harmful || valueChanged || becameLessAccessible) {
debug("Inline field changed it's access or value => a switch to non-incremental mode requested");
if (myConstantSearch != null) {
assert myDelayedWorks != null;
myDelayedWorks.addConstantWork(it.name, field, false, accessChanged);
if (myProcessConstantsIncrementally) {
debug("Potentially inlined field changed it's access or value => affecting field usages");
myFuture.affectFieldUsages(field, propagated.get(), field.createUsage(myContext, it.name), state.myAffectedUsages, state.myDependants);
}
else {
debug("Potentially inlined field changed it's access or value => a switch to non-incremental mode requested");
if (!incrementalDecision(it.name, field, myAffectedFiles, myFilesToCompile, myFilter)) {
debug("End of Differentiate, returning false");
return false;
@@ -1819,7 +1716,6 @@ public class Mappings {
}
if (d.base() != Difference.NONE) {
final Supplier<TIntHashSet> propagated = lazy(()-> myFuture.propagateFieldAccess(field.name, it.name));
if ((d.base() & Difference.TYPE) != 0 || (d.base() & Difference.SIGNATURE) != 0) {
debug("Type or signature changed --- affecting field usages");
@@ -2289,7 +2185,7 @@ public class Mappings {
}
private void processDependentFile(int depClass, @NotNull File depFile) {
if (myAffectedFiles.contains(depFile) || myCompiledFiles.contains(depFile)) {
if (myAffectedFiles.contains(depFile)) {
return;
}
@@ -2299,7 +2195,12 @@ public class Mappings {
if (repr == null) {
return;
}
if (repr instanceof ClassRepr && !((ClassRepr)repr).hasInlinedConstants() && myCompiledFiles.contains(depFile)) {
// Classes containing inlined constants from other classes and compiled against older constant values
// may need to be recompiled several times within a compile session.
// Otherwise it is safe to skip the file if it has already been compiled in this session.
return;
}
final Set<UsageRepr.Usage> depUsages = repr.getUsages();
if (depUsages == null || depUsages.isEmpty()) {
return;
@@ -2432,7 +2333,7 @@ public class Mappings {
return false;
}
assert myAffectedFiles != null;
assert myDelayedWorks != null;
//assert myDelayedWorks != null;
final Collection<String> removed = myDelta.myRemovedFiles;
if (removed != null) {
@@ -2440,7 +2341,7 @@ public class Mappings {
myAffectedFiles.remove(new File(r));
}
}
return myDelayedWorks.doWork(myAffectedFiles);
return true/*myDelayedWorks.doWork(myAffectedFiles)*/;
}
finally {
if (myFilesToCompile != null) {
@@ -2587,7 +2488,7 @@ public class Mappings {
final Collection<? super File> affectedFiles,
@NotNull final DependentFilesFilter filter,
@Nullable final Callbacks.ConstantAffectionResolver constantSearch) {
return new Differential(delta, removed, filesToCompile, compiledWithErrors, compiledFiles, affectedFiles, filter, constantSearch).differentiate();
return new Differential(delta, removed, filesToCompile, compiledWithErrors, compiledFiles, affectedFiles, filter).differentiate();
}
private void cleanupBackDependency(final int className, @Nullable Set<? extends UsageRepr.Usage> usages, final IntIntMultiMaplet buffer) {
@@ -2854,6 +2755,9 @@ public class Mappings {
public Callbacks.Backend getCallback() {
return new Callbacks.Backend() {
// className -> {imports; static_imports}
private final Map<String, Pair<Collection<String>, Collection<String>>> myImportRefs = Collections.synchronizedMap(new HashMap<>());
private final Map<String, Collection<Callbacks.ConstantRef>> myConstantRefs = Collections.synchronizedMap(new HashMap<>());
@Override
public void associate(String classFileName, Collection<String> sources, ClassReader cr) {
@@ -2864,6 +2768,20 @@ public class Mappings {
// since java9 'repr' can represent either a class or a compiled module-info.java
final int className = result.name;
if (result instanceof ClassRepr) {
final ClassRepr classRepr = (ClassRepr)result;
final String classNameStr = myContext.getValue(className);
if (addConstantUsages(classRepr, myConstantRefs.remove(classNameStr))) {
// Important: should register constants before imports, because imports can produce additional
// field references too and addConstantUsages may return false in this case
classRepr.setHasInlinedConstants(true);
}
final Pair<Collection<String>, Collection<String>> imports = myImportRefs.remove(classNameStr);
if (imports != null) {
addImportUsages(classRepr, imports.getFirst(), imports.getSecond());
}
}
for (String sourceFileName : sources) {
String relative = myRelativizer.toRelative(sourceFileName);
myClassToRelativeSourceFilePath.put(className, relative);
@@ -2893,47 +2811,61 @@ public class Mappings {
@Override
public void registerImports(String className, Collection<String> classImports, Collection<String> staticImports) {
final String key = className.replace('.', '/');
if (!classImports.isEmpty() || !staticImports.isEmpty()) {
myPostPasses.offer(() -> {
final int rootClassName = myContext.get(className.replace(".", "/"));
final Collection<File> files = classToSourceFileGet(rootClassName);
final ClassRepr repr = files != null && !files.isEmpty()? getClassReprByName(files.iterator().next(), rootClassName) : null;
boolean usageAdded = false;
for (final String anImport : classImports) {
if (!anImport.endsWith(IMPORT_WILDCARD_SUFFIX)) {
final int iname = myContext.get(anImport.replace('.', '/'));
myClassToClassDependency.put(iname, rootClassName);
usageAdded |= repr != null && repr.addUsage(UsageRepr.createClassUsage(myContext, iname));
}
}
for (String anImport : staticImports) {
if (anImport.endsWith(IMPORT_WILDCARD_SUFFIX)) {
final int iname = myContext.get(anImport.substring(0, anImport.length() - IMPORT_WILDCARD_SUFFIX.length()).replace('.', '/'));
myClassToClassDependency.put(iname, rootClassName);
usageAdded |= repr != null && repr.addUsage(UsageRepr.createClassUsage(myContext, iname));
usageAdded |= repr != null && repr.addUsage(UsageRepr.createImportStaticOnDemandUsage(myContext, iname));
}
else {
final int i = anImport.lastIndexOf('.');
if (i > 0 && i < anImport.length() - 1) {
final int iname = myContext.get(anImport.substring(0, i).replace('.', '/'));
final int memberName = myContext.get(anImport.substring(i+1));
myClassToClassDependency.put(iname, rootClassName);
usageAdded |= repr != null && repr.addUsage(UsageRepr.createClassUsage(myContext, iname));
usageAdded |= repr != null && repr.addUsage(UsageRepr.createImportStaticMemberUsage(myContext, memberName, iname));
}
}
}
if (usageAdded) {
for (File file : files) {
myRelativeSourceFilePathToClasses.put(toRelative(file), repr);
}
}
});
myImportRefs.put(key, Pair.create(classImports, staticImports));
}
else {
myImportRefs.remove(key);
}
}
@Override
public void registerConstantReferences(String className, Collection<Callbacks.ConstantRef> cRefs) {
final String key = className.replace('.', '/');
if (!cRefs.isEmpty()) {
myConstantRefs.put(key, cRefs);
}
else {
myConstantRefs.remove(key);
}
}
private void addImportUsages(ClassRepr repr, Collection<String> classImports, Collection<String> staticImports) {
for (final String anImport : classImports) {
if (!anImport.endsWith(IMPORT_WILDCARD_SUFFIX)) {
repr.addUsage(UsageRepr.createClassUsage(myContext, myContext.get(anImport.replace('.', '/'))));
}
}
for (String anImport : staticImports) {
if (anImport.endsWith(IMPORT_WILDCARD_SUFFIX)) {
final int iname = myContext.get(anImport.substring(0, anImport.length() - IMPORT_WILDCARD_SUFFIX.length()).replace('.', '/'));
repr.addUsage(UsageRepr.createClassUsage(myContext, iname));
repr.addUsage(UsageRepr.createImportStaticOnDemandUsage(myContext, iname));
}
else {
final int i = anImport.lastIndexOf('.');
if (i > 0 && i < anImport.length() - 1) {
final int iname = myContext.get(anImport.substring(0, i).replace('.', '/'));
final int memberName = myContext.get(anImport.substring(i+1));
repr.addUsage(UsageRepr.createClassUsage(myContext, iname));
repr.addUsage(UsageRepr.createImportStaticMemberUsage(myContext, memberName, iname));
}
}
}
}
private boolean addConstantUsages(ClassRepr repr, Collection<Callbacks.ConstantRef> cRefs) {
boolean addedNewUsages = false;
if (cRefs != null) {
for (Callbacks.ConstantRef ref : cRefs) {
final int owner = myContext.get(ref.getOwner().replace('.', '/'));
if (repr.name != owner) {
addedNewUsages |= repr.addUsage(UsageRepr.createFieldUsage(myContext, myContext.get(ref.getName()), owner, myContext.get(ref.getDescriptor())));
}
}
}
return addedNewUsages;
}
};
}
@@ -59,7 +59,7 @@ final class BuildSession implements Runnable, CanceledStatus {
private volatile long myLastEventOrdinal;
private volatile ProjectDescriptor myProjectDescriptor;
private final Map<Pair<String, String>, ConstantSearchFuture> mySearchTasks = Collections.synchronizedMap(new HashMap<>());
private final ConstantSearch myConstantSearch = new ConstantSearch();
private final ConstantSearch myConstantSearch = null/*new ConstantSearch()*/;
@NotNull
private final BuildRunner myBuildRunner;
private final boolean myForceModelLoading;
@@ -1,73 +0,0 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.jps.incremental.java;
import gnu.trove.TObjectIntHashMap;
import org.jetbrains.jps.builders.java.JavaBuilderUtil;
import org.jetbrains.jps.builders.java.dependencyView.Callbacks;
import org.jetbrains.jps.incremental.CompileContext;
import org.jetbrains.jps.javac.ast.api.JavacDef;
import org.jetbrains.jps.javac.ast.api.JavacFileReferencesRegistrar;
import org.jetbrains.jps.javac.ast.api.JavacRef;
import org.jetbrains.jps.javac.ast.api.JavacTypeCast;
import java.util.*;
public class ImportReferencesRegistrar implements JavacFileReferencesRegistrar {
@Override
public void initialize() {
}
@Override
public boolean isEnabled() {
return true;
}
@Override
public boolean onlyImports() {
return true;
}
@Override
public void registerFile(CompileContext context,
String filePath,
TObjectIntHashMap<? extends JavacRef> refs,
Collection<? extends JavacDef> defs,
Collection<? extends JavacTypeCast> casts,
Collection<? extends JavacRef> implicitToString) {
if (refs.isEmpty() || defs.isEmpty()) {
return;
}
final Set<String> classImports = new HashSet<>();
final Set<String> staticImports = new HashSet<>();
for (Object key : refs.keys()) {
final JavacRef ref = (JavacRef)key;
if (ref instanceof JavacRef.JavacClass) {
classImports.add(ref.getName());
final JavacRef.ImportProperties props = ref.getImportProperties();
if (props != null && props.isStatic() && props.isOnDemand()) {
staticImports.add(ref.getName() + ".*");
}
}
else {
if (ref instanceof JavacRef.JavacField || ref instanceof JavacRef.JavacMethod) {
staticImports.add(ref.getOwnerName() + "." + ref.getName());
}
}
}
final List<String> definedClasses = new ArrayList<>();
for (JavacDef def : defs) {
if (def instanceof JavacDef.JavacClassDef) {
final JavacRef element = def.getDefinedElement();
if (element instanceof JavacRef.JavacClass) {
definedClasses.add(element.getName());
}
}
}
if (!definedClasses.isEmpty() && (!classImports.isEmpty() || !staticImports.isEmpty())) {
final Callbacks.Backend deps = JavaBuilderUtil.getDependenciesRegistrar(context);
for (String aClass : definedClasses) {
deps.registerImports(aClass, classImports, staticImports);
}
}
}
}
@@ -1139,7 +1139,7 @@ public class JavaBuilder extends ModuleLevelBuilder {
@Override
public void registerJavacFileData(JavacFileData data) {
for (JavacFileReferencesRegistrar registrar : myRegistrars) {
registrar.registerFile(myContext, data.getFilePath(), registrar.onlyImports() ? data.getImportRefs() : data.getRefs(), data.getDefs(), data.getCasts(), data.getImplicitToStringRefs());
registrar.registerFile(myContext, data.getFilePath(), data.getRefs(), data.getDefs(), data.getCasts(), data.getImplicitToStringRefs());
}
}
@@ -0,0 +1,98 @@
// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.jps.incremental.java;
import gnu.trove.TObjectIntHashMap;
import org.jetbrains.jps.builders.java.JavaBuilderUtil;
import org.jetbrains.jps.builders.java.dependencyView.Callbacks;
import org.jetbrains.jps.incremental.CompileContext;
import org.jetbrains.jps.javac.ast.api.JavacDef;
import org.jetbrains.jps.javac.ast.api.JavacFileReferencesRegistrar;
import org.jetbrains.jps.javac.ast.api.JavacRef;
import org.jetbrains.jps.javac.ast.api.JavacTypeCast;
import javax.lang.model.element.Modifier;
import java.util.*;
/**
* register dependencies that are not discoverable from bytecode:
* - references caused by import statements
* - references to fields initialized with compile-time constant values. Such values can be inlined into referencing bytecode
*/
public class JpsReferenceDependenciesRegistrar implements JavacFileReferencesRegistrar {
@Override
public void initialize() {
}
@Override
public boolean isEnabled() {
return true;
}
@Override
public void registerFile(CompileContext context,
String filePath,
TObjectIntHashMap<? extends JavacRef> refs,
Collection<? extends JavacDef> defs,
Collection<? extends JavacTypeCast> casts,
Collection<? extends JavacRef> implicitToString) {
final Set<String> definedClasses = new HashSet<>();
for (JavacDef def : defs) {
if (def instanceof JavacDef.JavacClassDef) {
final JavacRef element = def.getDefinedElement();
if (element instanceof JavacRef.JavacClass) {
definedClasses.add(element.getName());
}
}
}
if (definedClasses.isEmpty()) {
return;
}
if (!refs.isEmpty()) {
final Set<String> classImports = new HashSet<>();
final Set<String> staticImports = new HashSet<>();
final Map<String, List<Callbacks.ConstantRef>> cRefs = new HashMap<>();
refs.forEachKey(ref -> {
final JavacRef.ImportProperties importProps = ref.getImportProperties();
if (importProps != null) { // the reference comes from import list
if (ref instanceof JavacRef.JavacClass) {
classImports.add(ref.getName());
if (importProps.isStatic() && importProps.isOnDemand()) {
staticImports.add(ref.getName() + ".*");
}
}
else {
if (ref instanceof JavacRef.JavacField || ref instanceof JavacRef.JavacMethod) {
staticImports.add(ref.getOwnerName() + "." + ref.getName());
}
}
}
else if (ref instanceof JavacRef.JavacField && ref.getModifiers().contains(Modifier.FINAL)) {
final JavacRef.JavacField fieldRef = (JavacRef.JavacField)ref;
final String descriptor = fieldRef.getDescriptor();
if (descriptor != null && definedClasses.contains(fieldRef.getContainingClass()) && !definedClasses.contains(fieldRef.getOwnerName())) {
List<Callbacks.ConstantRef> refsList = cRefs.get(fieldRef.getContainingClass());
if (refsList == null) {
refsList = new ArrayList<>();
cRefs.put(fieldRef.getContainingClass(), refsList);
}
refsList.add(Callbacks.createConstantReference(fieldRef.getOwnerName(), fieldRef.getName(), descriptor));
}
}
return true;
});
if (!classImports.isEmpty() || !staticImports.isEmpty()) {
final Callbacks.Backend reg = JavaBuilderUtil.getDependenciesRegistrar(context);
for (String aClass : definedClasses) {
reg.registerImports(aClass, classImports, staticImports);
}
}
if (!cRefs.isEmpty()) {
final Callbacks.Backend reg = JavaBuilderUtil.getDependenciesRegistrar(context);
for (String aClass : definedClasses) {
final List<Callbacks.ConstantRef> classCRefs = cRefs.get(aClass);
reg.registerConstantReferences(aClass, classCRefs != null? classCRefs : Collections.emptyList());
}
}
}
}
}
@@ -28,15 +28,4 @@ public class InProcessRefCollectorCompilerToolExtension extends AbstractRefColle
}
}
@Override
protected boolean divideImportsRefs() {
if (hasServiceManager()) {
for (JavacFileReferencesRegistrar registrar : JpsServiceManager.getInstance().getExtensions(JavacFileReferencesRegistrar.class)) {
if (registrar.isEnabled() && registrar.onlyImports()) {
return true;
}
}
}
return false;
}
}
@@ -22,52 +22,25 @@ import org.jetbrains.jps.javac.ast.api.JavacFileReferencesRegistrar;
import org.jetbrains.jps.model.java.compiler.JavaCompilers;
import org.jetbrains.jps.service.JpsServiceManager;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Collections;
public class ReferenceCollectorExternalJavacOptionsProvider implements ExternalJavacOptionsProvider {
@NotNull
@Override
public Collection<String> getOptions(@NotNull JavaCompilingTool tool) {
List<String> options = new ArrayList<>(2);
if (tool.getId().equals(JavaCompilers.JAVAC_ID)) {
final JavacReferenceCollectorOptions
javacReferenceCollectorOptions = new JavacReferenceCollectorOptions();
options.add("-D" + ExternalRefCollectorCompilerToolExtension.ENABLED_PARAM + "=" + javacReferenceCollectorOptions.isEnabled());
if (javacReferenceCollectorOptions.isEnabled() && javacReferenceCollectorOptions.isImportRefsDivided()) {
options.add("-D" + ExternalRefCollectorCompilerToolExtension.DIVIDE_IMPORTS_PARAM + "=true");
}
return Collections.singletonList("-D" + ExternalRefCollectorCompilerToolExtension.ENABLED_PARAM + "=" + isEnabled());
}
return options;
return Collections.emptyList();
}
private static class JavacReferenceCollectorOptions {
private final boolean myEnabled;
private final boolean myDivideImportRefs;
private JavacReferenceCollectorOptions() {
boolean enabled = false;
boolean divideImportRefs = false;
for (JavacFileReferencesRegistrar listener : JpsServiceManager.getInstance().getExtensions(JavacFileReferencesRegistrar.class)) {
if (listener.isEnabled()) {
enabled = true;
if (listener.onlyImports()) {
divideImportRefs = true;
}
}
private static boolean isEnabled() {
for (JavacFileReferencesRegistrar listener : JpsServiceManager.getInstance().getExtensions(JavacFileReferencesRegistrar.class)) {
if (listener.isEnabled()) {
return true;
}
myEnabled = enabled;
myDivideImportRefs = divideImportRefs;
}
public boolean isEnabled() {
return myEnabled;
}
public boolean isImportRefsDivided() {
return myDivideImportRefs;
}
return false;
}
}
@@ -11,8 +11,6 @@ public interface JavacFileReferencesRegistrar {
boolean isEnabled();
boolean onlyImports();
void registerFile(CompileContext context,
String filePath,
TObjectIntHashMap<? extends JavacRef> refs,
@@ -15,6 +15,7 @@
*/
package org.jetbrains.ether;
import org.jetbrains.jps.builders.java.dependencyView.Mappings;
import org.jetbrains.jps.incremental.storage.ProjectStamps;
import org.jetbrains.jps.model.JpsDummyElement;
import org.jetbrains.jps.model.JpsModuleRootModificationUtil;
@@ -174,8 +175,8 @@ public class CommonTest extends IncrementalTestCase {
doTest();
}
public void testIntegrateOnNonIncrementalMake() {
doTest();
public void testIntegrateOnNonIncrementalMake() throws Exception {
executeWithSystemProperty(Mappings.PROCESS_CONSTANTS_NON_INCREMENTAL_PROPERTY, String.valueOf(true), () -> doTest());
}
public void testNothingChanged() {
@@ -40,6 +40,7 @@ import org.jetbrains.jps.util.JpsPathUtil;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.Callable;
/**
* @author db
@@ -134,6 +135,22 @@ public abstract class IncrementalTestCase extends JpsBuildTestCase {
return new File(workDir, StringUtil.trimEnd(path, suffix));
}
public <T> T executeWithSystemProperty(String propName, String propValue, Callable<T> action) throws Exception {
final String oldValue = System.getProperty(propName);
try {
System.setProperty(propName, propValue);
return action.call();
}
finally {
if (oldValue != null) {
System.setProperty(propName, oldValue);
}
else {
System.clearProperty(propName);
}
}
}
public BuildResult doTest() {
setupInitialProject();
@@ -17,6 +17,7 @@ package org.jetbrains.ether;
import org.jetbrains.jps.builders.CompileScopeTestBuilder;
import org.jetbrains.jps.builders.java.JavaModuleBuildTargetType;
import org.jetbrains.jps.builders.java.dependencyView.Mappings;
import org.jetbrains.jps.model.JpsDummyElement;
import org.jetbrains.jps.model.JpsModuleRootModificationUtil;
import org.jetbrains.jps.model.java.JpsJavaDependencyScope;
@@ -97,7 +98,7 @@ public class MarkDirtyTest extends IncrementalTestCase {
doTestBuild(1).assertSuccessful();
}
public void testTransitiveRecompile() {
public void testTransitiveRecompile() throws Exception {
JpsModule module = addModule();
addTestRoot(module, "testSrc");
JpsModule util = addModule("util", "util/src");
@@ -106,7 +107,7 @@ public class MarkDirtyTest extends IncrementalTestCase {
JpsModule lib = addModule("lib", "lib/src");
addTestRoot(lib, "lib/testSrc");
JpsModuleRootModificationUtil.addDependency(util, lib);
doTestBuild(1).assertSuccessful();
executeWithSystemProperty(Mappings.PROCESS_CONSTANTS_NON_INCREMENTAL_PROPERTY, String.valueOf(true), () -> doTestBuild(1)).assertSuccessful();
}
public void testRecompileTwinDependencies() {