[kaffe] Debugging Help

Rob Gonzalez rgonzale at wso.williams.edu
Wed Jul 30 11:50:03 PDT 2003


Hi all,

Attached is a patch for a nearly completed bytecode verifier.  There are
two things that are still not checked that are required by JVM Spec 4.8.2,
but they will be pretty simple to add in later.

Kaffe with verification passes all the usual regression tests with it
except for LostTrampolineFrame.  The VerifyError is raised when
LostTrampolineFrame$DamagedClass is verified, and is caught by the wrong
handler.  I don't really know what's going on with that test, and have
tried following it in gdb, but am having troubles deducing the problem.

I've run Kaffe with the verifier on some larger programs successfully.  
I'm currently trying to get it to run Jython 2.1.  It executes the
installer program fine, but when trying to run Jython it chokes during
verification.  What's strange is that I don't think it's necessarily the
verifier that's messing up (though, when run with -noverify it executes
pretty much fine).  It throws a NoClassDefFoundError for
Lorg/python/core/PyException; when using getClassFromSignature() to find
it.  The ClassLoader actually does return NULL, though that class is in
the classpath and the Loader has successfully loaded other classes from
that same subpackage previously.

It's not perfect, but it's a pretty good beta release :).  If anyone has
the time to look at the bugs with either Jython 2.1 or with the
LastTrampolineFrame test, I'd be very grateful.


Thanks,
Rob
-------------- next part --------------
--- kaffe-cvs/kaffe/kaffevm/verify.c	2003-07-30 12:08:02.000000000 -0500
+++ kaffe/kaffe/kaffevm/verify.c	2003-07-30 13:51:10.000000000 -0500
@@ -876,44 +876,51 @@
 	uint32 startAddr;
 	uint32 lastAddr;  // whether it be the address of a GOTO, etc.
 	
-	// which address this block returns to on a RET...
-	// a value of zero is an error
-	uint32 retAddr;
-	
-	
 	// status of block...changed (needs to be re-evaluated), visited, etc.
-	char status;
-	
+	uint32 status;
 	
 	Hjava_lang_Class** locals;
+	uint32*            locals_info;
 	
 	uint32 stacksz;
+	uint32*            opstack_info;   // additional info for stuff on the opstack
 	Hjava_lang_Class** opstack;
-	
-	
-	// each block may have a set of retAddr contexts
-	struct block_info* next;
 } BlockInfo;
 
 
 // status flags for a basic block.
 // these also pertain to the status[] array for the entire instruction array
-#define CHANGED          1
-#define VISITED          2
-#define IS_INSTRUCTION   4
+#define CHANGED            1
+#define VISITED            2
+#define IS_INSTRUCTION     4
 
 // if the instruction is preceeded by WIDE
-#define WIDE_MODDED      8
+#define WIDE_MODDED        8
 
 // used at the instruction status level to find basic blocks
-#define START_BLOCK     16
-#define END_BLOCK       32
+#define START_BLOCK       16
+#define END_BLOCK         32
 
 #define EXCEPTION_HANDLER 64
 
 
+// status flags for opstack/local info arrays
+#define CLASS_SIGSTR       1
+#define CLASS_NAMESTR      2
+
+
+
+// TODO: use the builtin hash table data structure instead so we can avoid repeats
+typedef struct sig_stack
+{
+	const char* sig;
+	struct sig_stack* next;
+} SigStack;
+
+
+
 // types for type checking (pass 3b)
-#define	TUNSTABLE		((Hjava_lang_Class*)0)
+#define	TUNSTABLE		((Hjava_lang_Class*)1)
 
 // returnAddress type
 #define	TADDR			((Hjava_lang_Class*)2)
@@ -942,18 +949,92 @@
 #define IS_PRIMITIVE_TYPE(_T) (_T == TINT || _T == TFLOAT || _T == TLONG || _T == TDOUBLE)
 
 
+/*
+ * array types
+ */
+#define TCHARARR		(charArrClass)
+
+// internally, booleans are represented by bytes
+#define TBOOLARR                (byteArrClass)
+#define TBYTEARR		(byteArrClass)
+
+#define TSHORTARR		(shortArrClass)
+#define TINTARR			(intArrClass)
+#define TLONGARR		(longArrClass)
+
+#define TFLOATARR		(floatArrClass)
+#define TDOUBLEARR		(doubleArrClass)
+
+#define TOBJARR			(objectArrClass)
+
+
+#define IS_PRIMITIVE_ARRAY(_T) \
+           ((_T) == TCHARARR || (_T) == TBYTEARR || (_T) == TSHORTARR || (_T) == TINTARR || (_T) == TLONGARR || \
+            (_T) == TFLOATARR || (_T) == TDOUBLEARR)
+
+// for IS_ARRAY we need to make sure that CLASS_IS_ARRAY is passed something legitimate...
+#define IS_ARRAY(_T) \
+           ((_T) && ((_T) != TUNSTABLE) && ((_T) != TADDR) && ((_T) != TNULL) && ((_T) != TWIDE) && \
+	    CLASS_IS_ARRAY(_T))
+
+
 /***********************************************************************************
  * Methods for Pass 3 Verification
  ***********************************************************************************/
 #ifdef KAFFE_VMDEBUG
 static void printInstruction(const int opcode);
+static void printType(const Hjava_lang_Class* type, const uint32 tinfo);
+static void printBlock(const Method* method, const BlockInfo* binfo, const char* indent);
 #endif
 
+static BlockInfo*        createBlock(const Method* method);
+static void              copyBlockData(const Method* method, BlockInfo* fromBlock, BlockInfo* toBlock);
+static void              copyBlockState(const Method* method, BlockInfo* fromBlock, BlockInfo* toBlock);
+static void              freeBlock(BlockInfo* binfo);
+
+static BlockInfo*        inWhichBlock(uint32 pc, BlockInfo** blocks, uint32 numBlocks);
+
+
+static SigStack*         pushSig(SigStack* sigs, const char* sig);
+static void              freeSigStack(SigStack* sigs);
+
+
 static bool              verifyMethod(errorInfo* einfo, Method* method);
 static BlockInfo**       verifyMethod3a(errorInfo* einfo,
 					Method* method,
 					uint32* status,       // array of status info for all opcodes
 					uint32* numBlocks); // number of basic blocks
+static bool              verifyMethod3b(errorInfo* einfo,
+					const Method* method,
+					const uint32* status,
+					BlockInfo** blocks,
+					const uint32 numBlocks,
+					SigStack* sigs);
+static bool              merge(errorInfo* einfo, const Method* method, BlockInfo* fromBlock, BlockInfo* toBlock);
+static bool              verifyBasicBlock(errorInfo*, const Method*, BlockInfo*, SigStack**);
+
+static const char*       getNextArg(const char* sig, char* buf);
+static bool              loadInitialArgs(const Method* method, errorInfo* einfo,
+					 BlockInfo* block, SigStack** sigs);
+
+static Hjava_lang_Class* mergeTypes(errorInfo* einfo, Hjava_lang_Class* this,
+				    Hjava_lang_Class* t1, uint32 info1,
+				    Hjava_lang_Class* t2, uint32 info2,
+				    uint32* info);
+static Hjava_lang_Class* getCommonSuperclass(Hjava_lang_Class* t1, Hjava_lang_Class* t2);
+
+static bool              isReference(const Hjava_lang_Class*, const uint32);
+static bool              typecheck(errorInfo*, Hjava_lang_Class*,
+				   Hjava_lang_Class*, uint32,
+				   Hjava_lang_Class*, uint32);
+static bool              implements(Hjava_lang_Class* t1, Hjava_lang_Class* t2);
+
+static const char*       getReturnSig(const Method*);
+static uint32            countSizeOfArgsInSignature(const char* sig);
+static bool              checkMethodCall(errorInfo* einfo, const Method* method,
+					 BlockInfo* binfo, uint32 pc,
+					 SigStack** sigs);
+
 
 
 /*
@@ -1032,24 +1113,92 @@
 	int codelen  = METHOD_BYTECODE_LEN(method);
 	
 	uint32* status = NULL; // the status of each instruction...changed, visited, etc.
-                             // used primarily to help find the basic blocks initially
+                               // used primarily to help find the basic blocks initially
+	
+	SigStack* sigs = NULL;
 	
 	uint32      numBlocks = 0;
+	BlockInfo** blocks    = NULL;
+	
+	
+	/**************************************************************************************************
+	 * Memory Management Macros
+	 **************************************************************************************************/
+	// to make sure we don't forget to unalloc anything...
+	// should be called during ANY EXIT FROM THIS METHOD
+#define CLEANUP \
+	DBG(VERIFY3, dprintf("    cleaning up..."); ); \
+	KFREE(status); \
+        if (blocks != NULL) { \
+		while (numBlocks > 0) { \
+			freeBlock(blocks[--numBlocks]); \
+		} \
+		KFREE(blocks); \
+	} \
+        freeSigStack(sigs); \
+        DBG(VERIFY3, dprintf(" done\n"); )
+	
+#define FAIL \
+        DBG(VERIFY3, dprintf("    Verify Method 3b: %s.%s%s: FAILED\n", \
+			     CLASS_CNAME(method->class), METHOD_NAMED(method), METHOD_SIGD(method)); ); \
+	if (einfo->type == 0) { \
+		DBG(VERIFY3, dprintf("      DBG ERROR: should have raised an exception\n"); ); \
+		postException(einfo, JAVA_LANG(VerifyError)); \
+	} \
+        CLEANUP; \
+        return(false)
+	
 	
 	/**************************************************************************************************
 	 * Memory Allocation
 	 **************************************************************************************************/
-	DBG(VERIFY3, dprintf("    allocating memory for verification (codelen = %d)...\n", codelen); );
+	DBG(VERIFY3, dprintf("        allocating memory for verification (codelen = %d)...\n", codelen); );
 	
-        status = checkPtr((char*)KMALLOC(codelen * sizeof(uint32)));
+        status   = checkPtr((char*)KMALLOC(codelen * sizeof(uint32)));
 	
 	// find basic blocks and allocate memory for them
-	verifyMethod3a(einfo, method, status, &numBlocks);
+	blocks = verifyMethod3a(einfo, method, status, &numBlocks);
+	if (!blocks) {
+		DBG(VERIFY3, dprintf("        some kinda error finding the basic blocks in pass 3a\n"); );
+		
+		// propagate error
+		FAIL;
+	}
 	
-	DBG(VERIFY3, dprintf("    done allocating memory\n"); );
+	DBG(VERIFY3, dprintf("        done allocating memory\n"); );
+	/**************************************************************************************************
+	 * Prepare for data-flow analysis
+	 **************************************************************************************************/
 	
-	KFREE(status);
+	// load initial arguments into local variable array
+	DBG(VERIFY3, dprintf("    about to load initial args...\n"); );
+	if (!loadInitialArgs(method, einfo, blocks[0], &sigs)) {
+		// propagate error
+		FAIL;
+	}
+	DBG(VERIFY3, {
+		// print out the local arguments
+		int n;
+		for(n = 0; n < method->localsz; n++) {
+			dprintf("        local %d: ", n);
+			printType(blocks[0]->locals[n], blocks[0]->locals_info[n]);
+			dprintf("\n");
+		}
+	} );
+	
+	
+	if (!verifyMethod3b(einfo, method, status, blocks, numBlocks, sigs)) {
+		FAIL;
+	}
+	
+	
+	CLEANUP;
+	DBG(VERIFY3, dprintf("    Verify Method 3b: done\n"); );
 	return(true);
+	
+
+#undef FAIL
+#undef CLEANUP
 }
 
 
@@ -1130,10 +1279,10 @@
 	int32 low, high;
 	
 	bool wide;
-	// bool inABlock; // used when calculating the start/return address of each block
+	bool inABlock; // used when calculating the start/return address of each block
 	
 	uint32 blockCount  = 0;
-	// BlockInfo** blocks = NULL;
+	BlockInfo** blocks = NULL;
 	
 	
 	DBG(VERIFY3, dprintf("    Verifier Pass 3a: checking static constraints and finding basic blocks...\n"); );
@@ -1688,10 +1837,40 @@
 	
 	DBG(VERIFY3, dprintf("        done, %d blocks found.\n", blockCount); );
 	
-	// TODO: memory allocation here
+	
+	DBG(VERIFY3, dprintf("    Verifier Pass 3a: third pass to allocate memory for basic blocks...\n"); );
+	
+	blocks = checkPtr((BlockInfo**)KMALLOC(blockCount * sizeof(BlockInfo*)));
+	
+	for (inABlock = true, n = 0, pc = 0; pc < codelen; pc++) {
+		if (status[pc] & START_BLOCK) {
+			blocks[n] = createBlock(method);
+			blocks[n]->startAddr = pc;
+			n++;
+			
+			inABlock = true;
+			
+			
+			DBG(VERIFY3, dprintf("        setting blocks[%d]->startAddr = %d\n",
+					     n-1, blocks[n-1]->startAddr); );
+		}
+		
+		if (inABlock && (status[pc] & END_BLOCK)) {
+			blocks[n-1]->lastAddr = pc;
+			
+			inABlock = false;
+			
+			
+			DBG(VERIFY3, dprintf("        setting blocks[%d]->lastAddr = %d\n",
+					     n-1, blocks[n-1]->lastAddr); );
+		}
+	}
+	
+	
+	DBG(VERIFY3, dprintf("    Verifier Pass 3a: done\n"); );
 	
 	*numBlocks = blockCount;
-	return NULL;
+	return blocks;
 	
 	
 #undef CHECK_LOCAL_INDEX	
@@ -1705,6 +1884,2778 @@
 }
 
 
