test/hotspot/jtreg/vmTestbase/vm/runtime/defmeth/shared/MemoryClassLoader.java
changeset 50243 4fac3c99487d
equal deleted inserted replaced
50242:9a87afc49148 50243:4fac3c99487d
       
     1 /*
       
     2  * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.
       
     8  *
       
     9  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    12  * version 2 for more details (a copy is included in the LICENSE file that
       
    13  * accompanied this code).
       
    14  *
       
    15  * You should have received a copy of the GNU General Public License version
       
    16  * 2 along with this work; if not, write to the Free Software Foundation,
       
    17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    18  *
       
    19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    20  * or visit www.oracle.com if you need additional information or have any
       
    21  * questions.
       
    22  */
       
    23 
       
    24 package vm.runtime.defmeth.shared;
       
    25 
       
    26 import java.io.ByteArrayOutputStream;
       
    27 import java.io.IOException;
       
    28 import java.io.InputStream;
       
    29 import java.util.LinkedHashMap;
       
    30 import java.util.Map;
       
    31 import java.util.Map.Entry;
       
    32 import nsk.share.TestFailure;
       
    33 
       
    34 /**
       
    35  * Class loader for classes from internal in-memory cache
       
    36  */
       
    37 public class MemoryClassLoader extends ClassLoader {
       
    38     private Map<String, byte[]> classes;
       
    39 
       
    40     public MemoryClassLoader(Map<String, byte[]> classes) {
       
    41         this.classes = new LinkedHashMap<>(classes);
       
    42     }
       
    43 
       
    44     public MemoryClassLoader(Map<String, byte[]> classes, ClassLoader parent) {
       
    45         super(parent);
       
    46         this.classes = new LinkedHashMap<>(classes);
       
    47     }
       
    48 
       
    49     @Override
       
    50     public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
       
    51         // Lookup classes from default package eagerly.
       
    52         // Otherwise, it may interfere with other classes on classpath in testbase
       
    53         if (!name.contains(".") && classes.containsKey(name)) {
       
    54             // First, check if the class has already been loaded
       
    55             Class<?> c = findLoadedClass(name);
       
    56             if (c == null) {
       
    57                 byte[] code = classes.get(name);
       
    58                 c = defineClass(name, code, 0, code.length);
       
    59             }
       
    60             if (resolve)  resolveClass(c);
       
    61             return c;
       
    62         } else {
       
    63             return super.loadClass(name, resolve);
       
    64         }
       
    65     }
       
    66 
       
    67     @Override
       
    68     protected Class<?> findClass(String name) throws ClassNotFoundException {
       
    69         if (classes.containsKey(name)) {
       
    70             byte[] code = classes.get(name);
       
    71             return defineClass(name, code, 0, code.length);
       
    72         }
       
    73         return null;
       
    74     }
       
    75 
       
    76     /**
       
    77      * Check whether a class called {@code className} is loaded by any of
       
    78      * ancestor class loaders.
       
    79      *
       
    80      * @param className
       
    81      * @return
       
    82      */
       
    83     private boolean loadedByParent(String className) {
       
    84         try {
       
    85             return (getParent().loadClass(className) != null);
       
    86         } catch (ClassNotFoundException ex) {
       
    87             return false;
       
    88         }
       
    89     }
       
    90 
       
    91     /**
       
    92      * Pre-load all classes which are present in internal storage.
       
    93      *
       
    94      */
       
    95     public void tryPreloadClasses() {
       
    96         for (String name : classes.keySet()) {
       
    97             try {
       
    98                 loadClass(name);
       
    99             } catch (ClassNotFoundException e) {
       
   100                 // ignore errors during class loading
       
   101             }
       
   102         }
       
   103     }
       
   104 
       
   105     /**
       
   106      * Redefine all classes, loaded by this class loader with their very same
       
   107      * versions. Used to stress class redefinition code.
       
   108      *
       
   109      * @return
       
   110      */
       
   111     public MemoryClassLoader modifyClasses(boolean retransform) {
       
   112         for (Entry<String, byte[]> entry : classes.entrySet()) {
       
   113             modifyClass(entry, retransform);
       
   114         }
       
   115         return this;
       
   116     }
       
   117 
       
   118     public void modifyClasses(Map<String, byte[]> redefine, boolean retransform) {
       
   119         for (Entry<String,byte[]> entry : redefine.entrySet()) {
       
   120             modifyClass(entry, retransform);
       
   121         }
       
   122     }
       
   123 
       
   124     private void modifyClass(Entry<String,byte[]> entry, boolean retransform) {
       
   125         try {
       
   126             String name = entry.getKey();
       
   127 
       
   128             // Skip classes which are loaded by parent class loader and that are not
       
   129             // in the unnamed package (otherwise it'll pick up classes from other Testbase
       
   130             // tests, m.class for instance, that are on the regular classpath) e.g.
       
   131             // j.l.AbstractMethodError. Their placeholders are used during test creation,
       
   132             // but don't participate in test execution.
       
   133             if (name.contains(".") && loadedByParent(name)) {
       
   134                 return;
       
   135             }
       
   136 
       
   137             Class clz = loadClass(name);
       
   138             byte[] code = entry.getValue();
       
   139 
       
   140             if (clz == null) {
       
   141                 // Ignore not "loadable" classes.
       
   142                 // For example, reflection scenarios don't generate classes for testers
       
   143                 return;
       
   144             }
       
   145 
       
   146             if (retransform) {
       
   147                 Util.retransformClass(clz, code);
       
   148             } else {
       
   149                 Util.redefineClass(clz, code);
       
   150             }
       
   151 
       
   152         } catch (LinkageError e) {
       
   153             // Ignore errors during linkage, since some tests produce
       
   154             // incorrect class files intentionally.
       
   155         } catch (ClassNotFoundException ex) {
       
   156             throw new TestFailure(ex);
       
   157         }
       
   158     }
       
   159 
       
   160     /** Prepare test context (vm.runtime.defmeth.shared.TestContext) */
       
   161     private Class<?> contextClass;
       
   162 
       
   163     public synchronized Class<?> getTestContext() {
       
   164         if (contextClass == null) {
       
   165             String context = TestContext.class.getName();
       
   166             byte[] classFile = getClassFileFromParent(context);
       
   167             contextClass = defineClass(context, classFile, 0, classFile.length);
       
   168         }
       
   169         return contextClass;
       
   170     }
       
   171 
       
   172     /** Get class file content from parent class loader */
       
   173     private byte[] getClassFileFromParent(String name) {
       
   174         String resourceName = name.replaceAll("\\.","/")+".class";
       
   175         InputStream resource = getParent().getResourceAsStream(resourceName);
       
   176         ByteArrayOutputStream output = new ByteArrayOutputStream();
       
   177 
       
   178         byte[] buffer = new byte[8092];
       
   179         int count = -1;
       
   180         try {
       
   181             while ((count = resource.read(buffer)) != -1) {
       
   182                 output.write(buffer, 0, count);
       
   183             }
       
   184             resource.close();
       
   185         } catch (NullPointerException npe) {
       
   186             // getResourceAsStream returns null for IOException
       
   187             System.out.println("Unable to get resourceName " + resourceName);
       
   188             throw new Error(npe);
       
   189         } catch (IOException ex) {
       
   190             throw new Error(ex);
       
   191         }
       
   192 
       
   193         return output.toByteArray();
       
   194     }
       
   195 }