Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/Java/Openjdk/make/jdk/src/classes/build/tools/depend/   (Sun/Oracle ©)  Datei vom 13.11.2022 mit Größe 34 kB image not shown  

Quelle  Depend.java   Sprache: JAVA

 
/*
 * Copyright (c) 2017, 2022, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package build.tools.depend;

import com.sun.source.tree.ClassTree;
import com.sun.source.tree.CompilationUnitTree;
import com.sun.source.tree.IdentifierTree;
import com.sun.source.tree.ImportTree;
import com.sun.source.tree.LiteralTree;
import com.sun.source.tree.MemberReferenceTree;
import com.sun.source.tree.MemberSelectTree;
import com.sun.source.tree.MethodTree;
import com.sun.source.tree.ModifiersTree;
import com.sun.source.tree.Tree;
import com.sun.source.tree.VariableTree;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.annotation.Documented;
import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.HexFormat;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.AnnotationValueVisitor;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementVisitor;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.ModuleElement;
import javax.lang.model.element.ModuleElement.DirectiveVisitor;
import javax.lang.model.element.ModuleElement.ExportsDirective;
import javax.lang.model.element.ModuleElement.OpensDirective;
import javax.lang.model.element.ModuleElement.ProvidesDirective;
import javax.lang.model.element.ModuleElement.RequiresDirective;
import javax.lang.model.element.ModuleElement.UsesDirective;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.QualifiedNameable;
import javax.lang.model.element.RecordComponentElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.TypeParameterElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.ArrayType;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.ErrorType;
import javax.lang.model.type.ExecutableType;
import javax.lang.model.type.IntersectionType;
import javax.lang.model.type.NoType;
import javax.lang.model.type.NullType;
import javax.lang.model.type.PrimitiveType;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.type.TypeVariable;
import javax.lang.model.type.TypeVisitor;
import javax.lang.model.type.UnionType;
import javax.lang.model.type.WildcardType;
import javax.lang.model.util.ElementFilter;
import javax.tools.JavaFileObject;

import com.sun.source.util.JavacTask;
import com.sun.source.util.Plugin;
import com.sun.source.util.TaskEvent;
import com.sun.source.util.TaskEvent.Kind;
import com.sun.source.util.TaskListener;
import com.sun.source.util.TreePath;
import com.sun.source.util.TreeScanner;
import com.sun.source.util.Trees;
import com.sun.tools.javac.api.BasicJavacTask;
import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.main.JavaCompiler;
import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
import com.sun.tools.javac.tree.JCTree.JCVariableDecl;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.Context.Key;
import com.sun.tools.javac.util.ListBuffer;
import com.sun.tools.javac.util.Options;
import com.sun.tools.javac.util.StringUtils;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.IdentityHashMap;
import java.util.LinkedHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.StreamSupport;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.Name;
import javax.tools.ForwardingJavaFileManager;
import javax.tools.JavaFileManager;
import javax.tools.StandardLocation;
import javax.tools.ToolProvider;

public class Depend implements Plugin {

    @Override
    public String getName() {
        return "depend";
    }

    @Override
    public void init(JavacTask jt, String... args) {
        addExports();

        AtomicBoolean noApiChange = new AtomicBoolean();
        try {
            Context context = ((BasicJavacTask) jt).getContext();
            Options options = Options.instance(context);
            String modifiedInputs = options.get("modifiedInputs");
            if (modifiedInputs == null) {
                throw new IllegalStateException("Expected modifiedInputs to be set using -XDmodifiedInputs=");
            }
            String logLevel = options.get("LOG_LEVEL");
            boolean debug = "trace".equals(logLevel) || "debug".equals(logLevel);
            String internalAPIPath = options.get("internalAPIPath");
            if (internalAPIPath == null) {
                throw new IllegalStateException("Expected internalAPIPath to be set using -XDinternalAPIPath=");
            }
            Set<Path> modified = Files.readAllLines(Paths.get(modifiedInputs)).stream()
                                                                              .map(Paths::get)
                                                                              .collect(Collectors.toSet());
            Path internalAPIDigestFile = Paths.get(internalAPIPath);
            JavaCompiler compiler = JavaCompiler.instance(context);
            Class<?> initialFileParserIntf = Class.forName("com.sun.tools.javac.main.JavaCompiler$InitialFileParserIntf");
            Class<?> initialFileParser = Class.forName("com.sun.tools.javac.main.JavaCompiler$InitialFileParser");
            Field initialParserKeyField = initialFileParser.getDeclaredField("initialParserKey");
            @SuppressWarnings("unchecked")
            Key<Object> key = (Key<Object>) initialParserKeyField.get(null);
            Object initialParserInstance =
                    Proxy.newProxyInstance(Depend.class.getClassLoader(),
                                           new Class<?>[] {initialFileParserIntf},
                                           new FilteredInitialFileParser(compiler,
                                                                         modified,
                                                                         internalAPIDigestFile,
                                                                         noApiChange,
                                                                         debug));
            context.<Object>put(key, initialParserInstance);
        } catch (Exception ex) {
            throw new IllegalStateException(ex);
        }

        jt.addTaskListener(new TaskListener() {
            private final Map<ModuleElement, Set<PackageElement>> apiPackages = new HashMap<>();
            private final MessageDigest apiHash;
            {
                try {
                    apiHash = MessageDigest.getInstance("SHA-256");
                } catch (NoSuchAlgorithmException ex) {
                    throw new IllegalStateException(ex);
                }
            }
            @Override
            public void started(TaskEvent te) {
            }

            @Override
            public void finished(TaskEvent te) {
                if (te.getKind() == Kind.ANALYZE) {
                    if (te.getSourceFile().isNameCompatible("module-info", JavaFileObject.Kind.SOURCE)) {
                        ModuleElement mod = (ModuleElement) Trees.instance(jt).getElement(new TreePath(te.getCompilationUnit()));
                        new APIVisitor(apiHash).visit(mod);
                    } else if (te.getSourceFile().isNameCompatible("package-info", JavaFileObject.Kind.SOURCE)) {
                        //ignore - cannot contain important changes (?)
                    } else {
                        TypeElement clazz = te.getTypeElement();
                        ModuleElement mod = jt.getElements().getModuleOf(clazz);
                        Set<PackageElement> thisModulePackages = apiPackages.computeIfAbsent(mod, m -> {
                            return ElementFilter.exportsIn(mod.getDirectives())
                                                .stream()
                                                .map(ed -> ed.getPackage())
                                                .collect(Collectors.toSet());
                        });
                        if (thisModulePackages.contains(jt.getElements().getPackageOf(clazz))) {
                            new APIVisitor(apiHash).visit(clazz);
                        }
                    }
                }
                if (te.getKind() == Kind.COMPILATION && !noApiChange.get()) {
                    String previousSignature = null;
                    File digestFile = new File(args[0]);
                    try (InputStream in = new FileInputStream(digestFile)) {
                        previousSignature = new String(in.readAllBytes(), "UTF-8");
                    } catch (IOException ex) {
                        //ignore
                    }
                    String currentSignature = Depend.this.toString(apiHash.digest());
                    if (!Objects.equals(previousSignature, currentSignature)) {
                        digestFile.getParentFile().mkdirs();
                        try (OutputStream out = new FileOutputStream(digestFile)) {
                            out.write(currentSignature.getBytes("UTF-8"));
                        } catch (IOException ex) {
                            throw new IllegalStateException(ex);
                        }
                    }
                }
            }
        });
    }

    private void addExports() {
        var systemCompiler = ToolProvider.getSystemJavaCompiler();
        try (JavaFileManager jfm = systemCompiler.getStandardFileManager(nullnullnull)) {
            JavaFileManager fm = new ForwardingJavaFileManager<JavaFileManager>(jfm) {
                @Override
                public ClassLoader getClassLoader(JavaFileManager.Location location) {
                    if (location == StandardLocation.CLASS_PATH) {
                        return Depend.class.getClassLoader();
                    }
                    return super.getClassLoader(location);
                }
            };
            ((JavacTask) systemCompiler.getTask(null, fm, null,
                                                List.of("-proc:only""-XDaccessInternalAPI=true"),
                                                List.of("java.lang.Object"), null))
                                       .analyze();
        } catch (IOException ex) {
            throw new IllegalStateException(ex);
        }
    }

    private com.sun.tools.javac.util.List<JCCompilationUnit> doFilteredParse(
            JavaCompiler compiler, Iterable<JavaFileObject> fileObjects, Set<Path> modified,
            Path internalAPIDigestFile, AtomicBoolean noApiChange,
            boolean debug) {
        Map<String, String> internalAPI = new LinkedHashMap<>();
        if (Files.isReadable(internalAPIDigestFile)) {
            try {
                Files.readAllLines(internalAPIDigestFile, StandardCharsets.UTF_8)
                     .forEach(line -> {
                         String[] keyAndValue = line.split("=");
                         internalAPI.put(keyAndValue[0], keyAndValue[1]);
                     });
            } catch (IOException ex) {
                throw new IllegalStateException(ex);
            }
        }
        Map<JavaFileObject, JCCompilationUnit> files2CUT = new IdentityHashMap<>();
        boolean fullRecompile = modified.stream()
                                        .map(Path::toString)
                                        .anyMatch(f -> !StringUtils.toLowerCase(f).endsWith(".java"));
        ListBuffer<JCCompilationUnit> result = new ListBuffer<>();
        for (JavaFileObject jfo : fileObjects) {
            if (modified.contains(Path.of(jfo.getName()))) {
                JCCompilationUnit parsed = compiler.parse(jfo);
                files2CUT.put(jfo, parsed);
                String currentSignature = treeDigest(parsed);
                if (!currentSignature.equals(internalAPI.get(jfo.getName()))) {
                    fullRecompile |= true;
                    internalAPI.put(jfo.getName(), currentSignature);
                }
                result.add(parsed);
            }
        }

        if (fullRecompile) {
            for (JavaFileObject jfo : fileObjects) {
                if (!modified.contains(Path.of(jfo.getName()))) {
                    JCCompilationUnit parsed = files2CUT.get(jfo);
                    if (parsed == null) {
                        parsed = compiler.parse(jfo);
                        internalAPI.put(jfo.getName(), treeDigest(parsed));
                    }
                    result.add(parsed);
                }
            }
            try (OutputStream out = Files.newOutputStream(internalAPIDigestFile)) {
                String hashes = internalAPI.entrySet()
                                           .stream()
                                           .map(e -> e.getKey() + "=" + e.getValue())
                                           .collect(Collectors.joining("\n"));
                out.write(hashes.getBytes(StandardCharsets.UTF_8));
            } catch (IOException ex) {
                throw new IllegalStateException(ex);
            }
        } else {
            noApiChange.set(true);
        }
        if (debug) {
            long allJavaInputs = StreamSupport.stream(fileObjects.spliterator(), false).count();
            String module = StreamSupport.stream(fileObjects.spliterator(), false)
                         .map(fo -> fo.toUri().toString())
                         .filter(path -> path.contains("/share/classes/"))
                         .map(path -> path.substring(0, path.indexOf("/share/classes/")))
                         .map(path -> path.substring(path.lastIndexOf("/") + 1))
                         .findAny()
                         .orElseGet(() -> "unknown");
            String nonJavaModifiedFiles = modified.stream()
                                                  .map(Path::toString)
                                                  .filter(f -> !StringUtils.toLowerCase(f)
                                                                           .endsWith(".java"))
                                                  .collect(Collectors.joining(", "));
            System.err.println("compiling module: " + module +
                               ", all Java inputs: " + allJavaInputs +
                               ", modified files (Java or non-Java): " + modified.size() +
                               ", full recompile: " + fullRecompile +
                               ", non-Java modified files: " + nonJavaModifiedFiles);
        }
        return result.toList();
    }

    private String treeDigest(JCCompilationUnit cu) {
        try {
            TreeVisitor v = new TreeVisitor(MessageDigest.getInstance("MD5"));
            v.scan(cu, null);
            return Depend.this.toString(v.apiHash.digest());
        } catch (NoSuchAlgorithmException ex) {
            throw new IllegalStateException(ex);
        }
    }
    private String toString(byte[] digest) {
        return HexFormat.of().withUpperCase().formatHex(digest);
    }

    private static final class APIVisitor implements ElementVisitor<VoidVoid>,
                                                     TypeVisitor<VoidVoid>,
                                                     AnnotationValueVisitor<VoidVoid>,
                                                     DirectiveVisitor<VoidVoid> {

        private final MessageDigest apiHash;
        private final Charset utf8;

        public APIVisitor(MessageDigest apiHash) {
            this.apiHash = apiHash;
            utf8 = Charset.forName("UTF-8");
        }

        public Void visit(Iterable<? extends Element> list, Void p) {
            list.forEach(e -> visit(e, p));
            return null;
        }

        private void update(CharSequence data) {
            apiHash.update(data.toString().getBytes(utf8));
        }

        private void visit(Iterable<? extends TypeMirror> types) {
            for (TypeMirror type : types) {
                visit(type);
            }
        }

        private void updateAnnotation(AnnotationMirror am) {
            update("@");
            visit(am.getAnnotationType());
            am.getElementValues()
              .keySet()
              .stream()
              .sorted((ee1, ee2) -> ee1.getSimpleName().toString().compareTo(ee2.getSimpleName().toString()))
              .forEach(ee -> {
                  visit(ee);
                  visit(am.getElementValues().get(ee));
              });
        }

        private void updateAnnotations(Iterable<? extends AnnotationMirror> annotations) {
            for (AnnotationMirror am : annotations) {
                if (am.getAnnotationType().asElement().getAnnotation(Documented.class) == null)
                    continue;
                updateAnnotation(am);
            }
        }

        @Override
        public Void visit(Element e, Void p) {
            if (e.getKind() != ElementKind.MODULE &&
                !e.getModifiers().contains(Modifier.PUBLIC) &&
                !e.getModifiers().contains(Modifier.PROTECTED)) {
                return null;
            }
            update(e.getKind().name());
            update(e.getModifiers().stream()
                                   .map(mod -> mod.name())
                                   .collect(Collectors.joining(",""[""]")));
            update(e.getSimpleName());
            updateAnnotations(e.getAnnotationMirrors());
            return e.accept(this, p);
        }

        @Override
        public Void visit(Element e) {
            return visit(e, null);
        }

        @Override
        public Void visitModule(ModuleElement e, Void p) {
            update(String.valueOf(e.isOpen()));
            update(e.getQualifiedName());
            e.getDirectives()
             .stream()
             .forEach(d -> d.accept(thisnull));
            return null;
        }

        @Override
        public Void visitPackage(PackageElement e, Void p) {
            throw new UnsupportedOperationException("Should not get here.");
        }

        @Override
        public Void visitType(TypeElement e, Void p) {
            visit(e.getTypeParameters(), p);
            visit(e.getSuperclass());
            visit(e.getInterfaces());
            visit(e.getEnclosedElements(), p);
            return null;
        }

        @Override
        public Void visitRecordComponent(@SuppressWarnings("preview")RecordComponentElement e, Void p) {
            update(e.getSimpleName());
            visit(e.asType());
            return null;
        }

        @Override
        public Void visitVariable(VariableElement e, Void p) {
            visit(e.asType());
            update(String.valueOf(e.getConstantValue()));
            return null;
        }

        @Override
        public Void visitExecutable(ExecutableElement e, Void p) {
            update("<");
            visit(e.getTypeParameters(), p);
            update(">");
            visit(e.getReturnType());
            update("(");
            visit(e.getParameters(), p);
            update(")");
            visit(e.getThrownTypes());
            update(String.valueOf(e.getDefaultValue()));
            update(String.valueOf(e.isVarArgs()));
            return null;
        }

        @Override
        public Void visitTypeParameter(TypeParameterElement e, Void p) {
            visit(e.getBounds());
            return null;
        }

        @Override
        public Void visitUnknown(Element e, Void p) {
            throw new UnsupportedOperationException("Not supported.");
        }

        @Override
        public Void visit(TypeMirror t, Void p) {
            if (t == null) {
                update("null");
                return null;
            }
            update(t.getKind().name());
            updateAnnotations(t.getAnnotationMirrors());
            t.accept(this, p);
            return null;
        }

        @Override
        public Void visitPrimitive(PrimitiveType t, Void p) {
            return null//done
        }

        @Override
        public Void visitNull(NullType t, Void p) {
            return null//done
        }

        @Override
        public Void visitArray(ArrayType t, Void p) {
            visit(t.getComponentType());
            update("[]");
            return null;
        }

        @Override
        public Void visitDeclared(DeclaredType t, Void p) {
            update(((QualifiedNameable) t.asElement()).getQualifiedName());
            update("<");
            visit(t.getTypeArguments());
            update(">");
            return null;
        }

        @Override
        public Void visitError(ErrorType t, Void p) {
            return visitDeclared(t, p);
        }

        private final Set<Element> seenVariables = new HashSet<>();

        @Override
        public Void visitTypeVariable(TypeVariable t, Void p) {
            Element decl = t.asElement();
            if (!seenVariables.add(decl)) {
                return null;
            }
            visit(decl, null);
            visit(t.getLowerBound(), null);
            visit(t.getUpperBound(), null);
            seenVariables.remove(decl);
            return null;
        }

        @Override
        public Void visitWildcard(WildcardType t, Void p) {
            visit(t.getSuperBound());
            visit(t.getExtendsBound());
            return null;
        }

        @Override
        public Void visitExecutable(ExecutableType t, Void p) {
            throw new UnsupportedOperationException("Not supported.");
        }

        @Override
        public Void visitNoType(NoType t, Void p) {
            return null;//done
        }

        @Override
        public Void visitUnknown(TypeMirror t, Void p) {
            throw new UnsupportedOperationException("Not supported.");
        }

        @Override
        public Void visitUnion(UnionType t, Void p) {
            update("(");
            visit(t.getAlternatives());
            update(")");
            return null;
        }

        @Override
        public Void visitIntersection(IntersectionType t, Void p) {
            update("(");
            visit(t.getBounds());
            update(")");
            return null;
        }

        @Override
        public Void visit(AnnotationValue av, Void p) {
            return av.accept(this, p);
        }

        @Override
        public Void visitBoolean(boolean b, Void p) {
            update(String.valueOf(b));
            return null;
        }

        @Override
        public Void visitByte(byte b, Void p) {
            update(String.valueOf(b));
            return null;
        }

        @Override
        public Void visitChar(char c, Void p) {
            update(String.valueOf(c));
            return null;
        }

        @Override
        public Void visitDouble(double d, Void p) {
            update(String.valueOf(d));
            return null;
        }

        @Override
        public Void visitFloat(float f, Void p) {
            update(String.valueOf(f));
            return null;
        }

        @Override
        public Void visitInt(int i, Void p) {
            update(String.valueOf(i));
            return null;
        }

        @Override
        public Void visitLong(long i, Void p) {
            update(String.valueOf(i));
            return null;
        }

        @Override
        public Void visitShort(short s, Void p) {
            update(String.valueOf(s));
            return null;
        }

        @Override
        public Void visitString(String s, Void p) {
            update(s);
            return null;
        }

        @Override
        public Void visitType(TypeMirror t, Void p) {
            return visit(t);
        }

        @Override
        public Void visitEnumConstant(VariableElement c, Void p) {
            return visit(c);
        }

        @Override
        public Void visitAnnotation(AnnotationMirror a, Void p) {
            updateAnnotation(a);
            return null;
        }

        @Override
        public Void visitArray(List<? extends AnnotationValue> vals, Void p) {
            update("(");
            for (AnnotationValue av : vals) {
                visit(av);
            }
            update(")");
            return null;
        }

        @Override
        public Void visitUnknown(AnnotationValue av, Void p) {
            throw new UnsupportedOperationException("Not supported.");
        }

        @Override
        public Void visitRequires(RequiresDirective d, Void p) {
            update("RequiresDirective");
            update(String.valueOf(d.isStatic()));
            update(String.valueOf(d.isTransitive()));
            update(d.getDependency().getQualifiedName());
            return null;
        }

        @Override
        public Void visitExports(ExportsDirective d, Void p) {
            update("ExportsDirective");
            update(d.getPackage().getQualifiedName());
            if (d.getTargetModules() != null) {
                for (ModuleElement me : d.getTargetModules()) {
                    update(me.getQualifiedName());
                }
            } else {
                update("");
            }
            return null;
        }

        @Override
        public Void visitOpens(OpensDirective d, Void p) {
            update("OpensDirective");
            update(d.getPackage().getQualifiedName());
            if (d.getTargetModules() != null) {
                for (ModuleElement me : d.getTargetModules()) {
                    update(me.getQualifiedName());
                }
            } else {
                update("");
            }
            return null;
        }

        @Override
        public Void visitUses(UsesDirective d, Void p) {
            update("UsesDirective");
            update(d.getService().getQualifiedName());
            return null;
        }

        @Override
        public Void visitProvides(ProvidesDirective d, Void p) {
            update("ProvidesDirective");
            update(d.getService().getQualifiedName());
            update("(");
            for (TypeElement impl : d.getImplementations()) {
                update(impl.getQualifiedName());
            }
            update(")");
            return null;
        }

    }

    private static final class TreeVisitor extends TreeScanner<VoidVoid> {

        private final Set<Name> seenIdentifiers = new HashSet<>();
        private final MessageDigest apiHash;
        private final Charset utf8;

        public TreeVisitor(MessageDigest apiHash) {
            this.apiHash = apiHash;
            utf8 = Charset.forName("UTF-8");
        }

        private void update(CharSequence data) {
            apiHash.update(data.toString().getBytes(utf8));
        }

        @Override
        public Void scan(Tree tree, Void p) {
            update("(");
            if (tree != null) {
                update(tree.getKind().name());
            };
            super.scan(tree, p);
            update(")");
            return null;
        }

        @Override
        public Void visitCompilationUnit(CompilationUnitTree node, Void p) {
            seenIdentifiers.clear();
            scan(node.getPackage(), p);
            scan(node.getTypeDecls(), p);
            scan(((JCCompilationUnit) node).getModuleDecl(), p);
            List<ImportTree> importantImports = new ArrayList<>();
            for (ImportTree imp : node.getImports()) {
                Tree t = imp.getQualifiedIdentifier();
                if (t.getKind() == Tree.Kind.MEMBER_SELECT) {
                    Name member = ((MemberSelectTree) t).getIdentifier();
                    if (member.contentEquals("*") || seenIdentifiers.contains(member)) {
                        importantImports.add(imp);
                    }
                } else {
                    //should not happen, possibly erroneous source?
                    importantImports.add(imp);
                }
            }
            importantImports.sort((imp1, imp2) -> {
                if (imp1.isStatic() ^ imp2.isStatic()) {
                    return imp1.isStatic() ? -1 : 1;
                } else {
                    return imp1.getQualifiedIdentifier().toString().compareTo(imp2.getQualifiedIdentifier().toString());
                }
            });
            scan(importantImports, p);
            return null;
        }

        @Override
        public Void visitIdentifier(IdentifierTree node, Void p) {
            update(node.getName());
            seenIdentifiers.add(node.getName());
            return super.visitIdentifier(node, p);
        }

        @Override
        public Void visitMemberSelect(MemberSelectTree node, Void p) {
            update(node.getIdentifier());
            return super.visitMemberSelect(node, p);
        }

        @Override
        public Void visitMemberReference(MemberReferenceTree node, Void p) {
            update(node.getName());
            return super.visitMemberReference(node, p);
        }

        @Override
        public Void scan(Iterable<? extends Tree> nodes, Void p) {
            update("(");
            super.scan(nodes, p);
            update(")");
            return null;
        }

        @Override
        public Void visitClass(ClassTree node, Void p) {
            update(node.getSimpleName());
            scan(node.getModifiers(), p);
            scan(node.getTypeParameters(), p);
            scan(node.getExtendsClause(), p);
            scan(node.getImplementsClause(), p);
            scan(node.getMembers()
                     .stream()
                     .filter(this::importantMember)
                     .collect(Collectors.toList()),
                 p);
            return null;
        }

        private boolean importantMember(Tree m) {
            return switch (m.getKind()) {
                case ANNOTATION_TYPE, CLASSENUMINTERFACE, RECORD ->
                    !isPrivate(((ClassTree) m).getModifiers());
                case METHOD ->
                    !isPrivate(((MethodTree) m).getModifiers());
                case VARIABLE ->
                    !isPrivate(((VariableTree) m).getModifiers()) ||
                    isRecordComponent((VariableTree) m);
                case BLOCK -> false;
                default -> throw new IllegalStateException("Unexpected tree kind: " + m.getKind());
            };
        }

        private boolean isPrivate(ModifiersTree mt) {
            return mt.getFlags().contains(Modifier.PRIVATE);
        }

        private boolean isRecordComponent(VariableTree vt) {
            return (((JCVariableDecl) vt).mods.flags & Flags.RECORD) != 0;
        }

        @Override
        public Void visitVariable(VariableTree node, Void p) {
            update(node.getName());
            return super.visitVariable(node, p);
        }

        @Override
        public Void visitMethod(MethodTree node, Void p) {
            update(node.getName());
            scan(node.getModifiers(), p);
            scan(node.getReturnType(), p);
            scan(node.getTypeParameters(), p);
            scan(node.getParameters(), p);
            scan(node.getReceiverParameter(), p);
            scan(node.getThrows(), p);
            scan(node.getDefaultValue(), p);
            return null;
        }

        @Override
        public Void visitLiteral(LiteralTree node, Void p) {
            update(String.valueOf(node.getValue()));
            return super.visitLiteral(node, p);
        }

        @Override
        public Void visitModifiers(ModifiersTree node, Void p) {
            update(node.getFlags().toString());
            return super.visitModifiers(node, p);
        }

    }

    private class FilteredInitialFileParser implements InvocationHandler {

        private final JavaCompiler compiler;
        private final Set<Path> modified;
        private final Path internalAPIDigestFile;
        private final AtomicBoolean noApiChange;
        private final boolean debug;

        public FilteredInitialFileParser(JavaCompiler compiler,
                                         Set<Path> modified,
                                         Path internalAPIDigestFile,
                                         AtomicBoolean noApiChange,
                                         boolean debug) {
            this.compiler = compiler;
            this.modified = modified;
            this.internalAPIDigestFile = internalAPIDigestFile;
            this.noApiChange = noApiChange;
            this.debug = debug;
        }

        @Override
        @SuppressWarnings("unchecked")
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            return switch (method.getName()) {
                case "parse" -> doFilteredParse(compiler,
                                                (Iterable<JavaFileObject>) args[0],
                                                modified,
                                                internalAPIDigestFile,
                                                noApiChange,
                                                debug);
                default -> throw new UnsupportedOperationException();
            };
        }
    }
}

Messung V0.5
C=94 H=87 G=90

¤ Dauer der Verarbeitung: 0.20 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.