/* * Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.max.vm.hosted; import java.util.*; import com.sun.max.lang.*; import com.sun.max.program.*; import com.sun.max.vm.*; import com.sun.max.vm.type.*; /** * The Boot class loader used when running in hosted mode. * The singleton {@link #HOSTED_BOOT_CLASS_LOADER} instance is identical to the * singleton {@link BootClassLoader#BOOT_CLASS_LOADER} instance of the {@link BootClassLoader} * at runtime thanks to {@link JavaPrototype#hostToTarget(Object)}. * * Most of the logic is inherited from {@link HostedClassLoader}. * * The customizations are: * <ul> * <li>VM classes that are needed to satisfy the few references between boot classes and VM classes * can be resolved by this loader</li> * <li>JDK classes/packages can be explicitly prevented from being loaded into the image</li> * </ul> */ public final class HostedBootClassLoader extends HostedClassLoader { /* * These sets provide support for the explicit omission of certain JDK classes and packages. */ private static final Set<String> loadedPackages = new HashSet<String>(); private static final Set<String> omittedClasses = new HashSet<String>(); private static final Set<String> omittedPackages = new HashSet<String>(); private HostedBootClassLoader() { } /** * This value is identical to {@link BootClassLoader#BOOT_CLASS_LOADER} at runtime. * * @see JavaPrototype#hostToTarget(Object) */ public static final HostedBootClassLoader HOSTED_BOOT_CLASS_LOADER = new HostedBootClassLoader(); @Override protected Classpath getDefaultClasspath() { return Classpath.bootClassPath(); } /** * Adds a class that must not be loaded into the VM class registry. * Calling {@link #loadClass(String, boolean)} for * this class will return null. * * @param javaClass the class to be omitted */ public static void omitClass(Class javaClass) { omitClass(javaClass.getName()); } /** * Adds a class that must not be loaded into the {@link ClassRegistry#BOOT_CLASS_REGISTRY}. * Calling {@link #loadClass(String, boolean)} for * this class will return null. * * @param className the name of the class to be omitted */ public static void omitClass(String className) { if (ClassRegistry.BOOT_CLASS_REGISTRY.get(JavaTypeDescriptor.getDescriptorForJavaString(className)) != null) { throw ProgramError.unexpected("Cannot omit a class already in VM class registry: " + className); } omittedClasses.add(className); } /** * Adds the name of package whose constituent classes must not be loaded into the VM class registry. Calling * {@link #loadClass(String, boolean)} for a class in the named package will return null. * * @param packageName * @param retrospective if true, then this method verifies that the VM class registry does not currently contain any * classes in the specified package */ public static void omitPackage(String packageName, boolean retrospective) { if (retrospective) { synchronized (HostedClassLoader.class) { ProgramError.check(!loadedPackages.contains(packageName), "Cannot omit a package already in VM class registry: " + packageName); } } omittedPackages.add(packageName); } /** * Determines if a given type descriptor denotes a class that must not be loaded in VM class registry. * The set of omitted classes is determined by any preceding calls to {@link #omitClass(Class)} and {@link #omitPackage(String, boolean)}. * All inner classes of omitted classes are also omitted. * * @param className the name of a type to test * @return {@code true} if {@code typeDescriptor} denotes a class that must not be loaded in VM class registry */ public boolean isOmittedType(String className) { if (omittedClasses.contains(className)) { return true; } if (omittedPackages.contains(Classes.getPackageName(className))) { return true; } if (Classes.getSimpleName(className).lastIndexOf('$') >= 0) { return isOmittedType(className.substring(0, className.lastIndexOf('$'))); } return false; } /** * If {@code true}, no {@link OmittedClassError} exceptions will be thrown. */ private static boolean noOmittedClassExceptions; /** * Suppresses {@link OmittedClassError} exceptions. * For use by Inspector, which accepts any class. */ public static void noOmittedClassExceptions() { noOmittedClassExceptions = true; } @Override protected boolean extraLoadClassChecks(Class<?> javaType) throws ClassNotFoundException { final String name = javaType.getName(); if (isOmittedType(name) && !noOmittedClassExceptions) { throw new OmittedClassError(name); } // This check prevents any VM class for which the boot loader was given "initiating" loader status // from being added to the boot registry. N.B. Stub classes are actually "defined" by the Host loaders // and so are a special case. if (javaType.getClassLoader() != null && !isBootStubClass(javaType)) { throw new ClassNotFoundException(); } synchronized (this) { loadedPackages.add(Classes.getPackageName(name)); } return true; } private boolean isBootStubClass(Class<?> javaType) { ClassLoader cl = javaType.getClassLoader(); return isStubClass(javaType.getName()) && cl == this; } /** * This guard serves a similiar purpose to {@link BootClassLoader#vmResolveOk} to handle special cases * where boot classes refer to VM classes, e.g. native method stubs. */ private static final ThreadLocal<Boolean> attemptingVMClassResolution = new ThreadLocal<Boolean>() { @Override public Boolean initialValue() { return false; } }; @Override protected synchronized Class<?> loadClass(final String name, final boolean resolve) throws ClassNotFoundException { try { return super.loadClass(name, resolve); } catch (ClassNotFoundException exception) { if (!attemptingVMClassResolution.get()) { attemptingVMClassResolution.set(true); try { return HostedVMClassLoader.HOSTED_VM_CLASS_LOADER.findClass(name); } finally { attemptingVMClassResolution.set(false); } } throw exception; } } @Override public String toString() { return "Boot"; } }