EMMA Coverage Report (generated Wed Jun 28 22:15:27 PDT 2006)
[all classes][org.apache.derby.impl.services.bytecode]

COVERAGE SUMMARY FOR SOURCE FILE [BCMethod.java]

nameclass, %method, %block, %line, %
BCMethod.java100% (1/1)96%  (54/56)84%  (1490/1772)86%  (357.1/413)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class BCMethod100% (1/1)96%  (54/56)84%  (1490/1772)86%  (357.1/413)
<static initializer> 100% (1/1)100% (32/32)100% (1/1)
BCMethod (ClassBuilder, String, String, int, String [], BCJava): void 100% (1/1)100% (105/105)100% (23/23)
addThrownException (String): void 100% (1/1)55%  (17/31)83%  (5/6)
callMethod (Object): int 100% (1/1)73%  (35/48)79%  (11/14)
callMethod (short, String, String, String, int): int 100% (1/1)100% (126/126)100% (30/30)
callSuper (): void 100% (1/1)100% (13/13)100% (3/3)
cast (String): void 100% (1/1)84%  (82/98)94%  (17.9/19)
complete (): void 100% (1/1)100% (14/14)100% (3/3)
completeConditional (): void 100% (1/1)100% (10/10)100% (2/2)
conditionalIf (): void 100% (1/1)100% (4/4)100% (2/2)
conditionalIf (short): void 100% (1/1)100% (18/18)100% (4/4)
conditionalIfNull (): void 100% (1/1)100% (4/4)100% (2/2)
constantPool (): ClassHolder 0%   (0/1)0%   (0/3)0%   (0/1)
describeMethod (short, String, String, String): Object 100% (1/1)65%  (34/52)59%  (4.7/8)
dup (): void 100% (1/1)90%  (18/20)98%  (4.9/5)
endStatement (): void 100% (1/1)100% (6/6)100% (3/3)
getArrayElement (int): void 100% (1/1)96%  (47/49)92%  (11/12)
getField (LocalField): void 100% (1/1)100% (21/21)100% (7/7)
getField (String, String, String): void 100% (1/1)100% (19/19)100% (6/6)
getField (short, String, String, String): void 100% (1/1)100% (26/26)100% (5/5)
getName (): String 100% (1/1)100% (3/3)100% (1/1)
getParameter (int): void 100% (1/1)83%  (34/41)86%  (6/7)
getStaticField (String, String, String): void 100% (1/1)100% (7/7)100% (2/2)
growStack (Type): void 100% (1/1)100% (6/6)100% (2/2)
growStack (int, Type): void 100% (1/1)84%  (74/88)93%  (13/14)
isInstanceOf (String): void 100% (1/1)100% (18/18)100% (5/5)
methodReturn (): void 100% (1/1)69%  (22/32)88%  (7/8)
overflowMethodCheck (): void 100% (1/1)11%  (15/132)18%  (6/34)
pop (): void 100% (1/1)83%  (20/24)84%  (5.9/7)
popStack (): Type 100% (1/1)100% (21/21)100% (4/4)
push (String): void 100% (1/1)100% (15/15)100% (4/4)
push (boolean): void 100% (1/1)100% (9/9)100% (2/2)
push (byte): void 0%   (0/1)0%   (0/5)0%   (0/2)
push (double): void 100% (1/1)100% (25/25)100% (7/7)
push (float): void 100% (1/1)91%  (40/44)91%  (10/11)
push (int): void 100% (1/1)100% (5/5)100% (2/2)
push (int, Type): void 100% (1/1)100% (52/52)100% (11/11)
push (long): void 100% (1/1)100% (34/34)100% (7/7)
push (short): void 100% (1/1)100% (5/5)100% (2/2)
pushNewArray (String, int): void 100% (1/1)95%  (59/62)92%  (11/12)
pushNewComplete (int): void 100% (1/1)100% (10/10)100% (2/2)
pushNewStart (String): void 100% (1/1)100% (29/29)100% (7/7)
pushNull (String): void 100% (1/1)100% (13/13)100% (3/3)
pushThis (): void 100% (1/1)100% (11/11)100% (3/3)
putField (LocalField): void 100% (1/1)100% (14/14)100% (4/4)
putField (String, String): void 100% (1/1)100% (23/23)100% (4/4)
putField (String, String, String): void 100% (1/1)84%  (62/74)94%  (15.9/17)
putField (Type, int, boolean): void 100% (1/1)94%  (29/31)98%  (8.8/9)
setArrayElement (int): void 100% (1/1)94%  (32/34)90%  (9/10)
setField (LocalField): void 100% (1/1)100% (19/19)100% (6/6)
startElseCode (): void 100% (1/1)66%  (23/35)83%  (5/6)
statementNumHitLimit (int): boolean 100% (1/1)100% (14/14)100% (4/4)
swap (): void 100% (1/1)66%  (41/62)72%  (13/18)
upCast (String): void 100% (1/1)100% (15/15)100% (3/3)
vmNameDeclaringClass (String): Type 100% (1/1)100% (10/10)100% (3/3)
writeExceptions (): void 100% (1/1)98%  (50/51)93%  (13/14)

