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