/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development * and Distribution License("CDDL") (collectively, the "License"). You * may not use this file except in compliance with the License. You can * obtain a copy of the License at * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html * or packager/legal/LICENSE.txt. See the License for the specific * language governing permissions and limitations under the License. * * When distributing the software, include this License Header Notice in each * file and include the License file at packager/legal/LICENSE.txt. * * GPL Classpath Exception: * Oracle designates this particular file as subject to the "Classpath" * exception as provided by Oracle in the GPL Version 2 section of the License * file that accompanied this code. * * Modifications: * If applicable, add the following below the License Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyright [year] [name of copyright owner]" * * Contributor(s): * If you wish your version of this file to be governed by only the CDDL or * only the GPL Version 2, indicate your decision by adding "[Contributor] * elects to include this software in this distribution under the [CDDL or GPL * Version 2] license." If you don't indicate a single choice of license, a * recipient has the option to distribute your version of this file under * either the CDDL, the GPL Version 2 or to extend the choice of license to * its licensees as provided above. However, if you add GPL Version 2 code * and therefore, elected the GPL Version 2 license, then the option applies * only if the new code is made subject to such option by the copyright * holder. */ package com.sun.jdo.api.persistence.enhancer.impl; import java.util.Map; import java.util.HashMap; import java.util.Vector; import java.util.Stack; import java.util.Enumeration; import com.sun.jdo.api.persistence.enhancer.classfile.*; import com.sun.jdo.api.persistence.enhancer.util.Support; import com.sun.jdo.api.persistence.enhancer.util.InternalError; import com.sun.jdo.api.persistence.enhancer.util.ClassFileSource; //@olsen: added import import com.sun.jdo.api.persistence.enhancer.meta.JDOMetaData; //@olsen: cosmetics //@olsen: moved: this class -> package impl //@olsen: subst: (object)state -> flags //@olsen: subst: JDOFlags -> jdoFlags //@olsen: subst: JDO[gs]etFlags -> jdo[GS]etFlags //@olsen: subst: (object)reference -> stateManager //@olsen: subst: [Nn]eedsJDORefMethods -> [Nn]eedsJDOStateManagerMethods //@olsen: subst: JDORef -> jdoStateManager //@olsen: subst: JDO[gs]etRef -> jdo[GS]etStateManager //@olsen: subst: [iI]Persistent -> [pP]ersistenceCapable //@olsen: subst: PersistentAux -> StateManager //@olsen: subst: jdo/ -> com/sun/forte4j/persistence/internal/ //@olsen: subst: /* ... */ -> // ... //@olsen: subst: FilterEnv -> Environment //@olsen: dropped parameter 'Environment env', use association instead //@olsen: subst: Hashtable -> Map, HashMap //@olsen: subst: absolut jdo types and names -> constants from JDOMetaData //@olsen: subst: .classControl(). -> . //@olsen: subst: noteList -> note //@olsen: added: support for I18N //@olsen: subst: FilterError -> UserException, affirm() //@olsen: removed: proprietary support for HashCode //@olsen: removed: support for [No]AnnotateField //@olsen: removed: old, disabled ODI code /* * The current code annotation strategy * 1) getfield instructions operating on persistent types cause a fetch * 2) putfield instructions operating on persistent types cause a dirty * 3) fetches which can be identified to be a fetch of "this" are moved * to the start of the method. * 3) dirties which can be identified to be a dirty of "this" are moved * to the start of the method only if the code path to the dirty is * unconditional. * 4) Array loads cause array fetches * 5) Array stores cause array dirtys * 6) Each array fetch/dirty call which occurs in a loop construct * is allocated a dedicated local variable which is used to cache * the last array dirtied or fetched by the instruction in order to * minimize the overhead in array manipulation within a loop. * 7) In calls to methods in non-persistence-aware classes which are * declared to take array parameters, the array parameter is fetched. * If the called code stores to the array, the user must manually * annotate. * 8) Certain method invocations trigger fetches or dirties of arguments. * These special cases are listed in InvokeAnnotation.java. * * Possible Alternative Code Annotation strategy * 1) non-static non-private methods always fetch/dirty "this" * 2) non-static private methods never fetch but may dirty "this" * 3) invocations of private methods from static methods fetch the target * 4) invocations of private methods from non-static methods fetch the * target if it can't be identified as "this" * 5) putfields always cause dirtying of the target but if the * target is known to be this, promote to a dirty of this. * 6) getfields only cause fetching of the target if the * target is not known to be this * 7) Array loads cause array fetches * 8) Array stores cause array dirtys */ /** * MethodAnnotater controls the code annotation for a single method * within a class. */ //^olsen: move code -> MethodAction class MethodAnnotater extends Support implements AnnotationConstants { //@olsen: made final private final ClassAction ca; //@olsen: made final private final ClassMethod method; //@olsen: made final private final ConstantPool pool; /* Central repository for the options and classes */ //@olsen: added association //@olsen: made final private final Environment env; /* What types of annotation will be done on the method? */ private int annotate; /* List of single element register values (Integer) for temporaries */ private Vector tmpRegisters; /* List of double element register values (Integer) for temporaries */ private Vector tmpDoubleRegisters; /* List of single word register values which cache fetches/stores * Each of these registers must be initialized to null at the start of * the method */ //@olsen: disabled feature /* private Vector caches; */ /* The maximum amount of stack needed by any specific annotation sequence, * less the amount of stack which the annotated instruction is known to * need, if any */ private short annotationStack = 0; /* If true, the method will contain an unconditional fetch(this) */ //@olsen: disabled feature /* private boolean fetchThis = false; */ /* If true, the method will contain an unconditional dirty(this) */ //@olsen: disabled feature /* private boolean dirtyThis = false; */ /* Table mapping Insn to InsnNote - allows annotation computations to * be attached to instructions non-intrusively */ private Map insnNotes = new HashMap(11); /* The largest loop contained within the method, or null. */ //@olsen: disabled feature /* private Loop largestLoop; */ // package accessors /** * Is any annotation needed for this method? The result of this * method isn't valid until after checkMethod has been run. */ boolean needsAnnotation() { return annotate != 0; } /** * Constructor */ //@olsen: added parameter 'env' for association MethodAnnotater(ClassAction ca, ClassMethod method, Environment env) { this.ca = ca; this.method = method; this.env = env; this.pool = ca.classFile().pool(); } // --------------------------------------------------------------------------- /** * Examine the method to determine what sort of annotations are needed */ void checkMethod() { //@olsen: added printing output env.message( "checking method " + ca.userClassName()//NOI18N + "." + method.name().asString()//NOI18N + Descriptor.userMethodArgs(method.signature().asString())); //@olsen: cosmetics annotate = 0; final CodeAttribute codeAttr = method.codeAttribute(); if (codeAttr == null) { return; } //^olsen: make robust /* if (isAnnotated(codeAttr)) { env.message("Method " + ca.userClassName() + "." + method.name().asString() + Descriptor.userMethodArgs(method.signature().asString()) + " is already annotated."); return; } */ // look for certain special cases to avoid //@olsen: cosmetics if (avoidAnnotation()) { return; } checkCode(codeAttr); //@olsen: disabled feature /* if (!avoidAnnotation()) { largestLoop = Loop.checkLoops(codeAttr.theCode()); checkCode(codeAttr); } else if (methodIsPersistentFinalize()) { annotate = MakeThisTransient; } */ } /** * Check to see if the code attribute contains any calls to * Implementaiton.fetch, or Implementation.dirty */ //^olsen: make robust /* private boolean isAnnotated(CodeAttribute codeAttr) { for (Insn insn = codeAttr.theCode(); insn != null; insn = insn.next()) { // All non-interface method invocations are InsnConstOp if (insn instanceof InsnConstOp) { InsnConstOp coInsn = (InsnConstOp) insn; ConstBasic operand = coInsn.value(); if (operand instanceof ConstMethodRef) { // A method invocation of some sort ConstMethodRef methRef = (ConstMethodRef) operand; if (methRef.className().asString().equals("com/sun/forte4j/persistence/internal/Implementation")) { // A method invocation against class Persistent ConstNameAndType nt = methRef.nameAndType(); String ntName = nt.name().asString(); if (ntName.equals("fetch") || ntName.equals("dirty")) // A fetch or a dirty call return true; } } } } return false; } */ /** * Check to see if this method is an initializer. */ //@olsen: disabled feature /* private boolean methodIsInitializer() { String methName = method.name().asString(); //^olsen: check for serialization return (methName.equals("<init>") || (methName.equals("readObject") && method.signature().asString().equals( "(Ljava/io/ObjectInputStream;)V"))); } */ /** * Check to see if this method is a finalize method. */ //@olsen: disabled feature /* private boolean methodIsFinalize() { return (method.name().asString().equals("finalize") && method.signature().asString().equals("()V") && !method.isStatic()); } */ /** * Check to see if this method is an persistent finalize. */ //@olsen: disabled feature /* private boolean methodIsPersistentFinalize() { return (methodIsFinalize() && ca.persistCapable()); } */ /** * Check to see if this is a special case that should not be * annotated. */ private boolean avoidAnnotation() { //@olsen: cosmetics //@olsen: allow for annotating initializers+finalizers /* final String methodName = method.name().asString(); final String methodSig = method.signature().asString(); if (methodName.equals("<clinit>") || methodIsFinalize()) // Never annotate class initializers or finalizers return true; */ //^olsen: enable feature, rather use HashMap for lookup //@olsen: disabled feature /* if (ca.persistCapable()) { if ((methodName.equals("initializeContents") && methodSig.equals("(Lcom/sun/forte4j/persistence/internal/ObjectContents;)V")) || (methodName.equals("flushContents") && methodSig.equals("(Lcom/sun/forte4j/persistence/internal/ObjectContents;)V")) || (methodName.equals("clearContents") && methodSig.equals("()V")) || (methodName.equals("postInitializeContents") && methodSig.equals("()V")) || (methodName.equals("preFlushContents") && methodSig.equals("()V")) || (methodName.equals("preClearContents") && methodSig.equals("()V")) || (methodName.equals("jdoGetStateManager") && methodSig.equals("()Lcom/sun/forte4j/persistence/internal/StateManager;")) || (methodName.equals("jdoSetStateManager") && methodSig.equals("(Lcom/sun/forte4j/persistence/internal/StateManager;)V")) || (methodName.equals("jdoGetFlags") && methodSig.equals("()B")) || (methodName.equals("jdoSetFlags") && methodSig.equals("(B)V"))) // This is one of the special persistence actions. // Don't annotate it return true; } */ return false; } // --------------------------------------------------------------------------- /** * Check the code attribute for possible annotations */ //^olsen: move code to inner class ?! void checkCode(CodeAttribute codeAttr) { //@olsen: cosmetics Insn firstInsn = codeAttr.theCode(); // mark branch targets so we can distinguish them from // targets which exist for the benefit of line numbers, // local variables, etc. for (Insn markInsn = firstInsn; markInsn != null; markInsn = markInsn.next()) { markInsn.markTargets(); } int allFlags = 0; //@olsen: disabled feature /* boolean branchesSeen = false; */ for (Insn insn = firstInsn; insn != null; insn = insn.next() ) { InsnNote note = null; switch(insn.opcode()) { //@olsen: disabled feature /* case opc_invokestatic: case opc_invokespecial: case opc_invokevirtual: case opc_invokeinterface: note = noteInvokeAnnotation(insn); break; */ case opc_getfield: note = noteGetFieldAnnotation(insn); break; case opc_putfield: note = notePutFieldAnnotation(insn); break; //@olsen: disabled feature /* case opc_aaload: case opc_baload: case opc_caload: case opc_saload: case opc_iaload: case opc_laload: case opc_faload: case opc_daload: note = noteArrayLoadAnnotation(insn); break; case opc_aastore: case opc_bastore: case opc_castore: case opc_sastore: case opc_iastore: case opc_lastore: case opc_fastore: case opc_dastore: note = noteArrayStoreAnnotation(insn); break; */ default: break; } if (note != null) { addNoteList(note); //@olsen: ensured to use single note only (as instantiated) affirm((note.next() == null), "Number of annotation notes for instruction > 1.");//NOI18N allFlags |= note.insnFlags; //@olsen: ensured to use single note only (as instantiated) /* for (InsnNote aNote = note; aNote != null; aNote = aNote.next()) { //@olsen: disabled feature /// if (branchesSeen == false) aNote.insnFlags |= Unconditional; /// //@olsen: disabled feature /// if (largestLoop != null && largestLoop.contains(insn)) aNote.insnFlags |= InLoop; /// //@olsen: disabled feature /// // annotating based on thisOptimization will be done later if (aNote.dirtyThis() && aNote.unconditional()) dirtyThis = true; else if (aNote.dirtyThis() || aNote.fetchThis()) fetchThis = true; /// allFlags |= aNote.insnFlags; } */ } //@olsen: disabled feature /* if (insn.branches()) branchesSeen = true; */ } //@olsen: disabled feature /* if (methodIsInitializer()) { // An inititalizer - either force the fetchThis, dirtyThis flags // on or off. if (env.doInitializerOptimization()) { // turn on the fetchThis, dirtyThis flags to inhibit fetches // and stores of this if enabled. We won't really insert the // fetch/dirty, since it isn't needed. fetchThis = true; dirtyThis = true; } else { // Make sure that the fetchThis, dirtyThis flags are turned off fetchThis = false; dirtyThis = false; } } */ //^olsen: prepare for inheritance on PC classes //@olsen: check for annotating of clone() final String methodName = method.name().asString(); final String methodSig = method.signature().asString(); //^olsen: annotate persistence-capable root classes only // until the JDO spec precisely defines how to treat // user-defined clone methods in transient classes final boolean implementsPersistence = ca.getImplementsPersistence(); if (false) { System.out.println(" check for annotating clone()");//NOI18N System.out.println(" methodName = " + methodName);//NOI18N System.out.println(" methodSig = " + methodSig);//NOI18N System.out.println(" implementsPersistence = "//NOI18N + implementsPersistence); } if (methodName.equals("clone")//NOI18N && methodSig.equals("()Ljava/lang/Object;")//NOI18N && implementsPersistence) { //^olsen: rather scan for 'invokespecial clone()Ljava/lang/Object;' // in instruction loop above allFlags |= SuperClone; } //@olsen: disabled feature /* if (methodName.equals("clone") && methodSig.equals("()Ljava/lang/Object;") && ca.persistCapable()) { allFlags |= FetchThis; fetchThis = true; } */ annotate = allFlags; } /** * Make note of annotations if needed for a method invocation instruction. */ //@olsen: disabled feature /* private InsnNote noteInvokeAnnotation(Insn insn) { int flags = 0; ConstBasicMemberRef methRef = (ConstBasicMemberRef) ((InsnConstOp)insn).value(); InsnArgNote note = null; for (InvokeAnnotation invAnn = InvokeAnnotation.checkInvoke(methRef, env); invAnn != null; invAnn = invAnn.next()) { int thisFlags = 0; Insn dep = findArgDepositer(insn, invAnn.whichArg()); if (dep != null && dep.opcode() == opc_aload_0 && !method.isStatic()) { if ((invAnn.annotateHow() & DirtyAny) != 0) thisFlags = DirtyThis; else thisFlags = FetchThis; } else thisFlags = invAnn.annotateHow(); InsnArgNote newNote = new InsnArgNote(insn, thisFlags, invAnn.whichArg(), Descriptor.extractArgSig( methRef.nameAndType().signature().asString())); // sort in order of decreasing stack depth if (note == null || note.arg() < newNote.arg()) { newNote.nextNote = note; note = newNote; } else { InsnArgNote aNote = note; while (aNote.nextNote != null && aNote.nextNote.arg() > newNote.arg()) aNote = aNote.nextNote; newNote.nextNote = aNote.nextNote; aNote.nextNote = newNote; } } return note; } */ /** * make note of annotations if needed for the getField instruction. */ //^olsen: merge code with notePutFieldAnnotation() ?! private InsnNote noteGetFieldAnnotation(Insn insn) { //@olsen: cosmetics final InsnConstOp getFieldInsn = (InsnConstOp)insn; final ConstFieldRef fieldRef = (ConstFieldRef)getFieldInsn.value(); final String fieldOf = fieldRef.className().asString(); //@olsen: changed to use JDOMetaData final String fieldName = fieldRef.nameAndType().name().asString(); final JDOMetaData meta = env.getJDOMetaData(); if (!meta.isPersistentField(fieldOf, fieldName)) return null; //@olsen: disabled feature /* final ClassControl cc = env.findClass(fieldOf); if (cc == null || !cc.persistCapable()) return null; */ //@olsen: added checks final boolean dfgField = meta.isDefaultFetchGroupField(fieldOf, fieldName); final boolean pkField = meta.isPrimaryKeyField(fieldOf, fieldName); final int fieldIndex = meta.getFieldNo(fieldOf, fieldName); final String targetPCRootClass = meta.getPersistenceCapableRootClass(fieldOf); int flags = 0; //@olsen: added variables final String fieldSig = fieldRef.nameAndType().signature().asString(); // there's no field value on the stack yet final int stackArgSize = 0; //@olsen: added println() for debugging if (false) { System.out.println(" get field "//NOI18N + fieldOf + "." + fieldName//NOI18N + "[" + fieldIndex + "]"//NOI18N + "<" + fieldSig + ">"//NOI18N + " : p"//NOI18N + (dfgField ? ",dfg" : ",!dfg")//NOI18N + (pkField ? ",pk" : ",!pk")//NOI18N + ";");//NOI18N } Insn dep = findArgDepositer(insn, stackArgSize); if (dep != null && dep.opcode() == opc_aload_0 && !method.isStatic()) // This represents a fetch of "this" flags |= FetchThis; else flags |= FetchPersistent; //@olsen: added test if (dfgField) flags |= DFGField; //@olsen: added test if (pkField) flags |= PKField; //@olsen: changed to use JDOMetaData return new InsnNote(insn, flags, stackArgSize, fieldSig, fieldOf, fieldName, fieldIndex, targetPCRootClass); //@olsen: disabled feature /* return new InsnNote(insn, flags, 0, "", cc.action()); */ } /** * Generate annotations if needed for the putField instruction. */ //^olsen: merge code with noteGetFieldAnnotation() ?! private InsnNote notePutFieldAnnotation(Insn insn) { //@olsen: cosmetics final InsnConstOp putFieldInsn = (InsnConstOp)insn; final ConstFieldRef fieldRef = (ConstFieldRef)putFieldInsn.value(); final String fieldOf = fieldRef.className().asString(); //@olsen: changed to use JDOMetaData final String fieldName = fieldRef.nameAndType().name().asString(); final JDOMetaData meta = env.getJDOMetaData(); if (!meta.isPersistentField(fieldOf, fieldName)) return null; //@olsen: disabled feature /* final ClassControl cc = env.findClass(fieldOf); if (cc == null || !cc.persistCapable()) return null; */ //@olsen: added checks final boolean dfgField = meta.isDefaultFetchGroupField(fieldOf, fieldName); final boolean pkField = meta.isPrimaryKeyField(fieldOf, fieldName); final int fieldIndex = meta.getFieldNo(fieldOf, fieldName); final String targetPCRootClass = meta.getPersistenceCapableRootClass(fieldOf); int flags = 0; //@olsen: added variables final String fieldSig = fieldRef.nameAndType().signature().asString(); // size of field value on the stack final int stackArgSize = (fieldSig.equals("J") || fieldSig.equals("D")) ? 2 : 1;//NOI18N //@olsen: added println() for debugging if (false) { System.out.println(" put field "//NOI18N + fieldOf + "." + fieldName//NOI18N + "[" + fieldIndex + "]"//NOI18N + "<" + fieldSig + ">"//NOI18N + " : p"//NOI18N + (dfgField ? ",dfg" : ",!dfg")//NOI18N + (pkField ? ",pk" : ",!pk")//NOI18N + ";");//NOI18N } Insn dep = findArgDepositer(insn, stackArgSize); if (dep != null && dep.opcode() == opc_aload_0 && !method.isStatic()) // This represents a dirtyfication of "this" flags |= DirtyThis; else flags |= DirtyPersistent; //@olsen: added test if (dfgField) flags |= DFGField; //@olsen: added test if (pkField) flags |= PKField; //@olsen: changed to use JDOMetaData return new InsnNote(insn, flags, stackArgSize, fieldSig, fieldOf, fieldName, fieldIndex, targetPCRootClass); //@olsen: disabled feature /* return new InsnNote(insn, flags, stackArgSize, fieldSig, cc.action()); */ } /** * Generate annotations if needed for the arrayLoad instruction. */ //@olsen: disabled feature /* private InsnNote noteArrayLoadAnnotation(Insn insn) { int arrayFetchType = 0; switch(insn.opcode()) { case opc_aaload: arrayFetchType = ArrayTypeObject; break; case opc_caload: arrayFetchType = ArrayTypeChar; break; case opc_saload: arrayFetchType = ArrayTypeShort; break; case opc_iaload: arrayFetchType = ArrayTypeInt; break; case opc_laload: arrayFetchType = ArrayTypeLong; break; case opc_faload: arrayFetchType = ArrayTypeFloat; break; case opc_daload: arrayFetchType = ArrayTypeDouble; break; case opc_baload: // Unfortunately, both byte arrays and boolean arrays are accessed // using the same instruction so don't attempt to infer the array // element type for these. break; } return new InsnNote(insn, FetchArray | arrayFetchType, 1, "I", null); } */ //@olsen: disabled feature /* private InsnNote noteArrayStoreAnnotation(Insn insn) { int valueType = Insn.loadStoreDataType(insn.opcode()); int valueSize = Descriptor.elementSize(valueType); String stackSig = "I" + Descriptor.elementSig(valueType); // Compute the array store type for completeness. The generated // annotation currently doesn't use this information because there // are no array element type-specific overloads of dirty() but // perhaps we'll add them at some point. int arrayStoreType = 0; switch(insn.opcode()) { case opc_aastore: arrayStoreType = ArrayTypeObject; break; case opc_castore: arrayStoreType = ArrayTypeChar; break; case opc_sastore: arrayStoreType = ArrayTypeShort; break; case opc_iastore: arrayStoreType = ArrayTypeInt; break; case opc_lastore: arrayStoreType = ArrayTypeLong; break; case opc_fastore: arrayStoreType = ArrayTypeFloat; break; case opc_dastore: arrayStoreType = ArrayTypeDouble; break; case opc_bastore: // Unfortunately, both byte arrays and boolean arrays are accessed // using the same instruction so don't attempt to infer the array // element type for these. break; } return new InsnNote(insn, DirtyArray | arrayStoreType, valueSize+1, stackSig, null); } */ // --------------------------------------------------------------------------- /** * Annotate the class method. For now, brute force rules. */ void annotateMethod() { //@olsen: cosmetics final CodeAttribute codeAttr = method.codeAttribute(); if (codeAttr == null || !needsAnnotation()) return; //@olsen: disabled feature /* if ((annotate & MakeThisTransient) != 0) { makeThisTransient(codeAttr); if (annotate == MakeThisTransient) return; } */ //@olsen: added printing output env.message( "annotating method " + ca.userClassName()//NOI18N + "." + method.name().asString()//NOI18N + Descriptor.userMethodArgs(method.signature().asString())); //@olsen: disabled feature /* clearThisAnnotation(); removeRedundantThisAnnotation(); */ Insn firstInsn = codeAttr.theCode(); // First instruction is a target Insn insn = firstInsn.next(); while (insn != null) { switch(insn.opcode()) { //@olsen: disabled feature /* case opc_invokestatic: case opc_invokespecial: case opc_invokevirtual: case opc_invokeinterface: */ case opc_getfield: case opc_putfield: //@olsen: disabled feature /* case opc_aaload: case opc_baload: case opc_caload: case opc_saload: case opc_iaload: case opc_laload: case opc_faload: case opc_daload: case opc_aastore: case opc_bastore: case opc_castore: case opc_sastore: case opc_iastore: case opc_lastore: case opc_fastore: case opc_dastore: */ insn = insnAnnotation(insn); break; default: break; } insn = insn.next(); } //@olsen: do special annotation if detected super.clone() if ((annotate & SuperClone) != 0) { final String superName = ca.classFile().superName().asString(); annotateClone(codeAttr, superName); } //@olsen: disabled feature /* if (methodIsInitializer()) { } else { // Pre- fetch/dirty this if needed if (fetchThis || dirtyThis) { // Optimize a fetch(this) or dirty(this) to the start of // the method. // For fetch calls this is: // if (jdoFlags < 0) // Implementation.fetch(this); // // For dirty calls this is: // if ((jdoFlags&PersistenceCapable.writeBarrierSet) != 0) // Implementation.dirty(this); Insn newInsn = Insn.create(opc_aload_0); Insn annotationStart = newInsn; InsnTarget afterCondition = null; //@olsen: disabled feature /// if (ca.getFlagsMemberValid() && ca.getFlagsMember() != null) { /// //@olsen: changed to use JDOMetaData final String className = ca.className(); final String pcRootClass = env.getJDOMetaData().getPersistenceCapableRootClass(className); newInsn = newInsn.append( Insn.create(opc_getfield, pool.addFieldRef( pcRootClass, JDOMetaData.JDOFlagsFieldName, JDOMetaData.JDOFlagsFieldSig))); //@olsen: disabled feature /// ClassControl flagsCC = ca.getFlagsMemberClassControl(); newInsn = newInsn.append( Insn.create(opc_getfield, pool.addFieldRef( flagsCC.className(), ca.getFlagsMember(), "B"))); /// afterCondition = new InsnTarget(); if (dirtyThis) { newInsn = newInsn.append(Insn.create(opc_iconst_2)); newInsn = newInsn.append(Insn.create(opc_iand)); newInsn = newInsn.append(Insn.create(opc_ifeq, afterCondition)); newInsn = newInsn.append(Insn.create(opc_aload_0)); } else { newInsn = newInsn.append(Insn.create(opc_ifge, afterCondition)); newInsn = newInsn.append(Insn.create(opc_aload_0)); } //@olsen: disabled feature /// } /// newInsn = newInsn.append( Insn.create( opc_invokestatic, pool.addMethodRef("com/sun/forte4j/persistence/internal/Implementation", (dirtyThis ? "dirty" : "fetch"), "(" + JDOMetaData.JDOPersistenceCapableSig + ")V"))); if (afterCondition != null) newInsn = newInsn.append(afterCondition); firstInsn.insert(annotationStart); noteStack(2); } //@olsen: disabled feature /// if (methodName.equals("clone") && methodSig.equals("()Ljava/lang/Object;") && !ca.getNeedsClone()) { annotateClone(codeAttr, superName); } /// //@olsen: disabled feature } */ //@olsen: disabled feature /* //^olsen: caches -> int[] ? if (caches != null && caches.size() > 0) { // Generate fetch/dirty cache initializers Insn initInsn = null; //^olsen: optimize traversal ? for (int i = 0; i < caches.size(); i++) { int slot = ((Integer) caches.elementAt(i)).intValue(); Insn nullInsn = Insn.create(opc_aconst_null); if (initInsn == null) initInsn = nullInsn; else initInsn.append(nullInsn); initInsn.append(InsnUtils.aStore(slot, pool)); } // These initializations must not occur in an // exception handler or the code may fail to verify. If an // exception handler starts at offset 0, our initializations // will fall into the exception handler block. For // simplicity, just add a new target as the initial // instruction - it doesn't cost anything in the // generated code. InsnTarget newFirstInsn = new InsnTarget(); initInsn.append(firstInsn); newFirstInsn.append(initInsn); firstInsn = newFirstInsn; codeAttr.setTheCode(firstInsn); } */ if (annotationStack > 0) codeAttr.setStackUsed(codeAttr.stackUsed() + annotationStack); } // --------------------------------------------------------------------------- /** * If dirtyThis or fetchThis is set, remove flags indicating the need to * fetch or dirth "this" on individual instructions. */ //@olsen: disabled feature /* private void clearThisAnnotation() { // If the user has disabled "this" optimization, simply turn the // dirtyThis and fetchThis flags off unless this is an initializer // method, in which case we defer reseting the flags until the end // of this method. if (!env.doThisOptimization() && !methodIsInitializer()) { dirtyThis = false; fetchThis = false; } if (!dirtyThis && !fetchThis) return; final CodeAttribute codeAttr = method.codeAttribute(); if (codeAttr != null) { for (Insn insn = codeAttr.theCode(); insn != null; insn = insn.next()) { for (InsnNote note = getNoteList(insn); note != null; note = note.next()) { if (dirtyThis && note.dirtyThis()) note.dontDirtyThis(); if ((dirtyThis || fetchThis) && note.fetchThis()) note.dontFetchThis(); } } } if (methodIsInitializer()) { dirtyThis = false; fetchThis = false; } } */ /** * Optimize out obviously redundant fetch(this) and dirty(this) * annotations. These are repeated fetches and dirties which occur * in straight-line code with no intervening branch targets or * method calls. */ //@olsen: disabled feature /* private void removeRedundantThisAnnotation() { // This optimization doesn't apply to static methods or initializers. // Static methods are ignored because they don't have a "this" and // initializers may be excluded if we expect that there are no // fetch/dirty of "this". if (method.isStatic() || (methodIsInitializer() && env.doInitializerOptimization())) return; CodeAttribute codeAttr = method.codeAttribute(); if (codeAttr != null && needsAnnotation()) { Insn firstInsn = codeAttr.theCode(); // First instruction is a target Insn insn = firstInsn.next(); boolean thisFetched = false; boolean thisDirtied = false; while (insn != null) { for (InsnNote note = getNoteList(insn); note != null; note = note.next()) { if (note.fetchThis()) { if (thisFetched) note.dontFetchThis(); else thisFetched = true; } if (note.dirtyThis()) { if (thisDirtied) note.dontDirtyThis(); else { thisDirtied = true; thisFetched = true; } } } boolean invalidate = false; switch(insn.opcode()) { case opc_jsr: case opc_invokestatic: case opc_invokespecial: case opc_invokevirtual: case opc_invokeinterface: invalidate = true; break; case opc_monitorenter: // If the code is explicitly synchronizing then the user // might have some reason to expect instructions to // interleave against another thread execution in a // particular order, so invalidate any assumption about // the fetch/dirty flags invalidate = true; break; case Insn.opc_target: // targets which result from line-number info, etc. do not // invalidate the optimization if (((InsnTarget)insn).isBranchTarget()) invalidate = true; break; default: break; } if (invalidate) { thisFetched = false; thisDirtied = false; } insn = insn.next(); } } } */ // --------------------------------------------------------------------------- //^olsen: extend for full support of inheritance on PC classes //@olsen: reimplemented this method private void annotateClone(CodeAttribute codeAttr, String superName) { if (false) { final String methodName = method.name().asString(); final String methodSig = method.signature().asString(); System.out.println("annotateClone()");//NOI18N System.out.println(" methodName = " + methodName);//NOI18N System.out.println(" methodSig = " + methodSig);//NOI18N System.out.println(" superName = " + superName);//NOI18N } Insn insn; for (insn = codeAttr.theCode(); insn != null; insn = insn.next()) { // Found the clone method. See if it is the flavor of clone() // which does a super.clone() call, and if it is, add // field initializations for the jdoStateManager and jdoFlags // fields. if (insn.opcode() != opc_invokespecial) continue; final InsnConstOp invoke = (InsnConstOp)insn; final ConstMethodRef methodRef = (ConstMethodRef)invoke.value(); final ConstNameAndType methodNT = methodRef.nameAndType(); final String methodName = methodNT.name().asString(); final String methodSig = methodNT.signature().asString(); if (!(methodName.equals("clone")//NOI18N && methodSig.equals("()Ljava/lang/Object;")))//NOI18N continue; if (false) { final ConstClass methodClass = methodRef.className(); final String methodClassName = methodClass.asString(); System.out.println(" found invocation of: "//NOI18N + methodClassName + "." + methodName + methodSig);//NOI18N } // check whether next instruction already is a downcast to a // class implementing PersistenceCapable final String thisClass = ca.className(); final Insn checkCastInsn = insn.next(); final boolean needCheckcast; if (checkCastInsn.opcode() != opc_checkcast) { needCheckcast = true; } else { ConstClass target = (ConstClass) ((InsnConstOp) checkCastInsn).value(); if (target.asString().equals(thisClass)) { insn = checkCastInsn; needCheckcast = false; } else { needCheckcast = true; } } // clear jdo fields of clone { // duplicate downcastet reference final Insn newInsn = Insn.create(opc_dup); if (needCheckcast) { newInsn.append(Insn.create(opc_checkcast, pool.addClass(thisClass))); } newInsn.append(Insn.create(opc_dup)); // clear jdo fields newInsn.append(Insn.create(opc_aconst_null)); newInsn.append(Insn.create( opc_putfield, pool.addFieldRef(thisClass, JDOMetaData.JDOStateManagerFieldName, JDOMetaData.JDOStateManagerFieldSig))); newInsn.append(Insn.create(opc_iconst_0)); newInsn.append(Insn.create( opc_putfield, pool.addFieldRef(thisClass, JDOMetaData.JDOFlagsFieldName, JDOMetaData.JDOFlagsFieldSig))); // insert code insn.insert(newInsn); noteStack(3); } } } //@olsen: disabled feature /* private void annotateClone(CodeAttribute codeAttr, String superName) { Insn insn; for (insn = codeAttr.theCode(); insn != null; insn = insn.next()) { // Found the clone method. See if it is the flavor of clone() // which does a super.clone() call, and if it is, add // field initializations for the jdoStateManager and jdoFlags // fields. if (insn.opcode() == opc_invokespecial) { InsnConstOp invoke = (InsnConstOp) insn; ConstMethodRef methRef = (ConstMethodRef) invoke.value(); String methName = methRef.nameAndType().name().asString(); if (methName.equals("clone")) { String thisClass = ca.className(); //@olsen: disabled feature /// // The following change to the method ref is a // workaround for the Sun JIT. If there is a // derived class whose clone() method calls // super.clone() where the super class's clone() // was constructed by osjcfp, // the compiler will generate code which // calls java.lang.Object.clone() instead of the // base class's clone (since that's all it can // see at compile time). It also sets // (correctly) the ACC_SUPER bit. Unfortunately, // the JDK JIT will not call the inserted clone // method, but instead calls the // java.lang.Object.clone() method. The hackery // below modifies the invokespecial super.clone() // call to instead use the osjcfp supplied clone(). // It should be removed if/when the JIT // is fixed. -cwl 6/27/97 ClassControl cc = env.findClass(superName).findMethodClass( "clone", "()Ljava/lang/Object;"); if (cc != null && !cc.className().equals(methRef.className().asString())) { env.message("Changing " + thisClass + ".clone() to call " + cc.className() + ".clone() instead of " + methRef.className().asString() + ".clone()"); ConstMethodRef newMethRef = pool.addMethodRef(cc.className(), "clone", "()Ljava/lang/Object;"); invoke.setValue(newMethRef); } /// //@olsen: disabled feature /// boolean needCheckcast = false; Insn checkCastInsn = insn.next(); if (checkCastInsn.opcode() != opc_checkcast) needCheckcast = true; else { ConstClass target = (ConstClass) ((InsnConstOp) checkCastInsn).value(); ClassControl targetCC = env.findClass(target.asString()); if (targetCC != null && !targetCC.inherits(thisClass)) needCheckcast = true; else insn = checkCastInsn; } boolean checkStack = false; if (ca.getNeedsJDOStateManagerMethods()) { Insn newInsn = Insn.create(opc_dup); if (needCheckcast) newInsn.append(Insn.create(opc_checkcast, pool.addClass(thisClass))); newInsn.append(Insn.create(opc_aconst_null)); newInsn.append(Insn.create( opc_putfield, pool.addFieldRef(thisClass, ca.getRefMember(), JDOStateManagerSig))); insn.insert(newInsn); checkStack = true; } if (ca.getNeedsJDOFlagsMethods()) { Insn newInsn = Insn.create(opc_dup); if (needCheckcast) newInsn.append(Insn.create(opc_checkcast, pool.addClass(thisClass))); newInsn.append(Insn.create(opc_iconst_0)); newInsn.append(Insn.create(opc_putfield, pool.addFieldRef(thisClass, ca.getFlagsMember(), "B"))); insn.insert(newInsn); checkStack = true; } if (checkStack) noteStack(2); /// } } } } */ // --------------------------------------------------------------------------- /** * For a non-static method of a class which implements PersistenceCapable, * convert the object to a transient object by setting the stateManager * to null and the object flags to 0. It is assumed that * the ObjectTable will have already been cleaned up by the * garbage collector. */ //@olsen: disabled feature /* private void makeThisTransient(CodeAttribute codeAttr) { Insn insn = codeAttr.theCode(); while (insn.opcode() == Insn.opc_target) insn = insn.next(); // Set the statemanager to null Insn annotation = Insn.create(opc_aload_0); annotation.append(Insn.create(opc_aconst_null)); ConstInterfaceMethodRef methRef = pool.addInterfaceMethodRef(JDOPersistenceCapablePath, "jdoSetStateManager", "(Lcom/sun/forte4j/persistence/internal/StateManager;)V"); annotation.append(new InsnInterfaceInvoke(methRef, 2)); // Set the object flags to null annotation.append(Insn.create(opc_aload_0)); annotation.append(Insn.create(opc_iconst_0)); methRef = pool.addInterfaceMethodRef( JDOPersistenceCapablePath, "jdoSetFlags", "(B)V"); annotation.append(new InsnInterfaceInvoke(methRef, 2)); insn.prev().insert(annotation); if (codeAttr.stackUsed() < 2) codeAttr.setStackUsed(2); } */ // --------------------------------------------------------------------------- /** * Generate annotations if needed for the instruction. */ private Insn insnAnnotation(final Insn insn) { // The note list should be sorted in order of decreasing arg depth int initialSingleRegs = 0; //@olsen: ensured to use single note only (as instantiated) /* for (InsnNote note = getNoteList(insn); note != null; note = note.next()) { ... } */ InsnNote note = getNoteList(insn); if (note == null) return insn; //@olsen: ensured to use single note only (as instantiated) affirm(insn == note.insn); affirm((note.next() == null), "Number of annotation notes for instruction > 1.");//NOI18N //@olsen: not needed to ensure: note.dirtyThis() && !method.isStatic() final boolean fetch = (note.fetchPersistent() || note.fetchThis()); final boolean dirty = (note.dirtyPersistent() || note.dirtyThis()); //@olsen: added consistency check affirm((fetch ^ dirty), "Inconsistent fetch/dirty flags.");//NOI18N //@olsen: added checks final boolean dfgField = note.dfgFieldAccess(); final boolean pkField = note.pkFieldAccess(); //@olsen: added println() for debugging if (false) { final String targetClassName = note.targetClassName; final String targetFieldName = note.targetFieldName; //final String targetPCRootClass = note.targetPCRootClass; System.out.println(" build annotation: "//NOI18N + targetClassName + "." + targetFieldName + " : "//NOI18N + (pkField ? "pk," : "!pk,")//NOI18N + (dfgField ? "dfg," : "!dfg,")//NOI18N + (fetch ? "fetch " : "dirty ")//NOI18N + (note.fetchPersistent() ? "persistent" : "this")//NOI18N + ";");//NOI18N } //@olsen: improved control flow //@olsen: 4385427: do not enhance PK read access at all if (pkField && fetch) { return insn; } //@olsen: enhance for mediated access //@olsen: enhance PK write as mediated access //@olsen: added: mediated getfield/putfield insn annotation if (pkField || !dfgField) { //insn.prev().insert(Insn.create(opc_nop)); //@olsen: 4429769: drop putfield instruction on mediated write // access; isolate the get/putfield instruction to allow // to now be inserted by buildAccessAnnotation() itself final Insn prev = insn.prev(); insn.remove(); //@olsen: changed not to return null final AnnotationFragment frag1 = buildAccessAnnotation(note); affirm(frag1, "Illegal annotation of PK or non-dfg field.");//NOI18N //@olsen: 4429769, replace current instruction with fragment //insn.prev().insert(frag1.annotation); //noteStack(frag1.stackRequired - note.arg()); //return insn; final Insn last = prev.insert(frag1.annotation); noteStack(frag1.stackRequired - note.arg()); return last; } // do basic annotation //@olsen: enhance for non-mediated access final AnnotationFragment frag0 = buildBasicAnnotation(note); //@olsen: changed not to return null affirm(frag0, "Illegal annotation of dfg field.");//NOI18N //if (frag0 != null) { { // Attempt to find an instruction where the argument is known // to be on the top of stack StackState state = new StackState(note.arg(), note.sig(), insn.prev()); minimizeStack(state); if (false) { System.out.println(" state.argDepth = "//NOI18N + state.argDepth); System.out.print(" state.insn = ");//NOI18N state.insn.printInsn(System.out); System.out.print(" insn = ");//NOI18N insn.printInsn(System.out); } // generate the necessary instructions Insn annotation = null; if (state.argDepth == 0) { // The value is on top of the stack - the dup in the basic // annotation fragment will suffice annotation = frag0.annotation; noteStack(frag0.stackRequired); } else if (state.argDepth == 1) { // The value on top of the stack is one deep. Because the // operand of interest is also a single word value we can // simply execute a swap operation to get access to the // operand on top of the stack annotation = Insn.create(opc_swap); annotation.append(frag0.annotation); annotation.append(Insn.create(opc_swap)); // reduce the code fragment's stack requirements by // the amount that minimizeStack reduced the stack depth, // since that is the context in which the code fragment // will run. noteStack(frag0.stackRequired - (note.arg()-1)); } else { // The value is hidden by 2 or more stack operands. Move // the obscuring values into temporaries to get access to // the value - put them back when done Stack stackTypes = state.stackTypes; int depth = state.argDepth; int elem = stackTypes.size()-1; int singleRegs = initialSingleRegs; int doubleRegs = 0; int regnums[] = new int[depth]; int regtotal = 0; // Now, move values into temp registers while (depth > 0) { int elemType = ((Integer)stackTypes.elementAt(elem--)).intValue(); int elemSize = Descriptor.elementSize(elemType); depth -= elemSize; int reg = ((elemSize == 1) ? tmpReg(singleRegs++) : tmpReg2(doubleRegs++)); regnums[regtotal++] = reg; Insn store = InsnUtils.store(elemType, reg, pool); if (annotation == null) annotation = store; else annotation.append(store); } affirm((depth >= 0), "Stack underflow while computing save registers");//NOI18N annotation.append(frag0.annotation); while (regtotal > 0) annotation.append(InsnUtils.load( ((Integer)stackTypes.elementAt(++elem)).intValue(), regnums[--regtotal], pool)); noteStack(frag0.stackRequired - note.arg()); } state.insn.insert(annotation); } return insn; } //@olsen: added method for direct annotation of put/getfield //@olsen: must not return null private AnnotationFragment buildAccessAnnotation(final InsnNote note) { final int requiredStack; final Insn annotation; final String targetClassName = note.targetClassName; final String targetFieldName = note.targetFieldName; final String targetPCRootClass = note.targetPCRootClass; //@olsen: not needed to ensure: note.dirtyThis() && !method.isStatic() final boolean fetch = (note.fetchPersistent() || note.fetchThis()); final boolean dirty = (note.dirtyPersistent() || note.dirtyThis()); //@olsen: added consistency check affirm((fetch ^ dirty), "Inconsistent fetch/dirty flags.");//NOI18N //@olsen: added println() for debugging if (false) { final boolean dfgField = note.dfgFieldAccess(); final boolean pkField = note.pkFieldAccess(); System.out.println(" build access annotation: "//NOI18N + targetClassName + "." + targetFieldName + " : "//NOI18N + (pkField ? "pk," : "!pk,")//NOI18N + (dfgField ? "dfg," : "!dfg,")//NOI18N + (fetch ? "fetch " : "dirty ")//NOI18N + (note.fetchPersistent() ? "persistent" : "this")//NOI18N + ";");//NOI18N } final int argSize = note.arg(); final String fieldSig = note.sig(); final int fieldType = Descriptor.elementType(fieldSig); final int fieldIndex = note.targetFieldIndex; if (false) { System.out.println(" argSize = " + argSize);//NOI18N System.out.println(" fieldSig = " + fieldSig);//NOI18N System.out.println(" fieldType = " + fieldType);//NOI18N System.out.println(" fieldIndex = " + fieldIndex);//NOI18N } if (fetch) { // get jdoStateManager Insn insn = annotation = Insn.create(opc_dup); insn = insn.append( Insn.create(opc_getfield, pool.addFieldRef( targetPCRootClass, JDOMetaData.JDOStateManagerFieldName, JDOMetaData.JDOStateManagerFieldSig))); // test jdoStateManager // load/dirty field if nonnull InsnTarget fetchDirty = new InsnTarget(); InsnTarget afterFetchDirty = new InsnTarget(); insn = insn.append(Insn.create(opc_dup)); insn = insn.append( Insn.create(opc_ifnonnull, fetchDirty)); // pop jdoStateManager and skip loading/dirtying insn = insn.append(Insn.create(opc_pop)); insn = insn.append( Insn.create(opc_goto, afterFetchDirty)); // invoke StateManager's fetch method insn = insn.append(fetchDirty); // push field's unique index onto stack (1st arg) insn = insn.append(InsnUtils.integerConstant(fieldIndex, pool)); // call stateManager's void prepareGetField(int fieldID) method requiredStack = 2; insn = insn.append( new InsnInterfaceInvoke( pool.addInterfaceMethodRef( JDOMetaData.JDOStateManagerPath, "prepareGetField",//NOI18N "(I)V"),//NOI18N requiredStack)); insn = insn.append(afterFetchDirty); insn = insn.append(note.insn); } else { //affirm(dirty); int singleRegs = 0; int doubleRegs = 0; // move current value into temp registers affirm(argSize > 0); final int reg = ((argSize == 1) ? tmpReg(singleRegs++) : tmpReg2(doubleRegs++)); Insn insn = annotation = InsnUtils.store(fieldType, reg, pool); // get jdoStateManager insn = insn.append(Insn.create(opc_dup)); insn = insn.append( Insn.create(opc_getfield, pool.addFieldRef( targetPCRootClass, JDOMetaData.JDOStateManagerFieldName, JDOMetaData.JDOStateManagerFieldSig))); // test jdoStateManager // load/dirty field if nonnull InsnTarget fetchDirty = new InsnTarget(); InsnTarget afterFetchDirty = new InsnTarget(); insn = insn.append(Insn.create(opc_dup)); insn = insn.append( Insn.create(opc_ifnonnull, fetchDirty)); // pop jdoStateManager and skip loading/dirtying insn = insn.append(Insn.create(opc_pop)); // restore value from registers affirm(argSize > 0); insn = insn.append(InsnUtils.load(fieldType, reg, pool)); //@olsen: 4429769, insert the original putfield instruction here insn = insn.append(note.insn); insn = insn.append( Insn.create(opc_goto, afterFetchDirty)); // invoke StateManager's load method insn = insn.append(fetchDirty); // push field's unique index onto stack (1st arg) insn = insn.append(InsnUtils.integerConstant(fieldIndex, pool)); // restore value from registers (2nd arg) affirm(argSize > 0); insn = insn.append(InsnUtils.load(fieldType, reg, pool)); // call stateManager's set<Type>Field(index, value) method switch(fieldType) { case T_BOOLEAN: //boolean setBooleanField(int fieldNumber, boolean value); requiredStack = 3; insn = insn.append( new InsnInterfaceInvoke( pool.addInterfaceMethodRef( JDOMetaData.JDOStateManagerPath, "setBooleanField",//NOI18N "(IB)B"),//NOI18N requiredStack)); //@olsen: 4429769, disregard object and setField's return value insn = insn.append(Insn.create(opc_pop2)); break; case T_CHAR: //char setCharField(int fieldNumber, char 3); requiredStack = 3; insn = insn.append( new InsnInterfaceInvoke( pool.addInterfaceMethodRef( JDOMetaData.JDOStateManagerPath, "setCharField",//NOI18N "(IC)C"),//NOI18N requiredStack)); //@olsen: 4429769, disregard object and setField's return value insn = insn.append(Insn.create(opc_pop2)); break; case T_BYTE: //byte setByteField(int fieldNumber, byte value); requiredStack = 3; insn = insn.append( new InsnInterfaceInvoke( pool.addInterfaceMethodRef( JDOMetaData.JDOStateManagerPath, "setByteField",//NOI18N "(IZ)Z"),//NOI18N requiredStack)); //@olsen: 4429769, disregard object and setField's return value insn = insn.append(Insn.create(opc_pop2)); break; case T_SHORT: //short setShortField(int fieldNumber, short value); requiredStack = 3; insn = insn.append( new InsnInterfaceInvoke( pool.addInterfaceMethodRef( JDOMetaData.JDOStateManagerPath, "setShortField",//NOI18N "(IS)S"),//NOI18N requiredStack)); //@olsen: 4429769, disregard object and setField's return value insn = insn.append(Insn.create(opc_pop2)); break; case T_INT: //int setIntField(int fieldNumber, int value); requiredStack = 3; insn = insn.append( new InsnInterfaceInvoke( pool.addInterfaceMethodRef( JDOMetaData.JDOStateManagerPath, "setIntField",//NOI18N "(II)I"),//NOI18N requiredStack)); //@olsen: 4429769, disregard object and setField's return value insn = insn.append(Insn.create(opc_pop2)); break; case T_LONG: //long setLongField(int fieldNumber, long value); requiredStack = 4; insn = insn.append( new InsnInterfaceInvoke( pool.addInterfaceMethodRef( JDOMetaData.JDOStateManagerPath, "setLongField",//NOI18N "(IJ)J"),//NOI18N requiredStack)); //@olsen: 4429769, disregard object and setField's return value insn = insn.append(Insn.create(opc_pop2)); insn = insn.append(Insn.create(opc_pop)); break; case T_FLOAT: //float setFloatField(int fieldNumber, float value); requiredStack = 3; insn = insn.append( new InsnInterfaceInvoke( pool.addInterfaceMethodRef( JDOMetaData.JDOStateManagerPath, "setFloatField",//NOI18N "(IF)F"),//NOI18N requiredStack)); //@olsen: 4429769, disregard object and setField's return value insn = insn.append(Insn.create(opc_pop2)); break; case T_DOUBLE: //double setDoubleField(int fieldNumber, double value); requiredStack = 4; insn = insn.append( new InsnInterfaceInvoke( pool.addInterfaceMethodRef( JDOMetaData.JDOStateManagerPath, "setDoubleField",//NOI18N "(ID)D"),//NOI18N requiredStack)); //@olsen: 4429769, disregard object and setField's return value insn = insn.append(Insn.create(opc_pop2)); insn = insn.append(Insn.create(opc_pop)); break; case TC_OBJECT: case TC_INTERFACE: //Object setObjectField(int fieldNumber, Object value); requiredStack = 3; insn = insn.append( new InsnInterfaceInvoke( pool.addInterfaceMethodRef( JDOMetaData.JDOStateManagerPath, "setObjectField",//NOI18N "(ILjava/lang/Object;)Ljava/lang/Object;"),//NOI18N requiredStack)); //@olsen: 4429769, no need to downcast anymore /* // add a down-cast to the field's type affirm((fieldSig.charAt(0) == 'L' && fieldSig.charAt(fieldSig.length() - 1) == ';'), "Inconsistent field signature");//NOI18N final String fieldTypeClassName = fieldSig.substring(1, fieldSig.length() - 1); final ConstClass fieldTypeConstClass = pool.addClass(fieldTypeClassName); insn = insn.append( Insn.create(opc_checkcast, fieldTypeConstClass)); */ //@olsen: 4429769, disregard object and setField's return value insn = insn.append(Insn.create(opc_pop2)); break; default: throw new InternalError("Unexpected field type");//NOI18N } insn = insn.append(afterFetchDirty); } //@olsen: added println() for debugging if (false) { System.out.println(" built annotation, "//NOI18N + "required stack = "//NOI18N + requiredStack); } return new AnnotationFragment(annotation, requiredStack); } /** * Assuming that an object reference is on the top of stack, * generate an instruction sequence to perform the annotation * indicated by the note. */ //@olsen: must not return null private AnnotationFragment buildBasicAnnotation(InsnNote note) { int requiredStack = 2; Insn basicAnnotation = null; //@olsen: changed to use JDOMetaData final String targetClassName = note.targetClassName; final String targetFieldName = note.targetFieldName; final String targetPCRootClass = note.targetPCRootClass; //@olsen: not needed to ensure: note.dirtyThis() && !method.isStatic() final boolean fetch = (note.fetchPersistent() || note.fetchThis()); final boolean dirty = (note.dirtyPersistent() || note.dirtyThis()); //@olsen: added consistency check affirm((fetch ^ dirty), "Inconsistent fetch/dirty flags.");//NOI18N //@olsen: added println() for debugging if (false) { final boolean dfgField = note.dfgFieldAccess(); final boolean pkField = note.pkFieldAccess(); System.out.println(" build basic annotation: "//NOI18N + targetClassName + "." + targetFieldName + " : "//NOI18N + (pkField ? "pk," : "!pk,")//NOI18N + (dfgField ? "dfg," : "!dfg,")//NOI18N + (fetch ? "fetch " : "dirty ")//NOI18N + (note.fetchPersistent() ? "persistent" : "this")//NOI18N + ";");//NOI18N } //@olsen: changed code for annotation { Insn insn = null; //requiredStack = 2; // get jdoFlags basicAnnotation = insn = Insn.create(opc_dup); insn = insn.append( Insn.create(opc_getfield, pool.addFieldRef( targetPCRootClass, JDOMetaData.JDOFlagsFieldName, JDOMetaData.JDOFlagsFieldSig))); // test jdoFlags // skip loading for read if <= 0 / for update if == 0 InsnTarget afterFetchDirty = new InsnTarget(); insn = insn.append( Insn.create((fetch ? opc_ifle : opc_ifeq), afterFetchDirty)); // get jdoStateManager insn = insn.append(Insn.create(opc_dup)); insn = insn.append( Insn.create(opc_getfield, pool.addFieldRef( targetPCRootClass, JDOMetaData.JDOStateManagerFieldName, JDOMetaData.JDOStateManagerFieldSig))); // invoke StateManager's load method insn = insn.append( new InsnInterfaceInvoke( pool.addInterfaceMethodRef( JDOMetaData.JDOStateManagerPath, (fetch ? "loadForRead" : "loadForUpdate"),//NOI18N "()V"),//NOI18N 1)); insn = insn.append(afterFetchDirty); } //@olsen: added println() for debugging if (false) { System.out.println(" built annotation, "//NOI18N + "required stack = "//NOI18N + requiredStack); } return new AnnotationFragment(basicAnnotation, requiredStack); } /** * Assuming that an object reference is on the top of stack, * generate an instruction sequence to perform the annotation * indicated by the note. */ //@olsen: disabled feature /* private AnnotationFragment buildBasicAnnotation(InsnNote note) { Insn annotation = null; // used? int requiredStack = 2; Insn basicAnnotation = null; //@olsen: disabled feature /// ConstMethodRef methRef = null; InsnTarget afterFetchDirty = null; Insn flagsCheckAnnotation = null; /// //@olsen: disabled feature /// ClassAction targetCA = note.targetClassAction(); /// //@olsen: disabled feature /// // we may need to save the argument in a register for later use Insn regStore = null; if (note.getArgReg() >= 0) { regStore = Insn.create(opc_dup); regStore.append(InsnUtils.aStore(note.getArgReg(), pool)); } /// /// if (methRef != null) { if (flagsCheckAnnotation == null) { basicAnnotation = Insn.create(opc_dup); basicAnnotation.append(Insn.create(opc_invokestatic, methRef)); } else { basicAnnotation = flagsCheckAnnotation; basicAnnotation.append(Insn.create(opc_dup)); basicAnnotation.append(Insn.create(opc_invokestatic, methRef)); basicAnnotation.append(afterFetchDirty); } } /// //@olsen: disabled feature /// if (note.fetchPersistent() || (note.fetchThis() && thisIsPersistent())) { methRef = pool.addMethodRef("com/sun/forte4j/persistence/internal/Implementation", "fetch", "(" + JDOMetaData.JDOPersistenceCapableSig + ")V"); if (targetCA != null) { targetCA.ensureFlagsMemberValid(); if (targetCA.getFlagsMember() != null) { ClassControl flagsCC = targetCA.getFlagsMemberClassControl(); flagsCheckAnnotation = Insn.create(opc_dup); //@olsen: changed to use JDOMetaData flagsCheckAnnotation.append( Insn.create(opc_getfield, pool.addFieldRef( targetPCRootClass, JDOMetaData.JDOFlagsFieldName, JDOMetaData.JDOFlagsFieldSig))); flagsCheckAnnotation.append( Insn.create(opc_getfield, pool.addFieldRef(flagsCC.className(), targetCA.getFlagsMember(), "B"))); afterFetchDirty = new InsnTarget(); //@olsen: skip loading for read if <= 0 flagsCheckAnnotation.append( Insn.create(opc_ifle, afterFetchDirty)); } } } else if (note.dirtyPersistent() || (note.dirtyThis() && thisIsPersistent())) { methRef = pool.addMethodRef("com/sun/forte4j/persistence/internal/Implementation", "dirty", "(" + JDOMetaData.JDOPersistenceCapableSig + ")V"); if (targetCA != null) { targetCA.ensureFlagsMemberValid(); if (targetCA.getFlagsMember() != null) { ClassControl flagsCC = targetCA.getFlagsMemberClassControl(); flagsCheckAnnotation = Insn.create(opc_dup); //@olsen: changed to use JDOMetaData flagsCheckAnnotation.append( Insn.create(opc_getfield, pool.addFieldRef( targetPCRootClass, JDOMetaData.JDOFlagsFieldName, JDOMetaData.JDOFlagsFieldSig))); flagsCheckAnnotation.append( Insn.create(opc_getfield, pool.addFieldRef(flagsCC.className(), targetCA.getFlagsMember(), "B"))); afterFetchDirty = new InsnTarget(); flagsCheckAnnotation.append(Insn.create(opc_iconst_2)); flagsCheckAnnotation.append(Insn.create(opc_iand)); flagsCheckAnnotation.append( Insn.create(opc_ifeq, afterFetchDirty)); // One more word is needed here for the constant requiredStack = 2; } } } else if (note.fetchArray()) { String fetchSig = arrayFetchSignature(env.doArrayElementFetch() ? note.arrayElementType() : 0); methRef = pool.addMethodRef("com/sun/forte4j/persistence/internal/Implementation", "fetch", fetchSig); } else if (note.fetchObject()) { methRef = pool.addMethodRef("com/sun/forte4j/persistence/internal/Implementation", "fetch", "(Ljava/lang/Object;)V"); } else if (note.dirtyArray()) { methRef = pool.addMethodRef("com/sun/forte4j/persistence/internal/Implementation", "dirty", "(Ljava/lang/Object;)V"); } else if (note.dirtyObject()) { methRef = pool.addMethodRef("com/sun/forte4j/persistence/internal/Implementation", "dirty", "(Ljava/lang/Object;)V"); } if (methRef != null) { if (flagsCheckAnnotation == null) { basicAnnotation = Insn.create(opc_dup); basicAnnotation.append(Insn.create(opc_invokestatic, methRef)); } else { basicAnnotation = flagsCheckAnnotation; basicAnnotation.append(Insn.create(opc_dup)); basicAnnotation.append(Insn.create(opc_invokestatic, methRef)); basicAnnotation.append(afterFetchDirty); } boolean cacheResult = false; if (env.doArrayOptimization() && (note.fetchArray() || note.dirtyArray()) && note.inLoop()) cacheResult = true; if (cacheResult || note.checkNull()) { // Since this method appears to create a loop of some sort, // add a cache to remember what array has been fetched int cacheSlot = 0; InsnTarget skipTo = new InsnTarget(); // This generally requires at least two words of stack if (cacheResult) cacheSlot = newCacheSlot(); Insn skipAnnotation = Insn.create(opc_dup); if (note.checkNull()) { // skip cache-check/fetch if null skipAnnotation.append(Insn.create(opc_ifnull, skipTo)); if (cacheResult) // we used the dup'd result so dup again skipAnnotation.append(Insn.create(opc_dup)); } if (cacheResult) { skipAnnotation.append(InsnUtils.aLoad(cacheSlot, pool)); skipAnnotation.append(Insn.create(opc_if_acmpeq, skipTo)); } skipAnnotation.append(basicAnnotation); if (cacheResult) { skipAnnotation.append(Insn.create(opc_dup)); skipAnnotation.append(InsnUtils.aStore(cacheSlot, pool)); } if (requiredStack < 2) requiredStack = 2; skipAnnotation.append(skipTo); basicAnnotation = skipAnnotation; } } /// //@olsen: disabled feature /// // Put the unconditional store-arg-to-register annotation at the // front if non-null if (regStore != null) { regStore.append(basicAnnotation); basicAnnotation = regStore; } /// if (basicAnnotation != null) return new AnnotationFragment(basicAnnotation, requiredStack); return null; } */ /** * Compute the method signature for a array fetch() method call based * on the type of array element (as defined in AnnotationConstants */ //@olsen: disabled feature /* String arrayFetchSignature(int arrayElementType) { switch (arrayElementType) { case ArrayTypeBoolean: return "([Z)V"; case ArrayTypeByte: return "([B)V"; case ArrayTypeChar: return "([C)V"; case ArrayTypeShort: return "([S)V"; case ArrayTypeInt: return "([I)V"; case ArrayTypeLong: return "([J)V"; case ArrayTypeFloat: return "([F)V"; case ArrayTypeDouble: return "([D)V"; case ArrayTypeObject: return "([Ljava/lang/Object;)V"; } // No special matching type - just use the default signature. return "(Ljava/lang/Object;)V"; } */ /** * Allocate a two word temporary register * @param idx the index of the temporary register to return. If the * specified temporary hasn't been allocated, allocated it now. */ private int tmpReg2(int idx) { if (tmpDoubleRegisters == null) tmpDoubleRegisters = new Vector(3); // allocated as many 2 register pairs as necessary in order to // make idx be a valid index while (tmpDoubleRegisters.size() <= idx) { final CodeAttribute codeAttr = method.codeAttribute(); final int reg = codeAttr.localsUsed(); tmpDoubleRegisters.addElement(new Integer(reg)); codeAttr.setLocalsUsed(reg+2); } return ((Integer)tmpDoubleRegisters.elementAt(idx)).intValue(); } /** * Allocate a one word temporary register * @param idx the index of the temporary register to return. If the * specified temporary hasn't been allocated, allocated it now. */ private int tmpReg(int idx) { if (tmpRegisters == null) tmpRegisters = new Vector(3); // allocate as many registers as necessary in order to // make idx be a valid index while (tmpRegisters.size() <= idx) { final CodeAttribute codeAttr = method.codeAttribute(); final int reg = codeAttr.localsUsed(); tmpRegisters.addElement(new Integer(reg)); codeAttr.setLocalsUsed(reg+1); } return ((Integer)tmpRegisters.elementAt(idx)).intValue(); } /** * Allocate an object fetch/store cache slot */ //@olsen: disabled feature /* private int newCacheSlot() { CodeAttribute codeAttr = method.codeAttribute(); int slot = codeAttr.localsUsed(); codeAttr.setLocalsUsed(slot+1); if (caches == null) caches = new Vector(3); caches.addElement(new Integer(slot)); return slot; } */ /** * Note the following amount of stack used by a single annotation. */ private void noteStack(int stk) { if (stk > annotationStack) annotationStack = (short)stk; } /** * Is this a non-static method of a persistence-capable class? */ //@olsen: disabled feature /* private boolean thisIsPersistent() { return (ca.persistCapable() && !method.isStatic()); } */ // --------------------------------------------------------------------------- /** * Attempt to locate the instruction which deposits to the top of stack * the 1 word stack argument to currInsn which is argDepth deep on the * stack (top of stack == 0). * If unable to determine this with confidence, return null. * * Note that this method will not look back past a target. * Also, the operations performed by the dup2, dup_x1, dup_x2, dup2_x1, * dup2_x2 instructions are currently not understood by this method so * we don't attempt to chain back through these instructions. */ private Insn findArgDepositer(Insn currInsn, int argDepth) { Insn depositer = null; for (Insn i = currInsn.prev(); argDepth >= 0; i = i.prev()) { // At control flow branch/merge points, abort the search for the // target operand. if (i.branches() || ((i instanceof InsnTarget) && ((InsnTarget)i).isBranchTarget())) break; int nArgs = i.nStackArgs(); int nResults = i.nStackResults(); if (argDepth - nResults < 0) { // This instruction does deposit the value // For now, don't return depositers other than opc_dup which // deposit more than one value. These are the // long/doubleinstructions (which can't be depositing a one // word value) and the dupX variants if (nResults > 1 && i.opcode() != opc_dup) break; depositer = i; // consider special cases which may cause us to look further switch (i.opcode()) { case opc_dup: if (argDepth == 0) // keep going to find the real depositer at a greater depth argDepth++; break; case opc_checkcast: // keep going to find the real depositer break; default: return i; } } argDepth += (nArgs - nResults); } return depositer; } /** * Assume that after the execution of state.insn there is a word on * the stack which is state.argDepth words deep. * Scan backwards through the instruction sequence, attempting to * locate an instruction after which the argument is at a minimal * depth w.r.t. the top of stack. Update the state to indicate * progress. * Note that this method will not look back past a target. */ private void minimizeStack(StackState state) { Insn i = state.insn; int argDepth = state.argDepth; Stack argTypesStack = new Stack(); Stack resultTypesStack = new Stack(); Stack stackTypes = new Stack(); copyStack(state.stackTypes, stackTypes); for (; argDepth > 0; i = i.prev()) { // At control flow branch/merge points, abort the search for the // target operand. The caller will have to make do with the best // stack state computed thus far. if (i.branches() || ((i instanceof InsnTarget) && ((InsnTarget)i).isBranchTarget())) break; int nArgs = i.nStackArgs(); int nResults = i.nStackResults(); String argTypes = i.argTypes(); String resultTypes = i.resultTypes(); argDepth -= nResults; // If the target argument was placed there by an instruction which // deposited multiple results (one of the dup type instructions) // then we don't have the smarts to figure out where it came from // so just quit looking if (argDepth < 0) break; argDepth += nArgs; if (i.opcode() == opc_swap) { Object x = stackTypes.pop(); Object y = stackTypes.pop(); stackTypes.push(x); stackTypes.push(y); } else { // Make sure the arg types and result types stacks are empty while (!argTypesStack.empty()) argTypesStack.pop(); while (!resultTypesStack.empty()) resultTypesStack.pop(); Descriptor.computeStackTypes(argTypes, argTypesStack); Descriptor.computeStackTypes(resultTypes, resultTypesStack); int expectWords = 0; while (!resultTypesStack.empty()) expectWords += Descriptor.elementSize( ((Integer) resultTypesStack.pop()).intValue()); while (expectWords > 0) expectWords -= Descriptor.elementSize( ((Integer) stackTypes.pop()).intValue()); if (expectWords < 0) { // perhaps we ought to signal an exception, but returning // will keep things going just fine. return; } transferStackArgs(argTypesStack, stackTypes); } if (argDepth >= 0 && argDepth < state.argDepth && knownTypes(stackTypes, argDepth)) { state.argDepth = argDepth; state.insn = i.prev(); copyStack(stackTypes, state.stackTypes); } } } /* Take all stack elements in fromStack and push them onto toStack * such that they are in the same relative stack positions */ private final void transferStackArgs(Stack fromStack, Stack toStack) { if (!fromStack.empty()) { Object o = fromStack.pop(); transferStackArgs(fromStack, toStack); toStack.push(o); } } /* Make toStack look just like fromStack */ private final void copyStack(Stack fromStack, Stack toStack) { while (!toStack.empty()) toStack.pop(); // take advantage of Stack's inheritance from Vector for (int i=0; i<fromStack.size(); i++) toStack.addElement(fromStack.elementAt(i)); } /* Check that the top nWords worth of types on stack are well defined */ private final boolean knownTypes(Stack stack, int nWords) { // take advantage of Stack's inheritance from Vector for (int i=stack.size()-1; i>= 0 && nWords > 0; i--) { int words = 0; switch (((Integer)stack.elementAt(i)).intValue()) { case T_UNKNOWN: case T_WORD: case T_TWOWORD: return false; case T_BOOLEAN: case T_CHAR: case T_FLOAT: case T_BYTE: case T_SHORT: case T_INT: case TC_OBJECT: case TC_INTERFACE: case TC_STRING: words = 1; break; case T_DOUBLE: case T_LONG: words = 2; break; default: break; } nWords -= words; } return true; } // --------------------------------------------------------------------------- /** * Add a list of notes to the note list. */ //@olsen: made final private final void addNoteList(InsnNote note) { insnNotes.put(note.insn, note); } /** * Find the note list for the specified instruction. */ //@olsen: made final private final InsnNote getNoteList(Insn insn) { return (InsnNote)insnNotes.get(insn); } } // --------------------------------------------------------------------------- /** * Class loop is a simple class to represent a possible looping construct * within a method. */ //@olsen: disabled feature /* class Loop { int loopStart; // instruction offset - inclusive int loopEnd; // instruction offset - inclusive Loop(int lStart, int lEnd) { loopStart = lStart; loopEnd = lEnd; } */ /** * Scan the instructions looking for backward branches which suggests a * loop structure. If any are found, return a Loop object which * represents the largest possible loop. */ //@olsen: disabled feature /* static Loop checkLoops(Insn code) { // Use 999999 to represent an impossibly large instruction offset // The current VM design limits methods to 64k of instructions int loopStart = 999999; int loopEnd = 0; for (Insn i = code; i != null; i = i.next()) { if (i instanceof InsnTargetOp) { InsnTarget targ = ((InsnTargetOp) i).target(); if (targ.offset() < i.offset()) { // a backward branch if (targ.offset() < loopStart) loopStart = targ.offset(); if (i.offset() > loopEnd) loopEnd = i.offset(); } } } if (loopStart < loopEnd) return new Loop(loopStart, loopEnd); return null; } */ /** * Is the instruction contained within the loop? * Note that the instruction must have a valid offset for this * to answer correctly. */ //@olsen: disabled feature /* boolean contains(Insn i) { return i.offset() >= loopStart && i.offset() <= loopEnd; } } */ // --------------------------------------------------------------------------- /** * A structure to record what annotation requirements are implied * by a particular VM instruction. */ class InsnNote extends Support implements AnnotationConstants { //@olsen: made final final Insn insn; int insnFlags; final int argWord; //@olsen: disabled feature /* int argReg = -1; */ final String stackSig; //@olsen: added fields final String targetClassName; final String targetFieldName; final int targetFieldIndex; final String targetPCRootClass; //@olsen: disabled feature /* final ClassAction targetClassAction; */ /** * Return the next instruction note in this instruction sequence. * Most instructions need only a single note, but subtypes may need * to be chained, and should re-implement next(). */ InsnNote next() { return null; } /** * Return a descriptor for the current stack state. This descriptor * is a sequence of VM element type descriptors in decreasing stack * depth order. That is, the element on the top of stack is the last * element in the descriptor signature. This descriptor is only * guaranteed to represent the objects between the instruction stack * operand (which is offset arg()) deep on the stack and the top of * stack at the time insn is to be executed. It may however contain * additional descriptor elements. */ final String sig() { return stackSig; } //@olsen: disabled feature /* final ClassAction targetClassAction() { return targetClassAction; } */ /** * Return the offset from the top of the stack of the argument */ final int arg() { return argWord; } final boolean fetchThis() { return (insnFlags & FetchThis) != 0; } //@olsen: disabled feature /* final void dontFetchThis() { insnFlags &= ~FetchThis; } */ final boolean dirtyThis() { return (insnFlags & DirtyThis) != 0; } //@olsen: disabled feature /* final void dontDirtyThis() { insnFlags &= ~DirtyThis; } */ //@olsen: disabled feature /* final boolean unconditional() { return (insnFlags & Unconditional) != 0; } */ //@olsen: disabled feature / not used anymore /* final boolean fetchObject() { return (insnFlags & FetchObject) != 0; } final boolean dirtyObject() { return (insnFlags & DirtyObject) != 0; } */ final boolean fetchPersistent() { return (insnFlags & FetchPersistent) != 0; } final boolean dirtyPersistent() { return (insnFlags & DirtyPersistent) != 0; } //@olsen: added method final boolean dfgFieldAccess() { return (insnFlags & DFGField) != 0; } //@olsen: added method final boolean pkFieldAccess() { return (insnFlags & PKField) != 0; } //@olsen: disabled feature /* final boolean fetchArray() { return (insnFlags & FetchArray) != 0; } final boolean dirtyArray() { return (insnFlags & DirtyArray) != 0; } final int arrayElementType() { return insnFlags & ArrayTypeMask; } final boolean inLoop() { return (insnFlags & InLoop) != 0; } final boolean checkNull() { return (insnFlags & CheckNull) != 0; } */ /* If getArgReg returns < 0, the argReg is not set */ //@olsen: disabled feature /* final int getArgReg() { return argReg; } final void setArgReg(int reg) { argReg = reg; } */ /** * Construct an instruction note. * @param argWord must be the depth of the word on the stack in * word units * @param stackSig a stack descriptor for the stack - see * the doc for sig(). */ InsnNote(Insn i, int flags, int argWord, String stackSig, String targetClassName, String targetFieldName, int targetFieldIndex, String targetPCRootClass) { //@olsen: disabled feature /* InsnNote(Insn i, int flags, int argWord, String stackSig, ClassAction targetClassAction) { */ insn = i; insnFlags = flags; this.stackSig = stackSig; this.argWord = argWord; //@olsen: disabled feature /* this.targetClassAction = targetClassAction; */ this.targetClassName = targetClassName; this.targetFieldName = targetFieldName; this.targetFieldIndex = targetFieldIndex; this.targetPCRootClass = targetPCRootClass; //@olsen: added consistency check affirm(!(insn == null || argWord < 0 || targetClassName == null || targetFieldName == null || targetFieldIndex < 0 || targetPCRootClass == null), "Inconsistent instruction annotation note.");//NOI18N } } /** * A specialized form of instruction note for method invocation arguments. * The only thing that this adds is a link element to allow multiple notes * to apply to an instruction. */ //@olsen: disabled feature /* class InsnArgNote extends InsnNote { InsnArgNote nextNote; InsnNote next() { return nextNote; } InsnArgNote(Insn i, int flags, int argWord, String stackSig) { super(i, flags, argWord, stackSig, null, null); } } */ // --------------------------------------------------------------------------- /** * StackState is really just a simple association of instruction * and the depth of some stack operand on the operand stack. */ class StackState implements VMConstants { /* number of words deep that the target word is */ int argDepth; /* Stack of types */ Stack stackTypes; /* the instruction after which, the word is argDepth deep */ Insn insn; StackState(int depth, String stackSig, Insn i) { stackTypes = new Stack(); Descriptor.computeStackTypes(stackSig, stackTypes); argDepth = depth; insn = i; } } /** * AnnotationFragment is really just a simple association of instruction * and the number of words of stack used during the execution of the * fragment. */ class AnnotationFragment { Insn annotation; int stackRequired; AnnotationFragment(Insn i, int stack) { annotation = i; stackRequired = stack; } } // ---------------------------------------------------------------------------