1/*
2 
3   Derby - Class org.apache.derby.impl.services.bytecode.BCMethod
4 
5   Copyright 1997, 2005 The Apache Software Foundation or its licensors, as applicable.
6 
7   Licensed under the Apache License, Version 2.0 (the "License");
8   you may not use this file except in compliance with the License.
9   You may obtain a copy of the License at
10 
11      http://www.apache.org/licenses/LICENSE-2.0
12 
13   Unless required by applicable law or agreed to in writing, software
14   distributed under the License is distributed on an "AS IS" BASIS,
15   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   See the License for the specific language governing permissions and
17   limitations under the License.
18 
19 */
20 
21package org.apache.derby.impl.services.bytecode;
22 
23import org.apache.derby.iapi.services.compiler.ClassBuilder;
24import org.apache.derby.iapi.services.compiler.MethodBuilder;
25import org.apache.derby.iapi.services.classfile.ClassFormatOutput;
26import org.apache.derby.iapi.services.compiler.LocalField;
27import org.apache.derby.iapi.services.classfile.ClassHolder;
28import org.apache.derby.iapi.services.classfile.ClassMember;
29 
30import org.apache.derby.iapi.services.sanity.SanityManager;
31 
32import org.apache.derby.iapi.services.classfile.VMDescriptor;
33import org.apache.derby.iapi.services.classfile.VMOpcode;
34 
35import java.lang.reflect.Modifier;
36import java.util.Vector;
37import java.io.IOException;
38 
39/**
40 * MethodBuilder is used to piece together a method when
41 * building a java class definition.
42 * <p>
43 * When a method is first created, it has:
44 * <ul>
45 * <li> a return type
46 * <li> modifiers
47 * <li> a name
48 * <li> an empty parameter list
49 * <li> an empty throws list
50 * <li> an empty statement block
51 * </ul>
52 * <p>
53 * MethodBuilder implementations are required to supply a way for
54 * Statements and Expressions to give them code.  Most typically, they may have
55 * a stream to which their contents writes the code that is of
56 * the type to satisfy what the contents represent.
57 * MethodBuilder implementations also have to have a way to supply
58 * ClassBuilders with their code, that satisfies the type of class
59 * builder they are implemented with.  This is implementation-dependent,
60 * so ClassBuilders, MethodBuilders, Statements, and Expressions all have
61 * to be of the same implementation in order to interact to generate a class.
62 * <p>
63 * Method Builder implementation for generating bytecode.
64 *
65 */
66class BCMethod implements MethodBuilder {
67    
68    /**
69     * Code length at which to split into sub-methods.
70     * Normally set to the maximim code length the
71     * JVM can support, but for testing the split code
72     * it can be reduced so that the standard tests
73     * cause some splitting. Tested with value set to 2000.
74     */
75    static final int CODE_SPLIT_LENGTH = VMOpcode.MAX_CODE_LENGTH;
76 
77        final BCClass                cb;
78        protected final ClassHolder modClass; // the class it is in (modifiable fmt)
79        private final String myReturnType;
80        
81        /**
82         * The original name of the method, this
83         * represents how any user would call this method.
84         */
85        private final String myName;
86 
87    /**
88     * Fast access for the parametes, will be null
89     * if the method has no parameters.
90     */
91        BCLocalField[] parameters; 
92    
93    /**
94     * List of parameter types with java language class names.
95     * Can be null or zero length for no parameters.
96     */
97    private final String[] parameterTypes;
98    
99    
100        Vector thrownExceptions; // expected to be names of Classes under Throwable
101 
102        CodeChunk myCode;
103        protected ClassMember myEntry;
104 
105        private int currentVarNum;
106        private int statementNum;
107        
108        /**
109         * True if we are currently switching control
110         * over to a sub method to avoid hitting the code generation
111         * limit of 65535 bytes per method.
112         */
113        private boolean handlingOverflow;
114        
115        /**
116         * How many sub-methods we have overflowed to.
117         */
118        private int subMethodCount;
119 
120        BCMethod(ClassBuilder cb,
121                        String returnType,
122                        String methodName,
123                        int modifiers,
124                        String[] parms,
125                        BCJava factory) {
126 
127                this.cb = (BCClass) cb;
128                modClass = this.cb.modify();
129                myReturnType = returnType;
130                myName = methodName;
131 
132                if (SanityManager.DEBUG) {
133                           this.cb.validateType(returnType);
134                }
135 
136                // if the method is not static, allocate for "this".
137                if ((modifiers & Modifier.STATIC) == 0 )
138                        currentVarNum = 1;
139 
140                String[] vmParamterTypes;
141 
142                if (parms != null && parms.length != 0) {
143                        int len = parms.length;
144                        vmParamterTypes = new String[len];
145                        parameters = new BCLocalField[len];
146                        for (int i = 0; i < len; i++) {
147                                Type t = factory.type(parms[i]);
148                                parameters[i] = new BCLocalField(t, currentVarNum);
149                                currentVarNum += t.width();
150 
151                                // convert to vmname for the BCMethodDescriptor.get() call
152                                vmParamterTypes[i] = t.vmName();
153                        }
154                }
155                else
156                        vmParamterTypes = BCMethodDescriptor.EMPTY;
157 
158                // create a code attribute
159                String sig = BCMethodDescriptor.get(vmParamterTypes, factory.type(returnType).vmName(), factory);
160 
161                // stuff the completed information into the class.
162                myEntry = modClass.addMember(methodName, sig, modifiers);
163 
164                // get code chunk
165                myCode = new CodeChunk();
166        
167        parameterTypes = parms;
168        }
169        //
170        // MethodBuilder interface
171        //
172 
173        /**
174         * Return the logical name of the method. The current
175         * myEntry refers to the sub method we are currently
176         * overflowing to. Those sub-methods are hidden from any caller.
177         */
178        public String getName() {
179                return myName;
180        }
181 
182        public void getParameter(int id) {
183 
184                int num = parameters[id].cpi;
185                short typ = parameters[id].type.vmType();
186                if (num < 4)
187                        myCode.addInstr((short) (CodeChunk.LOAD_VARIABLE_FAST[typ]+num));
188                else
189                        myCode.addInstrWide(CodeChunk.LOAD_VARIABLE[typ], num);
190 
191                growStack(parameters[id].type);
192        }
193 
194        /**
195         * a throwable can be added to the end of
196         * the list of thrownExceptions.
197         */
198        public void addThrownException(String exceptionClass) {
199                
200                // cannot add exceptions after code generation has started.
201                // Allowing this would cause the method overflow/split to
202                // break as the top-level method would not have the exception
203                // added in the sub method.
204                if (SanityManager.DEBUG)
205                {
206                        if (myCode.getPC() != 0)
207                                SanityManager.THROWASSERT("Adding exception after code generation " + exceptionClass
208                                                + " to method " + getName());
209                }
210 
211                if (thrownExceptions == null)
212                        thrownExceptions = new Vector();
213                thrownExceptions.addElement(exceptionClass);
214        }
215 
216        /**
217         * when the method has had all of its parameters
218         * and thrown exceptions defined, and its statement
219          * block has been completed, it can be completed and
220         * its class file information generated.
221         * <p>
222         * further alterations of the method will not be
223         * reflected in the code generated for it.
224         */
225        public void complete() {
226        
227        // myCode.getPC() gives the code length since
228        // the program counter will be positioned after
229        // the last instruction. Note this value can
230        // be changed by the splitMethod call.
231        
232        if (myCode.getPC() > CODE_SPLIT_LENGTH)
233            splitMethod();
234                         
235       // write exceptions attribute info
236        writeExceptions();
237                
238                // get the code attribute to put itself into the class
239                // provide the final header information needed
240                myCode.complete(this, modClass, myEntry, maxStack, currentVarNum);
241        }
242    
243    /**
244     * Attempt to split a large method by pushing code out to several
245     * sub-methods. Performs a number of steps.
246     * <OL>
247     * <LI> Split at zero stack depth.
248     * <LI> Split at non-zero stack depth (FUTURE)
249     * </OL>
250     * 
251     * If the class has already exceeded some limit in building the
252     * class file format structures then don't attempt to split.
253     * Most likely the number of constant pool entries has been exceeded
254     * and thus the built class file no longer has integrity.
255     * The split code relies on being able to read the in-memory
256     * version of the class file in order to determine descriptors
257     * for methods and fields.
258     */
259    private void splitMethod() {
260        
261        int split_pc = 0;
262        for (int codeLength = myCode.getPC();
263               (cb.limitMsg == null) &&
264               (codeLength > CODE_SPLIT_LENGTH);
265            codeLength = myCode.getPC())
266        {
267            int lengthToCheck = codeLength - split_pc;
268 
269            int optimalMinLength;
270            if (codeLength < CODE_SPLIT_LENGTH * 2) {
271                // minimum required
272                optimalMinLength = codeLength - CODE_SPLIT_LENGTH;
273            } else {
274                // try to split as much as possible
275                // need one for the return instruction
276                optimalMinLength = CODE_SPLIT_LENGTH - 1;
277            }
278 
279            if (optimalMinLength > lengthToCheck)
280                optimalMinLength = lengthToCheck;
281 
282            split_pc = myCode.splitZeroStack(this, modClass, split_pc,
283                    lengthToCheck, optimalMinLength);
284 
285            // Negative split point returned means that no split
286            // was possible. Give up on this approach and goto
287            // the next approach (none-yet).
288            if (split_pc < 0) {
289                break;
290            }
291 
292            // success, continue on splitting after the call to the
293            // sub-method if the method still execeeds the maximum length.
294        }
295    }
296 
297        /*
298     * class interface
299     */
300 
301        /**
302     * In their giveCode methods, the parts of the method body will want to get
303     * to the constant pool to add their constants. We really only want them
304     * treating it like a constant pool inclusion mechanism, we could write a
305     * wrapper to limit it to that.
306     */
307        ClassHolder constantPool() {
308                return modClass;
309        }
310 
311 
312    //
313    // Class implementation
314    //
315 
316 
317        /**
318         * sets exceptionBytes to the attribute_info needed
319         * for a method's Exceptions attribute.
320         * The ClassUtilities take care of the header 6 bytes for us,
321         * so they are not included here.
322         * See The Java Virtual Machine Specification Section 4.7.5,
323         * Exceptions attribute.
324         */
325        protected void writeExceptions() {
326                if (thrownExceptions == null)
327                        return;
328 
329                int numExc = thrownExceptions.size();
330 
331                // don't write an Exceptions attribute if there are no exceptions.
332                if (numExc != 0) {
333 
334                        try{
335                                ClassFormatOutput eout = new ClassFormatOutput((numExc * 2) + 2);
336 
337                                eout.putU2(numExc); // number_of_exceptions
338 
339                                for (int i = 0; i < numExc; i++) {
340                                        // put each exception into the constant pool
341                                        String e = thrownExceptions.elementAt(i).toString();
342                                        int ei2 = modClass.addClassReference(e);
343 
344                                        // add constant pool index to exception attribute_info
345                                        eout.putU2(ei2);
346                                }
347 
348                                myEntry.addAttribute("Exceptions", eout);
349 
350                        } catch (IOException ioe) {
351                        }                        
352                }
353        }
354 
355        /*
356        ** New push compiler api.
357        */
358 
359        /**
360         * Array of the current types of the values on the stack.
361         * A type that types up two words on the stack, e.g. double
362         * will only occupy one element in this array.
363         * This array is dynamically re-sized as needed.
364         */
365        private Type[]        stackTypes = new Type[8];
366        
367        /**
368         * Points to the next array offset in stackTypes
369         * to be used. Really it's the number of valid entries
370         * in stackTypes.
371         */
372        private int     stackTypeOffset;
373 
374        /**
375         * Maximum stack depth seen in this method, measured in words.
376         * Corresponds to max_stack in the Code attribute of section 4.7.3
377         * of the vm spec.
378         */
379        int maxStack;
380        
381        /**
382         * Current stack depth in this method, measured in words.
383         */
384        private int stackDepth;
385 
386        private void growStack(int size, Type type) {
387                stackDepth += size;
388                if (stackDepth > maxStack)
389                        maxStack = stackDepth;
390                
391                if (stackTypeOffset >= stackTypes.length) {
392 
393                        Type[] newStackTypes = new Type[stackTypes.length + 8];
394                        System.arraycopy(stackTypes, 0, newStackTypes, 0, stackTypes.length);
395                        stackTypes = newStackTypes;
396                }
397 
398                stackTypes[stackTypeOffset++] = type;
399 
400                if (SanityManager.DEBUG) {
401 
402                        int sum = 0;
403                        for (int i = 0 ; i < stackTypeOffset; i++) {
404                                sum += stackTypes[i].width();
405                        }
406                        if (sum != stackDepth) {
407                                SanityManager.THROWASSERT("invalid stack depth " + stackDepth + " calc " + sum);
408                        }
409                }
410        }
411 
412        private void growStack(Type type) {
413                growStack(type.width(), type);
414        }
415 
416        private Type popStack() {
417                stackTypeOffset--;
418                Type topType = stackTypes[stackTypeOffset];
419                stackDepth -= topType.width();
420                return topType;
421 
422        }
423        
424        private Type[] copyStack()
425        {
426                Type[] stack = new Type[stackTypeOffset];
427                System.arraycopy(stackTypes, 0, stack, 0, stackTypeOffset);
428                return stack;
429        }
430 
431        public void pushThis() {
432                myCode.addInstr(VMOpcode.ALOAD_0);
433                growStack(1, cb.classType);
434        }
435 
436        public void push(byte value) {
437                push(value, Type.BYTE);
438        }
439 
440        public void push(boolean value) {
441                push(value ? 1 : 0, Type.BOOLEAN);
442        }
443 
444        public void push(short value) {
445                push(value, Type.SHORT);
446        }
447 
448        public void push(int value) {
449                push(value, Type.INT);
450        }
451 
452        public void dup() {
453                Type dup = popStack();
454                myCode.addInstr(dup.width() == 2  ? VMOpcode.DUP2 : VMOpcode.DUP);
455                growStack(dup);
456                growStack(dup);
457 
458        }
459 
460        public void swap() {
461 
462                // have A,B
463                // want B,A
464 
465                Type wB = popStack();
466                Type wA = popStack();
467                growStack(wB);
468                growStack(wA);
469 
470                if (wB.width() == 1) {
471                        // top value is one word
472                        if (wA.width() == 1) {
473                                myCode.addInstr(VMOpcode.SWAP);
474                                return;
475                        } else {
476                                myCode.addInstr(VMOpcode.DUP_X2);
477                                myCode.addInstr(VMOpcode.POP);
478                        }
479                } else {
480                        // top value is two words
481                        if (wA.width() == 1) {
482                                myCode.addInstr(VMOpcode.DUP2_X1);
483                                myCode.addInstr(VMOpcode.POP2);
484                        } else {
485                                myCode.addInstr(VMOpcode.DUP2_X2);
486                                myCode.addInstr(VMOpcode.POP2);
487                        }
488                }
489 
490                // all except the simple swap push an extra
491                // copy of B which needs to be popped.
492                growStack(wB);
493                popStack();
494 
495        }
496 
497    /**
498     * Push an integer value. Uses the special integer opcodes
499     * for the constants -1 to 5, BIPUSH for values that fit in
500     * a byte and SIPUSH for values that fit in a short. Otherwise
501     * uses LDC with a constant pool entry.
502     * 
503     * @param value Value to be pushed
504     * @param type Final type of the value.
505     */
506        private void push(int value, Type type) {
507 
508                CodeChunk chunk = myCode;
509 
510                if (value >= -1 && value <= 5)
511                        chunk.addInstr((short)(VMOpcode.ICONST_0+value));
512                else if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE)
513                        chunk.addInstrU1(VMOpcode.BIPUSH,value);
514                else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE)
515                        chunk.addInstrU2(VMOpcode.SIPUSH,value);
516                else {
517                        int cpe = modClass.addConstant(value);
518                        addInstrCPE(VMOpcode.LDC, cpe);
519                }
520                growStack(type.width(), type);
521                
522        }
523 
524    /**
525     * Push a long value onto the stack.
526     * For the values zero and one the LCONST_0 and
527     * LCONST_1 instructions are used.
528     * For values betwee Short.MIN_VALUE and Short.MAX_VALUE
529     * inclusive an byte/short/int value is pushed
530     * using push(int, Type) followed by an I2L instruction.
531     * This saves using a constant pool entry for such values.
532     * All other values use a constant pool entry. For values
533     * in the range of an Integer an integer constant pool
534     * entry is created to allow sharing with integer constants
535     * and to reduce constant pool slot entries.
536     */
537        public void push(long value) {
538        CodeChunk chunk = myCode;
539 
540        if (value == 0L || value == 1L) {
541            short opcode = value == 0L ? VMOpcode.LCONST_0 : VMOpcode.LCONST_1;
542            chunk.addInstr(opcode);
543        } else if (value >= Integer.MIN_VALUE && value <= Integer.MAX_VALUE) {
544            // the push(int, Type) method grows the stack for us.
545            push((int) value, Type.LONG);
546            chunk.addInstr(VMOpcode.I2L);
547            return;
548        } else {
549            int cpe = modClass.addConstant(value);
550            chunk.addInstrU2(VMOpcode.LDC2_W, cpe);
551        }
552        growStack(2, Type.LONG);
553    }
554        public void push(float value) {
555 
556                CodeChunk chunk = myCode;
557                
558                if (value == 0.0)
559                {
560                        chunk.addInstr(VMOpcode.FCONST_0);
561                }
562                else if (value == 1.0)
563                {
564                        chunk.addInstr(VMOpcode.FCONST_1);
565                }
566                else if (value == 2.0)
567                {
568                        chunk.addInstr(VMOpcode.FCONST_2);
569                }
570                else 
571                {
572                        int cpe = modClass.addConstant(value);
573                        addInstrCPE(VMOpcode.LDC, cpe);
574                }
575                growStack(1, Type.FLOAT);
576        }
577 
578        public void push(double value) {
579                CodeChunk chunk = myCode;
580 
581                if (value == 0.0) {
582                                chunk.addInstr(VMOpcode.DCONST_0);
583                }
584                else {
585                        int cpe = modClass.addConstant(value);
586                        chunk.addInstrU2(VMOpcode.LDC2_W, cpe);
587                }
588                growStack(2, Type.DOUBLE);
589        }
590        public void push(String value) {
591                int cpe = modClass.addConstant(value);
592                addInstrCPE(VMOpcode.LDC, cpe);
593                growStack(1, Type.STRING);
594        }
595 
596 
597        public void methodReturn() {
598 
599                short opcode;                
600                if (stackDepth != 0) {
601                        Type topType = popStack();
602                        opcode = CodeChunk.RETURN_OPCODE[topType.vmType()];
603                } else {
604                        opcode = VMOpcode.RETURN;
605                }
606 
607                myCode.addInstr(opcode);
608 
609                if (SanityManager.DEBUG) {
610                        if (stackDepth != 0)
611                                SanityManager.THROWASSERT("items left on stack " + stackDepth);
612                }
613        }
614 
615        public Object describeMethod(short opcode, String declaringClass, String methodName, String returnType) {
616 
617                Type rt = cb.factory.type(returnType);
618 
619                String methodDescriptor = BCMethodDescriptor.get(BCMethodDescriptor.EMPTY, rt.vmName(), cb.factory);
620 
621                if ((declaringClass == null) && (opcode != VMOpcode.INVOKESTATIC)) {
622 
623                        Type dt = stackTypes[stackTypeOffset - 1];
624 
625                        if (declaringClass == null)
626                                declaringClass = dt.javaName();
627                }
628                
629                int cpi = modClass.addMethodReference(declaringClass, methodName,
630                                methodDescriptor, opcode == VMOpcode.INVOKEINTERFACE);
631 
632                return new BCMethodCaller(opcode, rt, cpi);
633        }
634 
635        public int callMethod(Object methodDescriptor) {
636 
637                // pop the reference off the stack
638                popStack();
639 
640                BCMethodCaller mc = (BCMethodCaller) methodDescriptor;
641 
642                int cpi = mc.cpi;
643                short opcode = mc.opcode;
644 
645                if (opcode == VMOpcode.INVOKEINTERFACE) {
646                        myCode.addInstrU2U1U1(opcode, cpi, (short) 1, (short) 0);
647                }
648                else
649                        myCode.addInstrU2(opcode, cpi);
650                
651                // this is the return type of the method
652                Type rt = mc.type;
653                int rw = rt.width();
654                if (rw != 0)
655                        growStack(rw, rt);
656                else
657                {
658                        if (stackDepth == 0)
659                                overflowMethodCheck();
660                }
661                return cpi;
662        }
663 
664        public int callMethod(short opcode, String declaringClass, String methodName,
665                String returnType, int numArgs) {
666 
667                Type rt = cb.factory.type(returnType);
668 
669                int initialStackDepth = stackDepth;
670 
671                // get the array of parameter types
672 
673                String [] debugParameterTypes = null;
674                String[] vmParameterTypes;
675                if (numArgs == 0) {
676                        vmParameterTypes = BCMethodDescriptor.EMPTY;
677                } else {
678                        if (SanityManager.DEBUG) {
679                                debugParameterTypes = new String[numArgs];
680                        }
681                        vmParameterTypes = new String[numArgs];
682                        for (int i = numArgs - 1; i >= 0; i--) {
683                                Type at = popStack();
684 
685                                vmParameterTypes[i] = at.vmName();
686                                if (SanityManager.DEBUG) {
687                                        debugParameterTypes[i] = at.javaName();
688                                }
689                        }
690                }
691                
692                String methodDescriptor = BCMethodDescriptor.get(vmParameterTypes, rt.vmName(), cb.factory);
693 
694                Type dt = null;
695                if (opcode != VMOpcode.INVOKESTATIC) {
696 
697                        dt = popStack();
698                }
699                Type dtu = vmNameDeclaringClass(declaringClass);
700                if (dtu != null)
701                        dt = dtu;
702                
703                int cpi = modClass.addMethodReference(dt.vmNameSimple, methodName,
704                                methodDescriptor, opcode == VMOpcode.INVOKEINTERFACE);
705 
706                if (opcode == VMOpcode.INVOKEINTERFACE) {
707                        short callArgCount = (short) (initialStackDepth - stackDepth);
708                        myCode.addInstrU2U1U1(opcode, cpi, callArgCount, (short) 0);
709                }
710                else
711                        myCode.addInstrU2(opcode, cpi);
712                
713                // this is the return type of the method
714                int rw = rt.width();
715                if (rw != 0)
716                        growStack(rw, rt);
717                else
718                {
719                        if (stackDepth == 0)
720                                overflowMethodCheck();
721                }
722                // Check the declared type of the method
723                if (SanityManager.DEBUG) {
724 
725                        d_BCValidate.checkMethod(opcode, dt, methodName, debugParameterTypes, rt);
726                }
727 
728                return cpi;
729        }
730 
731        private Type vmNameDeclaringClass(String declaringClass) {
732                if (declaringClass == null)
733                        return null;
734                return cb.factory.type(declaringClass);
735        }
736 
737        public void callSuper() {
738 
739                pushThis();
740                callMethod(VMOpcode.INVOKESPECIAL, cb.getSuperClassName(), "<init>", "void", 0);
741        }
742 
743        public void pushNewStart(String className) {
744 
745                int cpi = modClass.addClassReference(className);
746 
747                // Use U2, not CPE, since only wide form exists.
748                myCode.addInstrU2(VMOpcode.NEW, cpi);
749                myCode.addInstr(VMOpcode.DUP);
750 
751                // Grow the stack twice as we are pushing
752                // two instances of newly created reference
753                Type nt = cb.factory.type(className);
754                growStack(1, nt);
755                growStack(1, nt);
756        }
757 
758        public void pushNewComplete(int numArgs) {
759                callMethod(VMOpcode.INVOKESPECIAL, (String) null, "<init>", "void", numArgs);
760        }
761 
762        public void upCast(String className) {
763                Type uct = cb.factory.type(className);
764 
765                stackTypes[stackTypeOffset - 1] = uct;
766                //popStack();
767                //growStack(1, uct);
768        }
769 
770        public void cast(String className) {
771                
772                // Perform a simple optimization to not
773                // insert a checkcast when the classname
774                // of the cast exactly matches the type name
775                // currently on the stack.
776                // This can reduce the amount of generated code.
777                // This compiler/class generator does not load
778                // classes to check relationships or any other
779                // information. Thus other optimizations where a cast
780                // is not required are not implemented.
781                Type tbc = stackTypes[stackTypeOffset - 1];
782                
783                short sourceType = tbc.vmType();
784                
785                if (sourceType == BCExpr.vm_reference)
786                {
787                        // Simple optimize step
788                        if (className.equals(tbc.javaName()))
789                        {
790                                // do nothing, exact matching type
791                                return;
792                        }
793                }
794                
795                Type ct = cb.factory.type(className);
796                popStack();
797                
798                short targetType = ct.vmType();
799 
800                if (SanityManager.DEBUG) {
801 
802                        if (!((sourceType == BCExpr.vm_reference &&
803                                targetType == BCExpr.vm_reference) ||
804                                (sourceType != BCExpr.vm_reference &&
805                                targetType != BCExpr.vm_reference))) {
806                                SanityManager.THROWASSERT("Both or neither must be object types " + ct.javaName() + " " + tbc.javaName());
807                        }
808                }
809 
810                // if it is an object type, do a checkcast on it.
811                if (sourceType == BCExpr.vm_reference) {
812 
813                        int cpi = modClass.addClassReference(ct.vmNameSimple);
814                        myCode.addInstrU2(VMOpcode.CHECKCAST, cpi);
815                }
816                // otherwise, try to convert it.
817                else {
818                        short opcode = VMOpcode.NOP;
819 
820                        // we use the conversionInfo array
821                        // to determine how to convert; if
822                        // the result type of the conversion
823                        // is not our target type, we are not done
824                        // yet.  Make sure there are no
825                        // infinite loop possibilities in the
826                        // conversionInfo array!
827                        while (sourceType!=targetType && opcode!=VMOpcode.BAD) {
828                                short[] currentConversion = 
829                                        CodeChunk.CAST_CONVERSION_INFO[sourceType][targetType];
830                                sourceType = currentConversion[1];
831                                opcode = currentConversion[0];
832                                if (opcode != VMOpcode.NOP) {
833                                        myCode.addInstr(opcode);
834                                }
835                        }
836                        if (SanityManager.DEBUG) {
837                                SanityManager.ASSERT(opcode != VMOpcode.BAD,
838                                        "BAD VMOpcode not expected in cast");
839                        }
840                }
841                growStack(ct);
842        }
843 
844        public void isInstanceOf(String className) {
845                int cpi = modClass.addClassReference(className);
846                myCode.addInstrU2(VMOpcode.INSTANCEOF, cpi);
847                popStack();
848                growStack(1, Type.BOOLEAN);
849        }
850 
851        public void pushNull(String type) {
852                myCode.addInstr(VMOpcode.ACONST_NULL);
853                growStack(1, cb.factory.type(type));
854        }
855 
856 
857        public void getField(LocalField field) {
858 
859                BCLocalField lf = (BCLocalField) field;
860                Type lt = lf.type;
861 
862                pushThis();
863                myCode.addInstrU2(VMOpcode.GETFIELD, lf.cpi);
864 
865                popStack();
866                growStack(lt);
867 
868        }
869 
870        public void getField(String declaringClass, String fieldName, String fieldType) {
871                Type dt = popStack();
872 
873                Type dtu = vmNameDeclaringClass(declaringClass);
874                if (dtu != null)
875                        dt = dtu;
876 
877                getField(VMOpcode.GETFIELD, dt.vmNameSimple, fieldName, fieldType);
878        }
879        /**
880                Push the contents of the described static field onto the stack.                
881        */
882        public void getStaticField(String declaringClass, String fieldName, String fieldType) {
883                getField(VMOpcode.GETSTATIC, declaringClass, fieldName, fieldType);
884        }
885 
886        private void getField(short opcode, String declaringClass, String fieldName, String fieldType) { 
887 
888                Type ft = cb.factory.type(fieldType);
889                int cpi = modClass.addFieldReference(vmNameDeclaringClass(declaringClass).vmNameSimple, fieldName, ft.vmName());
890                myCode.addInstrU2(opcode, cpi);
891 
892                growStack(ft);
893        }
894        
895        /**
896         * Set the field but don't duplicate its value so
897         * nothing is left on the stack after this call.
898         */
899        public void setField(LocalField field) {
900                BCLocalField lf = (BCLocalField) field;
901                Type lt = lf.type;
902 
903                putField(lf.type, lf.cpi, false);
904 
905                if (stackDepth == 0)
906                        overflowMethodCheck();
907        }
908 
909        /**
910                Upon entry the top word(s) on the stack is
911                the value to be put into the field. Ie.
912                we have
913                <PRE>
914                word
915                </PRE>
916 
917                Before the call we need 
918                <PRE>
919                word
920                this
921                word
922                </PRE>
923                word2,word1 -> word2, word1, word2
924 
925                So that we are left with word after the put.
926 
927        */
928        public void putField(LocalField field) {
929                BCLocalField lf = (BCLocalField) field;
930                Type lt = lf.type;
931 
932                putField(lf.type, lf.cpi, true);
933        }
934 
935        /**
936                Pop the top stack value and store it in the instance field of this class.
937        */
938        public void putField(String fieldName, String fieldType) {
939 
940                Type ft = cb.factory.type(fieldType);
941                int cpi = modClass.addFieldReference(cb.classType.vmNameSimple, fieldName, ft.vmName());
942 
943                putField(ft, cpi, true);
944        }
945 
946        private void putField(Type fieldType, int cpi, boolean dup) {
947 
948                // now have ...,value
949                if (dup)
950                {
951                        myCode.addInstr(fieldType.width() == 2  ? VMOpcode.DUP2 : VMOpcode.DUP);
952                        growStack(fieldType);
953                }
954                // now have
955                // dup true:  ...,value,value
956                // dup false: ...,value,
957 
958                pushThis();
959                // now have
960                // dup true:  ...,value,value,this
961                // dup false: ...,value,this
962 
963                swap();
964                // now have
965                // dup true:  ...,value,this,value
966                // dup false: ...,this,value
967 
968                myCode.addInstrU2(VMOpcode.PUTFIELD, cpi);
969                popStack(); // the value
970                popStack(); // this
971 
972                // now have
973                // dup true:  ...,value
974                // dup false: ...
975        }
976        /**
977                Pop the top stack value and store it in the field.
978                This call requires the instance to be pushed by the caller.
979        */
980        public void putField(String declaringClass, String fieldName, String fieldType) {
981                Type vt = popStack();
982                Type dt = popStack();
983 
984                if (SanityManager.DEBUG) {
985                        if (dt.width() != 1)
986                                SanityManager.THROWASSERT("reference expected for field access - is " + dt.javaName());
987                }
988 
989                // have objectref,value
990                // need value,objectref,value
991 
992                myCode.addInstr(vt.width() == 2  ? VMOpcode.DUP2_X1 : VMOpcode.DUP_X1);
993                growStack(vt);
994                growStack(dt);
995                growStack(vt);
996 
997                Type dtu = vmNameDeclaringClass(declaringClass);
998                if (dtu != null)
999                        dt = dtu;
1000 
1001                Type ft = cb.factory.type(fieldType);
1002                int cpi = modClass.addFieldReference(dt.vmNameSimple, fieldName, ft.vmName());
1003                myCode.addInstrU2(VMOpcode.PUTFIELD, cpi);
1004 
1005                popStack(); // value
1006                popStack(); // reference
1007        }
1008 
1009        public void conditionalIfNull() {
1010 
1011                conditionalIf(VMOpcode.IFNONNULL);
1012        }
1013 
1014        public void conditionalIf() {
1015                conditionalIf(VMOpcode.IFEQ);
1016        }
1017 
1018        private Conditional condition;
1019 
1020        private void conditionalIf(short opcode) {
1021                popStack();
1022                
1023                // Save the stack upon entry to the 'then' block of the
1024                // 'if' so that we can set up the 'else' block with the
1025                // correct stack on entry.
1026 
1027                condition = new Conditional(condition, myCode, opcode, copyStack());
1028        }
1029 
1030        public void startElseCode() {
1031                
1032                // start the else code
1033                Type[] entryStack = condition.startElse(this, myCode, copyStack());
1034                
1035                for (int i = stackDepth = 0; i  < entryStack.length; i++)
1036                {
1037                        stackDepth += (stackTypes[i] = entryStack[i]).width();
1038                }
1039                this.stackTypeOffset = entryStack.length;
1040 
1041        }
1042        public void completeConditional() {
1043                condition = condition.end(this, myCode, stackTypes, stackTypeOffset);
1044        }
1045        
1046        public void pop() {
1047                if (SanityManager.DEBUG) {
1048                        if (stackDepth == 0)
1049                                SanityManager.THROWASSERT("pop when stack is empty!");
1050                }
1051                Type toPop = popStack();
1052 
1053                myCode.addInstr(toPop.width() == 2  ? VMOpcode.POP2 : VMOpcode.POP);
1054                
1055                if (stackDepth == 0)
1056                        overflowMethodCheck();
1057        }        
1058 
1059        public void endStatement() {
1060                if (stackDepth != 0) {
1061                        pop();
1062                }
1063 
1064                //if (SanityManager.DEBUG) {
1065                //        if (stackDepth != 0)
1066                //                SanityManager.THROWASSERT("items left on stack " + stackDepth);
1067        //        }
1068        }
1069 
1070        /**
1071        */
1072        public void getArrayElement(int element) {
1073 
1074                push(element);
1075                popStack(); // int just pushed will be popped by array access
1076 
1077                Type arrayType = popStack();
1078 
1079                String arrayJava = arrayType.javaName();
1080                String componentString = arrayJava.substring(0,arrayJava.length()-2);
1081 
1082                Type componentType = cb.factory.type(componentString);
1083 
1084                short typ = componentType.vmType();
1085 
1086                // boolean has a type id of integer, here it needs to be byte.
1087                if ((typ == BCExpr.vm_int) && (componentType.vmName().equals("Z")))
1088                        typ = BCExpr.vm_byte;
1089                myCode.addInstr(CodeChunk.ARRAY_ACCESS[typ]);
1090 
1091                growStack(componentType);
1092 
1093        }
1094        // come in with ref, value
1095 
1096        public void setArrayElement(int element) {
1097 
1098                // ref, value
1099 
1100                push(element);
1101 
1102                // ref, value, index
1103                swap();
1104                
1105                Type componentType = popStack(); // value
1106                popStack(); // int just pushed will be popped by array access
1107                
1108                popStack(); // array ref.
1109 
1110                short typ = componentType.vmType();
1111 
1112                // boolean has a type id of integer, here it needs to be byte.
1113                if ((typ == BCExpr.vm_int) && (componentType.vmName().equals("Z")))
1114                        typ = BCExpr.vm_byte;
1115 
1116                myCode.addInstr(CodeChunk.ARRAY_STORE[typ]);
1117        }
1118        /**
1119                this array maps the BCExpr vm_* constants 0..6 to
1120                the expected VM type constants for the newarray instruction.
1121                <p>
1122                Because boolean was mapped to integer for general instructions,
1123                it will have to be specially matched and mapped to its value
1124                directly (4).
1125         */
1126        private static final byte newArrayElementTypeMap[] = { 8, 9, 10, 11, 6, 7, 5 };
1127        static final byte T_BOOLEAN = 4;
1128        /**
1129                Create an array instance
1130 
1131                Stack ... =>
1132                      ...,arrayref
1133        */
1134        public void pushNewArray(String className, int size) {
1135 
1136                push(size);
1137                popStack(); // int just pushed will be popped by array creation
1138 
1139                Type elementType = cb.factory.type(className);
1140 
1141                // determine the instruction to use based on the element type
1142                if (elementType.vmType() == BCExpr.vm_reference) {
1143 
1144                        // For an array of Java class/interface elements, generate:
1145                        // ANEWARRAY #cpei ; where cpei is a constant pool index for the class
1146 
1147                        int cpi = modClass.addClassReference(elementType.javaName());
1148                        // Use U2, not CPE, since only wide form exists.
1149                        myCode.addInstrU2(VMOpcode.ANEWARRAY, cpi);
1150                } else {
1151                        byte atype;
1152 
1153                        // get the argument for the array type
1154                        // if the element type is boolean, we can't use the map
1155                        // because the type id will say integer.
1156                        // but we can use vm_int test to weed out some tests
1157                        if (elementType.vmType() == BCExpr.vm_int &&
1158                            VMDescriptor.C_BOOLEAN == elementType.vmName().charAt(0))
1159                                atype = T_BOOLEAN;
1160                        else
1161                                atype = newArrayElementTypeMap[elementType.vmType()];
1162 
1163                        // For an array of Java builtin type elements, generate:
1164                        // NEWARRAY #atype ; where atype is a constant for the builtin type
1165 
1166                        myCode.addInstrU1(VMOpcode.NEWARRAY, atype);
1167                }
1168 
1169                // an array reference is an object, hence width of 1
1170                growStack(1, cb.factory.type(className.concat("[]")));
1171        }
1172    
1173    /**
1174     * Write a instruction that uses a constant pool entry
1175     * as an operand, add a limit exceeded message if
1176     * the number of constant pool entries has exceeded
1177     * the limit.
1178     */
1179    private void addInstrCPE(short opcode, int cpe)
1180    {
1181        if (cpe >= VMOpcode.MAX_CONSTANT_POOL_ENTRIES)
1182            cb.addLimitExceeded(this, "constant_pool_count",
1183                    VMOpcode.MAX_CONSTANT_POOL_ENTRIES, cpe);
1184        
1185        myCode.addInstrCPE(opcode, cpe);
1186    }
1187 
1188        /**
1189                Tell if statement number in this method builder hits limit.  This
1190                method builder keeps a counter of how many statements are added to it.
1191                Caller should call this function every time it tries to add a statement
1192                to this method builder (counter is increased by 1), then the function
1193                returns whether the accumulated statement number hits a limit.
1194                The reason of doing this is that Java compiler has a limit of 64K code
1195                size for each method.  We might hit this limit if an extremely long
1196                insert statement is issued, for example (see beetle 4293).  Counting
1197                statement number is an approximation without too much overhead.
1198        */
1199        public boolean statementNumHitLimit(int noStatementsAdded)
1200        {
1201                if (statementNum > 2048)    // 2K limit
1202                {
1203                        return true;
1204                }
1205                else
1206                {
1207                        statementNum = statementNum + noStatementsAdded;
1208                        return false;
1209                }
1210        }
1211        
1212        /**
1213         * Check to see if the current method byte code is nearing the
1214         * limit of 65535. If it is start overflowing to a new method.
1215         * <P>
1216         * Overflow is handled for a method named e23 as:
1217         * <CODE>
1218         public Object e23()
1219         {
1220           ... existing code
1221           // split point
1222           return e23_0();
1223         }
1224         private Object e23_0()
1225         {
1226            ... first set overflowed code
1227            // split point
1228            return e23_1(); 
1229         }
1230         private Object e23_1()
1231         {
1232            ... second set overflowed code
1233            // method complete
1234            return result; 
1235         }
1236                  </CODE>
1237         <P>
1238         
1239         These overflow methods are hidden from the code using this MethodBuilder,
1240         it continues to think that it is building a single method with the
1241         original name.
1242 
1243 
1244         * <BR> Restrictions:
1245         * <UL>
1246         * <LI> Only handles methods with no arguments
1247         * <LI> Stack depth must be zero
1248         * </UL>
1249         * 
1250         *
1251         */
1252        private void overflowMethodCheck()
1253        {
1254                if (handlingOverflow)
1255                        return;
1256                
1257                // don't sub method in the middle of a conditional
1258                if (condition != null)
1259                        return;
1260                
1261                int currentCodeSize = myCode.getPC();
1262                
1263                // Overflow at >= 55,000 bytes which is someway
1264                // below the limit of 65,535. Ideally overflow
1265                // would occur at 65535 minus the few bytes needed
1266                // to call the sub-method, but the issue is at this level
1267                // we don't know frequently we are called given the restriction
1268                // of only being called when the stack depth is zero.
1269                // Thus split earlier to try ensure most cases are caught.
1270                // Only downside is that we may split into N methods when N-1 would suffice.
1271                if (currentCodeSize < 55000)
1272                        return;
1273                                
1274                // only handle no-arg methods at the moment.
1275                if (parameters != null)
1276                {
1277                        if (parameters.length != 0)
1278                                return;
1279                }
1280                        
1281                BCMethod subMethod = getNewSubMethod(myReturnType, false);
1282                                
1283                // stop any recursion
1284                handlingOverflow = true;
1285                
1286                // in this method make a call to the sub method we will
1287                // be transferring control to.
1288        callSubMethod(subMethod);
1289        
1290                // and return its value, works just as well for a void method!
1291                this.methodReturn();
1292                this.complete();
1293                
1294                handlingOverflow = false;
1295                
1296                // now the tricky bit, make this object take over the
1297                // code etc. from the sub method. This is done so
1298                // that any code that has a reference to this MethodBuilder
1299                // will continue to work. They will be writing code into the
1300                // new sub method.
1301                
1302                this.myEntry = subMethod.myEntry;
1303                this.myCode = subMethod.myCode;
1304                this.currentVarNum = subMethod.currentVarNum;
1305                this.statementNum = subMethod.statementNum;
1306                
1307                // copy stack info
1308                this.stackTypes = subMethod.stackTypes;
1309                this.stackTypeOffset = subMethod.stackTypeOffset;
1310                this.maxStack = subMethod.maxStack;
1311                this.stackDepth = subMethod.stackDepth;
1312        }
1313        
1314    /**
1315     * Create a sub-method from this method to allow the code builder to split a
1316     * single logical method into multiple methods to avoid the 64k per-method
1317     * code size limit. The sub method with inherit the thrown exceptions of
1318     * this method.
1319     * 
1320     * @param returnType
1321     *            Return type of the new method
1322     * @param withParameters
1323     *            True to define the method with matching parameters false to
1324     *            define it with no parameters.
1325     * @return A valid empty sub method.
1326     */
1327    final BCMethod getNewSubMethod(String returnType, boolean withParameters) {
1328        int modifiers = myEntry.getModifier();
1329 
1330        // the sub-method can be private to ensure that no-one
1331        // can call it accidentally from outside the class.
1332        modifiers &= ~(Modifier.PROTECTED | Modifier.PUBLIC);
1333        modifiers |= Modifier.PRIVATE;
1334 
1335        String subMethodName = myName + "_s"
1336                + Integer.toString(subMethodCount++);
1337        BCMethod subMethod = (BCMethod) cb.newMethodBuilder(modifiers,
1338                returnType, subMethodName, withParameters ? parameterTypes
1339                        : null);
1340        subMethod.thrownExceptions = this.thrownExceptions;
1341 
1342        return subMethod;
1343    }
1344 
1345    /**
1346     * Call a sub-method created by getNewSubMethod handling parameters
1347     * correctly.
1348     */
1349    final void callSubMethod(BCMethod subMethod) {
1350        // in this method make a call to the sub method we will
1351        // be transferring control to.
1352        short op;
1353        if ((myEntry.getModifier() & Modifier.STATIC) == 0) {
1354            op = VMOpcode.INVOKEVIRTUAL;
1355            this.pushThis();
1356        } else {
1357            op = VMOpcode.INVOKESTATIC;
1358        }
1359 
1360        int parameterCount = subMethod.parameters == null ? 0
1361                : subMethod.parameters.length;
1362 
1363        // push my parameter values for the call.
1364        for (int pi = 0; pi < parameterCount; pi++)
1365            this.getParameter(pi);
1366 
1367        this.callMethod(op, modClass.getName(), subMethod.getName(),
1368                subMethod.myReturnType, parameterCount);
1369    }
1370}
1371 

[all classes][org.apache.derby.impl.services.bytecode]
EMMA 2.0.5312 (C) Vladimir Roubtsov