+/*
+ * verifyMethod3b()
+ *    The Data-flow Analyzer
+ *
+ * The data-flow algorithm is taken from the JVM 2 spec, which describes it more or less as follows:
+ *
+ *  0  data-flow analyzer is initialised
+ *       - for the first instruction of the method, the local variables that represent parameters
+ *         initially contain values of the types indicated by the method's type descriptor.
+ *       - the operand stack is empty.
+ *       - all local variables contain an illegal value.
+ *       - for the other instructions, which have not been examined yet, no information is available
+ *         regarding the operand stack or local variables.
+ *       - the "changed" bit is only set for the first instruction.
+ *
+ *  1  select a VM instruction whose "changed" bit is set
+ *
+ *       - if no such instruction remains, the method has successfully been verified.
+ *       - otherwise, turn off the "changed" bit of the selected instruction.
+ *
+ *  2  model the effect of the instruction on the operand stack and local variable array by:
+ *
+ *       - if the instruction uses values from the operand stack, ensure that there are a
+ *         sufficient number of values on the stack and that the top values on the stack are
+ *         of an appropriate type.
+ *       - if the instruction uses a local variable, ensure that the specified local variable
+ *         contains a value of the appropriate type.
+ *       - if the instruction pushes values onto the operand stack, ensure that there is sufficient
+ *         room on the operand stack for the new values.  add the indicated types to the type of the
+ *         modeled operand stack.
+ *       - if the instruction modifies a local variable, record that the local variable now contains
+ *         a type.
+ *
+ *  3  determine the instructions that can follow the current instruction.  successor instructions
+ *     can be one of the following:
+ *
+ *       - the next instruction, if the current instruction is not an unconditional control tranfer
+ *         instruction (ie - goto, return, or athrow).  basically check to make sure you don't
+ *         "fall off" the last instruction of the method.
+ *       - the target of a conditional or unconditional branch or switch.
+ *       - any exception handlers for this instruction.
+ *
+ *  4  merge the state of the operand stack and local variable array at the end of the execution of the
+ *     current instruction into each of the successor instructions.
+ *
+ *     (see merge function below)
+ *
+ *  5  continue at step 1.
+ */
+static
+bool
+verifyMethod3b(errorInfo* einfo, const Method* method,
+	       const uint32* status,
+	       BlockInfo** blocks, const uint32 numBlocks,
+	       SigStack* sigs)
+{
+	const uint32 codelen      = METHOD_BYTECODE_LEN(method);
+	const unsigned char* code = METHOD_BYTECODE_CODE(method);
+	
+	uint32 curIndex;
+	BlockInfo* curBlock;
+	BlockInfo* nextBlock;
+	
+#define VERIFY_ERROR(_MSG) \
+        KFREE(curBlock); \
+        if (einfo->type == 0) { \
+        	postExceptionMessage(einfo, JAVA_LANG(VerifyError), \
+				     "in method \"%s.%s\": %s", \
+				     CLASS_CNAME(method->class), METHOD_NAMED(method), _MSG); \
+	} \
+	return(false)
+	
+	
+	uint32 pc = 0, newpc = 0, n = 0;
+	int32 high = 0, low = 0;  // for the switching instructions
+	
+	
+	
+	DBG(VERIFY3, dprintf("    Verifier Pass 3b: Data Flow Analysis and Type Checking...\n"); );
+	DBG(VERIFY3, dprintf("        memory allocation...\n"); );
+	curBlock = createBlock(method);
+	
+	
+	DBG(VERIFY3, dprintf("        doing the dirty data flow analysis...\n"); );
+	blocks[0]->status |= CHANGED;
+	curIndex = 0;
+	while(curIndex < numBlocks) {
+		DBG(VERIFY3,
+		    dprintf("      blockNum/first pc/changed/stksz = %d / %d / %d / %d\n",
+			    curIndex,
+			    blocks[curIndex]->startAddr,
+			    blocks[curIndex]->status & CHANGED,
+			    blocks[curIndex]->stacksz);
+		    dprintf("          before:\n");
+		    printBlock(method, blocks[curIndex], "                 ");
+		    );
+		
+		if (!(blocks[curIndex]->status & CHANGED)) {
+			DBG(VERIFY3, dprintf("        not changed...skipping\n"); );
+			curIndex++;
+			continue;
+		}
+		
+		blocks[curIndex]->status ^= CHANGED; // unset CHANGED bit
+		blocks[curIndex]->status |= VISITED; // make sure we've visited it...important for merging
+		copyBlockData(method, blocks[curIndex], curBlock);
+		
+		if (curBlock->status & EXCEPTION_HANDLER && curBlock->stacksz > 0) {
+			VERIFY_ERROR("it's possible to reach an exception handler with a nonempty stack");
+		}
+		
+		
+		if (!verifyBasicBlock(einfo, method, curBlock, &sigs)) {
+			VERIFY_ERROR("failure to verify basic block");
+		}
+		
+		DBG(VERIFY3,
+		    dprintf("          after:\n");
+		    printBlock(method, curBlock, "                 ");
+		    );
+		
+		
+		//
+		// merge this block's information into the next block
+		//
+		pc = curBlock->lastAddr;
+		if (code[pc] == WIDE && code[pc + insnLen[code[pc]]] == RET)
+			pc += insnLen[code[pc]];
+		switch(code[pc])
+			{
+			case GOTO:
+				newpc = pc + 1;
+				newpc = pc + WORD(code, newpc);
+				nextBlock = inWhichBlock(newpc, blocks, numBlocks);
+				
+				if (!merge(einfo, method, curBlock, nextBlock)) {
+					VERIFY_ERROR("error merging operand stacks");
+				}
+				break;
+				
+			case GOTO_W:
+				newpc = pc + 1;
+				newpc = pc + DWORD(code, newpc);
+				nextBlock = inWhichBlock(newpc, blocks, numBlocks);
+				
+				if (!merge(einfo, method, curBlock, nextBlock)) {
+					VERIFY_ERROR("error merging operand stacks");
+				}
+				break;
+					
+			case JSR:
+				newpc = pc + 1;
+				newpc = pc + WORD(code, newpc);
+				goto JSR_common;
+			case JSR_W:
+				newpc = pc + 1;
+				newpc = pc + DWORD(code, newpc);
+			JSR_common:
+				nextBlock = inWhichBlock(newpc, blocks, numBlocks);
+				
+				if (!merge(einfo, method, curBlock, nextBlock)) {
+					VERIFY_ERROR("jsr: error merging operand stacks");
+				}
+				break;
+				
+			case RET:
+				if (status[pc] & WIDE_MODDED) {
+					n = pc + 1;
+					n = WORD(code, n);
+				} else {
+					n = code[pc + 1];
+				}
+				
+				if (curBlock->locals[n] != TADDR) {
+					VERIFY_ERROR("ret instruction does not refer to a variable with type returnAddress");
+				}
+				// each instance of return address can only be used once
+				curBlock->locals[n] = TUNSTABLE;
+				
+				newpc = curBlock->locals_info[n];
+				curBlock->locals_info[n] = 0;
+				nextBlock = inWhichBlock(newpc, blocks, numBlocks);
+				
+				if (!merge(einfo, method, curBlock, nextBlock)) {
+					VERIFY_ERROR("error merging opstacks when returning from a subroutine");
+				}
+				break;
+				
+				
+			case IF_ACMPEQ:  case IFNONNULL:
+			case IF_ACMPNE:  case IFNULL:
+			case IF_ICMPEQ:  case IFEQ:
+			case IF_ICMPNE:	 case IFNE:
+			case IF_ICMPGT:	 case IFGT:
+			case IF_ICMPGE:	 case IFGE:
+			case IF_ICMPLT:	 case IFLT:
+			case IF_ICMPLE:	 case IFLE:
+				newpc     = pc + 1;
+				newpc     = pc + WORD(code, newpc);
+				nextBlock = inWhichBlock(newpc, blocks, numBlocks);
+				
+				if (!merge(einfo, method, curBlock, nextBlock)) {
+					VERIFY_ERROR("error merging operand stacks");
+				}
+				
+				// if the condition is false, then the next block is the one that will be executed
+				curIndex++;
+				if (curIndex >= numBlocks) {
+					VERIFY_ERROR("execution falls off the end of a basic block");
+				}
+				else if (!merge(einfo, method, curBlock, blocks[curIndex])) {
+					VERIFY_ERROR("error merging operand stacks");
+				}
+				break;
+				
+				
+			case LOOKUPSWITCH:
+				// default branch...between 0 and 3 bytes of padding are added so that the
+				// default branch is at an address that is divisible by 4
+				n = (pc + 1) % 4;
+				if (n) n = pc + 5 - n;
+				else   n = pc + 1;
+				newpc = pc + DWORD(code, n);
+				nextBlock = inWhichBlock(newpc, blocks, numBlocks);
+				if (!merge(einfo, method, curBlock, nextBlock)) {
+					VERIFY_ERROR("error merging into the default branch of a lookupswitch instruction");
+				}
+				
+				// get number of key/target pairs
+				n += 4;
+				low = DWORD(code, n);
+				
+				// branch into all targets
+				for (n += 4, high = n + 8*low; n < high; n += 8) {
+					newpc = pc + DWORD(code, n+4);
+					nextBlock = inWhichBlock(newpc, blocks, numBlocks);
+					if (!merge(einfo, method, curBlock, nextBlock)) {
+						VERIFY_ERROR("error merging into a branch of a lookupswitch instruction");
+					}
+				}
+				
+				break;
+				
+			case TABLESWITCH:
+				// default branch...between 0 and 3 bytes of padding are added so that the
+				// default branch is at an address that is divisible by 4
+				n = (pc + 1) % 4;
+				if (n) n = pc + 5 - n;
+				else   n = pc + 1;
+				newpc = pc + DWORD(code, n);
+				
+				// get the high and low values of the table
+				low  = DWORD(code, n + 4);
+				high = DWORD(code, n + 8);
+				
+				n += 12;
+				
+				// high and low are used as temps in this loop that checks
+				// the validity of all the branches in the table
+				for (high = n + 4*(high - low + 1); n < high; n += 4) {
+					newpc = pc + DWORD(code, n);
+					nextBlock = inWhichBlock(newpc, blocks, numBlocks);
+					if (!merge(einfo, method, curBlock, nextBlock)) {
+						VERIFY_ERROR("error merging into a branch of a tableswitch instruction");
+					}
+				}
+				break;
+				
+				
+				// the rest of the ways to end a block
+			case RETURN:
+			case ARETURN:
+			case IRETURN:
+			case FRETURN:
+			case LRETURN:
+			case DRETURN:
+			case ATHROW:
+				curIndex++;
+				continue;
+				
+			default:
+				for (n = pc + 1; n < codelen; n++) {
+					if (status[n] & IS_INSTRUCTION) break;
+				}
+				if (n == codelen) {
+					VERIFY_ERROR("execution falls off the end of a code block");
+				}
+				else if (!merge(einfo, method, curBlock, blocks[curIndex+1])) {
+					VERIFY_ERROR("error merging operand stacks");
+				}
+			}
+		
+		
+		for (curIndex = 0; curIndex < numBlocks; curIndex++) {
+			if (blocks[curIndex]->status & CHANGED)
+				break;
+		}
+	}
+	
+	
+	DBG(VERIFY3, dprintf("    Verifier Pass 3b: Complete\n"); );
+	KFREE(curBlock);
+	return(true);
+	
+#undef VERIFY_ERROR
+#undef RETURN_3B
+}
+
+
+/*
+ * merges two operand stacks.  just to repeat what the JVML 2 spec says about this:
+ *   Merge the state of the operand stack and local variable array at the end of the
+ *   execution of the current instruction into each of the successor instructions.  In
+ *   the special case of control transfer to an exception handler, the operand stack is
+ *   set to contain a single object of the exception type indicated by the exception
+ *   handler information.
+ *     - if this if the first time the successor instruction has been visited, record
+ *       that the operand stack and local variable values calculated in steps 2 and 3
+ *       are the state of the operand stack and local variable array prior to executing
+ *       the successor instruction.  Set the "changed" bit for the successor instruction.
+ *     - if the successor instruction has been seen before, merge the operand stack and
+ *       local variable values calculated in steps 2 and 3 into the values already there.
+ *       set the "changed" bit if there is any modification to the values.
+ *
+ *   to merge two operand stacks, the number of values on each stack must be identical.
+ *   the types of values on the stacks must also be identical, except that differently
+ *   typed reference values may appear at corresponding places on the two stacks.  in this
+ *   case, the merged operand stack contains a reference to an instance of the first common
+ *   superclass of the two types.  such a reference type always exists because the type Object
+ *   is a superclass of all class and interface types.  if the operand stacks cannot be merged,
+ *   verification of the method fails.
+ *
+ *   to merge two local variable array states, corresponding pairs of local variables are
+ *   compared.  if the two types are not identical, then unless both contain reference values,
+ *   the verification records that the local variable contains an unusable value.  if both of
+ *   the pair of local variables contain reference values, the merged state contains a reference
+ *   to an instance of the first common superclass of the two types.
+ */
+static
+bool
+merge(errorInfo* einfo,
+      const Method* method,
+      BlockInfo* fromBlock,
+      BlockInfo* toBlock)
+{
+	uint32 n;
+	Hjava_lang_Class* type;
+	uint32 tinfo = 0;
+	
+	if (!(toBlock->status & VISITED)) {
+		DBG(VERIFY3, dprintf("          visiting block starting at %d for the first time\n",
+				     toBlock->startAddr); );
+		
+		copyBlockState(method, fromBlock, toBlock);
+		toBlock->status |= CHANGED;
+		return(true);
+	}
+	
+	DBG(VERIFY3,
+	    dprintf("%snot a first time merge\n", indent);
+	    dprintf("%s  from block:\n", indent);
+	    printBlock(method, fromBlock, indent2);
+	    dprintf("%s  to block:\n", indent);
+	    printBlock(method, toBlock, indent2);
+	    dprintf("\n");
+	    );
+	
+	
+	if (fromBlock->stacksz != toBlock->stacksz) {
+		postExceptionMessage(einfo, JAVA_LANG(VerifyError),
+				     "in method %s.%s: merging two operand stacks of unequal size",
+				     method->name->data,
+				     method->class->name->data);
+		return(false);
+	}
+	
+	
+	// merge the local variable arrays
+	for (n = 0; n < method->localsz; n++) {
+		type = mergeTypes(einfo, method->class,
+				  fromBlock->locals[n], fromBlock->locals_info[n],
+				  toBlock->locals[n], toBlock->locals_info[n],
+				  &tinfo);
+		
+		if (toBlock->locals[n] != type) {
+			DBG(VERIFY3,
+			    dprintf("%smerging locals[%d] ", indent2, n);
+			    printType(fromBlock->locals[n], fromBlock->locals_info[n]);
+			    dprintf(" and ");
+			    printType(toBlock->locals[n], toBlock->locals_info[n]);
+			    dprintf(" to get ");
+			    printType(type, tinfo);
+			    dprintf("\n");
+			    );
+			
+			toBlock->locals[n] = type;
+			toBlock->locals_info[n] = tinfo;
+			toBlock->status |= CHANGED;
+		}
+	}
+	
+	// merge the operand stacks
+	for (n = 0; n < fromBlock->stacksz; n++) {
+		type = mergeTypes(einfo, method->class,
+				  fromBlock->opstack[n], fromBlock->opstack_info[n],
+				  toBlock->opstack[n], toBlock->opstack_info[n],
+				  &tinfo);
+		
+		if (toBlock->opstack[n] != type) {
+			DBG(VERIFY3,
+			    dprintf("%smerging opstack[%d] ", indent2, n);
+			    printType(fromBlock->opstack[n], fromBlock->opstack_info[n]);
+			    dprintf(" and ");
+			    printType(toBlock->opstack[n], toBlock->opstack_info[n]);
+			    dprintf(" to get ");
+			    printType(type, tinfo);
+			    dprintf("\n");
+			    );
+			
+			toBlock->opstack[n] = type;
+			toBlock->opstack_info[n] = tinfo;
+			toBlock->status |= CHANGED;
+			
+			// if we get unstable here, not really a big deal until we try to use it.
+			// i mean, we could get an unstable value and then immediately pop it off the stack,
+			// for instance.
+		}
+	}
+	
+	
+	DBG(VERIFY3,
+	    dprintf("%s  result block:\n", indent);
+	    printBlock(method, toBlock, indent2);
+	    );
+	
+	
+	return(true);
+}
+
+
+
+
+/*
+ * verifyBasicBlock()
+ *   Simulates execution of a basic block by modifying its simulated operand stack and local variable array.
+ */
+static
+bool
+verifyBasicBlock(errorInfo* einfo, const Method* method, BlockInfo* block, SigStack** sigs)
+{
+	/**************************************************************************************************
+	 * VARIABLES
+	 **************************************************************************************************/
+	uint32         pc   = 0;
+	unsigned char* code = METHOD_BYTECODE_CODE(method);
+	Hjava_lang_Class* this = method->class;
+	
+	bool wide = false;       // was the previous opcode a WIDE instruction?
+	
+	uint32 n = 0;            // used as a general temporary variable, often as a temporary pc
+	
+	Hjava_lang_Class* type = NULL; // used often to hold the type of something to be pushed
+				       // onto/popped off of the stack
+	Hjava_lang_Class* arrayType;
+	
+	int tag;                // used for constant tag stuff
+	
+	uint32     idx;         // index into constant pool
+	constants* pool = CLASS_CONSTANTS(method->class);
+	
+	const char* sig;
+	
+	
+	/**************************************************************************************************
+	 * HANDY MACROS USED ONLY IN THIS METHOD
+	 *    most of these belong to one of two categories:
+	 *         - those dealing with locals variables
+	 *         - those dealing with the operand stack
+	 **************************************************************************************************/
+#define VERIFY_ERROR(_MSG) \
+	if (einfo->type == 0) { \
+		postExceptionMessage(einfo, JAVA_LANG(VerifyError), \
+				     "in method \"%s.%s\": %s", \
+				     CLASS_CNAME(this), METHOD_NAMED(method), _MSG); \
+	} \
+	return(false)
+
+#define GET_IDX idx = code[pc + 1]
+	
+#define GET_WIDX idx = pc + 1; idx = WORD(code, idx)
+	
+	
+	// checks whether the specified local variable is of the specified type.
+#define ENSURE_LOCAL_TYPE(_N, _T) \
+	if (_T == TLONG || _T == TDOUBLE) { \
+		if (_T != block->locals[_N]) { \
+			VERIFY_ERROR("local variable not of correct type"); \
+		} \
+		else if (block->locals[_N + 1] != TWIDE) { \
+			VERIFY_ERROR("accessing a long or double in a local where the following local has been corrupted"); \
+		} \
+	} \
+	else { \
+		if (typecheck(einfo, this, _T, 0, block->locals[_N], LOCALS_INFO(_N)) == false) { \
+			if (block->locals[_N] == TUNSTABLE) { \
+				VERIFY_ERROR("attempt to access an unstable local variable"); \
+			} else { \
+				VERIFY_ERROR("attempt to access a local variable not of the correct type"); \
+			} \
+		} \
+	}
+	
+#define ENSURE_OPSTACK_SIZE(_N) \
+	if (block->stacksz < (_N)) { \
+                DBG(VERIFY3, dprintf("                here's the stack: \n"); printBlock(method, block, "                    "); ); \
+		VERIFY_ERROR("not enough items on stack for operation"); \
+	}
+	
+	// the nth item on the operand stack from the top
+#define OPSTACK_ITEM(_N) \
+	(block->opstack[block->stacksz - _N])
+	
+#define OPSTACK_TOP  OPSTACK_ITEM(1)
+#define OPSTACK_WTOP OPSTACK_ITEM(2)
+
+#define OPSTACK_INFO(_N) \
+        (block->opstack_info[block->stacksz - _N])
+
+#define LOCALS_INFO(_N) \
+	(block->locals_info[_N])
+	
+#define CHECK_STACK_OVERFLOW(_N) \
+	if (block->stacksz + _N > method->stacksz) { \
+		DBG(VERIFY3, dprintf("                block->stacksz: %d :: N = %d :: method->stacksz = %d\n", \
+				     block->stacksz, _N, method->stacksz); ); \
+                DBG(VERIFY3, dprintf("                here's the stack: \n"); printBlock(method, block, "                    "); ); \
+		VERIFY_ERROR("stack overflow"); \
+	}
+	
+#define CHECK_STACK_UNDERFLOW(_N) \
+	if (block->stacksz < _N) { \
+                DBG(VERIFY3, dprintf("                here's the stack: \n"); printBlock(method, block, "                    "); ); \
+		VERIFY_ERROR("stack underflow"); \
+	}
+	
+	// push a type onto the stack without checking if there's enough
+	// space (useful if you _know_ there is enough space)
+#define OPSTACK_PUSH_BLIND_INFO(_T, _TI) \
+	block->opstack[block->stacksz] = (_T); \
+        block->opstack_info[block->stacksz] = (_TI); \
+	block->stacksz++
+	
+#define OPSTACK_PUSH_BLIND(_T) \
+	OPSTACK_PUSH_BLIND_INFO(_T, 0)
+	
+#define OPSTACK_WPUSH_BLIND(_T) \
+	OPSTACK_PUSH_BLIND(_T); \
+	OPSTACK_PUSH_BLIND(TWIDE)
+	
+	// push a type onto the operand stack, checking first to make sure
+	// there's enough space
+#define OPSTACK_PUSH_INFO(_T, _TI) \
+	CHECK_STACK_OVERFLOW(1); \
+        OPSTACK_PUSH_BLIND_INFO(_T, _TI)
+
+#define OPSTACK_PUSH(_T) \
+	OPSTACK_PUSH_INFO(_T, 0)
+	
+	// push a wide type onto the operand stack, checking first to make
+	// sure there's enough space
+#define OPSTACK_WPUSH(_T) \
+	CHECK_STACK_OVERFLOW(2); \
+        OPSTACK_WPUSH_BLIND(_T)
+	
+	// throw a verify error representing an empty stack
+#define EMPTY_STACK_ERROR \
+        VERIFY_ERROR("popping value off an empty stack")
+	
+	
+	// ensure that the top item on the stack is of type _T
+#define OPSTACK_PEEK_T(_T, _TI) \
+        ENSURE_OPSTACK_SIZE(1); \
+	if (!typecheck(einfo, this, (_T), _TI, OPSTACK_ITEM(1), OPSTACK_INFO(1))) { \
+		VERIFY_ERROR("mismatched stack types"); \
+	}
+	
+	// ensure that the top item on the stack is of wide type _T
+#define OPSTACK_WPEEK_T(_T, _TI) \
+	if (block->stacksz < 2) { \
+		VERIFY_ERROR("popping wide value off a stack with < 2 items"); \
+	} else if (OPSTACK_TOP != TWIDE) { \
+		VERIFY_ERROR("trying to pop a wide value off operand stack where there is none"); \
+	} else if (typecheck(einfo, this, _T, _TI, OPSTACK_ITEM(2), OPSTACK_INFO(2)) == false) { \
+		VERIFY_ERROR("mismatched stack types"); \
+	}
+	
+	
+#define OPSTACK_POP_BLIND \
+	block->opstack[--block->stacksz] = TUNSTABLE; \
+	block->opstack_info[block->stacksz] = 0
+
+#define OPSTACK_WPOP_BLIND \
+	OPSTACK_POP_BLIND; \
+	OPSTACK_POP_BLIND
+	
+	
+	// pop _N things off the stack off the stack
+#define OPSTACK_POP_N_BLIND(_N) \
+	for (n = 0; n < _N; n++) { \
+		OPSTACK_POP_BLIND; \
+	}
+	
+#define OPSTACK_POP_N(_N) \
+        CHECK_STACK_UNDERFLOW(_N); \
+	OPSTACK_POP_N_BLIND(_N)
+	
+#define OPSTACK_POP \
+        CHECK_STACK_UNDERFLOW(1); \
+	OPSTACK_POP_BLIND
+
+#define OPSTACK_WPOP \
+	CHECK_STACK_UNDERFLOW(2); \
+	OPSTACK_WPOP_BLIND
+	
+	// pop a type off the stack and typecheck it
+#define OPSTACK_POP_T(_T, _TI) \
+        ENSURE_OPSTACK_SIZE(1); \
+        if (!typecheck(einfo, this, _T, _TI, OPSTACK_ITEM(1), OPSTACK_INFO(1))) { \
+		DBG(VERIFY3, \
+		    dprintf("                OPSTACK_TOP: "); \
+		    printType(OPSTACK_ITEM(1), OPSTACK_INFO(1)); \
+		    dprintf(" vs. what's we wanted: "); \
+		    printType(_T, _TI); dprintf("\n"); ); \
+		VERIFY_ERROR("top of opstack does not have desired type"); \
+	} \
+        OPSTACK_POP_BLIND
+	
+	// pop a wide type off the stack and typecheck it
+#define OPSTACK_WPOP_T(_T, _TI) \
+        OPSTACK_WPEEK_T(_T, _TI); \
+        OPSTACK_WPOP_BLIND
+	
+	
+	/**************************************************************************************************
+	 * BLOCK-LEVEL DATA FLOW ANALYASIS
+	 *    this is actually pretty easy, since there are never any branches.  basically, it just
+	 *    manipulates the working stack after every instruction as if it were actually running the
+	 *    code so that, after verifying the block, the working block can be used to merge this block
+	 *    with its successors.
+	 **************************************************************************************************/
+	DBG(VERIFY3,
+	    dprintf("        about to verify the block...\n");
+	    dprintf("        block->startAddr = %d, block->lastAddr = %d, first instruction = %d\n",
+		    block->startAddr, block->lastAddr, code[block->startAddr]);
+	    );
+	
+	pc = block->startAddr;
+	while (pc <= block->lastAddr) {
+		DBG(VERIFY3,
+		    dprintf("            pc = %d, opcode = %d == ", pc, code[pc]);
+		    printInstruction(code[pc]);
+		    dprintf("\n");
+		    );
+		
+		switch(code[pc]) {
+			/**************************************************************
+			 * INSTRUCTIONS FOR PUSHING CONSTANTS ONTO THE STACK
+			 **************************************************************/
+			// pushes NULL onto the stack, which matches any object
+		case ACONST_NULL:
+			OPSTACK_PUSH(TNULL);
+			break;
+			
+			// iconst_<n> pushes n onto the stack
+		case ICONST_0: case ICONST_1: case ICONST_2:
+		case ICONST_3: case ICONST_4: case ICONST_5:
+			
+		case ICONST_M1: // pushes -1 onto the stack
+		case BIPUSH:    // sign extends an 8-bit int to 32-bits and pushes it onto stack
+		case SIPUSH:    // sign extends a 16-bit int to 32-bits and pushes it onto stack
+			OPSTACK_PUSH(TINT);
+			break;
+			
+		case FCONST_0:
+		case FCONST_1:
+		case FCONST_2:
+			OPSTACK_PUSH(TFLOAT);
+			break;
+			
+		case LCONST_0:
+		case LCONST_1:
+			OPSTACK_WPUSH(TLONG);
+			break;
+			
+		case DCONST_0:
+		case DCONST_1:
+			OPSTACK_WPUSH(TDOUBLE);
+			break;
+			
+			
+		case LDC1:
+			GET_IDX;
+			goto LDC_common;
+		case LDC2:
+			GET_WIDX;
+		LDC_common:
+			tag = CONST_TAG(idx, pool);
+			switch(tag) {
+			case CONSTANT_Integer: OPSTACK_PUSH(TINT);    break;
+			case CONSTANT_Float:   OPSTACK_PUSH(TFLOAT);  break;
+			case CONSTANT_ResolvedString:
+			case CONSTANT_String:  OPSTACK_PUSH(TSTRING); break;
+			}
+			break;
+			
+		case LDC2W:
+			GET_WIDX;
+			tag = CONST_TAG(idx, pool);
+			if (tag == CONSTANT_Long) {
+				OPSTACK_WPUSH(TLONG);
+			} else {
+				OPSTACK_WPUSH(TDOUBLE);
+			}
+			break;
+			
+			
+			/**************************************************************
+			 * INSTRUCTIONS DEALING WITH THE LOCALS AND STACK
+			 **************************************************************/
+		case POP:
+			OPSTACK_POP;
+			break;
+		case POP2:
+			OPSTACK_POP;
+			OPSTACK_POP;
+			break;
+			
+			
+#define GET_CONST_INDEX \
+			if (wide == true) { GET_WIDX; } \
+			else              { GET_IDX;  }
+			
+			
+			// aload_<n> takes the object reference in location <n> and pushes it onto the stack
+		case ALOAD_0: idx = 0; goto ALOAD_common;
+		case ALOAD_1: idx = 1; goto ALOAD_common;
+		case ALOAD_2: idx = 2; goto ALOAD_common;
+		case ALOAD_3: idx = 3; goto ALOAD_common;
+		case ALOAD:
+			GET_CONST_INDEX;
+		ALOAD_common:
+			if (!isReference(block->locals[idx], LOCALS_INFO(idx))) {
+				VERIFY_ERROR("aload<_n> where local variable does not contain an object reference");
+			}
+			
+			OPSTACK_PUSH_INFO(block->locals[idx], LOCALS_INFO(idx));
+			break;
+			
+			
+			// stores whatever's on the top of the stack in local <n>
+		case ASTORE_0: idx = 0; goto ASTORE_common;
+		case ASTORE_1: idx = 1; goto ASTORE_common;
+		case ASTORE_2: idx = 2; goto ASTORE_common;
+		case ASTORE_3: idx = 3; goto ASTORE_common;
+		case ASTORE:
+			GET_CONST_INDEX;
+		ASTORE_common:
+			ENSURE_OPSTACK_SIZE(1);
+			type = OPSTACK_ITEM(1);
+			
+			if (type != TADDR && !isReference(type, OPSTACK_INFO(1))) {
+				VERIFY_ERROR("astore: thing on top of stack is not a reference type");
+			}
+			
+			LOCALS_INFO(idx)   = OPSTACK_INFO(1);
+			block->locals[idx] = type;
+			
+			OPSTACK_POP_BLIND;
+			break;
+			
+			
+			
+			// iload_<n> takes the variable in location <n> and pushes it onto the stack
+		case ILOAD_0: idx = 0; goto ILOAD_common;
+		case ILOAD_1: idx = 1; goto ILOAD_common;
+		case ILOAD_2: idx = 2; goto ILOAD_common;
+		case ILOAD_3: idx = 3; goto ILOAD_common;
+		case ILOAD:
+			GET_CONST_INDEX;
+		ILOAD_common:
+			ENSURE_LOCAL_TYPE(idx, TINT);
+			OPSTACK_PUSH(TINT);
+			OPSTACK_INFO(1) = LOCALS_INFO(idx);
+			break;
+			
+			
+		case ISTORE_0: idx =0; goto ISTORE_common;
+		case ISTORE_1: idx =1; goto ISTORE_common;
+		case ISTORE_2: idx =2; goto ISTORE_common;
+		case ISTORE_3: idx =3; goto ISTORE_common;
+		case ISTORE:
+			GET_CONST_INDEX;
+		ISTORE_common:
+			LOCALS_INFO(idx) = OPSTACK_INFO(1);
+			block->locals[idx] = TINT;
+			block->locals_info[idx] = 0;
+			
+			OPSTACK_POP_T(TINT, 0);
+			break;
+			
+			
+			// fload_<n> takes the variable at location <n> and pushes it onto the stack
+		case FLOAD_0: idx =0; goto FLOAD_common;
+		case FLOAD_1: idx =1; goto FLOAD_common;
+		case FLOAD_2: idx =2; goto FLOAD_common;
+		case FLOAD_3: idx = 3; goto FLOAD_common;
+		case FLOAD:
+			GET_CONST_INDEX;
+		FLOAD_common:
+			ENSURE_LOCAL_TYPE(idx, TFLOAT);
+			OPSTACK_PUSH_INFO(TFLOAT, LOCALS_INFO(idx));
+			break;
+			
+			
+			// stores a float from top of stack into local <n>
+		case FSTORE_0: idx = 0; goto FSTORE_common;
+		case FSTORE_1: idx = 1; goto FSTORE_common;
+		case FSTORE_2: idx = 2; goto FSTORE_common;
+		case FSTORE_3: idx = 3; goto FSTORE_common;
+		case FSTORE:
+			GET_CONST_INDEX;
+		FSTORE_common:
+			LOCALS_INFO(idx) = OPSTACK_INFO(1);
+			block->locals[idx] = TFLOAT;
+			block->locals_info[idx] = 0;
+			
+			OPSTACK_POP_T(TFLOAT, 0);
+			break;
+			
+			
+			// lload_<n> takes the variable at location <n> and pushes it onto the stack
+		case LLOAD_0: idx = 0; goto LLOAD_common;
+		case LLOAD_1: idx = 1; goto LLOAD_common;
+		case LLOAD_2: idx = 2; goto LLOAD_common;
+		case LLOAD_3: idx = 3; goto LLOAD_common;
+		case LLOAD:
+			GET_CONST_INDEX;
+		LLOAD_common:
+			ENSURE_LOCAL_TYPE(idx, TLONG);
+			OPSTACK_WPUSH(TLONG);
+			OPSTACK_INFO(1) = 0;
+			OPSTACK_INFO(2) = LOCALS_INFO(idx);
+			break;
+			
+			
+			// lstore_<n> stores a long from top of stack into local <n>
+		case LSTORE_0: idx = 0; goto LSTORE_common;
+		case LSTORE_1: idx = 1; goto LSTORE_common;
+		case LSTORE_2: idx = 2; goto LSTORE_common;
+		case LSTORE_3: idx = 3; goto LSTORE_common;
+		case LSTORE:
+			GET_CONST_INDEX;
+		LSTORE_common:
+			LOCALS_INFO(idx)       = OPSTACK_INFO(1);
+			LOCALS_INFO(idx + 1)   = 0;
+			block->locals[idx]     = TLONG;
+			block->locals[idx + 1] = TWIDE;
+			block->locals_info[idx]   = 0;
+			block->locals_info[idx+1] = 0;
+			
+			OPSTACK_WPOP_T(TLONG, 0);
+			break;
+			
+			
+			// dload_<n> takes the double at local <n> and pushes it onto the stack
+		case DLOAD_0: idx = 0; goto DLOAD_common;
+		case DLOAD_1: idx = 1; goto DLOAD_common;
+		case DLOAD_2: idx = 2; goto DLOAD_common;
+		case DLOAD_3: idx = 3; goto DLOAD_common;
+		case DLOAD:
+			GET_CONST_INDEX;
+		DLOAD_common:
+			ENSURE_LOCAL_TYPE(idx, TDOUBLE);
+			OPSTACK_WPUSH(TDOUBLE);
+			OPSTACK_INFO(1) = 0;
+			OPSTACK_INFO(2) = LOCALS_INFO(idx);
+			break;
+			
+			
+			// dstore stores a double from the top of stack into a local variable
+		case DSTORE_0: idx = 0; goto DSTORE_common;
+		case DSTORE_1: idx = 1; goto DSTORE_common;
+		case DSTORE_2: idx = 2; goto DSTORE_common;
+		case DSTORE_3: idx = 3; goto DSTORE_common;
+		case DSTORE:
+			GET_CONST_INDEX;
+		DSTORE_common:
+			LOCALS_INFO(idx)       = OPSTACK_INFO(1);
+			LOCALS_INFO(idx + 1)   = 0;
+			block->locals[idx]     = TDOUBLE;
+			block->locals[idx + 1] = TWIDE;
+			block->locals_info[idx]   = 0;
+			block->locals_info[idx+1] = 0;
+			
+			OPSTACK_WPOP_T(TDOUBLE, 0);
+			break;
+			
+			
+#undef GET_CONST_INDEX
+			/**************************************************************
+			 * ARRAY INSTRUCTIONS!
+			 **************************************************************/
+			// i put ANEWARRAY code by NEW instead of in the array instructions
+			// section because of similarities with NEW
+			
+			
+			// for creating a primitive array
+		case NEWARRAY:
+			OPSTACK_POP_T(TINT, 0);   // array size
+			
+			switch(code[pc + 1]) {
+			case 4:  OPSTACK_PUSH(TBOOLARR);   break;
+			case 5:  OPSTACK_PUSH(TCHARARR);   break;
+			case 6:  OPSTACK_PUSH(TFLOATARR);  break;
+			case 7:  OPSTACK_PUSH(TDOUBLEARR); break;
+			case 8:  OPSTACK_PUSH(TBYTEARR);   break;
+			case 9:  OPSTACK_PUSH(TSHORTARR);  break;
+			case 10: OPSTACK_PUSH(TINTARR);    break;
+			case 11: OPSTACK_PUSH(TLONGARR);   break;
+			default: VERIFY_ERROR("newarray of unknown type");
+			}
+			break;
+			
+		case ARRAYLENGTH:
+			ENSURE_OPSTACK_SIZE(1);
+			
+			type = OPSTACK_ITEM(1);
+			n    = OPSTACK_INFO(1);
+			if (n & CLASS_SIGSTR || n & CLASS_NAMESTR) {
+				sig = (const char*)type;
+				if (*sig != '[') {
+					VERIFY_ERROR("arraylength on something that is not an array");
+				}
+			} else if (!IS_ARRAY(type)) {
+				VERIFY_ERROR("arraylength on something that is not an array");
+			}
+			
+			OPSTACK_POP_BLIND;
+			OPSTACK_PUSH_BLIND(TINT);
+			break;
+			
+			
+#define ARRAY_LOAD(_T, _ARRT) \
+                                OPSTACK_POP_T(TINT, 0); \
+                                OPSTACK_POP_T(_ARRT, 0); \
+				OPSTACK_PUSH(_T);
+
+#define ARRAY_WLOAD(_T, _ARRT) \
+                                OPSTACK_POP_T(TINT, 0); \
+                                OPSTACK_POP_T(_ARRT, 0); \
+				OPSTACK_WPUSH(_T);
+			
+			
+		case AALOAD:
+			ENSURE_OPSTACK_SIZE(2);
+			
+			if (OPSTACK_ITEM(1) != TINT) {
+				VERIFY_ERROR("aaload: item on top of stack is not an integer");
+			}
+			
+			type = OPSTACK_ITEM(2);
+			if (OPSTACK_INFO(2) & CLASS_NAMESTR || OPSTACK_INFO(2) & CLASS_SIGSTR) {
+				sig = (char*)type;
+				
+				if (*sig != '[') {
+					DBG(VERIFY3, dprintf("aaload: thing on opstack that is not an array: %s\n", sig); );
+					
+					VERIFY_ERROR("aaload: thing on opstack is not an array");
+				}
+				
+				sig++;
+				type = (Hjava_lang_Class*)sig;
+			}
+			else {
+				if (!IS_ARRAY(type)) {
+					VERIFY_ERROR("AALOAD on type that is not an array");
+				}
+				
+				type = (Hjava_lang_Class*)(CLASS_CNAME(type) + 1);
+			}
+			
+			DBG(VERIFY3,
+			    dprintf("                array type: ");
+			    printType(type, CLASS_SIGSTR);
+			    dprintf("\n");
+			    );
+			
+			// only push signature here...don't try to load unless needed for type checking
+			OPSTACK_POP_BLIND;
+			OPSTACK_POP_BLIND;
+			
+			OPSTACK_PUSH_BLIND_INFO(type, CLASS_SIGSTR);
+			break;
+			
+		case IALOAD: ARRAY_LOAD(TINT,   TINTARR);   break;
+		case FALOAD: ARRAY_LOAD(TFLOAT, TFLOATARR); break;
+		case BALOAD: ARRAY_LOAD(TINT,   TBYTEARR);  break;
+		case CALOAD: ARRAY_LOAD(TINT,   TCHARARR);  break;
+		case SALOAD: ARRAY_LOAD(TINT,   TSHORTARR); break;
+			
+		case LALOAD: ARRAY_WLOAD(TLONG,   TLONGARR);   break;
+		case DALOAD: ARRAY_WLOAD(TDOUBLE, TDOUBLEARR); break;
+#undef ARRAY_LOAD
+#undef ARRAY_WLOAD
+			
+			
+#define ARRAY_STORE(_T, _ARRT) \
+				OPSTACK_POP_T(_T, 0); \
+				OPSTACK_POP_T(TINT, 0); \
+				OPSTACK_POP_T(_ARRT, 0);
+			
+#define ARRAY_WSTORE(_T, _ARRT) \
+				OPSTACK_WPOP_T(_T, 0); \
+				OPSTACK_POP_T(TINT, 0); \
+				OPSTACK_POP_T(_ARRT, 0);
+			
+			
+		case AASTORE:
+			// the runtime value of the type on the top of the stack must be
+			// assignment compatible with the type of the array
+			ENSURE_OPSTACK_SIZE(3);
+			
+			if (OPSTACK_ITEM(2) != TINT) {
+				VERIFY_ERROR("aastore: array index is not an integer");
+			}
+			
+			type      = OPSTACK_ITEM(1);
+			arrayType = OPSTACK_ITEM(3);
+			
+			DBG(VERIFY3,
+			    dprintf("%sarrayType: ", indent); printType(arrayType, OPSTACK_INFO(3));
+			    dprintf(" vs. type: "); printType(type, OPSTACK_INFO(1));
+			    dprintf("\n");
+			    );
+			
+			if (OPSTACK_INFO(3) & CLASS_NAMESTR || OPSTACK_INFO(3) & CLASS_SIGSTR) {
+				sig = (const char*)arrayType;
+				if (*sig != '[') {
+					VERIFY_ERROR("trying to aastore into something that is not an array");
+				}
+				
+				sig++;
+				arrayType = (Hjava_lang_Class*)(sig);
+				n = CLASS_SIGSTR;
+			} else if (!IS_ARRAY(arrayType)) {
+				VERIFY_ERROR("trying to aastore into something that is not an array");
+			} else {
+				if (arrayType == TOBJARR) {
+					arrayType = TOBJ;
+					n = 0;
+				} else {
+					arrayType = (Hjava_lang_Class*)(CLASS_CNAME(arrayType) + 1);
+					n = CLASS_SIGSTR;
+				}
+				
+				
+			}
+			
+			if (!typecheck(einfo, this, arrayType, n, type, OPSTACK_INFO(1))) {
+				VERIFY_ERROR("attempting to store incompatible type in array");
+			}
+			
+			OPSTACK_POP_N_BLIND(3);
+			break;
+			
+			
+		case IASTORE: ARRAY_STORE(TINT,   TINTARR);   break;
+		case FASTORE: ARRAY_STORE(TFLOAT, TFLOATARR); break;
+		case BASTORE: ARRAY_STORE(TINT,   TBYTEARR);  break;
+		case CASTORE: ARRAY_STORE(TINT,   TCHARARR);  break;
+		case SASTORE: ARRAY_STORE(TINT,   TSHORTARR); break;
+			
+		case LASTORE: ARRAY_WSTORE(TLONG,   TLONGARR);   break;
+		case DASTORE: ARRAY_WSTORE(TDOUBLE, TDOUBLEARR); break;
+#undef ARRAY_STORE
+#undef ARRAY_WSTORE
+			
+			
+			
+			/**************************************************************
+			 * ARITHMETIC INSTRUCTIONS
+			 **************************************************************/
+		case IAND: case IOR:  case IXOR:
+		case IADD: case ISUB: case IMUL: case IDIV: case IREM:
+		case ISHL: case ISHR: case IUSHR:
+			OPSTACK_POP_T(TINT, 0);
+			break;
+		case INEG:
+			OPSTACK_PEEK_T(TINT, 0);
+			break;
+			
+			
+		case LAND: case LOR:  case LXOR:
+		case LADD: case LSUB: case LMUL: case LDIV: case LREM:
+			OPSTACK_WPOP_T(TLONG, 0);
+			break;
+		case LNEG:
+			OPSTACK_WPEEK_T(TLONG, 0);
+			break;
+			
+		case LSHL: case LSHR: case LUSHR:
+			OPSTACK_POP_T(TINT, 0);
+			OPSTACK_WPEEK_T(TLONG, 0);
+			break;
+			
+			
+		case FADD: case FSUB: case FMUL: case FDIV: case FREM:
+			OPSTACK_POP_T(TFLOAT, 0);
+			break;
+		case FNEG:
+			OPSTACK_PEEK_T(TFLOAT, 0);
+			break;
+			
+			
+		case DADD: case DSUB: case DDIV: case DMUL: case DREM:
+			OPSTACK_WPOP_T(TDOUBLE, 0);
+			break;
+		case DNEG:
+			OPSTACK_WPEEK_T(TDOUBLE, 0);
+			break;
+			
+			
+		case LCMP:
+			OPSTACK_WPOP_T(TLONG, 0);
+			OPSTACK_WPOP_T(TLONG, 0);
+			OPSTACK_PUSH(TINT);
+			break;
+			
+		case FCMPG:
+		case FCMPL:
+			OPSTACK_POP_T(TFLOAT, 0);
+			OPSTACK_POP_T(TFLOAT, 0);
+			OPSTACK_PUSH(TINT);
+			break;
+				
+		case DCMPG:
+		case DCMPL:
+			OPSTACK_WPOP_T(TDOUBLE, 0);
+			OPSTACK_WPOP_T(TDOUBLE, 0);
+			OPSTACK_PUSH(TINT);
+			break;
+			
+			
+		case IINC:
+			if (wide == true) { GET_WIDX; }
+			else              { GET_IDX; }
+			
+			ENSURE_LOCAL_TYPE(idx, TINT);
+			
+			pc += insnLen[code[pc]];
+			if (wide == true) {
+				pc += 2;
+				wide = false;
+			}
+			continue;
+			
+			
+			/**************************************************************
+			 * PRIMITIVE CONVERSION STUFF
+			 **************************************************************/
+		case INT2BYTE:
+		case INT2CHAR:
+		case INT2SHORT:
+			OPSTACK_PEEK_T(TINT, 0);
+			break;
+			
+		case I2F:
+			OPSTACK_POP_T(TINT, 0);
+			OPSTACK_PUSH_BLIND(TFLOAT);
+			break;
+		case I2L:
+			OPSTACK_POP_T(TINT, 0);
+			OPSTACK_WPUSH(TLONG);
+			break;
+		case I2D:
+			OPSTACK_POP_T(TINT, 0);
+			OPSTACK_WPUSH(TDOUBLE);
+			break;
+			
+		case F2I:
+			OPSTACK_POP_T(TFLOAT, 0);
+			OPSTACK_PUSH_BLIND(TINT);
+			break;
+		case F2L:
+			OPSTACK_POP_T(TFLOAT, 0);
+			OPSTACK_WPUSH(TLONG);
+			break;
+		case F2D:
+			OPSTACK_POP_T(TFLOAT, 0);
+			OPSTACK_WPUSH(TDOUBLE);
+			break;
+			
+		case L2I:
+			OPSTACK_WPOP_T(TLONG, 0);
+			OPSTACK_PUSH_BLIND(TINT);
+			break;
+		case L2F:
+			OPSTACK_WPOP_T(TLONG, 0);
+			OPSTACK_PUSH_BLIND(TFLOAT);
+			break;
+		case L2D:
+			OPSTACK_WPOP_T(TLONG, 0);
+			OPSTACK_WPUSH_BLIND(TDOUBLE);
+			break;
+			
+		case D2I:
+			OPSTACK_WPOP_T(TDOUBLE, 0);
+			OPSTACK_PUSH_BLIND(TINT);
+			break;
+		case D2F:
+			OPSTACK_WPOP_T(TDOUBLE, 0);
+			OPSTACK_PUSH_BLIND(TFLOAT);
+			break;
+		case D2L:
+			OPSTACK_WPOP_T(TDOUBLE, 0);
+			OPSTACK_WPUSH_BLIND(TLONG);
+			break;
+			
+			
+			
+			/**************************************************************
+			 * OBJECT CREATION/TYPE CHECKING
+			 **************************************************************/
+		case INSTANCEOF:
+			ENSURE_OPSTACK_SIZE(1);
+			if (!isReference(OPSTACK_ITEM(1), OPSTACK_INFO(1))) {
+				VERIFY_ERROR("instanceof: top of stack is not a reference type");
+			}
+			OPSTACK_POP_BLIND;
+			OPSTACK_PUSH_BLIND(TINT);
+			break;
+			
+		case CHECKCAST:
+			ENSURE_OPSTACK_SIZE(1);
+			OPSTACK_POP_BLIND;
+			goto NEW_COMMON;
+			
+		case NEW:
+		NEW_COMMON:
+			GET_WIDX;
+			
+			if (pool->tags[idx] == CONSTANT_ResolvedClass) {
+				OPSTACK_PUSH_INFO(CLASS_CLASS(idx, pool), 0);
+			} else {
+				const char* namestr;
+				
+				namestr = CLASS_NAMED(idx, pool);
+				
+				if (*namestr == '[') {
+					OPSTACK_PUSH_INFO((Hjava_lang_Class*)namestr, CLASS_SIGSTR);
+				} else {
+					OPSTACK_PUSH_INFO((Hjava_lang_Class*)namestr, CLASS_NAMESTR);
+				}
+			}
+			
+			DBG(VERIFY3,
+			    dprintf("%s", indent);
+			    printType(OPSTACK_ITEM(1), OPSTACK_INFO(1));
+			    dprintf("\n"); );
+			break;
+			
+			
+		case ANEWARRAY:
+			OPSTACK_POP_T(TINT, 0);
+			
+			GET_WIDX;
+			
+			if (pool->tags[idx] == CONSTANT_ResolvedClass) {
+				arrayType = CLASS_CLASS(idx, pool);
+				type = lookupArray(arrayType, einfo);
+				
+				if (type == NULL) {
+					VERIFY_ERROR("anewarray: error creating array type");
+				}
+				
+				OPSTACK_PUSH_BLIND_INFO(type, 0);
+			} else {
+				char* namestr;
+				
+				sig = CLASS_NAMED(idx, pool);
+				if (*sig == '[') {
+					namestr = checkPtr(KMALLOC(sizeof(char) * (strlen(sig) + 2)));
+					*sigs = pushSig(*sigs, namestr);
+					sprintf(namestr, "[%s", sig);
+				} else {
+					namestr = checkPtr(KMALLOC(sizeof(char) * (strlen(sig) + 4)));
+					*sigs = pushSig(*sigs, namestr);
+					sprintf(namestr, "[L%s;", sig);
+				}
+				OPSTACK_PUSH_BLIND_INFO((Hjava_lang_Class*)namestr, CLASS_SIGSTR);
+			}
+			
+			DBG(VERIFY3,
+			    dprintf("%s", indent);
+			    printType(OPSTACK_ITEM(1), OPSTACK_INFO(1));
+			    dprintf("\n"); );
+			break;
+			
+		case MULTIANEWARRAY:
+			n = code[pc + 3];
+			ENSURE_OPSTACK_SIZE(n);
+			while (n > 0) {
+				if (OPSTACK_ITEM(1) != TINT) {
+					VERIFY_ERROR("multinewarray: first <n> things on opstack must be integers");
+				}
+				OPSTACK_POP_BLIND;
+				n--;
+			}
+			
+			goto NEW_COMMON;
+			
+			
+		case GETFIELD:
+			GET_WIDX;
+			
+			n = FIELDREF_CLASS(idx, pool);
+			
+			if (pool->tags[n] == CONSTANT_ResolvedClass) {
+				type = CLASS_CLASS(n, pool);
+				n = 0;
+			} else {
+				type = (Hjava_lang_Class*)CLASS_NAMED(n, pool);
+				n = CLASS_NAMESTR;
+			}
+			
+			OPSTACK_POP_T(type, n);
+			goto GET_COMMON;
+			
+		case GETSTATIC:
+			GET_WIDX;
+			CHECK_STACK_OVERFLOW(1);
+		GET_COMMON:
+			sig = FIELDREF_SIGD(idx, pool);
+			
+			DBG(VERIFY3, dprintf("%sfield type: %s\n", indent, sig); );
+			
+			switch (*sig) {
+			case 'I': case 'Z': case 'S': case 'B': case 'C':
+				OPSTACK_PUSH_BLIND(TINT);
+				break;
+				
+			case 'F': OPSTACK_PUSH_BLIND(TFLOAT); break;
+			case 'J': OPSTACK_WPUSH_BLIND(TLONG); break;
+			case 'D': OPSTACK_WPUSH_BLIND(TDOUBLE); break;
+				
+			case '[':
+			case 'L':
+				OPSTACK_PUSH_BLIND_INFO((Hjava_lang_Class*)sig, CLASS_SIGSTR);
+				break;
+				
+			default:
+				DBG(VERIFY3, dprintf("%sweird type signature: %s", indent, sig); );
+				VERIFY_ERROR("get{field/static}: unrecognized type signature");
+				break;
+			}
+			break;
+			
+			
+			
+		case PUTFIELD:
+			GET_WIDX;
+			sig = FIELDREF_SIGD(idx, pool);
+			
+			DBG(VERIFY3, dprintf("                field name::type: %s::%s\n",
+					     FIELDREF_NAMED(idx, pool), sig); );
+			    
+			if (OPSTACK_TOP == TWIDE) n = 3;
+			else                      n = 2;
+			ENSURE_OPSTACK_SIZE(n);
+			
+			n = 0;
+			switch (*sig) {
+			case 'I': case 'Z': case 'S': case 'B': case 'C':
+				type = TINT;
+				break;
+				
+			case 'F': type = TFLOAT; break;
+			case 'J': OPSTACK_POP_BLIND; type = TLONG; break;
+			case 'D': OPSTACK_POP_BLIND; type = TDOUBLE; break;
+				
+			case '[':
+			case 'L':
+				type = (Hjava_lang_Class*)sig;
+				n = CLASS_SIGSTR;
+				break;
+				
+			default:
+				DBG(VERIFY3, dprintf("%sweird type signature: %s", indent, sig); );
+				VERIFY_ERROR("put{field/static}: unrecognized type signature");
+				break;
+			}
+			
+			if (!typecheck(einfo, this, type, n, OPSTACK_ITEM(1), OPSTACK_INFO(1))) {
+				VERIFY_ERROR("putfield: thing on top of stack doesn't match with FIELDREF type");
+			}
+			OPSTACK_POP_BLIND;
+			
+			
+			n = FIELDREF_CLASS(idx, pool);
+			if (pool->tags[n] == CONSTANT_ResolvedClass) {
+				type = CLASS_CLASS(n, pool);
+				n = 0;
+			} else {
+				type = (Hjava_lang_Class*)CLASS_NAMED(n, pool);
+				n = CLASS_NAMESTR;
+			}
+			
+			if (!typecheck(einfo, this, type, n, OPSTACK_ITEM(1), OPSTACK_INFO(1))) {
+				VERIFY_ERROR("putfield: thing on top of stack does not type check with FIELDREF type");
+			}
+			OPSTACK_POP_BLIND;
+			break;
+			
+			
+		case PUTSTATIC:
+			GET_WIDX;
+			sig = FIELDREF_SIGD(idx, pool);
+			
+			DBG(VERIFY3, dprintf("                field name::type: %s::%s\n",
+					     FIELDREF_NAMED(idx, pool), sig); );
+			    
+			if (OPSTACK_TOP == TWIDE) n = 2;
+			else                      n = 1;
+			ENSURE_OPSTACK_SIZE(n);
+			
+			n = 0;
+			switch (*sig) {
+			case 'I': case 'Z': case 'S': case 'B': case 'C':
+				type = TINT;
+				break;
+				
+			case 'F': type = TFLOAT; break;
+			case 'J': OPSTACK_POP_BLIND; type = TLONG; break;
+			case 'D': OPSTACK_POP_BLIND; type = TDOUBLE; break;
+				
+			case '[':
+			case 'L':
+				type = (Hjava_lang_Class*)sig;
+				n = CLASS_SIGSTR;
+				break;
+				
+			default:
+				DBG(VERIFY3, dprintf("%sweird type signature: %s", indent, sig); );
+				VERIFY_ERROR("put{field/static}: unrecognized type signature");
+				break;
+			}
+			
+			if (!typecheck(einfo, this, type, n, OPSTACK_ITEM(1), OPSTACK_INFO(1))) {
+				VERIFY_ERROR("putfield: thing on top of stack doesn't match with FIELDREF type");
+			}
+			OPSTACK_POP_BLIND;
+			break;
+			
+			
+			/**************************************************************
+			 * BRANCHING INSTRUCTIONS...END OF BASIC BLOCKS
+			 **************************************************************/
+		case GOTO:
+		case GOTO_W:
+			break;
+			
+		case JSR_W:
+		case JSR:
+			OPSTACK_PUSH(TADDR);
+			OPSTACK_INFO(1) = pc + insnLen[code[pc]];
+			break;
+		case RET:
+			// type checking done during merging stuff...
+			break;
+			
+		case IF_ACMPEQ:
+		case IF_ACMPNE:
+			ENSURE_OPSTACK_SIZE(2);
+			if (!isReference(OPSTACK_ITEM(1), OPSTACK_INFO(1)) ||
+			    !isReference(OPSTACK_ITEM(2), OPSTACK_INFO(2))) {
+				VERIFY_ERROR("if_acmp* when item on top of stack is not a reference type");
+			}
+			OPSTACK_POP_BLIND;
+			OPSTACK_POP_BLIND;
+			break;
+			
+		case IF_ICMPEQ:
+		case IF_ICMPNE:
+		case IF_ICMPGT:
+		case IF_ICMPGE:
+		case IF_ICMPLT:
+		case IF_ICMPLE:
+			OPSTACK_POP_T(TINT, 0);
+		case IFEQ:
+		case IFNE:
+		case IFGT:
+		case IFGE:
+		case IFLT:
+		case IFLE:
+			OPSTACK_POP_T(TINT, 0);
+			break;
+			
+		case IFNONNULL:
+		case IFNULL:
+			ENSURE_OPSTACK_SIZE(1);
+			if (!isReference(OPSTACK_ITEM(1), OPSTACK_INFO(1))) {
+				VERIFY_ERROR("if[non]null: thing on top of stack is not a reference");
+			}
+			OPSTACK_POP_BLIND;
+			break;
+			
+		case LOOKUPSWITCH:
+		case TABLESWITCH:
+			OPSTACK_POP_T(TINT, 0);
+			return(true);
+			
+			
+			/**************************************************************
+			 * METHOD CALLING/RETURNING
+			 **************************************************************/
+		case INVOKESTATIC:
+		case INVOKEVIRTUAL:
+		case INVOKESPECIAL:
+		case INVOKEINTERFACE:
+			if (checkMethodCall(einfo, method, block, pc, sigs) == false) {
+				DBG(VERIFY3,
+				    dprintf("\n                some problem with a method call...here's the block:\n");
+				    printBlock(method, block, "                "); );
+				
+				// propagate error
+				VERIFY_ERROR("some problem with a method call");
+			}
+			break;
+			
+			
+		case IRETURN:
+			OPSTACK_PEEK_T(TINT, 0);
+			sig = getReturnSig(method);
+			if (strlen(sig) != 1 || (*sig != 'I' && *sig != 'Z' && *sig != 'S' && *sig != 'B' && *sig != 'C')) {
+				VERIFY_ERROR("ireturn: method doesn't return an integer");
+			}
+			break;
+		case FRETURN:
+			OPSTACK_PEEK_T(TFLOAT, 0);
+			sig = getReturnSig(method);
+			if (strcmp(sig, "F")) {
+				VERIFY_ERROR("freturn: method doesn't return an float");
+			}
+			break;
+		case LRETURN:
+			OPSTACK_WPEEK_T(TLONG, 0);
+			sig = getReturnSig(method);
+			if (strcmp(sig, "J")) {
+				VERIFY_ERROR("lreturn: method doesn't return a long");
+			}
+			break;
+		case DRETURN:
+			OPSTACK_WPEEK_T(TDOUBLE, 0);
+			sig = getReturnSig(method);
+			if (strcmp(sig, "D")) {
+				VERIFY_ERROR("dreturn: method doesn't return a double");
+			}
+			break;
+		case RETURN:
+			sig = getReturnSig(method);
+			if (strcmp(sig, "V")) {
+				VERIFY_ERROR("return: must return something in a non-void function");
+			}
+			break;
+		case ARETURN:
+			ENSURE_OPSTACK_SIZE(1);
+			sig = getReturnSig(method);
+			if (!typecheck(einfo, this, (Hjava_lang_Class*)sig, CLASS_SIGSTR, OPSTACK_ITEM(1), OPSTACK_INFO(1))) {
+				VERIFY_ERROR("areturn: top of stack is not type compatible with method return type");
+			}
+			break;
+			
+		case ATHROW:
+			ENSURE_OPSTACK_SIZE(1);
+			if (!typecheck(einfo, this, javaLangThrowable, 0, OPSTACK_ITEM(1), OPSTACK_INFO(1))) {
+				DBG(VERIFY3,
+				    dprintf("%sATHROW error: ", indent);
+				    printType(OPSTACK_ITEM(1), OPSTACK_INFO(1));
+				    dprintf ("\n"); );
+				VERIFY_ERROR("athrow: object on top of stack is not a subclass of throwable");
+			}
+			break;
+			
+			
+			/**************************************************************
+			 * MISC
+			 **************************************************************/
+		case NOP:
+			break;
+			
+			
+		case BREAKPOINT:
+			// for internal use only: cannot appear in a class file
+			VERIFY_ERROR("breakpoint instruction cannot appear in classfile");
+			break;
+			
+			
+		case MONITORENTER:
+		case MONITOREXIT:
+			OPSTACK_POP_T(TOBJ, 0);
+			break;
+				
+				
+		case DUP:
+			ENSURE_OPSTACK_SIZE(1);
+			if (OPSTACK_TOP == TWIDE) {
+				VERIFY_ERROR("cannot dup a long or double");
+			}
+			
+			OPSTACK_PUSH(OPSTACK_TOP);
+			OPSTACK_INFO(1) = OPSTACK_INFO(2);
+			break;
+			
+		case DUP_X1:
+			ENSURE_OPSTACK_SIZE(2);
+			if (OPSTACK_TOP == TWIDE || OPSTACK_ITEM(2) == TWIDE) {
+				VERIFY_ERROR("dup_x1 splits up a double or long");
+			}
+			
+			OPSTACK_PUSH(OPSTACK_TOP);
+			OPSTACK_INFO(1) = OPSTACK_INFO(2);
+			
+			OPSTACK_ITEM(2) = OPSTACK_ITEM(3);
+			OPSTACK_INFO(2) = OPSTACK_INFO(3);
+
+			OPSTACK_ITEM(3) = OPSTACK_ITEM(1);
+			OPSTACK_INFO(3) = OPSTACK_INFO(1);
+			break;
+			
+		case DUP_X2:
+			ENSURE_OPSTACK_SIZE(3);
+			if (OPSTACK_TOP == TWIDE) {
+				VERIFY_ERROR("cannot dup_x2 when top item on operand stack is a two byte item");
+			}
+			
+			OPSTACK_PUSH(OPSTACK_TOP);
+			OPSTACK_INFO(1) = OPSTACK_INFO(2);
+			
+			OPSTACK_ITEM(2) = OPSTACK_ITEM(3);
+			OPSTACK_INFO(2) = OPSTACK_INFO(3);
+			
+			OPSTACK_ITEM(3) = OPSTACK_ITEM(4);
+			OPSTACK_INFO(3) = OPSTACK_INFO(4);
+			
+			OPSTACK_ITEM(4) = OPSTACK_ITEM(1);
+			OPSTACK_INFO(4) = OPSTACK_INFO(1);
+			break;
+			
+		case DUP2:
+			if (block->stacksz < 2) {
+				VERIFY_ERROR("dup2 with < 2 bytes on operand stack");
+			}
+			
+			OPSTACK_PUSH(OPSTACK_WTOP);
+			OPSTACK_PUSH(OPSTACK_WTOP);
+			
+			OPSTACK_INFO(1) = OPSTACK_INFO(3);
+			OPSTACK_INFO(2) = OPSTACK_INFO(4);
+			break;
+			
+		case DUP2_X1:
+			ENSURE_OPSTACK_SIZE(2);
+			if (OPSTACK_WTOP == TWIDE) {
+				VERIFY_ERROR("dup_x1 requires top 2 bytes on operand stack to be single bytes items");
+			}
+			
+			CHECK_STACK_OVERFLOW(2);
+			
+			OPSTACK_PUSH_BLIND(OPSTACK_ITEM(2));
+			OPSTACK_PUSH_BLIND(OPSTACK_ITEM(2));
+			
+			OPSTACK_INFO(1) = OPSTACK_INFO(3);
+			OPSTACK_INFO(2) = OPSTACK_INFO(4);
+			
+			OPSTACK_ITEM(3) = OPSTACK_ITEM(5);
+			OPSTACK_INFO(3) = OPSTACK_INFO(5);
+
+			OPSTACK_ITEM(4) = OPSTACK_ITEM(1);
+			OPSTACK_INFO(4) = OPSTACK_INFO(1);
+			
+			OPSTACK_ITEM(5) = OPSTACK_ITEM(2);
+			OPSTACK_INFO(5) = OPSTACK_INFO(2);
+			break;
+			
+		case DUP2_X2:
+			ENSURE_OPSTACK_SIZE(4);
+			if (OPSTACK_WTOP == TWIDE || OPSTACK_ITEM(4) == TWIDE) {
+				VERIFY_ERROR("dup2_x2 where either 2nd or 4th byte is 2nd half of a 2 byte item");
+			}
+			
+			CHECK_STACK_OVERFLOW(2);
+			
+			OPSTACK_PUSH_BLIND(OPSTACK_ITEM(2));
+			OPSTACK_PUSH_BLIND(OPSTACK_ITEM(2));
+			
+			OPSTACK_INFO(1) = OPSTACK_INFO(3);
+			OPSTACK_INFO(2) = OPSTACK_INFO(4);
+			
+			OPSTACK_ITEM(3) = OPSTACK_ITEM(5);
+			OPSTACK_INFO(3) = OPSTACK_INFO(5);
+			
+			OPSTACK_ITEM(4) = OPSTACK_ITEM(6);
+			OPSTACK_INFO(4) = OPSTACK_INFO(6);
+			
+			OPSTACK_ITEM(5) = OPSTACK_ITEM(1);
+			OPSTACK_INFO(5) = OPSTACK_INFO(1);
+			
+			OPSTACK_ITEM(6) = OPSTACK_ITEM(2);
+			OPSTACK_INFO(6) = OPSTACK_INFO(2);
+			break;
+			
+			
+		case SWAP:
+			if (block->stacksz < 2) {
+				VERIFY_ERROR("swap with < 2 bytes on operand stack");
+			} else if (OPSTACK_TOP == TWIDE || OPSTACK_ITEM(2) == TWIDE) {
+				VERIFY_ERROR("cannot swap 2 bytes of a long or double");
+			}
+			
+			type         = OPSTACK_TOP;
+			OPSTACK_TOP  = OPSTACK_WTOP;
+			OPSTACK_WTOP = type;
+			
+			n = OPSTACK_INFO(1);
+			OPSTACK_INFO(1) = OPSTACK_INFO(2);
+			OPSTACK_INFO(2) = n;
+			break;
+			
+			
+		case WIDE:
+			wide = true;
+			pc = insnLen[code[pc]];
+			continue;
+			
+		default:
+			// should never get here because of preprocessing in defineBasicBlocks()
+			VERIFY_ERROR("unknown opcode encountered");
+		}
+		
+		
+		pc += insnLen[code[pc]];
+		if (wide == true) {
+			wide = false;
+			pc++;
+		}
+	}
+		
+	
+	// SUCCESS!
+	return(true);
+	
+	
+#undef OPSTACK_WPOP_T
+#undef OPSTACK_POP_T
+#undef OPSTACK_POP
+#undef OPSTACK_WPOP
+#undef OPSTACK_POP_N
+#undef OPSTACK_POP_N_BLIND
+#undef OPSTACK_WPOP_BLIND
+#undef OPSTACK_POP_BLIND
+
+#undef OPSTACK_WPEEK_T
+#undef OPSTACK_PEEK_T
+
+#undef EMPTY_STACK_ERROR
+	
+#undef OPSTACK_WPUSH
+#undef OPSTACK_PUSH
+#undef OPSTACK_PUSH_INFO
+#undef OPSTACK_WPUSH_BLIND
+#undef OPSTACK_PUSH_BLIND
+#undef OPSTACK_PUSH_BLIND_INFO
+
+#undef CHECK_STACK_OVERFLOW
+#undef CHECK_STACK_UNDERFLOW
+
+#undef OPSTACK_ITEM
+#undef OPSTACK_TOP
+#undef OPSTACK_WTOP
+
+#undef OPSTACK_INFO
+#undef LOCALS_INFO
+
+#undef ENSURE_LOCAL_TYPE
+
+#undef VERIFY_ERROR
+}
+
+
+/* 
+ * parses the next argument from sig into buf, returning pointer beyond arg.
+ */
+static
+const char*
+getNextArg(const char* sig, char* buf)
+{
+	const char* afterSig;
+	
+	if (*sig == ')') {
+		buf[0] = ')';
+		buf[1] = '\0';
+		return sig;
+	}
+	// parseFieldTypeDescriptor doesn't deal with void signatures
+	else if (*sig == 'V') {
+		buf[0] = 'V';
+		buf[1] = '\0';
+		sig++;
+		return sig;
+	}
+	
+	for (afterSig = parseFieldTypeDescriptor(sig);
+	     sig < afterSig;
+	     sig++, buf++) {
+		*buf = *sig;
+	}
+	
+	*buf = '\0';
+	
+	return afterSig;
+}
+
+
+/*
+ * countSizeOfArgsInSignature()
+ *    Longs & Double count for 2, all else counts for one.
+ */
+static
+uint32
+countSizeOfArgsInSignature(const char* sig)
+{
+	uint32 count = 0;
+	
+	for (sig++; *sig != ')'; sig = parseFieldTypeDescriptor(sig)) {
+		if (*sig == 'J' || *sig == 'D')
+			count += 2;
+		else
+			count++;
+	}
+	
+	return count;
+}
+
+
+/* 
+ * checkMethodCall()
+ *    verify an invoke instruction.  this includes making sure that the types
+ *    on the operand stack are type compatible with those expected by the method
+ *    being called.
+ *
+ *    note: we don't check to make sure that the class being referenced by the
+ *          method call actually has the method, or that we have permission to
+ *          access it, as those checks are deferred until pass 4.
+ *
+ * returns whether the method's arguments type check correctly.
+ * it also pushes the return type onto binfo's operand stack.
+ */
+static
+bool
+checkMethodCall(errorInfo* einfo, const Method* method, BlockInfo* binfo, uint32 pc, SigStack** sigs)
+{
+#define VERIFY_ERROR(_MSG) \
+	KFREE(argbuf); \
+	DBG(VERIFY3, dprintf("                error with method invocation, pc = %d, method = %s%s\n", \
+			     pc, METHODREF_NAMED(idx, pool), methSig); ); \
+	if (einfo->type == 0) { \
+		postExceptionMessage(einfo, JAVA_LANG(VerifyError), \
+				     "in method \"%s.%s\": %s", \
+				     CLASS_CNAME(method->class), METHOD_NAMED(method), _MSG); \
+	} \
+	return(false)
+	
+#define TYPE_ERROR VERIFY_ERROR("parameters fail type checking in method invocation")
+	
+	const unsigned char* code        = METHOD_BYTECODE_CODE(method);
+	const uint32 opcode              = code[pc];
+	
+	const constants* pool            = CLASS_CONSTANTS(method->class);
+	const uint32 idx                 = WORD(code, pc + 1);
+				   				 
+	const uint32 classIdx            = METHODREF_CLASS(idx, pool);
+	Hjava_lang_Class* methodRefClass = NULL;
+	Hjava_lang_Class* receiver       = NULL;
+	
+	const char* methSig              = METHODREF_SIGD(idx, pool);
+	const char* sig                  = methSig;
+	uint32 nargs                     = countSizeOfArgsInSignature(sig);
+	
+	uint32 paramIndex                = 0;
+	char* argbuf                     = checkPtr(KMALLOC(strlen(sig) * sizeof(char)));
+	
+	
+	DBG(VERIFY3, dprintf("                calling method %s%s\n", METHODREF_NAMED(idx, pool), sig); );
+	
+	
+	if (nargs > binfo->stacksz) {
+		VERIFY_ERROR("not enough stuff on opstack for method invocation");
+	}
+	
+	
+	// make sure that the receiver is type compatible with the class being invoked
+	if (opcode != INVOKESTATIC) {
+		const uint32 receiverIdx  = binfo->stacksz - (nargs + 1);
+		const uint32 receiverInfo = binfo->opstack_info[receiverIdx];
+		receiver = binfo->opstack[receiverIdx];
+		
+		if (nargs == binfo->stacksz) {
+			VERIFY_ERROR("not enough stuff on opstack for method invocation");
+		}
+		
+		if (!isReference(receiver, receiverInfo)) {
+			VERIFY_ERROR("invoking a method on something that is not a reference");
+		}
+		
+		if (pool->tags[classIdx] == CONSTANT_Class) {
+			// unresolved class...
+			Utf8Const* name = WORD2UTF(pool->data[classIdx]);
+			
+			if (!typecheck(einfo, method->class, (Hjava_lang_Class*)(name->data), CLASS_NAMESTR, receiver, receiverInfo)) {
+				DBG(VERIFY3,
+				    dprintf("%srequired receiver type: ", indent);
+				    printType((Hjava_lang_Class*)(name->data), CLASS_NAMESTR);
+				    dprintf("\n%sactual   receiver type: ", indent);
+				    printType(receiver, receiverInfo);
+				    dprintf("\n");
+				    );
+				
+				VERIFY_ERROR("expected method receiver does not typecheck with object on operand stack");
+			}
+		} else {
+			// resolved class...verify2 guarantees this
+			methodRefClass = CLASS_CLASS(classIdx, pool);
+			
+			if (!typecheck(einfo, method->class, methodRefClass, 0, receiver, receiverInfo)) {
+				VERIFY_ERROR("expected method receiver does not typecheck with object on operand stack");
+			}
+		}
+	}
+	
+	
+	// here we use paramIndex to represent which parameter we're currently considering.
+	// remember, when we call a method, the first parameter is deepest in the stack,
+	// so when we traverse the parameter list in the method signature we have to look
+	// from the bottom up.
+	paramIndex = binfo->stacksz - nargs;
+	for (sig = getNextArg(sig + 1, argbuf); *argbuf != ')'; sig = getNextArg(sig, argbuf)) {
+		
+		if (paramIndex >= binfo->stacksz) {
+			KFREE(argbuf);
+			VERIFY_ERROR("error: not enough parameters on stack for method invocation");
+		}
+		
+		
+		switch (*argbuf) {
+		case '[':
+		case 'L':
+			if (!typecheck(einfo, method->class,
+				       (Hjava_lang_Class*)argbuf, CLASS_SIGSTR,
+				       binfo->opstack[paramIndex], binfo->opstack_info[paramIndex])) {
+				
+				TYPE_ERROR;
+			}
+			
+			binfo->opstack[paramIndex]      = TUNSTABLE;
+			binfo->opstack_info[paramIndex] = 0;
+			paramIndex++;
+			break;
+			
+		case 'Z': case 'S': case 'B': case 'C':
+		case 'I':
+			if (binfo->opstack[paramIndex] != TINT) {
+				TYPE_ERROR;
+			}
+			
+			binfo->opstack[paramIndex] = TUNSTABLE;
+			binfo->opstack_info[paramIndex] = 0;
+			paramIndex++;
+			break;
+			
+		case 'F':
+			if (binfo->opstack[paramIndex] != TFLOAT) {
+				TYPE_ERROR;
+			}
+			
+			binfo->opstack[paramIndex] = TUNSTABLE;
+			binfo->opstack_info[paramIndex] = 0;
+			paramIndex++;
+			break;
+			
+		case 'J':
+			if (binfo->opstack[paramIndex]     != TLONG ||
+			    binfo->opstack[paramIndex + 1] != TWIDE) {
+				TYPE_ERROR;
+			}
+			
+			binfo->opstack[paramIndex]   = TUNSTABLE;
+			binfo->opstack_info[paramIndex] = 0;
+			binfo->opstack[paramIndex+1] = TUNSTABLE;
+			binfo->opstack_info[paramIndex+1] = 0;
+			paramIndex += 2;
+			break;
+			
+		case 'D':
+			if (binfo->opstack[paramIndex]     != TDOUBLE ||
+			    binfo->opstack[paramIndex + 1] != TWIDE) {
+				TYPE_ERROR;
+			}
+			
+			binfo->opstack[paramIndex]   = TUNSTABLE;
+			binfo->opstack_info[paramIndex] = 0;
+			binfo->opstack[paramIndex+1] = TUNSTABLE;
+			binfo->opstack_info[paramIndex+1] = 0;
+			paramIndex += 2;
+			break;
+			
+		default:
+			TYPE_ERROR;
+		}
+	}
+	binfo->stacksz -= nargs;
+	
+	
+	if (opcode != INVOKESTATIC) {
+		// pop object reference off the stack
+		binfo->stacksz -= 1;
+		binfo->opstack[binfo->stacksz] = TUNSTABLE;
+		binfo->opstack_info[binfo->stacksz] = 0;
+	}
+	
+	
+	/**************************************************************
+	 * Process Return Type
+	 **************************************************************/
+	sig++;
+	sig = getNextArg(sig, argbuf);
+	switch (*argbuf) {
+	case 'Z': case 'S': case 'B': case 'C':
+	case 'I':
+		binfo->opstack[binfo->stacksz] = TINT;
+		binfo->opstack_info[binfo->stacksz] = 0;
+		binfo->stacksz++;
+		break;
+		
+	case 'F':
+		binfo->opstack[binfo->stacksz]      = TFLOAT;
+		binfo->opstack_info[binfo->stacksz] = 0;
+		binfo->stacksz++;
+		break;
+		
+	case 'J':
+		binfo->opstack[binfo->stacksz]          = TLONG;
+		binfo->opstack_info[binfo->stacksz]     = 0;
+		binfo->opstack[binfo->stacksz + 1]      = TWIDE;
+		binfo->opstack_info[binfo->stacksz + 1] = 0;
+		binfo->stacksz += 2;
+		break;
+		
+	case 'D':
+		binfo->opstack[binfo->stacksz]          = TDOUBLE;
+		binfo->opstack_info[binfo->stacksz]     = 0;
+		binfo->opstack[binfo->stacksz + 1]      = TWIDE;
+		binfo->opstack_info[binfo->stacksz + 1] = 0;
+		binfo->stacksz += 2;
+		break;
+		
+	case 'V':
+		break;
+		
+	case '[':
+	case 'L':
+		*sigs = pushSig(*sigs, argbuf);
+			
+		binfo->opstack[binfo->stacksz]      = (Hjava_lang_Class*)argbuf;
+		binfo->opstack_info[binfo->stacksz] = CLASS_SIGSTR;
+		binfo->stacksz++;
+		
+		// no freeing of the argbuf here...
+		return(true);
+		
+	default:
+		// shouldn't get here becuase of parsing...
+		DBG(VERIFY3, dprintf("                unrecognized return type signature: %s\n", argbuf); );
+		KFREE(argbuf);
+		postExceptionMessage(einfo, JAVA_LANG(InternalError),
+				     "unrecognized return type signature");
+		return(false);
+	}
+	
+	
+	KFREE(argbuf);
+	return(true);
+#undef TYPE_ERROR
+#undef VERIFY_ERROR
+}
+
+
+
+
+/*
+ * pushes the initial method arguments into local variable array
+ */
+static
+bool
+loadInitialArgs(const Method* method, errorInfo* einfo,
+		BlockInfo* block, SigStack** sigs)
+{
+	uint32 paramCount = 0;
+	Hjava_lang_Class* type = NULL; // used as a temp for parameter and return type processing
+	
+	// the +1 skips the initial '('
+	const char* sig = METHOD_SIGD(method) + 1;
+	char* argbuf    = checkPtr(KMALLOC((strlen(sig)+1) * sizeof(char)));
+	char* newsig    = NULL;
+	
+	Hjava_lang_Class** locals = block->locals;
+	
+	DBG(VERIFY3, dprintf("        sig: %s\n", sig); );
+	
+	// must have at least 1 local variable for the object reference	
+	if (!METHOD_IS_STATIC(method)) {
+		if (method->localsz <= 0) {
+			DBG(VERIFY3, dprintf("ERROR, loadInitialArgs(): number of locals in a non-static method must be > 0"); );
+			
+			postExceptionMessage(einfo, JAVA_LANG(ClassFormatError),
+					     "method %s.%s: number of locals in non-static method must be > 0",
+					     CLASS_CNAME(method->class), METHOD_NAMED(method));
+			goto failure;
+		}
+		
+		// the first local variable in every method is the class to which it belongs
+		locals[0] = method->class;
+		paramCount++;
+	}
+	
+	for (sig = getNextArg(sig, argbuf); *argbuf != ')'; sig = getNextArg(sig, argbuf)) {
+		
+		if (paramCount > method->localsz) {
+			DBG(VERIFY3, dprintf("ERROR, loadInitialArgs(): arguments can't fit into local variables\n"); );
+			
+			postExceptionMessage(einfo, JAVA_LANG(VerifyError),
+					     "method %s.%s: method arguments cannot fit into local variables",
+					     CLASS_CNAME(method->class), METHOD_NAMED(method));
+			goto failure;
+		}
+		
+		switch (*argbuf) {
+		case 'Z': case 'S': case 'B': case 'C':
+		case 'I': type = TINT;    break;
+		case 'F': type = TFLOAT;  break;
+			
+		case 'J': locals[paramCount] = TLONG;   goto WIDE_param;
+		case 'D': locals[paramCount] = TDOUBLE; goto WIDE_param;
+			
+		WIDE_param:
+			paramCount++;
+			if (paramCount > method->localsz) {
+				DBG(VERIFY3,
+				    dprintf("ERROR, loadInitialArgs(): arguments can't fit into local variables\n");
+				    dprintf("        overflow occurred in the middle of a wide parameter\n");
+				    );
+				
+				postExceptionMessage(einfo, JAVA_LANG(VerifyError),
+						     "method %s.%s: method arguments cannot fit into local variables",
+						     CLASS_CNAME(method->class), METHOD_NAMED(method));
+				goto failure;
+			}
+			type = TWIDE;
+			break;
+			
+		default:
+			DBG(VERIFY3,
+			    dprintf("ERROR, loadInitialArgs(): argument to method has bad signature.\n");
+			    dprintf("        it starts with an unrecognized character: %c\n", *argbuf);
+			    dprintf("        the rest of argbuf: %s\n", argbuf);
+			    );
+			
+			postExceptionMessage(einfo, JAVA_LANG(InternalError),
+					     "method %s.%s: unrecognized first character in parameter type descriptor, \"%c\"",
+					     CLASS_CNAME(method->class), METHOD_NAMED(method), *argbuf);
+			goto failure;
+			
+		case '[':
+		case 'L':
+			newsig = checkPtr(KMALLOC((strlen(argbuf) + 1) * sizeof(char)));
+			*sigs = pushSig(*sigs, newsig);
+			sprintf(newsig, "%s", argbuf);
+			block->locals[paramCount]      = (Hjava_lang_Class*)newsig;
+			block->locals_info[paramCount] = CLASS_SIGSTR;
+			paramCount++;
+			continue;
+		}
+		
+		locals[paramCount] = type;
+		paramCount++;
+	}
+	
+	
+	// success!
+	free(argbuf);
+	return(true);
+ failure:
+	free(argbuf);
+	return(false);
+}
+
+
+/*
+ * getReturnSig()
+ */
+static
+const char*
+getReturnSig(const Method* method)
+{
+	const char* sig = METHOD_SIGD(method);
+	
+	// skip the type parameters
+	for (sig++; *sig != ')'; sig = parseFieldTypeDescriptor(sig));
+	sig++;
+	
+	return sig;
+}
+
+
+/*
+ * returns the type when merging two types, t1 and t2.  this result could
+ * be a common superclass, a common class that both types implement, or,
+ * in the event that the types are not compatible, TUNSTABLE.
+ *
+ * note: the precedence of merged types goes (from highest to lowest):
+ *     actual pointer to Hjava_lang_Class*
+ *     CLASS_SIGSTR
+ *     CLASS_NAMESTR
+ * all else being equal, t2's info should be used
+ *
+ * TODO: right now the priority is to be a common superclass, as stated in
+ *       the JVML2 specs.  a better verification technique might check this first,
+ *       and then check interfaces that both classes implement.  of course, depending
+ *       on the complexity of the inheritance hirearchy, this could take a lot of time.
+ *       
+ *       the ideal solution is to remember *all* possible highest resolution types,
+ *       which, of course, would require allocating more memory on the fly, etc., so,
+ *       at least for now, we're not really even considering it.
+ */
+static
+Hjava_lang_Class*
+mergeTypes(errorInfo* einfo, Hjava_lang_Class* this,
+	   Hjava_lang_Class* t1, uint32 info1,
+	   Hjava_lang_Class* t2, uint32 info2,
+	   uint32* info)
+{
+	char* sig;
+	char* tmp = NULL;
+	Hjava_lang_Class* type;
+	*info = 0;
+	
+	// remove the trivial case
+	if (t1 == t2) {
+		*info = info2;
+		return t2;
+	}
+	
+	if (t1 == TUNSTABLE   || t2 == TUNSTABLE ||
+	    t1 == NULL        || t2 == NULL ||  // better safe than sorry...
+	    t1 == TWIDE       || t2 == TWIDE || // ditto.
+	    t1 == TVOID       || t2 == TVOID) { // ditto.
+		
+		return TUNSTABLE;
+	}
+	else if (IS_PRIMITIVE_TYPE(t1) || IS_PRIMITIVE_TYPE(t2)  ||
+		 IS_ADDRESS(t1)        || IS_ADDRESS(t2)) {
+		
+		return TUNSTABLE;
+	}
+	else if (t1 == TNULL) {
+		*info = info2;
+		return t2;
+	}
+	else if (t2 == TNULL) {
+		*info = info1;
+		return t1;
+	}
+	else if (t1 == TOBJ) {
+		*info = 0;
+		return TOBJ;
+	}
+	
+	
+	// if they're both names or both signatures, we can do straightup string compare
+	if (((info1 & CLASS_NAMESTR && info2 & CLASS_NAMESTR) ||
+	     (info1 & CLASS_SIGSTR && info2 & CLASS_SIGSTR)) &&
+	    !strcmp((const char*)t1, (const char*)t2)) {
+			
+		*info = info2;
+		return t2;
+	}
+	
+	
+	// make sure things are in proper signature form
+	if (info1 & CLASS_NAMESTR) {
+		sig = (char*)t1;
+		
+		if (!info2 && !strcmp(sig, t2->name->data)) {
+			*info = info2;
+			return t2;
+		}
+		
+		if (*sig != '[') {
+			tmp = checkPtr(KMALLOC((strlen(sig) + 3) * sizeof(char)));
+			sprintf(tmp, "L%s;", sig);
+			sig = tmp;
+		}
+		
+		if (info2 & CLASS_SIGSTR) {
+			if (!strcmp(sig, (const char*)t2)) {
+				if (tmp) {
+					KFREE(tmp);
+				}
+				
+				*info = info2;
+				return t2;
+			}
+			
+			t2 = getClassFromSignature((const char *)t2, this->loader, einfo);
+			info2 = 0;
+		}
+		
+		t1 = getClassFromSignature(sig, this->loader, einfo);
+		if (!t1) return(false);  // necessary for the next if-block
+		info1 = 0;
+		
+		if (tmp) {
+			KFREE(tmp);
+		}
+	}
+	if (info2 & CLASS_NAMESTR) {
+		sig = (char*)t2;
+		
+		if (!info1 && !strcmp(sig, t1->name->data)) {
+			*info = info1;
+			return t1;
+		}
+		
+		if (*sig != '[') {
+			tmp = checkPtr(KMALLOC((strlen(sig) + 3) * sizeof(char)));
+			sprintf(tmp, "L%s;", sig);
+			sig = tmp;
+		}
+		
+		if (info1 & CLASS_SIGSTR) {
+			if (!strcmp(sig, (const char*)t1)) {
+				if (tmp) {
+					KFREE(tmp);
+				}
+				
+				*info = info1;
+				return t1;
+			}
+			
+			t1 = getClassFromSignature((const char *)t1, this->loader, einfo);
+			info1 = 0;
+		}
+		
+		t2 = getClassFromSignature(sig, this->loader, einfo);
+		info2 = 0;
+		
+		if (tmp) {
+			KFREE(tmp);
+		}
+	}
+	
+	if (info1 & CLASS_SIGSTR) {
+		t1 = getClassFromSignature((const char*)t1, this->loader, einfo);
+		info1 = 0;
+	}
+	if (info2 & CLASS_SIGSTR) {
+		t2 = getClassFromSignature((const char*)t2, this->loader, einfo);
+		info2 = 0;
+	}
+	
+	if (!t1 || !t2) {
+		return TUNSTABLE;
+	}
+	
+	
+	type = getCommonSuperclass(t1, t2);
+	if (type == TOBJ) {
+		if (implements(t1,t2)) {
+			return t1;
+		} else if (implements(t2,t1)) {
+			return t2;
+		}
+	}
+	return type;
+}
+
+
+/*
+ * returns the first (highest) common superclass of classes A and B.
+ *
+ * precondition: neither type is an array type
+ *               nor is either a primitive type
+ */
+static
+Hjava_lang_Class*
+getCommonSuperclass(Hjava_lang_Class* classA, Hjava_lang_Class* classB)
+{
+	Hjava_lang_Class* A;
+	Hjava_lang_Class* B;
+	
+	for (A = classA; A != NULL; A = A->superclass) {
+		for (B = classB; B != NULL; B = B->superclass) {
+			if (A == B) return A;
+		}
+	}
+	
+	// error of some kind...at the very least, we shoulda gotten to Object
+	// when traversing the class hirearchy
+	return TUNSTABLE;
+}
+
+
+/*
+ * isReference()
+ *    returns whether the type is a reference type
+ */
+static
+bool
+isReference(const Hjava_lang_Class* type, const uint32 info)
+{
+	if (info & CLASS_NAMESTR || info & CLASS_SIGSTR)
+		return true;
+	
+	return type &&
+		(type != TUNSTABLE &&
+		 type != TWIDE &&
+		 type != TVOID &&
+		 !IS_PRIMITIVE_TYPE(type) &&
+		 !IS_ADDRESS(type));
+}
+
+
+/*
+ * returns whether t2 can be a t1
+ */
+static
+bool
+typecheck(errorInfo* einfo, Hjava_lang_Class* this,
+	  Hjava_lang_Class* t1, uint32 info1,
+	  Hjava_lang_Class* t2, uint32 info2)
+{
+	char* sig = NULL;
+	char* tmp = NULL;
+	
+	DBG(VERIFY3,
+	    dprintf("%stypechecking ", indent);
+	    printType(t1, info1);
+	    dprintf("  vs.  ");
+	    printType(t2, info2);
+	    dprintf("\n");
+	    );
+	
+	// remove all the trivial cases
+	if (t1 == t2)
+		return true;
+	
+	if (t1 == NULL || t2 == NULL) {
+		// better safe than sorry
+		return false;
+	}
+	else if (t1 == TUNSTABLE   || t2 == TUNSTABLE ||
+		 t1 == TWIDE       || t2 == TWIDE ||
+		 t1 == TVOID       || t2 == TVOID) {
+		
+		return false;
+	}
+	else if (IS_PRIMITIVE_TYPE(t1)  || IS_PRIMITIVE_TYPE(t2)  ||
+		 IS_ADDRESS(t1)         || IS_ADDRESS(t2)) {
+		
+		return false;
+	}
+	else if (t1 == TNULL || t2 == TNULL) {
+		// we are guaranteed that both t1 and t2 are not primitive by this point...
+		// if either is null, it works out
+		return true;
+	}
+	
+	// if they're both names or both signatures, we can do straightup string compare
+	if (((info1 & CLASS_NAMESTR && info2 & CLASS_NAMESTR) ||
+	     (info1 & CLASS_SIGSTR && info2 & CLASS_SIGSTR)) &&
+	    !strcmp((const char*)t1, (const char*)t2)) {
+		return true;
+	}
+	
+	// make sure things are in proper signature form
+	if (info1 & CLASS_NAMESTR) {
+		sig = (char*)t1;
+		
+		if (!info2 && !strcmp(sig, t2->name->data))
+			return true;
+		
+		if (*sig != '[') {
+			tmp = checkPtr(KMALLOC((strlen(sig) + 3) * sizeof(char)));
+			sprintf(tmp, "L%s;", sig);
+			sig = tmp;
+		}
+		
+		if (info2 & CLASS_SIGSTR) {
+			if (!strcmp(sig, (const char*)t2)) {
+				if (tmp) {
+					KFREE(tmp);
+				}
+				return true;
+			}
+			
+			t2 = getClassFromSignature((const char *)t2, this->loader, einfo);
+			info2 = 0;
+		}
+		
+		t1 = getClassFromSignature(sig, this->loader, einfo);
+		if (!t1) return(false);  // necessary for the next if-block
+		info1 = 0;
+		
+		if (tmp) {
+			KFREE(tmp);
+		}
+	}
+	if (info2 & CLASS_NAMESTR) {
+		sig = (char*)t2;
+		
+		if (!info1 && !strcmp(sig, t1->name->data)) {
+			return true;
+		}
+		
+		if (*sig != '[') {
+			tmp = checkPtr(KMALLOC((strlen(sig) + 3) * sizeof(char)));
+			sprintf(tmp, "L%s;", sig);
+			sig = tmp;
+		}
+		
+		if (info1 & CLASS_SIGSTR) {
+			if (!strcmp(sig, (const char*)t1)) {
+				if (tmp) {
+					KFREE(tmp);
+				}
+				return true;
+			}
+			
+			t1 = getClassFromSignature((const char *)t1, this->loader, einfo);
+			info1 = 0;
+		}
+		
+		t2 = getClassFromSignature(sig, this->loader, einfo);
+		info2 = 0;
+		
+		if (tmp) {
+			KFREE(tmp);
+		}
+	}
+	
+	if (info1 & CLASS_SIGSTR) {
+		t1 = getClassFromSignature((const char *)t1, this->loader, einfo);
+		info1 = 0;
+	}
+	if (info2 & CLASS_SIGSTR) {
+		t2 = getClassFromSignature((const char *)t2, this->loader, einfo);
+		info2 = 0;
+	}
+	
+	if (t1 == NULL || t2 == NULL) {
+		DBG(VERIFY3,
+		    dprintf("%stypecheck ERROR: t1 = ", indent);
+		    printType(t1, info1);
+		    dprintf(" :: t2 = ");
+		    printType(t2, info2);
+		    dprintf("\n");
+		    );
+		return false;
+	}
+	
+	return (instanceof(t1,t2) || implements(t1,t2));
+}
+
+
+
+/*
+ * returns whether t2 implements t1
+ *
+ * here we have to check if any of the interfaces implemented by t2 are subclasses of t1
+ *
+ * precondition: t1 and t2 must be reference types, or we get some serious issues here
+ */
+static
+bool
+implements(Hjava_lang_Class* t1, Hjava_lang_Class* t2)
+{
+	int i;
+	
+	if (!CLASS_IS_INTERFACE(t1))
+		return(false);
+	
+	for (i = 0; i < t2->interface_len; i++)
+		if (instanceof(t1, t2->interfaces[i]))
+			return(true);
+	
+	return(false);
+}
+
+
+
+/*
+ * allocate memory for a block info and fill in with default values
+ */
+BlockInfo*
+createBlock(const Method* method)
+{
+	int i;
+	
+	BlockInfo* binfo = checkPtr((BlockInfo*)KMALLOC(sizeof(BlockInfo)));
+	
+	binfo->startAddr   = 0;
+	binfo->status      = IS_INSTRUCTION | START_BLOCK;  // not VISITED or CHANGED
+	
+	// allocate memory for locals
+	if (method->localsz > 0) {
+		binfo->locals      = checkPtr(KMALLOC(method->localsz * sizeof(Hjava_lang_Class*)));
+		binfo->locals_info = checkPtr(KMALLOC(method->localsz * sizeof(uint32)));
+		
+		for (i = 0; i < method->localsz; i++) {
+			binfo->locals[i]      = TUNSTABLE;
+			binfo->locals_info[i] = 0;
+		}
+	} else {
+		binfo->locals = NULL;
+	}
+	
+	
+	// allocate memory for operand stack
+	binfo->stacksz = 0;
+	if (method->stacksz > 0) {
+		binfo->opstack      = checkPtr(KMALLOC(method->stacksz * sizeof(Hjava_lang_Class*)));
+		binfo->opstack_info = checkPtr(KMALLOC(method->stacksz * sizeof(uint32)));
+		
+		for (i = 0; i < method->stacksz; i++) {
+			binfo->opstack[i]      = TUNSTABLE;
+			binfo->opstack_info[i] = 0;
+		}
+	} else {
+		binfo->opstack = NULL;
+	}
+	
+	return binfo;
+}
+
+/*
+ * frees the memory of a basic block
+ */
+void
+freeBlock(BlockInfo* binfo)
+{
+	if (binfo == NULL) return;
+	
+	if (binfo->locals != NULL)
+		KFREE(binfo->locals);
+	if (binfo->locals_info != NULL)
+		KFREE(binfo->locals_info);
+	if (binfo->opstack != NULL)
+		KFREE(binfo->opstack);
+	if (binfo->opstack_info != NULL)
+		KFREE(binfo->opstack_info);
+	
+	KFREE(binfo);
+}
+
+/*
+ * copies information from one stack of basic blocks to another
+ */
+void
+copyBlockData(const Method* method, BlockInfo* fromBlock, BlockInfo* toBlock)
+{
+	toBlock->startAddr = fromBlock->startAddr;
+	toBlock->lastAddr  = fromBlock->lastAddr;
+	
+	copyBlockState(method, fromBlock, toBlock);
+}
+
+/*
+ * copies the local variables, operand stack, status, and context
+ * from one block to another.
+ */
+void
+copyBlockState(const Method* method, BlockInfo* fromBlock, BlockInfo* toBlock)
+{
+	uint32 n;
+	
+	toBlock->status  = fromBlock->status;
+	
+	for (n = 0; n < method->localsz; n++) {
+		toBlock->locals[n]      = fromBlock->locals[n];
+		toBlock->locals_info[n] = fromBlock->locals_info[n];
+	}
+	
+	toBlock->stacksz = fromBlock->stacksz;
+	for (n = 0; n < method->stacksz; n++) {
+		toBlock->opstack[n]      = fromBlock->opstack[n];
+		toBlock->opstack_info[n] = fromBlock->opstack_info[n];
+	}
+}
+
+/*
+ * returns which block the given pc is in
+ */
+static
+BlockInfo*
+inWhichBlock(uint32 pc, BlockInfo** blocks, uint32 numBlocks)
+{
+	uint32 i;
+	for (i = 0; i < numBlocks; i++) {
+		if (pc < blocks[i]->startAddr) continue;
+		if (pc <= blocks[i]->lastAddr) return blocks[i];
+	}
+	
+	// shouldn't ever get here unless the specified PC is messed up
+	DBG(VERIFY3, dprintf("inWhichBlock(...): pc = %d out of range...weird.\n", pc); );
+	
+	return NULL;
+}
+
+
+
+/*
+ * pushSig()
+ *     Pushes a new signature on the Stack
+ */
+static
+SigStack*
+pushSig(SigStack* sigs, const char* sig)
+{
+	SigStack* new_sig = checkPtr(KMALLOC(sizeof(SigStack)));
+	new_sig->sig = sig;
+	new_sig->next = sigs;
+	return new_sig;
+}
+
+
+/*
+ * freeSigStack()
+ *     Frees the memory consumed by a stack of names and signatures.
+ */
+static
+void
+freeSigStack(SigStack* sigs)
+{
+	SigStack* tmp;
+	while(sigs != NULL) {
+		tmp = sigs->next;
+		KFREE(sigs);
+		sigs = tmp;
+	}
+}
+
+
+
 // for debugging
 #ifdef KAFFE_VMDEBUG
 static
