1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * https://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19 package org.apache.bcel.util;
20
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.util.HashMap;
24 import java.util.Map;
25
26 import org.apache.bcel.classfile.ClassParser;
27 import org.apache.bcel.classfile.JavaClass;
28 import org.apache.bcel.classfile.Utility;
29
30 /**
31 * The repository maintains information about which classes have been loaded.
32 *
33 * It loads its data from the ClassLoader implementation passed into its constructor.
34 *
35 * @see org.apache.bcel.Repository
36 */
37 public class ClassLoaderRepository implements Repository {
38
39 private final java.lang.ClassLoader loader;
40 private final Map<String, JavaClass> loadedClasses = new HashMap<>(); // CLASSNAME X JAVACLASS
41
42 /**
43 * Constructs a ClassLoaderRepository.
44 *
45 * @param loader The class loader.
46 */
47 public ClassLoaderRepository(final java.lang.ClassLoader loader) {
48 this.loader = loader;
49 }
50
51 /**
52 * Clear all entries from cache.
53 */
54 @Override
55 public void clear() {
56 loadedClasses.clear();
57 }
58
59 /**
60 * Find an already defined JavaClass.
61 */
62 @Override
63 public JavaClass findClass(final String className) {
64 return loadedClasses.get(className);
65 }
66
67 /*
68 * @return null.
69 */
70 @Override
71 public ClassPath getClassPath() {
72 return null;
73 }
74
75 @Override
76 public JavaClass loadClass(final Class<?> clazz) throws ClassNotFoundException {
77 return loadClass(clazz.getName());
78 }
79
80 /**
81 * Lookup a JavaClass object from the Class Name provided.
82 */
83 @Override
84 public JavaClass loadClass(final String className) throws ClassNotFoundException {
85 final String classFile = Utility.packageToPath(className);
86 JavaClass RC = findClass(className);
87 if (RC != null) {
88 return RC;
89 }
90 try (InputStream is = loader.getResourceAsStream(classFile + JavaClass.EXTENSION)) {
91 if (is == null) {
92 throw new ClassNotFoundException(className + " not found.");
93 }
94 final ClassParser parser = new ClassParser(is, className);
95 RC = parser.parse();
96 // The this_class name inside the parsed bytes is attacker-controlled: caching it under its own name
97 // would let a class file found under one name poison the repository entry for another (the JDK's
98 // defineClass() rejects the same mismatch). Refuse to cache or return it.
99 if (!RC.getClassName().equals(className)) {
100 throw new ClassNotFoundException("Class name mismatch: requested " + className + " but the class file declares " + RC.getClassName());
101 }
102 storeClass(RC);
103 return RC;
104 } catch (final IOException e) {
105 throw new ClassNotFoundException(className + " not found: " + e, e);
106 }
107 }
108
109 /**
110 * Remove class from repository
111 */
112 @Override
113 public void removeClass(final JavaClass clazz) {
114 loadedClasses.remove(clazz.getClassName());
115 }
116
117 /**
118 * Store a new JavaClass into this Repository.
119 */
120 @Override
121 public void storeClass(final JavaClass clazz) {
122 loadedClasses.put(clazz.getClassName(), clazz);
123 clazz.setRepository(this);
124 }
125 }