Fixing a few bugs in the statistics printout.
[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     ClassLoaderInfo cl = ClassLoaderInfo.getCurrentSystemClassLoader();
116
117     // TODO: Fix for Groovy's model-checking
118     // Get a clean class name and try to resolve
119     ClassInfo cir = cl.getAlreadyResolvedClassInfo(Types.getClassNameFromTypeName(cname));
120     if (cir != null) {
121       checkForIllegalName(env, cname);
122       if(env.hasException()) {
123         return MJIEnv.NULL;
124       }
125     }
126
127     ClassInfo ci = cl.getResolvedClassInfo(cname);
128
129     if(!ci.isRegistered()) {
130       ci.registerClass(env.getThreadInfo());
131     }
132
133     return ci.getClassObjectRef();
134   }
135
136   // TODO: Fix for Groovy's model-checking
137   // TODO: Load the SystemClassLoader if this ClassLoader fails, but just get it if it is already defined!
138   // TODO: This is needed for sun.reflect.GroovyMagic and some other classes
139   @MJI
140   public int defineClass0__Ljava_lang_String_2_3BII__Ljava_lang_Class_2 
141                                       (MJIEnv env, int objRef, int nameRef, int bufferRef, int offset, int length) {
142     String cname = env.getStringObject(nameRef);
143     ClassLoaderInfo cl = env.getClassLoaderInfo(objRef);
144
145     // determine whether that the corresponding class is already defined by this
146     // classloader, if so, this attempt is invalid, and loading throws a LinkageError
147     if (cl.getDefinedClassInfo(cname) != null) {  // attempt to define twice
148       env.throwException("java.lang.LinkageError");
149       return MJIEnv.NULL;
150     }
151         
152     byte[] buffer = env.getByteArrayObject(bufferRef);
153     
154     try {
155       ClassInfo ci = cl.getResolvedClassInfo( cname, buffer, offset, length);
156
157       // Note: if the representation is not of a supported major or minor version, loading
158       // throws an UnsupportedClassVersionError. But for now, we do not check for this here 
159       // since we don't do much with minor and major versions
160
161       ThreadInfo ti = env.getThreadInfo();
162       ci.registerClass(ti);
163
164       return ci.getClassObjectRef();
165
166     } catch (LoadOnJPFRequired rre) {
167       return searchThroughSystemClassLoader(env, objRef, nameRef, bufferRef, offset, length);
168
169     } catch (ClassInfoException cix){
170       if (cix.getExceptionClass().equals("java.lang.ClassNotFoundException")) {
171         return searchThroughSystemClassLoader(env, objRef, nameRef, bufferRef, offset, length);
172       }
173       env.throwException("java.lang.ClassFormatError");
174       return MJIEnv.NULL;
175
176     }
177   }
178
179   private int searchThroughSystemClassLoader
180           (MJIEnv env, int objRef, int nameRef, int bufferRef, int offset, int length) {
181
182     int sysObjRef = env.getSystemClassLoaderInfo().getClassLoaderObjectRef();
183     if (objRef != sysObjRef) {
184       // Check if this class has been defined in the SystemClassLoader
185       ClassLoaderInfo cl = env.getSystemClassLoaderInfo();
186       String cname = env.getStringObject(nameRef);
187       if (cl.getDefinedClassInfo(cname) != null) {
188         ClassInfo ci = cl.getResolvedClassInfo(cname);
189         return ci.getClassObjectRef();
190       } else {
191         return defineClass0__Ljava_lang_String_2_3BII__Ljava_lang_Class_2
192                 (env, sysObjRef, nameRef, bufferRef, offset, length);
193       }
194     }
195     env.throwException("java.lang.ClassNotFoundException");
196     return MJIEnv.NULL;
197   }
198
199
200   protected static boolean check(MJIEnv env, String cname, byte[] buffer, int offset, int length) {
201     // throw SecurityExcpetion if the package prefix is java
202     checkForProhibitedPkg(env, cname);
203
204     // throw NoClassDefFoundError if the given class does name might 
205     // not be a valid binary name
206     checkForIllegalName(env, cname);
207
208     // throw IndexOutOfBoundsException if buffer length is not consistent
209     // with offset
210     checkData(env, buffer, offset, length);
211
212     return !env.hasException();
213   }
214
215   protected static void checkForProhibitedPkg(MJIEnv env, String name) {
216     if(name != null && name.startsWith("java.")) {
217       env.throwException("java.lang.SecurityException", "Prohibited package name: " + name);
218     }
219   }
220
221   protected static void checkForIllegalName(MJIEnv env, String name) {
222     if((name == null) || (name.length() == 0)) {
223       return;
224     }
225
226     if((name.indexOf('/') != -1) || (name.charAt(0) == '[')) {
227       env.throwException("java.lang.NoClassDefFoundError", "IllegalName: " + name);
228     }
229   }
230
231   protected static void checkData(MJIEnv env, byte[] buffer, int offset, int length) {
232     if(offset<0 || length<0 || offset+length > buffer.length) {
233       env.throwException("java.lang.IndexOutOfBoundsException");
234     }
235   }
236
237   static String pkg_class_name = "java.lang.Package";
238
239   @MJI
240   public int getPackages_____3Ljava_lang_Package_2 (MJIEnv env, int objRef) {
241     ClassLoaderInfo sysLoader = ClassLoaderInfo.getCurrentSystemClassLoader();
242     ClassInfo pkgClass = null; 
243     try {
244       pkgClass = sysLoader.getInitializedClassInfo(pkg_class_name, env.getThreadInfo());
245     } catch (ClinitRequired x){
246       env.handleClinitRequest(x.getRequiredClassInfo());
247       return MJIEnv.NULL;
248     }
249
250     ClassLoaderInfo cl = env.getClassLoaderInfo(objRef);
251     // Returns all of the Packages defined by this class loader and its ancestors
252     Map<String, ClassLoaderInfo> pkgs = cl.getPackages();
253     int size = pkgs.size();
254     // create an array of type java.lang.Package
255     int pkgArr = env.newObjectArray(pkg_class_name, size);
256
257     int i = 0;
258     for(String name: cl.getPackages().keySet()) {
259       int pkgRef = createPackageObject(env, pkgClass, name, cl);
260       // place the object into the array
261       env.setReferenceArrayElement(pkgArr, i++, pkgRef);
262     }
263
264     return pkgArr;
265   }
266
267   @MJI
268   public int getPackage__Ljava_lang_String_2__Ljava_lang_Package_2 (MJIEnv env, int objRef, int nameRef) {
269     ClassLoaderInfo sysLoader = ClassLoaderInfo.getCurrentSystemClassLoader();
270
271     ClassInfo pkgClass = null; 
272     try {
273       pkgClass = sysLoader.getInitializedClassInfo(pkg_class_name, env.getThreadInfo());
274     } catch (ClinitRequired x){
275       env.handleClinitRequest(x.getRequiredClassInfo());
276       return MJIEnv.NULL;
277     }
278
279     ClassLoaderInfo cl = env.getClassLoaderInfo(objRef);
280     String pkgName = env.getStringObject(nameRef);
281     if(cl.getPackages().get(pkgName)!=null) {
282       return createPackageObject(env, pkgClass, pkgName, cl);
283     } else {
284       return MJIEnv.NULL;
285     }
286   }
287
288   public static int createPackageObject(MJIEnv env, ClassInfo pkgClass, String pkgName, ClassLoaderInfo cl) {
289     int pkgRef = env.newObject(pkgClass);
290     ElementInfo ei = env.getModifiableElementInfo(pkgRef);
291
292     ei.setReferenceField("pkgName", env.newString(pkgName));
293     ei.setReferenceField("loader", cl.getClassLoaderObjectRef());
294     // the rest of the fields set to some dummy value
295     ei.setReferenceField("specTitle", env.newString("spectitle"));
296     ei.setReferenceField("specVersion", env.newString("specversion"));
297     ei.setReferenceField("specVendor", env.newString("specvendor"));
298     ei.setReferenceField("implTitle", env.newString("impltitle"));
299     ei.setReferenceField("implVersion", env.newString("implversion"));
300     ei.setReferenceField("sealBase", MJIEnv.NULL);
301
302     return pkgRef;
303   }
304
305   @MJI
306   public void setDefaultAssertionStatus__Z__V (MJIEnv env, int objRef, boolean enabled) {
307     ClassLoaderInfo cl = env.getClassLoaderInfo(objRef);
308     cl.setDefaultAssertionStatus(enabled);
309   }
310
311   @MJI
312   public void setPackageAssertionStatus__Ljava_lang_String_2Z__V (MJIEnv env, int objRef, int strRef, boolean enabled) {
313     ClassLoaderInfo cl = env.getClassLoaderInfo(objRef);
314     String pkgName = env.getStringObject(strRef);
315     cl.setPackageAssertionStatus(pkgName, enabled);
316   }
317
318   @MJI
319   public void setClassAssertionStatus__Ljava_lang_String_2Z__V (MJIEnv env, int objRef, int strRef, boolean enabled) {
320     ClassLoaderInfo cl = env.getClassLoaderInfo(objRef);
321     String clsName = env.getStringObject(strRef);
322     cl.setClassAssertionStatus(clsName, enabled);
323   }
324
325   @MJI
326   public void clearAssertionStatus____V (MJIEnv env, int objRef) {
327     ClassLoaderInfo cl = env.getClassLoaderInfo(objRef);
328     cl.clearAssertionStatus();
329   }
330 }