db-derby-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From kmars...@apache.org
Subject svn commit: r590776 - in /db/derby/code/branches/10.1/java: engine/org/apache/derby/iapi/services/classfile/ engine/org/apache/derby/impl/services/bytecode/ testing/org/apache/derbyTesting/functionTests/master/ testing/org/apache/derbyTesting/functionT...
Date Wed, 31 Oct 2007 18:24:56 GMT
Author: kmarsden
Date: Wed Oct 31 11:24:56 2007
New Revision: 590776

URL: http://svn.apache.org/viewvc?rev=590776&view=rev
Log:
DERBY-176 (partial) Add the initial utility code and split algorithm
to split a single generated method that execeeds the java virtual
machine limit of 65535 bytes of instructions. Allows the byte-code api
caller to generate code without worrying about exceeding the limit.
The initial split algorithm is the ability to split methods
that consist of multiple independent statements, seen by the stack
depth dropping to zero after a statement.
In the largeCodeGen test this change allowed the number of parameters
in the IN list query to increase from 3,400 to 97,000. The limit hit
at 98,000 was the number of constant pool entries.

merge from trunk revision 377609,378383


Modified:
    db/derby/code/branches/10.1/java/engine/org/apache/derby/iapi/services/classfile/VMOpcode.java
    db/derby/code/branches/10.1/java/engine/org/apache/derby/impl/services/bytecode/BCClass.java
    db/derby/code/branches/10.1/java/engine/org/apache/derby/impl/services/bytecode/BCMethod.java
    db/derby/code/branches/10.1/java/engine/org/apache/derby/impl/services/bytecode/CodeChunk.java
    db/derby/code/branches/10.1/java/engine/org/apache/derby/impl/services/bytecode/Conditional.java
    db/derby/code/branches/10.1/java/testing/org/apache/derbyTesting/functionTests/master/largeCodeGen.out
    db/derby/code/branches/10.1/java/testing/org/apache/derbyTesting/functionTests/tests/lang/largeCodeGen.java

Modified: db/derby/code/branches/10.1/java/engine/org/apache/derby/iapi/services/classfile/VMOpcode.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.1/java/engine/org/apache/derby/iapi/services/classfile/VMOpcode.java?rev=590776&r1=590775&r2=590776&view=diff
==============================================================================
--- db/derby/code/branches/10.1/java/engine/org/apache/derby/iapi/services/classfile/VMOpcode.java
(original)
+++ db/derby/code/branches/10.1/java/engine/org/apache/derby/iapi/services/classfile/VMOpcode.java
Wed Oct 31 11:24:56 2007
@@ -257,5 +257,11 @@
      * Used in conditional handling.
     */
     int GOTO_W_INS_LENGTH = 5;
+    
+    /**
+     * Maximum number of entries in the constant pool.
+     * See section 4.10 of JVM spec version 1.
+      */
+    int MAX_CONSTANT_POOL_ENTRIES = 65535;
    
 }

Modified: db/derby/code/branches/10.1/java/engine/org/apache/derby/impl/services/bytecode/BCClass.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.1/java/engine/org/apache/derby/impl/services/bytecode/BCClass.java?rev=590776&r1=590775&r2=590776&view=diff
==============================================================================
--- db/derby/code/branches/10.1/java/engine/org/apache/derby/impl/services/bytecode/BCClass.java
(original)
+++ db/derby/code/branches/10.1/java/engine/org/apache/derby/impl/services/bytecode/BCClass.java
Wed Oct 31 11:24:56 2007
@@ -85,7 +85,7 @@
 	 * Simple text indicating any limits execeeded while generating
 	 * the class file.
 	 */
-	private String limitMsg;
+	String limitMsg;
 	
 	//
 	// ClassBuilder interface

