// // Copyright (C) 2006 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.bytecode; import cmu.conditional.Conditional; import cmu.conditional.One; import de.fosd.typechef.featureexpr.FeatureExpr; import de.fosd.typechef.featureexpr.FeatureExprFactory; import gov.nasa.jpf.vm.ChoiceGenerator; import gov.nasa.jpf.vm.ClassInfo; import gov.nasa.jpf.vm.ElementInfo; import gov.nasa.jpf.vm.Instruction; import gov.nasa.jpf.vm.MethodInfo; import gov.nasa.jpf.vm.ThreadInfo; import gov.nasa.jpf.vm.VM; /** * this is an artificial bytecode that we use to deal with the particularities of * <clinit> calls, which are never in the loaded bytecode but always directly called by * the VM. The most obvious difference is that <clinit> execution does not trigger * class initialization. * A more subtle difference is that we save a wait() - if a class * is concurrently initialized, both enter INVOKECLINIT (i.e. compete and sync for/on * the class object lock), but once the second thread gets resumed and detects that the * class is now initialized (by the first thread), it skips the method execution and * returns right away (after deregistering as a lock contender). That's kind of hackish, * but we have no method to do the wait in, unless we significantly complicate the * direct call stubs, which would obfuscate observability (debugging dynamically * generated code isn't very appealing). */ public class INVOKECLINIT extends INVOKESTATIC { public INVOKECLINIT (ClassInfo ci){ super(ci.getSignature(), "<clinit>", "()V"); } public Conditional<Instruction> execute (FeatureExpr ctx, ThreadInfo ti) { MethodInfo callee = getInvokedMethod(ctx, ti); ClassInfo ci = callee.getClassInfo(); ElementInfo ei = ci.getModifiableClassObject(); if (!ti.isFirstStepInsn()) { // if we can't acquire the lock, it means somebody else is initializing concurrently if (!ei.canLock(ti)) { //ei = ei.getInstanceWithUpdatedSharedness(ti); ei.block(ti); VM vm = ti.getVM(); ChoiceGenerator<?> cg = vm.getSchedulerFactory().createSyncMethodEnterCG(ei, ti); if (vm.setNextChoiceGenerator(cg)){ return new One<Instruction>(this); // repeat exec, keep insn on stack } } } else { // re-execution after being blocked // if we got here, we can enter, and have the lock but there still might have been // another thread that passed us with the clinit if (!ci.needsInitialization(ti)) { return new One<>(getNext()); } } // JM initialize without condition setupCallee(FeatureExprFactory.True(), ti, callee); // this creates, initializes and pushes the callee StackFrame return ti.getPC(); // we can't just return the first callee insn if a listener throws an exception } public boolean isExtendedInstruction() { return true; } public static final int OPCODE = 256; public int getByteCode () { return OPCODE; } public void accept(InstructionVisitor insVisitor) { insVisitor.visit(this); } }