// // Copyright (C) 2013 United States Government as represented by the // Administrator of the National Aeronautics and Space Administration // (NASA). All Rights Reserved. // // This software is distributed under the NASA Open Source Agreement // (NOSA), version 1.3. The NOSA has been approved by the Open Source // Initiative. See the file NOSA-1.3-JPF at the top of the distribution // directory tree for the complete NOSA document. // // THE SUBJECT SOFTWARE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY OF ANY // KIND, EITHER EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT // LIMITED TO, ANY WARRANTY THAT THE SUBJECT SOFTWARE WILL CONFORM TO // SPECIFICATIONS, ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR // A PARTICULAR PURPOSE, OR FREEDOM FROM INFRINGEMENT, ANY WARRANTY THAT // THE SUBJECT SOFTWARE WILL BE ERROR FREE, OR ANY WARRANTY THAT // DOCUMENTATION, IF PROVIDED, WILL CONFORM TO THE SUBJECT SOFTWARE. // package gov.nasa.jpf.jvm; import java.lang.reflect.Modifier; import java.util.HashMap; import java.util.LinkedHashMap; import cmu.conditional.One; import de.fosd.typechef.featureexpr.FeatureExpr; import gov.nasa.jpf.Config; import gov.nasa.jpf.util.Misc; import gov.nasa.jpf.util.StringSetMatcher; import gov.nasa.jpf.vm.AnnotationInfo; import gov.nasa.jpf.vm.BootstrapMethodInfo; import gov.nasa.jpf.vm.ClassInfo; import gov.nasa.jpf.vm.ClassLoaderInfo; import gov.nasa.jpf.vm.ClassParseException; import gov.nasa.jpf.vm.DirectCallStackFrame; import gov.nasa.jpf.vm.ExceptionHandler; import gov.nasa.jpf.vm.FieldInfo; import gov.nasa.jpf.vm.GenericSignatureHolder; import gov.nasa.jpf.vm.InfoObject; import gov.nasa.jpf.vm.LocalVarInfo; import gov.nasa.jpf.vm.MethodInfo; import gov.nasa.jpf.vm.NativeMethodInfo; import gov.nasa.jpf.vm.StackFrame; import gov.nasa.jpf.vm.ThreadInfo; import gov.nasa.jpf.vm.Types; /** * a ClassInfo that was created from a Java classfile */ public class JVMClassInfo extends ClassInfo { /** * this is the inner class that does the actual ClassInfo initialization from ClassFile. It is an inner class so that * (a) it can set ClassInfo fields, (b) it can extend ClassFileReaderAdapter, and (c) we don't clutter JVMClassInfo with * fields that are only temporarily used during parsing */ class Initializer extends ClassFileReaderAdapter { protected ClassFile cf; protected JVMCodeBuilder cb; public Initializer (ClassFile cf, JVMCodeBuilder cb) throws ClassParseException { this.cf = cf; this.cb = cb; cf.parse(this); } @Override public void setClass (ClassFile cf, String clsName, String superClsName, int flags, int cpCount) throws ClassParseException { JVMClassInfo.this.setClass(clsName, superClsName, flags, cpCount); } @Override public void setClassAttribute (ClassFile cf, int attrIndex, String name, int attrLength) { if (name == ClassFile.SOURCE_FILE_ATTR) { cf.parseSourceFileAttr(this, null); } else if (name == ClassFile.SIGNATURE_ATTR) { cf.parseSignatureAttr(this, JVMClassInfo.this); } else if (name == ClassFile.RUNTIME_VISIBLE_ANNOTATIONS_ATTR) { cf.parseAnnotationsAttr(this, JVMClassInfo.this); } else if (name == ClassFile.RUNTIME_INVISIBLE_ANNOTATIONS_ATTR) { //cf.parseAnnotationsAttr(this, ClassInfo.this); } else if (name == ClassFile.RUNTIME_VISIBLE_TYPE_ANNOTATIONS_ATTR) { cf.parseTypeAnnotationsAttr(this, JVMClassInfo.this); } else if (name == ClassFile.INNER_CLASSES_ATTR) { cf.parseInnerClassesAttr(this, JVMClassInfo.this); } else if (name == ClassFile.ENCLOSING_METHOD_ATTR) { cf.parseEnclosingMethodAttr(this, JVMClassInfo.this); } else if (name == ClassFile.BOOTSTRAP_METHOD_ATTR) { cf.parseBootstrapMethodAttr(this, JVMClassInfo.this); } } @Override public void setBootstrapMethodCount (ClassFile cf, Object tag, int count) { bootstrapMethods = new BootstrapMethodInfo[count]; } @Override public void setBootstrapMethod (ClassFile cf, Object tag, int idx, int refKind, String cls, String mth, String descriptor, int[] cpArgs) { int lambdaRefKind = cf.mhRefTypeAt(cpArgs[1]); int mrefIdx = cf.mhMethodRefIndexAt(cpArgs[1]); String clsName = cf.methodClassNameAt(mrefIdx).replace('/', '.'); ClassInfo eclosingLambdaCls; if(!clsName.equals(JVMClassInfo.this.getName())) { eclosingLambdaCls = ClassLoaderInfo.getCurrentResolvedClassInfo(clsName); } else { eclosingLambdaCls = JVMClassInfo.this; } assert (eclosingLambdaCls!=null); String mthName = cf.methodNameAt(mrefIdx); String signature = cf.methodDescriptorAt(mrefIdx); MethodInfo lambdaBody = eclosingLambdaCls.getMethod(mthName + signature, false); String samDescriptor = cf.methodTypeDescriptorAt(cpArgs[2]); if(lambdaBody!=null) { bootstrapMethods[idx] = new BootstrapMethodInfo(lambdaRefKind, JVMClassInfo.this, lambdaBody, samDescriptor); } } //--- inner/enclosing classes @Override public void setInnerClassCount (ClassFile cf, Object tag, int classCount) { innerClassNames = new String[classCount]; } @Override public void setInnerClass (ClassFile cf, Object tag, int innerClsIndex, String outerName, String innerName, String innerSimpleName, int accessFlags) { // Ok, this is a total mess - some names are in dot notation, others use '/' // and to make it even more confusing, some InnerClass attributes refer NOT // to the currently parsed class, so we have to check if we are the outerName, // but then 'outerName' can also be null instead of our own name. // Oh, and there are also InnerClass attributes that have their own name as inner names // (see java/lang/String$CaseInsensitiveComparator or ...System and java/lang/System$1 for instance) if (outerName != null) { outerName = Types.getClassNameFromTypeName(outerName); } innerName = Types.getClassNameFromTypeName(innerName); if (!innerName.equals(name)) { innerClassNames[innerClsIndex] = innerName; } else { // this refers to ourself, and can be a force fight with setEnclosingMethod if (outerName != null) { // only set if this is a direct member, otherwise taken from setEnclosingMethod setEnclosingClass(outerName); } } } @Override public void setEnclosingMethod (ClassFile cf, Object tag, String enclosingClassName, String enclosingMethodName, String descriptor) { setEnclosingClass(enclosingClassName); if (enclosingMethodName != null) { JVMClassInfo.this.setEnclosingMethod(enclosingMethodName + descriptor); } } @Override public void setInnerClassesDone (ClassFile cf, Object tag) { // we have to check if we allocated too many - see the mess above for (int i = 0; i < innerClassNames.length; i++) { innerClassNames = Misc.stripNullElements(innerClassNames); } } //--- source file @Override public void setSourceFile (ClassFile cf, Object tag, String fileName) { JVMClassInfo.this.setSourceFile(fileName); } //--- interfaces @Override public void setInterfaceCount (ClassFile cf, int ifcCount) { interfaceNames = new String[ifcCount]; } @Override public void setInterface (ClassFile cf, int ifcIndex, String ifcName) { interfaceNames[ifcIndex] = Types.getClassNameFromTypeName(ifcName); } //--- fields // unfortunately they are stored together in the ClassFile, i.e. we // have to split them up once we are done protected FieldInfo[] fields; protected FieldInfo curFi; // need to cache for attributes @Override public void setFieldCount (ClassFile cf, int fieldCount) { if (fieldCount > 0){ fields = new FieldInfo[fieldCount]; } else { fields = null; } } @Override public void setField (ClassFile cf, int fieldIndex, int accessFlags, String name, String descriptor) { FieldInfo fi = FieldInfo.create(name, descriptor, accessFlags); fields[fieldIndex] = fi; curFi = fi; // for attributes } @Override public void setFieldAttribute (ClassFile cf, int fieldIndex, int attrIndex, String name, int attrLength) { if (name == ClassFile.SIGNATURE_ATTR) { cf.parseSignatureAttr(this, curFi); } else if (name == ClassFile.CONST_VALUE_ATTR) { cf.parseConstValueAttr(this, curFi); } else if (name == ClassFile.RUNTIME_VISIBLE_ANNOTATIONS_ATTR) { cf.parseAnnotationsAttr(this, curFi); } else if (name == ClassFile.RUNTIME_INVISIBLE_ANNOTATIONS_ATTR) { //cf.parseAnnotationsAttr(this, curFi); } } @Override public void setConstantValue (ClassFile cf, Object tag, Object constVal) { curFi.setConstantValue(constVal); } @Override public void setFieldsDone (ClassFile cf) { setFields(fields); } //--- declaredMethods protected MethodInfo curMi; @Override public void setMethodCount (ClassFile cf, int methodCount) { methods = new LinkedHashMap<String, MethodInfo>(); } @Override public void setMethod (ClassFile cf, int methodIndex, int accessFlags, String name, String signature) { MethodInfo mi = MethodInfo.create(name, signature, accessFlags); curMi = mi; mi.linkToClass(JVMClassInfo.this); methods.put(mi.getUniqueName(), mi); if (attributor != null) { attributor.setMethodInfoAttributes(mi); } } @Override public void setMethodAttribute (ClassFile cf, int methodIndex, int attrIndex, String name, int attrLength) { if (name == ClassFile.CODE_ATTR) { cf.parseCodeAttr(this, curMi); } else if (name == ClassFile.SIGNATURE_ATTR) { cf.parseSignatureAttr(this, curMi); } else if (name == ClassFile.EXCEPTIONS_ATTR) { cf.parseExceptionAttr(this, curMi); } else if (name == ClassFile.RUNTIME_VISIBLE_ANNOTATIONS_ATTR) { cf.parseAnnotationsAttr(this, curMi); } else if (name == ClassFile.RUNTIME_INVISIBLE_ANNOTATIONS_ATTR) { //cf.parseAnnotationsAttr(this, curMi); } else if (name == ClassFile.RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS_ATTR) { cf.parseParameterAnnotationsAttr(this, curMi); } else if (name == ClassFile.RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS_ATTR) { //cf.parseParameterAnnotationsAttr(this, curMi); } } //--- current methods throws list protected String[] exceptions; @Override public void setExceptionCount (ClassFile cf, Object tag, int exceptionCount) { exceptions = new String[exceptionCount]; } @Override public void setException (ClassFile cf, Object tag, int exceptionIndex, String exceptionType) { exceptions[exceptionIndex] = Types.getClassNameFromTypeName(exceptionType); } @Override public void setExceptionsDone (ClassFile cf, Object tag) { curMi.setThrownExceptions(exceptions); } //--- current method exception handlers protected ExceptionHandler[] handlers; @Override public void setExceptionHandlerTableCount (ClassFile cf, Object tag, int exceptionTableCount) { handlers = new ExceptionHandler[exceptionTableCount]; } @Override public void setExceptionHandler (ClassFile cf, Object tag, int handlerIndex, int startPc, int endPc, int handlerPc, String catchType) { ExceptionHandler xh = new ExceptionHandler(catchType, startPc, endPc, handlerPc); handlers[handlerIndex] = xh; } @Override public void setExceptionHandlerTableDone (ClassFile cf, Object tag) { curMi.setExceptionHandlers(handlers); } //--- current method code @Override public void setCode (ClassFile cf, Object tag, int maxStack, int maxLocals, int codeLength) { curMi.setMaxLocals(maxLocals); curMi.setMaxStack(maxStack); cb.reset(cf, curMi); cf.parseBytecode(cb, tag, codeLength); cb.installCode(); } @Override public void setCodeAttribute (ClassFile cf, Object tag, int attrIndex, String name, int attrLength) { if (name == ClassFile.LINE_NUMBER_TABLE_ATTR) { cf.parseLineNumberTableAttr(this, tag); } else if (name == ClassFile.LOCAL_VAR_TABLE_ATTR) { cf.parseLocalVarTableAttr(this, tag); } } //--- current method line numbers protected int[] lines, startPcs; @Override public void setLineNumberTableCount (ClassFile cf, Object tag, int lineNumberCount) { lines = new int[lineNumberCount]; startPcs = new int[lineNumberCount]; } @Override public void setLineNumber (ClassFile cf, Object tag, int lineIndex, int lineNumber, int startPc) { lines[lineIndex] = lineNumber; startPcs[lineIndex] = startPc; } @Override public void setLineNumberTableDone (ClassFile cf, Object tag) { curMi.setLineNumbers(lines, startPcs); } //--- current method local variables protected LocalVarInfo[] localVars; @Override public void setLocalVarTableCount (ClassFile cf, Object tag, int localVarCount) { localVars = new LocalVarInfo[localVarCount]; } @Override public void setLocalVar (ClassFile cf, Object tag, int localVarIndex, String varName, String descriptor, int scopeStartPc, int scopeEndPc, int slotIndex) { LocalVarInfo lvi = new LocalVarInfo(varName, descriptor, "", scopeStartPc, scopeEndPc, slotIndex); localVars[localVarIndex] = lvi; } @Override public void setLocalVarTableDone (ClassFile cf, Object tag) { curMi.setLocalVarTable(localVars); } //--- annotations protected AnnotationInfo[] annotations; protected AnnotationInfo curAi; protected AnnotationInfo[][] parameterAnnotations; protected Object[] values; @Override public void setAnnotationCount (ClassFile cf, Object tag, int annotationCount) { annotations = new AnnotationInfo[annotationCount]; } @Override public void setAnnotationsDone (ClassFile cf, Object tag) { if (tag instanceof InfoObject) { ((InfoObject) tag).setAnnotations(annotations); } } @Override public void setParameterCount (ClassFile cf, Object tag, int parameterCount) { parameterAnnotations = new AnnotationInfo[parameterCount][]; } @Override public void setParameterAnnotationCount (ClassFile cf, Object tag, int paramIndex, int annotationCount) { annotations = new AnnotationInfo[annotationCount]; parameterAnnotations[paramIndex] = annotations; } @Override public void setParameterAnnotation (ClassFile cf, Object tag, int annotationIndex, String annotationType) { curAi = getResolvedAnnotationInfo(Types.getClassNameFromTypeName(annotationType)); annotations[annotationIndex] = curAi; } @Override public void setParametersDone (ClassFile cf, Object tag) { curMi.setParameterAnnotations(parameterAnnotations); } @Override public void setAnnotation (ClassFile cf, Object tag, int annotationIndex, String annotationType) { if (tag instanceof InfoObject) { curAi = getResolvedAnnotationInfo(Types.getClassNameFromTypeName(annotationType)); annotations[annotationIndex] = curAi; } } //--- AnnotationInfo entries @Override public void setAnnotationValueCount (ClassFile cf, Object tag, int annotationIndex, int nValuePairs) { // if we have values, we need to clone the defined annotation so that we can overwrite entries curAi = curAi.cloneForOverriddenValues(); annotations[annotationIndex] = curAi; } @Override public void setPrimitiveAnnotationValue (ClassFile cf, Object tag, int annotationIndex, int valueIndex, String elementName, int arrayIndex, Object val) { if (arrayIndex >= 0) { values[arrayIndex] = val; } else { curAi.setClonedEntryValue(elementName, val); } } @Override public void setStringAnnotationValue (ClassFile cf, Object tag, int annotationIndex, int valueIndex, String elementName, int arrayIndex, String val) { if (arrayIndex >= 0) { values[arrayIndex] = val; } else { curAi.setClonedEntryValue(elementName, val); } } @Override public void setClassAnnotationValue (ClassFile cf, Object tag, int annotationIndex, int valueIndex, String elementName, int arrayIndex, String typeName) { Object val = AnnotationInfo.getClassValue(typeName); if (arrayIndex >= 0) { values[arrayIndex] = val; } else { curAi.setClonedEntryValue(elementName, val); } } @Override public void setEnumAnnotationValue (ClassFile cf, Object tag, int annotationIndex, int valueIndex, String elementName, int arrayIndex, String enumType, String enumValue) { Object val = AnnotationInfo.getEnumValue(enumType, enumValue); if (arrayIndex >= 0) { values[arrayIndex] = val; } else { curAi.setClonedEntryValue(elementName, val); } } @Override public void setAnnotationValueElementCount (ClassFile cf, Object tag, int annotationIndex, int valueIndex, String elementName, int elementCount) { values = new Object[elementCount]; } @Override public void setAnnotationValueElementsDone (ClassFile cf, Object tag, int annotationIndex, int valueIndex, String elementName) { curAi.setClonedEntryValue(elementName, values); } //--- common attrs @Override public void setSignature (ClassFile cf, Object tag, String signature) { if (tag instanceof GenericSignatureHolder) { ((GenericSignatureHolder) tag).setGenericSignature(signature); } } } // since nested class init locking can explode the state space, we make it optional and controllable protected static boolean nestedInit; protected static StringSetMatcher includeNestedInit; protected static StringSetMatcher excludeNestedInit; protected static boolean init(Config config) { nestedInit = config.getBoolean("jvm.nested_init", false); if (nestedInit) { includeNestedInit = StringSetMatcher.getNonEmpty(config.getStringArray("jvm.nested_init.include")); excludeNestedInit = StringSetMatcher.getNonEmpty(config.getStringArray("jvm.nested_init.exclude")); } return true; } JVMClassInfo (FeatureExpr ctx, String name, ClassLoaderInfo cli, ClassFile cf, String srcUrl, JVMCodeBuilder cb) throws ClassParseException { super( name, cli, srcUrl); new Initializer( cf, cb); // we just need the ctor resolveAndLink(ctx); } //--- for annotation classinfos // called on the annotation classinfo @Override protected ClassInfo createAnnotationProxy (String proxyName){ return new JVMClassInfo (this, proxyName, classLoader, null); } // concrete proxy ctor protected JVMClassInfo (ClassInfo ciAnnotation, String proxyName, ClassLoaderInfo cli, String url) { super( ciAnnotation, proxyName, cli, url); } /** * This is called on the functional interface type. It creates a synthetic type which * implements the functional interface and contains a method capturing the behavior * of the lambda expression. */ @Override protected ClassInfo createFuncObjClassInfo (BootstrapMethodInfo bootstrapMethod, String name, String samUniqueName, String[] fieldTypesName) { return new JVMClassInfo(this, bootstrapMethod, name, samUniqueName, fieldTypesName); } protected JVMClassInfo (ClassInfo funcInterface, BootstrapMethodInfo bootstrapMethod, String name, String samUniqueName, String[] fieldTypesName) { super(funcInterface, bootstrapMethod, name, fieldTypesName); // creating a method corresponding to the single abstract method of the functional interface methods = new HashMap<String, MethodInfo>(); MethodInfo fiMethod = funcInterface.getInterfaceAbstractMethod(); int modifiers = fiMethod.getModifiers() & (~Modifier.ABSTRACT); int nLocals = fiMethod.getArgumentsSize(); int nOperands = this.nInstanceFields + nLocals; MethodInfo mi = new MethodInfo(fiMethod.getName(), fiMethod.getSignature(), modifiers, nLocals, nOperands); mi.linkToClass(this); methods.put(mi.getUniqueName(), mi); setLambdaDirectCallCode(mi, bootstrapMethod); try { resolveAndLink(null); } catch (ClassParseException e) { // we do not even get here - this a synthetic class, and at this point // the interfaces are already loaded. } } /** * perform initialization of this class and its not-yet-initialized superclasses (top down), * which includes calling clinit() methods * * This is overridden here to model a questionable yet consequential behavior of hotspot, which * is holding derived class locks when initializing base classes. The generic implementation in * ClassInfo uses non-nested locks (i.e. A.clinit() only synchronizes on A.class) and hence cannot * produce the same static init deadlocks as hotspot. In order to catch such defects we implement * nested locking here. * * The main difference is that the generic implementation only pushes DCSFs for required clinits * and otherwise doesn't lock anything. Here, we create one static init specific DCSF which wraps * all clinits in nested monitorenter/exits. We create this even if there is no clinit so that we * mimic hotspot locking. * * Note this scheme also enables us to get rid of the automatic clinit sync (they don't have * a 0x20 sync modifier in classfiles) * * @return true if client needs to re-execute because we pushed DirectCallStackFrames */ @Override public boolean initializeClass(FeatureExpr ctx, ThreadInfo ti) { if (needsInitialization(ti)) { if (nestedInit && StringSetMatcher.isMatch(name, includeNestedInit, excludeNestedInit)) { registerClass(ctx, ti); // this is recursively upwards int nOps = 2 * (getNumberOfSuperClasses() + 1); // this is just an upper bound for the number of operands we need MethodInfo miInitialize = new MethodInfo("[initializeClass]", "()V", Modifier.STATIC, 0, nOps); JVMDirectCallStackFrame frame = new JVMDirectCallStackFrame(ctx, miInitialize, null); JVMCodeBuilder cb = getSystemCodeBuilder(null, miInitialize); addClassInit(ti, frame, cb); // this is recursively upwards until we hit a initialized superclass cb.directcallreturn(); cb.installCode(); // this is normally initialized in the ctor, but at that point we don't have the code yet frame.setPC(new One<>(miInitialize.getFirstInsn())); ti.pushFrame(frame); return true; // client has to re-execute, we pushed a stackframe } else { // use generic initialization without nested locks (directly calling clinits) return super.initializeClass(ctx, ti); } } else { return false; // nothing to do } } protected void addClassInit (ThreadInfo ti, JVMDirectCallStackFrame frame, JVMCodeBuilder cb){ int clsObjRef = getClassObjectRef(); frame.pushRef(null, new One<>(clsObjRef)); cb.monitorenter(); if (superClass != null && superClass.needsInitialization(ti)) { ((JVMClassInfo) superClass).addClassInit(ti, frame, cb); // go recursive } if (getMethod("<clinit>()V", false) != null) { // do we have a clinit cb.invokeclinit(this); } else { cb.finishclinit(this); // we can't just do call ci.setInitialized() since that has to be deferred } frame.pushRef(null, new One<>(clsObjRef)); cb.monitorexit(); } //--- call processing protected JVMCodeBuilder getSystemCodeBuilder (ClassFile cf, MethodInfo mi){ JVMSystemClassLoaderInfo sysCl = (JVMSystemClassLoaderInfo) ClassLoaderInfo.getCurrentSystemClassLoader(); JVMCodeBuilder cb = sysCl.getSystemCodeBuilder(cf, mi); return cb; } /** * to be called from super proxy ctor * this needs to be in the VM specific ClassInfo because we need to create code */ @Override protected void setAnnotationValueGetterCode (MethodInfo pmi, FieldInfo fi){ JVMCodeBuilder cb = getSystemCodeBuilder(null, pmi); cb.aload(0); cb.getfield( pmi.getName(), name, pmi.getReturnType()); if (fi.isReference()) { cb.areturn(); } else { if (fi.getStorageSize() == 1) { cb.ireturn(); } else { cb.lreturn(); } } cb.installCode(); } @Override protected void setDirectCallCode (MethodInfo miDirectCall, MethodInfo miCallee){ JVMCodeBuilder cb = getSystemCodeBuilder(null, miDirectCall); String calleeName = miCallee.getName(); String calleeSig = miCallee.getSignature(); if (miCallee.isStatic()){ if (miCallee.isClinit()) { cb.invokeclinit(this); } else { cb.invokestatic( name, calleeName, calleeSig); } } else if (name.equals("<init>") || miCallee.isPrivate()){ cb.invokespecial( name, calleeName, calleeSig); } else { cb.invokevirtual( name, calleeName, calleeSig); } cb.directcallreturn(); cb.installCode(); } @Override protected void setNativeCallCode (NativeMethodInfo miNative){ JVMCodeBuilder cb = getSystemCodeBuilder(null, miNative); cb.executenative(miNative); cb.nativereturn(); cb.installCode(); } @Override protected void setRunStartCode (MethodInfo miStub, MethodInfo miRun){ JVMCodeBuilder cb = getSystemCodeBuilder(null, miStub); cb.runStart( miStub); cb.invokevirtual( name, miRun.getName(), miRun.getSignature()); cb.directcallreturn(); cb.installCode(); } /** * This method creates the body of the function object method that captures the * lambda behavior. */ @Override protected void setLambdaDirectCallCode (MethodInfo miDirectCall, BootstrapMethodInfo bootstrapMethod) { MethodInfo miCallee = bootstrapMethod.getLambdaBody(); String samSignature = bootstrapMethod.getSamDescriptor(); JVMCodeBuilder cb = getSystemCodeBuilder(null, miDirectCall); String calleeName = miCallee.getName(); String calleeSig = miCallee.getSignature(); ClassInfo callerCi = miDirectCall.getClassInfo(); // loading free variables, which are used in the body of the lambda // expression and captured by the lexical scope. These variables // are stored by the fields of the synthetic function object class int n = callerCi.getNumberOfInstanceFields(); for(int i=0; i<n; i++) { cb.aload(0); FieldInfo fi = callerCi.getInstanceField(i); cb.getfield(fi.getName(), callerCi.getName(), Types.getTypeSignature(fi.getSignature(), false)); } // adding bytecode instructions to load input parameters of the lambda expression n = miDirectCall.getArgumentsSize(); for(int i=1; i<n; i++) { cb.aload(i); } String calleeClass = miCallee.getClassName(); // adding the bytecode instruction to invoke lambda method switch (bootstrapMethod.getLambdaRefKind()) { case ClassFile.REF_INVOKESTATIC: cb.invokestatic(calleeClass, calleeName, calleeSig); break; case ClassFile.REF_INVOKEINTERFACE: cb.invokeinterface(calleeClass, calleeName, calleeSig); break; case ClassFile.REF_INVOKEVIRTUAL: cb.invokevirtual(calleeClass, calleeName, calleeSig); break; case ClassFile.REF_NEW_INVOKESPECIAL: cb.new_(calleeClass); cb.invokespecial(calleeClass, calleeName, calleeSig); break; case ClassFile.REF_INVOKESPECIAL: cb.invokespecial(calleeClass, calleeName, calleeSig); break; } String returnType = Types.getReturnTypeSignature(samSignature); int len = returnType.length(); char c = returnType.charAt(0); // adding a return statement for function object method if (len == 1) { switch (c) { case 'B': case 'I': case 'C': case 'Z': case 'S': cb.ireturn(); break; case 'D': cb.dreturn(); break; case 'J': cb.lreturn(); break; case 'F': cb.freturn(); break; case 'V': cb.return_(); break; } } else { cb.areturn(); } cb.installCode(); } // create a stack frame that has properly initialized arguments @Override public StackFrame createStackFrame (FeatureExpr ctx, ThreadInfo ti, MethodInfo callee){ if (callee.isMJI()){ NativeMethodInfo nativeCallee = (NativeMethodInfo) callee; JVMNativeStackFrame calleeFrame = new JVMNativeStackFrame( ctx, nativeCallee); calleeFrame.setArguments(ctx, ti); return calleeFrame; } else { JVMStackFrame calleeFrame = new JVMStackFrame(ctx, callee); calleeFrame.setCallArguments(ctx, ti); return calleeFrame; } } @Override public DirectCallStackFrame createDirectCallStackFrame (FeatureExpr ctx, ThreadInfo ti, MethodInfo miCallee, int nLocals){ int nOperands = miCallee.getNumberOfCallerStackSlots(); MethodInfo miDirect = new MethodInfo(miCallee, nLocals, nOperands); setDirectCallCode( miDirect, miCallee); return new JVMDirectCallStackFrame( ctx, miDirect, miCallee); } /** * while this is a normal DirectCallStackFrame, it has different code which has to be created here */ @Override public DirectCallStackFrame createRunStartStackFrame (FeatureExpr ctx, ThreadInfo ti, MethodInfo miRun){ MethodInfo miDirect = new MethodInfo( miRun, 0, 1); setRunStartCode( miDirect, miRun); return new JVMDirectCallStackFrame( ctx, miDirect, miRun); } }