You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by su...@apache.org on 2020/02/19 01:27:29 UTC

[groovy] branch danielsun/tweak-resolving updated (9fdfb92 -> 0b40947)

This is an automated email from the ASF dual-hosted git repository.

sunlan pushed a change to branch danielsun/tweak-resolving
in repository https://gitbox.apache.org/repos/asf/groovy.git.


 discard 9fdfb92  Fix the failing JDK8 build
 discard 3154026  Fix the failing build
 discard d32fa0d  Try to output the log - 2
 discard bdd8599  Try to output the log
 discard 045ea6b  Warmup the cache of resolve visitor
 discard 9e5bf89  Add `ClassFinder` to tweak resolving types
     new 0b40947  GROOVY-9408: Avoid unnecessary looking up default import classes when resolving types

This update added new revisions after undoing existing revisions.
That is to say, some revisions that were in the old version of the
branch are not in the new version.  This situation occurs
when a user --force pushes a change and generates a repository
containing something like this:

 * -- * -- B -- O -- O -- O   (9fdfb92)
            \
             N -- N -- N   refs/heads/danielsun/tweak-resolving (0b40947)

You should already have received notification emails for all of the O
revisions, and so the following emails describe only the N revisions
from the common base, B.

Any revisions marked "omit" are not gone; other references still
refer to them.  Any revisions marked "discard" are gone forever.

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../codehaus/groovy/control/ResolveVisitor.java    | 10 +---
 .../org/codehaus/groovy/vmplugin/VMPlugin.java     |  7 +++
 .../org/codehaus/groovy/vmplugin/v9/Java9.java     | 60 ++++++++++++++--------
 3 files changed, 47 insertions(+), 30 deletions(-)


[groovy] 01/01: GROOVY-9408: Avoid unnecessary looking up default import classes when resolving types

Posted by su...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

sunlan pushed a commit to branch danielsun/tweak-resolving
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit 0b409479e139d48887387f47bed286e30f89c7d4
Author: Daniel Sun <su...@apache.org>
AuthorDate: Wed Feb 19 09:22:16 2020 +0800

    GROOVY-9408: Avoid unnecessary looking up default import classes when resolving types
---
 .../codehaus/groovy/control/ResolveVisitor.java    |   5 +
 .../org/codehaus/groovy/vmplugin/VMPlugin.java     |  14 +++
 .../codehaus/groovy/vmplugin/v9/ClassFinder.java   | 125 +++++++++++++++++++++
 .../org/codehaus/groovy/vmplugin/v9/Java9.java     |  60 ++++++++++
 .../groovy/vmplugin/v9/ClassFinderTest.groovy      |  95 ++++++++++++++++
 5 files changed, 299 insertions(+)

diff --git a/src/main/java/org/codehaus/groovy/control/ResolveVisitor.java b/src/main/java/org/codehaus/groovy/control/ResolveVisitor.java
index 07c3c88..4395dc8 100644
--- a/src/main/java/org/codehaus/groovy/control/ResolveVisitor.java
+++ b/src/main/java/org/codehaus/groovy/control/ResolveVisitor.java
@@ -67,6 +67,7 @@ import org.codehaus.groovy.runtime.memoize.EvictableCache;
 import org.codehaus.groovy.runtime.memoize.UnlimitedConcurrentCache;
 import org.codehaus.groovy.syntax.Types;
 import org.codehaus.groovy.transform.trait.Traits;
+import org.codehaus.groovy.vmplugin.VMPluginFactory;
 import org.objectweb.asm.Opcodes;
 
 import java.lang.annotation.Annotation;
@@ -636,6 +637,10 @@ public class ResolveVisitor extends ClassCodeExpressionTransformer {
     }
 
     private static final EvictableCache<String, Set<String>> DEFAULT_IMPORT_CLASS_AND_PACKAGES_CACHE = new UnlimitedConcurrentCache<>();
