/* * 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.macro.transform; import org.codehaus.groovy.ast.ClassHelper; import org.codehaus.groovy.ast.ClassNode; import org.codehaus.groovy.ast.MethodNode; import org.codehaus.groovy.ast.Parameter; import org.codehaus.groovy.macro.runtime.Macro; import org.codehaus.groovy.runtime.m12n.ExtensionModule; import org.codehaus.groovy.runtime.m12n.ExtensionModuleScanner; import org.codehaus.groovy.runtime.m12n.MetaInfExtensionModule; import org.codehaus.groovy.transform.stc.ExtensionMethodNode; import java.util.*; import java.util.concurrent.locks.ReentrantReadWriteLock; /** * TODO share some code with {@link org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.ExtensionMethodCache} * @author Sergei Egorov <bsideup@gmail.com> * @since 2.5.0 */ class MacroMethodsCache { private static final ClassNode MACRO_ANNOTATION_CLASS_NODE = ClassHelper.make(Macro.class); private static volatile Map<ClassLoader, Map<String, List<MethodNode>>> CACHE = new WeakHashMap<>(); private static final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(); public static Map<String, List<MethodNode>> get(ClassLoader classLoader) { try { lock.readLock().lock(); if (!CACHE.containsKey(classLoader)) { lock.readLock().unlock(); lock.writeLock().lock(); try { if (!CACHE.containsKey(classLoader)) { WeakHashMap<ClassLoader, Map<String, List<MethodNode>>> newCache = new WeakHashMap<>(CACHE); Map<String, List<MethodNode>> methods = getMacroMethodsFromClassLoader(classLoader); newCache.put(classLoader, methods); CACHE = Collections.unmodifiableMap(newCache); } } finally { lock.readLock().lock(); lock.writeLock().unlock(); } } return CACHE.get(classLoader); } finally { lock.readLock().unlock(); } } protected static Map<String, List<MethodNode>> getMacroMethodsFromClassLoader(ClassLoader classLoader) { final Map<String, List<MethodNode>> result = new HashMap<String, List<MethodNode>>(); ExtensionModuleScanner.ExtensionModuleListener listener = new ExtensionModuleScanner.ExtensionModuleListener() { @Override public void onModule(ExtensionModule module) { if (!(module instanceof MetaInfExtensionModule)) { return; } MetaInfExtensionModule extensionModule = (MetaInfExtensionModule) module; scanExtClasses(result, extensionModule.getInstanceMethodsExtensionClasses(), false); scanExtClasses(result, extensionModule.getStaticMethodsExtensionClasses(), true); } }; ExtensionModuleScanner macroModuleScanner = new ExtensionModuleScanner(listener, classLoader); macroModuleScanner.scanClasspathModules(); for (Map.Entry<String, List<MethodNode>> entry : result.entrySet()) { result.put(entry.getKey(), Collections.unmodifiableList(entry.getValue())); } return Collections.unmodifiableMap(result); } private static void scanExtClasses(Map<String, List<MethodNode>> accumulator, List<Class> classes, boolean isStatic) { for (Class dgmLikeClass : classes) { ClassNode cn = ClassHelper.makeWithoutCaching(dgmLikeClass, true); for (MethodNode metaMethod : cn.getMethods()) { Parameter[] types = metaMethod.getParameters(); if (!(metaMethod.isStatic() && metaMethod.isPublic())) { continue; } if (types.length == 0) { continue; } if (metaMethod.getAnnotations(MACRO_ANNOTATION_CLASS_NODE).isEmpty()) { continue; } Parameter[] parameters = new Parameter[types.length - 1]; System.arraycopy(types, 1, parameters, 0, parameters.length); ExtensionMethodNode node = new ExtensionMethodNode( metaMethod, metaMethod.getName(), metaMethod.getModifiers(), metaMethod.getReturnType(), parameters, ClassNode.EMPTY_ARRAY, null, isStatic); node.setGenericsTypes(metaMethod.getGenericsTypes()); ClassNode declaringClass = types[0].getType(); node.setDeclaringClass(declaringClass); List<MethodNode> macroMethods = accumulator.get(metaMethod.getName()); if (macroMethods == null) { macroMethods = new ArrayList<>(); accumulator.put(metaMethod.getName(), macroMethods); } macroMethods.add(node); } } } }