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 int lambdaRefKind = cf.mhRefTypeAt(cpArgs[1]);
123 int mrefIdx = cf.mhMethodRefIndexAt(cpArgs[1]);
124 String clsName = cf.methodClassNameAt(mrefIdx).replace('/', '.');
125 ClassInfo eclosingLambdaCls;
127 if(!clsName.equals(JVMClassInfo.this.getName())) {
128 eclosingLambdaCls = ClassLoaderInfo.getCurrentResolvedClassInfo(clsName);
130 eclosingLambdaCls = JVMClassInfo.this;
133 assert (eclosingLambdaCls!=null);
135 String mthName = cf.methodNameAt(mrefIdx);
136 String signature = cf.methodDescriptorAt(mrefIdx);
138 MethodInfo lambdaBody = eclosingLambdaCls.getMethod(mthName + signature, false);
140 String samDescriptor = cf.methodTypeDescriptorAt(cpArgs[2]);
142 if(lambdaBody!=null) {
143 bootstrapMethods[idx] = new BootstrapMethodInfo(lambdaRefKind, JVMClassInfo.this, lambdaBody, samDescriptor);
147 //--- inner/enclosing classes
149 public void setInnerClassCount (ClassFile cf, Object tag, int classCount) {
150 innerClassNames = new String[classCount];
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);
166 innerName = Types.getClassNameFromTypeName(innerName);
167 if (!innerName.equals(name)) {
168 innerClassNames[innerClsIndex] = innerName;
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);
179 public void setEnclosingMethod (ClassFile cf, Object tag, String enclosingClassName, String enclosingMethodName, String descriptor) {
180 setEnclosingClass(enclosingClassName);
182 if (enclosingMethodName != null) {
183 JVMClassInfo.this.setEnclosingMethod(enclosingMethodName + descriptor);
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);
197 public void setSourceFile (ClassFile cf, Object tag, String fileName) {
198 JVMClassInfo.this.setSourceFile(fileName);
203 public void setInterfaceCount (ClassFile cf, int ifcCount) {
204 interfaceNames = new String[ifcCount];
208 public void setInterface (ClassFile cf, int ifcIndex, String ifcName) {
209 interfaceNames[ifcIndex] = Types.getClassNameFromTypeName(ifcName);
213 // unfortunately they are stored together in the ClassFile, i.e. we
214 // have to split them up once we are done
216 protected FieldInfo[] fields;
217 protected FieldInfo curFi; // need to cache for attributes
220 public void setFieldCount (ClassFile cf, int fieldCount) {
222 fields = new FieldInfo[fieldCount];
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
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);
240 } else if (name == ClassFile.CONST_VALUE_ATTR) {
241 cf.parseConstValueAttr(this, curFi);
243 } else if (name == ClassFile.RUNTIME_VISIBLE_ANNOTATIONS_ATTR) {
244 cf.parseAnnotationsAttr(this, curFi);
246 } else if (name == ClassFile.RUNTIME_VISIBLE_TYPE_ANNOTATIONS_ATTR) {
247 cf.parseTypeAnnotationsAttr(this, curFi);
249 } else if (name == ClassFile.RUNTIME_INVISIBLE_ANNOTATIONS_ATTR) {
250 //cf.parseAnnotationsAttr(this, curFi);
255 public void setConstantValue (ClassFile cf, Object tag, Object constVal) {
256 curFi.setConstantValue(constVal);
260 public void setFieldsDone (ClassFile cf) {
264 //--- declaredMethods
265 protected MethodInfo curMi;
268 public void setMethodCount (ClassFile cf, int methodCount) {
269 methods = new LinkedHashMap<String, MethodInfo>();
273 public void setMethod (ClassFile cf, int methodIndex, int accessFlags, String name, String signature) {
274 MethodInfo mi = MethodInfo.create(name, signature, accessFlags);
279 public void setMethodDone (ClassFile cf, int methodIndex){
280 curMi.setLocalVarAnnotations();
282 JVMClassInfo.this.setMethod(curMi);
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);
290 } else if (name == ClassFile.SIGNATURE_ATTR) {
291 cf.parseSignatureAttr(this, curMi);
293 } else if (name == ClassFile.EXCEPTIONS_ATTR) {
294 cf.parseExceptionAttr(this, curMi);
296 } else if (name == ClassFile.RUNTIME_VISIBLE_ANNOTATIONS_ATTR) {
297 cf.parseAnnotationsAttr(this, curMi);
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);
304 } else if (name == ClassFile.RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS_ATTR) {
305 //cf.parseParameterAnnotationsAttr(this, curMi);
307 } else if (name == ClassFile.RUNTIME_VISIBLE_TYPE_ANNOTATIONS_ATTR) {
308 cf.parseTypeAnnotationsAttr(this, curMi);
313 //--- current methods throws list
314 protected String[] exceptions;
317 public void setExceptionCount (ClassFile cf, Object tag, int exceptionCount) {
318 exceptions = new String[exceptionCount];
322 public void setException (ClassFile cf, Object tag, int exceptionIndex, String exceptionType) {
323 exceptions[exceptionIndex] = Types.getClassNameFromTypeName(exceptionType);
327 public void setExceptionsDone (ClassFile cf, Object tag) {
328 curMi.setThrownExceptions(exceptions);
331 //--- current method exception handlers
332 protected ExceptionHandler[] handlers;
335 public void setExceptionHandlerTableCount (ClassFile cf, Object tag, int exceptionTableCount) {
336 handlers = new ExceptionHandler[exceptionTableCount];
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;
347 public void setExceptionHandlerTableDone (ClassFile cf, Object tag) {
348 curMi.setExceptionHandlers(handlers);
351 //--- current method code
353 public void setCode (ClassFile cf, Object tag, int maxStack, int maxLocals, int codeLength) {
354 curMi.setMaxLocals(maxLocals);
355 curMi.setMaxStack(maxStack);
359 cf.parseBytecode(cb, tag, codeLength);
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);
368 } else if (name == ClassFile.LOCAL_VAR_TABLE_ATTR) {
369 cf.parseLocalVarTableAttr(this, tag);
371 } else if (name == ClassFile.RUNTIME_VISIBLE_TYPE_ANNOTATIONS_ATTR){
372 cf.parseTypeAnnotationsAttr(this, tag);
376 //--- current method line numbers
377 protected int[] lines, startPcs;
380 public void setLineNumberTableCount (ClassFile cf, Object tag, int lineNumberCount) {
381 lines = new int[lineNumberCount];
382 startPcs = new int[lineNumberCount];
386 public void setLineNumber (ClassFile cf, Object tag, int lineIndex, int lineNumber, int startPc) {
387 lines[lineIndex] = lineNumber;
388 startPcs[lineIndex] = startPc;
392 public void setLineNumberTableDone (ClassFile cf, Object tag) {
393 curMi.setLineNumbers(lines, startPcs);
396 //--- current method local variables
397 protected LocalVarInfo[] localVars;
400 public void setLocalVarTableCount (ClassFile cf, Object tag, int localVarCount) {
401 localVars = new LocalVarInfo[localVarCount];
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;
412 public void setLocalVarTableDone (ClassFile cf, Object tag) {
413 curMi.setLocalVarTable(localVars);
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;
426 //--- declaration annotations
429 public void setAnnotationCount (ClassFile cf, Object tag, int annotationCount) {
430 annotations = new AnnotationInfo[annotationCount];
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<>();
442 annotationStack.addFirst(curAi);
443 valuesStack.addFirst(values);
446 curAi = getResolvedAnnotationInfo(Types.getClassNameFromTypeName(annotationType));
447 if(annotationIndex != -1) {
448 annotations[annotationIndex] = curAi;
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) {
455 compactAnnotationArray = true;
456 annotations[annotationIndex] = null;
457 // skip this annotation
458 throw new SkipAnnotation();
464 public void setAnnotationsDone (ClassFile cf, Object tag) {
465 if (tag instanceof InfoObject) {
466 AnnotationInfo[] toSet;
467 if(compactAnnotationArray) {
469 for(AnnotationInfo ai : annotations) {
474 toSet = new AnnotationInfo[nAnnot];
476 for(AnnotationInfo ai : annotations) {
484 ((InfoObject) tag).addAnnotations(toSet);
486 compactAnnotationArray = false;
490 public void setParameterCount (ClassFile cf, Object tag, int parameterCount) {
491 parameterAnnotations = new AnnotationInfo[parameterCount][];
495 public void setParameterAnnotationCount (ClassFile cf, Object tag, int paramIndex, int annotationCount) {
496 annotations = new AnnotationInfo[annotationCount];
497 parameterAnnotations[paramIndex] = annotations;
501 public void setParameterAnnotation (ClassFile cf, Object tag, int annotationIndex, String annotationType) {
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();
513 public void setParametersDone (ClassFile cf, Object tag) {
514 curMi.setParameterAnnotations(parameterAnnotations);
517 //--- Java 8 type annotations
520 public void setTypeAnnotationCount(ClassFile cf, Object tag, int annotationCount){
521 annotations = new AnnotationInfo[annotationCount];
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;
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;
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;
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;
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;
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;
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);
572 annotations[annotationIndex] = curAi;
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;
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;
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;
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];
605 // we can get them in batches (e.g. VariableTypeAnnos from code attrs and ReturnTypeAnnos from method attrs
606 ((InfoObject) tag).addTypeAnnotations( tais);
610 //--- AnnotationInfo values entries
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;
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;
626 curAi.setClonedEntryValue(elementName, val);
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;
639 curAi.setClonedEntryValue(elementName, ai);
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;
649 curAi.setClonedEntryValue(elementName, val);
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;
660 curAi.setClonedEntryValue(elementName, val);
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;
671 curAi.setClonedEntryValue(elementName, val);
676 public void setAnnotationValueElementCount (ClassFile cf, Object tag, int annotationIndex, int valueIndex,
677 String elementName, int elementCount) {
678 values = new Object[elementCount];
682 public void setAnnotationValueElementsDone (ClassFile cf, Object tag, int annotationIndex, int valueIndex, String elementName) {
683 curAi.setClonedEntryValue(elementName, values);
688 public void setSignature (ClassFile cf, Object tag, String signature) {
689 if (tag instanceof GenericSignatureHolder) {
690 ((GenericSignatureHolder) tag).setGenericSignature(signature);
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;
700 protected static boolean init (Config config){
701 nestedInit = config.getBoolean("jvm.nested_init", false);
703 includeNestedInit = StringSetMatcher.getNonEmpty(config.getStringArray("jvm.nested_init.include"));
704 excludeNestedInit = StringSetMatcher.getNonEmpty(config.getStringArray("jvm.nested_init.exclude"));
710 JVMClassInfo (String name, ClassLoaderInfo cli, ClassFile cf, String srcUrl, JVMCodeBuilder cb) throws ClassParseException {
711 super( name, cli, srcUrl);
713 new Initializer( cf, cb); // we just need the ctor
719 //--- for annotation classinfos
721 // called on the annotation classinfo
723 protected ClassInfo createAnnotationProxy (String proxyName){
724 return new JVMClassInfo (this, proxyName, classLoader, null);
727 // concrete proxy ctor
728 protected JVMClassInfo (ClassInfo ciAnnotation, String proxyName, ClassLoaderInfo cli, String url) {
729 super( ciAnnotation, proxyName, cli, url);
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.
738 protected ClassInfo createFuncObjClassInfo (BootstrapMethodInfo bootstrapMethod, String name, String samUniqueName, String[] fieldTypesName) {
739 return new JVMClassInfo(this, bootstrapMethod, name, samUniqueName, fieldTypesName);
742 protected JVMClassInfo (ClassInfo funcInterface, BootstrapMethodInfo bootstrapMethod, String name, String samUniqueName, String[] fieldTypesName) {
743 super(funcInterface, bootstrapMethod, name, fieldTypesName);
745 // creating a method corresponding to the single abstract method of the functional interface
746 methods = new HashMap<String, MethodInfo>();
748 MethodInfo fiMethod = funcInterface.getInterfaceAbstractMethod();
749 int modifiers = fiMethod.getModifiers() & (~Modifier.ABSTRACT);
750 int nLocals = fiMethod.getArgumentsSize();
751 int nOperands = this.nInstanceFields + nLocals;
753 MethodInfo mi = new MethodInfo(fiMethod.getName(), fiMethod.getSignature(), modifiers, nLocals, nOperands);
754 mi.linkToClass(this);
756 methods.put(mi.getUniqueName(), mi);
758 setLambdaDirectCallCode(mi, bootstrapMethod);
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.
769 * perform initialization of this class and its not-yet-initialized superclasses (top down),
770 * which includes calling clinit() methods
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.
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.
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)
786 * @return true if client needs to re-execute because we pushed DirectCallStackFrames
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
795 MethodInfo miInitialize = new MethodInfo("[initializeClass]", "()V", Modifier.STATIC, 0, nOps);
796 JVMDirectCallStackFrame frame = new JVMDirectCallStackFrame(miInitialize, null);
797 JVMCodeBuilder cb = getSystemCodeBuilder(null, miInitialize);
799 addClassInit(ti, frame, cb); // this is recursively upwards until we hit a initialized superclass
800 cb.directcallreturn();
803 // this is normally initialized in the ctor, but at that point we don't have the code yet
804 frame.setPC(miInitialize.getFirstInsn());
807 return true; // client has to re-execute, we pushed a stackframe
810 } else { // use generic initialization without nested locks (directly calling clinits)
811 return super.initializeClass(ti);
815 return false; // nothing to do
819 protected void addClassInit (ThreadInfo ti, JVMDirectCallStackFrame frame, JVMCodeBuilder cb){
820 int clsObjRef = getClassObjectRef();
822 frame.pushRef(clsObjRef);
825 if (superClass != null && superClass.needsInitialization(ti)) {
826 ((JVMClassInfo) superClass).addClassInit(ti, frame, cb); // go recursive
829 if (getMethod("<clinit>()V", false) != null) { // do we have a clinit
830 cb.invokeclinit(this);
832 cb.finishclinit(this);
833 // we can't just do call ci.setInitialized() since that has to be deferred
836 frame.pushRef(clsObjRef);
840 //--- call processing
842 protected JVMCodeBuilder getSystemCodeBuilder (ClassFile cf, MethodInfo mi){
843 JVMSystemClassLoaderInfo sysCl = (JVMSystemClassLoaderInfo) ClassLoaderInfo.getCurrentSystemClassLoader();
844 JVMCodeBuilder cb = sysCl.getSystemCodeBuilder(cf, mi);
850 * to be called from super proxy ctor
851 * this needs to be in the VM specific ClassInfo because we need to create code
854 protected void setAnnotationValueGetterCode (MethodInfo pmi, FieldInfo fi){
855 JVMCodeBuilder cb = getSystemCodeBuilder(null, pmi);
858 cb.getfield( pmi.getName(), name, pmi.getReturnType());
859 if (fi.isReference()) {
862 if (fi.getStorageSize() == 1) {
873 protected void setDirectCallCode (MethodInfo miDirectCall, MethodInfo miCallee){
874 JVMCodeBuilder cb = getSystemCodeBuilder(null, miDirectCall);
876 String calleeName = miCallee.getName();
877 String calleeSig = miCallee.getSignature();
879 if (miCallee.isStatic()){
880 if (miCallee.isClinit()) {
881 cb.invokeclinit(this);
883 cb.invokestatic( name, calleeName, calleeSig);
885 } else if (name.equals("<init>") || miCallee.isPrivate()){
886 cb.invokespecial( name, calleeName, calleeSig);
888 cb.invokevirtual( name, calleeName, calleeSig);
891 cb.directcallreturn();
897 protected void setNativeCallCode (NativeMethodInfo miNative){
898 JVMCodeBuilder cb = getSystemCodeBuilder(null, miNative);
900 cb.executenative(miNative);
907 protected void setRunStartCode (MethodInfo miStub, MethodInfo miRun){
908 JVMCodeBuilder cb = getSystemCodeBuilder(null, miStub);
910 cb.runStart( miStub);
911 cb.invokevirtual( name, miRun.getName(), miRun.getSignature());
912 cb.directcallreturn();
918 * This method creates the body of the function object method that captures the
922 protected void setLambdaDirectCallCode (MethodInfo miDirectCall, BootstrapMethodInfo bootstrapMethod) {
924 MethodInfo miCallee = bootstrapMethod.getLambdaBody();
925 String samSignature = bootstrapMethod.getSamDescriptor();
926 JVMCodeBuilder cb = getSystemCodeBuilder(null, miDirectCall);
928 String calleeName = miCallee.getName();
929 String calleeSig = miCallee.getSignature();
931 ClassInfo callerCi = miDirectCall.getClassInfo();
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++) {
939 FieldInfo fi = callerCi.getInstanceField(i);
941 cb.getfield(fi.getName(), callerCi.getName(), Types.getTypeSignature(fi.getSignature(), false));
944 // adding bytecode instructions to load input parameters of the lambda expression
945 n = miDirectCall.getArgumentsSize();
946 for(int i=1; i<n; i++) {
950 String calleeClass = miCallee.getClassName();
952 // adding the bytecode instruction to invoke lambda method
953 switch (bootstrapMethod.getLambdaRefKind()) {
954 case ClassFile.REF_INVOKESTATIC:
955 cb.invokestatic(calleeClass, calleeName, calleeSig);
957 case ClassFile.REF_INVOKEINTERFACE:
958 cb.invokeinterface(calleeClass, calleeName, calleeSig);
960 case ClassFile.REF_INVOKEVIRTUAL:
961 cb.invokevirtual(calleeClass, calleeName, calleeSig);
963 case ClassFile.REF_NEW_INVOKESPECIAL:
964 cb.new_(calleeClass);
965 cb.invokespecial(calleeClass, calleeName, calleeSig);
967 case ClassFile.REF_INVOKESPECIAL:
968 cb.invokespecial(calleeClass, calleeName, calleeSig);
972 String returnType = Types.getReturnTypeSignature(samSignature);
973 int len = returnType.length();
974 char c = returnType.charAt(0);
976 // adding a return statement for function object method
1006 // create a stack frame that has properly initialized arguments
1008 public StackFrame createStackFrame (ThreadInfo ti, MethodInfo callee){
1010 if (callee.isMJI()){
1011 NativeMethodInfo nativeCallee = (NativeMethodInfo) callee;
1012 JVMNativeStackFrame calleeFrame = new JVMNativeStackFrame( nativeCallee);
1013 calleeFrame.setArguments( ti);
1017 JVMStackFrame calleeFrame = new JVMStackFrame( callee);
1018 calleeFrame.setCallArguments( ti);
1024 public DirectCallStackFrame createDirectCallStackFrame (ThreadInfo ti, MethodInfo miCallee, int nLocals){
1025 int nOperands = miCallee.getNumberOfCallerStackSlots();
1027 MethodInfo miDirect = new MethodInfo(miCallee, nLocals, nOperands);
1028 setDirectCallCode( miDirect, miCallee);
1030 return new JVMDirectCallStackFrame( miDirect, miCallee);
1034 * while this is a normal DirectCallStackFrame, it has different code which has to be created here
1037 public DirectCallStackFrame createRunStartStackFrame (ThreadInfo ti, MethodInfo miRun){
1038 MethodInfo miDirect = new MethodInfo( miRun, 0, 1);
1039 setRunStartCode( miDirect, miRun);
1041 return new JVMDirectCallStackFrame( miDirect, miRun);