Modified: db/derby/code/branches/10.1/java/engine/org/apache/derby/impl/services/bytecode/BCMethod.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.1/java/engine/org/apache/derby/impl/services/bytecode/BCMethod.java?rev=590776&r1=590775&r2=590776&view=diff
==============================================================================
--- db/derby/code/branches/10.1/java/engine/org/apache/derby/impl/services/bytecode/BCMethod.java
(original)
+++ db/derby/code/branches/10.1/java/engine/org/apache/derby/impl/services/bytecode/BCMethod.java
Wed Oct 31 11:24:56 2007
@@ -64,6 +64,15 @@
  *
  */
 class BCMethod implements MethodBuilder {
+    
+    /**
+     * Code length at which to split into sub-methods.
+     * Normally set to the maximim code length the
+     * JVM can support, but for testing the split code
+     * it can be reduced so that the standard tests
+     * cause some splitting. Tested with value set to 2000.
+     */
+    static final int CODE_SPLIT_LENGTH = VMOpcode.MAX_CODE_LENGTH;
 
 	final BCClass		cb;
 	protected final ClassHolder modClass; // the class it is in (modifiable fmt)
@@ -75,8 +84,20 @@
 	 */
 	private final String myName;
 
-	protected BCLocalField[] parameters; 
-	protected Vector thrownExceptions; // expected to be names of Classes under Throwable
+    /**
+     * Fast access for the parametes, will be null
+     * if the method has no parameters.
+     */
+	BCLocalField[] parameters; 
+    
+    /**
+     * List of parameter types with java language class names.
+     * Can be null or zero length for no parameters.
+     */
+    private final String[] parameterTypes;
+    
+    
+	Vector thrownExceptions; // expected to be names of Classes under Throwable
 
 	CodeChunk myCode;
 	protected ClassMember myEntry;
@@ -118,7 +139,7 @@
 
 		String[] vmParamterTypes;
 
-		if (parms != null) {
+		if (parms != null && parms.length != 0) {
 			int len = parms.length;
 			vmParamterTypes = new String[len];
 			parameters = new BCLocalField[len];
@@ -142,6 +163,8 @@
 
 		// get code chunk
 		myCode = new CodeChunk();
+        
+        parameterTypes = parms;
 	}
 	//
 	// MethodBuilder interface
@@ -200,24 +223,84 @@
 	 * reflected in the code generated for it.
 	 */
 	public void complete() {
-		// write exceptions attribute info
-		writeExceptions();
-		
+        
+        
+        int codeLength = myCode.getPC();
+        if (codeLength > CODE_SPLIT_LENGTH)
+            splitMethod();
+                  
+       // write exceptions attribute info
+        writeExceptions();
+        	
 		// get the code attribute to put itself into the class
 		// provide the final header information needed
 		myCode.complete(this, modClass, myEntry, maxStack, currentVarNum);
 	}
+    
+    /**
+     * Attempt to split a large method by pushing code out to several
+     * sub-methods. Performs a number of steps.
+     * <OL>
+     * <LI> Split at zero stack depth.
+     * <LI> Split at non-zero stack depth (FUTURE)
+     * </OL>
+     * 
+     * If the class has already exceeded some limit in building the
+     * class file format structures then don't attempt to split.
+     * Most likely the number of constant pool entries has been exceeded
+     * and thus the built class file no longer has integrity.
+     * The split code relies on being able to read the in-memory
+     * version of the class file in order to determine descriptors
+     * for methods and fields.
+     */
+    private void splitMethod() {
+        
+        int split_pc = 0;
+        for (int codeLength = myCode.getPC();
+               (cb.limitMsg == null) &&
+               (codeLength > CODE_SPLIT_LENGTH);
+            codeLength = myCode.getPC())
+        {
+            int lengthToCheck = codeLength - split_pc;
+
+            int optimalMinLength;
+            if (codeLength < CODE_SPLIT_LENGTH * 2) {
+                // minimum required
+                optimalMinLength = codeLength - CODE_SPLIT_LENGTH;
+            } else {
+                // try to split as much as possible
+                // need one for the return instruction
+                optimalMinLength = CODE_SPLIT_LENGTH - 1;
+            }
+
+            if (optimalMinLength > lengthToCheck)
+                optimalMinLength = lengthToCheck;
+
+            split_pc = myCode.splitZeroStack(this, modClass, split_pc,
+                    lengthToCheck, optimalMinLength);
+
+            // Negative split point returned means that no split
+            // was possible. Give up on this approach and goto
+            // the next approach (none-yet).
+            if (split_pc < 0) {
+                break;
+            }
+
+            // success, continue on splitting after the call to the
+            // sub-method if the method still execeeds the maximum length.
+        }
+    }
 
 	/*
-	 * class interface
-	 */
+     * class interface
+     */
 
 	/**
-	 * In their giveCode methods, the parts of the method body
-	 * will want to get to the constant pool to add their constants.
-	 * We really only want them treating it like a constant pool
-	 * inclusion mechanism, we could write a wrapper to limit it to that.
-	 */
+     * In their giveCode methods, the parts of the method body will want to get
+     * to the constant pool to add their constants. We really only want them
+     * treating it like a constant pool inclusion mechanism, we could write a
+     * wrapper to limit it to that.
+     */
 	ClassHolder constantPool() {
 		return modClass;
 	}
@@ -290,7 +373,7 @@
 	 * Corresponds to max_stack in the Code attribute of section 4.7.3
 	 * of the vm spec.
 	 */
-	private int maxStack;
+	int maxStack;
 	
 	/**
 	 * Current stack depth in this method, measured in words.
@@ -420,7 +503,7 @@
 			chunk.addInstrU2(VMOpcode.SIPUSH,value);
 		else {
 			int cpe = modClass.addConstant(value);
-			chunk.addInstrCPE(VMOpcode.LDC, cpe);
+			addInstrCPE(VMOpcode.LDC, cpe);
 		}
 		growStack(1, type);
 		
@@ -458,7 +541,7 @@
 		else 
 		{
 			int cpe = modClass.addConstant(value);
-			chunk.addInstrCPE(VMOpcode.LDC, cpe);
+			addInstrCPE(VMOpcode.LDC, cpe);
 		}
 		growStack(1, Type.FLOAT);
 	}
@@ -477,7 +560,7 @@
 	}
 	public void push(String value) {
 		int cpe = modClass.addConstant(value);
-		myCode.addInstrCPE(VMOpcode.LDC, cpe);
+		addInstrCPE(VMOpcode.LDC, cpe);
 		growStack(1, Type.STRING);
 	}
  
@@ -1057,6 +1140,21 @@
 		// an array reference is an object, hence width of 1
 		growStack(1, cb.factory.type(className.concat("[]")));
 	}
+    
+    /**
+     * Write a instruction that uses a constant pool entry
+     * as an operand, add a limit exceeded message if
+     * the number of constant pool entries has exceeded
+     * the limit.
+     */
+    private void addInstrCPE(short opcode, int cpe)
+    {
+        if (cpe >= VMOpcode.MAX_CONSTANT_POOL_ENTRIES)
+            cb.addLimitExceeded(this, "constant_pool_count",
+                    VMOpcode.MAX_CONSTANT_POOL_ENTRIES, cpe);
+        
+        myCode.addInstrCPE(opcode, cpe);
+    }
 
 	/**
 		Tell if statement number in this method builder hits limit.  This
@@ -1072,7 +1170,9 @@
 	public boolean statementNumHitLimit(int noStatementsAdded)
 	{
 		if (statementNum > 2048)    // 2K limit
+		{
 			return true;
+		}
 		else
 		{
 			statementNum = statementNum + noStatementsAdded;
@@ -1141,43 +1241,22 @@
 		// Only downside is that we may split into N methods when N-1 would suffice.
 		if (currentCodeSize < 55000)
 			return;
-		
+				
 		// only handle no-arg methods at the moment.
 		if (parameters != null)
 		{
 			if (parameters.length != 0)
 				return;
 		}
-		
-		int modifiers = myEntry.getModifier();	
-		//System.out.println("NEED TO SPLIT " + myEntry.getName() + "  " + currentCodeSize + "
stack " + stackDepth);
-
-		// the sub-method can be private to ensure that no-one
-		// can call it accidentally from outside the class.
-		modifiers &= ~(Modifier.PROTECTED | Modifier.PUBLIC);
-		modifiers |= Modifier.PRIVATE;
-		
-		String subMethodName = myName + "_s" + Integer.toString(subMethodCount++);
-		BCMethod subMethod = (BCMethod) cb.newMethodBuilder(
-				modifiers,
-				myReturnType, subMethodName);
-		subMethod.thrownExceptions = this.thrownExceptions;
+        		
+		BCMethod subMethod = getNewSubMethod(myReturnType, false);
 				
 		// stop any recursion
 		handlingOverflow = true;
 		
 		// in this method make a call to the sub method we will
 		// be transferring control to.
-		short op;
-		if ((modifiers & Modifier.STATIC) == 0)
-		{
-			op = VMOpcode.INVOKEVIRTUAL;
-			this.pushThis();
-		} else {
-			op = VMOpcode.INVOKESTATIC;
-		}
-		
-		this.callMethod(op, (String) null, subMethodName, myReturnType, 0);
+        callSubMethod(subMethod);
 	
 		// and return its value, works just as well for a void method!
 		this.methodReturn();
@@ -1202,5 +1281,62 @@
 		this.maxStack = subMethod.maxStack;
 		this.stackDepth = subMethod.stackDepth;
 	}
+	
+    /**
+     * Create a sub-method from this method to allow the code builder to split a
+     * single logical method into multiple methods to avoid the 64k per-method
+     * code size limit. The sub method with inherit the thrown exceptions of
+     * this method.
+     * 
+     * @param returnType
+     *            Return type of the new method
+     * @param withParameters
+     *            True to define the method with matching parameters false to
+     *            define it with no parameters.
+     * @return A valid empty sub method.
+     */
+    final BCMethod getNewSubMethod(String returnType, boolean withParameters) {
+        int modifiers = myEntry.getModifier();
+
+        // the sub-method can be private to ensure that no-one
+        // can call it accidentally from outside the class.
+        modifiers &= ~(Modifier.PROTECTED | Modifier.PUBLIC);
+        modifiers |= Modifier.PRIVATE;
+
+        String subMethodName = myName + "_s"
+                + Integer.toString(subMethodCount++);
+        BCMethod subMethod = (BCMethod) cb.newMethodBuilder(modifiers,
+                returnType, subMethodName, withParameters ? parameterTypes
+                        : null);
+        subMethod.thrownExceptions = this.thrownExceptions;
+
+        return subMethod;
+    }
+
+    /**
+     * Call a sub-method created by getNewSubMethod handling parameters
+     * correctly.
+     */
+    final void callSubMethod(BCMethod subMethod) {
+        // in this method make a call to the sub method we will
+        // be transferring control to.
+        short op;
+        if ((myEntry.getModifier() & Modifier.STATIC) == 0) {
+            op = VMOpcode.INVOKEVIRTUAL;
+            this.pushThis();
+        } else {
+            op = VMOpcode.INVOKESTATIC;
+        }
+
+        int parameterCount = subMethod.parameters == null ? 0
+                : subMethod.parameters.length;
+
+        // push my parameter values for the call.
+        for (int pi = 0; pi < parameterCount; pi++)
+            this.getParameter(pi);
+
+        this.callMethod(op, modClass.getName(), subMethod.getName(),
+                subMethod.myReturnType, parameterCount);
+    }
 }
 

Modified: db/derby/code/branches/10.1/java/engine/org/apache/derby/impl/services/bytecode/CodeChunk.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.1/java/engine/org/apache/derby/impl/services/bytecode/CodeChunk.java?rev=590776&r1=590775&r2=590776&view=diff
==============================================================================
--- db/derby/code/branches/10.1/java/engine/org/apache/derby/impl/services/bytecode/CodeChunk.java
(original)
+++ db/derby/code/branches/10.1/java/engine/org/apache/derby/impl/services/bytecode/CodeChunk.java
Wed Oct 31 11:24:56 2007
@@ -32,6 +32,7 @@
 import org.apache.derby.iapi.services.io.ArrayOutputStream;
 
 import java.io.IOException;
+import java.lang.reflect.Modifier;
 import java.util.Arrays;
 
 /**
@@ -733,7 +734,7 @@
 	void complete(BCMethod mb, ClassHolder ch,
 			ClassMember method, int maxStack, int maxLocals) {
 
-		int codeLength = getPC();
+        int codeLength =  getPC();
 
 		ClassFormatOutput out = cout;
 		
@@ -920,13 +921,14 @@
         for (; pc < endPc;) {
 
             short opcode = getOpcode(pc);
-
+            
+ 
             int stackDelta = stackWordDelta(ch, pc, opcode);
 
             stack += stackDelta;
             if (stack > maxStack)
                 maxStack = stack;
-
+ 
             int[] cond_pcs = findConditionalPCs(pc, opcode);
             if (cond_pcs != null) {
                 // an else block exists.
@@ -994,8 +996,7 @@
      * type.
      */
     private static int getDescriptorWordCount(String vmDescriptor) {
-        // System.out.println("vmDescriptor" + vmDescriptor);
-
+ 
         int width;
         if (VMDescriptor.DOUBLE.equals(vmDescriptor))
             width = 2;
@@ -1228,5 +1229,286 @@
         ret[5] = end_pc;
 
         return ret;
+    }
+    
+    /**
+     * Attempt to split the current method by pushing a chunk of
+     * its code into a sub-method. The starting point of the split
+     * (split_pc) must correspond to a stack depth of zero. It is the
+     * reponsibility of the caller to ensure this.
+     * Split is only made if there exists a chunk of code starting at
+     * pc=split_pc, whose stack depth upon termination is zero.
+     * The method will try to split a code section greater than
+     * optimalMinLength but may split earlier if no such block exists.
+     * <P>
+     * The method is aimed at splitting methods that contain
+     * many independent statements.
+     * <P>
+     * If a split is possible this method will perform the split and
+     * create a void sub method, and move the code into the sub-method
+     * and setup this method to call the sub-method before continuing.
+     * This method's max stack and current pc will be correctly set
+     * as though the method had just been created.
+     * 
+     * @param mb Method for this chunk.
+     * @param ch Class definition
+     * @param codeLength codeLength to check
+     * @param optimalMinLength minimum length required for split
+     */
+    final int splitZeroStack(BCMethod mb, ClassHolder ch, final int split_pc,
+            final int codeLength, final int optimalMinLength) {
+        int stack = 0;
+
+        // maximum possible split seen that is less than
+        // the minimum.
+        int possibleSplitLength = -1;
+
+        // do not split until at least this point (inclusive)
+        // used to ensure no split occurs in the middle of
+        // a conditional.
+        int outerConditionalEnd_pc = -1;
+
+        int end_pc = split_pc + codeLength;
+        for (int pc = split_pc; pc < end_pc;) {
+
+            short opcode = getOpcode(pc);
+
+            int stackDelta = stackWordDelta(ch, pc, opcode);
+
+            stack += stackDelta;
+
+            // Cannot split a conditional but need to calculate
+            // the stack depth at the end of the conditional.
+            // Each path through the conditional will have the
+            // same stack depth.
+            int[] cond_pcs = findConditionalPCs(pc, opcode);
+            if (cond_pcs != null) {
+                // an else block exists, skip the then block.
+                if (cond_pcs[3] != -1) {
+                    pc = cond_pcs[3];
+                    continue;
+                }
+
+                if (SanityManager.DEBUG) {
+                    if (outerConditionalEnd_pc != -1) {
+                        if (cond_pcs[5] >= outerConditionalEnd_pc)
+                            SanityManager.THROWASSERT("NESTED CONDITIONALS!");
+                    }
+                }
+
+                if (outerConditionalEnd_pc == -1) {
+                    outerConditionalEnd_pc = cond_pcs[5];
+                }
+            }
+
+            pc += instructionLength(opcode);
+
+            // Don't split in the middle of a conditional
+            if (outerConditionalEnd_pc != -1) {
+                if (pc > outerConditionalEnd_pc) {
+                    // passed the outermost conditional
+                    outerConditionalEnd_pc = -1;
+                }
+                continue;
+            }
+
+            if (stack != 0)
+                continue;
+
+            int splitLength = pc - split_pc;
+
+            if (splitLength < optimalMinLength) {
+                // record we do have a possible split.
+                possibleSplitLength = splitLength;
+                continue;
+            }
+
+            // no point splitting to a method bigger
+            // than the VM can handle. Save one for
+            // return instruction.
+            if (splitLength > BCMethod.CODE_SPLIT_LENGTH - 1) {
+                if (possibleSplitLength == -1)
+                    return -1;
+
+                // Decide if the earlier possible split is
+                // worth it. 100 is an arbitary number,
+                // a real low limit would be the number of
+                // bytes of instructions required to call
+                // the sub-method, four I think.
+                if (possibleSplitLength < 100)
+                    return -1;
+
+                // OK go with the earlier split
+                splitLength = possibleSplitLength;
+
+            }
+
+            // Yes, we can split this big method into a smaller method!!
+
+            BCMethod subMethod = startSubMethod(mb, "void", split_pc,
+                    splitLength);
+
+            CodeChunk subChunk = subMethod.myCode;
+
+            byte[] codeBytes = cout.getData();
+
+            // the code to be moved into the sub method
+            // as a block. This will correctly increase the
+            // program counter.
+            try {
+                subChunk.cout.write(codeBytes, CODE_OFFSET + split_pc,
+                        splitLength);
+            } catch (IOException ioe) {
+                // writing to a byte array
+            }
+
+            // Just cause the sub-method to return,
+            // fix up its maxStack and then complete it.
+            subChunk.addInstr(VMOpcode.RETURN);
+            subMethod.maxStack = subChunk.findMaxStack(ch, 0, subChunk.getPC());
+            subMethod.complete();
+
+            return removePushedCode(mb, ch, subMethod, split_pc, splitLength,
+                    codeLength);
+        }
+        return -1;
+    }
+
+    /**
+     * Start a sub method that we will split the portion of our current code to,
+     * starting from start_pc and including codeLength bytes of code.
+     * 
+     * Return a BCMethod obtained from BCMethod.getNewSubMethod with the passed
+     * in return type and same parameters as mb if the code block to be moved
+     * uses parameters.
+     */
+    private BCMethod startSubMethod(BCMethod mb, String returnType,
+            int split_pc, int codeLength) {
+
+        boolean needParameters = usesParameters(mb, split_pc, codeLength);
+
+        return mb.getNewSubMethod(returnType, needParameters);
+    }
+
+    /**
+     * Does a section of code use parameters.
+     * Any load, exception ALOAD_0 in an instance method, is
+     * seen as using parameters, as this complete byte code
+     * implementation does not use local variables.
+     * 
+     */
+    private boolean usesParameters(BCMethod mb, int pc, int codeLength) {
+
+        // does the method even have parameters?
+        if (mb.parameters == null)
+            return false;
+
+        boolean isStatic = (mb.myEntry.getModifier() & Modifier.STATIC) != 0;
+
+        int endPc = pc + codeLength;
+
+        for (; pc < endPc;) {
+            short opcode = getOpcode(pc);
+            switch (opcode) {
+            case VMOpcode.ILOAD_0:
+            case VMOpcode.LLOAD_0:
+            case VMOpcode.FLOAD_0:
+            case VMOpcode.DLOAD_0:
+                return true;
+
+            case VMOpcode.ALOAD_0:
+                if (isStatic)
+                    return true;
+                break;
+
+            case VMOpcode.ILOAD_1:
+            case VMOpcode.LLOAD_1:
+            case VMOpcode.FLOAD_1:
+            case VMOpcode.DLOAD_1:
+            case VMOpcode.ALOAD_1:
+                return true;
+
+            case VMOpcode.ILOAD_2:
+            case VMOpcode.LLOAD_2:
+            case VMOpcode.FLOAD_2:
+            case VMOpcode.DLOAD_2:
+            case VMOpcode.ALOAD_2:
+                return true;
+
+            case VMOpcode.ILOAD_3:
+            case VMOpcode.LLOAD_3:
+            case VMOpcode.FLOAD_3:
+            case VMOpcode.DLOAD_3:
+            case VMOpcode.ALOAD_3:
+                return true;
+
+            case VMOpcode.ILOAD:
+            case VMOpcode.LLOAD:
+            case VMOpcode.FLOAD:
+            case VMOpcode.DLOAD:
+            case VMOpcode.ALOAD:
+                return true;
+            default:
+                break;
+
+            }
+            pc += instructionLength(opcode);
+        }
+        return false;
+    }
+
+    /**
+     * Remove a block of code from this method that was
+     * pushed into a sub-method and call the sub-method.
+     * 
+     * Returns the pc of this method just after the call
+     * to the sub-method.
+     
+     * @param mb My method
+     * @param ch My class
+     * @param subMethod Sub-method code was pushed into
+     * @param split_pc Program counter the split started at
+     * @param splitLength Length of code split
+     * @param codeLength Length of code before split
+     */
+    private int removePushedCode(BCMethod mb, ClassHolder ch,
+            BCMethod subMethod, int split_pc, int splitLength, int codeLength) {
+        // now need to fix up this method, create
+        // a new CodeChunk just to be clearer than
+        // trying to modify this chunk directly.
+        CodeChunk replaceChunk = new CodeChunk();
+        mb.myCode = replaceChunk;
+        mb.maxStack = 0;
+
+        byte[] codeBytes = cout.getData();
+
+        // write any existing code before the split point
+        // into the replacement chunk.
+        if (split_pc != 0) {
+            try {
+                replaceChunk.cout.write(codeBytes, CODE_OFFSET, split_pc);
+            } catch (IOException ioe) {
+                // writing to a byte array
+            }
+        }
+
+        // Call the sub method, will write into replaceChunk.
+        mb.callSubMethod(subMethod);
+
+        int postSplit_pc = replaceChunk.getPC();
+
+        // Write the code remaining in this method into the replacement chunk
+
+        int remainingCodeLength = codeLength - splitLength;
+        try {
+            replaceChunk.cout.write(codeBytes, CODE_OFFSET + split_pc
+                    + splitLength, remainingCodeLength);
+        } catch (IOException ioe) {
+            // writing to a byte array
+        }
+
+        mb.maxStack = replaceChunk.findMaxStack(ch, 0, replaceChunk.getPC());
+
+        return postSplit_pc;
     }
 }