@@ -1983,4 +4934,109 @@
 	
 #undef PRINT
 }
+
+static
+void
+printType(const Hjava_lang_Class* type, const uint32 tinfo)
+{
+	dprintf("(%d)", tinfo);
+	
+	if (type == NULL) {
+		dprintf("NULL");
+	}
+	else if (tinfo & CLASS_NAMESTR || tinfo & CLASS_SIGSTR) {
+		dprintf("%s", (const char*)type);
+	}
+	else if (type == TNULL) {
+		dprintf("TNULL");
+	}
+	else if (type == TADDR) {
+		dprintf("TADDR");
+	}
+	else if (type == TUNSTABLE) {
+		dprintf("TUNSTABLE");
+	}
+	else if (type == TWIDE) {
+		dprintf("TWIDE");
+	}
+	
+	else if (type == TVOID) {
+		dprintf("TVOID");
+	}
+	
+	else if (type == TINT) {
+		dprintf("TINT");
+	}
+	else if (type == TLONG) {
+		dprintf("TLONG");
+	}
+	else if (type == TFLOAT) {
+		dprintf("TFLOAT");
+	}
+	else if (type == TDOUBLE) {
+		dprintf("TDOUBLE");
+	}
+	
+	else if (type == TCHARARR) {
+		dprintf("TCHARARR");
+	}
+	else if (type == TBOOLARR) {
+		dprintf("TBOOLARR");
+	}
+	else if (type == TBYTEARR) {
+		dprintf("TBYTEARR");
+	}
+	else if (type == TSHORTARR) {
+		dprintf("TSHORTARR");
+	}
+	else if (type == TINTARR) {
+		dprintf("TINTARR");
+	}
+	else if (type == TLONGARR) {
+		dprintf("TLONGARR");
+	}
+	else if (type == TFLOATARR) {
+		dprintf("TFLOATARR");
+	}
+	else if (type == TDOUBLEARR) {
+		dprintf("TDOUBLEARR");
+	}
+	else if (type == TOBJARR) {
+		dprintf("TOBJARR");
+	}
+	else {
+		if (type->name == NULL || type->name->data == NULL) {
+			dprintf("<NULL NAME>");
+		} else {
+			dprintf("%s", CLASS_CNAME(type));
+		}
+	}
+}
+
+
+/*
+ * printBlock()
+ *    For debugging.  Prints out a basic block.
+ */
+static
+void
+printBlock(const Method* method, const BlockInfo* binfo, const char* indent)
+{
+	uint32 n;
+	
+	dprintf("%slocals:\n", indent);
+	for (n = 0; n < method->localsz; n++) {
+		dprintf("%s    %d: ", indent, n);
+		printType(binfo->locals[n], binfo->locals_info[n]);
+		dprintf("\n");
+	}
+	dprintf("%sopstack:\n", indent);
+	for (n = 0; n < method->stacksz; n++) {
+		dprintf("%s    %d: ", indent, n);
+		printType(binfo->opstack[n], binfo->opstack_info[n]);
+		dprintf("\n");
+	}
+}
+
+
 #endif // ifdef KAFFE_VMDEBUG
