2 * Copyright (C) 2014, United States Government, as represented by the
3 * Administrator of the National Aeronautics and Space Administration.
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
10 * http://www.apache.org/licenses/LICENSE-2.0.
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.
18 package gov.nasa.jpf.vm;
21 import java.io.IOException;
22 import java.net.MalformedURLException;
23 import java.util.ArrayList;
24 import java.util.HashMap;
25 import java.util.Iterator;
27 import java.util.jar.JarEntry;
28 import java.util.jar.JarFile;
30 import gov.nasa.jpf.Config;
31 import gov.nasa.jpf.JPF;
32 import gov.nasa.jpf.JPFException;
33 import gov.nasa.jpf.SystemAttribute;
34 import gov.nasa.jpf.util.JPFLogger;
35 import gov.nasa.jpf.util.SparseIntVector;
36 import gov.nasa.jpf.util.StringSetMatcher;
39 * @author Nastaran Shafiei <nastaran.shafiei@gmail.com>
41 * Represents the classloader construct in VM which is responsible for loading
44 public class ClassLoaderInfo
45 implements Iterable<ClassInfo>, Comparable<ClassLoaderInfo>, Cloneable, Restorable<ClassLoaderInfo> {
47 static JPFLogger log = JPF.getLogger("class");
50 // the model class field name where we store our id
51 protected static final String ID_FIELD = "nativeId";
53 protected static Config config;
55 // this is where we keep the global list of classloader ids
56 protected static SparseIntVector globalCLids;
59 * Map from class file URLs to first ClassInfo that was read from it. This search
60 * global map is used to make sure we only read class files once
62 protected static Map<String,ClassInfo> loadedClasses;
65 * map from annotation class file URLs to AnnotationInfos, which have a separate JPF internal
66 * representation. Again, using a global map ensures we only read the related class files once
68 protected static Map<String,AnnotationInfo> loadedAnnotations;
70 // Map that keeps the classes defined (directly loaded) by this loader and the
71 // ones that are resolved from these defined classes
72 protected Map<String,ClassInfo> resolvedClasses;
74 // annotations directly loaded by this classloader
75 protected Map<String,AnnotationInfo> resolvedAnnotations;
77 // Represents the locations where this classloader can load classes form - has to be populated subclasses
78 protected ClassPath cp;
80 // The type of the corresponding class loader object
81 protected ClassInfo classInfo;
83 // The area containing static fields and classes
84 protected Statics statics;
86 protected boolean roundTripRequired = false;
88 // Search global id, which is the basis for canonical order of classloaders
91 // The java.lang.ClassLoader object reference
94 protected ClassLoaderInfo parent;
97 static class ClMemento implements Memento<ClassLoaderInfo> {
98 // note that we don't have to store the invariants (gid, parent, isSystemClassLoader)
100 Memento<Statics> staticsMemento;
101 Memento<ClassPath> cpMemento;
102 Map<String, Boolean> classAssertionStatus;
103 Map<String, Boolean> packageAssertionStatus;
104 boolean defaultAssertionStatus;
105 boolean isDefaultSet;
107 ClMemento (ClassLoaderInfo cl){
109 staticsMemento = cl.statics.getMemento();
110 cpMemento = cl.cp.getMemento();
111 classAssertionStatus = new HashMap<String, Boolean>(cl.classAssertionStatus);
112 packageAssertionStatus = new HashMap<String, Boolean>(cl.packageAssertionStatus);
113 defaultAssertionStatus = cl.defaultAssertionStatus;
114 isDefaultSet = cl.isDefaultSet;
118 public ClassLoaderInfo restore(ClassLoaderInfo ignored) {
119 staticsMemento.restore(cl.statics);
120 cpMemento.restore(null);
121 cl.classAssertionStatus = this.classAssertionStatus;
122 cl.packageAssertionStatus = this.packageAssertionStatus;
123 cl.defaultAssertionStatus = this.defaultAssertionStatus;
124 cl.isDefaultSet = this.isDefaultSet;
130 * This is invoked by VM.initSubsystems()
132 static void init (Config config) {
133 ClassLoaderInfo.config = config;
135 globalCLids = new SparseIntVector();
136 loadedClasses = new HashMap<String,ClassInfo>(); // not sure we actually want this for multiple runs (unless we check file stamps)
137 loadedAnnotations = new HashMap<String,AnnotationInfo>();
139 enabledAssertionPatterns = StringSetMatcher.getNonEmpty(config.getStringArray("vm.enable_assertions"));
140 disabledAssertionPatterns = StringSetMatcher.getNonEmpty(config.getStringArray("vm.disable_assertions"));
143 public static int getNumberOfLoadedClasses (){
144 return loadedClasses.size();
147 public static ClassInfo getCurrentResolvedClassInfo (String clsName){
148 ClassLoaderInfo cl = getCurrentClassLoader();
149 return cl.getResolvedClassInfo(clsName);
152 public static ClassInfo getSystemResolvedClassInfo (String clsName){
153 ClassLoaderInfo cl = getCurrentSystemClassLoader();
154 return cl.getResolvedClassInfo(clsName);
158 * for use from SystemClassLoaderInfo ctor, which doesn't have a ClassLoader object
159 * yet and has to set cp and id itself
161 protected ClassLoaderInfo (VM vm){
162 resolvedClasses = new HashMap<String,ClassInfo>();
163 resolvedAnnotations = new HashMap<String,AnnotationInfo>();
165 this.statics = createStatics(vm);
167 cp = new ClassPath();
169 // registration has to happen from SystemClassLoaderInfo ctor since we are
170 // only partially initialized at this point
174 * for all other classloaders, which require an already instantiated ClassLoader object
176 protected ClassLoaderInfo (VM vm, int objRef, ClassPath cp, ClassLoaderInfo parent) {
177 resolvedClasses = new HashMap<String,ClassInfo>();
178 resolvedAnnotations = new HashMap<String,AnnotationInfo>();
180 this.parent = parent;
181 this.objRef = objRef;
183 this.statics = createStatics(vm);
185 this.id = computeId(objRef);
186 ElementInfo ei = vm.getModifiableElementInfo(objRef);
188 ei.setIntField(ID_FIELD, id);
189 if (parent != null) {
190 ei.setReferenceField("parent", parent.objRef);
192 classInfo = ei.getClassInfo();
193 roundTripRequired = isRoundTripRequired();
195 vm.registerClassLoader(this);
199 public Memento<ClassLoaderInfo> getMemento (MementoFactory factory) {
200 return factory.getMemento(this);
203 public Memento<ClassLoaderInfo> getMemento(){
204 return new ClMemento(this);
207 protected Statics createStatics (VM vm){
208 Class<?>[] argTypes = { Config.class, KernelState.class };
209 Object[] args = { config, vm.getKernelState() };
211 return config.getEssentialInstance("vm.statics.class", Statics.class, argTypes, args);
215 * this is our internal, search global id that is used for the
223 * Returns the type of the corresponding class loader object
225 public ClassInfo getClassInfo () {
230 * Returns the object reference.
232 public int getClassLoaderObjectRef () {
236 protected int computeId (int objRef) {
237 int id = globalCLids.get(objRef);
239 id = globalCLids.size() + 1; // the first systemClassLoader is not in globalCLids and always has id '0'
240 globalCLids.set(objRef, id);
246 * For optimizing the class loading mechanism, if the class loader class and the
247 * classes of the whole parents hierarchy are descendant of URLClassLoader and
248 * do not override the ClassLoader.loadClass() & URLClassLoader.findClass, resolving
249 * the class is done natively within JPF
251 protected boolean isRoundTripRequired() {
252 return (parent!=null? parent.roundTripRequired: true) || !hasOriginalLoadingImp();
255 private boolean hasOriginalLoadingImp() {
256 String signature = "(Ljava/lang/String;)Ljava/lang/Class;";
257 MethodInfo loadClass = classInfo.getMethod("loadClass" + signature, true);
258 MethodInfo findClass = classInfo.getMethod("findClass" + signature, true);
260 return (loadClass.getClassName().equals("java.lang.ClassLoader") &&
261 findClass.getClassName().equals("java.net.URLClassLoader"));
264 public boolean isSystemClassLoader() {
268 public static ClassLoaderInfo getCurrentClassLoader() {
269 return getCurrentClassLoader( ThreadInfo.getCurrentThread());
272 public static ClassLoaderInfo getCurrentClassLoader (ThreadInfo ti) {
273 for (StackFrame frame = ti.getTopFrame(); frame != null; frame = frame.getPrevious()){
274 MethodInfo miFrame = frame.getMethodInfo();
275 ClassInfo ciFrame = miFrame.getClassInfo();
276 if (ciFrame != null){
277 return ciFrame.getClassLoaderInfo();
281 return ti.getSystemClassLoaderInfo();
284 public static SystemClassLoaderInfo getCurrentSystemClassLoader() {
285 ThreadInfo ti = ThreadInfo.getCurrentThread();
287 return ti.getSystemClassLoaderInfo();
289 // this is kind of a hack - we just use the latest SystemClassLoaderInfo instance
290 // this might happen if the SystemClassLoader preloads classes before we have a main thread
291 return SystemClassLoaderInfo.lastInstance;
295 public SystemClassLoaderInfo getSystemClassLoader() {
296 return getCurrentSystemClassLoader();
299 protected ClassInfo loadSystemClass (String clsName){
300 return getCurrentSystemClassLoader().loadSystemClass(clsName);
303 protected ClassInfo createClassInfo (String typeName, ClassFileMatch match, ClassLoaderInfo definingLoader) throws ClassParseException {
304 return getCurrentSystemClassLoader().createClassInfo( typeName, match, definingLoader);
307 protected ClassInfo createClassInfo (String typeName, String url, byte[] data, ClassLoaderInfo definingLoader) throws ClassParseException {
308 return getCurrentSystemClassLoader().createClassInfo( typeName, url, data, definingLoader);
311 protected void setAttributes (ClassInfo ci){
312 getCurrentSystemClassLoader().setAttributes(ci);
317 * obtain ClassInfo object for given class name
319 * if the requested class or any of its superclasses and interfaces
320 * is not found this method will throw a ClassInfoException. Loading
321 * of respective superclasses and interfaces happens recursively from here.
323 * Returned ClassInfo objects are not registered yet, i.e. still have to
324 * be added to the ClassLoaderInfo's statics, and don't have associated java.lang.Class
325 * objects until registerClass(ti) is called.
327 * Before any field or method access, the class also has to be initialized,
328 * which can include overlayed execution of <clinit> declaredMethods, which is done
329 * by calling initializeClass(ti,insn)
331 * this is for loading classes from the file system
333 public ClassInfo getResolvedClassInfo (String className) throws ClassInfoException {
334 String typeName = Types.getClassNameFromTypeName( className);
336 ClassInfo ci = resolvedClasses.get( typeName);
338 if (ClassInfo.isBuiltinClass( typeName)){
339 ci = loadSystemClass( typeName);
342 ClassFileMatch match = getMatch( typeName);
344 String url = match.getClassURL();
345 ci = loadedClasses.get( url); // have we loaded the class from this source before
347 if (ci.getClassLoaderInfo() != this){ // might have been loaded by another classloader
348 ci = ci.cloneFor(this);
352 log.info("loading class ", typeName, " from ", url);
353 ci = match.createClassInfo(this);
355 } catch (ClassParseException cpx){
356 throw new ClassInfoException( "error parsing class", this, "java.lang.NoClassDefFoundError", typeName, cpx);
359 loadedClasses.put( url, ci);
362 } else { // no match found
363 throw new ClassInfoException("class not found: " + typeName, this, "java.lang.ClassNotFoundException", typeName);
368 resolvedClasses.put(typeName, ci);
375 * this is for user defined ClassLoaders that explicitly provide the class file data
377 public ClassInfo getResolvedClassInfo (String className, byte[] data, int offset, int length) throws ClassInfoException {
378 String typeName = Types.getClassNameFromTypeName( className);
379 ClassInfo ci = resolvedClasses.get( typeName);
383 // it can't be a builtin class since we have classfile contents
384 String url = typeName; // three isn't really a URL for it, just choose somehting
385 SystemClassLoaderInfo sysCl = getCurrentSystemClassLoader();
386 ci = sysCl.createClassInfo(typeName, url, data, this);
388 // no use to store it in loadedClasses since the data might be dynamically generated
390 } catch (ClassParseException cpx) {
391 throw new ClassInfoException("error parsing class", this, "java.lang.NoClassDefFoundError", typeName, cpx);
395 resolvedClasses.put( typeName, ci);
401 public AnnotationInfo getResolvedAnnotationInfo (String typeName) throws ClassInfoException {
402 AnnotationInfo ai = resolvedAnnotations.get(typeName);
405 ClassFileMatch match = getMatch( typeName);
407 String url = match.getClassURL();
408 ai = loadedAnnotations.get(url); // have we loaded the class from this source before
410 if (ai.getClassLoaderInfo() != this) { // might have been loaded by another classloader
411 ai = ai.cloneFor(this);
416 ai = match.createAnnotationInfo(this);
418 } catch (ClassParseException cpx) {
419 throw new ClassInfoException("error parsing class", this, "java.lang.NoClassDefFoundError", typeName, cpx);
422 loadedAnnotations.put( url, ai);
425 } else { // no match found
426 throw new ClassInfoException("class not found: " + typeName, this, "java.lang.ClassNotFoundException", typeName);
429 resolvedAnnotations.put( typeName, ai);
435 public ClassInfo getResolvedAnnotationProxy (ClassInfo ciAnnotation){
436 String typeName = ciAnnotation.getName() + "$Proxy";
438 ClassInfo ci = resolvedClasses.get( typeName);
440 ci = ciAnnotation.createAnnotationProxy(typeName);
441 resolvedClasses.put( typeName, ci);
448 * This method returns a type which implements the given functional interface
449 * and contains a method that captures the behavior of the lambda expression.
451 public ClassInfo getResolvedFuncObjType (int bsIdx, ClassInfo fiClassInfo, String samUniqueName, BootstrapMethodInfo bmi, String[] freeVariableTypeNames) {
452 String typeName = bmi.enclosingClass.getName() + "$$Lambda$" + bsIdx;
454 ClassInfo funcObjType = resolvedClasses.get( typeName);
456 if (funcObjType == null) {
457 funcObjType = fiClassInfo.createFuncObjClassInfo(bmi, typeName, samUniqueName, freeVariableTypeNames);
458 resolvedClasses.put( typeName, funcObjType);
464 protected ClassInfo getAlreadyResolvedClassInfo(String cname) {
465 return resolvedClasses.get(cname);
468 protected void addResolvedClass(ClassInfo ci) {
469 resolvedClasses.put(ci.getName(), ci);
472 protected boolean hasResolved(String cname) {
473 return (resolvedClasses.get(cname)!=null);
477 * this one is for clients that need to synchronously get an initialized classinfo.
478 * NOTE: we don't handle clinits here. If there is one, this will throw
479 * an exception. NO STATIC BLOCKS / FIELDS ALLOWED
481 public ClassInfo getInitializedClassInfo (String clsName, ThreadInfo ti){
482 ClassInfo ci = getResolvedClassInfo(clsName);
483 ci.initializeClassAtomic(ti);
488 * obtain ClassInfo from context that does not care about resolution, i.e.
489 * does not check for NoClassInfoExceptions
491 * @param className fully qualified classname to get a ClassInfo for
492 * @return null if class was not found
494 public ClassInfo tryGetResolvedClassInfo (String className){
496 return getResolvedClassInfo(className);
497 } catch (ClassInfoException cx){
502 public ClassInfo getClassInfo (int id) {
503 ElementInfo ei = statics.get(id);
505 return ei.getClassInfo();
511 // it acquires the resolvedClassInfo by executing the class loader loadClass() method
512 public ClassInfo loadClass(String cname) {
514 if(roundTripRequired) {
515 // loadClass bytecode needs to be executed by the JPF vm
516 ci = loadClassOnJPF(cname);
518 // This class loader and the whole parent hierarchy use the standard class loading
519 // mechanism, therefore the class is loaded natively
520 ci = loadClassOnJVM(cname);
526 protected ClassInfo loadClassOnJVM(String cname) {
527 String className = Types.getClassNameFromTypeName(cname);
528 // Check if the given class is already resolved by this loader
529 ClassInfo ci = getAlreadyResolvedClassInfo(className);
534 ci = parent.loadClassOnJVM(cname);
536 ClassLoaderInfo systemClassLoader = getCurrentSystemClassLoader();
537 ci = systemClassLoader.getResolvedClassInfo(cname);
539 } catch(ClassInfoException cie) {
540 if(cie.getExceptionClass().equals("java.lang.ClassNotFoundException")) {
541 ci = getResolvedClassInfo(cname);
551 // we need a system attribute to
552 class LoadClassRequest implements SystemAttribute {
555 LoadClassRequest (String typeName){
556 this.typeName = typeName;
559 boolean isRequestFor( String typeName){
560 return this.typeName.equals( typeName);
564 protected ClassInfo loadClassOnJPF (String typeName) {
565 String className = Types.getClassNameFromTypeName(typeName);
566 // Check if the given class is already resolved by this loader
567 ClassInfo ci = getAlreadyResolvedClassInfo(className);
569 if(ci != null) { // class already resolved
572 } else { // class is not yet resolved, do a roundtrip for the respective loadClass() method
573 ThreadInfo ti = VM.getVM().getCurrentThread();
574 StackFrame frame = ti.getReturnedDirectCall();
576 if (frame != null){ // there was a roundtrip, but make sure it wasn't a recursive one
577 LoadClassRequest a = frame.getFrameAttr(LoadClassRequest.class);
578 if (a != null && a.isRequestFor(typeName)){ // the roundtrip is completed
579 int clsObjRef = frame.pop();
581 if (clsObjRef == MJIEnv.NULL) {
582 throw new ClassInfoException("class not found: " + typeName, this, "java.lang.NoClassDefFoundError", typeName);
584 return ti.getEnv().getReferredClassInfo(clsObjRef);
589 // initiate the roundtrip & bail out
590 pushloadClassFrame(typeName);
591 throw new LoadOnJPFRequired(typeName);
595 protected void pushloadClassFrame (String typeName) {
596 ThreadInfo ti = VM.getVM().getCurrentThread();
598 // obtain the class of this ClassLoader
599 ClassInfo clClass = VM.getVM().getClassInfo(objRef);
601 // retrieve the loadClass() method of this ClassLoader class
602 MethodInfo miLoadClass = clClass.getMethod("loadClass(Ljava/lang/String;)Ljava/lang/Class;", true);
604 // create a frame representing loadClass() & push it to the stack of the current thread
605 DirectCallStackFrame frame = miLoadClass.createDirectCallStackFrame( ti, 0);
607 String clsName = typeName.replace('/', '.');
608 int sRef = ti.getEnv().newString( clsName);
609 int argOffset = frame.setReferenceArgument( 0, objRef, null);
610 frame.setReferenceArgument( argOffset, sRef, null);
612 frame.setFrameAttr( new LoadClassRequest(typeName));
617 protected ClassInfo getDefinedClassInfo(String typeName){
618 ClassInfo ci = resolvedClasses.get(typeName);
619 if(ci != null && ci.classLoader == this) {
626 public ElementInfo getElementInfo (String typeName) {
627 ClassInfo ci = resolvedClasses.get(typeName);
629 ClassLoaderInfo cli = ci.classLoader;
630 Statics st = cli.statics;
631 return st.get(ci.getId());
634 return null; // not resolved
638 public ElementInfo getModifiableElementInfo (String typeName) {
639 ClassInfo ci = resolvedClasses.get(typeName);
641 ClassLoaderInfo cli = ci.classLoader;
642 Statics st = cli.statics;
643 return st.getModifiable(ci.getId());
646 return null; // not resolved
650 protected ClassFileMatch getMatch(String typeName) {
651 if(ClassInfo.isBuiltinClass(typeName)) {
655 ClassFileMatch match;
657 match = cp.findMatch(typeName);
658 } catch (ClassParseException cfx){
659 throw new JPFException("error reading class " + typeName, cfx);
666 * Finds the first Resource in the classpath which has the specified name.
667 * Returns null if no Resource is found.
669 public String findResource (String resourceName){
670 for (String cpe : getClassPathElements()) {
671 String URL = getResourceURL(cpe, resourceName);
680 * Finds all resources in the classpath with the given name. Returns an
681 * enumeration of the URL objects.
683 public String[] findResources (String resourceName){
684 ArrayList<String> resources = new ArrayList(0);
685 for (String cpe : getClassPathElements()) {
686 String URL = getResourceURL(cpe, resourceName);
688 if(!resources.contains(URL)) {
693 return resources.toArray(new String[resources.size()]);
696 protected String getResourceURL(String path, String resource) {
697 if(resource != null) {
699 if (path.endsWith(".jar")){
700 JarFile jar = new JarFile(path);
701 JarEntry e = jar.getJarEntry(resource);
703 File f = new File(path);
704 return "jar:" + f.toURI().toURL().toString() + "!/" + resource;
707 File f = new File(path, resource);
709 return f.toURI().toURL().toString();
712 } catch (MalformedURLException mfx){
714 } catch (IOException iox){
722 public Statics getStatics() {
726 public ClassPath getClassPath() {
730 public String[] getClassPathElements() {
731 return cp.getPathNames();
734 protected ClassFileContainer createClassFileContainer (String path){
735 return getCurrentSystemClassLoader().createClassFileContainer(path);
738 public void addClassPathElement (String path){
739 ClassFileContainer cfc = createClassFileContainer(path);
742 cp.addClassFileContainer(cfc);
744 log.warning("unknown classpath element: ", path);
749 * Comparison for sorting based on index.
752 public int compareTo (ClassLoaderInfo that) {
753 return this.id - that.id;
757 * Returns an iterator over the classes that are defined (directly loaded) by this classloader.
760 public Iterator<ClassInfo> iterator () {
761 return resolvedClasses.values().iterator();
765 * For now, this always returns true, and it used while the classloader is being
766 * serialized. That is going to be changed if we ever consider unloading the
767 * classes. For now, it is just added in analogy to ThreadInfo
769 public boolean isAlive () {
773 public Map<String, ClassLoaderInfo> getPackages() {
774 Map<String, ClassLoaderInfo> pkgs = new HashMap<String, ClassLoaderInfo>();
775 for(String cname: resolvedClasses.keySet()) {
776 if(!ClassInfo.isBuiltinClass(cname) && cname.indexOf('.')!=-1) {
777 pkgs.put(cname.substring(0, cname.lastIndexOf('.')), this);
781 Map<String, ClassLoaderInfo> parentPkgs = null;
783 parentPkgs = parent.getPackages();
786 if (parentPkgs != null) {
787 for (String pName: parentPkgs.keySet()) {
788 if (pkgs.get(pName) == null) {
789 pkgs.put(pName, parentPkgs.get(pName));
796 //-------- assertion management --------
798 // set in the jpf.properties file
799 static StringSetMatcher enabledAssertionPatterns;
800 static StringSetMatcher disabledAssertionPatterns;
802 protected Map<String, Boolean> classAssertionStatus = new HashMap<String, Boolean>();
803 protected Map<String, Boolean> packageAssertionStatus = new HashMap<String, Boolean>();
804 protected boolean defaultAssertionStatus = false;
805 protected boolean isDefaultSet = false;
807 protected boolean desiredAssertionStatus(String cname) {
808 // class level assertion can override all their assertion settings
809 Boolean result = classAssertionStatus.get(cname);
810 if (result != null) {
811 return result.booleanValue();
814 // package level assertion can override the default assertion settings
815 int dotIndex = cname.lastIndexOf(".");
816 if (dotIndex < 0) { // check for default package
817 result = packageAssertionStatus.get(null);
818 if (result != null) {
819 return result.booleanValue();
824 String pkgName = cname;
825 while(dotIndex > 0) { // check for the class package and its upper level packages
826 pkgName = pkgName.substring(0, dotIndex);
827 result = packageAssertionStatus.get(pkgName);
828 if (result != null) {
829 return result.booleanValue();
831 dotIndex = pkgName.lastIndexOf(".", dotIndex-1);
835 // class loader default, if it has been set, can override the settings
836 // specified by VM arguments
838 return defaultAssertionStatus;
840 return StringSetMatcher.isMatch(cname, enabledAssertionPatterns, disabledAssertionPatterns);
844 public void setDefaultAssertionStatus(boolean enabled) {
846 defaultAssertionStatus = enabled;
849 public void setClassAssertionStatus(String cname, boolean enabled) {
850 classAssertionStatus.put(cname, enabled);
853 public void setPackageAssertionStatus(String pname, boolean enabled) {
854 packageAssertionStatus.put(pname, enabled);
857 public void clearAssertionStatus() {
858 classAssertionStatus = new HashMap<String, Boolean>();
859 packageAssertionStatus = new HashMap<String, Boolean>();
860 defaultAssertionStatus = false;