Moving recursive lookup for defineClass0 just in the native method itself; messing...
[jpf-core.git] / src / peers / gov / nasa / jpf / vm / JPF_java_lang_ClassLoader.java
1 /*
2  * Copyright (C) 2014, United States Government, as represented by the
3  * Administrator of the National Aeronautics and Space Administration.
4  * All rights reserved.
5  *
6  * The Java Pathfinder core (jpf-core) platform is licensed under the
7  * Apache License, Version 2.0 (the "License"); you may not use this file except
8  * in compliance with the License. You may obtain a copy of the License at
9  * 
10  *        http://www.apache.org/licenses/LICENSE-2.0. 
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and 
16  * limitations under the License.
17  */
18 package gov.nasa.jpf.vm;
19
20 import java.util.Map;
21
22 import gov.nasa.jpf.annotation.MJI;
23 import gov.nasa.jpf.vm.ClassPath;
24 import gov.nasa.jpf.vm.ClassInfo;
25 import gov.nasa.jpf.vm.ClassLoaderInfo;
26 import gov.nasa.jpf.vm.ClassInfoException;
27 import gov.nasa.jpf.vm.ClinitRequired;
28 import gov.nasa.jpf.vm.ElementInfo;
29 import gov.nasa.jpf.vm.Heap;
30 import gov.nasa.jpf.vm.MJIEnv;
31 import gov.nasa.jpf.vm.NativePeer;
32 import gov.nasa.jpf.vm.ThreadInfo;
33
34 /**
35  * @author Nastaran Shafiei <nastaran.shafiei@gmail.com>
36  * 
37  * Native peer for java.lang.ClassLoader
38  */
39 public class JPF_java_lang_ClassLoader extends NativePeer {
40
41   @MJI
42   public void $init____V (MJIEnv env, int objRef) {
43     ClassLoaderInfo systemCl = ClassLoaderInfo.getCurrentSystemClassLoader();
44     $init__Ljava_lang_ClassLoader_2__V (env, objRef, systemCl.getClassLoaderObjectRef());
45   }
46
47   @MJI
48   public void $init__Ljava_lang_ClassLoader_2__V (MJIEnv env, int objRef, int parentRef) {
49     Heap heap = env.getHeap();
50
51     //--- Retrieve the parent ClassLoaderInfo
52     ClassLoaderInfo parent = env.getClassLoaderInfo(parentRef);
53
54     //--- create the internal representation of the classloader
55     ClassLoaderInfo cl = new ClassLoaderInfo(env.getVM(), objRef, new ClassPath(), parent);
56
57     //--- initialize the java.lang.ClassLoader object
58     ElementInfo ei = heap.getModifiable(objRef);
59     ei.setIntField( ClassLoaderInfo.ID_FIELD, cl.getId());
60
61     // we should use the following block if we ever decide to make systemClassLoader 
62     // unavailable if(parent.isSystemClassLoader) {
63     //  // we don't want to make the systemCLassLoader available through SUT
64     //  parentRef = MJIEnv.NULL;
65     // }
66
67     ei.setReferenceField("parent", parentRef);
68   }
69
70   @MJI
71   public int getSystemClassLoader____Ljava_lang_ClassLoader_2 (MJIEnv env, int clsObjRef) {
72     return ClassLoaderInfo.getCurrentSystemClassLoader().getClassLoaderObjectRef();
73   }
74
75   @MJI
76   public int getResource0__Ljava_lang_String_2__Ljava_lang_String_2 (MJIEnv env, int objRef, int resRef){
77     String rname = env.getStringObject(resRef);
78
79     ClassLoaderInfo cl = env.getClassLoaderInfo(objRef);
80
81     String resourcePath = cl.findResource(rname);
82
83     return env.newString(resourcePath);
84   }
85
86   @MJI
87   public int getResources0__Ljava_lang_String_2___3Ljava_lang_String_2 (MJIEnv env, int objRef, int resRef) {
88     String rname = env.getStringObject(resRef);
89
90     ClassLoaderInfo cl = env.getClassLoaderInfo(objRef);
91
92     String[] resources = cl.findResources(rname);
93
94     return env.newStringArray(resources);
95   }
96
97   @MJI
98   public int findLoadedClass__Ljava_lang_String_2__Ljava_lang_Class_2 (MJIEnv env, int objRef, int nameRef) {
99     String cname = env.getStringObject(nameRef);
100
101     ClassLoaderInfo cl = env.getClassLoaderInfo(objRef);
102
103     ClassInfo ci = cl.getAlreadyResolvedClassInfo(cname);
104     if(ci != null) {
105       return ci.getClassObjectRef();
106     }
107
108     return MJIEnv.NULL;
109   }
110
111   @MJI
112   public int findSystemClass__Ljava_lang_String_2__Ljava_lang_Class_2 (MJIEnv env, int objRef, int nameRef) {
113     String cname = env.getStringObject(nameRef);
114
115     checkForIllegalName(env, cname);
116     if(env.hasException()) {
117       return MJIEnv.NULL;
118     }
119
120     ClassLoaderInfo cl = ClassLoaderInfo.getCurrentSystemClassLoader();
121
122     ClassInfo ci = cl.getResolvedClassInfo(cname);
123
124     if(!ci.isRegistered()) {
125       ci.registerClass(env.getThreadInfo());
126     }
127
128     return ci.getClassObjectRef();
129   }
130
131   // TODO: Fix for Groovy's model-checking
132   // TODO: Load the SystemClassLoader if this ClassLoader fails, but just get it if it is already defined!
133   // TODO: This is needed for sun.reflect.GroovyMagic and some other classes
134   @MJI
135   public int defineClass0__Ljava_lang_String_2_3BII__Ljava_lang_Class_2 
136                                       (MJIEnv env, int objRef, int nameRef, int bufferRef, int offset, int length) {
137     String cname = env.getStringObject(nameRef);
138     ClassLoaderInfo cl = env.getClassLoaderInfo(objRef);
139
140     // determine whether that the corresponding class is already defined by this
141     // classloader, if so, just return the already defined class!
142     if (cl.getDefinedClassInfo(cname) != null) {  // attempt to define twice
143       return cl.getDefinedClassInfo(cname).getClassObjectRef();
144     }
145         
146     byte[] buffer = env.getByteArrayObject(bufferRef);
147     
148     try {
149       ClassInfo ci = cl.getResolvedClassInfo( cname, buffer, offset, length);
150
151       // Note: if the representation is not of a supported major or minor version, loading
152       // throws an UnsupportedClassVersionError. But for now, we do not check for this here 
153       // since we don't do much with minor and major versions
154
155       ThreadInfo ti = env.getThreadInfo();
156       ci.registerClass(ti);
157
158       return ci.getClassObjectRef();
159
160     } catch (LoadOnJPFRequired rre) {
161       return searchThroughSystemClassLoader(env, objRef, nameRef, bufferRef, offset, length);
162
163     } catch (ClassInfoException cix){
164       if (cix.getExceptionClass().equals("java.lang.ClassNotFoundException")) {
165         return searchThroughSystemClassLoader(env, objRef, nameRef, bufferRef, offset, length);
166       }
167       env.throwException("java.lang.ClassFormatError");
168       return MJIEnv.NULL;
169
170     }
171   }
172
173   private int searchThroughSystemClassLoader
174           (MJIEnv env, int objRef, int nameRef, int bufferRef, int offset, int length) {
175
176     int sysObjRef = env.getSystemClassLoaderInfo().getClassLoaderObjectRef();
177     if (objRef != sysObjRef) {
178       return defineClass0__Ljava_lang_String_2_3BII__Ljava_lang_Class_2
179               (env, sysObjRef, nameRef, bufferRef, offset, length);
180     }
181     env.throwException("java.lang.ClassNotFoundException");
182     return MJIEnv.NULL;
183   }
184
185
186   protected static boolean check(MJIEnv env, String cname, byte[] buffer, int offset, int length) {
187     // throw SecurityExcpetion if the package prefix is java
188     checkForProhibitedPkg(env, cname);
189
190     // throw NoClassDefFoundError if the given class does name might 
191     // not be a valid binary name
192     checkForIllegalName(env, cname);
193
194     // throw IndexOutOfBoundsException if buffer length is not consistent
195     // with offset
196     checkData(env, buffer, offset, length);
197
198     return !env.hasException();
199   }
200
201   protected static void checkForProhibitedPkg(MJIEnv env, String name) {
202     if(name != null && name.startsWith("java.")) {
203       env.throwException("java.lang.SecurityException", "Prohibited package name: " + name);
204     }
205   }
206
207   protected static void checkForIllegalName(MJIEnv env, String name) {
208     if((name == null) || (name.length() == 0)) {
209       return;
210     }
211
212     if((name.indexOf('/') != -1) || (name.charAt(0) == '[')) {
213       env.throwException("java.lang.NoClassDefFoundError", "IllegalName: " + name);
214     }
215   }
216
217   protected static void checkData(MJIEnv env, byte[] buffer, int offset, int length) {
218     if(offset<0 || length<0 || offset+length > buffer.length) {
219       env.throwException("java.lang.IndexOutOfBoundsException");
220     }
221   }
222
223   static String pkg_class_name = "java.lang.Package";
224
225   @MJI
226   public int getPackages_____3Ljava_lang_Package_2 (MJIEnv env, int objRef) {
227     ClassLoaderInfo sysLoader = ClassLoaderInfo.getCurrentSystemClassLoader();
228     ClassInfo pkgClass = null; 
229     try {
230       pkgClass = sysLoader.getInitializedClassInfo(pkg_class_name, env.getThreadInfo());
231     } catch (ClinitRequired x){
232       env.handleClinitRequest(x.getRequiredClassInfo());
233       return MJIEnv.NULL;
234     }
235
236     ClassLoaderInfo cl = env.getClassLoaderInfo(objRef);
237     // Returns all of the Packages defined by this class loader and its ancestors
238     Map<String, ClassLoaderInfo> pkgs = cl.getPackages();
239     int size = pkgs.size();
240     // create an array of type java.lang.Package
241     int pkgArr = env.newObjectArray(pkg_class_name, size);
242
243     int i = 0;
244     for(String name: cl.getPackages().keySet()) {
245       int pkgRef = createPackageObject(env, pkgClass, name, cl);
246       // place the object into the array
247       env.setReferenceArrayElement(pkgArr, i++, pkgRef);
248     }
249
250     return pkgArr;
251   }
252
253   @MJI
254   public int getPackage__Ljava_lang_String_2__Ljava_lang_Package_2 (MJIEnv env, int objRef, int nameRef) {
255     ClassLoaderInfo sysLoader = ClassLoaderInfo.getCurrentSystemClassLoader();
256
257     ClassInfo pkgClass = null; 
258     try {
259       pkgClass = sysLoader.getInitializedClassInfo(pkg_class_name, env.getThreadInfo());
260     } catch (ClinitRequired x){
261       env.handleClinitRequest(x.getRequiredClassInfo());
262       return MJIEnv.NULL;
263     }
264
265     ClassLoaderInfo cl = env.getClassLoaderInfo(objRef);
266     String pkgName = env.getStringObject(nameRef);
267     if(cl.getPackages().get(pkgName)!=null) {
268       return createPackageObject(env, pkgClass, pkgName, cl);
269     } else {
270       return MJIEnv.NULL;
271     }
272   }
273
274   public static int createPackageObject(MJIEnv env, ClassInfo pkgClass, String pkgName, ClassLoaderInfo cl) {
275     int pkgRef = env.newObject(pkgClass);
276     ElementInfo ei = env.getModifiableElementInfo(pkgRef);
277
278     ei.setReferenceField("pkgName", env.newString(pkgName));
279     ei.setReferenceField("loader", cl.getClassLoaderObjectRef());
280     // the rest of the fields set to some dummy value
281     ei.setReferenceField("specTitle", env.newString("spectitle"));
282     ei.setReferenceField("specVersion", env.newString("specversion"));
283     ei.setReferenceField("specVendor", env.newString("specvendor"));
284     ei.setReferenceField("implTitle", env.newString("impltitle"));
285     ei.setReferenceField("implVersion", env.newString("implversion"));
286     ei.setReferenceField("sealBase", MJIEnv.NULL);
287
288     return pkgRef;
289   }
290
291   @MJI
292   public void setDefaultAssertionStatus__Z__V (MJIEnv env, int objRef, boolean enabled) {
293     ClassLoaderInfo cl = env.getClassLoaderInfo(objRef);
294     cl.setDefaultAssertionStatus(enabled);
295   }
296
297   @MJI
298   public void setPackageAssertionStatus__Ljava_lang_String_2Z__V (MJIEnv env, int objRef, int strRef, boolean enabled) {
299     ClassLoaderInfo cl = env.getClassLoaderInfo(objRef);
300     String pkgName = env.getStringObject(strRef);
301     cl.setPackageAssertionStatus(pkgName, enabled);
302   }
303
304   @MJI
305   public void setClassAssertionStatus__Ljava_lang_String_2Z__V (MJIEnv env, int objRef, int strRef, boolean enabled) {
306     ClassLoaderInfo cl = env.getClassLoaderInfo(objRef);
307     String clsName = env.getStringObject(strRef);
308     cl.setClassAssertionStatus(clsName, enabled);
309   }
310
311   @MJI
312   public void clearAssertionStatus____V (MJIEnv env, int objRef) {
313     ClassLoaderInfo cl = env.getClassLoaderInfo(objRef);
314     cl.clearAssertionStatus();
315   }
316 }