1 /*
   2  * Copyright (c) 2018, 2019, 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.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package jdk.jfr.event.oldobject;
  27 
  28 import java.io.IOException;
  29 import java.io.InputStream;
  30 import java.util.ArrayList;
  31 import java.util.List;
  32 
  33 /*
  34  * A special class loader that will, for our test class, make sure that each
  35  * load will become a unique class
  36  */
  37 public final class TestClassLoader extends ClassLoader {
  38 
  39     public static final class TestClass0000000 {
  40         public static final byte[] oneByte = new byte[1];
  41     }
  42 
  43     static byte[] classByteCode = readTestClassBytes();
  44     private static int classIdCounter;
  45 
  46     TestClassLoader() {
  47         super(TestClassLoader.class.getClassLoader());
  48     }
  49 
  50     public List<Class<?>> loadClasses(int count) throws Exception {
  51         List<Class<?>> classes = new ArrayList<>();
  52         for (int i = 0; i < count; i++) {
  53             String className = "jdk.jfr.event.oldobject.TestClassLoader$";
  54             className += "TestClass" + String.format("%07d", classIdCounter++);
  55             Class<?> clazz = Class.forName(className, true, this);
  56             classes.add(clazz);
  57         }
  58         return classes;
  59     }
  60 
  61     @Override
  62     protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
  63         // If not loading the test class, just go on with the normal class loader
  64         if (!name.contains("TestClass")) {
  65             return super.loadClass(name, resolve);
  66         }
  67         String[] classNameParts = name.split("\\$");
  68         String newName = classNameParts[1];
  69         String oldName = "TestClass0000000";
  70         if (oldName.length() != newName.length()) {
  71             throw new AssertionError("String lengths don't match. Unable to replace class name");
  72         }
  73         byte[] newBytes = classByteCode.clone();
  74         replaceBytes(newBytes, oldName.getBytes(), newName.getBytes());
  75         Class<?> c = defineClass(name, newBytes, 0, newBytes.length);
  76         if (resolve) {
  77             resolveClass(c);
  78         }
  79         return c;
  80     }
  81 
  82     static void replaceBytes(byte[] bytes, byte[] find, byte[] replacement) {
  83         for (int index = 0; index < bytes.length - find.length; index++) {
  84             if (matches(bytes, index, find)) {
  85                 replace(bytes, index, replacement);
  86             }
  87         }
  88     }
  89 
  90     private static void replace(byte[] bytes, int startIndex, byte[] replacement) {
  91         for (int index = 0; index < replacement.length; index++) {
  92             bytes[startIndex + index] = replacement[index];
  93         }
  94     }
  95 
  96     private static boolean matches(byte[] bytes, int startIndex, byte[] matching) {
  97         for (int i = 0; i < matching.length; i++) {
  98             if (bytes[startIndex + i] != matching[i]) {
  99                 return false;
 100             }
 101         }
 102         return true;
 103     }
 104 
 105     private static byte[] readTestClassBytes() {
 106         try {
 107             String classFileName = "jdk/jfr/event/oldobject/TestClassLoader$TestClass0000000.class";
 108             InputStream is = TestClassLoader.class.getClassLoader().getResourceAsStream(classFileName);
 109             if (is == null) {
 110                 throw new RuntimeException("Culd not find class file " + classFileName);
 111             }
 112             byte[] b = is.readAllBytes();
 113             is.close();
 114             return b;
 115         } catch (IOException ioe) {
 116             ioe.printStackTrace();
 117             throw new RuntimeException(ioe);
 118         }
 119     }
 120 }