Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* 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 groovy.lang.GroovyClassLoader;
import groovy.lang.GroovyRuntimeException;
import groovy.lang.MetaClass;
import groovy.lang.MetaMethod;
import groovy.lang.Tuple;
import groovy.lang.Tuple2;
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;
import java.lang.invoke.MethodHandles;
import java.lang.module.ModuleDescriptor;
import java.lang.module.ModuleFinder;
import java.lang.module.ModuleReference;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
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;
import java.util.Optional;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
/**
* Additional Java 9 based functions will be added here as needed.
*/
public class Java9 extends Java8 {
private static final Logger LOGGER = Logger.getLogger(Java9.class.getName());
@Override
public Map> getDefaultImportClasses(String[] packageNames) {
List javaPns = new ArrayList<>(4);
List 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);
} else {
throw new GroovyBugError("unexpected package: " + pn);
}
}
Map> result = new LinkedHashMap<>(2048);
try {
result.putAll(doFindClasses(URI.create("jrt:/modules/java.base/"), "java", javaPns));
GroovyClassLoader gcl = new GroovyClassLoader(this.getClass().getClassLoader());
URI gsLocation = DefaultGroovyMethods.getLocation(gcl.loadClass("groovy.lang.GroovySystem")).toURI();
result.putAll(doFindClasses(gsLocation, "groovy", groovyPns));
// in production environment, groovy-core classes, e.g. `GroovySystem`(java class) and `GrapeIvy`(groovy class) are all packaged in the groovy-core jar file,
// but in Groovy development environment, groovy-core classes are distributed in different directories
URI giLocation = DefaultGroovyMethods.getLocation(gcl.loadClass("groovy.grape.GrapeIvy")).toURI();
if (!gsLocation.equals(giLocation)) {
result.putAll(doFindClasses(giLocation, "groovy", groovyPns));
}
} catch (Exception ignore) {
if (LOGGER.isLoggable(Level.FINEST)) {
LOGGER.finest("[WARNING] Failed to find default imported classes:\n" + DefaultGroovyMethods.asString(ignore));
}
}
return result;
}
private static Map> doFindClasses(URI uri, String packageName, List defaultPackageNames) {
Map> 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()
.filter(e -> defaultPackageNames.contains(e))
.map(e -> e.replace('/', '.') + ".")
.collect(Collectors.toSet())
)
);
return result;
}
private static class LookupHolder {
private static final Method PRIVATE_LOOKUP;
private static final Constructor LOOKUP_Constructor;
static {
Constructor lookup = null;
Method privateLookup = null;
try { // java 9
privateLookup = MethodHandles.class.getMethod("privateLookupIn", Class.class, MethodHandles.Lookup.class);
} catch (final NoSuchMethodException | RuntimeException e) { // java 8 or fallback if anything else goes wrong
try {
lookup = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class, Integer.TYPE);
if (!lookup.isAccessible()) {
ReflectionUtils.trySetAccessible(lookup);
}
} catch (final NoSuchMethodException ex) {
throw new IllegalStateException("Incompatible JVM", e);
}
}
PRIVATE_LOOKUP = privateLookup;
LOOKUP_Constructor = lookup;
}
}
private static Constructor getLookupConstructor() {
return LookupHolder.LOOKUP_Constructor;
}
private static Method getPrivateLookup() {
return LookupHolder.PRIVATE_LOOKUP;
}
public static MethodHandles.Lookup of(final Class> declaringClass) {
try {
final Method privateLookup = getPrivateLookup();
if (privateLookup != null) {
return (MethodHandles.Lookup) privateLookup.invoke(null, declaringClass, MethodHandles.lookup());
}
return getLookupConstructor().newInstance(declaringClass, MethodHandles.Lookup.PRIVATE).in(declaringClass);
} catch (final IllegalAccessException | InstantiationException e) {
throw new IllegalArgumentException(e);
} catch (final InvocationTargetException e) {
throw new GroovyRuntimeException(e);
}
}
@Override
public int getVersion() {
return 9;
}
@Override
public Object getInvokeSpecialHandle(Method method, Object receiver) {
final Class> receiverType = receiver.getClass();
try {
return of(receiverType).unreflectSpecial(method, receiverType).bindTo(receiver);
} catch (ReflectiveOperationException e) {
return super.getInvokeSpecialHandle(method, receiver);
}
}
/**
* This method may be used by a caller in class C to check whether to enable access to a member of declaring class D successfully
* if {@link Java8#checkCanSetAccessible(java.lang.reflect.AccessibleObject, java.lang.Class)} returns true and any of the following hold:
*
* 1) C and D are in the same module.
* 2) The member is public and D is public in a package that the module containing D exports to at least the module containing C.
* 3) The member is protected static, D is public in a package that the module containing D exports to at least the module containing C, and C is a subclass of D.
* 4) D is in a package that the module containing D opens to at least the module containing C. All packages in unnamed and open modules are open to all modules and so this method always succeeds when D is in an unnamed or open module.
*
* @param accessibleObject the accessible object to check
* @param callerClass the callerClass to invoke {@code setAccessible}
* @return the check result
*/
public boolean checkCanSetAccessible(AccessibleObject accessibleObject, Class> callerClass) {
if (!super.checkCanSetAccessible(accessibleObject, callerClass)) return false;
if (callerClass == MethodHandle.class) {
throw new IllegalCallerException(); // should not happen
}
if (!(accessibleObject instanceof Member)) {
throw new IllegalArgumentException("accessibleObject should be a member of type: " + accessibleObject); // should not happen
}
Member member = (Member) accessibleObject;
Class> declaringClass = member.getDeclaringClass();
Module callerModule = callerClass.getModule();
Module declaringModule = declaringClass.getModule();
if (callerModule == declaringModule) return true;
if (callerModule == Object.class.getModule()) return true;
if (!declaringModule.isNamed()) return true;
int modifiers = member.getModifiers();
return checkAccessible(callerClass, declaringClass, modifiers, true);
}
@Override
public boolean trySetAccessible(AccessibleObject ao) {
return ao.trySetAccessible();
}
@Override
public MetaMethod transformMetaMethod(MetaClass metaClass, MetaMethod metaMethod, Class> caller) {
if (!(metaMethod instanceof CachedMethod)) {
return metaMethod;
}
CachedMethod cachedMethod = (CachedMethod) metaMethod;
CachedClass methodDeclaringClass = cachedMethod.getDeclaringClass();
if (null == methodDeclaringClass) {
return metaMethod;
}
if (null == caller) {
caller = ReflectionUtils.class; // "set accessible" are done via `org.codehaus.groovy.reflection.ReflectionUtils` as shown in warnings
}
return getOrTransformMetaMethod(metaClass, caller, cachedMethod);
}
private CachedMethod getOrTransformMetaMethod(MetaClass metaClass, Class> caller, CachedMethod cachedMethod) {
CachedMethod transformedMethod = cachedMethod.getTransformedMethod();
if (null != transformedMethod) {
return transformedMethod;
}
transformedMethod = doTransformMetaMethod(metaClass, cachedMethod, caller);
cachedMethod.setTransformedMethod(transformedMethod);
return transformedMethod;
}
private CachedMethod doTransformMetaMethod(MetaClass metaClass, CachedMethod metaMethod, Class> caller) {
CachedClass methodDeclaringClass = metaMethod.getDeclaringClass();
Class> declaringClass = methodDeclaringClass.getTheClass();
int methodModifiers = metaMethod.getModifiers();
// if caller can access the method,
// no need to transform the meta method
if (checkAccessible(caller, declaringClass, methodModifiers, false)) {
return metaMethod;
}
Class>[] params = metaMethod.getPT();
Class> theClass = metaClass.getTheClass();
if (declaringClass == theClass) {
if (BigInteger.class == theClass) {
CachedMethod bigIntegerMetaMethod = transformBigIntegerMetaMethod(metaMethod, params);
if (bigIntegerMetaMethod != metaMethod) {
return bigIntegerMetaMethod;
}
}
// GROOVY-9081 "3) Access public members of private class", e.g. Collections.unmodifiableMap([:]).toString()
// try to find the visible method from its superclasses
List> classList = findSuperclasses(theClass);
classList.add(0, theClass);
for (Class> sc : classList) {
Optional optionalMetaMethod = getAccessibleMetaMethod(metaMethod, params, caller, sc, true);
if (optionalMetaMethod.isPresent()) {
return optionalMetaMethod.get();
}
}
return metaMethod;
} else if (declaringClass.isAssignableFrom(theClass)) {
// if caller can not access the method,
// try to find the corresponding method in its derived class
// GROOVY-9081 Sub-class derives the protected members from public class, "Invoke the members on the sub class instances"
// e.g. StringBuilder sb = new StringBuilder(); sb.setLength(0);
// `setLength` is the method of `AbstractStringBuilder`, which is `package-private`
Optional optionalMetaMethod = getAccessibleMetaMethod(metaMethod, params, caller, theClass, false);
if (optionalMetaMethod.isPresent()) {
return optionalMetaMethod.get();
}
}
return metaMethod;
}
private static CachedMethod transformBigIntegerMetaMethod(CachedMethod metaMethod, Class>[] params) {
if (1 == params.length && MULTIPLY.equals(metaMethod.getName())) {
Class> param = params[0];
if (Long.class == param || long.class == param
|| Integer.class == param || int.class == param
|| Short.class == param || short.class == param) {
return new CachedMethod(BigIntegerMultiplyMethodHolder.MULTIPLY_METHOD);
}
}
return metaMethod;
}
private Optional getAccessibleMetaMethod(CachedMethod metaMethod, Class>[] params, Class> caller, Class> sc, boolean declared) {
List metaMethodList = getMetaMethods(metaMethod, params, sc, declared);
for (CachedMethod mm : metaMethodList) {
if (checkAccessible(caller, mm.getDeclaringClass().getTheClass(), mm.getModifiers(), false)) {
return Optional.of(mm);
}
}
return Optional.empty();
}
private static List getMetaMethods(CachedMethod metaMethod, Class>[] params, Class> sc, boolean declared) {
String metaMethodName = metaMethod.getName();
List optionalMethodList = declared
? ReflectionUtils.getDeclaredMethods(sc, metaMethodName, params)
: ReflectionUtils.getMethods(sc, metaMethodName, params);
return optionalMethodList.stream().map(CachedMethod::new).collect(Collectors.toList());
}
@Override
public boolean checkAccessible(Class> callerClass, Class> declaringClass, int memberModifiers, boolean allowIllegalAccess) {
Module callerModule = callerClass.getModule();
Module declaringModule = declaringClass.getModule();
String pn = declaringClass.getPackageName();
boolean unnamedModuleAccessNamedModule = !callerModule.isNamed() && declaringModule.isNamed();
boolean toCheckIllegalAccess = !allowIllegalAccess && unnamedModuleAccessNamedModule;
// class is public and package is exported to callerClass
boolean isClassPublic = Modifier.isPublic(declaringClass.getModifiers());
if (isClassPublic && declaringModule.isExported(pn, callerModule)) {
// member is public
if (Modifier.isPublic(memberModifiers)) {
return !(toCheckIllegalAccess && isExportedForIllegalAccess(declaringModule, pn));
}
// member is protected-static
if (Modifier.isProtected(memberModifiers)
&& Modifier.isStatic(memberModifiers)
&& isSubclassOf(callerClass, declaringClass)) {
return !(toCheckIllegalAccess && isExportedForIllegalAccess(declaringModule, pn));
}
}
// package is open to callerClass
if (declaringModule.isOpen(pn, callerModule)) {
return !(toCheckIllegalAccess && isOpenedForIllegalAccess(declaringModule, pn));
}
return false;
}
private static boolean isExportedForIllegalAccess(Module declaringModule, String pn) {
return concealedPackageList(declaringModule).contains(pn);
}
private static boolean isOpenedForIllegalAccess(Module declaringModule, String pn) {
if (isExportedForIllegalAccess(declaringModule, pn)) return true;
return exportedPackageList(declaringModule).contains(pn);
}
private static boolean isSubclassOf(Class> queryClass, Class> ofClass) {
while (queryClass != null) {
if (queryClass == ofClass) {
return true;
}
queryClass = queryClass.getSuperclass();
}
return false;
}
private static List> findSuperclasses(Class> clazz) {
List> result = new LinkedList<>();
for (Class> c = clazz.getSuperclass(); null != c; c = c.getSuperclass()) {
result.add(c);
}
return result;
}
private static Set concealedPackageList(Module module) {
return CONCEALED_PACKAGES_TO_OPEN.computeIfAbsent(module.getName(), m -> new HashSet<>());
}
private static Set exportedPackageList(Module module) {
return EXPORTED_PACKAGES_TO_OPEN.computeIfAbsent(module.getName(), m -> new HashSet<>());
}
private static final Map> CONCEALED_PACKAGES_TO_OPEN;
private static final Map> EXPORTED_PACKAGES_TO_OPEN;
static {
Tuple2