--- kaffe-cvs/kaffe/kaffevm/baseClasses.c	2003-07-27 16:42:23.000000000 -0500
+++ kaffe/kaffe/kaffevm/baseClasses.c	2003-07-29 11:32:23.000000000 -0500
@@ -296,7 +296,10 @@
 
 	/* Fixup primitive types */
 	finishTypes();
-
+	
+	/* Initialize array types supported by the instruction set */
+	initArrayClasses(&einfo);
+	
 	if (!processClass(StringClass, CSTATE_COMPLETE, &einfo))
 		abortWithEarlyClassFailure(&einfo);
 }
--- kaffe-cvs/kaffe/kaffevm/itypes.h	1999-11-04 15:29:12.000000000 -0500
+++ kaffe/kaffe/kaffevm/itypes.h	2003-07-29 11:26:58.000000000 -0500
@@ -13,6 +13,7 @@
 #define __itypes_h
 
 #include "gtypes.h"
+#include "errors.h"
 
 #define	TYPE_Boolean	4
 #define	TYPE_Char	5
@@ -39,9 +40,19 @@
 extern struct Hjava_lang_Class* shortClass;     
 extern struct Hjava_lang_Class* voidClass;
 
+extern struct Hjava_lang_Class* intArrClass;
+extern struct Hjava_lang_Class* byteArrClass;
+extern struct Hjava_lang_Class* charArrClass;
+extern struct Hjava_lang_Class* shortArrClass;
+extern struct Hjava_lang_Class* longArrClass;
+extern struct Hjava_lang_Class* floatArrClass;
+extern struct Hjava_lang_Class* doubleArrClass;
+extern struct Hjava_lang_Class* objectArrClass;
+
 #define	TYPE_CLASS(t)		types[t]
 
 extern void finishTypes(void);
 extern void initTypes(void);