+    static {
+        Map<String, Set<String>> defaultImportClasses = VMPluginFactory.getPlugin().getDefaultImportClasses(DEFAULT_IMPORTS);
+        DEFAULT_IMPORT_CLASS_AND_PACKAGES_CACHE.putAll(defaultImportClasses);
+    }
 
     protected boolean resolveFromDefaultImports(final ClassNode type, final String[] packagePrefixes) {
         String typeName = type.getName();
diff --git a/src/main/java/org/codehaus/groovy/vmplugin/VMPlugin.java b/src/main/java/org/codehaus/groovy/vmplugin/VMPlugin.java
index 40ba4fb..7ef1065 100644
--- a/src/main/java/org/codehaus/groovy/vmplugin/VMPlugin.java
+++ b/src/main/java/org/codehaus/groovy/vmplugin/VMPlugin.java
@@ -26,6 +26,9 @@ import org.codehaus.groovy.ast.CompileUnit;
 
 import java.lang.reflect.AccessibleObject;
 import java.lang.reflect.Method;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
 
 /**
  * Interface to access VM version based actions.
@@ -111,4 +114,15 @@ public interface VMPlugin {
      * @return the transformed meta method
      */
     MetaMethod transformMetaMethod(MetaClass metaClass, MetaMethod metaMethod);
+
+    /**
+     * Returns the default import classes: class name -> the relevant package names
+     *
+     * @param packageNames the default import package names, e.g. java.lang.
+     * @return the default import classes
+     * @since 3.0.2
+     */
+    default Map<String, Set<String>> getDefaultImportClasses(String[] packageNames) {
+        return Collections.emptyMap();
+    }
 }
diff --git a/src/main/java/org/codehaus/groovy/vmplugin/v9/ClassFinder.java b/src/main/java/org/codehaus/groovy/vmplugin/v9/ClassFinder.java
new file mode 100644
index 0000000..90d0c03
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/vmplugin/v9/ClassFinder.java
@@ -0,0 +1,125 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.codehaus.groovy.vmplugin.v9;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URI;
+import java.nio.file.FileSystem;
+import java.nio.file.FileSystemAlreadyExistsException;
+import java.nio.file.FileSystems;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+/**
+ * Find classes under the specified package
+ *
+ * @since 3.0.2
+ */
+public class ClassFinder {
+    public static Map<String, Set<String>> find(URI originalUri, String packageName) {
+        return find(originalUri, packageName, false);
+    }
+
+    public static Map<String, Set<String>> find(URI originalUri, String packageName, boolean recursive) {
+        URI uri;
+        String prefix;
+        if ("jrt".equals(originalUri.getScheme())) {
+            uri = URI.create("jrt:/");
+            prefix = originalUri.getPath().substring(1) + "/";
+        } else {
+            Path path = Paths.get(originalUri);
+            if (Files.isDirectory(path)) {
+                uri = URI.create("file:/");
+                prefix = path.toString();
+            } else {
+                uri = URI.create("jar:" + originalUri.toString());
+                prefix = "";
+            }
+        }
+
+        return find(uri, prefix, packageName, recursive);
+    }
+
+    private static Map<String, Set<String>> find(URI uri, String prefix, String packageName, boolean recursive) {
+        boolean wfs = "file".equals(uri.getScheme());
+        if (wfs) prefix = prefix.replace("/", File.separator);
+
+        final String sepPatten = Pattern.quote(wfs ? File.separator : "/");
+        final int prefixElemCnt = prefix.trim().isEmpty() ? 0 : prefix.split(sepPatten).length;
+
+        Map<String, Set<String>> result = new LinkedHashMap<>();
+        try (FileSystem fs = newFileSystem(uri)) {
+            Files.walkFileTree(fs.getPath(prefix + "/" + packageName), new SimpleFileVisitor<Path>() {
+                @Override
+                public FileVisitResult preVisitDirectory(Path path, BasicFileAttributes attrs) {
+                    return FileVisitResult.CONTINUE;
+                }
+
+                @Override
+                public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) {
+                    String[] pathElems = path.toString().split(sepPatten);
+                    int nameCount = pathElems.length;
+                    if (nameCount <= prefixElemCnt) {
+                        return FileVisitResult.CONTINUE;
+                    }
+
+                    String filename = pathElems[nameCount - 1];
+                    if (!filename.endsWith(".class") || "module-info.class".equals(filename) || "package-info.class".equals(filename)) {
+                        return FileVisitResult.CONTINUE;
+                    }
+
+                    String packagePathStr = Arrays.stream(Arrays.copyOfRange(pathElems, prefixElemCnt + (!wfs && pathElems[0].isEmpty() ? 1 : 0), nameCount - 1)).collect(Collectors.joining("/"));
+
+                    if (recursive || packageName.equals(packagePathStr)) {
+                        Set<String> packageNameSet = result.computeIfAbsent(filename.substring(0, filename.lastIndexOf(".")), f -> new HashSet<>(2));
+                        packageNameSet.add(packagePathStr);
+                    }
+
+                    return FileVisitResult.CONTINUE;
+                }
+            });
+        } catch (UnsupportedOperationException ignored) {
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+        return result;
+    }
+
+    private static FileSystem newFileSystem(URI uri) throws IOException {
+        try {
+            return FileSystems.newFileSystem(uri, Collections.emptyMap());
+        } catch (FileSystemAlreadyExistsException e) {
+            return FileSystems.getFileSystem(uri);
+        }
+    }
+}
diff --git a/src/main/java/org/codehaus/groovy/vmplugin/v9/Java9.java b/src/main/java/org/codehaus/groovy/vmplugin/v9/Java9.java
index cfcb9d8..b320de7 100644
--- a/src/main/java/org/codehaus/groovy/vmplugin/v9/Java9.java
+++ b/src/main/java/org/codehaus/groovy/vmplugin/v9/Java9.java
@@ -19,6 +19,7 @@
 package org.codehaus.groovy.vmplugin.v9;
 
 import groovy.lang.GroovyRuntimeException;
