Fixing a few bugs in the statistics printout.
[jpf-core.git] / src / main / gov / nasa / jpf / jvm / JVMClassInfo.java
1 /*
2  * Copyright (C) 2014, United States Government, as represented by the
3  * Administrator of the National Aeronautics and Space Administration.
4  * All rights reserved.
5  *
6  * The Java Pathfinder core (jpf-core) platform is licensed under the
7  * Apache License, Version 2.0 (the "License"); you may not use this file except
8  * in compliance with the License. You may obtain a copy of the License at
9  * 
10  *        http://www.apache.org/licenses/LICENSE-2.0. 
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and 
16  * limitations under the License.
17  */
18
19 package gov.nasa.jpf.jvm;
20
21 import java.lang.reflect.Modifier;
22 import java.util.HashMap;
23 import java.util.LinkedHashMap;
24 import java.util.LinkedList;
25
26 import gov.nasa.jpf.Config;
27 import gov.nasa.jpf.util.Misc;
28 import gov.nasa.jpf.util.StringSetMatcher;
29 import gov.nasa.jpf.vm.AbstractTypeAnnotationInfo;
30 import gov.nasa.jpf.vm.AnnotationInfo;
31 import gov.nasa.jpf.vm.BootstrapMethodInfo;
32 import gov.nasa.jpf.vm.BytecodeAnnotationInfo;
33 import gov.nasa.jpf.vm.BytecodeTypeParameterAnnotationInfo;
34 import gov.nasa.jpf.vm.ClassInfo;
35 import gov.nasa.jpf.vm.ClassInfoException;
36 import gov.nasa.jpf.vm.ClassLoaderInfo;
37 import gov.nasa.jpf.vm.ClassParseException;
38 import gov.nasa.jpf.vm.DirectCallStackFrame;
39 import gov.nasa.jpf.vm.ExceptionHandler;
40 import gov.nasa.jpf.vm.ExceptionParameterAnnotationInfo;
41 import gov.nasa.jpf.vm.FieldInfo;
42 import gov.nasa.jpf.vm.FormalParameterAnnotationInfo;
43 import gov.nasa.jpf.vm.GenericSignatureHolder;
44 import gov.nasa.jpf.vm.InfoObject;
45 import gov.nasa.jpf.vm.LocalVarInfo;
46 import gov.nasa.jpf.vm.MethodInfo;
47 import gov.nasa.jpf.vm.NativeMethodInfo;
48 import gov.nasa.jpf.vm.StackFrame;
49 import gov.nasa.jpf.vm.SuperTypeAnnotationInfo;
50 import gov.nasa.jpf.vm.ThreadInfo;
51 import gov.nasa.jpf.vm.ThrowsAnnotationInfo;
52 import gov.nasa.jpf.vm.TypeAnnotationInfo;
53 import gov.nasa.jpf.vm.TypeParameterAnnotationInfo;
54 import gov.nasa.jpf.vm.TypeParameterBoundAnnotationInfo;
55 import gov.nasa.jpf.vm.Types;
56 import gov.nasa.jpf.vm.VariableAnnotationInfo;
57
58 /**
59  * a ClassInfo that was created from a Java classfile
60  */
61 public class JVMClassInfo extends ClassInfo {
62
63   /**
64    * this is the inner class that does the actual ClassInfo initialization from ClassFile. It is an inner class so that
65    * (a) it can set ClassInfo fields, (b) it can extend ClassFileReaderAdapter, and (c) we don't clutter JVMClassInfo with
66    * fields that are only temporarily used during parsing
67    */
68   class Initializer extends ClassFileReaderAdapter {
69     protected ClassFile cf;
70     protected JVMCodeBuilder cb;
71
72     public Initializer (ClassFile cf, JVMCodeBuilder cb) throws ClassParseException {
73       this.cf = cf;
74       this.cb = cb;
75       
76       cf.parse(this);
77     }
78
79     @Override
80     public void setClass (ClassFile cf, String clsName, String superClsName, int flags, int cpCount) throws ClassParseException {
81       JVMClassInfo.this.setClass(clsName, superClsName, flags, cpCount);
82     }
83
84     @Override
85     public void setClassAttribute (ClassFile cf, int attrIndex, String name, int attrLength) {
86       if (name == ClassFile.SOURCE_FILE_ATTR) {
87         cf.parseSourceFileAttr(this, null);
88
89       } else if (name == ClassFile.SIGNATURE_ATTR) {
90         cf.parseSignatureAttr(this, JVMClassInfo.this);
91
92       } else if (name == ClassFile.RUNTIME_VISIBLE_ANNOTATIONS_ATTR) {
93         cf.parseAnnotationsAttr(this, JVMClassInfo.this);
94
95       } else if (name == ClassFile.RUNTIME_INVISIBLE_ANNOTATIONS_ATTR) {
96         //cf.parseAnnotationsAttr(this, ClassInfo.this);
97         
98       } else if (name == ClassFile.RUNTIME_VISIBLE_TYPE_ANNOTATIONS_ATTR) {
99         cf.parseTypeAnnotationsAttr(this, JVMClassInfo.this);
100         
101       } else if (name == ClassFile.INNER_CLASSES_ATTR) {
102         cf.parseInnerClassesAttr(this, JVMClassInfo.this);
103
104       } else if (name == ClassFile.ENCLOSING_METHOD_ATTR) {
105         cf.parseEnclosingMethodAttr(this, JVMClassInfo.this);
106         
107       } else if (name == ClassFile.BOOTSTRAP_METHOD_ATTR) {
108         cf.parseBootstrapMethodAttr(this, JVMClassInfo.this);
109         
110       }
111     }
112     
113     @Override
114     public void setBootstrapMethodCount (ClassFile cf, Object tag, int count) {
115       bootstrapMethods = new BootstrapMethodInfo[count];
116     }
117     
118     @Override
119     public void setBootstrapMethod (ClassFile cf, Object tag, int idx, int refKind, String cls, String mth, String descriptor, int[] cpArgs) {
120
121       // TODO: Fix for Groovy's model-checking
122       // TODO: Probably a bug here since cpArgs could be of length 1 sometimes!
123       int lambdaRefKind = cf.mhRefTypeAt(cpArgs[1]);
124
125       int mrefIdx = cf.mhMethodRefIndexAt(cpArgs[1]);
126       String clsName = cf.methodClassNameAt(mrefIdx).replace('/', '.');
127       ClassInfo eclosingLambdaCls;
128       
129       if(!clsName.equals(JVMClassInfo.this.getName())) {
130         eclosingLambdaCls = ClassLoaderInfo.getCurrentResolvedClassInfo(clsName);
131       } else {
132         eclosingLambdaCls = JVMClassInfo.this;
133       }
134       
135       assert (eclosingLambdaCls!=null);
136       
137       String mthName = cf.methodNameAt(mrefIdx);
138       String signature = cf.methodDescriptorAt(mrefIdx);
139       
140       MethodInfo lambdaBody = eclosingLambdaCls.getMethod(mthName + signature, false);
141       
142       String samDescriptor = cf.methodTypeDescriptorAt(cpArgs[2]);
143             
144       if(lambdaBody!=null) {
145         bootstrapMethods[idx] = new BootstrapMethodInfo(lambdaRefKind, JVMClassInfo.this, lambdaBody, samDescriptor);
146       }
147     }
148     
149    //--- inner/enclosing classes 
150     @Override
151     public void setInnerClassCount (ClassFile cf, Object tag, int classCount) {
152       innerClassNames = new String[classCount];
153     }
154
155     @Override
156     public void setInnerClass (ClassFile cf, Object tag, int innerClsIndex,
157             String outerName, String innerName, String innerSimpleName, int accessFlags) {
158       // Ok, this is a total mess - some names are in dot notation, others use '/'
159       // and to make it even more confusing, some InnerClass attributes refer NOT
160       // to the currently parsed class, so we have to check if we are the outerName,
161       // but then 'outerName' can also be null instead of our own name.
162       // Oh, and there are also InnerClass attributes that have their own name as inner names
163       // (see java/lang/String$CaseInsensitiveComparator or ...System and java/lang/System$1 for instance)
164       if (outerName != null) {
165         outerName = Types.getClassNameFromTypeName(outerName);
166       }
167
168       innerName = Types.getClassNameFromTypeName(innerName);
169       if (!innerName.equals(name)) {
170         innerClassNames[innerClsIndex] = innerName;
171
172       } else {
173         // this refers to ourself, and can be a force fight with setEnclosingMethod
174         if (outerName != null) { // only set if this is a direct member, otherwise taken from setEnclosingMethod
175           setEnclosingClass(outerName);
176         }
177       }
178     }
179
180     @Override
181     public void setEnclosingMethod (ClassFile cf, Object tag, String enclosingClassName, String enclosingMethodName, String descriptor) {
182       setEnclosingClass(enclosingClassName);
183
184       if (enclosingMethodName != null) {
185         JVMClassInfo.this.setEnclosingMethod(enclosingMethodName + descriptor);
186       }
187     }
188
189     @Override
190     public void setInnerClassesDone (ClassFile cf, Object tag) {
191       // we have to check if we allocated too many - see the mess above
192       for (int i = 0; i < innerClassNames.length; i++) {
193         innerClassNames = Misc.stripNullElements(innerClassNames);
194       }
195     }
196
197     //--- source file
198     @Override
199     public void setSourceFile (ClassFile cf, Object tag, String fileName) {
200       JVMClassInfo.this.setSourceFile(fileName);
201     }
202     
203     //--- interfaces
204     @Override
205     public void setInterfaceCount (ClassFile cf, int ifcCount) {
206       interfaceNames = new String[ifcCount];
207     }
208
209     @Override
210     public void setInterface (ClassFile cf, int ifcIndex, String ifcName) {
211       interfaceNames[ifcIndex] = Types.getClassNameFromTypeName(ifcName);
212     }
213
214     //--- fields
215     // unfortunately they are stored together in the ClassFile, i.e. we 
216     // have to split them up once we are done
217     
218     protected FieldInfo[] fields;
219     protected FieldInfo curFi; // need to cache for attributes
220
221     @Override
222     public void setFieldCount (ClassFile cf, int fieldCount) {
223       if (fieldCount > 0){
224         fields = new FieldInfo[fieldCount];
225       } else {
226         fields = null;
227       }
228     }
229
230     @Override
231     public void setField (ClassFile cf, int fieldIndex, int accessFlags, String name, String descriptor) {
232       FieldInfo fi = FieldInfo.create(name, descriptor, accessFlags);
233       fields[fieldIndex] = fi;
234       curFi = fi; // for attributes
235     }
236
237     @Override
238     public void setFieldAttribute (ClassFile cf, int fieldIndex, int attrIndex, String name, int attrLength) {
239       if (name == ClassFile.SIGNATURE_ATTR) {
240         cf.parseSignatureAttr(this, curFi);
241
242       } else if (name == ClassFile.CONST_VALUE_ATTR) {
243         cf.parseConstValueAttr(this, curFi);
244
245       } else if (name == ClassFile.RUNTIME_VISIBLE_ANNOTATIONS_ATTR) {
246         cf.parseAnnotationsAttr(this, curFi);
247
248       } else if (name == ClassFile.RUNTIME_VISIBLE_TYPE_ANNOTATIONS_ATTR) {
249         cf.parseTypeAnnotationsAttr(this, curFi);
250         
251       } else if (name == ClassFile.RUNTIME_INVISIBLE_ANNOTATIONS_ATTR) {
252         //cf.parseAnnotationsAttr(this, curFi);
253       }
254     }
255
256     @Override
257     public void setConstantValue (ClassFile cf, Object tag, Object constVal) {
258       curFi.setConstantValue(constVal);
259     }
260
261     @Override
262     public void setFieldsDone (ClassFile cf) {
263       setFields(fields);
264     }
265  
266   //--- declaredMethods
267     protected MethodInfo curMi;
268
269     @Override
270     public void setMethodCount (ClassFile cf, int methodCount) {
271       methods = new LinkedHashMap<String, MethodInfo>();
272     }
273
274     @Override
275     public void setMethod (ClassFile cf, int methodIndex, int accessFlags, String name, String signature) {
276       MethodInfo mi = MethodInfo.create(name, signature, accessFlags);
277       curMi = mi;
278     }
279     
280     @Override
281     public void setMethodDone (ClassFile cf, int methodIndex){
282       curMi.setLocalVarAnnotations();
283
284       JVMClassInfo.this.setMethod(curMi);
285     }
286
287     @Override
288     public void setMethodAttribute (ClassFile cf, int methodIndex, int attrIndex, String name, int attrLength) {
289       if (name == ClassFile.CODE_ATTR) {
290         cf.parseCodeAttr(this, curMi);
291
292       } else if (name == ClassFile.SIGNATURE_ATTR) {
293         cf.parseSignatureAttr(this, curMi);
294
295       } else if (name == ClassFile.EXCEPTIONS_ATTR) {
296         cf.parseExceptionAttr(this, curMi);
297
298       } else if (name == ClassFile.RUNTIME_VISIBLE_ANNOTATIONS_ATTR) {
299         cf.parseAnnotationsAttr(this, curMi);
300
301       } else if (name == ClassFile.RUNTIME_INVISIBLE_ANNOTATIONS_ATTR) {
302         //cf.parseAnnotationsAttr(this, curMi);
303       } else if (name == ClassFile.RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS_ATTR) {
304         cf.parseParameterAnnotationsAttr(this, curMi);
305
306       } else if (name == ClassFile.RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS_ATTR) {
307         //cf.parseParameterAnnotationsAttr(this, curMi);
308         
309       } else if (name == ClassFile.RUNTIME_VISIBLE_TYPE_ANNOTATIONS_ATTR) {
310         cf.parseTypeAnnotationsAttr(this, curMi);
311       }      
312       
313     }
314
315     //--- current methods throws list
316     protected String[] exceptions;
317
318     @Override
319     public void setExceptionCount (ClassFile cf, Object tag, int exceptionCount) {
320       exceptions = new String[exceptionCount];
321     }
322
323     @Override
324     public void setException (ClassFile cf, Object tag, int exceptionIndex, String exceptionType) {
325       exceptions[exceptionIndex] = Types.getClassNameFromTypeName(exceptionType);
326     }
327
328     @Override
329     public void setExceptionsDone (ClassFile cf, Object tag) {
330       curMi.setThrownExceptions(exceptions);
331     }
332
333     //--- current method exception handlers
334     protected ExceptionHandler[] handlers;
335
336     @Override
337     public void setExceptionHandlerTableCount (ClassFile cf, Object tag, int exceptionTableCount) {
338       handlers = new ExceptionHandler[exceptionTableCount];
339     }
340
341     @Override
342     public void setExceptionHandler (ClassFile cf, Object tag, int handlerIndex,
343             int startPc, int endPc, int handlerPc, String catchType) {
344       ExceptionHandler xh = new ExceptionHandler(catchType, startPc, endPc, handlerPc);
345       handlers[handlerIndex] = xh;
346     }
347
348     @Override
349     public void setExceptionHandlerTableDone (ClassFile cf, Object tag) {
350       curMi.setExceptionHandlers(handlers);
351     }
352
353     //--- current method code  
354     @Override
355     public void setCode (ClassFile cf, Object tag, int maxStack, int maxLocals, int codeLength) {
356       curMi.setMaxLocals(maxLocals);
357       curMi.setMaxStack(maxStack);
358
359       cb.reset(cf, curMi);
360
361       cf.parseBytecode(cb, tag, codeLength);
362       cb.installCode();
363     }
364
365     @Override
366     public void setCodeAttribute (ClassFile cf, Object tag, int attrIndex, String name, int attrLength) {
367       if (name == ClassFile.LINE_NUMBER_TABLE_ATTR) {
368         cf.parseLineNumberTableAttr(this, tag);
369
370       } else if (name == ClassFile.LOCAL_VAR_TABLE_ATTR) {
371         cf.parseLocalVarTableAttr(this, tag);
372         
373       } else if (name == ClassFile.RUNTIME_VISIBLE_TYPE_ANNOTATIONS_ATTR){
374         cf.parseTypeAnnotationsAttr(this, tag);
375       }
376     }
377
378     //--- current method line numbers
379     protected int[] lines, startPcs;
380
381     @Override
382     public void setLineNumberTableCount (ClassFile cf, Object tag, int lineNumberCount) {
383       lines = new int[lineNumberCount];
384       startPcs = new int[lineNumberCount];
385     }
386
387     @Override
388     public void setLineNumber (ClassFile cf, Object tag, int lineIndex, int lineNumber, int startPc) {
389       lines[lineIndex] = lineNumber;
390       startPcs[lineIndex] = startPc;
391     }
392
393     @Override
394     public void setLineNumberTableDone (ClassFile cf, Object tag) {
395       curMi.setLineNumbers(lines, startPcs);
396     }
397     
398     //--- current method local variables
399     protected LocalVarInfo[] localVars;
400
401     @Override
402     public void setLocalVarTableCount (ClassFile cf, Object tag, int localVarCount) {
403       localVars = new LocalVarInfo[localVarCount];
404     }
405
406     @Override
407     public void setLocalVar (ClassFile cf, Object tag, int localVarIndex,
408             String varName, String descriptor, int scopeStartPc, int scopeEndPc, int slotIndex) {
409       LocalVarInfo lvi = new LocalVarInfo(varName, descriptor, "", scopeStartPc, scopeEndPc, slotIndex);
410       localVars[localVarIndex] = lvi;
411     }
412
413     @Override
414     public void setLocalVarTableDone (ClassFile cf, Object tag) {
415       curMi.setLocalVarTable(localVars);
416     }
417     
418     //--- annotations
419     protected AnnotationInfo[] annotations;
420     protected AnnotationInfo curAi;
421     protected LinkedList<AnnotationInfo> annotationStack;
422     protected LinkedList<Object[]> valuesStack;
423     protected AnnotationInfo[][] parameterAnnotations;
424     protected Object[] values;
425     // true if we need to filter null annotations
426     private boolean compactAnnotationArray = false;
427
428     //--- declaration annotations
429     
430     @Override
431     public void setAnnotationCount (ClassFile cf, Object tag, int annotationCount) {
432       annotations = new AnnotationInfo[annotationCount];
433     }
434
435     @Override
436     public void setAnnotation (ClassFile cf, Object tag, int annotationIndex, String annotationType) {
437       if (tag instanceof InfoObject) {
438         if(annotationIndex == -1) {
439           if(annotationStack == null) {
440             assert valuesStack == null;
441             valuesStack = new LinkedList<>();
442             annotationStack = new LinkedList<>();
443           }
444           annotationStack.addFirst(curAi);
445           valuesStack.addFirst(values);
446         }
447         try { 
448           curAi = getResolvedAnnotationInfo(Types.getClassNameFromTypeName(annotationType));
449           if(annotationIndex != -1) {
450             annotations[annotationIndex] = curAi;
451           }
452         } catch(ClassInfoException cie) {
453           // if we can't parse a field, we're sunk, throw and tank the reflective call
454           if(annotationIndex == -1) {
455             throw cie;
456           }
457           compactAnnotationArray = true;
458           annotations[annotationIndex] = null;
459           // skip this annotation
460           throw new SkipAnnotation();
461         }
462       }
463     }
464     
465     @Override
466     public void setAnnotationsDone (ClassFile cf, Object tag) {
467       if (tag instanceof InfoObject) {
468         AnnotationInfo[] toSet;
469         if(compactAnnotationArray) {
470           int nAnnot = 0;
471           for(AnnotationInfo ai : annotations) {
472             if(ai != null) {
473               nAnnot++;
474             }
475           }
476           toSet = new AnnotationInfo[nAnnot];
477           int idx = 0;
478           for(AnnotationInfo ai : annotations) {
479             if(ai != null) {
480               toSet[idx++] = ai;
481             }
482           }
483         } else {
484           toSet = annotations;
485         }
486         ((InfoObject) tag).addAnnotations(toSet);
487       }
488       compactAnnotationArray = false;
489     }
490
491     @Override
492     public void setParameterCount (ClassFile cf, Object tag, int parameterCount) {
493       parameterAnnotations = new AnnotationInfo[parameterCount][];
494     }
495
496     @Override
497     public void setParameterAnnotationCount (ClassFile cf, Object tag, int paramIndex, int annotationCount) {
498       annotations = new AnnotationInfo[annotationCount];
499       parameterAnnotations[paramIndex] = annotations;
500     }
501
502     @Override
503     public void setParameterAnnotation (ClassFile cf, Object tag, int annotationIndex, String annotationType) {
504       try {
505         curAi = getResolvedAnnotationInfo(Types.getClassNameFromTypeName(annotationType));
506         annotations[annotationIndex] = curAi;
507       } catch(ClassInfoException cie) {
508         compactAnnotationArray = true;
509         annotations[annotationIndex] = null;
510         throw new SkipAnnotation();
511       }
512     }
513
514     @Override
515     public void setParametersDone (ClassFile cf, Object tag) {
516       curMi.setParameterAnnotations(parameterAnnotations);
517     }
518     
519     //--- Java 8 type annotations    
520     
521     @Override
522     public void setTypeAnnotationCount(ClassFile cf, Object tag, int annotationCount){
523       annotations = new AnnotationInfo[annotationCount];
524     }
525
526     @Override
527     public void setTypeParameterAnnotation(ClassFile cf, Object tag, int annotationIndex, int targetType, 
528                                            int typeIndex, short[] typePath, String annotationType){
529       AnnotationInfo base = getResolvedAnnotationInfo(Types.getClassNameFromTypeName(annotationType));
530       curAi = new TypeParameterAnnotationInfo(base, targetType, typePath, typeIndex);
531       annotations[annotationIndex] = curAi;
532     }
533     @Override
534     public void setSuperTypeAnnotation(ClassFile cf, Object tag, int annotationIndex, int targetType, 
535                                        int superTypeIdx, short[] typePath, String annotationType){
536       AnnotationInfo base = getResolvedAnnotationInfo(Types.getClassNameFromTypeName(annotationType));
537       curAi = new SuperTypeAnnotationInfo(base, targetType, typePath, superTypeIdx);
538       annotations[annotationIndex] = curAi;
539     }
540     @Override
541     public void setTypeParameterBoundAnnotation(ClassFile cf, Object tag, int annotationIndex, int targetType,
542                                        int typeIndex, int boundIndex, short[] typePath, String annotationType){
543       AnnotationInfo base = getResolvedAnnotationInfo(Types.getClassNameFromTypeName(annotationType));
544       curAi = new TypeParameterBoundAnnotationInfo(base, targetType, typePath, typeIndex, boundIndex);
545       annotations[annotationIndex] = curAi;
546     }
547     @Override
548     public void setTypeAnnotation(ClassFile cf, Object tag, int annotationIndex, int targetType,
549                                   short[] typePath, String annotationType){
550       AnnotationInfo base = getResolvedAnnotationInfo(Types.getClassNameFromTypeName(annotationType));
551       curAi = new TypeAnnotationInfo(base, targetType, typePath);
552       annotations[annotationIndex] = curAi;
553     }
554     @Override
555     public void setFormalParameterAnnotation(ClassFile cf, Object tag, int annotationIndex, int targetType, 
556                                              int paramIndex, short[] typePath, String annotationType){
557       AnnotationInfo base = getResolvedAnnotationInfo(Types.getClassNameFromTypeName(annotationType));
558       curAi = new FormalParameterAnnotationInfo(base, targetType, typePath, paramIndex);
559       annotations[annotationIndex] = curAi;
560     }
561     @Override
562     public void setThrowsAnnotation(ClassFile cf, Object tag, int annotationIndex, int targetType, 
563                                     int throwsTypeIdx, short[] typePath, String annotationType){
564       AnnotationInfo base = getResolvedAnnotationInfo(Types.getClassNameFromTypeName(annotationType));
565       curAi = new ThrowsAnnotationInfo(base, targetType, typePath, throwsTypeIdx);
566       annotations[annotationIndex] = curAi;
567     }
568     @Override
569     public void setVariableAnnotation(ClassFile cf, Object tag, int annotationIndex, int targetType, 
570                                       long[] scopeEntries, short[] typePath, String annotationType){
571       AnnotationInfo base = getResolvedAnnotationInfo(Types.getClassNameFromTypeName(annotationType));
572       VariableAnnotationInfo vai = new VariableAnnotationInfo(base, targetType, typePath, scopeEntries);
573       curAi = vai;
574       annotations[annotationIndex] = curAi;
575     }
576     @Override
577     public void setExceptionParameterAnnotation(ClassFile cf, Object tag, int annotationIndex, int targetType, 
578                                                 int exceptionIndex, short[] typePath, String annotationType){
579       AnnotationInfo base = getResolvedAnnotationInfo(Types.getClassNameFromTypeName(annotationType));
580       curAi= new ExceptionParameterAnnotationInfo(base, targetType, typePath, exceptionIndex);
581       annotations[annotationIndex] = curAi;
582     }
583     @Override
584     public void setBytecodeAnnotation(ClassFile cf, Object tag, int annotationIndex, int targetType, 
585                                       int offset, short[] typePath, String annotationType){
586       AnnotationInfo base = getResolvedAnnotationInfo(Types.getClassNameFromTypeName(annotationType));
587       curAi = new BytecodeAnnotationInfo(base, targetType, typePath, offset);
588       annotations[annotationIndex] = curAi;
589     }
590     @Override
591     public void setBytecodeTypeParameterAnnotation(ClassFile cf, Object tag, int annotationIndex, int targetType, 
592                                              int offset, int typeArgIdx, short[] typePath, String annotationType){
593       AnnotationInfo base = getResolvedAnnotationInfo(Types.getClassNameFromTypeName(annotationType));
594       curAi = new BytecodeTypeParameterAnnotationInfo(base, targetType, typePath, offset, typeArgIdx);
595       annotations[annotationIndex] = curAi;
596     }
597
598     @Override
599     public void setTypeAnnotationsDone(ClassFile cf, Object tag) {
600       if (tag instanceof InfoObject) {
601         int len = annotations.length;
602         AbstractTypeAnnotationInfo[] tais = new AbstractTypeAnnotationInfo[annotations.length];
603         for (int i=0; i<len; i++){
604           tais[i] = (AbstractTypeAnnotationInfo)annotations[i];
605         }
606         
607         // we can get them in batches (e.g. VariableTypeAnnos from code attrs and ReturnTypeAnnos from method attrs
608         ((InfoObject) tag).addTypeAnnotations( tais);
609       }
610     }
611
612     //--- AnnotationInfo values entries
613     @Override
614     public void setAnnotationValueCount (ClassFile cf, Object tag, int annotationIndex, int nValuePairs) {
615       // if we have values, we need to clone the defined annotation so that we can overwrite entries
616       curAi = curAi.cloneForOverriddenValues();
617       if(annotationIndex != -1) {
618         annotations[annotationIndex] = curAi;
619       }
620     }
621     
622     @Override
623     public void setPrimitiveAnnotationValue (ClassFile cf, Object tag, int annotationIndex, int valueIndex,
624             String elementName, int arrayIndex, Object val) {
625       if (arrayIndex >= 0) {
626         values[arrayIndex] = val;
627       } else {
628         curAi.setClonedEntryValue(elementName, val);
629       }
630     }
631     
632     @Override
633     public void setAnnotationFieldValue(ClassFile cf, Object tag, int annotationIndex, int valueIndex, String elementName, int arrayIndex) {
634       assert annotationStack.size() > 0;
635       AnnotationInfo ai = curAi;
636       values = valuesStack.pop();
637       curAi = annotationStack.pop();
638       if(arrayIndex >= 0) {
639         values[arrayIndex] = ai;
640       } else {
641         curAi.setClonedEntryValue(elementName, ai);
642       }
643     }
644
645     @Override
646     public void setStringAnnotationValue (ClassFile cf, Object tag, int annotationIndex, int valueIndex,
647             String elementName, int arrayIndex, String val) {
648       if (arrayIndex >= 0) {
649         values[arrayIndex] = val;
650       } else {
651         curAi.setClonedEntryValue(elementName, val);
652       }
653     }
654
655     @Override
656     public void setClassAnnotationValue (ClassFile cf, Object tag, int annotationIndex, int valueIndex, String elementName,
657             int arrayIndex, String typeName) {
658       Object val = AnnotationInfo.getClassValue(typeName);
659       if (arrayIndex >= 0) {
660         values[arrayIndex] = val;
661       } else {
662         curAi.setClonedEntryValue(elementName, val);
663       }
664     }
665
666     @Override
667     public void setEnumAnnotationValue (ClassFile cf, Object tag, int annotationIndex, int valueIndex,
668             String elementName, int arrayIndex, String enumType, String enumValue) {
669       Object val = AnnotationInfo.getEnumValue(enumType, enumValue);
670       if (arrayIndex >= 0) {
671         values[arrayIndex] = val;
672       } else {
673         curAi.setClonedEntryValue(elementName, val);
674       }
675     }
676
677     @Override
678     public void setAnnotationValueElementCount (ClassFile cf, Object tag, int annotationIndex, int valueIndex,
679             String elementName, int elementCount) {
680       values = new Object[elementCount];
681     }
682
683     @Override
684     public void setAnnotationValueElementsDone (ClassFile cf, Object tag, int annotationIndex, int valueIndex, String elementName) {
685       curAi.setClonedEntryValue(elementName, values);
686     }
687
688     //--- common attrs
689     @Override
690     public void setSignature (ClassFile cf, Object tag, String signature) {
691       if (tag instanceof GenericSignatureHolder) {
692         ((GenericSignatureHolder) tag).setGenericSignature(signature);
693       }
694     }
695   }
696
697   // since nested class init locking can explode the state space, we make it optional and controllable
698   protected static boolean nestedInit;
699   protected static StringSetMatcher includeNestedInit;
700   protected static StringSetMatcher excludeNestedInit;
701
702   protected static boolean init (Config config){
703     nestedInit = config.getBoolean("jvm.nested_init", false);
704     if (nestedInit){
705       includeNestedInit =  StringSetMatcher.getNonEmpty(config.getStringArray("jvm.nested_init.include"));
706       excludeNestedInit = StringSetMatcher.getNonEmpty(config.getStringArray("jvm.nested_init.exclude"));
707     }
708
709     return true;
710   }
711
712   JVMClassInfo (String name, ClassLoaderInfo cli, ClassFile cf, String srcUrl, JVMCodeBuilder cb) throws ClassParseException {
713     super( name, cli, srcUrl);
714     
715     new Initializer( cf, cb); // we just need the ctor
716     
717     resolveAndLink();
718   }
719   
720   
721   //--- for annotation classinfos
722   
723   // called on the annotation classinfo
724   @Override
725   protected ClassInfo createAnnotationProxy (String proxyName){
726     return new JVMClassInfo (this, proxyName, classLoader, null);
727   }
728   
729   // concrete proxy ctor
730   protected JVMClassInfo (ClassInfo ciAnnotation, String proxyName, ClassLoaderInfo cli, String url) {
731     super( ciAnnotation, proxyName, cli, url);
732   }
733
734   /**
735    * This is called on the functional interface type. It creates a synthetic type which 
736    * implements the functional interface and contains a method capturing the behavior 
737    * of the lambda expression.
738    */
739   @Override
740   protected ClassInfo createFuncObjClassInfo (BootstrapMethodInfo bootstrapMethod, String name, String samUniqueName, String[] fieldTypesName) {
741     return new JVMClassInfo(this, bootstrapMethod, name, samUniqueName, fieldTypesName);
742   }
743   
744   protected JVMClassInfo (ClassInfo funcInterface, BootstrapMethodInfo bootstrapMethod, String name, String samUniqueName, String[] fieldTypesName) {
745     super(funcInterface, bootstrapMethod, name, fieldTypesName);
746     
747     // creating a method corresponding to the single abstract method of the functional interface
748     methods = new HashMap<String, MethodInfo>();
749     
750     MethodInfo fiMethod = funcInterface.getInterfaceAbstractMethod();
751     int modifiers = fiMethod.getModifiers() & (~Modifier.ABSTRACT);
752     int nLocals = fiMethod.getArgumentsSize();
753     int nOperands = this.nInstanceFields + nLocals;
754
755     MethodInfo mi = new MethodInfo(fiMethod.getName(), fiMethod.getSignature(), modifiers, nLocals, nOperands);
756     mi.linkToClass(this);
757     
758     methods.put(mi.getUniqueName(), mi);
759     
760     setLambdaDirectCallCode(mi, bootstrapMethod);
761     
762     try {
763       resolveAndLink();
764     } catch (ClassParseException e) {
765       // we do not even get here - this a synthetic class, and at this point
766       // the interfaces are already loaded.
767     }
768   }
769
770   /**
771    * perform initialization of this class and its not-yet-initialized superclasses (top down),
772    * which includes calling clinit() methods
773    *
774    * This is overridden here to model a questionable yet consequential behavior of hotspot, which
775    * is holding derived class locks when initializing base classes. The generic implementation in
776    * ClassInfo uses non-nested locks (i.e. A.clinit() only synchronizes on A.class) and hence cannot
777    * produce the same static init deadlocks as hotspot. In order to catch such defects we implement
778    * nested locking here.
779    *
780    * The main difference is that the generic implementation only pushes DCSFs for required clinits
781    * and otherwise doesn't lock anything. Here, we create one static init specific DCSF which wraps
782    * all clinits in nested monitorenter/exits. We create this even if there is no clinit so that we
783    * mimic hotspot locking.
784    *
785    * Note this scheme also enables us to get rid of the automatic clinit sync (they don't have
786    * a 0x20 sync modifier in classfiles)
787    *
788    * @return true if client needs to re-execute because we pushed DirectCallStackFrames
789    */
790   @Override
791   public boolean initializeClass(ThreadInfo ti) {
792     if (needsInitialization(ti)) {
793       if (nestedInit && StringSetMatcher.isMatch(name, includeNestedInit, excludeNestedInit)) {
794         registerClass(ti); // this is recursively upwards
795         int nOps = 2 * (getNumberOfSuperClasses() + 1); // this is just an upper bound for the number of operands we need
796
797         MethodInfo miInitialize = new MethodInfo("[initializeClass]", "()V", Modifier.STATIC, 0, nOps);
798         JVMDirectCallStackFrame frame = new JVMDirectCallStackFrame(miInitialize, null);
799         JVMCodeBuilder cb = getSystemCodeBuilder(null, miInitialize);
800
801         addClassInit(ti, frame, cb); // this is recursively upwards until we hit a initialized superclass
802         cb.directcallreturn();
803         cb.installCode();
804
805         // this is normally initialized in the ctor, but at that point we don't have the code yet
806         frame.setPC(miInitialize.getFirstInsn());
807
808         ti.pushFrame(frame);
809         return true; // client has to re-execute, we pushed a stackframe
810
811
812       } else { // use generic initialization without nested locks (directly calling clinits)
813         return super.initializeClass(ti);
814       }
815
816     } else {
817       return false; // nothing to do
818     }
819   }
820
821   protected void addClassInit (ThreadInfo ti, JVMDirectCallStackFrame frame, JVMCodeBuilder cb){
822     int clsObjRef = getClassObjectRef();
823
824     frame.pushRef(clsObjRef);
825     cb.monitorenter();
826
827     if (superClass != null && superClass.needsInitialization(ti)) {
828       ((JVMClassInfo) superClass).addClassInit(ti, frame, cb);      // go recursive
829     }
830
831     if (getMethod("<clinit>()V", false) != null) { // do we have a clinit
832       cb.invokeclinit(this);
833     } else {
834       cb.finishclinit(this);
835       // we can't just do call ci.setInitialized() since that has to be deferred
836     }
837
838     frame.pushRef(clsObjRef);
839     cb.monitorexit();
840   }
841
842   //--- call processing
843   
844   protected JVMCodeBuilder getSystemCodeBuilder (ClassFile cf, MethodInfo mi){
845     JVMSystemClassLoaderInfo sysCl = (JVMSystemClassLoaderInfo) ClassLoaderInfo.getCurrentSystemClassLoader();
846     JVMCodeBuilder cb = sysCl.getSystemCodeBuilder(cf, mi);
847     
848     return cb;
849   }
850   
851   /**
852    * to be called from super proxy ctor
853    * this needs to be in the VM specific ClassInfo because we need to create code
854    */
855   @Override
856   protected void setAnnotationValueGetterCode (MethodInfo pmi, FieldInfo fi){
857     JVMCodeBuilder cb = getSystemCodeBuilder(null, pmi);
858
859     cb.aload(0);
860     cb.getfield( pmi.getName(), name, pmi.getReturnType());
861     if (fi.isReference()) {
862       cb.areturn();
863     } else {
864       if (fi.getStorageSize() == 1) {
865         cb.ireturn();
866       } else {
867         cb.lreturn();
868       }
869     }
870
871     cb.installCode();
872   }
873   
874   @Override
875   protected void setDirectCallCode (MethodInfo miDirectCall, MethodInfo miCallee){
876     JVMCodeBuilder cb = getSystemCodeBuilder(null, miDirectCall);
877     
878     String calleeName = miCallee.getName();
879     String calleeSig = miCallee.getSignature();
880
881     if (miCallee.isStatic()){
882       if (miCallee.isClinit()) {
883         cb.invokeclinit(this);
884       } else {
885         cb.invokestatic( name, calleeName, calleeSig);
886       }
887     } else if (name.equals("<init>") || miCallee.isPrivate()){
888       cb.invokespecial( name, calleeName, calleeSig);
889     } else {
890       cb.invokevirtual( name, calleeName, calleeSig);
891     }
892
893     cb.directcallreturn();
894     
895     cb.installCode();
896   }
897   
898   @Override
899   protected void setNativeCallCode (NativeMethodInfo miNative){
900     JVMCodeBuilder cb = getSystemCodeBuilder(null, miNative);
901     
902     cb.executenative(miNative);
903     cb.nativereturn();
904     
905     cb.installCode();
906   }
907   
908   @Override
909   protected void setRunStartCode (MethodInfo miStub, MethodInfo miRun){
910     JVMCodeBuilder cb = getSystemCodeBuilder(null, miStub);
911     
912     cb.runStart( miStub);
913     cb.invokevirtual( name, miRun.getName(), miRun.getSignature());
914     cb.directcallreturn();
915     
916     cb.installCode();    
917   }
918   
919   /**
920    * This method creates the body of the function object method that captures the 
921    * lambda behavior.
922    */
923   @Override
924   protected void setLambdaDirectCallCode (MethodInfo miDirectCall, BootstrapMethodInfo bootstrapMethod) {
925     
926     MethodInfo miCallee = bootstrapMethod.getLambdaBody();
927     String samSignature = bootstrapMethod.getSamDescriptor();
928     JVMCodeBuilder cb = getSystemCodeBuilder(null, miDirectCall);
929     
930     String calleeName = miCallee.getName();
931     String calleeSig = miCallee.getSignature();
932     
933     ClassInfo callerCi = miDirectCall.getClassInfo();
934     
935     // loading free variables, which are used in the body of the lambda 
936     // expression and captured by the lexical scope. These variables  
937     // are stored by the fields of the synthetic function object class
938     int n = callerCi.getNumberOfInstanceFields();
939     for(int i=0; i<n; i++) {
940       cb.aload(0);
941       FieldInfo fi = callerCi.getInstanceField(i);
942       
943       cb.getfield(fi.getName(), callerCi.getName(), Types.getTypeSignature(fi.getSignature(), false));
944     }
945
946     // adding bytecode instructions to load input parameters of the lambda expression
947     n = miDirectCall.getArgumentsSize();
948     for(int i=1; i<n; i++) {
949       cb.aload(i);
950     }
951     
952     String calleeClass = miCallee.getClassName(); 
953     
954     // adding the bytecode instruction to invoke lambda method
955     switch (bootstrapMethod.getLambdaRefKind()) {
956     case ClassFile.REF_INVOKESTATIC:
957       cb.invokestatic(calleeClass, calleeName, calleeSig);
958       break;
959     case ClassFile.REF_INVOKEINTERFACE:
960       cb.invokeinterface(calleeClass, calleeName, calleeSig);
961       break;
962     case ClassFile.REF_INVOKEVIRTUAL:
963       cb.invokevirtual(calleeClass, calleeName, calleeSig);
964       break;
965     case ClassFile.REF_NEW_INVOKESPECIAL:
966       cb.new_(calleeClass);
967       cb.invokespecial(calleeClass, calleeName, calleeSig);
968       break;
969     case ClassFile.REF_INVOKESPECIAL:
970       cb.invokespecial(calleeClass, calleeName, calleeSig);
971       break;
972     }
973     
974     String returnType = Types.getReturnTypeSignature(samSignature);
975     int  len = returnType.length();
976     char c = returnType.charAt(0);
977
978     // adding a return statement for function object method
979     if (len == 1) {
980       switch (c) {
981       case 'B':
982       case 'I':
983       case 'C':
984       case 'Z':
985       case 'S':
986         cb.ireturn();
987         break;
988       case 'D':
989         cb.dreturn();
990         break;
991       case 'J':
992         cb.lreturn();
993         break;
994       case 'F':
995         cb.freturn();
996         break;
997       case 'V':
998         cb.return_();
999         break;
1000       }
1001     } else {
1002       cb.areturn();
1003     }
1004     
1005     cb.installCode();
1006   }
1007   
1008   // create a stack frame that has properly initialized arguments
1009   @Override
1010   public StackFrame createStackFrame (ThreadInfo ti, MethodInfo callee){
1011     
1012     if (callee.isMJI()){
1013       NativeMethodInfo nativeCallee = (NativeMethodInfo) callee;
1014       JVMNativeStackFrame calleeFrame = new JVMNativeStackFrame( nativeCallee);
1015       calleeFrame.setArguments( ti);
1016       return calleeFrame; 
1017       
1018     } else {
1019       JVMStackFrame calleeFrame = new JVMStackFrame( callee);
1020       calleeFrame.setCallArguments( ti);
1021       return calleeFrame;      
1022     }
1023   }
1024   
1025   @Override
1026   public DirectCallStackFrame createDirectCallStackFrame (ThreadInfo ti, MethodInfo miCallee, int nLocals){
1027     int nOperands = miCallee.getNumberOfCallerStackSlots();
1028     
1029     MethodInfo miDirect = new MethodInfo(miCallee, nLocals, nOperands);
1030     setDirectCallCode( miDirect, miCallee);
1031     
1032     return new JVMDirectCallStackFrame( miDirect, miCallee);
1033   }
1034   
1035   /**
1036    * while this is a normal DirectCallStackFrame, it has different code which has to be created here 
1037    */
1038   @Override
1039   public DirectCallStackFrame createRunStartStackFrame (ThreadInfo ti, MethodInfo miRun){
1040     MethodInfo miDirect = new MethodInfo( miRun, 0, 1);
1041     setRunStartCode( miDirect, miRun);
1042     
1043     return new JVMDirectCallStackFrame( miDirect, miRun);
1044   }
1045   
1046 }