Mercurial > hg > Members > kono > jpf-core
view src/main/gov/nasa/jpf/vm/Instruction.java @ 23:db918c531e6d
streamlined class init, which was a mixed case of registerClass()/initializeClass() and pushRequiredClinits(). Now it is a single initializeClass(ti) method which combines the previous initializeClass(), pushRequiredClinits() and pushClinit() methods. The reason for combining these is the forthcoming replacement of separately locked clinits from different DirectCallStackFrames with a single synthetic frame that calls clinits from nested synchronized blocks. This is required to model hotspot, which does cause deadlocks with concurrent init of classes that cause subclass init during their clinit executions.
author | Peter Mehlitz <Peter.C.Mehlitz@nasa.gov> |
---|---|
date | Wed, 15 Apr 2015 22:40:21 -0700 |
parents | 61d41facf527 |
children |
line wrap: on
line source
/* * Copyright (C) 2014, United States Government, as represented by the * Administrator of the National Aeronautics and Space Administration. * All rights reserved. * * The Java Pathfinder core (jpf-core) platform is licensed under the * Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0. * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package gov.nasa.jpf.vm; import gov.nasa.jpf.util.ObjectList; import gov.nasa.jpf.util.Source; import gov.nasa.jpf.vm.bytecode.InstructionInterface; /** * common root of all JPF bytecode instruction classes * */ public abstract class Instruction implements Cloneable, InstructionInterface { protected int insnIndex; // code[] index of instruction protected int position; // accumulated bytecode position (prev pos + prev bc-length) protected MethodInfo mi; // the method this insn belongs to // property/mode specific attributes protected Object attr; // this is for changing from InstructionInterface types to Instruction types @Override public Instruction asInstruction(){ return this; } // to allow a classname and methodname context for each instruction public void setContext(String className, String methodName, int lineNumber, int offset) { } /** * is this the first instruction in a method */ @Override public boolean isFirstInstruction() { return (insnIndex == 0); } /** * answer if this is a potential loop closing jump */ @Override public boolean isBackJump() { return false; } /** * is this instruction part of a monitorenter code pattern */ public boolean isMonitorEnterPrologue(){ return false; } /** * is this one of our own, artificial insns? */ @Override public boolean isExtendedInstruction() { return false; } @Override public MethodInfo getMethodInfo() { return mi; } /** * that's used for explicit construction of MethodInfos (synthetic methods) */ public void setMethodInfo(MethodInfo mi) { this.mi = mi; } /** * this returns the instruction at the following code insnIndex within the same * method, which might or might not be the next one to enter (branches, overlay calls etc.). */ @Override public Instruction getNext() { return mi.getInstruction(insnIndex + 1); } @Override public int getInstructionIndex() { return insnIndex; } @Override public int getPosition() { return position; } public void setLocation(int insnIdx, int pos) { insnIndex = insnIdx; position = pos; } /** * return the length in bytes of this instruction. * override if this is not 1 */ @Override public int getLength() { return 1; } @Override public Instruction getPrev() { if (insnIndex > 0) { return mi.getInstruction(insnIndex - 1); } else { return null; } } /** * this is for listeners that process instructionExecuted(), but need to * determine if there was a CG registration, an overlayed direct call * (like clinit) etc. * The easy case is the instruction not having been executed yet, in * which case ti.getNextPC() == null * There are two cases for re-execution: either nextPC was set to the * same insn (which is what CG creators usually use), or somebody just * pushed another stackframe that executes something which will return to the * same insn (that is what automatic <clinit> calls and the like do - we call * it overlays) */ @Override public boolean isCompleted(ThreadInfo ti) { Instruction nextPc = ti.getNextPC(); if (nextPc == null) { return ti.isTerminated(); } else { return (nextPc != this) && (ti.getStackFrameExecuting(this, 1) == null); } // <2do> how do we account for exceptions? } /** * this method can be overridden if instruction classes have to store * information for instructionExecuted() notifications, and this information * should not be stored persistent to avoid memory leaks (e.g. via traces). * Called by ThreadInfo.executeInstruction */ public void cleanupTransients(){ // nothing here } public boolean isSchedulingRelevant(SystemState ss, KernelState ks, ThreadInfo ti) { return false; } /** * this is the real workhorse * returns next instruction to enter in this thread * * <2do> it's unfortunate we roll every side effect into this method, because * it diminishes the value of the 'executeInstruction' notification: all * insns that require some sort of late binding (InvokeVirtual, GetField, ..) * are not yet fully analyzable (e.g. the callee of InvokeVirtuals is not * known yet), putting the burden of duplicating the related code of * enter() in the listener. It would be better if we factor this * 'prepareExecution' out of enter() */ @Override public abstract Instruction execute(ThreadInfo ti); @Override public String toString() { return getMnemonic(); } /** * this can contain additional info that was gathered/cached during execution */ @Override public String toPostExecString(){ return toString(); } @Override public String getMnemonic() { String s = getClass().getSimpleName(); return s.toLowerCase(); } @Override public int getLineNumber() { return mi.getLineNumber(this); } @Override public String getSourceLine() { ClassInfo ci = mi.getClassInfo(); if (ci != null) { int line = mi.getLineNumber(this); String fileName = ci.getSourceFileName(); Source src = Source.getSource(fileName); if (src != null) { String srcLine = src.getLine(line); if (srcLine != null) { return srcLine; } } } return null; } /** * this is for debugging/logging if we always want something back telling * us where this insn came from */ public String getSourceOrLocation(){ ClassInfo ci = mi.getClassInfo(); if (ci != null) { int line = mi.getLineNumber(this); String file = ci.getSourceFileName(); Source src = Source.getSource(file); if (src != null) { String srcLine = src.getLine(line); if (srcLine != null) { return srcLine; } } return "(" + file + ':' + line + ')'; // fallback } else { return "[synthetic] " + mi.getName(); } } /** * this returns a "pathname:line" string */ @Override public String getFileLocation() { ClassInfo ci = mi.getClassInfo(); if (ci != null) { int line = mi.getLineNumber(this); String fname = ci.getSourceFileName(); return (fname + ':' + line); } else { return "[synthetic] " + mi.getName(); } } /** * this returns a "filename:line" string */ @Override public String getFilePos() { String file = null; int line = -1; ClassInfo ci = mi.getClassInfo(); if (ci != null){ line = mi.getLineNumber(this); file = ci.getSourceFileName(); if (file != null){ int i = file.lastIndexOf('/'); // ClassInfo.sourceFileName is using '/' if (i >= 0) { file = file.substring(i + 1); } } } if (file != null) { if (line != -1){ return (file + ':' + line); } else { return file; } } else { return ("pc " + position); } } /** * this returns a "class.method(line)" string */ @Override public String getSourceLocation() { ClassInfo ci = mi.getClassInfo(); if (ci != null) { String s = ci.getName() + '.' + mi.getName() + '(' + getFilePos() + ')'; return s; } else { return null; } } public void init(MethodInfo mi, int offset, int position) { this.mi = mi; this.insnIndex = offset; this.position = position; } /** * this is a misnomer - we actually push the clinit calls here in case * we need some. 'causedClinitCalls' might be more appropriate, but it is * used in a number of external projects */ public boolean requiresClinitExecution(ThreadInfo ti, ClassInfo ci) { return ci.initializeClass(ti); } /** * this is returning the next Instruction to enter, to be called to obtain * the return value of enter() if this is not a branch insn * * Be aware of that we might have had exceptions caused by our execution * (-> lower frame), or we might have had overlaid calls (-> higher frame), * i.e. we can't simply assume it's the following insn. We have to * acquire this through the top frame of the ThreadInfo. * * note: the System.exit() problem should be gone, now that it is implemented * as ThreadInfo state (TERMINATED), rather than purged stacks */ @Override public Instruction getNext (ThreadInfo ti) { return ti.getPC().getNext(); } //--- the generic attribute API @Override public boolean hasAttr () { return (attr != null); } @Override public boolean hasAttr (Class<?> attrType){ return ObjectList.containsType(attr, attrType); } /** * this returns all of them - use either if you know there will be only * one attribute at a time, or check/process result with ObjectList */ @Override public Object getAttr(){ return attr; } /** * this replaces all of them - use only if you know * - there will be only one attribute at a time * - you obtained the value you set by a previous getXAttr() * - you constructed a multi value list with ObjectList.createList() */ @Override public void setAttr (Object a){ attr = ObjectList.set(attr, a); } @Override public void addAttr (Object a){ attr = ObjectList.add(attr, a); } @Override public void removeAttr (Object a){ attr = ObjectList.remove(attr, a); } @Override public void replaceAttr (Object oldAttr, Object newAttr){ attr = ObjectList.replace(attr, oldAttr, newAttr); } /** * this only returns the first attr of this type, there can be more * if you don't use client private types or the provided type is too general */ @Override public <T> T getAttr (Class<T> attrType) { return ObjectList.getFirst(attr, attrType); } @Override public <T> T getNextAttr (Class<T> attrType, Object prev) { return ObjectList.getNext(attr, attrType, prev); } @Override public ObjectList.Iterator attrIterator(){ return ObjectList.iterator(attr); } @Override public <T> ObjectList.TypedIterator<T> attrIterator(Class<T> attrType){ return ObjectList.typedIterator(attr, attrType); } // -- end attrs -- /** * this is overridden by any Instruction that use a cache for class or * method to provide a type safe cloning */ public Instruction typeSafeClone(MethodInfo mi) { Instruction clone = null; try { clone = (Instruction) super.clone(); // reset the method that this insn belongs to clone.mi = mi; } catch (CloneNotSupportedException e) { e.printStackTrace(); } return clone; } }