/* * Copyright 2016 the original author or authors. * * Licensed 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.gradle.initialization; import org.gradle.api.GradleException; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.URL; import java.util.HashSet; import java.util.Set; /** * Enriches class loading with empty interfaces for certain types that have been removed, * but which are baked into the bytecode generated by the Groovy compiler. */ public class DefaultLegacyTypesSupport implements LegacyTypesSupport { private static final Type OBJECT_TYPE = Type.getType(Object.class); private final Set<String> classesToMixInGroovyObject = readClassNames("converted-types.txt"); private final Set<String> syntheticClasses = readClassNames("removed-types.txt"); @Override public Set<String> getClassesToMixInGroovyObject() { return classesToMixInGroovyObject; } @Override public Set<String> getSyntheticClasses() { return syntheticClasses; } private Set<String> readClassNames(String resourceName) { Set<String> classNames = new HashSet<String>(); URL resource = LegacyTypesSupport.class.getResource(resourceName); try { BufferedReader reader = new BufferedReader(new InputStreamReader(resource.openStream())); try { String line; while ((line = reader.readLine()) != null) { classNames.add(line.trim()); } } finally { reader.close(); } } catch (IOException e) { throw new GradleException("Could not load class names from '" + resource + "'.", e); } return classNames; } @Override public byte[] generateSyntheticClass(String name) { ClassWriter visitor = new ClassWriter(0); visitor.visit(Opcodes.V1_7, Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC | Opcodes.ACC_INTERFACE | Opcodes.ACC_ABSTRACT, name.replace('.', '/'), null, OBJECT_TYPE.getInternalName(), null); visitor.visitEnd(); return visitor.toByteArray(); } /** * Injects the interfaces into an arbitrary classloader via * {@link ClassLoader#defineClass(String, byte[], int, int)}. */ @Override public void injectEmptyInterfacesIntoClassLoader(ClassLoader classLoader) { try { java.lang.reflect.Method defineClassMethod = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class); defineClassMethod.setAccessible(true); for (String name : syntheticClasses) { byte[] bytes = generateSyntheticClass(name); defineClassMethod.invoke(classLoader, name, bytes, 0, bytes.length); } defineClassMethod.setAccessible(false); } catch (Exception e) { throw new GradleException("Could not inject synthetic classes.", e); } } }