+extern void initArrayClasses(errorInfo*);
 
 #endif
--- kaffe-cvs/kaffe/kaffevm/itypes.c	2003-06-11 11:54:14.000000000 -0500
+++ kaffe/kaffe/kaffevm/itypes.c	2003-07-29 11:30:10.000000000 -0500
@@ -31,6 +31,15 @@
 Hjava_lang_Class* shortClass;     
 Hjava_lang_Class* voidClass;
 
+Hjava_lang_Class* intArrClass;
+Hjava_lang_Class* byteArrClass;
+Hjava_lang_Class* charArrClass;
+Hjava_lang_Class* shortArrClass;
+Hjava_lang_Class* longArrClass;
+Hjava_lang_Class* floatArrClass;
+Hjava_lang_Class* doubleArrClass;
+Hjava_lang_Class* objectArrClass;
+
 Hjava_lang_Class* types[MAXTYPES];
 
 static
@@ -138,6 +147,26 @@
 	DBG(INIT, dprintf("finishTypes() done\n"); )
 }
 
+/*
+ * Initialize the array classes.
+ */
+void 
+initArrayClasses(errorInfo *einfo)
+{
+	DBG(INIT, dprintf("initArrayClasses()\n"); );
+	
+	intArrClass    = lookupArray(intClass, einfo);
+	byteArrClass   = lookupArray(byteClass, einfo);
+	charArrClass   = lookupArray(charClass, einfo);
+	shortArrClass  = lookupArray(shortClass, einfo);
+	longArrClass   = lookupArray(longClass, einfo);
+	floatArrClass  = lookupArray(floatClass, einfo);
+	doubleArrClass = lookupArray(doubleClass, einfo);
+	objectArrClass = lookupArray(ObjectClass, einfo);
+	
+	DBG(INIT, dprintf("initArrayClasses() done\n"); );
+}
+
 static
 Hjava_lang_Class*
 classFromSig(const char** strp, Hjava_lang_ClassLoader* loader, errorInfo *einfo)


More information about the kaffe mailing list