mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-04-30 18:28:55 +07:00
JPS mappings for incremental compilation refactoring: add usages implementation, bytecode parsing, diff implementation
GitOrigin-RevId: 4d48d46b9f3906ac2356db9a2b133a1e1ad01cbb
This commit is contained in:
committed by
intellij-monorepo-bot
parent
bc39cf7eb4
commit
8c7e3a6aa8
@@ -1,6 +1,8 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.jps.dependency;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public interface DifferentiateContext {
|
||||
|
||||
/**
|
||||
@@ -15,5 +17,7 @@ public interface DifferentiateContext {
|
||||
|
||||
void affectUsage(Usage usage);
|
||||
|
||||
void affectUsage(Predicate<Usage> usageQuery);
|
||||
|
||||
void affectNodeSource(NodeSource source);
|
||||
}
|
||||
|
||||
@@ -5,23 +5,10 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jps.dependency.diff.DiffCapable;
|
||||
import org.jetbrains.jps.dependency.diff.Difference;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public interface Node<T extends Node<T, D>, D extends Difference> extends DiffCapable<T, D>, SerializableGraphElement {
|
||||
|
||||
@NotNull
|
||||
ReferenceID getReferenceID();
|
||||
|
||||
Iterable<Usage> getUsages();
|
||||
|
||||
default boolean containsAny(Set<Usage> usages) {
|
||||
if (!usages.isEmpty()) {
|
||||
for (Usage usage : getUsages()) {
|
||||
if (usages.contains(usage)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,5 +6,35 @@ public interface DiffCapable<T extends DiffCapable<T, D>, D extends Difference>
|
||||
|
||||
int diffHashCode();
|
||||
|
||||
D difference(T other);
|
||||
D difference(T past);
|
||||
|
||||
|
||||
interface Adapter<T> extends DiffCapable<Adapter<T>, Difference>{
|
||||
T getValue();
|
||||
}
|
||||
|
||||
static <T> Adapter<T> wrap(T value) {
|
||||
return new Adapter<>() {
|
||||
@Override
|
||||
public T getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSame(DiffCapable<?, ?> other) {
|
||||
return other instanceof Adapter && value.equals(((Adapter<?>)other).getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int diffHashCode() {
|
||||
return value.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Difference difference(Adapter past) {
|
||||
return () -> value.equals(past.getValue());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -59,7 +59,29 @@ public interface Difference {
|
||||
}
|
||||
}
|
||||
|
||||
static <T extends DiffCapable<T, D>, D extends Difference> Specifier<T, D> make(@Nullable Iterable<T> past, @Nullable Iterable<T> now) {
|
||||
static <V> Specifier<V, ?> diff(@Nullable Iterable<V> past, @Nullable Iterable<V> now) {
|
||||
var _past = Iterators.map(past, v -> DiffCapable.wrap(v));
|
||||
var _now = Iterators.map(now, v -> DiffCapable.wrap(v));
|
||||
var diff = deepDiff(_past, _now);
|
||||
return new Specifier<>() {
|
||||
@Override
|
||||
public Iterable<V> added() {
|
||||
return Iterators.map(diff.added(), adapter -> adapter.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<V> removed() {
|
||||
return Iterators.map(diff.removed(), adapter -> adapter.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Change<V, Difference>> changed() {
|
||||
return Iterators.map(diff.changed(), c -> Change.create(c.getPast().getValue(), c.getNow().getValue(), c.getDiff()));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
static <T extends DiffCapable<T, D>, D extends Difference> Specifier<T, D> deepDiff(@Nullable Iterable<T> past, @Nullable Iterable<T> now) {
|
||||
if (Iterators.isEmpty(past)) {
|
||||
if (Iterators.isEmpty(now)) {
|
||||
return new Specifier<>() {
|
||||
@@ -151,6 +173,5 @@ public interface Difference {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class DependencyGraphImpl extends GraphImpl implements DependencyGraph {
|
||||
private List<DifferentiateStrategy> myDifferentiateStrategies = new ArrayList<>(); // todo: fill the list
|
||||
@@ -32,6 +33,7 @@ public class DependencyGraphImpl extends GraphImpl implements DependencyGraph {
|
||||
|
||||
var diffContext = new DifferentiateContext() {
|
||||
final Set<Usage> affectedUsages = new HashSet<>();
|
||||
final Set<Predicate<Usage>> usageQueries = new HashSet<>();
|
||||
final Set<NodeSource> affectedSources = new HashSet<>();
|
||||
|
||||
@Override
|
||||
@@ -49,10 +51,29 @@ public class DependencyGraphImpl extends GraphImpl implements DependencyGraph {
|
||||
affectedUsages.add(usage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void affectUsage(Predicate<Usage> usageQuery) {
|
||||
usageQueries.add(usageQuery);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void affectNodeSource(NodeSource source) {
|
||||
affectedSources.add(source);
|
||||
}
|
||||
|
||||
boolean isNodeAffected(Node<?, ?> node) {
|
||||
for (Usage usage : node.getUsages()) {
|
||||
if (affectedUsages.contains(usage)) {
|
||||
return true;
|
||||
}
|
||||
for (Predicate<Usage> query : usageQueries) {
|
||||
if (query.test(usage)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
for (DifferentiateStrategy diffStrategy : myDifferentiateStrategies) {
|
||||
@@ -73,7 +94,7 @@ public class DependencyGraphImpl extends GraphImpl implements DependencyGraph {
|
||||
for (NodeSource depSrc : getSources(dependent)) {
|
||||
if (!affectedSources.contains(depSrc)) {
|
||||
for (var depNode : getNodes(depSrc)) {
|
||||
if (depNode.containsAny(diffContext.affectedUsages)) {
|
||||
if (diffContext.isNodeAffected(depNode)) {
|
||||
affectedSources.add(depSrc);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.jps.dependency.impl;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jps.dependency.NodeSource;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class FileSource implements NodeSource {
|
||||
|
||||
private final Path myPath;
|
||||
|
||||
public FileSource(@NotNull File file) {
|
||||
this(file.toPath());
|
||||
}
|
||||
|
||||
public FileSource(@NotNull Path path) {
|
||||
myPath = path;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path getPath() {
|
||||
return myPath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final FileSource that = (FileSource)o;
|
||||
|
||||
if (!myPath.equals(that.myPath)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return myPath.hashCode();
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,10 @@ public final class StringReferenceID implements ReferenceID {
|
||||
myValue = value;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return myValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.jps.dependency.java;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jps.dependency.Usage;
|
||||
import org.jetbrains.jps.dependency.diff.Difference;
|
||||
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
public class AnnotationType extends ObjectType<AnnotationType, AnnotationType.Diff> {
|
||||
|
||||
private final Iterable<ElemType> myAnnotationTargets;
|
||||
private final RetentionPolicy myRetentionPolicy;
|
||||
|
||||
public AnnotationType(JVMFlags flags, String signature, String name, String outFilePath,
|
||||
@NotNull Iterable<TypeRepr.ClassType> annotations,
|
||||
@NotNull Iterable<Field> fields,
|
||||
@NotNull Iterable<Method> methods,
|
||||
@NotNull Iterable<Usage> usages,
|
||||
@NotNull Iterable<ElemType> annotationTargets,
|
||||
@NotNull RetentionPolicy retentionPolicy
|
||||
) {
|
||||
super(flags, signature, name, outFilePath, fields, methods, annotations, usages);
|
||||
myAnnotationTargets = annotationTargets;
|
||||
myRetentionPolicy = retentionPolicy;
|
||||
}
|
||||
|
||||
public Iterable<ElemType> getAnnotationTargets() {
|
||||
return myAnnotationTargets;
|
||||
}
|
||||
|
||||
public RetentionPolicy getRetentionPolicy() {
|
||||
return myRetentionPolicy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Diff difference(AnnotationType other) {
|
||||
return new Diff(other);
|
||||
}
|
||||
|
||||
public static class Diff implements Difference {
|
||||
|
||||
public Diff(AnnotationType other) {
|
||||
// todo: diff necessary data
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean unchanged() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.jps.dependency.java;
|
||||
|
||||
import org.jetbrains.jps.dependency.impl.StringReferenceID;
|
||||
|
||||
public class AnnotationUsage extends JvmElementUsage {
|
||||
|
||||
private final TypeRepr.ClassType myClassType;
|
||||
private final Iterable<String> myUserArgNames;
|
||||
private final Iterable<ElemType> myTargets;
|
||||
|
||||
public AnnotationUsage(TypeRepr.ClassType classType, Iterable<String> userArgNames, Iterable<ElemType> targets) {
|
||||
super(new StringReferenceID(classType.getJvmName()));
|
||||
myClassType = classType;
|
||||
myUserArgNames = userArgNames;
|
||||
myTargets = targets;
|
||||
}
|
||||
|
||||
public TypeRepr.ClassType getClassType() {
|
||||
return myClassType;
|
||||
}
|
||||
|
||||
public Iterable<String> getUserArgNames() {
|
||||
return myUserArgNames;
|
||||
}
|
||||
|
||||
public Iterable<ElemType> getTargets() {
|
||||
return myTargets;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final AnnotationUsage that = (AnnotationUsage)o;
|
||||
|
||||
if (!myClassType.equals(that.myClassType)) {
|
||||
return false;
|
||||
}
|
||||
if (!myUserArgNames.equals(that.myUserArgNames)) {
|
||||
return false;
|
||||
}
|
||||
if (!myTargets.equals(that.myTargets)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = myClassType.hashCode();
|
||||
result = 31 * result + myUserArgNames.hashCode();
|
||||
result = 31 * result + myTargets.hashCode();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.jps.dependency.java;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class ClassAsGenericBoundUsage extends ClassUsage {
|
||||
|
||||
public ClassAsGenericBoundUsage(@NotNull String className) {
|
||||
super(className);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return super.hashCode() + 3;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.jps.dependency.java;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class ClassExtendsUsage extends ClassUsage {
|
||||
|
||||
public ClassExtendsUsage(@NotNull String className) {
|
||||
super(className);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return super.hashCode() + 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.jps.dependency.java;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class ClassNewUsage extends ClassUsage {
|
||||
|
||||
public ClassNewUsage(@NotNull String className) {
|
||||
super(className);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return super.hashCode() + 2;
|
||||
}
|
||||
}
|
||||
@@ -2,21 +2,16 @@
|
||||
package org.jetbrains.jps.dependency.java;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jps.dependency.ReferenceID;
|
||||
import org.jetbrains.jps.dependency.Usage;
|
||||
import org.jetbrains.jps.dependency.impl.StringReferenceID;
|
||||
|
||||
public class ClassUsage implements Usage {
|
||||
public class ClassUsage extends JvmElementUsage {
|
||||
|
||||
@NotNull
|
||||
private final ReferenceID myClassNode;
|
||||
|
||||
public ClassUsage(@NotNull ReferenceID classNode) {
|
||||
myClassNode = classNode;
|
||||
public ClassUsage(@NotNull String className) {
|
||||
super(new StringReferenceID(className));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ReferenceID getElementOwner() {
|
||||
return myClassNode;
|
||||
public String getClassName() {
|
||||
return ((StringReferenceID)getElementOwner()).getValue();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.jps.dependency.java;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jps.dependency.diff.DiffCapable;
|
||||
import org.jetbrains.jps.dependency.diff.Difference;
|
||||
|
||||
public class Field extends ProtoMember implements DiffCapable<Field, Field.Diff> {
|
||||
|
||||
public Field(JVMFlags flags, String signature, String name, String descriptor, @NotNull Iterable<TypeRepr.ClassType> annotations, Object value) {
|
||||
super(flags, signature, name, TypeRepr.getType(descriptor), annotations, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSame(DiffCapable<?, ?> other) {
|
||||
return other instanceof Field && getName().equals(((Field)other).getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int diffHashCode() {
|
||||
return getName().hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Field.Diff difference(Field other) {
|
||||
return new Diff(other);
|
||||
}
|
||||
|
||||
public static class Diff implements Difference {
|
||||
|
||||
public Diff(Field other) {
|
||||
// todo: diff necessary data
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean unchanged() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.jps.dependency.java;
|
||||
|
||||
public class FieldAssignUsage extends FieldUsage{
|
||||
|
||||
public FieldAssignUsage(String className, String name, String descriptor) {
|
||||
super(className, name, descriptor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return super.hashCode() + 2;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.jps.dependency.java;
|
||||
|
||||
public class FieldUsage extends MemberUsage{
|
||||
|
||||
private final String myDescriptor;
|
||||
|
||||
public FieldUsage(String className, String name, String descriptor) {
|
||||
super(className, name);
|
||||
myDescriptor = descriptor;
|
||||
}
|
||||
|
||||
public String getDescriptor() {
|
||||
return myDescriptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (!super.equals(o)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final FieldUsage that = (FieldUsage)o;
|
||||
|
||||
if (!myDescriptor.equals(that.myDescriptor)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = super.hashCode();
|
||||
result = 31 * result + myDescriptor.hashCode();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.jps.dependency.java;
|
||||
|
||||
public class ImportStaticMemberUsage extends MemberUsage{
|
||||
|
||||
public ImportStaticMemberUsage(String className, String name) {
|
||||
super(className, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return super.hashCode() + 1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.jps.dependency.java;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jps.dependency.impl.StringReferenceID;
|
||||
|
||||
public class ImportStaticOnDemandUsage extends JvmElementUsage {
|
||||
|
||||
public ImportStaticOnDemandUsage(@NotNull String importedClassName) {
|
||||
super(new StringReferenceID(importedClassName));
|
||||
}
|
||||
|
||||
public String getImportedClassName() {
|
||||
return ((StringReferenceID)getElementOwner()).getValue();
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,8 @@ package org.jetbrains.jps.dependency.java;
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes;
|
||||
|
||||
public final class JVMFlags {
|
||||
public static final JVMFlags EMPTY = new JVMFlags(0);
|
||||
|
||||
// using the highes 4th byte
|
||||
private static final int LOCAL_MASK = 0x1000000;
|
||||
private static final int ANON_MASK = 0x2000000;
|
||||
@@ -27,6 +29,14 @@ public final class JVMFlags {
|
||||
return new JVMFlags(myFlags | GENERATED_MASK);
|
||||
}
|
||||
|
||||
public JVMFlags deriveAdded(JVMFlags past) {
|
||||
return new JVMFlags(~past.myFlags & myFlags);
|
||||
}
|
||||
|
||||
public JVMFlags deriveRemoved(JVMFlags past) {
|
||||
return new JVMFlags(~myFlags & past.myFlags);
|
||||
}
|
||||
|
||||
// standard access flags
|
||||
public boolean isPublic() {
|
||||
return isSet(Opcodes.ACC_PUBLIC);
|
||||
@@ -150,4 +160,28 @@ public final class JVMFlags {
|
||||
private boolean isSet(int mask) {
|
||||
return (myFlags & mask) != 0;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final JVMFlags jvmFlags = (JVMFlags)o;
|
||||
|
||||
if (myFlags != jvmFlags.myFlags) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return myFlags;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.jps.dependency.java;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jps.dependency.Usage;
|
||||
import org.jetbrains.jps.dependency.diff.Difference;
|
||||
|
||||
public class JavaModule extends JVMClassNode<JavaModule, JavaModule.Diff>{
|
||||
|
||||
public JavaModule(JVMFlags flags, String signature, String name, String outFilePath, @NotNull Iterable<TypeRepr.ClassType> annotations, @NotNull Iterable<Usage> usages) {
|
||||
super(flags, signature, name, outFilePath, annotations, usages);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Diff difference(JavaModule other) {
|
||||
return new Diff(other);
|
||||
}
|
||||
|
||||
public static class Diff implements Difference {
|
||||
|
||||
public Diff(JavaModule other) {
|
||||
// todo: diff necessary data
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean unchanged() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.jps.dependency.java;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jps.dependency.Usage;
|
||||
import org.jetbrains.jps.dependency.diff.Difference;
|
||||
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.Objects;
|
||||
|
||||
public class JvmAnnotation extends JvmObjectType<JvmAnnotation, JvmAnnotation.Diff> {
|
||||
|
||||
private final Iterable<ElemType> myAnnotationTargets;
|
||||
private final RetentionPolicy myRetentionPolicy;
|
||||
|
||||
public JvmAnnotation(JVMFlags flags, String signature, String name, String outFilePath,
|
||||
@NotNull Iterable<TypeRepr.ClassType> annotations,
|
||||
@NotNull Iterable<JvmField> fields,
|
||||
@NotNull Iterable<JvmMethod> methods,
|
||||
@NotNull Iterable<Usage> usages,
|
||||
@NotNull Iterable<ElemType> annotationTargets,
|
||||
@NotNull RetentionPolicy retentionPolicy
|
||||
) {
|
||||
super(flags, signature, name, outFilePath, fields, methods, annotations, usages);
|
||||
myAnnotationTargets = annotationTargets;
|
||||
myRetentionPolicy = retentionPolicy;
|
||||
}
|
||||
|
||||
public Iterable<ElemType> getAnnotationTargets() {
|
||||
return myAnnotationTargets;
|
||||
}
|
||||
|
||||
public RetentionPolicy getRetentionPolicy() {
|
||||
return myRetentionPolicy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Diff difference(JvmAnnotation past) {
|
||||
return new Diff(past);
|
||||
}
|
||||
|
||||
public class Diff extends JvmObjectType<JvmAnnotation, JvmAnnotation.Diff>.Diff<JvmAnnotation> {
|
||||
|
||||
public Diff(JvmAnnotation past) {
|
||||
super(past);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean unchanged() {
|
||||
return super.unchanged() && !retentionPolicyChanged() && annotations().unchanged();
|
||||
}
|
||||
|
||||
public boolean retentionPolicyChanged() {
|
||||
return !Objects.equals(myPast.getRetentionPolicy(), getRetentionPolicy());
|
||||
}
|
||||
|
||||
public Specifier<ElemType, ?> annotationTargets() {
|
||||
return Difference.diff(myPast.getAnnotationTargets(), getAnnotationTargets());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,18 +4,20 @@ package org.jetbrains.jps.dependency.java;
|
||||
import org.jetbrains.jps.dependency.Usage;
|
||||
import org.jetbrains.jps.dependency.diff.Difference;
|
||||
|
||||
public class JavaClassType extends ObjectType<JavaClassType, JavaClassType.Diff> {
|
||||
import java.util.Objects;
|
||||
|
||||
public class JvmClass extends JvmObjectType<JvmClass, JvmClass.Diff> {
|
||||
private final String mySuperFqName;
|
||||
private final String myOuterFqName;
|
||||
private final Iterable<String> myInterfaces;
|
||||
|
||||
public JavaClassType(
|
||||
public JvmClass(
|
||||
JVMFlags flags, String signature, String fqName, String outFilePath,
|
||||
String superFqName,
|
||||
String outerFqName,
|
||||
Iterable<String> interfaces,
|
||||
Iterable<Field> fields,
|
||||
Iterable<Method> methods,
|
||||
Iterable<JvmField> fields,
|
||||
Iterable<JvmMethod> methods,
|
||||
Iterable<TypeRepr.ClassType> annotations,
|
||||
Iterable<Usage> usages
|
||||
) {
|
||||
@@ -39,20 +41,32 @@ public class JavaClassType extends ObjectType<JavaClassType, JavaClassType.Diff>
|
||||
}
|
||||
|
||||
@Override
|
||||
public Diff difference(JavaClassType other) {
|
||||
return new Diff(other);
|
||||
public Diff difference(JvmClass past) {
|
||||
return new Diff(past);
|
||||
}
|
||||
|
||||
public static class Diff implements Difference {
|
||||
public class Diff extends JvmObjectType<JvmClass, JvmClass.Diff>.Diff<JvmClass> {
|
||||
|
||||
public Diff(JavaClassType other) {
|
||||
// todo: diff necessary data
|
||||
public Diff(JvmClass past) {
|
||||
super(past);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean unchanged() {
|
||||
return false;
|
||||
return super.unchanged() && !superClassChanged() && !outerClassChanged() && interfaces().unchanged();
|
||||
}
|
||||
|
||||
|
||||
public boolean superClassChanged() {
|
||||
return !Objects.equals(myPast.getSuperFqName(), getSuperFqName());
|
||||
}
|
||||
|
||||
public boolean outerClassChanged() {
|
||||
return !Objects.equals(myPast.getOuterFqName(), getOuterFqName());
|
||||
}
|
||||
|
||||
public Specifier<String, ?> interfaces() {
|
||||
return Difference.diff(myPast.getInterfaces(), getInterfaces());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,848 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.jps.dependency.java;
|
||||
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.SmartList;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.jps.dependency.Usage;
|
||||
import org.jetbrains.jps.javac.Iterators;
|
||||
import org.jetbrains.org.objectweb.asm.*;
|
||||
import org.jetbrains.org.objectweb.asm.signature.SignatureReader;
|
||||
import org.jetbrains.org.objectweb.asm.signature.SignatureVisitor;
|
||||
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.*;
|
||||
|
||||
final class JvmClassAnalyzer {
|
||||
private final static Logger LOG = Logger.getInstance(JvmClassAnalyzer.class);
|
||||
public static final String LAMBDA_FACTORY_CLASS = "java/lang/invoke/LambdaMetafactory";
|
||||
private static final String KOTLIN_LAMBDA_USAGE_CLASS_MARKER = "$sam$";
|
||||
private static final int ASM_API_VERSION = Opcodes.API_VERSION;
|
||||
|
||||
|
||||
JvmClassAnalyzer() {
|
||||
}
|
||||
|
||||
private static class ClassCrawler extends ClassVisitor {
|
||||
private final class AnnotationRetentionPolicyCrawler extends AnnotationVisitor {
|
||||
private AnnotationRetentionPolicyCrawler() {
|
||||
super(ASM_API_VERSION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(String name, Object value) { }
|
||||
|
||||
@Override
|
||||
public void visitEnum(String name, String desc, String value) {
|
||||
myRetentionPolicy = RetentionPolicy.valueOf(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnnotationVisitor visitAnnotation(String name, String desc) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnnotationVisitor visitArray(String name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitEnd() { }
|
||||
}
|
||||
|
||||
private final class AnnotationTargetCrawler extends AnnotationVisitor {
|
||||
private AnnotationTargetCrawler() {
|
||||
super(ASM_API_VERSION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(String name, Object value) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitEnum(final String name, String desc, final String value) {
|
||||
myTargets.add(ElemType.valueOf(value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnnotationVisitor visitAnnotation(String name, String desc) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnnotationVisitor visitArray(String name) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitEnd() {
|
||||
}
|
||||
}
|
||||
|
||||
private final class AnnotationCrawler extends AnnotationVisitor {
|
||||
private final TypeRepr.ClassType myType;
|
||||
private final ElemType myTarget;
|
||||
|
||||
private final Set<String> myUsedArguments = new HashSet<>();
|
||||
|
||||
private AnnotationCrawler(final TypeRepr.ClassType type, final ElemType target) {
|
||||
super(ASM_API_VERSION);
|
||||
this.myType = type;
|
||||
this.myTarget = target;
|
||||
final Set<ElemType> targets = myAnnotationTargets.get(type);
|
||||
if (targets == null) {
|
||||
myAnnotationTargets.put(type, EnumSet.of(target));
|
||||
}
|
||||
else {
|
||||
targets.add(target);
|
||||
}
|
||||
myUsages.add(new ClassUsage(type.getJvmName()));
|
||||
}
|
||||
|
||||
private String getMethodDescr(final Object value, boolean isArray) {
|
||||
final StringBuilder descriptor = new StringBuilder();
|
||||
descriptor.append("()");
|
||||
if (isArray) {
|
||||
descriptor.append("[");
|
||||
}
|
||||
if (value instanceof Type) {
|
||||
descriptor.append("Ljava/lang/Class;");
|
||||
}
|
||||
else {
|
||||
final String name = Type.getType(value.getClass()).getInternalName();
|
||||
// only primitive, String, Class, Enum, another Annotation or array of any of these are allowed
|
||||
switch (name) {
|
||||
case "java/lang/Integer":
|
||||
descriptor.append("I;");
|
||||
break;
|
||||
case "java/lang/Short":
|
||||
descriptor.append("S;");
|
||||
break;
|
||||
case "java/lang/Long":
|
||||
descriptor.append("J;");
|
||||
break;
|
||||
case "java/lang/Byte":
|
||||
descriptor.append("B;");
|
||||
break;
|
||||
case "java/lang/Char":
|
||||
descriptor.append("C;");
|
||||
break;
|
||||
case "java/lang/Boolean":
|
||||
descriptor.append("Z;");
|
||||
break;
|
||||
case "java/lang/Float":
|
||||
descriptor.append("F;");
|
||||
break;
|
||||
case "java/lang/Double":
|
||||
descriptor.append("D;");
|
||||
break;
|
||||
default:
|
||||
descriptor.append("L").append(name).append(";");
|
||||
break;
|
||||
}
|
||||
}
|
||||
return descriptor.toString();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private String myArrayName;
|
||||
|
||||
@Override
|
||||
public void visit(String name, Object value) {
|
||||
final boolean isArray = name == null && myArrayName != null;
|
||||
final String argName;
|
||||
if (name != null) {
|
||||
argName = name;
|
||||
}
|
||||
else {
|
||||
argName = myArrayName;
|
||||
// not interested in collecting complete array value; need to know just array type
|
||||
myArrayName = null;
|
||||
}
|
||||
if (argName != null) {
|
||||
registerUsages(argName, getMethodDescr(value, isArray), value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitEnum(String name, String desc, String value) {
|
||||
final boolean isArray = name == null && myArrayName != null;
|
||||
final String argName;
|
||||
if (name != null) {
|
||||
argName = name;
|
||||
}
|
||||
else {
|
||||
argName = myArrayName;
|
||||
// not interested in collecting complete array value; need to know just array type
|
||||
myArrayName = null;
|
||||
}
|
||||
if (argName != null) {
|
||||
registerUsages(argName, (isArray? "()[" : "()") + desc, value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnnotationVisitor visitAnnotation(String name, String desc) {
|
||||
return new AnnotationCrawler((TypeRepr.ClassType)TypeRepr.getType(desc), myTarget);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnnotationVisitor visitArray(String name) {
|
||||
myArrayName = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
private void registerUsages(String methodName, String methodDescr, Object value) {
|
||||
|
||||
if (value instanceof Type) {
|
||||
final String className = ((Type)value).getClassName().replace('.', '/');
|
||||
myUsages.add(new ClassUsage(className));
|
||||
}
|
||||
|
||||
myUsages.add(new MethodUsage(myType.getJvmName(), methodName, methodDescr));
|
||||
//myUsages.add(UsageRepr.createMetaMethodUsage(myContext, methodName, myType.className));
|
||||
|
||||
myUsedArguments.add(methodName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitEnd() {
|
||||
Set<String> s = myAnnotationArguments.get(myType);
|
||||
if (s == null) {
|
||||
myAnnotationArguments.put(myType, myUsedArguments);
|
||||
}
|
||||
else {
|
||||
s.retainAll(myUsedArguments);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class ModuleCrawler extends ModuleVisitor {
|
||||
ModuleCrawler() {
|
||||
super(ASM_API_VERSION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMainClass(String mainClass) {
|
||||
myUsages.add(new ClassUsage(mainClass));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitRequire(String module, int access, String version) {
|
||||
if (isExplicit(access)) {
|
||||
// collect non-synthetic dependencies only
|
||||
myModuleRequires.add(new ModuleRequires(new JVMFlags(access), module, version));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitExport(String packaze, int access, String... modules) {
|
||||
if (isExplicit(access)) {
|
||||
// collect non-synthetic dependencies only
|
||||
myModuleExports.add(new ModulePackage(packaze, modules != null? Arrays.asList(modules) : Collections.emptyList()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitUse(String service) {
|
||||
myUsages.add(new ClassUsage(service));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitProvide(String service, String... providers) {
|
||||
myUsages.add(new ClassUsage(service));
|
||||
if (providers != null) {
|
||||
for (String provider : providers) {
|
||||
myUsages.add(new ClassUsage(provider));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isExplicit(int access) {
|
||||
return (access & (Opcodes.ACC_SYNTHETIC | Opcodes.ACC_MANDATED)) == 0;
|
||||
}
|
||||
}
|
||||
|
||||
private void processSignature(final String sig) {
|
||||
if (sig != null) {
|
||||
try {
|
||||
new SignatureReader(sig).accept(mySignatureCrawler);
|
||||
}
|
||||
catch (Exception e) {
|
||||
LOG.info("Problems parsing signature \"" + sig + "\" in " + myFileName, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final SignatureVisitor mySignatureCrawler = new BaseSignatureVisitor() {
|
||||
@Override
|
||||
public SignatureVisitor visitClassBound() {
|
||||
return mySignatureWithGenericBoundUsageCrawler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SignatureVisitor visitInterfaceBound() {
|
||||
return mySignatureWithGenericBoundUsageCrawler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SignatureVisitor visitTypeArgument(char wildcard) {
|
||||
return wildcard == '+' || wildcard == '-' ? mySignatureWithGenericBoundUsageCrawler : super.visitTypeArgument(wildcard);
|
||||
}
|
||||
};
|
||||
|
||||
private final SignatureVisitor mySignatureWithGenericBoundUsageCrawler = new BaseSignatureVisitor() {
|
||||
@Override
|
||||
public void visitClassType(String name) {
|
||||
myUsages.add(new ClassUsage(name));
|
||||
myUsages.add(new ClassAsGenericBoundUsage(name));
|
||||
}
|
||||
};
|
||||
|
||||
private boolean myTakeIntoAccount = false;
|
||||
private boolean myIsModule = false;
|
||||
private final String myFileName;
|
||||
private final boolean myIsGenerated;
|
||||
private int myAccess;
|
||||
private String myName;
|
||||
private String myVersion; // for class contains a class bytecode version, for module contains a module version
|
||||
private String mySuperClass;
|
||||
private String[] myInterfaces;
|
||||
private String mySignature;
|
||||
|
||||
private final Ref<String> myClassNameHolder = Ref.create();
|
||||
private final Ref<String> myOuterClassName = Ref.create();
|
||||
private final Ref<Boolean> myLocalClassFlag = Ref.create(false);
|
||||
private final Ref<Boolean> myAnonymousClassFlag = Ref.create(false);
|
||||
|
||||
private final Set<JvmMethod> myMethods = new HashSet<>();
|
||||
private final Set<JvmField> myFields = new HashSet<>();
|
||||
private final Set<Usage> myUsages = new HashSet<>();
|
||||
private final Set<ElemType> myTargets = EnumSet.noneOf(ElemType.class);
|
||||
private RetentionPolicy myRetentionPolicy = null;
|
||||
|
||||
private final Map<TypeRepr.ClassType, Set<String>> myAnnotationArguments = new HashMap<>();
|
||||
private final Map<TypeRepr.ClassType, Set<ElemType>> myAnnotationTargets = new HashMap<>();
|
||||
private final Set<TypeRepr.ClassType> myAnnotations = new HashSet<>();
|
||||
|
||||
private final Set<ModuleRequires> myModuleRequires = new HashSet<>();
|
||||
private final Set<ModulePackage> myModuleExports = new HashSet<>();
|
||||
|
||||
ClassCrawler(final String fn, boolean isGenerated) {
|
||||
super(ASM_API_VERSION);
|
||||
myFileName = fn;
|
||||
myIsGenerated = isGenerated;
|
||||
}
|
||||
|
||||
private boolean notPrivate(final int access) {
|
||||
return (access & Opcodes.ACC_PRIVATE) == 0;
|
||||
}
|
||||
|
||||
public JVMClassNode getResult() {
|
||||
if (!myTakeIntoAccount) {
|
||||
return null;
|
||||
}
|
||||
JVMFlags flags = new JVMFlags(myAccess);
|
||||
if (myLocalClassFlag.get()) {
|
||||
flags = flags.deriveIsLocal();
|
||||
}
|
||||
if (myAnonymousClassFlag.get()) {
|
||||
flags = flags.deriveIsAnonymous();
|
||||
}
|
||||
if (myIsGenerated) {
|
||||
flags = flags.deriveIsGenerated();
|
||||
}
|
||||
if (myIsModule) {
|
||||
return new JvmModule(flags, myVersion, myFileName, myName, myModuleRequires, myModuleExports, myUsages);
|
||||
}
|
||||
if ((myAccess & Opcodes.ACC_ANNOTATION) > 0) {
|
||||
return new JvmAnnotation(flags, mySignature, myName, myFileName, myAnnotations, myFields, myMethods, myUsages, myTargets, myRetentionPolicy);
|
||||
}
|
||||
return new JvmClass(flags, mySignature, myName, myFileName, mySuperClass, myOuterClassName.get(), Arrays.asList(myInterfaces), myFields, myMethods, myAnnotations, myUsages);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(int version, int access, String name, String sig, String superName, String[] interfaces) {
|
||||
myTakeIntoAccount = notPrivate(access);
|
||||
|
||||
myAccess = access;
|
||||
myName = name;
|
||||
myVersion = String.valueOf(version);
|
||||
mySignature = sig;
|
||||
mySuperClass = superName;
|
||||
myInterfaces = interfaces;
|
||||
|
||||
myClassNameHolder.set(name);
|
||||
|
||||
if (mySuperClass != null) {
|
||||
myUsages.add(new ClassUsage(mySuperClass));
|
||||
}
|
||||
|
||||
if (myInterfaces != null) {
|
||||
for (String ifaceName : myInterfaces) {
|
||||
myUsages.add(new ClassUsage(ifaceName));
|
||||
}
|
||||
}
|
||||
|
||||
processSignature(sig);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitEnd() {
|
||||
for (Map.Entry<TypeRepr.ClassType, Set<ElemType>> entry : myAnnotationTargets.entrySet()) {
|
||||
TypeRepr.ClassType type = entry.getKey();
|
||||
Set<ElemType> targets = entry.getValue();
|
||||
Set<String> usedArguments = myAnnotationArguments.get(type);
|
||||
myUsages.add(new AnnotationUsage(type, usedArguments != null? usedArguments : Collections.emptyList(), targets));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModuleVisitor visitModule(String name, int access, String version) {
|
||||
myIsModule = true;
|
||||
myAccess = access;
|
||||
myName = name;
|
||||
myVersion = version;
|
||||
return new ModuleCrawler();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnnotationVisitor visitAnnotation(final String desc, final boolean visible) {
|
||||
if (desc.equals("Ljava/lang/annotation/Target;")) {
|
||||
return new AnnotationTargetCrawler();
|
||||
}
|
||||
|
||||
if (desc.equals("Ljava/lang/annotation/Retention;")) {
|
||||
return new AnnotationRetentionPolicyCrawler();
|
||||
}
|
||||
|
||||
TypeRepr.ClassType annotationType = (TypeRepr.ClassType)TypeRepr.getType(desc);
|
||||
myAnnotations.add(annotationType);
|
||||
return new AnnotationCrawler(annotationType, (myAccess & Opcodes.ACC_ANNOTATION) > 0 ? ElemType.ANNOTATION_TYPE : ElemType.TYPE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitSource(String source, String debug) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public FieldVisitor visitField(final int access, final String name, final String desc, final String signature, final Object value) {
|
||||
processSignature(signature);
|
||||
|
||||
return new FieldVisitor(ASM_API_VERSION) {
|
||||
final Set<TypeRepr.ClassType> annotations = new HashSet<>();
|
||||
|
||||
@Override
|
||||
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
|
||||
final TypeRepr.ClassType annotation = (TypeRepr.ClassType)TypeRepr.getType(desc);
|
||||
annotations.add(annotation);
|
||||
return new AnnotationCrawler(annotation, ElemType.FIELD);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitEnd() {
|
||||
try {
|
||||
super.visitEnd();
|
||||
}
|
||||
finally {
|
||||
if ((access & Opcodes.ACC_SYNTHETIC) == 0) {
|
||||
myFields.add(new JvmField(new JVMFlags(access), signature, name, desc, annotations, value));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodVisitor visitMethod(final int access, final String n, final String desc, final String signature, final String[] exceptions) {
|
||||
final Ref<Object> defaultValue = Ref.create();
|
||||
final Set<TypeRepr.ClassType> annotations = new HashSet<>();
|
||||
final Set<ParamAnnotation> paramAnnotations = new HashSet<>();
|
||||
processSignature(signature);
|
||||
|
||||
return new MethodVisitor(ASM_API_VERSION) {
|
||||
@Override
|
||||
public void visitEnd() {
|
||||
if ((access & Opcodes.ACC_SYNTHETIC) == 0 || (access & Opcodes.ACC_BRIDGE) > 0) {
|
||||
myMethods.add(new JvmMethod(new JVMFlags(access), signature, n, desc, annotations, paramAnnotations, exceptions, defaultValue.get()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
|
||||
final TypeRepr.ClassType annoType = (TypeRepr.ClassType)TypeRepr.getType(desc);
|
||||
annotations.add(annoType);
|
||||
return new AnnotationCrawler(annoType, "<init>".equals(n) ? ElemType.CONSTRUCTOR : ElemType.METHOD);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnnotationVisitor visitAnnotationDefault() {
|
||||
return new AnnotationVisitor(ASM_API_VERSION) {
|
||||
private @Nullable List<Object> myAcc;
|
||||
|
||||
@Override
|
||||
public void visit(String name, Object value) {
|
||||
collectValue(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitEnum(String name, String desc, String value) {
|
||||
collectValue(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnnotationVisitor visitArray(String name) {
|
||||
myAcc = new SmartList<>();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitEnd() {
|
||||
if (myAcc != null) {
|
||||
Object[] template = null;
|
||||
if (!myAcc.isEmpty()) {
|
||||
final Object elem = myAcc.get(0);
|
||||
if (elem != null) {
|
||||
template = ArrayUtil.newArray(elem.getClass(), 0);
|
||||
}
|
||||
}
|
||||
defaultValue.set(template != null? myAcc.toArray(template) : myAcc.toArray());
|
||||
}
|
||||
}
|
||||
|
||||
private void collectValue(Object value) {
|
||||
if (myAcc != null) {
|
||||
myAcc.add(value);
|
||||
}
|
||||
else {
|
||||
defaultValue.set(value);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) {
|
||||
final TypeRepr.ClassType annoType = (TypeRepr.ClassType)TypeRepr.getType(desc);
|
||||
paramAnnotations.add(new ParamAnnotation(parameter, annoType));
|
||||
return new AnnotationCrawler(annoType, ElemType.PARAMETER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitLdcInsn(Object cst) {
|
||||
if (cst instanceof Type) {
|
||||
myUsages.add(new ClassUsage(((Type)cst).getInternalName()));
|
||||
}
|
||||
|
||||
super.visitLdcInsn(cst);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMultiANewArrayInsn(String desc, int dims) {
|
||||
final TypeRepr.ArrayType typ = (TypeRepr.ArrayType)TypeRepr.getType(desc);
|
||||
Iterators.collect(typ.getUsages(), myUsages);
|
||||
|
||||
final TypeRepr element = typ.getDeepElementType();
|
||||
if (element instanceof TypeRepr.ClassType) {
|
||||
myUsages.add(new ClassNewUsage(((TypeRepr.ClassType)element).getJvmName()));
|
||||
}
|
||||
|
||||
super.visitMultiANewArrayInsn(desc, dims);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitLocalVariable(String n, String desc, String signature, Label start, Label end, int index) {
|
||||
processSignature(signature);
|
||||
Iterators.collect(TypeRepr.getType(desc).getUsages(), myUsages);
|
||||
super.visitLocalVariable(n, desc, signature, start, end, index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
|
||||
if (type != null) {
|
||||
Iterators.collect(new TypeRepr.ClassType(type).getUsages(), myUsages);
|
||||
}
|
||||
super.visitTryCatchBlock(start, end, handler, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitTypeInsn(int opcode, String type) {
|
||||
final TypeRepr typ = type.startsWith("[")? TypeRepr.getType(type) : new TypeRepr.ClassType(type);
|
||||
|
||||
if (opcode == Opcodes.NEW) {
|
||||
myUsages.add(new ClassUsage(((TypeRepr.ClassType)typ).getJvmName()));
|
||||
myUsages.add(new ClassNewUsage(((TypeRepr.ClassType)typ).getJvmName()));
|
||||
final int ktLambdaMarker = type.indexOf(KOTLIN_LAMBDA_USAGE_CLASS_MARKER);
|
||||
if (ktLambdaMarker > 0) {
|
||||
final int ifNameStart = ktLambdaMarker + KOTLIN_LAMBDA_USAGE_CLASS_MARKER.length();
|
||||
final int ifNameEnd = type.indexOf("$", ifNameStart);
|
||||
if (ifNameEnd > ifNameStart) {
|
||||
myUsages.add(new ClassNewUsage(type.substring(ifNameStart, ifNameEnd).replace('_', '/')));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (opcode == Opcodes.ANEWARRAY) {
|
||||
if (typ instanceof TypeRepr.ClassType) {
|
||||
myUsages.add(new ClassUsage(((TypeRepr.ClassType)typ).getJvmName()));
|
||||
myUsages.add(new ClassNewUsage(((TypeRepr.ClassType)typ).getJvmName()));
|
||||
}
|
||||
}
|
||||
|
||||
Iterators.collect(typ.getUsages(), myUsages);
|
||||
|
||||
super.visitTypeInsn(opcode, type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitFieldInsn(int opcode, String owner, String name, String desc) {
|
||||
registerFieldUsage(opcode, owner, name, desc);
|
||||
super.visitFieldInsn(opcode, owner, name, desc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
|
||||
registerMethodUsage(owner, name, desc);
|
||||
super.visitMethodInsn(opcode, owner, name, desc, itf);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitInvokeDynamicInsn(String methodName, String desc, Handle bsm, Object... bsmArgs) {
|
||||
final Type returnType = Type.getReturnType(desc);
|
||||
addClassUsage(TypeRepr.getType(returnType));
|
||||
|
||||
// common args processing
|
||||
for (Object arg : bsmArgs) {
|
||||
if (arg instanceof Type) {
|
||||
final Type type = (Type)arg;
|
||||
if (type.getSort() == Type.METHOD) {
|
||||
for (Type argType : type.getArgumentTypes()) {
|
||||
addClassUsage(TypeRepr.getType(argType));
|
||||
}
|
||||
addClassUsage(TypeRepr.getType(type.getReturnType()));
|
||||
}
|
||||
else {
|
||||
addClassUsage(TypeRepr.getType(type));
|
||||
}
|
||||
}
|
||||
else if (arg instanceof Handle) {
|
||||
processMethodHandle((Handle)arg);
|
||||
}
|
||||
}
|
||||
|
||||
if (LAMBDA_FACTORY_CLASS.equals(bsm.getOwner())) {
|
||||
// This invokeDynamic implements a lambda or method reference usage.
|
||||
// Need to register method usage for the corresponding SAM-type.
|
||||
// First three arguments to the bootstrap methods are provided automatically by VM.
|
||||
// Arguments in args array are expected to be as following:
|
||||
// [0]: Type: Signature and return type of method to be implemented by the function object.
|
||||
// [1]: Handle: implementation method handle
|
||||
// [2]: Type: The signature and return type that should be enforced dynamically at invocation time. May be the same as samMethodType, or may be a specialization of it
|
||||
// [...]: optional additional arguments
|
||||
|
||||
if (returnType.getSort() == Type.OBJECT && bsmArgs.length >= 3) {
|
||||
if (bsmArgs[0] instanceof Type) {
|
||||
final Type samMethodType = (Type)bsmArgs[0];
|
||||
if (samMethodType.getSort() == Type.METHOD) {
|
||||
registerMethodUsage(returnType.getInternalName(), methodName, samMethodType.getDescriptor());
|
||||
// reflect dynamic proxy instantiation with NewClassUsage
|
||||
myUsages.add(new ClassNewUsage(returnType.getInternalName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
super.visitInvokeDynamicInsn(methodName, desc, bsm, bsmArgs);
|
||||
}
|
||||
|
||||
private void processMethodHandle(Handle handle) {
|
||||
final String memberOwner = handle.getOwner();
|
||||
if (memberOwner != null && !memberOwner.equals(myClassNameHolder.get())) {
|
||||
// do not register access to own class members
|
||||
final String memberName = handle.getName();
|
||||
final String memberDescriptor = handle.getDesc();
|
||||
final int opCode = getFieldAccessOpcode(handle);
|
||||
if (opCode > 0) {
|
||||
registerFieldUsage(opCode, memberOwner, memberName, memberDescriptor);
|
||||
}
|
||||
else {
|
||||
registerMethodUsage(memberOwner, memberName, memberDescriptor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void registerFieldUsage(int opcode, String owner, String fName, String desc) {
|
||||
if (opcode == Opcodes.PUTFIELD || opcode == Opcodes.PUTSTATIC) {
|
||||
myUsages.add(new FieldAssignUsage(owner, fName, desc));
|
||||
}
|
||||
if (opcode == Opcodes.GETFIELD || opcode == Opcodes.GETSTATIC) {
|
||||
addClassUsage(TypeRepr.getType(desc));
|
||||
}
|
||||
myUsages.add(new FieldUsage(owner, fName, desc));
|
||||
}
|
||||
|
||||
private void registerMethodUsage(String owner, String name, @Nullable String desc) {
|
||||
//myUsages.add(UsageRepr.createMetaMethodUsage(myContext, methodName, methodOwner));
|
||||
if (desc != null) {
|
||||
myUsages.add(new MethodUsage(owner, name, desc));
|
||||
addClassUsage(TypeRepr.getType(Type.getReturnType(desc)));
|
||||
}
|
||||
else {
|
||||
// todo: verify for which methods null descriptor is passed
|
||||
myUsages.add(new MethodUsage(owner, name, ""));
|
||||
}
|
||||
}
|
||||
|
||||
private void addClassUsage(final TypeRepr type) {
|
||||
TypeRepr.ClassType classType = null;
|
||||
if (type instanceof TypeRepr.ClassType) {
|
||||
classType = (TypeRepr.ClassType)type;
|
||||
}
|
||||
else if (type instanceof TypeRepr.ArrayType) {
|
||||
final TypeRepr elemType = ((TypeRepr.ArrayType)type).getDeepElementType();
|
||||
if (elemType instanceof TypeRepr.ClassType) {
|
||||
classType = (TypeRepr.ClassType)elemType;
|
||||
}
|
||||
}
|
||||
if (classType != null) {
|
||||
myUsages.add(new ClassUsage(classType.getJvmName()));
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @return corresponding field access opcode or -1 if the handle does not represent field access handle
|
||||
*/
|
||||
private int getFieldAccessOpcode(Handle handle) {
|
||||
switch (handle.getTag()) {
|
||||
case Opcodes.H_GETFIELD: return Opcodes.GETFIELD;
|
||||
case Opcodes.H_GETSTATIC: return Opcodes.GETSTATIC;
|
||||
case Opcodes.H_PUTFIELD: return Opcodes.PUTFIELD;
|
||||
case Opcodes.H_PUTSTATIC: return Opcodes.PUTSTATIC;
|
||||
default: return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitInnerClass(String name, String outerName, String innerName, int access) {
|
||||
if (name != null && name.equals(myClassNameHolder.get())) {
|
||||
// set outer class name only if we are parsing the real inner class and
|
||||
// not the reference to inner class inside some top-level class
|
||||
myAccess |= access; // information about some access flags for the inner class is missing from the mask passed to 'visit' method
|
||||
if (outerName != null) {
|
||||
myOuterClassName.set(outerName);
|
||||
}
|
||||
if (innerName == null) {
|
||||
myAnonymousClassFlag.set(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitOuterClass(final String owner, final String name, final String desc) {
|
||||
myOuterClassName.set(owner);
|
||||
|
||||
if (name != null) {
|
||||
myLocalClassFlag.set(true);
|
||||
}
|
||||
}
|
||||
|
||||
private class BaseSignatureVisitor extends SignatureVisitor {
|
||||
BaseSignatureVisitor() {
|
||||
super(ASM_API_VERSION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitFormalTypeParameter(String name) { }
|
||||
|
||||
@Override
|
||||
public SignatureVisitor visitClassBound() {
|
||||
return super.visitClassBound();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SignatureVisitor visitInterfaceBound() {
|
||||
return super.visitInterfaceBound();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SignatureVisitor visitSuperclass() {
|
||||
return super.visitSuperclass();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SignatureVisitor visitInterface() {
|
||||
return super.visitInterface();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SignatureVisitor visitParameterType() {
|
||||
return super.visitParameterType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SignatureVisitor visitReturnType() {
|
||||
return super.visitReturnType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SignatureVisitor visitExceptionType() {
|
||||
return super.visitExceptionType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitBaseType(char descriptor) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitTypeVariable(String name) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public SignatureVisitor visitArrayType() {
|
||||
return super.visitArrayType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitInnerClassType(String name) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitTypeArgument() {
|
||||
super.visitTypeArgument();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SignatureVisitor visitTypeArgument(char wildcard) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitEnd() {
|
||||
super.visitEnd();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitClassType(String name) {
|
||||
myUsages.add(new ClassUsage(name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public JVMClassNode analyze(String fileName, ClassReader cr, boolean isGenerated) {
|
||||
ClassCrawler visitor = new ClassCrawler(fileName, isGenerated);
|
||||
|
||||
try {
|
||||
cr.accept(visitor, 0);
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
throw new RuntimeException("Corrupted .class file: " + fileName, e);
|
||||
}
|
||||
|
||||
return visitor.getResult();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.jps.dependency.java;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jps.dependency.ReferenceID;
|
||||
import org.jetbrains.jps.dependency.Usage;
|
||||
|
||||
abstract class JvmElementUsage implements Usage {
|
||||
|
||||
@NotNull
|
||||
private final ReferenceID myOwner;
|
||||
|
||||
JvmElementUsage(@NotNull ReferenceID owner) {
|
||||
myOwner = owner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ReferenceID getElementOwner() {
|
||||
return myOwner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final JvmElementUsage jvmUsage = (JvmElementUsage)o;
|
||||
|
||||
if (!myOwner.equals(jvmUsage.myOwner)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return myOwner.hashCode();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.jps.dependency.java;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jps.dependency.diff.DiffCapable;
|
||||
|
||||
public class JvmField extends ProtoMember implements DiffCapable<JvmField, JvmField.Diff> {
|
||||
|
||||
public JvmField(JVMFlags flags, String signature, String name, String descriptor, @NotNull Iterable<TypeRepr.ClassType> annotations, Object value) {
|
||||
super(flags, signature, name, TypeRepr.getType(descriptor), annotations, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSame(DiffCapable<?, ?> other) {
|
||||
return other instanceof JvmField && getName().equals(((JvmField)other).getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int diffHashCode() {
|
||||
return getName().hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JvmField.Diff difference(JvmField past) {
|
||||
return new Diff(past);
|
||||
}
|
||||
|
||||
public class Diff extends ProtoMember.Diff<JvmField> {
|
||||
|
||||
public Diff(JvmField past) {
|
||||
super(past);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.jps.dependency.java;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jps.dependency.diff.DiffCapable;
|
||||
import org.jetbrains.jps.dependency.diff.Difference;
|
||||
import org.jetbrains.jps.javac.Iterators;
|
||||
import org.jetbrains.org.objectweb.asm.Type;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
public class JvmMethod extends ProtoMember implements DiffCapable<JvmMethod, JvmMethod.Diff> {
|
||||
public final Iterable<TypeRepr> myArgTypes;
|
||||
private final Set<ParamAnnotation> myParamAnnotations;
|
||||
private final Set<TypeRepr.ClassType> myExceptions;
|
||||
|
||||
public JvmMethod(
|
||||
JVMFlags flags, String signature, String name, String descriptor,
|
||||
@NotNull Iterable<TypeRepr.ClassType> annotations, Set<ParamAnnotation> parameterAnnotations,
|
||||
String[] exceptions, Object defaultValue) {
|
||||
|
||||
super(flags, signature, name, TypeRepr.getType(Type.getReturnType(descriptor)), annotations, defaultValue);
|
||||
myParamAnnotations = parameterAnnotations;
|
||||
myExceptions = Iterators.collect(Iterators.map(Arrays.asList(exceptions), s -> new TypeRepr.ClassType(s)), new HashSet<>());
|
||||
myArgTypes = TypeRepr.getTypes(Type.getArgumentTypes(descriptor));
|
||||
}
|
||||
|
||||
public Set<ParamAnnotation> getParamAnnotations() {
|
||||
return myParamAnnotations;
|
||||
}
|
||||
|
||||
public Set<TypeRepr.ClassType> getExceptions() {
|
||||
return myExceptions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSame(DiffCapable<?, ?> other) {
|
||||
if (!(other instanceof JvmMethod)) {
|
||||
return false;
|
||||
}
|
||||
JvmMethod that = (JvmMethod)other;
|
||||
return getName().equals(that.getName()) && Iterators.equals(myArgTypes, that.myArgTypes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int diffHashCode() {
|
||||
return 31 * (31 * Iterators.hashCode(myArgTypes) + getName().hashCode());
|
||||
}
|
||||
|
||||
@Override
|
||||
public JvmMethod.Diff difference(JvmMethod past) {
|
||||
return new Diff(past);
|
||||
}
|
||||
|
||||
public class Diff extends ProtoMember.Diff<JvmMethod> {
|
||||
|
||||
public Diff(JvmMethod past) {
|
||||
super(past);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean unchanged() {
|
||||
return super.unchanged() && !returnTypeChanged() && paramAnnotationsChanged().unchanged() && exceptionsChanged().unchanged();
|
||||
}
|
||||
|
||||
public boolean returnTypeChanged() {
|
||||
return !Objects.equals(myPast.getType(), getType());
|
||||
}
|
||||
|
||||
public Specifier<ParamAnnotation, ?> paramAnnotationsChanged() {
|
||||
return Difference.diff(myPast.getParamAnnotations(), getParamAnnotations());
|
||||
}
|
||||
|
||||
public Specifier<TypeRepr.ClassType, ?> exceptionsChanged() {
|
||||
return Difference.diff(myPast.getExceptions(), getExceptions());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.jps.dependency.java;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jps.dependency.Usage;
|
||||
import org.jetbrains.jps.dependency.diff.Difference;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
public class JvmModule extends JVMClassNode<JvmModule, JvmModule.Diff>{
|
||||
|
||||
private final String myVersion;
|
||||
private final Iterable<ModuleRequires> myRequires;
|
||||
private final Iterable<ModulePackage> myExports;
|
||||
|
||||
public JvmModule(JVMFlags flags, String name, String outFilePath, String version, @NotNull Iterable<ModuleRequires> requires, @NotNull Iterable<ModulePackage> exports, @NotNull Iterable<Usage> usages) {
|
||||
super(flags, "", name, outFilePath, Collections.emptyList(), usages);
|
||||
myVersion = version;
|
||||
myRequires = requires;
|
||||
myExports = exports;
|
||||
}
|
||||
|
||||
public final String getVersion() {
|
||||
return myVersion;
|
||||
}
|
||||
|
||||
public Iterable<ModuleRequires> getRequires() {
|
||||
return myRequires;
|
||||
}
|
||||
|
||||
public Iterable<ModulePackage> getExports() {
|
||||
return myExports;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Diff difference(JvmModule past) {
|
||||
return new Diff(past);
|
||||
}
|
||||
|
||||
public class Diff extends Proto.Diff<JvmModule> {
|
||||
|
||||
public Diff(JvmModule past) {
|
||||
super(past);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean unchanged() {
|
||||
return super.unchanged() && requires().unchanged() && exports().unchanged();
|
||||
}
|
||||
|
||||
public Specifier<ModuleRequires, ModuleRequires.Diff> requires() {
|
||||
return Difference.deepDiff(myPast.getRequires(), getRequires());
|
||||
}
|
||||
|
||||
public Specifier<ModulePackage, ModulePackage.Diff> exports() {
|
||||
return Difference.deepDiff(myPast.getExports(), getExports());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.jps.dependency.java;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jps.dependency.Usage;
|
||||
import org.jetbrains.jps.dependency.diff.Difference;
|
||||
|
||||
public abstract class JvmObjectType<T extends JvmObjectType<T, D>, D extends Difference> extends JVMClassNode<T, D>{
|
||||
|
||||
private final Iterable<JvmField> myFields;
|
||||
private final Iterable<JvmMethod> myMethods;
|
||||
|
||||
public JvmObjectType(JVMFlags flags, String signature, String name, String outFilePath,
|
||||
Iterable<JvmField> fields,
|
||||
Iterable<JvmMethod> methods,
|
||||
@NotNull Iterable<TypeRepr.ClassType> annotations,
|
||||
@NotNull Iterable<Usage> usages) {
|
||||
super(flags, signature, name, outFilePath, annotations, usages);
|
||||
myFields = fields;
|
||||
myMethods = methods;
|
||||
}
|
||||
|
||||
public Iterable<JvmField> getFields() {
|
||||
return myFields;
|
||||
}
|
||||
|
||||
public Iterable<JvmMethod> getMethods() {
|
||||
return myMethods;
|
||||
}
|
||||
|
||||
public class Diff<V extends JvmObjectType<T, D>> extends Proto.Diff<V> {
|
||||
public Diff(V past) {
|
||||
super(past);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean unchanged() {
|
||||
return super.unchanged() && methods().unchanged() && fields().unchanged();
|
||||
}
|
||||
|
||||
public Specifier<JvmMethod, JvmMethod.Diff> methods() {
|
||||
return Difference.deepDiff(myPast.getMethods(), getMethods());
|
||||
}
|
||||
|
||||
public Specifier<JvmField, JvmField.Diff> fields() {
|
||||
return Difference.deepDiff(myPast.getFields(), getFields());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.jps.dependency.java;
|
||||
|
||||
import org.jetbrains.jps.dependency.impl.StringReferenceID;
|
||||
|
||||
abstract class MemberUsage extends JvmElementUsage {
|
||||
|
||||
private final String myName;
|
||||
|
||||
protected MemberUsage(String className, String name) {
|
||||
super(new StringReferenceID(className));
|
||||
myName = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return myName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (!super.equals(o)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final MemberUsage that = (MemberUsage)o;
|
||||
|
||||
if (!myName.equals(that.myName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = super.hashCode();
|
||||
result = 31 * result + myName.hashCode();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.jps.dependency.java;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jps.dependency.diff.DiffCapable;
|
||||
import org.jetbrains.jps.dependency.diff.Difference;
|
||||
import org.jetbrains.jps.javac.Iterators;
|
||||
import org.jetbrains.org.objectweb.asm.Type;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class Method extends ProtoMember implements DiffCapable<Method, Method.Diff> {
|
||||
public final Iterable<TypeRepr> argTypes;
|
||||
private final Set<ParamAnnotation> paramAnnotations;
|
||||
private final Set<TypeRepr.ClassType> exceptions;
|
||||
|
||||
public Method(
|
||||
JVMFlags flags, String signature, String name, String descriptor,
|
||||
@NotNull Iterable<TypeRepr.ClassType> annotations, Set<ParamAnnotation> parameterAnnotations,
|
||||
String[] exceptions, Object defaultValue) {
|
||||
|
||||
super(flags, signature, name, TypeRepr.getType(Type.getReturnType(descriptor)), annotations, defaultValue);
|
||||
paramAnnotations = parameterAnnotations;
|
||||
this.exceptions = Iterators.collect(Iterators.map(Arrays.asList(exceptions), s -> new TypeRepr.ClassType(s)), new HashSet<>());
|
||||
argTypes = TypeRepr.getTypes(Type.getArgumentTypes(descriptor));
|
||||
}
|
||||
|
||||
public Set<ParamAnnotation> getParamAnnotations() {
|
||||
return paramAnnotations;
|
||||
}
|
||||
|
||||
public Set<TypeRepr.ClassType> getExceptions() {
|
||||
return exceptions;
|
||||
}
|
||||
// todo: need to additionally define normal equals-hashcode?
|
||||
@Override
|
||||
public boolean isSame(DiffCapable<?, ?> other) {
|
||||
if (!(other instanceof Method)) {
|
||||
return false;
|
||||
}
|
||||
Method that = (Method)other;
|
||||
return getName().equals(that.getName()) && getType().equals(that.getType()) && Iterators.equals(argTypes, that.argTypes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int diffHashCode() {
|
||||
return 31 * (31 * Iterators.hashCode(argTypes) + getType().hashCode()) + getName().hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Method.Diff difference(Method other) {
|
||||
return new Diff(other);
|
||||
}
|
||||
|
||||
public static class Diff implements Difference {
|
||||
|
||||
public Diff(Method other) {
|
||||
// todo: diff necessary data
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean unchanged() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.jps.dependency.java;
|
||||
|
||||
public class MethodUsage extends MemberUsage{
|
||||
|
||||
private final String myDescriptor;
|
||||
|
||||
public MethodUsage(String className, String name, String descriptor) {
|
||||
super(className, name);
|
||||
myDescriptor = descriptor;
|
||||
}
|
||||
|
||||
public String getDescriptor() {
|
||||
return myDescriptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (!super.equals(o)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final MethodUsage that = (MethodUsage)o;
|
||||
|
||||
if (!myDescriptor.equals(that.myDescriptor)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = super.hashCode();
|
||||
result = 31 * result + myDescriptor.hashCode();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.jps.dependency.java;
|
||||
|
||||
import org.jetbrains.jps.dependency.diff.DiffCapable;
|
||||
import org.jetbrains.jps.dependency.diff.Difference;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
public class ModulePackage extends Proto implements DiffCapable<ModulePackage, ModulePackage.Diff> {
|
||||
|
||||
private final Iterable<String> myModules;
|
||||
|
||||
public ModulePackage(String name, Iterable<String> modules) {
|
||||
super(JVMFlags.EMPTY, "", name, Collections.emptyList());
|
||||
myModules = modules;
|
||||
}
|
||||
|
||||
public Iterable<String> getModules() {
|
||||
return myModules;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSame(DiffCapable<?, ?> other) {
|
||||
return other instanceof ModulePackage && getName().equals(((ModulePackage)other).getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int diffHashCode() {
|
||||
return getName().hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Diff difference(ModulePackage past) {
|
||||
return new Diff(past);
|
||||
}
|
||||
|
||||
public class Diff extends Proto.Diff<ModulePackage> {
|
||||
|
||||
public Diff(ModulePackage past) {
|
||||
super(past);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean unchanged() {
|
||||
return super.unchanged() && targetModules().unchanged();
|
||||
}
|
||||
|
||||
public Specifier<String, ?> targetModules() {
|
||||
return Difference.diff(myPast.getModules(), getModules());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.jps.dependency.java;
|
||||
|
||||
import org.jetbrains.jps.dependency.diff.DiffCapable;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Objects;
|
||||
|
||||
public class ModuleRequires extends Proto implements DiffCapable<ModuleRequires, ModuleRequires.Diff> {
|
||||
|
||||
private final String myVersion;
|
||||
|
||||
public ModuleRequires(JVMFlags flags, String name, String version) {
|
||||
super(flags, "", name, Collections.emptyList());
|
||||
myVersion = version;
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return myVersion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSame(DiffCapable<?, ?> other) {
|
||||
return other instanceof ModuleRequires && getName().equals(((ModuleRequires)other).getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int diffHashCode() {
|
||||
return getName().hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Diff difference(ModuleRequires past) {
|
||||
return new Diff(past);
|
||||
}
|
||||
|
||||
public class Diff extends Proto.Diff<ModuleRequires> {
|
||||
|
||||
public Diff(ModuleRequires past) {
|
||||
super(past);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean unchanged() {
|
||||
return super.unchanged() && !versionChanged();
|
||||
}
|
||||
|
||||
public boolean versionChanged() {
|
||||
return !Objects.equals(myPast.getVersion(), getVersion());
|
||||
}
|
||||
|
||||
public boolean becameNonTransitive() {
|
||||
return myPast.getFlags().isTransitive() && !getFlags().isTransitive();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.jps.dependency.java;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jps.dependency.impl.StringReferenceID;
|
||||
|
||||
public class ModuleUsage extends JvmElementUsage {
|
||||
|
||||
public ModuleUsage(@NotNull String moduleName) {
|
||||
super(new StringReferenceID(moduleName));
|
||||
}
|
||||
|
||||
public String getModuleName() {
|
||||
return ((StringReferenceID)getElementOwner()).getValue();
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package org.jetbrains.jps.dependency.java;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jps.dependency.Usage;
|
||||
import org.jetbrains.jps.dependency.diff.Difference;
|
||||
|
||||
public abstract class ObjectType<T extends ObjectType<T, D>, D extends Difference> extends JVMClassNode<T, D>{
|
||||
|
||||
private final Iterable<Field> myFields;
|
||||
private final Iterable<Method> myMethods;
|
||||
|
||||
public ObjectType(JVMFlags flags, String signature, String name, String outFilePath,
|
||||
Iterable<Field> fields,
|
||||
Iterable<Method> methods,
|
||||
@NotNull Iterable<TypeRepr.ClassType> annotations,
|
||||
@NotNull Iterable<Usage> usages) {
|
||||
super(flags, signature, name, outFilePath, annotations, usages);
|
||||
myFields = fields;
|
||||
myMethods = methods;
|
||||
}
|
||||
|
||||
public Iterable<Field> getFields() {
|
||||
return myFields;
|
||||
}
|
||||
|
||||
public Iterable<Method> getMethods() {
|
||||
return myMethods;
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,11 @@
|
||||
package org.jetbrains.jps.dependency.java;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jps.dependency.diff.Difference;
|
||||
import org.jetbrains.jps.dependency.java.TypeRepr.ClassType;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
public class Proto {
|
||||
private final JVMFlags access;
|
||||
private final String signature;
|
||||
@@ -92,4 +95,33 @@ public class Proto {
|
||||
return false;
|
||||
}
|
||||
|
||||
public class Diff<V extends Proto> implements Difference {
|
||||
protected final V myPast;
|
||||
|
||||
public Diff(V past) {
|
||||
myPast = past;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean unchanged() {
|
||||
return myPast.getFlags().equals(getFlags()) && signature().unchanged() && annotations().unchanged();
|
||||
}
|
||||
|
||||
public JVMFlags getAddedFlags() {
|
||||
return getFlags().deriveAdded(myPast.getFlags());
|
||||
}
|
||||
|
||||
public JVMFlags getRemovedFlags() {
|
||||
return getFlags().deriveRemoved(myPast.getFlags());
|
||||
}
|
||||
|
||||
public Specifier<String, ?> signature() {
|
||||
return Difference.diff(Collections.singleton(myPast.getSignature()), Collections.singleton(getSignature()));
|
||||
}
|
||||
|
||||
public Specifier<ClassType, ?> annotations() {
|
||||
return Difference.diff(myPast.getAnnotations(), getAnnotations());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ package org.jetbrains.jps.dependency.java;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public abstract class ProtoMember extends Proto {
|
||||
@NotNull
|
||||
private final TypeRepr type;
|
||||
@@ -23,4 +25,24 @@ public abstract class ProtoMember extends Proto {
|
||||
public @Nullable Object getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public class Diff<V extends ProtoMember> extends Proto.Diff<V> {
|
||||
|
||||
public Diff(V past) {
|
||||
super(past);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean unchanged() {
|
||||
return super.unchanged() && !typeChanged() && !valueChanged();
|
||||
}
|
||||
|
||||
public boolean typeChanged() {
|
||||
return !Objects.equals(myPast.getType(), getType());
|
||||
}
|
||||
|
||||
public boolean valueChanged() {
|
||||
return !Objects.equals(myPast.getValue(), getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
package org.jetbrains.jps.dependency.java;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jps.dependency.Usage;
|
||||
import org.jetbrains.jps.javac.Iterators;
|
||||
import org.jetbrains.org.objectweb.asm.Type;
|
||||
|
||||
@@ -16,6 +17,10 @@ public abstract class TypeRepr {
|
||||
|
||||
public abstract int hashCode();
|
||||
|
||||
public Iterable<Usage> getUsages() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
public static class PrimitiveType extends TypeRepr {
|
||||
|
||||
@NotNull
|
||||
@@ -62,6 +67,15 @@ public abstract class TypeRepr {
|
||||
myJvmName = jvmName;
|
||||
}
|
||||
|
||||
public String getJvmName() {
|
||||
return myJvmName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Usage> getUsages() {
|
||||
return Collections.singleton(new ClassUsage(myJvmName));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getDescriptor() {
|
||||
return "L" + myJvmName + ";";
|
||||
@@ -104,6 +118,19 @@ public abstract class TypeRepr {
|
||||
return "[" + myElementType.getDescriptor();
|
||||
}
|
||||
|
||||
public TypeRepr getDeepElementType() {
|
||||
TypeRepr current = this;
|
||||
while (current instanceof ArrayType) {
|
||||
current = ((ArrayType)current).myElementType;
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Usage> getUsages() {
|
||||
return getDeepElementType().getUsages();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
|
||||
Reference in New Issue
Block a user