View Javadoc

1   // jq_Class.java, created Mon Feb  5 23:23:20 2001 by joewhaley
2   // Copyright (C) 2001-3 John Whaley <jwhaley@alum.mit.edu>
3   // Licensed under the terms of the GNU LGPL; see COPYING for details.
4   package joeq.Class;
5   
6   import java.util.Arrays;
7   import java.util.Collection;
8   import java.util.Comparator;
9   import java.util.HashMap;
10  import java.util.Iterator;
11  import java.util.LinkedHashMap;
12  import java.util.LinkedList;
13  import java.util.List;
14  import java.util.Map;
15  import java.util.Set;
16  import java.util.StringTokenizer;
17  import java.io.DataInput;
18  import java.io.DataInputStream;
19  import java.io.DataOutput;
20  import java.io.IOException;
21  import joeq.Allocator.ObjectLayout;
22  import joeq.ClassLib.ClassLibInterface;
23  import joeq.Compiler.CompilationConstants;
24  import joeq.Compiler.BytecodeAnalysis.Bytecodes;
25  import joeq.Main.jq;
26  import joeq.Memory.Address;
27  import joeq.Memory.CodeAddress;
28  import joeq.Memory.HeapAddress;
29  import joeq.Memory.StackAddress;
30  import joeq.Runtime.Debug;
31  import joeq.Runtime.Reflection;
32  import joeq.Runtime.TypeCheck;
33  import joeq.UTF.UTFDataFormatError;
34  import joeq.UTF.Utf8;
35  import jwutil.io.Textualizer;
36  import jwutil.strings.Strings;
37  import jwutil.util.Assert;
38  import jwutil.util.Convert;
39  
40  /***
41   * jq_Class
42   *
43   * @author  John Whaley <jwhaley@alum.mit.edu>
44   * @version $Id: jq_Class.java 2465 2006-06-07 23:03:17Z joewhaley $
45   */
46  public final class jq_Class extends jq_Reference implements jq_ClassFileConstants, CompilationConstants {
47      
48      public static /*final*/ boolean TRACE = false;
49      public static /*final*/ boolean WARN_STALE_CLASS_FILES = false;
50      
51      //CR: used when trying to replace class contents.
52      public static boolean REPLACE_CLASS = false;
53      public static boolean TRACE_REPLACE_CLASS = false;
54      public static List classToReplace = new java.util.LinkedList(); // a set of Strings
55  
56      /***** INTERFACE ****/
57      
58      //// Always available
59      public final boolean isClassType() { return true; }
60      public final boolean isArrayType() { return false; }
61      public final boolean isAddressType() {
62          return this == Address._class || this == HeapAddress._class ||
63                 this == CodeAddress._class || this == StackAddress._class;
64          //return TypeCheck.isAssignable_noload(this, Address._class) == TypeCheck.YES;
65      }
66      public final String getName() { // fully-qualified name, e.g. java.lang.String
67          return className(desc);
68      }
69      public final String shortName() { // name with package removed
70          String s = desc.toString();
71          int index = s.lastIndexOf('/')+1;
72          if (index == 0) index = 1;
73          return s.substring(index, s.length()-1);
74      }
75      public final boolean isInSamePackage(jq_Class that) {
76          if (this.getClassLoader() != that.getClassLoader()) return false;
77          String s1 = this.getName();
78          String s2 = that.getName();
79          int ind1 = s1.lastIndexOf('.');
80          int ind2 = s2.lastIndexOf('.');
81          if (ind1 != ind2) return false;
82          if (ind1 != -1) {
83              if (!s1.substring(0, ind1).equals(s2.substring(0, ind1)))
84                  return false;
85          }
86          return true;
87      }
88      public final String getJDKName() {
89          return getName();
90      }
91      public final String getJDKDesc() {
92          return desc.toString().replace('/','.');
93      }
94      public jq_Member getDeclaredMember(jq_NameAndDesc nd) {
95          return (jq_Member)members.get(nd);
96      }
97      public jq_Member getDeclaredMember(String name, String desc) {
98          return (jq_Member)members.get(new jq_NameAndDesc(Utf8.get(name), Utf8.get(desc)));
99      }
100     public Collection getMembers() {
101         return members.values();
102     }
103     private void addDeclaredMember(jq_NameAndDesc nd, jq_Member m) {
104         Object b = members.put(nd, m);
105         if (TRACE) {
106             Debug.writeln("Added member to "+this+": "+m+" (old value "+b+")");
107             //new InternalError().printStackTrace();
108         }
109     }
110     public void accept(jq_TypeVisitor tv) {
111         tv.visitClass(this);
112         super.accept(tv);
113     }
114     
115     public static final boolean DETERMINISTIC = true;
116     
117     public int hashCode() {
118         if (DETERMINISTIC)
119             return desc.hashCode();
120         else
121             return System.identityHashCode(this);
122     }
123 
124     //// Available only after loading
125     public final int getMinorVersion() { return minor_version; }
126     public final int getMajorVersion() { return major_version; }
127     public final char getAccessFlags() { return access_flags; }
128     public final boolean isPublic() {
129         chkState(STATE_LOADING2);
130         return (access_flags & ACC_PUBLIC) != 0;
131     }
132     public final boolean isFinal() {
133         chkState(STATE_LOADING2);
134         return (access_flags & ACC_FINAL) != 0;
135     }
136     public final boolean isSpecial() {
137         chkState(STATE_LOADING2);
138         return (access_flags & ACC_SUPER) != 0;
139     }
140     public final boolean isInterface() {
141         chkState(STATE_LOADING2);
142         return (access_flags & ACC_INTERFACE) != 0;
143     }
144     public final boolean isAbstract() {
145         chkState(STATE_LOADING2);
146         return (access_flags & ACC_ABSTRACT) != 0;
147     }
148     public final jq_Class getSuperclass() {
149         chkState(STATE_LOADING3);
150         return super_class;
151     }
152     public final int getDepth() {
153         chkState(STATE_LOADED);
154         if (super_class == null) return 0;
155         jq_Reference t = getDirectPrimarySupertype();
156         t.load();
157         return 1+getDirectPrimarySupertype().getDepth();
158     }
159     public final jq_Reference getDirectPrimarySupertype() {
160         chkState(STATE_LOADING3);
161         if (this.isInterface()) return PrimordialClassLoader.getJavaLangObject();
162         return super_class;
163     }
164     public final jq_Class[] getDeclaredInterfaces() {
165         chkState(STATE_LOADING3);
166         return declared_interfaces;
167     }
168     public final jq_Class getDeclaredInterface(Utf8 desc) {
169         chkState(STATE_LOADING3);
170         for (int i=0; i<declared_interfaces.length; ++i) {
171             jq_Class in = declared_interfaces[i];
172             if (in.getDesc() == desc)
173                 return in;
174         }
175         return null;
176     }
177     public final jq_InstanceField[] getDeclaredInstanceFields() {
178         chkState(STATE_LOADING3);
179         return declared_instance_fields;
180     }
181     public final jq_InstanceField getDeclaredInstanceField(jq_NameAndDesc nd) {
182         chkState(STATE_LOADING3);
183         return (jq_InstanceField)findByNameAndDesc(declared_instance_fields, nd);
184     }
185     public final void setDeclaredInstanceFields(jq_InstanceField[] dif) {
186         chkState(STATE_LOADED);
187         declared_instance_fields = dif;
188     }
189     public final jq_StaticField[] getDeclaredStaticFields() {
190         chkState(STATE_LOADING3);
191         return static_fields;
192     }
193     public final jq_StaticField getDeclaredStaticField(jq_NameAndDesc nd) {
194         chkState(STATE_LOADING3);
195         return (jq_StaticField)findByNameAndDesc(static_fields, nd);
196     }
197     public final void setDeclaredStaticFields(jq_StaticField[] dsf) {
198         chkState(STATE_LOADED);
199         static_fields = dsf;
200     }
201 
202     public final jq_StaticField getStaticField(jq_NameAndDesc nd) {
203         chkState(STATE_LOADING3);
204         jq_StaticField f = (jq_StaticField)findByNameAndDesc(static_fields, nd);
205         if (f != null) return f;
206         
207         // check superclasses.
208         if (super_class != null) {
209             super_class.load();
210             f = super_class.getStaticField(nd);
211             if (f != null) return f;
212         }
213         
214         // static fields may be in implemented interfaces.
215         for (int i=0; i<declared_interfaces.length; ++i) {
216             jq_Class in = declared_interfaces[i];
217             in.load();
218             f = in.getStaticField(nd);
219             if (f != null) return f;
220         }
221 
222         return null;
223     }
224 
225     public final int getNumberOfStaticFields() {
226         chkState(STATE_LOADED);
227         int length = static_fields.length;
228         if (this.isInterface()) {
229             for (int i=0; i<declared_interfaces.length; ++i) {
230                 jq_Class in = declared_interfaces[i];
231                 in.load();
232                 length += in.getNumberOfStaticFields();
233             }
234         }
235         if (super_class != null) {
236             super_class.load();
237             length += super_class.getNumberOfStaticFields();
238         }
239         return length;
240     }
241 
242     private int getStaticFields_helper(jq_StaticField[] sfs, int current) {
243         System.arraycopy(static_fields, 0, sfs, current, static_fields.length);
244         current += static_fields.length;
245         if (this.isInterface()) {
246             for (int i=0; i<declared_interfaces.length; ++i) {
247                 jq_Class in = declared_interfaces[i];
248                 current = in.getStaticFields_helper(sfs, current);
249             }
250         }
251         if (super_class != null) {
252             current = super_class.getStaticFields_helper(sfs, current);
253         }
254         return current;
255     }
256 
257     // NOTE: fields in superinterfaces may appear multiple times.
258     public final jq_StaticField[] getStaticFields() {
259         chkState(STATE_LOADING3);
260         int length = this.getNumberOfStaticFields();
261         jq_StaticField[] sfs = new jq_StaticField[length];
262         int current = this.getStaticFields_helper(sfs, 0);
263         Assert._assert(current == sfs.length);
264         return sfs;
265     }
266 
267     public final jq_InstanceMethod[] getDeclaredInstanceMethods() {
268         chkState(STATE_LOADING3);
269         return declared_instance_methods;
270     }
271     public final jq_InstanceMethod getDeclaredInstanceMethod(jq_NameAndDesc nd) {
272         chkState(STATE_LOADING3);
273         return (jq_InstanceMethod)findByNameAndDesc(declared_instance_methods, nd);
274     }
275     public final void setDeclaredInstanceMethods(jq_InstanceMethod[] dim) {
276         chkState(STATE_LOADED);
277         declared_instance_methods = dim;
278     }
279     public final jq_StaticMethod[] getDeclaredStaticMethods() {
280         chkState(STATE_LOADING3);
281         return static_methods;
282     }
283     public final jq_StaticMethod getDeclaredStaticMethod(jq_NameAndDesc nd) {
284         chkState(STATE_LOADING3);
285         return (jq_StaticMethod)findByNameAndDesc(static_methods, nd);
286     }
287     public final void setDeclaredStaticMethods(jq_StaticMethod[] dsm) {
288         chkState(STATE_LOADED);
289         static_methods = dsm;
290     }
291     public final jq_StaticMethod getStaticMethod(jq_NameAndDesc nd) {
292         chkState(STATE_LOADING3);
293         jq_StaticMethod m = (jq_StaticMethod)findByNameAndDesc(static_methods, nd);
294         if (m != null) return m;
295         // check superclasses.
296         if (super_class != null) {
297             super_class.load();
298             m = super_class.getStaticMethod(nd);
299             if (m != null) return m;
300         }
301         // static methods may also be in superinterfaces.
302         for (int i=0; i<declared_interfaces.length; ++i) {
303             jq_Class in = declared_interfaces[i];
304             in.load();
305             m = in.getStaticMethod(nd);
306             if (m != null) return m;
307         }
308         return null;
309     }
310 
311     public final int getNumberOfStaticMethods() {
312         chkState(STATE_LOADED);
313         int length = static_methods.length;
314         for (int i=0; i<declared_interfaces.length; ++i) {
315             jq_Class in = declared_interfaces[i];
316             in.load();
317             length += in.getNumberOfStaticMethods();
318         }
319         if (super_class != null) {
320             super_class.load();
321             length += super_class.getNumberOfStaticMethods();
322         }
323         return length;
324     }
325 
326     private int getStaticMethods_helper(jq_StaticMethod[] sfs, int current) {
327         System.arraycopy(static_methods, 0, sfs, current, static_methods.length);
328         current += static_methods.length;
329         for (int i=0; i<declared_interfaces.length; ++i) {
330             jq_Class in = declared_interfaces[i];
331             current = in.getStaticMethods_helper(sfs, current);
332         }
333         if (super_class != null) {
334             current = super_class.getStaticMethods_helper(sfs, current);
335         }
336         return current;
337     }
338 
339     // NOTE: methods in superinterfaces may appear multiple times.
340     public final jq_StaticMethod[] getStaticMethods() {
341         chkState(STATE_LOADED);
342         int length = this.getNumberOfStaticMethods();
343         jq_StaticMethod[] sfs = new jq_StaticMethod[length];
344         int current = this.getStaticMethods_helper(sfs, 0);
345         Assert._assert(current == sfs.length);
346         return sfs;
347     }
348 
349     public final jq_InstanceMethod getInstanceMethod(jq_NameAndDesc nd) {
350         chkState(STATE_LOADING3);
351         jq_InstanceMethod m = (jq_InstanceMethod)findByNameAndDesc(declared_instance_methods, nd);
352         if (m != null) return m;
353         // check superclasses.
354         if (super_class != null) {
355             super_class.load();
356             m = super_class.getInstanceMethod(nd);
357             if (m != null) return m;
358         }
359         // check superinterfaces.
360         for (int i=0; i<declared_interfaces.length; ++i) {
361             jq_Class in = declared_interfaces[i];
362             in.load();
363             m = in.getInstanceMethod(nd);
364             if (m != null) return m;
365         }
366         return null;
367     }
368     public final jq_Initializer getInitializer(Utf8 desc) {
369         return getInitializer(new jq_NameAndDesc(Utf8.get("<init>"), desc));
370     }
371     public final jq_Initializer getInitializer(jq_NameAndDesc nd) {
372         chkState(STATE_LOADING3);
373         return (jq_Initializer)getDeclaredInstanceMethod(nd);
374     }
375     public final jq_ClassInitializer getClassInitializer() {
376         chkState(STATE_LOADING3);
377         return (jq_ClassInitializer)getDeclaredStaticMethod(new jq_NameAndDesc(Utf8.get("<clinit>"), Utf8.get("()V")));
378     }
379     
380     public jq_Field getDeclaredField(String name) {
381         return getDeclaredField(Utf8.get(name));
382     }
383     
384     public jq_Field getDeclaredField(Utf8 name) {
385         chkState(STATE_LOADED);
386         for (int i=0; i<declared_instance_fields.length; ++i) {
387             jq_Field m = declared_instance_fields[i];
388             if (m.getName() == name) return m;
389         }
390         for (int i=0; i<static_fields.length; ++i) {
391             jq_Field m = static_fields[i];
392             if (m.getName() == name) return m;
393         }
394         return null;
395     }
396     
397     public jq_Method getDeclaredMethod(String name) {
398         return getDeclaredMethod(Utf8.get(name));
399     }
400     
401     public jq_Method getDeclaredMethod(Utf8 name) {
402         chkState(STATE_LOADED);
403         for (int i=0; i<declared_instance_methods.length; ++i) {
404             jq_Method m = declared_instance_methods[i];
405             if (m.getName() == name) return m;
406         }
407         for (int i=0; i<static_methods.length; ++i) {
408             jq_Method m = static_methods[i];
409             if (m.getName() == name) return m;
410         }
411         return null;
412     }
413     
414     public jq_Method getMethodContainingLine(char lineNum) {
415         chkState(STATE_LOADED);
416         for (int i=0; i<declared_instance_methods.length; ++i) {
417             jq_Method m = declared_instance_methods[i];
418             jq_LineNumberBC a = m.getLineNumber(lineNum);
419             if (a != null) return m;
420         }
421         for (int i=0; i<static_methods.length; ++i) {
422             jq_Method m = static_methods[i];
423             jq_LineNumberBC a = m.getLineNumber(lineNum);
424             if (a != null) return m;
425         }
426         return null;
427     }
428     
429 
430     public final jq_ConstantPool getCP() {
431         chkState(STATE_LOADING2);
432         return const_pool;
433     }
434     public final void setCP(jq_ConstantPool cp) {
435         this.const_pool = cp;
436     }
437     public final Object getCP(char index) {
438         chkState(STATE_LOADING2);
439         return const_pool.get(index);
440     }
441     public final int getCPCount() {
442         chkState(STATE_LOADING2);
443         return const_pool.getCount();
444     }
445     public final byte getCPtag(char index) {
446         chkState(STATE_LOADING2);
447         return const_pool.getTag(index);
448     }
449     public final Integer getCPasInt(char index) {
450         chkState(STATE_LOADING2);
451         return const_pool.getAsInt(index);
452     }
453     public final Float getCPasFloat(char index) {
454         chkState(STATE_LOADING2);
455         return const_pool.getAsFloat(index);
456     }
457     public final Long getCPasLong(char index) {
458         chkState(STATE_LOADING2);
459         return const_pool.getAsLong(index);
460     }
461     public final Double getCPasDouble(char index) {
462         chkState(STATE_LOADING2);
463         return const_pool.getAsDouble(index);
464     }
465     public final String getCPasString(char index) {
466         chkState(STATE_LOADING2);
467         return const_pool.getAsString(index);
468     }
469     public final Object getCPasObjectConstant(char index) {
470         chkState(STATE_LOADING2);
471         return const_pool.getAsObjectConstant(index);
472     }
473     public final Utf8 getCPasUtf8(char index) {
474         chkState(STATE_LOADING2);
475         return const_pool.getAsUtf8(index);
476     }
477     public final jq_Type getCPasType(char index) {
478         chkState(STATE_LOADING2);
479         return const_pool.getAsType(index);
480     }
481     public final jq_Member getCPasMember(char index) {
482         chkState(STATE_LOADING2);
483         return const_pool.getAsMember(index);
484     }
485     public jq_StaticField getOrCreateStaticField(String name, String desc) {
486         return getOrCreateStaticField(new jq_NameAndDesc(Utf8.get(name), Utf8.get(desc)));
487     }
488     public jq_StaticField getOrCreateStaticField(jq_NameAndDesc nd) {
489         jq_StaticField sf = (jq_StaticField)getDeclaredMember(nd);
490         if (sf != null) return sf;
491         return createStaticField(nd);
492     }
493     jq_StaticField createStaticField(jq_NameAndDesc nd) {
494         Assert._assert(getDeclaredMember(nd) == null);
495         jq_StaticField f = jq_StaticField.newStaticField(this, nd);
496         addDeclaredMember(nd, f);
497         return f;
498     }
499     public final jq_StaticField getCPasStaticField(char index) {
500         chkState(STATE_LOADING2);
501         return const_pool.getAsStaticField(index);
502     }
503     public jq_InstanceField getOrCreateInstanceField(String name, String desc) {
504         return getOrCreateInstanceField(new jq_NameAndDesc(Utf8.get(name), Utf8.get(desc)));
505     }
506     public jq_InstanceField getOrCreateInstanceField(jq_NameAndDesc nd) {
507         jq_InstanceField sf = (jq_InstanceField)getDeclaredMember(nd);
508         if (sf != null) return sf;
509         return createInstanceField(nd);
510     }
511     jq_InstanceField createInstanceField(jq_NameAndDesc nd) {
512         Assert._assert(getDeclaredMember(nd) == null);
513         jq_InstanceField f = jq_InstanceField.newInstanceField(this, nd);
514         addDeclaredMember(nd, f);
515         return f;
516     }
517     public final jq_InstanceField getCPasInstanceField(char index) {
518         chkState(STATE_LOADING2);
519         return const_pool.getAsInstanceField(index);
520     }
521     public jq_StaticMethod getOrCreateStaticMethod(String name, String desc) {
522         return getOrCreateStaticMethod(new jq_NameAndDesc(Utf8.get(name), Utf8.get(desc)));
523     }
524     public jq_StaticMethod getOrCreateStaticMethod(jq_NameAndDesc nd) {
525         jq_StaticMethod sf = (jq_StaticMethod)getDeclaredMember(nd);
526         if (sf != null) return sf;
527         return createStaticMethod(nd);
528     }
529     jq_StaticMethod createStaticMethod(jq_NameAndDesc nd) {
530         Assert._assert(getDeclaredMember(nd) == null);
531         jq_StaticMethod f;
532         if (nd.getName() == Utf8.get("<clinit>") &&
533             nd.getDesc() == Utf8.get("()V")) {
534             f = jq_ClassInitializer.newClassInitializer(this, nd);
535         } else {
536             f = jq_StaticMethod.newStaticMethod(this, nd);
537         }
538         addDeclaredMember(nd, f);
539         return f;
540     }
541     public final jq_StaticMethod getCPasStaticMethod(char index) {
542         chkState(STATE_LOADING2);
543         return const_pool.getAsStaticMethod(index);
544     }
545     public jq_InstanceMethod getOrCreateInstanceMethod(String name, String desc) {
546         return getOrCreateInstanceMethod(new jq_NameAndDesc(Utf8.get(name), Utf8.get(desc)));
547     }
548     public jq_InstanceMethod getOrCreateInstanceMethod(jq_NameAndDesc nd) {
549         jq_InstanceMethod sf = (jq_InstanceMethod)getDeclaredMember(nd);
550         if (sf != null) return sf;
551         return createInstanceMethod(nd);
552     }
553     jq_InstanceMethod createInstanceMethod(jq_NameAndDesc nd) {
554         Assert._assert(getDeclaredMember(nd) == null);
555         jq_InstanceMethod f;
556         if (nd.getName() == Utf8.get("<init>")) {
557             f = jq_Initializer.newInitializer(this, nd);
558         } else {
559             f = jq_InstanceMethod.newInstanceMethod(this, nd);
560         }
561         addDeclaredMember(nd, f);
562         return f;
563     }
564     public final jq_InstanceMethod getCPasInstanceMethod(char index) {
565         chkState(STATE_LOADING2);
566         return const_pool.getAsInstanceMethod(index);
567     }
568     public final byte[] getAttribute(Utf8 name) {
569         chkState(STATE_LOADING3);
570         return (byte[])attributes.get(name);
571     }
572     public final byte[] getAttribute(String name) {
573         chkState(STATE_LOADING3);
574         return getAttribute(Utf8.get(name));
575     }
576     public final Iterator getAttributes() {
577         return attributes.keySet().iterator();
578     }
579     public final Utf8 getSourceFile() {
580         chkState(STATE_LOADING3);
581         byte[] attrib = getAttribute("SourceFile");
582         if (attrib == null) return null;
583         if (attrib.length != 2)
584             throw new ClassFormatError();
585         char cpi = Convert.twoBytesToChar(attrib, 0);
586         if (getCPtag(cpi) != CONSTANT_Utf8)
587             throw new ClassFormatError("cp tag "+(int)cpi+" is "+(int)getCPtag(cpi));
588         return getCPasUtf8(cpi);
589     }
590     public final boolean isSynthetic() {
591         chkState(STATE_LOADING3);
592         return getAttribute("Synthetic") != null;
593     }
594     public final boolean isDeprecated() {
595         chkState(STATE_LOADING3);
596         return getAttribute("Deprecated") != null;
597     }
598     public final void removeAttribute(String s) {
599         removeAttribute(Utf8.get(s));
600     }
601     public final void removeAttribute(Utf8 u) {
602         attributes.remove(u);
603     }
604     public final jq_Class[] getInnerClasses() {
605         chkState(STATE_LOADING3);
606         Assert.TODO();
607         return null;
608     }
609     public final jq_Class[] getSubClasses() {
610         chkState(STATE_LOADING3);
611         return subclasses;
612     }
613     public final jq_Class[] getSubInterfaces() {
614         chkState(STATE_LOADING3);
615         return subinterfaces;
616     }
617    //// Available after resolving
618     public final jq_Class[] getInterfaces() {
619         chkState(STATE_PREPARED);
620         return interfaces;
621     }
622     public final jq_Class getInterface(Utf8 desc) {
623         chkState(STATE_PREPARED);
624         for (int i=0; i<interfaces.length; ++i) {
625             jq_Class in = interfaces[i];
626             if (in.getDesc() == desc)
627                 return in;
628         }
629         return null;
630     }
631     public final boolean implementsInterface(jq_Class k) {
632         chkState(STATE_PREPARED);
633         for (int i=0; i<interfaces.length; ++i) {
634             if (interfaces[i] == k)
635                 return true;
636         }
637         if (false) {
638             for (int i=0; i<interfaces.length; ++i) {
639                 jq_Class k2 = interfaces[i];
640                 k2.prepare();
641                 if (k2.implementsInterface(k))
642                     return true;
643             }
644         }
645         return false;
646     }
647     public final jq_InstanceField[] getInstanceFields() {
648         chkState(STATE_PREPARED);
649         return instance_fields;
650     }
651     public final int[] getReferenceOffsets() {
652         chkState(STATE_PREPARED);
653         return reference_offsets;
654     }
655     public final jq_InstanceField getInstanceField(jq_NameAndDesc nd) {
656         chkState(STATE_LOADING3);
657         jq_InstanceField m = (jq_InstanceField)findByNameAndDesc(declared_instance_fields, nd);
658         if (m != null) return m;
659         // check superclasses.
660         if (super_class != null) {
661             super_class.load();
662             m = super_class.getInstanceField(nd);
663             if (m != null) return m;
664         }
665         return null;
666     }
667     public final jq_InstanceMethod[] getVirtualMethods() {
668         chkState(STATE_PREPARED);
669         return virtual_methods;
670     }
671     public final jq_InstanceMethod getVirtualMethod(jq_NameAndDesc nd) {
672         chkState(STATE_PREPARED);
673         return (jq_InstanceMethod)findByNameAndDesc(virtual_methods, nd);
674     }
675     public final int getInstanceSize() {
676         chkState(STATE_PREPARED);
677         return instance_size;
678     }
679     
680     public final int[] getStaticData() {
681         chkState(STATE_SFINITIALIZED);
682         return static_data;
683     }
684     public final void setStaticData(jq_StaticField sf, int data) {
685         chkState(STATE_SFINITIALIZED);
686         Assert._assert(sf.getDeclaringClass() == this);
687         Assert._assert(sf.getType().getReferenceSize() != 8);
688         int index = sf.getAddress().difference(HeapAddress.addressOf(static_data)) >> 2;
689         if (index < 0 || index >= static_data.length) {
690             Assert.UNREACHABLE("sf: "+sf+" index: "+index);
691         }
692         static_data[index] = data;
693     }
694     public final void setStaticData(jq_StaticField sf, long data) {
695         chkState(STATE_SFINITIALIZED);
696         Assert._assert(sf.getDeclaringClass() == this);
697         Assert._assert(sf.getType().getReferenceSize() == 8);
698         int index = sf.getAddress().difference(HeapAddress.addressOf(static_data)) >> 2;
699         static_data[index  ] = (int)(data);
700         static_data[index+1] = (int)(data >> 32);
701     }
702     public final void setStaticData(jq_StaticField sf, float data) {
703         setStaticData(sf, Float.floatToRawIntBits(data));
704     }
705     public final void setStaticData(jq_StaticField sf, double data) {
706         setStaticData(sf, Double.doubleToRawLongBits(data));
707     }
708     public final void setStaticData(jq_StaticField sf, Object data) {
709         chkState(STATE_SFINITIALIZED);
710         Assert._assert(sf.getDeclaringClass() == this);
711         Assert._assert(sf.getType().getReferenceSize() != 8);
712         int index = sf.getAddress().difference(HeapAddress.addressOf(static_data)) >> 2;
713         static_data[index] = HeapAddress.addressOf(data).to32BitValue();
714     }
715     public final void setStaticData(jq_StaticField sf, Address data) {
716         chkState(STATE_SFINITIALIZED);
717         Assert._assert(sf.getDeclaringClass() == this);
718         Assert._assert(sf.getType().getReferenceSize() != 8);
719         int index = sf.getAddress().difference(HeapAddress.addressOf(static_data)) >> 2;
720         static_data[index] = data.to32BitValue();
721     }
722 
723     public final Object newInstance() {
724         this.prepare(); // prepare(): to set instance_size and vtable
725         return _delegate.newInstance(this, instance_size, vtable);
726     }
727     
728     //// Implementation garbage.
729     private Map/*<jq_NameAndDesc->jq_Member>*/ members;
730     private char minor_version;
731     private char major_version;
732     private char access_flags;
733     private jq_Class super_class;
734     private jq_Class[] subclasses, subinterfaces;
735     private jq_Class[] declared_interfaces, interfaces;
736     private jq_StaticField[] static_fields;
737     private jq_StaticMethod[] static_methods;
738     private jq_InstanceField[] declared_instance_fields;
739     private jq_InstanceMethod[] declared_instance_methods;
740     private Map attributes;
741     private jq_ConstantPool const_pool;
742     private int static_data_size;
743     private int instance_size;
744     private jq_InstanceField[] instance_fields;
745     private int[] reference_offsets;
746     private jq_InstanceMethod[] virtual_methods;
747     private int[] static_data;
748     private boolean dont_align;
749 
750     private static jq_Member findByNameAndDesc(jq_Member[] array, jq_NameAndDesc nd) {
751         // linear search
752         for (int i=0; i<array.length; ++i) {
753             jq_Member m = array[i];
754             if (m.getNameAndDesc().equals(nd)) return m;
755         }
756         return null;
757     }
758     
759     /***
760      * Private constructor.
761      * Use a ClassLoader to create a jq_Class instance.
762      */
763     private jq_Class(ClassLoader class_loader, Utf8 desc) {
764         super(desc, class_loader);
765         this.subclasses = new jq_Class[0];
766         this.subinterfaces = new jq_Class[0];
767         this.members = new HashMap();
768     }
769     // ONLY TO BE CALLED BY ClassLoader!!!
770     public static jq_Class newClass(ClassLoader classLoader, Utf8 desc) {
771         Assert._assert(desc.isDescriptor(TC_CLASS));
772         return new jq_Class(classLoader, desc);
773     }
774 
775     /***
776      * Loads the binary data for this class.  See Jvm spec 2.17.2.
777      *
778      * Throws: ClassFormatError  if the binary data is malformed in some way
779      *         UnsupportedClassVersionError  if the binary data is of an unsupported version
780      *         ClassCircularityError  if it would be its own superclass or superinterface
781      *         NoClassDefFoundError  if the class definition cannot be found
782      */
783     public void load()
784     throws ClassFormatError, UnsupportedClassVersionError, ClassCircularityError, NoClassDefFoundError {
785         if (isLoaded()) return; // quick test
786         Assert._assert(class_loader == PrimordialClassLoader.loader);
787         DataInputStream in = null;
788         try {
789             in = ((PrimordialClassLoader)class_loader).getClassFileStream(desc);
790             if (in == null) throw new NoClassDefFoundError(className(desc));
791             try {
792                 load(in);
793             } catch (NoClassDefFoundError x) {
794                 x.printStackTrace();
795                 Assert.UNREACHABLE("Class not found error while attempting to load class!");
796             }
797             in.close();
798         } catch (IOException x) {
799             x.printStackTrace(); // for debugging
800             throw new ClassFormatError(x.toString());
801         } finally {
802             try { if (in != null) in.close(); } catch (IOException _) { }
803         }
804     }
805     public void load(DataInput in)
806     throws ClassFormatError, UnsupportedClassVersionError, ClassCircularityError {
807         if (isLoaded()) return; // quick test.
808         if (state == STATE_LOADERROR) throw new ClassFormatError();
809         synchronized (this) {
810             if (isLoaded()) return; // other thread already loaded this type.
811             if ((state == STATE_LOADING1) || (state == STATE_LOADING2) || (state == STATE_LOADING3))
812                 throw new ClassCircularityError(this.toString()); // recursively called load (?)
813             state = STATE_LOADING1;
814             if (TRACE) Debug.writeln("Beginning loading "+this+"...");
815             try {
816                 int magicNum = in.readInt(); // 0xCAFEBABE
817                 if (magicNum != 0xCAFEBABE)
818                     throw new ClassFormatError("bad magic number: "+Integer.toHexString(magicNum));
819                 minor_version = (char)in.readUnsignedShort(); // 3 or 0
820                 major_version = (char)in.readUnsignedShort(); // 45 to 48
821                 if (((major_version != 45) || (minor_version != 0)) &&
822                     ((major_version != 45) || (minor_version != 3)) &&
823                     ((major_version != 46) || (minor_version != 0)) &&
824                     ((major_version != 47) || (minor_version != 0)) &&
825                     ((major_version != 48) || (minor_version != 0)) &&
826                     ((major_version != 49) || (minor_version != 0))) {
827                     throw new UnsupportedClassVersionError("unsupported version "+(int)major_version+"."+(int)minor_version);
828                 }
829 
830                 char constant_pool_count = (char)in.readUnsignedShort();
831                 const_pool = new jq_ConstantPool(constant_pool_count);
832                 // read in the constant pool
833                 const_pool.load(in);
834                 // resolve the non-primitive stuff
835                 try {
836                     const_pool.resolve(class_loader);
837                 } catch (NoSuchMethodError x) {
838                     throw new NoSuchMethodError("In class "+this+": "+x.getMessage());
839                 } catch (NoSuchFieldError x) {
840                     throw new NoSuchFieldError("In class "+this+": "+x.getMessage());
841                 }
842                 
843                 access_flags = (char)in.readUnsignedShort();
844                 state = STATE_LOADING2;
845                 char selfindex = (char)in.readUnsignedShort();
846                 if (getCPtag(selfindex) != CONSTANT_ResolvedClass) {
847                     throw new ClassFormatError("constant pool entry "+(int)selfindex+", referred to by field this_class" +
848                                                ", is wrong type tag (expected="+CONSTANT_Class+", actual="+getCPtag(selfindex)+")");
849                 }
850                 if (getCP(selfindex) != this && !this.getDesc().toString().startsWith("LREPLACE")) {
851                     throw new ClassFormatError("expected class "+this+" but found class "+getCP(selfindex));
852                 }
853                 char superindex = (char)in.readUnsignedShort();
854                 if (superindex != 0) {
855                     if (getCPtag(superindex) != CONSTANT_ResolvedClass) {
856                         throw new ClassFormatError("constant pool entry "+(int)superindex+", referred to by field super_class" +
857                                                    ", is wrong type tag (expected="+CONSTANT_Class+", actual="+getCPtag(superindex)+")");
858                     }
859                     jq_Type super_type = getCPasType(superindex);
860                     if (!super_type.isClassType()) {
861                         throw new ClassFormatError("superclass ("+super_class.getName()+") is not a class type");
862                     }
863                     if (super_type == this) {
864                         throw new ClassCircularityError(this.getName()+" has itself as a superclass!");
865                     }
866                     super_class = (jq_Class)super_type;
867                     super_class.addSubclass(this);
868                 } else {
869                     // no superclass --> java.lang.Object
870                     if (PrimordialClassLoader.getJavaLangObject() != this) {
871                         throw new ClassFormatError("no superclass listed for class "+this);
872                     }
873                 }
874                 char n_interfaces = (char)in.readUnsignedShort();
875                 declared_interfaces = new jq_Class[n_interfaces];
876                 for (int i=0; i<n_interfaces; ++i) {
877                     char interface_index = (char)in.readUnsignedShort();
878                     if (getCPtag(interface_index) != CONSTANT_ResolvedClass) {
879                         throw new ClassFormatError("constant pool entry "+(int)interface_index+", referred to by interfaces["+i+"]"+
880                                                    ", is wrong type tag (expected="+CONSTANT_Class+", actual="+getCPtag(interface_index)+")");
881                     }
882                     declared_interfaces[i] = (jq_Class)getCPasType(interface_index);
883                     if (!declared_interfaces[i].isClassType()) {
884                         throw new ClassFormatError("implemented interface ("+super_class.getName()+") is not a class type");
885                     }
886                     if (declared_interfaces[i].isLoaded() && !declared_interfaces[i].isInterface()) {
887                         throw new ClassFormatError("implemented interface ("+super_class.getName()+") is not an interface type");
888                     }
889                     if (declared_interfaces[i] == jq_DontAlign._class) dont_align = true;
890                     declared_interfaces[i].addSubinterface(this);
891                 }
892 
893                 char n_declared_fields = (char)in.readUnsignedShort();
894                 char[] temp_declared_field_flags = new char[n_declared_fields];
895                 jq_Field[] temp_declared_fields = new jq_Field[n_declared_fields];
896                 int numStaticFields = 0, numInstanceFields = 0;
897                 for (int i=0; i<n_declared_fields; ++i) {
898                     temp_declared_field_flags[i] = (char)in.readUnsignedShort();
899                     // TODO: check flags for validity.
900                     char field_name_index = (char)in.readUnsignedShort();
901                     if (getCPtag(field_name_index) != CONSTANT_Utf8)
902                         throw new ClassFormatError("constant pool entry "+(int)field_name_index+", referred to by field "+i+
903                                                    ", is wrong type tag (expected="+CONSTANT_Utf8+", actual="+getCPtag(field_name_index)+")");
904                     Utf8 field_name = getCPasUtf8(field_name_index);
905                     char field_desc_index = (char)in.readUnsignedShort();
906                     if (getCPtag(field_desc_index) != CONSTANT_Utf8)
907                         throw new ClassFormatError("constant pool entry "+(int)field_desc_index+", referred to by field "+i+
908                                                    ", is wrong type tag (expected="+CONSTANT_Utf8+", actual="+getCPtag(field_desc_index)+")");
909                     Utf8 field_desc = getCPasUtf8(field_desc_index);
910                     if (!field_desc.isValidTypeDescriptor())
911                         throw new ClassFormatError(field_desc+" is not a valid type descriptor");
912                     jq_NameAndDesc nd = new jq_NameAndDesc(field_name, field_desc);
913                     jq_Field field = (jq_Field)getDeclaredMember(nd);
914                     if ((temp_declared_field_flags[i] & ACC_STATIC) != 0) {
915                         if (field == null) {
916                             field = createStaticField(nd);
917                         } else if (!field.isStatic())
918                             throw new VerifyError("static field "+field+" was referred to as an instance field");
919                         ++numStaticFields;
920                     } else {
921                         if (field == null) {
922                             field = createInstanceField(nd);
923                         } else if (field.isStatic())
924                             throw new VerifyError("instance field "+field+" was referred to as a static field");
925                         ++numInstanceFields;
926                     }
927                     field.load(temp_declared_field_flags[i], in);
928                     temp_declared_fields[i] = field;
929                 }
930                 static_data_size = 0;
931                 declared_instance_fields = new jq_InstanceField[numInstanceFields];
932                 static_fields = new jq_StaticField[numStaticFields];
933                 for (int i=0, di=-1, si=-1; i<n_declared_fields; ++i) {
934                     if ((temp_declared_field_flags[i] & ACC_STATIC) != 0) {
935                         static_fields[++si] = (jq_StaticField)temp_declared_fields[i];
936                         static_data_size += static_fields[si].getWidth();
937                     } else {
938                         declared_instance_fields[++di] = (jq_InstanceField)temp_declared_fields[i];
939                     }
940                 }
941                 if (!dont_align) {
942                     // sort instance fields in reverse by their size.
943                     Arrays.sort(declared_instance_fields, new Comparator() {
944                         public int compare(jq_InstanceField o1, jq_InstanceField o2) {
945                             int s1 = o1.getSize(), s2 = o2.getSize();
946                             if (s1 > s2) return -1;
947                             else if (s1 < s2) return 1;
948                             else return 0;
949                         }
950                         public int compare(Object o1, Object o2) {
951                             return compare((jq_InstanceField)o1, (jq_InstanceField)o2);
952                         }
953                     });
954                 }
955 
956                 char n_declared_methods = (char)in.readUnsignedShort();
957                 char[] temp_declared_method_flags = new char[n_declared_methods];
958                 jq_Method[] temp_declared_methods = new jq_Method[n_declared_methods];
959                 int numStaticMethods = 0, numInstanceMethods = 0;
960                 for (int i=0; i<n_declared_methods; ++i) {
961                     temp_declared_method_flags[i] = (char)in.readUnsignedShort();
962                     // TODO: check flags for validity.
963                     char method_name_index = (char)in.readUnsignedShort();
964                     if (getCPtag(method_name_index) != CONSTANT_Utf8)
965                         throw new ClassFormatError("constant pool entry "+(int)method_name_index+", referred to by method "+i+
966                                                    ", is wrong type tag (expected="+CONSTANT_Utf8+", actual="+getCPtag(method_name_index)+")");
967                     Utf8 method_name = getCPasUtf8(method_name_index);
968                     char method_desc_index = (char)in.readUnsignedShort();
969                     if (getCPtag(method_desc_index) != CONSTANT_Utf8)
970                         throw new ClassFormatError("constant pool entry "+(int)method_desc_index+", referred to by method "+i+
971                                                    ", is wrong type tag (expected="+CONSTANT_Utf8+", actual="+getCPtag(method_desc_index)+")");
972                     Utf8 method_desc = getCPasUtf8(method_desc_index);
973                     if (!method_desc.isValidMethodDescriptor())
974                         throw new ClassFormatError(method_desc+" is not a valid method descriptor");
975                     jq_NameAndDesc nd = new jq_NameAndDesc(method_name, method_desc);
976                     jq_Method method = (jq_Method)getDeclaredMember(nd);
977                     if ((temp_declared_method_flags[i] & ACC_STATIC) != 0) {
978                         if (method == null) {
979                             method = createStaticMethod(nd);
980                         } else if (!method.isStatic())
981                             throw new VerifyError();
982                         ++numStaticMethods;
983                     } else {
984                         if (method == null) {
985                             method = createInstanceMethod(nd);
986                         } else if (method.isStatic())
987                             throw new VerifyError();
988                         ++numInstanceMethods;
989                     }
990                     method.load(temp_declared_method_flags[i], in);
991                     temp_declared_methods[i] = method;
992                 }
993                 declared_instance_methods = new jq_InstanceMethod[numInstanceMethods];
994                 static_methods = new jq_StaticMethod[numStaticMethods];
995                 for (int i=0, di=-1, si=-1; i<n_declared_methods; ++i) {
996                     if ((temp_declared_method_flags[i] & ACC_STATIC) != 0) {
997                         static_methods[++si] = (jq_StaticMethod)temp_declared_methods[i];
998                     } else {
999                         declared_instance_methods[++di] = (jq_InstanceMethod)temp_declared_methods[i];
1000                     }
1001                 }
1002                 // now read class attributes
1003                 attributes = new HashMap();
1004                 readAttributes(in, attributes);
1005 
1006                 state = STATE_LOADING3;
1007                 
1008                 // if this is a class library, look for and load our mirror (implementation) class
1009                 // CR: il essaye tous les chemins possibles ou le mirror pourrait se trouver. Ie. ../common/.. ou sun_13/.. par ex.
1010                 Iterator impls = ClassLibInterface.DEFAULT.getImplementationClassDescs(getDesc());
1011                 while (impls.hasNext()) {
1012                     Utf8 impl_utf = (Utf8)impls.next();
1013                     jq_Class mirrorclass = (jq_Class)PrimordialClassLoader.getOrCreateType(class_loader, impl_utf);
1014                     try {
1015                         if (TRACE) Debug.writeln("Attempting to load mirror class "+mirrorclass);
1016                         mirrorclass.load();
1017                     } catch (NoClassDefFoundError x) {
1018                         // no mirror class
1019                         PrimordialClassLoader.unloadType(class_loader, mirrorclass);
1020                         mirrorclass = null;
1021                     }
1022                     if (mirrorclass != null) {
1023                         this.merge(mirrorclass);
1024                     }
1025                 }
1026                 
1027                 // if this is in the class library, remap method bodies.
1028                 if (this.isInClassLib()) {
1029                     if (TRACE) Debug.writeln(this+" is in the class library, rewriting method bodies.");
1030                     final jq_ConstantPool.ConstantPoolRebuilder cpr = this.rebuildConstantPool(false);
1031                     // visit instance fields
1032                     for (int i=0; i<this.declared_instance_fields.length; ++i) {
1033                         jq_InstanceField this_m = this.declared_instance_fields[i];
1034                         jq_NameAndDesc nd = ClassLibInterface.convertClassLibNameAndDesc(this, this_m.getNameAndDesc());
1035                         if (this_m.getNameAndDesc() != nd) {
1036                             if (TRACE) Debug.writeln("Rewriting field signature from "+this_m.getNameAndDesc()+" to "+nd);
1037                             jq_InstanceField this_m2 = getOrCreateInstanceField(nd);
1038                             this_m2.load(this_m);
1039                             this_m.unload(); Object b = this.members.remove(this_m.getNameAndDesc()); cpr.remove(this_m);
1040                             if (TRACE) Debug.writeln("Removed member "+this_m.getNameAndDesc()+" from member set of "+this+": "+b);
1041                             this.addDeclaredMember(nd, this_m2);
1042                             this_m = declared_instance_fields[i] = this_m2;
1043                         }
1044                     }
1045                     // visit static fields
1046                     for (int i=0; i<this.static_fields.length; ++i) {
1047                         jq_StaticField this_m = this.static_fields[i];
1048                         jq_NameAndDesc nd = ClassLibInterface.convertClassLibNameAndDesc(this, this_m.getNameAndDesc());
1049                         if (this_m.getNameAndDesc() != nd) {
1050                             if (TRACE) Debug.writeln("Rewriting field signature from "+this_m.getNameAndDesc()+" to "+nd);
1051                             jq_StaticField this_m2 = getOrCreateStaticField(nd);
1052                             this_m2.load(this_m);
1053                             this_m.unload(); Object b = this.members.remove(this_m.getNameAndDesc()); cpr.remove(this_m);
1054                             if (TRACE) Debug.writeln("Removed member "+this_m.getNameAndDesc()+" from member set of "+this+": "+b);
1055                             this.addDeclaredMember(nd, this_m2);
1056                             this_m = static_fields[i] = this_m2;
1057                         }
1058                     }
1059                     // visit all instance methods.
1060                     LinkedHashMap newInstanceMethods = new LinkedHashMap();
1061                     for (int i=0; i<this.declared_instance_methods.length; ++i) {
1062                         jq_InstanceMethod this_m = this.declared_instance_methods[i];
1063                         jq_NameAndDesc nd = ClassLibInterface.convertClassLibNameAndDesc(this, this_m.getNameAndDesc());
1064                         if (this_m.getNameAndDesc() != nd) {
1065                             if (TRACE) Debug.writeln("Rewriting method signature from "+this_m.getNameAndDesc()+" to "+nd);
1066                             jq_InstanceMethod this_m2 = getOrCreateInstanceMethod(nd);
1067                             this_m2.load(this_m);
1068                             this_m.unload(); Object b = this.members.remove(this_m.getNameAndDesc()); cpr.remove(this_m);
1069                             if (TRACE) Debug.writeln("Removed member "+this_m.getNameAndDesc()+" from member set of "+this+": "+b);
1070                             this.addDeclaredMember(nd, this_m2);
1071                             this_m = this_m2;
1072                         }
1073                         byte[] bc = this_m.getBytecode();
1074                         Bytecodes.InstructionList il;
1075                         if (bc == null) {
1076                             il = null;
1077                         } else {
1078                             // extract instructions of method.
1079                             if (TRACE) Debug.writeln("Extracting instructions of "+this_m);
1080                             il = new Bytecodes.InstructionList(this_m);
1081 
1082                             // update constant pool references in the instructions, and add them to our constant pool.
1083                             rewriteMethod(cpr, il);
1084                         }
1085                         
1086                         // cache the instruction list for later.
1087                         newInstanceMethods.put(this_m, il);
1088                     }
1089                     // visit all static methods.
1090                     LinkedHashMap newStaticMethods = new LinkedHashMap();
1091                     for (int i=0; i<this.static_methods.length; ++i) {
1092                         jq_StaticMethod this_m = this.static_methods[i];
1093                         jq_NameAndDesc nd = ClassLibInterface.convertClassLibNameAndDesc(this, this_m.getNameAndDesc());
1094                         if (this_m.getNameAndDesc() != nd) {
1095                             if (TRACE) Debug.writeln("Rewriting method signature from "+this_m.getNameAndDesc()+" to "+nd);
1096                             jq_StaticMethod this_m2 = getOrCreateStaticMethod(nd);
1097                             this_m2.load(this_m);
1098                             this_m.unload(); Object b = this.members.remove(this_m.getNameAndDesc()); cpr.remove(this_m);
1099                             if (TRACE) Debug.writeln("Removed member "+this_m.getNameAndDesc()+" from member set of "+this+": "+b);
1100                             this.addDeclaredMember(nd, this_m2);
1101                             this_m = this_m2;
1102                         }
1103                         byte[] bc = this_m.getBytecode();
1104                         Bytecodes.InstructionList il;
1105                         if (bc == null) {
1106                             il = null;
1107                         } else {
1108                             // extract instructions of method.
1109                             if (TRACE) Debug.writeln("Extracting instructions of "+this_m);
1110                             il = new Bytecodes.InstructionList(this_m);
1111 
1112                             // update constant pool references in the instructions, and add them to our constant pool.
1113                             rewriteMethod(cpr, il);
1114                         }
1115                         
1116                         // cache the instruction list for later.
1117                         newStaticMethods.put(this_m, il);
1118                     }
1119                     jq_ConstantPool new_cp = cpr.finish();
1120                     // rebuild method arrays
1121                     this.declared_instance_methods = new jq_InstanceMethod[newInstanceMethods.size()];
1122                     int j = -1;
1123                     for (Iterator i=newInstanceMethods.entrySet().iterator(); i.hasNext(); ) {
1124                         Map.Entry e = (Map.Entry)i.next();
1125                         jq_InstanceMethod i_m = (jq_InstanceMethod)e.getKey();
1126                         Bytecodes.InstructionList i_l = (Bytecodes.InstructionList)e.getValue();
1127                         if (i_l != null) {
1128                             if (TRACE) Debug.writeln("Rebuilding bytecodes for instance method "+i_m+", entry "+(j+1));
1129                             Bytecodes.CodeException[] ex_table = i_m.getExceptionTable(i_l);
1130                             Bytecodes.LineNumber[] line_num = i_m.getLineNumberTable(i_l);
1131                             i_m.setCode(i_l, ex_table, line_num, cpr);
1132                         } else {
1133                             if (TRACE) Debug.writeln("No bytecodes for instance method "+i_m+", entry "+(j+1));
1134                         }
1135                         //if (TRACE) Debug.writeln("Adding instance method "+i_m+" to array.");
1136                         this.declared_instance_methods[++j] = i_m;
1137                     }
1138                     this.static_methods = new jq_StaticMethod[newStaticMethods.size()];
1139                     j = -1;
1140                     for (Iterator i=newStaticMethods.entrySet().iterator(); i.hasNext(); ) {
1141                         Map.Entry e = (Map.Entry)i.next();
1142                         jq_StaticMethod i_m = (jq_StaticMethod)e.getKey();
1143                         Bytecodes.InstructionList i_l = (Bytecodes.InstructionList)e.getValue();
1144                         if (i_l != null) {
1145                             if (TRACE) Debug.writeln("Rebuilding bytecodes for static method "+i_m+", entry "+(j+1));
1146                             Bytecodes.CodeException[] ex_table = i_m.getExceptionTable(i_l);
1147                             Bytecodes.LineNumber[] line_num = i_m.getLineNumberTable(i_l);
1148                             i_m.setCode(i_l, ex_table, line_num, cpr);
1149                         } else {
1150                             if (TRACE) Debug.writeln("No bytecodes for static method "+i_m+", entry "+(j+1));
1151                         }
1152                         //if (TRACE) Debug.writeln("Adding static method "+i_m+" to array.");
1153                         this.static_methods[++j] = i_m;
1154                     }
1155                     this.remakeAttributes(cpr);
1156                     this.const_pool = new_cp;
1157                     getSourceFile(); // check for bug.
1158                     if (TRACE) Debug.writeln("Finished rebuilding constant pool.");
1159                 } else {
1160                     
1161                     // make sure that all member references from other classes point to actual members.
1162                     Iterator it = members.entrySet().iterator();
1163                     while (it.hasNext()) {
1164                         Map.Entry e = (Map.Entry)it.next();
1165                         jq_Member m = (jq_Member)e.getValue();
1166                         if (m.getState() < STATE_LOADED) {
1167                             // may be a reference to a member of a superclass or superinterface.
1168                             // this can happen when using old class files.
1169                             it.remove();
1170                             if (WARN_STALE_CLASS_FILES) {
1171                                 Set s = PrimordialClassLoader.loader.getClassesThatReference(m);
1172                                 System.err.println("Warning: classes "+s+" refer to member "+m+", which does not exist. This may indicate stale class files.");
1173                             }
1174                             //throw new ClassFormatError("no such member "+m+", referenced by "+s);
1175                         }
1176                     }
1177                     
1178                 }
1179 
1180                 // all done!
1181                 if (TRACE) Debug.writeln("Finished loading "+this);
1182                 state = STATE_LOADED;
1183                 
1184                 // classes to be replaced see the system loading a clone which contains the new implementation and
1185                 // whose name is set to "REPLACE<originalclassName>".
1186                 // If the currently loading class is a Replacing one, do the replacement.
1187                 // Old below refers to the class being replaced.
1188                 String thisDesc = this.getDesc().toString();
1189                 if (thisDesc.startsWith("LREPLACE")) {
1190                     Utf8 oldDesc = Utf8.get("L" + thisDesc.substring( 8 , thisDesc.length() )); // remove the 'LREPLACE' in name and restore 'L'
1191                     jq_Type old = PrimordialClassLoader.getOrCreateType(class_loader , oldDesc) ;
1192                     Assert._assert(old instanceof jq_Class);
1193                     if (((jq_Class)old).getState() < STATE_LOADED) {
1194                         // old has not been loaded yet, since it was not in the image
1195                         if (TRACE_REPLACE_CLASS) Debug.writeln("REPLACING Class: " + old.getDesc() + ". This class was not in the original image: doing nothing!");
1196                         PrimordialClassLoader.unloadType(class_loader , old) ;
1197                     } else {
1198                         replaceMethodIn((jq_Class) old);
1199                     }
1200                 }
1201             }
1202             catch (UTFDataFormatError x) {
1203                 state = STATE_LOADERROR;
1204                 //state = STATE_UNLOADED;
1205                 throw new ClassFormatError(x.toString());
1206             }
1207             catch (IOException x) {
1208                 state = STATE_LOADERROR;
1209                 //state = STATE_UNLOADED;
1210                 throw new ClassFormatError(x.toString());
1211             }
1212             catch (ArrayIndexOutOfBoundsException x) {
1213                 state = STATE_LOADERROR;
1214                 //state = STATE_UNLOADED;
1215                 x.printStackTrace();
1216                 throw new ClassFormatError("bad constant pool index");
1217             }
1218             catch (Error x) {
1219                 state = STATE_LOADERROR;
1220                 //state = STATE_UNLOADED;
1221                 throw x;
1222             }
1223         } // synchronized
1224     }
1225     
1226     // old : the class already present in the system whose members have to be replaced
1227     // this: the substitute, ie. the new version that contains the members to be added to OLD.
1228     private void replaceMethodIn(jq_Class old) {
1229         // cpa will manipulate and add elements to old.const_pool
1230         jq_ConstantPool.ConstantPoolAdder cpa =
1231             new jq_ConstantPool.ConstantPoolAdder(old.const_pool);
1232 
1233         // visit all STATIC methods of OLD to see
1234         // whether they have changed compared to those of NEW
1235         for (int i = 0;
1236             (old.static_methods != null) && i < old.static_methods.length;
1237             ++i) {
1238             jq_StaticMethod old_m = old.static_methods[i];
1239             jq_NameAndDesc nd = old_m.getNameAndDesc();
1240             jq_StaticMethod new_m = this.getDeclaredStaticMethod(nd);
1241             if (new_m != null) {
1242                 // verify if method has changed from NEW to OLD
1243                 byte[] new_bc = new_m.getBytecode();
1244                 byte[] old_bc = old_m.getBytecode();
1245                 //good enough to check whether a method has changed.
1246                 // Comparing both byte arrays byte by byte does not work!
1247                 if (new_bc.length == old_bc.length)
1248                     continue;
1249 
1250                 // update OLD according to NEW
1251                 if (TRACE_REPLACE_CLASS)
1252                     joeq.Runtime.Debug.writeln(
1253                         Strings.lineSep+Strings.lineSep+"In REPLACE: STARTING REPLACEMENT of:\t" + old_m);
1254 
1255                 jq_NameAndDesc old_m_nd = old_m.getNameAndDesc();
1256 
1257                 Bytecodes.InstructionList il;
1258                 if (TRACE) Debug.writeln("Extracting instructions of "+new_m);
1259                 il = new Bytecodes.InstructionList(new_m);
1260 
1261                 // update constant pool references in instructions, and add them to our constant pool.
1262                 if (TRACE_REPLACE_CLASS)
1263                     Debug.writeln(
1264                         "\tIn Replace: Rebuilding CP for static method "
1265                             + new_m);
1266                 old.rewriteMethodForReplace(cpa, il);
1267                 cpa.finish();
1268                 Bytecodes.CodeException[] ex_table = new_m.getExceptionTable(il);
1269                 Bytecodes.LineNumber[] line_num = new_m.getLineNumberTable(il);
1270                 // as a side-effect cpa will set OLD's cp to its new value.
1271                 new_m.setCode(il, ex_table, line_num, cpa);
1272 
1273                 old.remakeAttributes(cpa); // reset sourcefile
1274 
1275                 if (TRACE_REPLACE_CLASS)
1276                     Debug.writeln(
1277                         "\tIn Replace: Finished Rebuilding CP for static method "
1278                             + new_m);
1279 
1280                 // rename and rattach new_m to old
1281                 new_m.setNameAndDesc(old_m_nd);
1282                 old.addDeclaredMember(old_m_nd, new_m);
1283                 new_m.setDeclaringClass(old);
1284 
1285                 //preparing new method
1286                 new_m.prepare(); //state = prepared.
1287 
1288                 //compile
1289                 if (TRACE_REPLACE_CLASS)
1290                     Debug.writeln(
1291                         "\tIn REPLACE: Compiling stub for: " + new_m);
1292                 new_m.compile();
1293 
1294                 old.static_methods[i] = new_m;
1295                 old_m.default_compiled_version.redirect(new_m.default_compiled_version);
1296                 old_m.default_compiled_version =
1297                     new_m.default_compiled_version;
1298 
1299                 if (TRACE_REPLACE_CLASS)
1300                     Debug.writeln(
1301                         Strings.lineSep+Strings.lineSep+"In Replace: DONE REPLACEMENT for STATIC method "
1302                             + old_m);
1303             } else {
1304                 //TODO:
1305                 // user wants to remove a method from OLD
1306                 // not handled.
1307             }
1308         } // end static_methods
1309 
1310         // visit all INSTANCE methods of OLD to see whether they have changed compared to those of NEW
1311         for (int i = 0;
1312             (old.declared_instance_methods != null)
1313                 && i < old.declared_instance_methods.length;
1314             ++i) {
1315             jq_InstanceMethod old_m = old.declared_instance_methods[i];
1316             jq_NameAndDesc nd = old_m.getNameAndDesc();
1317             jq_InstanceMethod new_m = this.getDeclaredInstanceMethod(nd);
1318             if (new_m != null) {
1319                 // verify if method has changed from NEW to OLD
1320                 byte[] new_bc = new_m.getBytecode();
1321                 byte[] old_bc = old_m.getBytecode();
1322                 
1323                 if (new_bc == null || old_bc == null)
1324                     continue;
1325                                     
1326                 //good enough to check whether a method has changed.
1327                 // Comparing both byte arrays byte by byte does not work!
1328                 if (new_bc.length == old_bc.length)
1329                     continue;
1330                 // take next method
1331 
1332                 if (TRACE_REPLACE_CLASS)
1333                     joeq.Runtime.Debug.writeln(
1334                         Strings.lineSep+Strings.lineSep+"In REPLACE: STARTING REPLACEMENT of:\t" + old_m);
1335 
1336                 //info useful for new_m
1337                 jq_NameAndDesc old_m_nd = old_m.getNameAndDesc();
1338 
1339                 Bytecodes.InstructionList il;
1340                 // extract new instructions.
1341                 if (TRACE) Debug.writeln("Extracting instructions of "+new_m);
1342                 il = new Bytecodes.InstructionList(new_m);
1343 
1344                 // update constant pool references in the instructions, and add them to our constant pool.
1345                 if (TRACE_REPLACE_CLASS)
1346                     Debug.writeln(
1347                         "\tIn Replace: Rebuilding CP for instance method "
1348                             + new_m);
1349                 old.rewriteMethodForReplace(cpa, il);
1350                 //collect new entries for cp
1351                 cpa.finish(); //side-effect: commit new entries into cp.
1352                 Bytecodes.CodeException[] ex_table = new_m.getExceptionTable(il);
1353                 Bytecodes.LineNumber[] line_num = new_m.getLineNumberTable(il);
1354                 new_m.setCode(il, ex_table, line_num, cpa); // update ref. to new entries.
1355 
1356                 old.remakeAttributes(cpa); // reset sourcefile
1357                 //old.getSourceFile();
1358 
1359                 if (TRACE_REPLACE_CLASS)
1360                     Debug.writeln(
1361                         "\tIn Replace: Finished Rebuilding CP for instance method "
1362                             + new_m);
1363 
1364                 //make new appear as old.
1365                 new_m.setNameAndDesc(old_m_nd);
1366                 old.addDeclaredMember(old_m_nd, new_m);
1367                 new_m.setDeclaringClass(old);
1368 
1369                 if (new_m.isInitializer() || new_m.isPrivate()) {
1370                     new_m.prepare();
1371                     //compile
1372                     if (TRACE_REPLACE_CLASS)
1373                         Debug.writeln(
1374                             "\tIn REPLACE: Compiling stub for: " + new_m);
1375                     new_m.compile();
1376                 } else //ovverridable methods.
1377                     {
1378                     //prepare new_m to really become old_m.
1379                     //get old_m position and  get new entrypoint in vtable
1380                     int old_m_offset = old_m.getOffset();
1381                     new_m.prepare(old_m_offset);
1382                     //keep old_m offset and set state = prepared.
1383                     //compile
1384                     if (TRACE_REPLACE_CLASS)
1385                         Debug.writeln(
1386                             "\tIn REPLACE: Compiling stub for: " + new_m);
1387                     new_m.compile();
1388 
1389                     int index = (old_m_offset >> 2) - 1;
1390                     //old_m index in the array of virtualmethods.
1391                     Assert._assert(old.virtual_methods[index] == old_m);
1392                     old.virtual_methods[index] = new_m;
1393                     CodeAddress entryPoint =
1394                         new_m.getDefaultCompiledVersion().getEntrypoint();
1395                     ((Address[]) old.vtable)[index + 1] = entryPoint;
1396                     //+1 since vt[0] is this
1397                 }
1398 
1399                 old.declared_instance_methods[i] = new_m;
1400 
1401                 old_m.default_compiled_version.redirect(new_m.default_compiled_version);
1402                 old_m.default_compiled_version =
1403                     new_m.default_compiled_version;
1404 
1405                 if (TRACE_REPLACE_CLASS)
1406                     Debug.writeln(
1407                         Strings.lineSep+Strings.lineSep+"In Replace: DONE REPLACING instance method "
1408                             + old_m);
1409             } else {
1410                 //TODO:
1411                 // user wants to remove a method from OLD
1412                 // not handled yet
1413             }
1414         } // end declared_instances_methods
1415 
1416         {
1417             //TODO:
1418             // visit all methods in new to see whether there are completely new methods
1419             // that were NOT present before in old.
1420         }
1421     }
1422     
1423     public boolean isInClassLib() {
1424         return (this.getDesc().toString().startsWith("Ljoeq/ClassLib/") &&
1425                 this.getDesc().toString().indexOf('/', 11) != -1);
1426     }
1427     
1428     public boolean doesConstantPoolContain(Object o) {
1429         if (const_pool == null) return false;
1430         return const_pool.contains(o);
1431     }
1432     
1433     public jq_StaticMethod generateStaticMethodStub(jq_NameAndDesc nd, jq_StaticMethod m, char access_flags, char classfield_idx, char method_idx) {
1434         jq_Type[] params = m.getParamTypes();
1435         Assert._assert(params.length >= 1);
1436         int size = 3+((params.length-1)*2)+3+1;
1437         byte[] bc = new byte[size];
1438         bc[0] = (byte)0xb2; // getstatic
1439         bc[1] = (byte)(classfield_idx >> 8);
1440         bc[2] = (byte)classfield_idx;
1441         int k=2;
1442         for (int j=1, n=0; j<params.length; ++j, ++n) {
1443             if (params[j].isReferenceType()) {
1444                 bc[++k] = (byte)0x19; // aload
1445             } else if (params[j] == jq_Primitive.LONG) {
1446                 bc[++k] = (byte)0x16; // lload
1447             } else if (params[j] == jq_Primitive.FLOAT) {
1448                 bc[++k] = (byte)0x17; // fload
1449             } else if (params[j] == jq_Primitive.DOUBLE) {
1450                 bc[++k] = (byte)0x18; // dload
1451             } else {
1452                 bc[++k] = (byte)0x15; // iload
1453             }
1454             bc[++k] = (byte)n;
1455             if ((params[j] == jq_Primitive.LONG) || (params[j] == jq_Primitive.DOUBLE))
1456                 ++n;
1457         }
1458         bc[++k] = (byte)0xb8; // invokestatic
1459         bc[++k] = (byte)(method_idx>>8);
1460         bc[++k] = (byte)method_idx;
1461         jq_Type t = m.getReturnType();
1462         if (t.isReferenceType()) {
1463             bc[++k] = (byte)0xb0; // areturn
1464         } else if (t == jq_Primitive.LONG) {
1465             bc[++k] = (byte)0xad; // lreturn
1466         } else if (t == jq_Primitive.FLOAT) {
1467             bc[++k] = (byte)0xae; // freturn
1468         } else if (t == jq_Primitive.DOUBLE) {
1469             bc[++k] = (byte)0xaf; // dreturn
1470         } else if (t == jq_Primitive.VOID) {
1471             bc[++k] = (byte)0xb1; // return
1472         } else {
1473             bc[++k] = (byte)0xac; // ireturn
1474         }
1475         jq_Method stubm = (jq_Method)getDeclaredMember(nd);
1476         jq_StaticMethod stub;
1477         if (stubm == null) stub = jq_StaticMethod.newStaticMethod(this, nd);
1478         else {
1479             // method that we are overwriting must be static.
1480             Assert._assert(stubm.isStatic(), stubm.toString());
1481             stub = (jq_StaticMethod)stubm;
1482         }
1483         //char access_flags = (char)(m.getAccessFlags() & ~ACC_NATIVE);
1484         char max_stack = (char)Math.max(m.getParamWords(), m.getReturnType().getReferenceSize()>>2);
1485         char max_locals = (char)(m.getParamWords()-1);
1486         stub.load(access_flags, max_stack, max_locals, bc, new jq_TryCatchBC[0], new jq_LineNumberBC[0], new HashMap());
1487         return stub;
1488     }
1489     
1490     public jq_InstanceMethod generateInstanceMethodStub(jq_NameAndDesc nd, jq_StaticMethod m, char access_flags, char method_idx) {
1491         jq_Type[] params = m.getParamTypes();
1492         Assert._assert(params.length >= 1);
1493         int size = 1+((params.length-1)*2)+3+1;
1494         byte[] bc = new byte[size];
1495         bc[0] = (byte)0x2a; // aload_0
1496         int k=0;
1497         for (int j=1, n=1; j<params.length; ++j, ++n) {
1498             if (params[j].isReferenceType()) {
1499                 bc[++k] = (byte)0x19; // aload
1500             } else if (params[j] == jq_Primitive.LONG) {
1501                 bc[++k] = (byte)0x16; // lload
1502             } else if (params[j] == jq_Primitive.FLOAT) {
1503                 bc[++k] = (byte)0x17; // fload
1504             } else if (params[j] == jq_Primitive.DOUBLE) {
1505                 bc[++k] = (byte)0x18; // dload
1506             } else {
1507                 bc[++k] = (byte)0x15; // iload
1508             }
1509             bc[++k] = (byte)n;
1510             if ((params[j] == jq_Primitive.LONG) || (params[j] == jq_Primitive.DOUBLE))
1511                 ++n;
1512         }
1513         bc[++k] = (byte)0xb8; // invokestatic
1514         bc[++k] = (byte)(method_idx>>8);
1515         bc[++k] = (byte)method_idx;
1516         jq_Type t = m.getReturnType();
1517         if (t.isReferenceType()) {
1518             bc[++k] = (byte)0xb0; // areturn
1519         } else if (t == jq_Primitive.LONG) {
1520             bc[++k] = (byte)0xad; // lreturn
1521         } else if (t == jq_Primitive.FLOAT) {
1522             bc[++k] = (byte)0xae; // freturn
1523         } else if (t == jq_Primitive.DOUBLE) {
1524             bc[++k] = (byte)0xaf; // dreturn
1525         } else if (t == jq_Primitive.VOID) {
1526             bc[++k] = (byte)0xb1; // return
1527         } else {
1528             bc[++k] = (byte)0xac; // ireturn
1529         }
1530         jq_Method stubm = (jq_Method)getDeclaredMember(nd);
1531         jq_InstanceMethod stub;
1532         if (stubm == null) stub = jq_InstanceMethod.newInstanceMethod(this, nd);
1533         else {
1534             // method that we are overwriting must be instance.
1535             Assert._assert(!stubm.isStatic(), stubm.toString());
1536             stub = (jq_InstanceMethod)stubm;
1537         }
1538         //char access_flags = (char)(m.getAccessFlags() & ~ACC_NATIVE);
1539         char max_stack = (char)Math.max(m.getParamWords(), m.getReturnType().getReferenceSize()>>2);
1540         char max_locals = (char)m.getParamWords();
1541         stub.load(access_flags, max_stack, max_locals, bc, new jq_TryCatchBC[0], new jq_LineNumberBC[0], new HashMap());
1542         return stub;
1543     }
1544     
1545     // that: mirror
1546     public void merge(jq_Class that) {
1547         // initialize constant pool rebuilder
1548         final jq_ConstantPool.ConstantPoolRebuilder cpr = rebuildConstantPool(true);
1549         
1550         // add all instance fields.
1551         LinkedList newInstanceFields = new LinkedList();
1552         for (int i=0; i<that.declared_instance_fields.length; ++i) {
1553             jq_InstanceField that_f = that.declared_instance_fields[i];
1554             jq_NameAndDesc nd = ClassLibInterface.convertClassLibNameAndDesc(that, that_f.getNameAndDesc());
1555             jq_InstanceField this_f = this.getDeclaredInstanceField(nd);
1556             if (this_f != null) {
1557                 if (TRACE) Debug.writeln("Instance field "+this_f+" already exists, skipping.");
1558                 if (this_f.getAccessFlags() != that_f.getAccessFlags()) {
1559                     if (TRACE) 
1560                         Debug.writeln("Access flags of instance field "+this_f+" from merged class do not match. ("+
1561                                                  (int)this_f.getAccessFlags()+"!="+(int)that_f.getAccessFlags()+")");
1562                 }
1563                 continue;
1564             }
1565             this_f = getOrCreateInstanceField(nd);
1566             Assert._assert(this_f.getState() == STATE_UNLOADED);
1567             this_f.load(that_f);
1568             that_f.unload(); Object b = that.members.remove(that_f.getNameAndDesc());
1569             if (TRACE) Debug.writeln("Removed member "+that_f.getNameAndDesc()+" from member set of "+that+": "+b);
1570             if (TRACE) Debug.writeln("Adding instance field: "+this_f);
1571             this.addDeclaredMember(nd, this_f);
1572             newInstanceFields.add(this_f);
1573             cpr.addUtf8(this_f.getName());
1574             cpr.addUtf8(this_f.getDesc());
1575             cpr.addAttributeNames(this_f);
1576         }
1577         if (newInstanceFields.size() > 0) {
1578             jq_InstanceField[] ifs = new jq_InstanceField[this.declared_instance_fields.length+newInstanceFields.size()];
1579             System.arraycopy(this.declared_instance_fields, 0, ifs, 0, this.declared_instance_fields.length);
1580             int j = this.declared_instance_fields.length-1;
1581             for (Iterator i=newInstanceFields.iterator(); i.hasNext(); )
1582                 ifs[++j] = (jq_InstanceField)i.next();
1583             this.declared_instance_fields = ifs;
1584         }
1585         
1586         // add all static fields.
1587         LinkedList newStaticFields = new LinkedList();
1588         for (int i=0; i<that.static_fields.length; ++i) {
1589             jq_StaticField that_f = that.static_fields[i];
1590             jq_NameAndDesc nd = ClassLibInterface.convertClassLibNameAndDesc(that, that_f.getNameAndDesc());
1591             jq_StaticField this_f = this.getDeclaredStaticField(nd);
1592             if (this_f != null) {
1593                 if (TRACE) Debug.writeln("Static field "+this_f+" already exists, skipping.");
1594                 if (this_f.getAccessFlags() != that_f.getAccessFlags()) {
1595                     if (TRACE) 
1596                         Debug.writeln("Access flags of static field "+this_f+" from merged class do not match. ("+
1597                                                  (int)this_f.getAccessFlags()+"!="+(int)that_f.getAccessFlags()+")");
1598                 }
1599                 continue;
1600             }
1601             this_f = getOrCreateStaticField(nd);
1602             Assert._assert(this_f.getState() == STATE_UNLOADED);
1603             this_f.load(that_f);
1604             that_f.unload(); Object b = that.members.remove(that_f.getNameAndDesc());
1605             if (TRACE) Debug.writeln("Removed member "+that_f.getNameAndDesc()+" from member set of "+that+": "+b);
1606             if (TRACE) Debug.writeln("Adding static field: "+this_f);
1607             this.addDeclaredMember(nd, this_f);
1608             newStaticFields.add(this_f);
1609             cpr.addUtf8(this_f.getName());
1610             cpr.addUtf8(this_f.getDesc());
1611             cpr.addAttributeNames(this_f);
1612         }
1613         if (newStaticFields.size() > 0) {
1614             jq_StaticField[] ifs = new jq_StaticField[this.static_fields.length+newStaticFields.size()];
1615             System.arraycopy(this.static_fields, 0, ifs, 0, this.static_fields.length);
1616             int j = this.static_fields.length-1;
1617             for (Iterator i=newStaticFields.iterator(); i.hasNext(); ) {
1618                 ifs[++j] = (jq_StaticField)i.next();
1619                 this.static_data_size += ifs[j].getWidth();
1620             }
1621             this.static_fields = ifs;
1622         }
1623         
1624         // visit all instance methods.
1625         //
1626         // CR: visite toutes les methodes du mirror, dans le but de trouver celles qui sont a ajouter
1627         // a this. Une fois trouvees, ces methodes sont stockees dans newInstancesMethods.
1628         LinkedHashMap newInstanceMethods = new LinkedHashMap();
1629         for (int i=0; i<that.declared_instance_methods.length; ++i) {
1630             jq_InstanceMethod that_m = that.declared_instance_methods[i];
1631             jq_NameAndDesc nd = that_m.getNameAndDesc();
1632             //jq_NameAndDesc nd = merge_convertNameAndDesc(that_m.getNameAndDesc());
1633             Assert._assert(ClassLibInterface.convertClassLibNameAndDesc(that, nd) == nd);
1634             jq_InstanceMethod this_m = this.getDeclaredInstanceMethod(nd);
1635             byte[] bc = that_m.getBytecode();
1636             if (bc == null) {
1637                 if (this_m != null) {
1638                     if (TRACE) Debug.writeln("Using existing body for instance method "+this_m+".");
1639                 } else {
1640                     if (TRACE)
1641                         System.err.println("Body of method "+that_m+" doesn't already exist!");
1642                 }
1643                 continue;
1644             }
1645             if (bc.length == 5 && that_m instanceof jq_Initializer && that_m.getDesc() == Utf8.get("()V") &&
1646                 this.getInitializer(Utf8.get("()V")) != null) {
1647                 if (TRACE) Debug.writeln("Skipping default initializer "+that_m+".");
1648                 continue;
1649             }
1650             
1651             // extract instructions of method.
1652             if (TRACE) Debug.writeln("Extracting instructions of "+that_m);
1653             Bytecodes.InstructionList il = new Bytecodes.InstructionList(that_m);
1654             
1655             // update constant pool references in the instructions, and add them to our constant pool.
1656             rewriteMethod(cpr, il);
1657             
1658             if (false) { //(this_m != null) {
1659                 // method exists, use that one.
1660                 if (TRACE) Debug.writeln("Using existing instance method object "+this_m+".");
1661             } else {
1662                 if (TRACE) Debug.writeln("Creating new instance method object "+nd+".");
1663                 this_m = this.getOrCreateInstanceMethod(nd);
1664                 this.addDeclaredMember(nd, this_m);
1665                 that_m.unload(); Object b = that.members.remove(that_m.getNameAndDesc());
1666                 if (TRACE) Debug.writeln("Removed member "+that_m.getNameAndDesc()+" from member set of "+that+": "+b);
1667             }
1668             //CR: load porte mal son nom ici, car en fait il fait des choses tres basiques comme mettre le correct access code,
1669             // stack depth etc...
1670             this_m.load(that_m);
1671             
1672             // cache the instruction list for later.
1673             newInstanceMethods.put(this_m, il);
1674         }
1675         // CR: se contente de ramasser les instructions des methodes declarees dans la lib java sans celles qui viennent de Classlib.
1676         for (int i=0; i<this.declared_instance_methods.length; ++i) {
1677             jq_InstanceMethod this_m = this.declared_instance_methods[i];
1678             jq_Member this_m2 = this.getDeclaredMember(this_m.getNameAndDesc());
1679             if (newInstanceMethods.containsKey(this_m2)) {
1680                 if (TRACE) Debug.writeln("Skipping replaced instance method object "+this_m+".");
1681                 continue;
1682             }
1683             Assert._assert(this_m == this_m2);
1684             byte[] bc = this_m.getBytecode();
1685             if (bc == null) {
1686                 if (TRACE) Debug.writeln("Skipping native/abstract instance method object "+this_m+".");
1687                 newInstanceMethods.put(this_m, null);
1688                 continue;
1689             }
1690             
1691             // extract instruction list.
1692             if (TRACE) Debug.writeln("Extracting instructions of "+this_m);
1693             Bytecodes.InstructionList il = new Bytecodes.InstructionList(this_m);
1694             
1695             // add constant pool references from instruction list.
1696             cpr.addCode(il);
1697             
1698             // cache the instruction list for later.
1699             newInstanceMethods.put(this_m, il);
1700         }
1701         
1702         Bytecodes.InstructionList rebuilt_clinit = null;
1703         
1704         // visit all static methods.
1705         LinkedHashMap newStaticMethods = new LinkedHashMap();
1706         for (int i=0; i<that.static_methods.length; ++i) {
1707             jq_StaticMethod that_m = that.static_methods[i];
1708             Bytecodes.InstructionList il;
1709             jq_StaticMethod this_m;
1710             if (that_m instanceof jq_ClassInitializer) {
1711                 if (TRACE) Debug.writeln("Creating special static method for "+that_m+" class initializer.");
1712                 Assert._assert(that_m.getBytecode() != null);
1713                 Utf8 newname = Utf8.get("clinit_"+that.getJDKName());
1714                 jq_NameAndDesc nd = new jq_NameAndDesc(newname, that_m.getDesc());
1715                 this_m = getOrCreateStaticMethod(nd);
1716                 this.addDeclaredMember(nd, this_m);
1717                 this_m.load(that_m);
1718                 
1719                 // add a call to the special method in our class initializer.
1720                 jq_ClassInitializer clinit = getClassInitializer();
1721                 if (clinit == null) {
1722                     jq_NameAndDesc nd2 = new jq_NameAndDesc(Utf8.get("<clinit>"), Utf8.get("()V"));
1723                     clinit = (jq_ClassInitializer)getOrCreateStaticMethod(nd2);
1724                     this.addDeclaredMember(nd2, clinit);
1725                     clinit.load((char)(ACC_PUBLIC | ACC_STATIC), (char)0, (char)0, new byte[0],
1726                            new jq_TryCatchBC[0], new jq_LineNumberBC[0], new HashMap());
1727                     if (TRACE) Debug.writeln("Created class initializer "+clinit);
1728                     rebuilt_clinit = new Bytecodes.InstructionList();
1729                     Bytecodes.RETURN re = new Bytecodes.RETURN();
1730                     rebuilt_clinit.append(re);
1731                     rebuilt_clinit.setPositions();
1732                 } else {
1733                     if (TRACE) Debug.writeln("Using existing class initializer "+clinit);
1734                     rebuilt_clinit = new Bytecodes.InstructionList(clinit);
1735                 }
1736                 Bytecodes.INVOKESTATIC is = new Bytecodes.INVOKESTATIC(this_m);
1737                 rebuilt_clinit.insert(is);
1738                 cpr.addCode(rebuilt_clinit);
1739                 
1740                 // extract instructions of method.
1741                 if (TRACE) Debug.writeln("Extracting instructions of "+that_m);
1742                 il = new Bytecodes.InstructionList(that_m);
1743                 
1744                 newStaticMethods.put(clinit, rebuilt_clinit);
1745                 
1746                 that_m.unload(); Object b = that.members.remove(that_m.getNameAndDesc());
1747                 if (TRACE) Debug.writeln("Removed member "+that_m.getNameAndDesc()+" from member set of "+that+": "+b);
1748             } else {
1749                 jq_NameAndDesc nd = that_m.getNameAndDesc();
1750                 //jq_NameAndDesc nd = merge_convertNameAndDesc(that_m.getNameAndDesc());
1751                 Assert._assert(ClassLibInterface.convertClassLibNameAndDesc(that, nd) == nd);
1752                 this_m = this.getDeclaredStaticMethod(nd);
1753                 byte[] bc = that_m.getBytecode();
1754                 if (bc == null) {
1755                     if (this_m != null) {
1756                         if (TRACE) Debug.writeln("Using existing body for static method "+this_m+".");
1757                     } else {
1758                         if (TRACE)
1759                             System.err.println("Body of method "+that_m+" doesn't already exist!");
1760                     }
1761                     continue;
1762                 }
1763                 // extract instructions of method.
1764                 if (TRACE) Debug.writeln("Extracting instructions of "+that_m);
1765                 il = new Bytecodes.InstructionList(that_m);
1766                 
1767                 if (false) { //(this_m != null) {
1768                     // method exists, use that one.
1769                     if (TRACE) Debug.writeln("Using existing static method object "+this_m+".");
1770                 } else {
1771                     this_m = getOrCreateStaticMethod(nd);
1772                     this.addDeclaredMember(nd, this_m);
1773                     that_m.unload(); Object b = that.members.remove(that_m.getNameAndDesc());
1774                     if (TRACE) Debug.writeln("Removed member "+that_m.getNameAndDesc()+" from member set of "+that+": "+b);
1775                     if (TRACE) Debug.writeln("Created new static method object "+this_m+".");
1776                 }
1777                 this_m.load(that_m);
1778             }
1779             
1780             // update constant pool references in the instructions, and add them to our constant pool.
1781             rewriteMethod(cpr, il);
1782             
1783             // cache the instruction list for later.
1784             newStaticMethods.put(this_m, il);
1785         }
1786         for (int i=0; i<this.static_methods.length; ++i) {
1787             jq_StaticMethod this_m = this.static_methods[i];
1788             jq_Member this_m2 = this.getDeclaredMember(this_m.getNameAndDesc());
1789             if (newStaticMethods.containsKey(this_m2)) {
1790                 //if (TRACE) Debug.writeln("Skipping replaced static method object "+this_m+".");
1791                 continue;
1792             }
1793             Assert._assert(this_m == this_m2);
1794             byte[] bc = this_m.getBytecode();
1795             if (bc == null) {
1796                 //if (TRACE) Debug.writeln("Skipping native/abstract static method object "+this_m+".");
1797                 newStaticMethods.put(this_m, null);
1798                 continue;
1799             }
1800             
1801             // extract instruction list.
1802             if (TRACE) Debug.writeln("Extracting instructions of "+this_m);
1803             Bytecodes.InstructionList il = new Bytecodes.InstructionList(this_m);
1804             
1805             // add constant pool references from instruction list.
1806             cpr.addCode(il);
1807             
1808             // cache the instruction list for later.
1809             newStaticMethods.put(this_m, il);
1810         }
1811         
1812         // nothing more to add to constant pool, finish it.
1813         jq_ConstantPool new_cp = cpr.finish();
1814         
1815         // rebuild method arrays.
1816         this.declared_instance_methods = new jq_InstanceMethod[newInstanceMethods.size()];
1817         int j = -1;
1818         for (Iterator i=newInstanceMethods.entrySet().iterator(); i.hasNext(); ) {
1819             Map.Entry e = (Map.Entry)i.next();
1820             jq_InstanceMethod i_m = (jq_InstanceMethod)e.getKey();
1821             Bytecodes.InstructionList i_l = (Bytecodes.InstructionList)e.getValue();
1822             if (i_l != null) {
1823                 if (TRACE) Debug.writeln("Rebuilding bytecodes for instance method "+i_m+".");
1824                 Bytecodes.CodeException[] ex_table = i_m.getExceptionTable(i_l);
1825                 Bytecodes.LineNumber[] line_num = i_m.getLineNumberTable(i_l);
1826                 i_m.setCode(i_l, ex_table, line_num, cpr);
1827             } else {
1828                 if (TRACE) Debug.writeln("No bytecodes for instance method "+i_m+".");
1829             }
1830             //if (TRACE) Debug.writeln("Adding instance method "+i_m+" to array.");
1831             this.declared_instance_methods[++j] = i_m;
1832         }
1833         this.static_methods = new jq_StaticMethod[newStaticMethods.size()];
1834         j = -1;
1835         for (Iterator i=newStaticMethods.entrySet().iterator(); i.hasNext(); ) {
1836             Map.Entry e = (Map.Entry)i.next();
1837             jq_StaticMethod i_m = (jq_StaticMethod)e.getKey();
1838             Bytecodes.InstructionList i_l = (Bytecodes.InstructionList)e.getValue();
1839             if (i_l != null) {
1840                 if (TRACE) Debug.writeln("Rebuilding bytecodes for static method "+i_m+".");
1841                 Bytecodes.CodeException[] ex_table = i_m.getExceptionTable(i_l);
1842                 Bytecodes.LineNumber[] line_num = i_m.getLineNumberTable(i_l);
1843                 i_m.setCode(i_l, ex_table, line_num, cpr);
1844             } else {
1845                 if (TRACE) Debug.writeln("No bytecodes for static method "+i_m+".");
1846             }
1847             //if (TRACE) Debug.writeln("Adding static method "+i_m+" to array.");
1848             this.static_methods[++j] = i_m;
1849         }
1850         this.remakeAttributes(cpr);
1851         this.const_pool = new_cp;
1852         getSourceFile(); // check for bug.
1853         if (TRACE) Debug.writeln("Finished rebuilding constant pool.");
1854         //CR: ??? pourquoi faire ce qui suit a that?
1855         that.super_class.removeSubclass(that);
1856         for (int i=0; i<that.declared_interfaces.length; ++i) {
1857             jq_Class di = that.declared_interfaces[i];
1858             di.removeSubinterface(that);
1859         }
1860         PrimordialClassLoader.unloadType(class_loader, that);
1861         if (TRACE) Debug.writeln("Finished merging class "+this+".");
1862     }
1863     
1864     public void remakeAttributes(jq_ConstantPool.ConstantPoolRebuilder cpr) {
1865         Utf8 sf = getSourceFile();
1866         if (sf != null) {
1867             byte[] b = new byte[2];
1868             Convert.charToTwoBytes(cpr.get(sf), b, 0);
1869             attributes.put(Utf8.get("SourceFile"), b);
1870             if (TRACE) Debug.writeln("Reset SourceFile attribute to cp idx "+(int)cpr.get(sf)+".");
1871         }
1872     }
1873     
1874     private void rewriteMethod(jq_ConstantPool.ConstantPoolRebuilder cp, Bytecodes.InstructionList il) {
1875         final jq_ConstantPool.ConstantPoolRebuilder cpr = cp;
1876         il.accept(new Bytecodes.EmptyVisitor() {
1877             public void visitCPInstruction(Bytecodes.CPInstruction x) {
1878                 Object o = x.getObject();
1879                 if (o instanceof String) {
1880                     cpr.addString((String)o);
1881                 } else if (o instanceof Class) {
1882                     jq_Type r = Reflection.getJQType((Class)o);
1883                     if (r instanceof jq_Reference) {
1884                         r = ClassLibInterface.convertClassLibCPEntry((jq_Reference)r);
1885                         x.setObject(r.getJavaLangClassObject());
1886                     }
1887                     cpr.addType(r);
1888                 } else if (o instanceof jq_Type) {
1889                     if (o instanceof jq_Reference)
1890                         x.setObject(o = ClassLibInterface.convertClassLibCPEntry((jq_Reference)o));
1891                     cpr.addType((jq_Type)o);
1892                 } else if (o instanceof jq_Member) {
1893                     x.setObject(o = ClassLibInterface.convertClassLibCPEntry((jq_Member)o));
1894                     cpr.addMember((jq_Member)o);
1895                 } else {
1896                     cpr.addOther(o);
1897                 }
1898             }
1899         });
1900     }
1901     
1902     private void rewriteMethodForReplace(jq_ConstantPool.ConstantPoolRebuilder cp,
1903                                          Bytecodes.InstructionList il) {
1904         final jq_ConstantPool.ConstantPoolRebuilder cpr = cp;
1905         il.accept(new Bytecodes.EmptyVisitor() {
1906             public void visitCPInstruction(Bytecodes.CPInstruction x) {
1907                 Object o = x.getObject();
1908                 if (o instanceof String) {
1909                     cpr.addString((String) o);
1910                 } else if (o instanceof Class) {
1911                     cpr.addType(Reflection.getJQType((Class)o));
1912                 } else if (o instanceof jq_Type) {
1913                     if (o instanceof jq_Reference)
1914                         cpr.addType((jq_Type) o);
1915                 } else if (o instanceof jq_Member) {
1916                     cpr.addMember((jq_Member) o);
1917                 } else {
1918                     cpr.addOther(o);
1919                 }
1920             }
1921         });
1922     }
1923     
1924     public void merge_old(jq_Class that) {
1925         // initialize constant pool adder
1926         jq_ConstantPool.Adder cp_adder = const_pool.getAdder();
1927         
1928         // generate stubs for each of the methods in the other class.
1929         Assert._assert(that.declared_instance_methods.length <= 1, that.toString()); // the only instance method should be the fake <init> method.
1930         LinkedList toadd_instance = new LinkedList();
1931         LinkedList toadd_static = new LinkedList();
1932         char classfield_index = 0;
1933         for (int i=0; i<that.static_methods.length; ++i) {
1934             jq_StaticMethod sm = that.static_methods[i];
1935             if (sm.isClassInitializer()) continue;
1936             jq_Type[] that_param = sm.getParamTypes();
1937             Assert._assert(that_param.length >= 1, sm.toString());
1938             Utf8 name_utf = sm.getName();
1939             if (name_utf == Utf8.get("__init__")) name_utf = Utf8.get("<init>");
1940             char method_idx = cp_adder.add(sm, CONSTANT_ResolvedSMethodRef);
1941             if (that_param[0] == jq_Class._class) {
1942                 // overridden static method
1943                 char access_flags = sm.getAccessFlags();
1944                 if (classfield_index == 0) {
1945                     jq_StaticField that_sf = that.getDeclaredStaticField(new jq_NameAndDesc(Utf8.get("_class"), Utf8.get("Ljoeq/Class/jq_Class;")));
1946                     Assert._assert(that_sf != null);
1947                     classfield_index = cp_adder.add(that_sf, CONSTANT_ResolvedSFieldRef);
1948                 }
1949 uphere1:
1950                 for (int j=0; ; ++j) {
1951                     if (j>=static_methods.length) {
1952                         StringBuffer desc = new StringBuffer("(");
1953                         for (int k=1; k<that_param.length; ++k) {
1954                             desc.append(that_param[k].getDesc().toString());
1955                         }
1956                         desc.append(")");
1957                         desc.append(sm.getReturnType().getDesc().toString());
1958                         Utf8 desc_utf = Utf8.get(desc.toString());
1959                         jq_NameAndDesc nd = new jq_NameAndDesc(name_utf, desc_utf);
1960                         jq_StaticMethod stub = generateStaticMethodStub(nd, sm, access_flags, (char)classfield_index, (char)method_idx);
1961                         toadd_static.add(stub);
1962                         break;
1963                     }
1964                     jq_StaticMethod f = this.static_methods[j];
1965                     if (f.getName() == name_utf) {
1966                         // non-public classes may have "Ljava/lang/Object;", so we need to check element-by-element.
1967                         jq_Type[] this_param = f.getParamTypes();
1968                         if (this_param.length+1 != that_param.length) continue;
1969                         for (int k=0; k<this_param.length; ++k) {
1970                             if ((this_param[k] != that_param[k+1]) &&
1971                                 (that_param[k+1] != PrimordialClassLoader.getJavaLangObject())) continue uphere1;
1972                         }
1973                         jq_NameAndDesc nd = f.getNameAndDesc();
1974                         access_flags = f.getAccessFlags();
1975                         jq_StaticMethod stub = generateStaticMethodStub(nd, sm, access_flags, (char)classfield_index, (char)method_idx);
1976                         if (TRACE) Debug.writeln("Replacing static method: "+stub);
1977                         this.static_methods[j] = stub;
1978                         break;
1979                     }
1980                 }
1981             } else {
1982                 // overridden instance method
1983                 char access_flags = (char)(sm.getAccessFlags() & ~ACC_STATIC);
1984                 Assert._assert(that_param[0] == PrimordialClassLoader.getJavaLangObject() || that_param[0] == this, sm.toString());
1985 uphere2:
1986                 for (int j=0; ; ++j) {
1987                     if (j>=declared_instance_methods.length) {
1988                         StringBuffer desc = new StringBuffer("(");
1989                         for (int k=1; k<that_param.length; ++k) {
1990                             desc.append(that_param[k].getDesc().toString());
1991                         }
1992                         desc.append(")");
1993                         desc.append(sm.getReturnType().getDesc().toString());
1994                         Utf8 desc_utf = Utf8.get(desc.toString());
1995                         jq_NameAndDesc nd = new jq_NameAndDesc(name_utf, desc_utf);
1996                         jq_InstanceMethod stub = generateInstanceMethodStub(nd, sm, access_flags, (char)method_idx);
1997                         toadd_instance.add(stub);
1998                         break;
1999                     }
2000                     jq_InstanceMethod f = this.declared_instance_methods[j];
2001                     if (f.getName() == name_utf) {
2002                         // non-public classes may have "Ljava/lang/Object;", so we need to check element-by-element.
2003                         jq_Type[] this_param = f.getParamTypes();
2004                         if (this_param.length != that_param.length) continue;
2005                         for (int k=0; k<this_param.length; ++k) {
2006                             if ((this_param[k] != that_param[k]) &&
2007                             (that_param[k] != PrimordialClassLoader.getJavaLangObject())) continue uphere2;
2008                         }
2009                         jq_NameAndDesc nd = f.getNameAndDesc();
2010                         access_flags = f.getAccessFlags();
2011                         jq_InstanceMethod stub = generateInstanceMethodStub(nd, sm, access_flags, (char)method_idx);
2012                         if (TRACE) Debug.writeln("Replacing instance method: "+stub);
2013                         this.declared_instance_methods[j] = stub;
2014                         break;
2015                     }
2016                 }
2017             }
2018         }
2019         if (toadd_static.size() > 0) {
2020             jq_StaticMethod[] sms = new jq_StaticMethod[this.static_methods.length+toadd_static.size()];
2021             int i = this.static_methods.length-1;
2022             System.arraycopy(this.static_methods, 0, sms, 0, this.static_methods.length);
2023             Iterator it = toadd_static.iterator();
2024             while (it.hasNext()) {
2025                 jq_StaticMethod stub = (jq_StaticMethod)it.next();
2026                 if (TRACE) Debug.writeln("Adding static method stub: "+stub);
2027                 sms[++i] = stub;
2028             }
2029             this.static_methods = sms;
2030         }
2031         if (toadd_instance.size() > 0) {
2032             jq_InstanceMethod[] ims = new jq_InstanceMethod[this.declared_instance_methods.length+toadd_instance.size()];
2033             int i = this.declared_instance_methods.length-1;
2034             System.arraycopy(this.declared_instance_methods, 0, ims, 0, this.declared_instance_methods.length);
2035             Iterator it = toadd_instance.iterator();
2036             while (it.hasNext()) {
2037                 jq_InstanceMethod stub = (jq_InstanceMethod)it.next();
2038                 if (TRACE) Debug.writeln("Adding instance method stub: "+stub);
2039                 ims[++i] = stub;
2040             }
2041             this.declared_instance_methods = ims;
2042         }
2043         // add all instance fields.
2044         if (that.declared_instance_fields.length > 0) {
2045             jq_InstanceField[] ifs = new jq_InstanceField[this.declared_instance_fields.length+that.declared_instance_fields.length];
2046             System.arraycopy(this.declared_instance_fields, 0, ifs, 0, this.declared_instance_fields.length);
2047             int i = this.declared_instance_fields.length-1;
2048             for (int j=0; j<that.declared_instance_fields.length; ++j) {
2049                 jq_InstanceField that_f = that.declared_instance_fields[j];
2050                 jq_InstanceField this_f = getOrCreateInstanceField(that_f.getNameAndDesc());
2051                 Assert._assert(this_f.getState() == STATE_UNLOADED, "conflict in field names in merged class: "+this_f);
2052                 this_f.load(that_f.getAccessFlags(), that_f.getAttributes());
2053                 if (TRACE) Debug.writeln("Adding instance field: "+this_f);
2054                 ifs[++i] = this_f;
2055             }
2056             this.declared_instance_fields = ifs;
2057         }
2058         cp_adder.finish();
2059     }
2060 
2061     public void verify() {
2062         if (isVerified()) return; // quick test.
2063         synchronized (this) {
2064             if (isVerified()) return; // other thread already loaded this type.
2065             if (!isLoaded()) load();
2066             if (state == STATE_VERIFYING)
2067                 throw new ClassCircularityError(this.toString()); // recursively called verify
2068             if (state == STATE_VERIFYERROR)
2069                 throw new VerifyError();
2070             state = STATE_VERIFYING;
2071             try {
2072                 if (TRACE) Debug.writeln("Beginning verifying "+this+"...");
2073                 if (super_class != null) {
2074                     super_class.verify();
2075                 }
2076                 // TODO: classfile verification
2077                 if (TRACE) Debug.writeln("Finished verifying "+this);
2078                 state = STATE_VERIFIED;
2079             } catch (Error x) {
2080                 state = STATE_VERIFYERROR;
2081                 throw x;
2082             }
2083         }
2084     }
2085     
2086     public void prepare() {
2087         if (isPrepared()) return; // quick test.
2088         synchronized (this) {
2089             if (isPrepared()) return; // other thread already loaded this type.
2090             if (!isVerified()) verify();
2091             if (state == STATE_PREPARING)
2092                 throw new ClassCircularityError(this.toString()); // recursively called prepare (?)
2093             if (state == STATE_PREPAREERROR)
2094                 throw new ClassFormatError();
2095             state = STATE_PREPARING;
2096             try {
2097                 if (TRACE) Debug.writeln("Beginning preparing "+this+"...");
2098     
2099                 // note: this method is a good candidate for specialization on super_class != null.
2100                 if (super_class != null) {
2101                     super_class.prepare();
2102                 }
2103     
2104                 int superfields;
2105                 if (super_class != null) superfields = super_class.instance_fields.length;
2106                 else superfields = 0;
2107                 int numOfInstanceFields = superfields + this.declared_instance_fields.length;
2108                 this.instance_fields = new jq_InstanceField[numOfInstanceFields];
2109                 if (superfields > 0)
2110                     System.arraycopy(super_class.instance_fields, 0, this.instance_fields, 0, superfields);
2111     
2112                 int superreferencefields;
2113                 if (super_class != null) superreferencefields = super_class.reference_offsets.length;
2114                 else superreferencefields = 0;
2115                 int numOfReferenceFields = superreferencefields;
2116                 
2117                 // lay out instance fields
2118                 int currentInstanceField = superfields-1;
2119                 int size;
2120                 if (super_class != null) size = super_class.instance_size;
2121                 else size = ObjectLayout.OBJ_HEADER_SIZE;
2122                 if (declared_instance_fields.length > 0) {
2123                     if (!dont_align) {
2124                         // align on the largest data type
2125                         int largestDataType = declared_instance_fields[0].getSize();
2126                         int align = size & largestDataType-1;
2127                         if (align != 0) {
2128                             if (TRACE) Debug.writeln("Gap of size "+align+" has been filled.");
2129                             // fill in the gap with smaller fields
2130                             for (int i=1; i<declared_instance_fields.length; ++i) {
2131                                 jq_InstanceField f = declared_instance_fields[i];
2132                                 int fsize = f.getSize();
2133                                 if (fsize <= largestDataType-align) {
2134                                     instance_fields[++currentInstanceField] = f;
2135                                     if (TRACE) Debug.writeln("Filling in field #"+currentInstanceField+" "+f+" at offset "+Strings.shex(size - ObjectLayout.OBJ_HEADER_SIZE));
2136                                     f.prepare(size - ObjectLayout.OBJ_HEADER_SIZE);
2137                                     if (f.getType().isReferenceType() && !f.getType().isAddressType())
2138                                         ++numOfReferenceFields;
2139                                     size += fsize;
2140                                     align += fsize;
2141                                 }
2142                                 if (align == largestDataType) {
2143                                     if (TRACE) Debug.writeln("Gap of size "+align+" has been filled.");
2144                                     break;
2145                                 }
2146                             }
2147                         }
2148                     } else {
2149                         if (TRACE) Debug.writeln("Skipping field alignment for class "+this);
2150                     }
2151                     for (int i=0; i<declared_instance_fields.length; ++i) {
2152                         jq_InstanceField f = declared_instance_fields[i];
2153                         if (f.getState() == STATE_LOADED) {
2154                             instance_fields[++currentInstanceField] = f;
2155                             if (TRACE) Debug.writeln("Laying out field #"+currentInstanceField+" "+f+" at offset "+Strings.shex(size - ObjectLayout.OBJ_HEADER_SIZE));
2156                             f.prepare(size - ObjectLayout.OBJ_HEADER_SIZE);
2157                             if (f.getType().isReferenceType() && !f.getType().isAddressType())
2158                                 ++numOfReferenceFields;
2159                             size += f.getSize();
2160                         }
2161                     }
2162                 }
2163                 this.instance_size = (size+3) & ~3;
2164     
2165                 this.reference_offsets = new int[numOfReferenceFields];
2166                 int k = -1;
2167                 for (int i=0; i < instance_fields.length; ++i) {
2168                     jq_InstanceField f = instance_fields[i];
2169                     if (f.getType().isReferenceType() && !f.getType().isAddressType()) {
2170                         reference_offsets[++k] = f.getOffset(); 
2171                     }
2172                 }
2173                 Assert._assert(k+1 == this.reference_offsets.length);
2174     
2175                 // lay out virtual method table
2176                 int numOfNewVirtualMethods = 0;
2177                 for (int i=0; i<declared_instance_methods.length; ++i) {
2178                     jq_InstanceMethod m = declared_instance_methods[i];
2179                     Assert._assert(m.getState() == STATE_LOADED);
2180                     if (m.isInitializer()) {
2181                         // initializers cannot override or be overridden
2182                         continue;
2183                     }
2184                     if (super_class != null) {
2185                         jq_InstanceMethod m2 = super_class.getVirtualMethod(m.getNameAndDesc());
2186                         if (m2 != null) {
2187                             if (m.isPrivate() ||
2188                                 m2.isPrivate() || m2.isFinal()) {// should not be overridden
2189                                 System.out.println("error: method "+m+" overrides method "+m2);
2190                             }
2191                             m2.overriddenBy(m);
2192                             if (TRACE) Debug.writeln("Virtual method "+m+" overrides method "+m2+" offset "+Strings.shex(m2.getOffset()));
2193                             m.prepare(m2.getOffset());
2194                             continue;
2195                         }
2196                     }
2197                     if (m.isPrivate()) {
2198                         // private methods cannot override or be overridden
2199                         continue;
2200                     }
2201                     ++numOfNewVirtualMethods;
2202                 }
2203                 int super_virtual_methods;
2204                 if (super_class != null)
2205                     super_virtual_methods = super_class.virtual_methods.length;
2206                 else
2207                     super_virtual_methods = 0;
2208                 int num_virtual_methods = super_virtual_methods + numOfNewVirtualMethods;
2209                 virtual_methods = new jq_InstanceMethod[num_virtual_methods];
2210                 if (super_virtual_methods > 0)
2211                     System.arraycopy(super_class.virtual_methods, 0, this.virtual_methods, 0, super_virtual_methods);
2212                 for (int i=0, j=super_virtual_methods-1; i<declared_instance_methods.length; ++i) {
2213                     jq_InstanceMethod m = declared_instance_methods[i];
2214                     if (m.isInitializer() || m.isPrivate()) {
2215                         // not in vtable
2216                         if (TRACE) Debug.writeln("Skipping "+m+" in virtual method table.");
2217                         m.prepare();
2218                         continue;
2219                     }
2220                     if (m.isOverriding()) {
2221                         if (m.getState() != STATE_PREPARED) {
2222                             Assert.UNREACHABLE("Method "+m+" overrides superclass, but is not prepared");
2223                         }
2224                         int entry = (m.getOffset() >> 2) - 1;
2225                         virtual_methods[entry] = m;
2226                         continue;
2227                     }
2228                     Assert._assert(m.getState() == STATE_LOADED);
2229                     virtual_methods[++j] = m;
2230                     if (TRACE) Debug.writeln("Virtual method "+m+" is new, offset "+Strings.shex((j+1)*CodeAddress.size()));
2231                     m.prepare((j+1)*CodeAddress.size());
2232                 }
2233                 // allocate space for vtable
2234                 vtable = new Address[num_virtual_methods+1];
2235     
2236                 // prepare declared superinterfaces
2237                 for (int i=0; i<declared_interfaces.length; ++i) {
2238                     declared_interfaces[i].prepare();
2239                 }
2240     
2241                 // calculate interfaces
2242                 int n_super_interfaces;
2243                 if (super_class != null) {
2244                     n_super_interfaces = super_class.interfaces.length;
2245                     if (super_class.isInterface())
2246                         ++n_super_interfaces; // add super_class to the list, too.
2247                 } else
2248                     n_super_interfaces = 0;
2249                 for (int i=0; i<declared_interfaces.length; ++i) {
2250                     n_super_interfaces += declared_interfaces[i].interfaces.length;
2251                 }
2252     
2253                 interfaces = new jq_Class[n_super_interfaces + declared_interfaces.length];
2254                 int n = 0;
2255                 if (n_super_interfaces > 0) {
2256                     System.arraycopy(super_class.interfaces, 0, this.interfaces, 0, super_class.interfaces.length);
2257                     n += super_class.interfaces.length;
2258                     if (super_class.isInterface())
2259                         this.interfaces[n++] = super_class;
2260                     for (int i=0; i<declared_interfaces.length; ++i) {
2261                         System.arraycopy(declared_interfaces[i].interfaces, 0, this.interfaces, n, declared_interfaces[i].interfaces.length);
2262                         n += declared_interfaces[i].interfaces.length;
2263                     }
2264                 }
2265                 Assert._assert (n == n_super_interfaces);
2266                 System.arraycopy(declared_interfaces, 0, this.interfaces, n_super_interfaces, declared_interfaces.length);
2267     
2268                 // set up tables for fast type checking.
2269                 this.display = new jq_Type[DISPLAY_SIZE+2];
2270                 if (!this.isInterface()) {
2271                     jq_Reference dps = this.getDirectPrimarySupertype();
2272                     if (dps != null) {
2273                         Assert._assert(dps.isPrepared());
2274                         int num = dps.offset;
2275                         if (num < 2) num = DISPLAY_SIZE+1;
2276                         System.arraycopy(dps.display, 2, this.display, 2, num-1);
2277                         this.offset = num + 1;
2278                         if (this.offset >= DISPLAY_SIZE+2)
2279                             this.offset = 0;
2280                     } else {
2281                         this.offset = 2;
2282                     }
2283                     this.display[this.offset] = this;
2284                 } else {
2285                     this.display[2] = PrimordialClassLoader.getJavaLangObject();
2286                 }
2287                 this.s_s_array = interfaces;
2288                 this.s_s_array_length = interfaces.length;
2289     
2290                 if (TRACE) {
2291                     System.out.println(this+" offset="+this.offset);
2292                     if (this.offset != 0) {
2293                         for (int i=0; i<this.display.length; ++i) {
2294                             System.out.println(this+" display["+i+"] = "+this.display[i]);
2295                         }
2296                     }
2297                     for (int i=0; i<this.s_s_array_length; ++i) {
2298                         System.out.println(this+" s_s_array["+i+"] = "+this.s_s_array[i]);
2299                     }
2300                 }
2301                 
2302                 // set prepared flags for static methods
2303                 for (int i=0; i<static_methods.length; ++i) {
2304                     jq_StaticMethod m = static_methods[i];
2305                     m.prepare();
2306                 }
2307                 // set prepared flags for static fields
2308                 for (int i=0; i<static_fields.length; ++i) {
2309                     jq_StaticField m = static_fields[i];
2310                     m.prepare();
2311                 }
2312                 
2313                 if (TRACE) Debug.writeln("Finished preparing "+this);
2314                 state = STATE_PREPARED;
2315             } catch (Error x) {
2316                 state = STATE_PREPAREERROR;
2317                 throw x;
2318             }
2319         }
2320     }
2321     
2322     public void sf_initialize() {
2323         if (isSFInitialized()) return; // quick test.
2324         synchronized (this) {
2325             if (isSFInitialized()) return;
2326             if (!isPrepared()) prepare();
2327             if (state == STATE_SFINITIALIZING)
2328                 throw new ClassCircularityError(this.toString()); // recursively called sf_initialize (?)
2329             if (state == STATE_SFINITERROR)
2330                 throw new NoClassDefFoundError();
2331             state = STATE_SFINITIALIZING;
2332             try {
2333                 if (TRACE) Debug.writeln("Beginning SF init "+this+"...");
2334                 if (super_class != null) {
2335                     super_class.sf_initialize();
2336                 }
2337                 // lay out static fields and set their constant values
2338                 if (static_data_size > 0) {
2339                     static_data = new int[static_data_size>>2];
2340                     for (int i=0, j=0; i<static_fields.length; ++i) {
2341                         jq_StaticField f = static_fields[i];
2342                         f.sf_initialize(static_data, j << 2);
2343                         if (f.isConstant()) {
2344                             Object cv = f.getConstantValue();
2345                             if (f.getType().isPrimitiveType()) {
2346                                 if (f.getType() == jq_Primitive.LONG) {
2347                                     long l = ((Long)cv).longValue();
2348                                     static_data[j  ] = (int)l;
2349                                     static_data[j+1] = (int)(l >> 32);
2350                                 } else if (f.getType() == jq_Primitive.FLOAT) {
2351                                     static_data[j] = Float.floatToRawIntBits(((Float)cv).floatValue());
2352                                 } else if (f.getType() == jq_Primitive.DOUBLE) {
2353                                     long l = Double.doubleToRawLongBits(((Double)cv).doubleValue());
2354                                     static_data[j  ] = (int)l;
2355                                     static_data[j+1] = (int)(l >> 32);
2356                                 } else {
2357                                     static_data[j] = ((Integer)cv).intValue();
2358                                 }
2359                             } else {
2360                                 // java/lang/String
2361                                 HeapAddress a = HeapAddress.addressOf(cv);
2362                                 if (a != null) {
2363                                     static_data[j] = a.to32BitValue();
2364                                 }
2365                             }
2366                         }
2367                         j += f.getWidth() >> 2;
2368                     }
2369                 }
2370                 if (TRACE) Debug.writeln("Finished SF init "+this);
2371                 state = STATE_SFINITIALIZED;
2372             } catch (Error x) {
2373                 state = STATE_SFINITERROR;
2374                 throw x;
2375             }
2376         }
2377     }
2378     
2379     public void compile() {
2380         if (isCompiled()) return; // quick test.
2381         synchronized (this) {
2382             if (isCompiled()) return;
2383             if (!isSFInitialized()) sf_initialize();
2384             state = STATE_COMPILING;
2385             if (TRACE) Debug.writeln("Beginning compilation "+this+"...");
2386             if (super_class != null) {
2387                 super_class.compile();
2388             }
2389             // generate compile stubs for each declared method
2390             for (int i=0; i<static_methods.length; ++i) {
2391                 jq_StaticMethod m = static_methods[i];
2392                 if (m.getState() == STATE_PREPARED) {
2393                     if (TRACE) Debug.writeln("Compiling stub for: "+m);
2394                     jq_CompiledCode cc = m.compile_stub();
2395                     if (jq.RunningNative) cc.patchDirectBindCalls();
2396                 }
2397             }
2398             for (int i=0; i<declared_instance_methods.length; ++i) {
2399                 jq_InstanceMethod m = declared_instance_methods[i];
2400                 if (m.getState() == STATE_PREPARED) {
2401                     if (TRACE) Debug.writeln("Compiling stub for: "+m);
2402                     jq_CompiledCode cc = m.compile_stub();
2403                     if (jq.RunningNative) cc.patchDirectBindCalls();
2404                 }
2405             }
2406             Address[] vt = (Address[])vtable;
2407             // 0th entry of vtable is class pointer
2408             vt[0] = HeapAddress.addressOf(this);
2409             for (int i=0; i<virtual_methods.length; ++i) {
2410                 vt[i+1] = virtual_methods[i].getDefaultCompiledVersion().getEntrypoint();
2411             }
2412             if (TRACE) Debug.writeln(this+": "+vt[0].stringRep()+" vtable "+HeapAddress.addressOf(vt).stringRep());
2413             if (TRACE) Debug.writeln("Finished compilation "+this);
2414             state = STATE_COMPILED;
2415         }
2416     }
2417     
2418     public void cls_initialize() throws ExceptionInInitializerError, NoClassDefFoundError {
2419         if (isClsInitialized()) return; // quick test.
2420         synchronized (this) {
2421             if (state == STATE_CLSINITERROR) throw new NoClassDefFoundError(this+": clinit failed");
2422             if (state >= STATE_CLSINITIALIZING) return;
2423             if (!isCompiled()) compile();
2424             state = STATE_CLSINITIALIZING;
2425             if (TRACE) Debug.writeln("Beginning class init "+this+"...");
2426             if (super_class != null) {
2427                 super_class.cls_initialize();
2428             }
2429             state = STATE_CLSINITRUNNING;
2430             if (jq.RunningNative)
2431                 invokeclinit();
2432             if (TRACE) Debug.writeln("Finished class init "+this);
2433             state = STATE_CLSINITIALIZED;
2434         }
2435     }
2436 
2437     private void invokeclinit() throws ExceptionInInitializerError {
2438         try {
2439             state = STATE_CLSINITRUNNING;
2440             jq_ClassInitializer clinit = this.getClassInitializer();
2441             if (clinit != null) 
2442                 Reflection.invokestatic_V(clinit);
2443         } catch (Error x) {
2444             state = STATE_CLSINITERROR;
2445             throw x;
2446         } catch (Throwable x) {
2447             state = STATE_CLSINITERROR;
2448             throw new ExceptionInInitializerError(x);
2449         }
2450     }
2451 
2452     public static int NumOfIFieldsKept = 0;
2453     public static int NumOfSFieldsKept = 0;
2454     public static int NumOfIMethodsKept = 0;
2455     public static int NumOfSMethodsKept = 0;
2456     public static int NumOfIFieldsEliminated = 0;
2457     public static int NumOfSFieldsEliminated = 0;
2458     public static int NumOfIMethodsEliminated = 0;
2459     public static int NumOfSMethodsEliminated = 0;
2460     
2461     void readAttributes(DataInput in, Map attribMap) 
2462     throws IOException {
2463         char n_attributes = (char)in.readUnsignedShort();
2464         for (int i=0; i<n_attributes; ++i) {
2465             char attribute_name_index = (char)in.readUnsignedShort();
2466             if (getCPtag(attribute_name_index) != CONSTANT_Utf8)
2467                 throw new ClassFormatError("constant pool entry "+attribute_name_index+", referred to by attribute "+i+
2468                                            ", is wrong type tag (expected="+CONSTANT_Utf8+", actual="+getCPtag(attribute_name_index));
2469             Utf8 attribute_desc = getCPasUtf8(attribute_name_index);
2470             int attribute_length = in.readInt();
2471             // todo: maybe we only want to read in attributes we care about...
2472             byte[] attribute_data = new byte[attribute_length];
2473             in.readFully(attribute_data);
2474             attribMap.put(attribute_desc, attribute_data);
2475         }
2476     }
2477     
2478     public static String className(Utf8 desc) {
2479         String temp = desc.toString();
2480         Assert._assert(temp.startsWith("L"), temp);
2481         Assert._assert(temp.endsWith(";"), temp);
2482         return temp.substring(1, temp.length()-1).replace('/','.');
2483     }
2484 
2485     private void addSubclass(jq_Class subclass) {
2486         jq_Class[] newsubclasses = new jq_Class[subclasses.length+1];
2487         System.arraycopy(subclasses, 0, newsubclasses, 0, subclasses.length);
2488         newsubclasses[subclasses.length] = subclass;
2489         subclasses = newsubclasses;
2490     }
2491     
2492     private void addSubinterface(jq_Class subinterface) {
2493         jq_Class[] newsubinterfaces = new jq_Class[subinterfaces.length+1];
2494         System.arraycopy(subinterfaces, 0, newsubinterfaces, 0, subinterfaces.length);
2495         newsubinterfaces[subinterfaces.length] = subinterface;
2496         subinterfaces = newsubinterfaces;
2497     }
2498     
2499     private void removeSubclass(jq_Class subclass) {
2500         jq_Class[] newsubclasses = new jq_Class[subclasses.length-1];
2501         for (int i=-1, j=0; i<newsubclasses.length-1; ++j) {
2502             if (subclass != subclasses[j]) {
2503                 newsubclasses[++i] = subclasses[j];
2504             }
2505         }
2506         subclasses = newsubclasses;
2507     }
2508     
2509     private void removeSubinterface(jq_Class subinterface) {
2510         jq_Class[] newsubinterfaces = new jq_Class[subinterfaces.length-1];
2511         for (int i=-1, j=0; i<newsubinterfaces.length-1; ++j) {
2512             if (subinterface != subinterfaces[j]) {
2513                 newsubinterfaces[++i] = subinterfaces[j];
2514             }
2515         }
2516         subinterfaces = newsubinterfaces;
2517     }
2518     
2519     public static jq_InstanceMethod getInvokespecialTarget(jq_Class clazz, jq_InstanceMethod method)
2520     throws AbstractMethodError {
2521         clazz.load();
2522         if (!clazz.isSpecial())
2523             return method;
2524         if (method.isInitializer())
2525             return method;
2526         if (TypeCheck.isSuperclassOf(method.getDeclaringClass(), clazz, true) != YES)
2527             return method;
2528         jq_NameAndDesc nd = method.getNameAndDesc();
2529         for (;;) {
2530             clazz = clazz.getSuperclass();
2531             if (clazz == null)
2532                 throw new AbstractMethodError();
2533             clazz.load();
2534             method = clazz.getDeclaredInstanceMethod(nd);
2535             if (method != null)
2536                 return method;
2537         }
2538     }
2539     
2540     public jq_ConstantPool.ConstantPoolRebuilder rebuildConstantPool(boolean addCode) {
2541         jq_ConstantPool.ConstantPoolRebuilder cpr = new jq_ConstantPool.ConstantPoolRebuilder();
2542         cpr.addType(this);
2543         if (this.getSuperclass() != null)
2544             cpr.addType(this.getSuperclass());
2545         for (int i=0; i < declared_interfaces.length; ++i) {
2546             jq_Class f = declared_interfaces[i];
2547             cpr.addType(f);
2548         }
2549         for (int i=0; i < declared_instance_fields.length; ++i) {
2550             jq_InstanceField f = declared_instance_fields[i];
2551             cpr.addUtf8(f.getName());
2552             cpr.addUtf8(f.getDesc());
2553             cpr.addAttributeNames(f);
2554         }
2555         for (int i=0; i < static_fields.length; ++i) {
2556             jq_StaticField f = static_fields[i];
2557             cpr.addUtf8(f.getName());
2558             cpr.addUtf8(f.getDesc());
2559             cpr.addAttributeNames(f);
2560             if (f.isConstant()) {
2561                 Object o = f.getConstantValue();
2562                 if (o instanceof String)
2563                     cpr.addString((String) f.getConstantValue());
2564                 else if (o instanceof Class)
2565                     cpr.addType(Reflection.getJQType((Class)o));
2566                 else
2567                     cpr.addOther(f.getConstantValue());
2568             }
2569         }
2570         for (int i=0; i < declared_instance_methods.length; ++i) {
2571             jq_InstanceMethod f = declared_instance_methods[i];
2572             cpr.addUtf8(f.getName());
2573             cpr.addUtf8(f.getDesc());
2574             cpr.addAttributeNames(f);
2575             if (addCode) cpr.addCode(f);
2576             cpr.addExceptions(f);
2577         }
2578         for (int i=0; i < static_methods.length; ++i) {
2579             jq_StaticMethod f = static_methods[i];
2580             cpr.addUtf8(f.getName());
2581             cpr.addUtf8(f.getDesc());
2582             cpr.addAttributeNames(f);
2583             if (addCode) cpr.addCode(f);
2584             cpr.addExceptions(f);
2585         }
2586         Utf8 sourcefile = getSourceFile();
2587         if (sourcefile != null) {
2588             cpr.addUtf8(sourcefile);
2589         }
2590         // TODO: InnerClasses
2591         for (Iterator i = attributes.entrySet().iterator(); i.hasNext(); ) {
2592             Map.Entry e = (Map.Entry)i.next();
2593             Utf8 name = (Utf8)e.getKey();
2594             cpr.addUtf8(name);
2595         }
2596         
2597         return cpr;
2598     }
2599     
2600     public void dump(DataOutput out) throws IOException {
2601         chkState(STATE_LOADED);
2602         out.writeInt(0xcafebabe);
2603         out.writeChar(minor_version);
2604         out.writeChar(major_version);
2605         
2606         jq_ConstantPool.ConstantPoolRebuilder cpr = rebuildConstantPool(true);
2607         cpr.dump(out);
2608         
2609         out.writeChar(access_flags);
2610         out.writeChar(cpr.get(this));
2611         char sc;
2612         if (super_class == null) sc = 0;
2613         else sc = cpr.get(super_class);
2614         out.writeChar(sc);
2615         
2616         out.writeChar(declared_interfaces.length);
2617         for(int i=0; i < declared_interfaces.length; i++)
2618             out.writeChar(cpr.get(declared_interfaces[i]));
2619         
2620         int nfields = static_fields.length + declared_instance_fields.length;
2621         Assert._assert(nfields <= Character.MAX_VALUE);
2622         out.writeChar(nfields);
2623         for(int i=0; i < static_fields.length; i++) {
2624             static_fields[i].dump(out, cpr);
2625         }
2626         for(int i=0; i < declared_instance_fields.length; i++) {
2627             declared_instance_fields[i].dump(out, cpr);
2628         }
2629         
2630         int nmethods = static_methods.length + declared_instance_methods.length;
2631         out.writeChar(nmethods);
2632         for(int i=0; i < static_methods.length; i++) {
2633             static_methods[i].dump(out, cpr);
2634         }
2635         for(int i=0; i < declared_instance_methods.length; i++) {
2636             declared_instance_methods[i].dump(out, cpr);
2637         }
2638         
2639         int nattributes = attributes.size();
2640         Assert._assert(nattributes <= Character.MAX_VALUE);
2641         out.writeChar(nattributes);
2642         for (Iterator i = attributes.entrySet().iterator(); i.hasNext(); ) {
2643             Map.Entry e = (Map.Entry)i.next();
2644             Utf8 name = (Utf8)e.getKey();
2645             out.writeChar(cpr.get(name));
2646             byte[] value = (byte[])e.getValue();
2647             if (name == Utf8.get("SourceFile")) {
2648                 char oldIndex = Convert.twoBytesToChar(value, 0);
2649                 Utf8 oldValue = (Utf8)const_pool.get(oldIndex);
2650                 Convert.charToTwoBytes(cpr.get(oldValue), value, 0);
2651             } else if (name == Utf8.get("InnerClasses")) {
2652                 // TODO
2653             }
2654             out.writeInt(value.length);
2655             out.write(value);
2656         }
2657     }
2658 
2659     public static final jq_Class _class = (jq_Class)PrimordialClassLoader.loader.getOrCreateBSType("Ljoeq/Class/jq_Class;");
2660 
2661     static interface Delegate {
2662         Object newInstance(jq_Class c, int instance_size, Object vtable);
2663     }
2664 
2665     private static Delegate _delegate;
2666 
2667     static {
2668         /* Set up delegates. */
2669         _delegate = null;
2670         boolean nullVM = jq.nullVM;
2671         if (!nullVM) {
2672             _delegate = attemptDelegate("joeq.Class.Delegates$Klass");
2673         }
2674         if (_delegate == null) {
2675             _delegate = new NullDelegates.Klass();
2676         }
2677     }
2678 
2679     private static Delegate attemptDelegate(String s) {
2680         //String type = "class delegate";
2681         try {
2682             Class c = Class.forName(s);
2683             return (Delegate)c.newInstance();
2684         } catch (java.lang.ClassNotFoundException x) {
2685             //System.err.println("Cannot find "+type+" "+s+": "+x);
2686         } catch (java.lang.InstantiationException x) {
2687             //System.err.println("Cannot instantiate "+type+" "+s+": "+x);
2688         } catch (java.lang.IllegalAccessException x) {
2689             //System.err.println("Cannot access "+type+" "+s+": "+x);
2690         }
2691         return null;
2692     }
2693 
2694     static final HashMap/*<String,StringConstant>*/ stringConstants = new HashMap();
2695     public StringConstant findStringConstant(String s) {
2696         StringConstant sc = (StringConstant)stringConstants.get(s); 
2697         if (sc == null) {
2698             char idx = getCP().findEqual(s, CONSTANT_String);
2699             if (idx != 0)
2700                 stringConstants.put(s, sc = new StringConstant(idx));
2701         }
2702         return sc;
2703     }
2704     public static StringConstant readStringConstant(StringTokenizer st) {
2705         jq_Class clazz = (jq_Class)jq_Type.read(st);
2706         char cpindex = (char)Integer.parseInt(st.nextToken());
2707         clazz.load();
2708         return clazz.findStringConstant(clazz.getCPasString(cpindex));
2709     }
2710 
2711     public class StringConstant {
2712         char cpindex;
2713         StringConstant(char cpindex) {
2714             this.cpindex = cpindex;
2715         }
2716         public void write(Textualizer t) throws IOException {
2717             jq_Class.this.write(t);
2718             t.writeString(" "+(int)cpindex);
2719         }
2720         public String getString() {
2721             load();
2722             return getCPasString(cpindex);
2723         }
2724         public String toString() {
2725             return "StringConstant[class="+jq_Class.this+",idx="+(int)cpindex+"]";
2726         }
2727     }
2728 }