2 * Copyright (C) 2014, United States Government, as represented by the
3 * Administrator of the National Aeronautics and Space Administration.
6 * The Java Pathfinder core (jpf-core) platform is licensed under the
7 * Apache License, Version 2.0 (the "License"); you may not use this file except
8 * in compliance with the License. You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0.
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
19 package gov.nasa.jpf.jvm;
21 import java.lang.reflect.Modifier;
22 import java.util.HashMap;
23 import java.util.LinkedHashMap;
24 import java.util.LinkedList;
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;
59 * a ClassInfo that was created from a Java classfile
61 public class JVMClassInfo extends ClassInfo {
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
68 class Initializer extends ClassFileReaderAdapter {
69 protected ClassFile cf;
70 protected JVMCodeBuilder cb;
72 public Initializer (ClassFile cf, JVMCodeBuilder cb) throws ClassParseException {
80 public void setClass (ClassFile cf, String clsName, String superClsName, int flags, int cpCount) throws ClassParseException {
81 JVMClassInfo.this.setClass(clsName, superClsName, flags, cpCount);
85 public void setClassAttribute (ClassFile cf, int attrIndex, String name, int attrLength) {
86 if (name == ClassFile.SOURCE_FILE_ATTR) {
87 cf.parseSourceFileAttr(this, null);
89 } else if (name == ClassFile.SIGNATURE_ATTR) {
90 cf.parseSignatureAttr(this, JVMClassInfo.this);
92 } else if (name == ClassFile.RUNTIME_VISIBLE_ANNOTATIONS_ATTR) {
93 cf.parseAnnotationsAttr(this, JVMClassInfo.this);
95 } else if (name == ClassFile.RUNTIME_INVISIBLE_ANNOTATIONS_ATTR) {
96 //cf.parseAnnotationsAttr(this, ClassInfo.this);
98 } else if (name == ClassFile.RUNTIME_VISIBLE_TYPE_ANNOTATIONS_ATTR) {
99 cf.parseTypeAnnotationsAttr(this, JVMClassInfo.this);
101 } else if (name == ClassFile.INNER_CLASSES_ATTR) {
102 cf.parseInnerClassesAttr(this, JVMClassInfo.this);
104 } else if (name == ClassFile.ENCLOSING_METHOD_ATTR) {
105 cf.parseEnclosingMethodAttr(this, JVMClassInfo.this);
107 } else if (name == ClassFile.BOOTSTRAP_METHOD_ATTR) {
108 cf.parseBootstrapMethodAttr(this, JVMClassInfo.this);
114 public void setBootstrapMethodCount (ClassFile cf, Object tag, int count) {
115 bootstrapMethods = new BootstrapMethodInfo[count];
119 public void setBootstrapMethod (ClassFile cf, Object tag, int idx, int refKind, String cls, String mth, String descriptor, int[] cpArgs) {
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]);
125 int mrefIdx = cf.mhMethodRefIndexAt(cpArgs[1]);
126 String clsName = cf.methodClassNameAt(mrefIdx).replace('/', '.');
127 ClassInfo eclosingLambdaCls;
129 if(!clsName.equals(JVMClassInfo.this.getName())) {
130 eclosingLambdaCls = ClassLoaderInfo.getCurrentResolvedClassInfo(clsName);
132 eclosingLambdaCls = JVMClassInfo.this;
135 assert (eclosingLambdaCls!=null);
137 String mthName = cf.methodNameAt(mrefIdx);
138 String signature = cf.methodDescriptorAt(mrefIdx);
140 MethodInfo lambdaBody = eclosingLambdaCls.getMethod(mthName + signature, false);
142 String samDescriptor = cf.methodTypeDescriptorAt(cpArgs[2]);
144 if(lambdaBody!=null) {
145 bootstrapMethods[idx] = new BootstrapMethodInfo(lambdaRefKind, JVMClassInfo.this, lambdaBody, samDescriptor);
149 //--- inner/enclosing classes
151 public void setInnerClassCount (ClassFile cf, Object tag, int classCount) {
152 innerClassNames = new String[classCount];
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);
168 innerName = Types.getClassNameFromTypeName(innerName);
169 if (!innerName.equals(name)) {
170 innerClassNames[innerClsIndex] = innerName;
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);
181 public void setEnclosingMethod (ClassFile cf, Object tag, String enclosingClassName, String enclosingMethodName, String descriptor) {
182 setEnclosingClass(enclosingClassName);
184 if (enclosingMethodName != null) {
185 JVMClassInfo.this.setEnclosingMethod(enclosingMethodName + descriptor);
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);
199 public void setSourceFile (ClassFile cf, Object tag, String fileName) {
200 JVMClassInfo.this.setSourceFile(fileName);
205 public void setInterfaceCount (ClassFile cf, int ifcCount) {
206 interfaceNames = new String[ifcCount];
210 public void setInterface (ClassFile cf, int ifcIndex, String ifcName) {
211 interfaceNames[ifcIndex] = Types.getClassNameFromTypeName(ifcName);
215 // unfortunately they are stored together in the ClassFile, i.e. we
216 // have to split them up once we are done
218 protected FieldInfo[] fields;
219 protected FieldInfo curFi; // need to cache for attributes
222 public void setFieldCount (ClassFile cf, int fieldCount) {
224 fields = new FieldInfo[fieldCount];
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
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);
242 } else if (name == ClassFile.CONST_VALUE_ATTR) {
243 cf.parseConstValueAttr(this, curFi);
245 } else if (name == ClassFile.RUNTIME_VISIBLE_ANNOTATIONS_ATTR) {
246 cf.parseAnnotationsAttr(this, curFi);
248 } else if (name == ClassFile.RUNTIME_VISIBLE_TYPE_ANNOTATIONS_ATTR) {
249 cf.parseTypeAnnotationsAttr(this, curFi);
251 } else if (name == ClassFile.RUNTIME_INVISIBLE_ANNOTATIONS_ATTR) {
252 //cf.parseAnnotationsAttr(this, curFi);
257 public void setConstantValue (ClassFile cf, Object tag, Object constVal) {
258 curFi.setConstantValue(constVal);
262 public void setFieldsDone (ClassFile cf) {
266 //--- declaredMethods
267 protected MethodInfo curMi;
270 public void setMethodCount (ClassFile cf, int methodCount) {
271 methods = new LinkedHashMap<String, MethodInfo>();
275 public void setMethod (ClassFile cf, int methodIndex, int accessFlags, String name, String signature) {
276 MethodInfo mi = MethodInfo.create(name, signature, accessFlags);
281 public void setMethodDone (ClassFile cf, int methodIndex){
282 curMi.setLocalVarAnnotations();
284 JVMClassInfo.this.setMethod(curMi);
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);
292 } else if (name == ClassFile.SIGNATURE_ATTR) {
293 cf.parseSignatureAttr(this, curMi);
295 } else if (name == ClassFile.EXCEPTIONS_ATTR) {
296 cf.parseExceptionAttr(this, curMi);
298 } else if (name == ClassFile.RUNTIME_VISIBLE_ANNOTATIONS_ATTR) {
299 cf.parseAnnotationsAttr(this, curMi);
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);
306 } else if (name == ClassFile.RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS_ATTR) {
307 //cf.parseParameterAnnotationsAttr(this, curMi);
309 } else if (name == ClassFile.RUNTIME_VISIBLE_TYPE_ANNOTATIONS_ATTR) {
310 cf.parseTypeAnnotationsAttr(this, curMi);
315 //--- current methods throws list
316 protected String[] exceptions;
319 public void setExceptionCount (ClassFile cf, Object tag, int exceptionCount) {
320 exceptions = new String[exceptionCount];
324 public void setException (ClassFile cf, Object tag, int exceptionIndex, String exceptionType) {
325 exceptions[exceptionIndex] = Types.getClassNameFromTypeName(exceptionType);
329 public void setExceptionsDone (ClassFile cf, Object tag) {
330 curMi.setThrownExceptions(exceptions);
333 //--- current method exception handlers
334 protected ExceptionHandler[] handlers;
337 public void setExceptionHandlerTableCount (ClassFile cf, Object tag, int exceptionTableCount) {
338 handlers = new ExceptionHandler[exceptionTableCount];
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;
349 public void setExceptionHandlerTableDone (ClassFile cf, Object tag) {
350 curMi.setExceptionHandlers(handlers);
353 //--- current method code
355 public void setCode (ClassFile cf, Object tag, int maxStack, int maxLocals, int codeLength) {
356 curMi.setMaxLocals(maxLocals);
357 curMi.setMaxStack(maxStack);
361 cf.parseBytecode(cb, tag, codeLength);
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);
370 } else if (name == ClassFile.LOCAL_VAR_TABLE_ATTR) {
371 cf.parseLocalVarTableAttr(this, tag);
373 } else if (name == ClassFile.RUNTIME_VISIBLE_TYPE_ANNOTATIONS_ATTR){
374 cf.parseTypeAnnotationsAttr(this, tag);
378 //--- current method line numbers
379 protected int[] lines, startPcs;
382 public void setLineNumberTableCount (ClassFile cf, Object tag, int lineNumberCount) {
383 lines = new int[lineNumberCount];
384 startPcs = new int[lineNumberCount];
388 public void setLineNumber (ClassFile cf, Object tag, int lineIndex, int lineNumber, int startPc) {
389 lines[lineIndex] = lineNumber;
390 startPcs[lineIndex] = startPc;
394 public void setLineNumberTableDone (ClassFile cf, Object tag) {
395 curMi.setLineNumbers(lines, startPcs);
398 //--- current method local variables
399 protected LocalVarInfo[] localVars;
402 public void setLocalVarTableCount (ClassFile cf, Object tag, int localVarCount) {
403 localVars = new LocalVarInfo[localVarCount];
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;
414 public void setLocalVarTableDone (ClassFile cf, Object tag) {
415 curMi.setLocalVarTable(localVars);
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;
428 //--- declaration annotations
431 public void setAnnotationCount (ClassFile cf, Object tag, int annotationCount) {
432 annotations = new AnnotationInfo[annotationCount];
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<>();
444 annotationStack.addFirst(curAi);
445 valuesStack.addFirst(values);
448 curAi = getResolvedAnnotationInfo(Types.getClassNameFromTypeName(annotationType));
449 if(annotationIndex != -1) {
450 annotations[annotationIndex] = curAi;
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) {
457 compactAnnotationArray = true;
458 annotations[annotationIndex] = null;
459 // skip this annotation
460 throw new SkipAnnotation();
466 public void setAnnotationsDone (ClassFile cf, Object tag) {
467 if (tag instanceof InfoObject) {
468 AnnotationInfo[] toSet;
469 if(compactAnnotationArray) {
471 for(AnnotationInfo ai : annotations) {
476 toSet = new AnnotationInfo[nAnnot];
478 for(AnnotationInfo ai : annotations) {
486 ((InfoObject) tag).addAnnotations(toSet);
488 compactAnnotationArray = false;
492 public void setParameterCount (ClassFile cf, Object tag, int parameterCount) {
493 parameterAnnotations = new AnnotationInfo[parameterCount][];
497 public void setParameterAnnotationCount (ClassFile cf, Object tag, int paramIndex, int annotationCount) {
498 annotations = new AnnotationInfo[annotationCount];
499 parameterAnnotations[paramIndex] = annotations;
503 public void setParameterAnnotation (ClassFile cf, Object tag, int annotationIndex, String annotationType) {
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();
515 public void setParametersDone (ClassFile cf, Object tag) {
516 curMi.setParameterAnnotations(parameterAnnotations);
519 //--- Java 8 type annotations
522 public void setTypeAnnotationCount(ClassFile cf, Object tag, int annotationCount){
523 annotations = new AnnotationInfo[annotationCount];
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;
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;
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;
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;
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;
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;
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);
574 annotations[annotationIndex] = curAi;
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;
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;
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;
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];
607 // we can get them in batches (e.g. VariableTypeAnnos from code attrs and ReturnTypeAnnos from method attrs
608 ((InfoObject) tag).addTypeAnnotations( tais);
612 //--- AnnotationInfo values entries
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;
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;
628 curAi.setClonedEntryValue(elementName, val);
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;
641 curAi.setClonedEntryValue(elementName, ai);
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;
651 curAi.setClonedEntryValue(elementName, val);
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;
662 curAi.setClonedEntryValue(elementName, val);
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;
673 curAi.setClonedEntryValue(elementName, val);
678 public void setAnnotationValueElementCount (ClassFile cf, Object tag, int annotationIndex, int valueIndex,
679 String elementName, int elementCount) {
680 values = new Object[elementCount];
684 public void setAnnotationValueElementsDone (ClassFile cf, Object tag, int annotationIndex, int valueIndex, String elementName) {
685 curAi.setClonedEntryValue(elementName, values);
690 public void setSignature (ClassFile cf, Object tag, String signature) {
691 if (tag instanceof GenericSignatureHolder) {
692 ((GenericSignatureHolder) tag).setGenericSignature(signature);
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;
702 protected static boolean init (Config config){
703 nestedInit = config.getBoolean("jvm.nested_init", false);
705 includeNestedInit = StringSetMatcher.getNonEmpty(config.getStringArray("jvm.nested_init.include"));
706 excludeNestedInit = StringSetMatcher.getNonEmpty(config.getStringArray("jvm.nested_init.exclude"));
712 JVMClassInfo (String name, ClassLoaderInfo cli, ClassFile cf, String srcUrl, JVMCodeBuilder cb) throws ClassParseException {
713 super( name, cli, srcUrl);
715 new Initializer( cf, cb); // we just need the ctor
721 //--- for annotation classinfos
723 // called on the annotation classinfo
725 protected ClassInfo createAnnotationProxy (String proxyName){
726 return new JVMClassInfo (this, proxyName, classLoader, null);
729 // concrete proxy ctor
730 protected JVMClassInfo (ClassInfo ciAnnotation, String proxyName, ClassLoaderInfo cli, String url) {
731 super( ciAnnotation, proxyName, cli, url);
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.
740 protected ClassInfo createFuncObjClassInfo (BootstrapMethodInfo bootstrapMethod, String name, String samUniqueName, String[] fieldTypesName) {
741 return new JVMClassInfo(this, bootstrapMethod, name, samUniqueName, fieldTypesName);
744 protected JVMClassInfo (ClassInfo funcInterface, BootstrapMethodInfo bootstrapMethod, String name, String samUniqueName, String[] fieldTypesName) {
745 super(funcInterface, bootstrapMethod, name, fieldTypesName);
747 // creating a method corresponding to the single abstract method of the functional interface
748 methods = new HashMap<String, MethodInfo>();
750 MethodInfo fiMethod = funcInterface.getInterfaceAbstractMethod();
751 int modifiers = fiMethod.getModifiers() & (~Modifier.ABSTRACT);
752 int nLocals = fiMethod.getArgumentsSize();
753 int nOperands = this.nInstanceFields + nLocals;
755 MethodInfo mi = new MethodInfo(fiMethod.getName(), fiMethod.getSignature(), modifiers, nLocals, nOperands);
756 mi.linkToClass(this);
758 methods.put(mi.getUniqueName(), mi);
760 setLambdaDirectCallCode(mi, bootstrapMethod);
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.
771 * perform initialization of this class and its not-yet-initialized superclasses (top down),
772 * which includes calling clinit() methods
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.
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.
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)
788 * @return true if client needs to re-execute because we pushed DirectCallStackFrames
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
797 MethodInfo miInitialize = new MethodInfo("[initializeClass]", "()V", Modifier.STATIC, 0, nOps);
798 JVMDirectCallStackFrame frame = new JVMDirectCallStackFrame(miInitialize, null);
799 JVMCodeBuilder cb = getSystemCodeBuilder(null, miInitialize);
801 addClassInit(ti, frame, cb); // this is recursively upwards until we hit a initialized superclass
802 cb.directcallreturn();
805 // this is normally initialized in the ctor, but at that point we don't have the code yet
806 frame.setPC(miInitialize.getFirstInsn());
809 return true; // client has to re-execute, we pushed a stackframe
812 } else { // use generic initialization without nested locks (directly calling clinits)
813 return super.initializeClass(ti);
817 return false; // nothing to do
821 protected void addClassInit (ThreadInfo ti, JVMDirectCallStackFrame frame, JVMCodeBuilder cb){
822 int clsObjRef = getClassObjectRef();
824 frame.pushRef(clsObjRef);
827 if (superClass != null && superClass.needsInitialization(ti)) {
828 ((JVMClassInfo) superClass).addClassInit(ti, frame, cb); // go recursive
831 if (getMethod("<clinit>()V", false) != null) { // do we have a clinit
832 cb.invokeclinit(this);
834 cb.finishclinit(this);
835 // we can't just do call ci.setInitialized() since that has to be deferred
838 frame.pushRef(clsObjRef);
842 //--- call processing
844 protected JVMCodeBuilder getSystemCodeBuilder (ClassFile cf, MethodInfo mi){
845 JVMSystemClassLoaderInfo sysCl = (JVMSystemClassLoaderInfo) ClassLoaderInfo.getCurrentSystemClassLoader();
846 JVMCodeBuilder cb = sysCl.getSystemCodeBuilder(cf, mi);
852 * to be called from super proxy ctor
853 * this needs to be in the VM specific ClassInfo because we need to create code
856 protected void setAnnotationValueGetterCode (MethodInfo pmi, FieldInfo fi){
857 JVMCodeBuilder cb = getSystemCodeBuilder(null, pmi);
860 cb.getfield( pmi.getName(), name, pmi.getReturnType());
861 if (fi.isReference()) {
864 if (fi.getStorageSize() == 1) {
875 protected void setDirectCallCode (MethodInfo miDirectCall, MethodInfo miCallee){
876 JVMCodeBuilder cb = getSystemCodeBuilder(null, miDirectCall);
878 String calleeName = miCallee.getName();
879 String calleeSig = miCallee.getSignature();
881 if (miCallee.isStatic()){
882 if (miCallee.isClinit()) {
883 cb.invokeclinit(this);
885 cb.invokestatic( name, calleeName, calleeSig);
887 } else if (name.equals("<init>") || miCallee.isPrivate()){
888 cb.invokespecial( name, calleeName, calleeSig);
890 cb.invokevirtual( name, calleeName, calleeSig);
893 cb.directcallreturn();
899 protected void setNativeCallCode (NativeMethodInfo miNative){
900 JVMCodeBuilder cb = getSystemCodeBuilder(null, miNative);
902 cb.executenative(miNative);
909 protected void setRunStartCode (MethodInfo miStub, MethodInfo miRun){
910 JVMCodeBuilder cb = getSystemCodeBuilder(null, miStub);
912 cb.runStart( miStub);
913 cb.invokevirtual( name, miRun.getName(), miRun.getSignature());
914 cb.directcallreturn();
920 * This method creates the body of the function object method that captures the
924 protected void setLambdaDirectCallCode (MethodInfo miDirectCall, BootstrapMethodInfo bootstrapMethod) {
926 MethodInfo miCallee = bootstrapMethod.getLambdaBody();
927 String samSignature = bootstrapMethod.getSamDescriptor();
928 JVMCodeBuilder cb = getSystemCodeBuilder(null, miDirectCall);
930 String calleeName = miCallee.getName();
931 String calleeSig = miCallee.getSignature();
933 ClassInfo callerCi = miDirectCall.getClassInfo();
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++) {
941 FieldInfo fi = callerCi.getInstanceField(i);
943 cb.getfield(fi.getName(), callerCi.getName(), Types.getTypeSignature(fi.getSignature(), false));
946 // adding bytecode instructions to load input parameters of the lambda expression
947 n = miDirectCall.getArgumentsSize();
948 for(int i=1; i<n; i++) {
952 String calleeClass = miCallee.getClassName();
954 // adding the bytecode instruction to invoke lambda method
955 switch (bootstrapMethod.getLambdaRefKind()) {
956 case ClassFile.REF_INVOKESTATIC:
957 cb.invokestatic(calleeClass, calleeName, calleeSig);
959 case ClassFile.REF_INVOKEINTERFACE:
960 cb.invokeinterface(calleeClass, calleeName, calleeSig);
962 case ClassFile.REF_INVOKEVIRTUAL:
963 cb.invokevirtual(calleeClass, calleeName, calleeSig);
965 case ClassFile.REF_NEW_INVOKESPECIAL:
966 cb.new_(calleeClass);
967 cb.invokespecial(calleeClass, calleeName, calleeSig);
969 case ClassFile.REF_INVOKESPECIAL:
970 cb.invokespecial(calleeClass, calleeName, calleeSig);
974 String returnType = Types.getReturnTypeSignature(samSignature);
975 int len = returnType.length();
976 char c = returnType.charAt(0);
978 // adding a return statement for function object method
1008 // create a stack frame that has properly initialized arguments
1010 public StackFrame createStackFrame (ThreadInfo ti, MethodInfo callee){
1012 if (callee.isMJI()){
1013 NativeMethodInfo nativeCallee = (NativeMethodInfo) callee;
1014 JVMNativeStackFrame calleeFrame = new JVMNativeStackFrame( nativeCallee);
1015 calleeFrame.setArguments( ti);
1019 JVMStackFrame calleeFrame = new JVMStackFrame( callee);
1020 calleeFrame.setCallArguments( ti);
1026 public DirectCallStackFrame createDirectCallStackFrame (ThreadInfo ti, MethodInfo miCallee, int nLocals){
1027 int nOperands = miCallee.getNumberOfCallerStackSlots();
1029 MethodInfo miDirect = new MethodInfo(miCallee, nLocals, nOperands);
1030 setDirectCallCode( miDirect, miCallee);
1032 return new JVMDirectCallStackFrame( miDirect, miCallee);
1036 * while this is a normal DirectCallStackFrame, it has different code which has to be created here
1039 public DirectCallStackFrame createRunStartStackFrame (ThreadInfo ti, MethodInfo miRun){
1040 MethodInfo miDirect = new MethodInfo( miRun, 0, 1);
1041 setRunStartCode( miDirect, miRun);
1043 return new JVMDirectCallStackFrame( miDirect, miRun);