Modified: db/derby/code/branches/10.1/java/engine/org/apache/derby/impl/services/bytecode/Conditional.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.1/java/engine/org/apache/derby/impl/services/bytecode/Conditional.java?rev=590776&r1=590775&r2=590776&view=diff
==============================================================================
--- db/derby/code/branches/10.1/java/engine/org/apache/derby/impl/services/bytecode/Conditional.java
(original)
+++ db/derby/code/branches/10.1/java/engine/org/apache/derby/impl/services/bytecode/Conditional.java
Wed Oct 31 11:24:56 2007
@@ -265,19 +265,15 @@
 		
 		if (branchOpcode == VMOpcode.GOTO)
 		{
-			// Ensure the pc we are jumping to (the current pc)
-			// is within bounds of a valid method *after*
-			// we have added the extra bytes.
-			if ((target_pc + 2) >= VMOpcode.MAX_CODE_LENGTH)
-			{
-				mb.cb.addLimitExceeded(mb, "goto_target",
-						VMOpcode.MAX_CODE_LENGTH, target_pc + 2);
-				
-				// even if we fail continue to generate the correct code
-				// so that the assumptions in the patch up code are not broken.
-			}
-			
-		
+	
+            // The goto could be beyond the code length
+            // supported by the virtual machine: VMOpcode.MAX_CODE_LENGTH
+            // We allow this because later splits may bring the goto
+            // offset to within the required limits. If the goto
+            // still points outside the limits of the JVM then
+            // building the class will fail anyway since the code
+            // size will be too large. So no need to flag an error here.
+            	
 			// Change the GOTO to a GOTO_W, which means
 			// inserting 2 bytes into the stream.
 			CodeChunk mod = chunk.insertCodeSpace(branch_pc, 2);

Modified: db/derby/code/branches/10.1/java/testing/org/apache/derbyTesting/functionTests/master/largeCodeGen.out
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.1/java/testing/org/apache/derbyTesting/functionTests/master/largeCodeGen.out?rev=590776&r1=590775&r2=590776&view=diff
==============================================================================
--- db/derby/code/branches/10.1/java/testing/org/apache/derbyTesting/functionTests/master/largeCodeGen.out
(original)
+++ db/derby/code/branches/10.1/java/testing/org/apache/derbyTesting/functionTests/master/largeCodeGen.out
Wed Oct 31 11:24:56 2007
@@ -13,11 +13,13 @@
 PASS: PREPARE: Logical operators with 800 parameters
 PASS: Logical operators with 800 parameters
 FAILED QUERY: Logical operators with 900 parameters.
-PASS: PREPARE: IN clause with 3300 parameters
-PASS: IN clause with 3300 parameters
 PASS: PREPARE: IN clause with 3400 parameters
 PASS: IN clause with 3400 parameters
-FAILED QUERY: IN clause with 3500 parameters.
+PASS: PREPARE: IN clause with 97000 parameters
+PASS: IN clause with 97000 parameters
+PASS: PREPARE: IN clause with 98000 parameters
+PASS: IN clause with 98000 parameters
+FAILED QUERY: IN clause with 99000 parameters.
 PASS: PREPARE: SELECT with 800 unions
 PASS: EXECUTE SELECT with 800 unions Row data check ok
 PASS: PREPARE: SELECT with 900 unions

Modified: db/derby/code/branches/10.1/java/testing/org/apache/derbyTesting/functionTests/tests/lang/largeCodeGen.java
URL: http://svn.apache.org/viewvc/db/derby/code/branches/10.1/java/testing/org/apache/derbyTesting/functionTests/tests/lang/largeCodeGen.java?rev=590776&r1=590775&r2=590776&view=diff
==============================================================================
--- db/derby/code/branches/10.1/java/testing/org/apache/derbyTesting/functionTests/tests/lang/largeCodeGen.java
(original)
+++ db/derby/code/branches/10.1/java/testing/org/apache/derbyTesting/functionTests/tests/lang/largeCodeGen.java
Wed Oct 31 11:24:56 2007
@@ -162,7 +162,10 @@
 	  
 		// DERBY-739 raised number of parameters from 2700 to 3400
         // svn 372388 trunk - passed @ 3400
-		 for (int count = 3300; count <= 10000 ; count += 100)
+        // fixes for DERBY-766 to split methods with individual statements
+        // bumps the limit to 98,000 parameters.
+        testInClause(con, 3400);
+		 for (int count = 97000; count <= 200000 ; count += 1000)
 		 {
 			 // keep testing until it fails.
 			 if (testInClause(con, count))
@@ -214,7 +217,6 @@
 		{
 			createView.append(" UNION ALL (SELECT * FROM t0 )");
 		}
-		String createViewString = createView.toString();
 		//System.out.println(createViewString);
 		stmt.executeUpdate(createView.toString());
 		



Mime
View raw message