+import groovy.lang.GroovySystem;
 import groovy.lang.MetaClass;
 import groovy.lang.MetaMethod;
 import groovy.lang.Tuple;
@@ -27,6 +28,7 @@ import org.codehaus.groovy.GroovyBugError;
 import org.codehaus.groovy.reflection.CachedClass;
 import org.codehaus.groovy.reflection.CachedMethod;
 import org.codehaus.groovy.reflection.ReflectionUtils;
+import org.codehaus.groovy.runtime.DefaultGroovyMethods;
 import org.codehaus.groovy.vmplugin.v8.Java8;
 
 import java.lang.invoke.MethodHandle;
@@ -41,9 +43,12 @@ import java.lang.reflect.Member;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
 import java.math.BigInteger;
+import java.net.URI;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.LinkedHashMap;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
@@ -55,6 +60,61 @@ import java.util.stream.Collectors;
  * Additional Java 9 based functions will be added here as needed.
  */
 public class Java9 extends Java8 {
+    @Override
+    public Map<String, Set<String>> getDefaultImportClasses(String[] packageNames) {
+        Map<String, Set<String>> result = new LinkedHashMap<>();
+
+        List<String> javaPns = new ArrayList<>(4);
+        List<String> groovyPns = new ArrayList<>(4);
+        for (String prefix : packageNames) {
+            String pn = prefix.substring(0, prefix.length() - 1).replace('.', '/');
+
+            if (pn.startsWith("java/")) {
+                javaPns.add(pn);
+            } else if (pn.startsWith("groovy/")) {
+                groovyPns.add(pn);
+            }
+        }
+
+        ClassFinder.find(URI.create("jrt:/modules/java.base/"), "java", true)
+                .entrySet().stream()
+                .filter(e -> e.getValue().stream().anyMatch(javaPns::contains))
+                .collect(
+                        Collectors.toMap(
+                                Map.Entry::getKey,
+                                entry -> entry.getValue().stream()
+                                            .map(e -> e.replace('/', '.') + ".")
+                                            .collect(Collectors.toSet())
+                        )
+                );
+
+        result.putAll(doFindClasses(URI.create("jrt:/modules/java.base/"), "java", javaPns));
+
+        try {
+            URI gsLocation = DefaultGroovyMethods.getLocation(GroovySystem.class).toURI();
+            result.putAll(doFindClasses(gsLocation, "groovy", groovyPns));
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+        return result;
+    }
+
+    private static Map<String, Set<String>> doFindClasses(URI uri, String packageName, List<String> defaultPackageNames) {
+        Map<String, Set<String>> result = ClassFinder.find(uri, packageName, true)
+                .entrySet().stream()
+                .filter(e -> e.getValue().stream().anyMatch(defaultPackageNames::contains))
+                .collect(
+                        Collectors.toMap(
+                                Map.Entry::getKey,
+                                entry -> entry.getValue().stream()
+                                        .map(e -> e.replace('/', '.') + ".")
+                                        .collect(Collectors.toSet())
+                        )
+                );
+        return result;
+    }
+
 
     private static class LookupHolder {
         private static final Method PRIVATE_LOOKUP;
diff --git a/src/test/org/codehaus/groovy/vmplugin/v9/ClassFinderTest.groovy b/src/test/org/codehaus/groovy/vmplugin/v9/ClassFinderTest.groovy
new file mode 100644
index 0000000..e3afd8a
--- /dev/null
+++ b/src/test/org/codehaus/groovy/vmplugin/v9/ClassFinderTest.groovy
@@ -0,0 +1,95 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package org.codehaus.groovy.vmplugin.v9
+
+
+import org.junit.Test
+
+class ClassFinderTest {
+    @Test
+    void findGroovyClass() {
+        Map<String, Set<String>> result = ClassFinder.find(GroovySystem.location.toURI(), "groovy/lang")
+        assert ["groovy/lang"] == result.get("GString")?.toList()
+        assert null == result.get("GroovydocHolder")
+    }
+
+    @Test
+    void findGroovyClass2() {
+        Map<String, Set<String>> result = ClassFinder.find(GroovySystem.location.toURI(), "groovy/util")
+        assert ["groovy/util"] == result.get("NodeBuilder")?.toList()
+    }
+
+    @Test
+    void findGroovyClass3() {
+        Map<String, Set<String>> result = ClassFinder.find(org.codehaus.groovy.control.ResolveVisitor.location.toURI(), "org/codehaus/groovy/control")
+        assert ["org/codehaus/groovy/control"] == result.get("ResolveVisitor")?.toList()
+    }
+
+    @Test
+    void findGroovyClassRecursive() {
+        Map<String, Set<String>> result = ClassFinder.find(GroovySystem.location.toURI(), "groovy/lang", true)
+        assert ["groovy/lang"] == result.get("GString")?.toList()
+        assert ["groovy/lang/groovydoc"] == result.get("GroovydocHolder")?.toList()
+    }
+
+    @Test
+    void findJavaClass() {
+        Map<String, Set<String>> result = ClassFinder.find(URI.create("jrt:/modules/java.base/"), "java/lang")
+        assert ["java/lang"] == result.get("String")?.toList()
+        assert null == result.get("MethodHandle")
+    }
+
+    @Test
+    void findJavaClass2() {
+        Map<String, Set<String>> result = ClassFinder.find(URI.create("jrt:/modules/java.base/"), "java/util")
+        assert ["java/util"] == result.get("Map")?.toList()
+    }
+
+    @Test
+    void findJavaClass3() {
+        Map<String, Set<String>> result = ClassFinder.find(URI.create("jrt:/modules/java.base/"), "java/io")
+        assert ["java/io"] == result.get("InputStream")?.toList()
+    }
+
+    @Test
+    void findJavaClass4() {
+        Map<String, Set<String>> result = ClassFinder.find(URI.create("jrt:/modules/java.base/"), "java/net")
+        assert ["java/net"] == result.get("Inet4Address")?.toList()
+    }
+
+    @Test
+    void findJavaClassRecursive() {
+        Map<String, Set<String>> result = ClassFinder.find(URI.create("jrt:/modules/java.base/"), "java/lang", true)
+        assert ["java/lang/invoke"] == result.get("MethodHandle")?.toList()
+    }
+
+    @Test
+    void findJarClass() {
+        Map<String, Set<String>> result = ClassFinder.find(org.antlr.v4.runtime.tree.ParseTree.location.toURI(), "org/antlr/v4/runtime/tree")
+        assert ["org/antlr/v4/runtime/tree"] == result.get("ParseTree")?.toList()
+        assert null == result.get("ParseTreePattern")
+    }
+
+    @Test
+    void findJarClassRecursive() {
+        Map<String, Set<String>> result = ClassFinder.find(org.antlr.v4.runtime.tree.ParseTree.location.toURI(), "org/antlr/v4/runtime/tree", true)
+        assert ["org/antlr/v4/runtime/tree"] == result.get("ParseTree")?.toList()
+        assert ["org/antlr/v4/runtime/tree/pattern"] == result.get("ParseTreePattern")?.toList()
+    }
+}