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