From 512ba8f80c0a2422f9e58a1401cb142db190a56c Mon Sep 17 00:00:00 2001 From: Paul Duffin Date: Wed, 6 May 2020 12:55:46 +0000 Subject: Revert "Remove unused project." This reverts commit eaada771a6e62ca494a625ce1b060f1a94fa9877. Reason for revert: Breaks build_test and other builds Bug: 155862492 Change-Id: I1a14b84d54b05596ec250b31ae56e437cf49fd81 --- .../resources/manager/ProjectClassLoader.java | 376 +++++++++++++++++++++ 1 file changed, 376 insertions(+) create mode 100644 eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ProjectClassLoader.java (limited to 'eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ProjectClassLoader.java') diff --git a/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ProjectClassLoader.java b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ProjectClassLoader.java new file mode 100644 index 000000000..e07f09927 --- /dev/null +++ b/eclipse/plugins/com.android.ide.eclipse.adt/src/com/android/ide/eclipse/adt/internal/resources/manager/ProjectClassLoader.java @@ -0,0 +1,376 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php + * + * 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 com.android.ide.eclipse.adt.internal.resources.manager; + +import com.android.SdkConstants; +import com.android.ide.eclipse.adt.AdtPlugin; +import com.android.ide.eclipse.adt.internal.build.BuildHelper; +import com.android.ide.eclipse.adt.internal.sdk.ProjectState; +import com.android.ide.eclipse.adt.internal.sdk.Sdk; + +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IWorkspaceRoot; +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.jdt.core.IClasspathContainer; +import org.eclipse.jdt.core.IClasspathEntry; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.core.JavaCore; +import org.eclipse.jdt.core.JavaModelException; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.Opcodes; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.List; + +/** + * ClassLoader able to load class from output of an Eclipse project. + */ +public final class ProjectClassLoader extends ClassLoader { + + private final IJavaProject mJavaProject; + private URLClassLoader mJarClassLoader; + private boolean mInsideJarClassLoader = false; + + public ProjectClassLoader(ClassLoader parentClassLoader, IProject project) { + super(parentClassLoader); + mJavaProject = JavaCore.create(project); + } + + @Override + protected Class findClass(String name) throws ClassNotFoundException { + // if we are here through a child classloader, throw an exception. + if (mInsideJarClassLoader) { + throw new ClassNotFoundException(name); + } + + // attempt to load the class from the main project + Class clazz = loadFromProject(mJavaProject, name); + + if (clazz != null) { + return clazz; + } + + // attempt to load the class from the jar dependencies + clazz = loadClassFromJar(name); + if (clazz != null) { + return clazz; + } + + // attempt to load the class from the libraries + try { + // get the project info + ProjectState projectState = Sdk.getProjectState(mJavaProject.getProject()); + + // this can happen if the project has no project.properties. + if (projectState != null) { + + List libProjects = projectState.getFullLibraryProjects(); + List referencedJavaProjects = BuildHelper.getJavaProjects( + libProjects); + + for (IJavaProject javaProject : referencedJavaProjects) { + clazz = loadFromProject(javaProject, name); + + if (clazz != null) { + return clazz; + } + } + } + } catch (CoreException e) { + // log exception? + } + + throw new ClassNotFoundException(name); + } + + /** + * Attempts to load a class from a project output folder. + * @param project the project to load the class from + * @param name the name of the class + * @return a class object if found, null otherwise. + */ + private Class loadFromProject(IJavaProject project, String name) { + try { + // get the project output folder. + IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); + IPath outputLocation = project.getOutputLocation(); + IResource outRes = root.findMember(outputLocation); + if (outRes == null) { + return null; + } + + File outFolder = new File(outRes.getLocation().toOSString()); + + // get the class name segments + String[] segments = name.split("\\."); //$NON-NLS-1$ + + // try to load the class from the bin folder of the project. + File classFile = getFile(outFolder, segments, 0); + if (classFile == null) { + return null; + } + + // load the content of the file and create the class. + FileInputStream fis = new FileInputStream(classFile); + byte[] data = new byte[(int)classFile.length()]; + int read = 0; + try { + read = fis.read(data); + } catch (IOException e) { + data = null; + } + fis.close(); + + if (data != null) { + try { + Class clazz = defineClass(null, data, 0, read); + if (clazz != null) { + return clazz; + } + } catch (UnsupportedClassVersionError e) { + // Attempt to reload on lower version + int maxVersion = 50; // JDK 1.6 + try { + byte[] rewritten = rewriteClass(data, maxVersion, 0); + return defineClass(null, rewritten, 0, rewritten.length); + } catch (UnsupportedClassVersionError e2) { + throw e; // throw *original* exception, not attempt to rewrite + } + } + } + } catch (Exception e) { + // log the exception? + } + + return null; + } + + /** + * Rewrites the given class to the given target class file version. + */ + public static byte[] rewriteClass(byte[] classData, final int maxVersion, final int minVersion) { + assert maxVersion >= minVersion; + ClassWriter classWriter = new ClassWriter(0); + ClassVisitor classVisitor = new ClassVisitor(Opcodes.ASM5, classWriter) { + @Override + public void visit(int version, int access, String name, String signature, + String superName, String[] interfaces) { + if (version > maxVersion) { + version = maxVersion; + } + if (version < minVersion) { + version = minVersion; + } + super.visit(version, access, name, signature, superName, interfaces); + } + }; + ClassReader reader = new ClassReader(classData); + reader.accept(classVisitor, 0); + return classWriter.toByteArray(); + } + + /** + * Returns the File matching the a certain path from a root {@link File}. + *

The methods checks that the file ends in .class even though the last segment + * does not. + * @param parent the root of the file. + * @param segments the segments containing the path of the file + * @param index the offset at which to start looking into segments. + * @throws FileNotFoundException + */ + private File getFile(File parent, String[] segments, int index) + throws FileNotFoundException { + // reached the end with no match? + if (index == segments.length) { + throw new FileNotFoundException(); + } + + String toMatch = segments[index]; + File[] files = parent.listFiles(); + + // we're at the last segments. we look for a matching .class + if (index == segments.length - 1) { + toMatch = toMatch + ".class"; + + if (files != null) { + for (File file : files) { + if (file.isFile() && file.getName().equals(toMatch)) { + return file; + } + } + } + + // no match? abort. + throw new FileNotFoundException(); + } + + String innerClassName = null; + + if (files != null) { + for (File file : files) { + if (file.isDirectory()) { + if (toMatch.equals(file.getName())) { + return getFile(file, segments, index+1); + } + } else if (file.getName().startsWith(toMatch)) { + if (innerClassName == null) { + StringBuilder sb = new StringBuilder(segments[index]); + for (int i = index + 1 ; i < segments.length ; i++) { + sb.append('$'); + sb.append(segments[i]); + } + sb.append(".class"); + + innerClassName = sb.toString(); + } + + if (file.getName().equals(innerClassName)) { + return file; + } + } + } + } + + return null; + } + + /** + * Loads a class from the 3rd party jar present in the project + * + * @return the class loader or null if not found. + */ + private Class loadClassFromJar(String name) { + if (mJarClassLoader == null) { + // get the OS path to all the external jars + URL[] jars = getExternalJars(); + + mJarClassLoader = new URLClassLoader(jars, this /* parent */); + } + + try { + // because a class loader always look in its parent loader first, we need to know + // that we are querying the jar classloader. This will let us know to not query + // it again for classes we don't find, or this would create an infinite loop. + mInsideJarClassLoader = true; + return mJarClassLoader.loadClass(name); + } catch (ClassNotFoundException e) { + // not found? return null. + return null; + } finally { + mInsideJarClassLoader = false; + } + } + + /** + * Returns an array of external jar files used by the project. + * @return an array of OS-specific absolute file paths + */ + private final URL[] getExternalJars() { + // get a java project from it + IJavaProject javaProject = JavaCore.create(mJavaProject.getProject()); + + ArrayList oslibraryList = new ArrayList(); + IClasspathEntry[] classpaths = javaProject.readRawClasspath(); + if (classpaths != null) { + for (IClasspathEntry e : classpaths) { + if (e.getEntryKind() == IClasspathEntry.CPE_LIBRARY || + e.getEntryKind() == IClasspathEntry.CPE_VARIABLE) { + // if this is a classpath variable reference, we resolve it. + if (e.getEntryKind() == IClasspathEntry.CPE_VARIABLE) { + e = JavaCore.getResolvedClasspathEntry(e); + } + + handleClassPathEntry(e, oslibraryList); + } else if (e.getEntryKind() == IClasspathEntry.CPE_CONTAINER) { + // get the container. + try { + IClasspathContainer container = JavaCore.getClasspathContainer( + e.getPath(), javaProject); + // ignore the system and default_system types as they represent + // libraries that are part of the runtime. + if (container != null && + container.getKind() == IClasspathContainer.K_APPLICATION) { + IClasspathEntry[] entries = container.getClasspathEntries(); + for (IClasspathEntry entry : entries) { + // TODO: Xav -- is this necessary? + if (entry.getEntryKind() == IClasspathEntry.CPE_VARIABLE) { + entry = JavaCore.getResolvedClasspathEntry(entry); + } + + handleClassPathEntry(entry, oslibraryList); + } + } + } catch (JavaModelException jme) { + // can't resolve the container? ignore it. + AdtPlugin.log(jme, "Failed to resolve ClasspathContainer: %s", + e.getPath()); + } + } + } + } + + return oslibraryList.toArray(new URL[oslibraryList.size()]); + } + + private void handleClassPathEntry(IClasspathEntry e, ArrayList oslibraryList) { + // get the IPath + IPath path = e.getPath(); + + // check the name ends with .jar + if (SdkConstants.EXT_JAR.equalsIgnoreCase(path.getFileExtension())) { + boolean local = false; + IResource resource = ResourcesPlugin.getWorkspace().getRoot().findMember(path); + if (resource != null && resource.exists() && + resource.getType() == IResource.FILE) { + local = true; + try { + oslibraryList.add(new File(resource.getLocation().toOSString()) + .toURI().toURL()); + } catch (MalformedURLException mue) { + // pass + } + } + + if (local == false) { + // if the jar path doesn't match a workspace resource, + // then we get an OSString and check if this links to a valid file. + String osFullPath = path.toOSString(); + + File f = new File(osFullPath); + if (f.exists()) { + try { + oslibraryList.add(f.toURI().toURL()); + } catch (MalformedURLException mue) { + // pass + } + } + } + } + } +} -- cgit v1.2.3