KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > derby > impl > services > bytecode > BCMethod


1 /*
2
3    Derby - Class org.apache.derby.impl.services.bytecode.BCMethod
4
5    Licensed to the Apache Software Foundation (ASF) under one or more
6    contributor license agreements. See the NOTICE file distributed with
7    this work for additional information regarding copyright ownership.
8    The ASF licenses this file to you under the Apache License, Version 2.0
9    (the "License"); you may not use this file except in compliance with
10    the License. You may obtain a copy of the License at
11
12       http://www.apache.org/licenses/LICENSE-2.0
13
14    Unless required by applicable law or agreed to in writing, software
15    distributed under the License is distributed on an "AS IS" BASIS,
16    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17    See the License for the specific language governing permissions and
18    limitations under the License.
19
20  */

21
22 package org.apache.derby.impl.services.bytecode;
23
24 import org.apache.derby.iapi.services.compiler.ClassBuilder;
25 import org.apache.derby.iapi.services.compiler.MethodBuilder;
26 import org.apache.derby.iapi.services.classfile.ClassFormatOutput;
27 import org.apache.derby.iapi.services.compiler.LocalField;
28 import org.apache.derby.iapi.services.classfile.ClassHolder;
29 import org.apache.derby.iapi.services.classfile.ClassMember;
30
31 import org.apache.derby.iapi.services.sanity.SanityManager;
32
33 import org.apache.derby.iapi.services.classfile.VMDescriptor;
34 import org.apache.derby.iapi.services.classfile.VMOpcode;
35
36 import java.lang.reflect.Modifier JavaDoc;
37 import java.util.Vector JavaDoc;
38 import java.io.IOException JavaDoc;
39
40 /**
41  * MethodBuilder is used to piece together a method when
42  * building a java class definition.
43  * <p>
44  * When a method is first created, it has:
45  * <ul>
46  * <li> a return type
47  * <li> modifiers
48  * <li> a name
49  * <li> an empty parameter list
50  * <li> an empty throws list
51  * <li> an empty statement block
52  * </ul>
53  * <p>
54  * MethodBuilder implementations are required to supply a way for
55  * Statements and Expressions to give them code. Most typically, they may have
56  * a stream to which their contents writes the code that is of
57  * the type to satisfy what the contents represent.
58  * MethodBuilder implementations also have to have a way to supply
59  * ClassBuilders with their code, that satisfies the type of class
60  * builder they are implemented with. This is implementation-dependent,
61  * so ClassBuilders, MethodBuilders, Statements, and Expressions all have
62  * to be of the same implementation in order to interact to generate a class.
63  * <p>
64  * Method Builder implementation for generating bytecode.
65  *
66  */

67 class BCMethod implements MethodBuilder {
68     
69     /**
70      * Code length at which to split into sub-methods.
71      * Normally set to the maximim code length the
72      * JVM can support, but for testing the split code
73      * it can be reduced so that the standard tests
74      * cause some splitting. Tested with value set to 2000.
75      */

76     static final int CODE_SPLIT_LENGTH = VMOpcode.MAX_CODE_LENGTH;
77     
78     final BCClass cb;
79     protected final ClassHolder modClass; // the class it is in (modifiable fmt)
80
final String JavaDoc myReturnType;
81     
82     /**
83      * The original name of the method, this
84      * represents how any user would call this method.
85      */

86     private final String JavaDoc myName;
87
88     /**
89      * Fast access for the parametes, will be null
90      * if the method has no parameters.
91      */

92     BCLocalField[] parameters;
93     
94     /**
95      * List of parameter types with java language class names.
96      * Can be null or zero length for no parameters.
97      */

98     private final String JavaDoc[] parameterTypes;
99     
100     
101     Vector JavaDoc thrownExceptions; // expected to be names of Classes under Throwable
102

103     CodeChunk myCode;
104     protected ClassMember myEntry;
105
106     private int currentVarNum;
107     private int statementNum;
108     
109     /**
110      * True if we are currently switching control
111      * over to a sub method to avoid hitting the code generation
112      * limit of 65535 bytes per method.
113      */

114     private boolean handlingOverflow;
115     
116     /**
117      * How many sub-methods we have overflowed to.
118      */

119     private int subMethodCount;
120
121     BCMethod(ClassBuilder cb,
122             String JavaDoc returnType,
123             String JavaDoc methodName,
124             int modifiers,
125             String JavaDoc[] parms,
126             BCJava factory) {
127
128         this.cb = (BCClass) cb;
129         modClass = this.cb.modify();
130         myReturnType = returnType;
131         myName = methodName;
132
133         if (SanityManager.DEBUG) {
134             this.cb.validateType(returnType);
135         }
136
137         // if the method is not static, allocate for "this".
138
if ((modifiers & Modifier.STATIC) == 0 )
139             currentVarNum = 1;
140
141         String JavaDoc[] vmParamterTypes;
142
143         if (parms != null && parms.length != 0) {
144             int len = parms.length;
145             vmParamterTypes = new String JavaDoc[len];
146             parameters = new BCLocalField[len];
147             for (int i = 0; i < len; i++) {
148                 Type t = factory.type(parms[i]);
149                 parameters[i] = new BCLocalField(t, currentVarNum);
150                 currentVarNum += t.width();
151
152                 // convert to vmname for the BCMethodDescriptor.get() call
153
vmParamterTypes[i] = t.vmName();
154             }
155         }
156         else
157             vmParamterTypes = BCMethodDescriptor.EMPTY;
158
159         // create a code attribute
160
String JavaDoc sig = BCMethodDescriptor.get(vmParamterTypes, factory.type(returnType).vmName(), factory);
161
162         // stuff the completed information into the class.
163
myEntry = modClass.addMember(methodName, sig, modifiers);
164
165         // get code chunk
166
myCode = new CodeChunk(this.cb);
167         
168         parameterTypes = parms;
169     }
170     //
171
// MethodBuilder interface
172
//
173

174     /**
175      * Return the logical name of the method. The current
176      * myEntry refers to the sub method we are currently
177      * overflowing to. Those sub-methods are hidden from any caller.
178      */

179     public String JavaDoc getName() {
180         return myName;
181     }
182
183     public void getParameter(int id) {
184
185         int num = parameters[id].cpi;
186         short typ = parameters[id].type.vmType();
187         if (num < 4)
188             myCode.addInstr((short) (CodeChunk.LOAD_VARIABLE_FAST[typ]+num));
189         else
190             myCode.addInstrWide(CodeChunk.LOAD_VARIABLE[typ], num);
191
192         growStack(parameters[id].type);
193     }
194
195     /**
196      * a throwable can be added to the end of
197      * the list of thrownExceptions.
198      */

199     public void addThrownException(String JavaDoc exceptionClass) {
200         
201         // cannot add exceptions after code generation has started.
202
// Allowing this would cause the method overflow/split to
203
// break as the top-level method would not have the exception
204
// added in the sub method.
205
if (SanityManager.DEBUG)
206         {
207             if (myCode.getPC() != 0)
208                 SanityManager.THROWASSERT("Adding exception after code generation " + exceptionClass
209                         + " to method " + getName());
210         }
211
212         if (thrownExceptions == null)
213             thrownExceptions = new Vector JavaDoc();
214         thrownExceptions.addElement(exceptionClass);
215     }
216
217     /**
218      * when the method has had all of its parameters
219      * and thrown exceptions defined, and its statement
220      * block has been completed, it can be completed and
221      * its class file information generated.
222      * <p>
223      * further alterations of the method will not be
224      * reflected in the code generated for it.
225      */

226     public void complete() {
227         
228         // myCode.getPC() gives the code length since
229
// the program counter will be positioned after
230
// the last instruction. Note this value can
231
// be changed by the splitMethod call.
232

233         if (myCode.getPC() > CODE_SPLIT_LENGTH)
234             splitMethod();
235                          
236        // write exceptions attribute info
237
writeExceptions();
238             
239         // get the code attribute to put itself into the class
240
// provide the final header information needed
241
myCode.complete(this, modClass, myEntry, maxStack, currentVarNum);
242     }
243     
244     /**
245      * Attempt to split a large method by pushing code out to several
246      * sub-methods. Performs a number of steps.
247      * <OL>
248      * <LI> Split at zero stack depth.
249      * <LI> Split at non-zero stack depth (FUTURE)
250      * </OL>
251      *
252      * If the class has already exceeded some limit in building the
253      * class file format structures then don't attempt to split.
254      * Most likely the number of constant pool entries has been exceeded
255      * and thus the built class file no longer has integrity.
256      * The split code relies on being able to read the in-memory
257      * version of the class file in order to determine descriptors
258      * for methods and fields.
259      */

260     private void splitMethod() {
261         
262         int split_pc = 0;
263         boolean splittingZeroStack = true;
264         for (int codeLength = myCode.getPC();
265                (cb.limitMsg == null) &&
266                (codeLength > CODE_SPLIT_LENGTH);
267             codeLength = myCode.getPC())
268         {
269             int lengthToCheck = codeLength - split_pc;
270
271             int optimalMinLength;
272             if (codeLength < CODE_SPLIT_LENGTH * 2) {
273                 // minimum required
274
optimalMinLength = codeLength - CODE_SPLIT_LENGTH;
275             } else {
276                 // try to split as much as possible
277
// need one for the return instruction
278
optimalMinLength = CODE_SPLIT_LENGTH - 1;
279             }
280
281             if (optimalMinLength > lengthToCheck)
282                 optimalMinLength = lengthToCheck;
283
284             if (splittingZeroStack)
285             {
286                 split_pc = myCode.splitZeroStack(this, modClass, split_pc,
287                     optimalMinLength);
288             }
289             else
290             {
291                 // Note the split expression does not re-start split
292
// at point left off by the previous split expression.
293
// This could be done but would require some level
294
// of stack depth history to be kept across calls.
295
split_pc = myCode.splitExpressionOut(this, modClass,
296                         optimalMinLength, maxStack);
297
298              }
299
300             // Negative split point returned means that no split
301
// was possible. Give up on this approach and goto
302
// the next approach.
303
if (split_pc < 0) {
304                 if (!splittingZeroStack)
305                    break;
306                 splittingZeroStack = false;
307                 split_pc = 0;
308             }
309
310             // success, continue on splitting after the call to the
311
// sub-method if the method still execeeds the maximum length.
312
}
313         
314  
315     }
316
317     /*
318      * class interface
319      */

320
321     /**
322      * In their giveCode methods, the parts of the method body will want to get
323      * to the constant pool to add their constants. We really only want them
324      * treating it like a constant pool inclusion mechanism, we could write a
325      * wrapper to limit it to that.
326      */

327     ClassHolder constantPool() {
328         return modClass;
329     }
330
331
332     //
333
// Class implementation
334
//
335

336
337     /**
338      * sets exceptionBytes to the attribute_info needed
339      * for a method's Exceptions attribute.
340      * The ClassUtilities take care of the header 6 bytes for us,
341      * so they are not included here.
342      * See The Java Virtual Machine Specification Section 4.7.5,
343      * Exceptions attribute.
344      */

345     protected void writeExceptions() {
346         if (thrownExceptions == null)
347             return;
348
349         int numExc = thrownExceptions.size();
350
351         // don't write an Exceptions attribute if there are no exceptions.
352
if (numExc != 0) {
353
354             try{
355                 ClassFormatOutput eout = new ClassFormatOutput((numExc * 2) + 2);
356
357                 eout.putU2(numExc); // number_of_exceptions
358

359                 for (int i = 0; i < numExc; i++) {
360                     // put each exception into the constant pool
361
String JavaDoc e = thrownExceptions.elementAt(i).toString();
362                     int ei2 = modClass.addClassReference(e);
363
364                     // add constant pool index to exception attribute_info
365
eout.putU2(ei2);
366                 }
367
368                 myEntry.addAttribute("Exceptions", eout);
369
370             } catch (IOException JavaDoc ioe) {
371             }
372         }
373     }
374
375     /*
376     ** New push compiler api.
377     */

378
379     /**
380      * Array of the current types of the values on the stack.
381      * A type that types up two words on the stack, e.g. double
382      * will only occupy one element in this array.
383      * This array is dynamically re-sized as needed.
384      */

385     private Type[] stackTypes = new Type[8];
386     
387     /**
388      * Points to the next array offset in stackTypes
389      * to be used. Really it's the number of valid entries
390      * in stackTypes.
391      */

392     private int stackTypeOffset;
393
394     /**
395      * Maximum stack depth seen in this method, measured in words.
396      * Corresponds to max_stack in the Code attribute of section 4.7.3
397      * of the vm spec.
398      */

399     int maxStack;
400     
401     /**
402      * Current stack depth in this method, measured in words.
403      */

404     private int stackDepth;
405
406     private void growStack(int size, Type type) {
407         stackDepth += size;
408         if (stackDepth > maxStack)
409             maxStack = stackDepth;
410         
411         if (stackTypeOffset >= stackTypes.length) {
412
413             Type[] newStackTypes = new Type[stackTypes.length + 8];
414             System.arraycopy(stackTypes, 0, newStackTypes, 0, stackTypes.length);
415             stackTypes = newStackTypes;
416         }
417
418         stackTypes[stackTypeOffset++] = type;
419
420         if (SanityManager.DEBUG) {
421
422             int sum = 0;
423             for (int i = 0 ; i < stackTypeOffset; i++) {
424                 sum += stackTypes[i].width();
425             }
426             if (sum != stackDepth) {
427                 SanityManager.THROWASSERT("invalid stack depth " + stackDepth + " calc " + sum);
428             }
429         }
430     }
431
432     private void growStack(Type type) {
433         growStack(type.width(), type);
434     }
435
436     private Type popStack() {
437         stackTypeOffset--;
438         Type topType = stackTypes[stackTypeOffset];
439         stackDepth -= topType.width();
440         return topType;
441
442     }
443     
444     private Type[] copyStack()
445     {
446         Type[] stack = new Type[stackTypeOffset];
447         System.arraycopy(stackTypes, 0, stack, 0, stackTypeOffset);
448         return stack;
449     }
450
451     public void pushThis() {
452         myCode.addInstr(VMOpcode.ALOAD_0);
453         growStack(1, cb.classType);
454     }
455
456     public void push(byte value) {
457         push(value, Type.BYTE);
458     }
459
460     public void push(boolean value) {
461         push(value ? 1 : 0, Type.BOOLEAN);
462     }
463
464     public void push(short value) {
465         push(value, Type.SHORT);
466     }
467
468     public void push(int value) {
469         push(value, Type.INT);
470     }
471
472     public void dup() {
473         Type dup = popStack();
474         myCode.addInstr(dup.width() == 2 ? VMOpcode.DUP2 : VMOpcode.DUP);
475         growStack(dup);
476         growStack(dup);
477
478     }
479
480     public void swap() {
481
482         // have A,B
483
// want B,A
484

485         Type wB = popStack();
486         Type wA = popStack();
487         growStack(wB);
488         growStack(wA);
489
490         if (wB.width() == 1) {
491             // top value is one word
492
if (wA.width() == 1) {
493                 myCode.addInstr(VMOpcode.SWAP);
494                 return;
495             } else {
496                 myCode.addInstr(VMOpcode.DUP_X2);
497                 myCode.addInstr(VMOpcode.POP);
498             }
499         } else {
500             // top value is two words
501
if (wA.width() == 1) {
502                 myCode.addInstr(VMOpcode.DUP2_X1);
503                 myCode.addInstr(VMOpcode.POP2);
504             } else {
505                 myCode.addInstr(VMOpcode.DUP2_X2);
506                 myCode.addInstr(VMOpcode.POP2);
507             }
508         }
509
510         // all except the simple swap push an extra
511
// copy of B which needs to be popped.
512
growStack(wB);
513         popStack();
514
515     }
516
517     /**
518      * Push an integer value. Uses the special integer opcodes
519      * for the constants -1 to 5, BIPUSH for values that fit in
520      * a byte and SIPUSH for values that fit in a short. Otherwise
521      * uses LDC with a constant pool entry.
522      *
523      * @param value Value to be pushed
524      * @param type Final type of the value.
525      */

526     private void push(int value, Type type) {
527
528         CodeChunk chunk = myCode;
529
530         if (value >= -1 && value <= 5)
531             chunk.addInstr((short)(VMOpcode.ICONST_0+value));
532         else if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE)
533             chunk.addInstrU1(VMOpcode.BIPUSH,value);
534         else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE)
535             chunk.addInstrU2(VMOpcode.SIPUSH,value);
536         else {
537             int cpe = modClass.addConstant(value);
538             addInstrCPE(VMOpcode.LDC, cpe);
539         }
540         growStack(type.width(), type);
541         
542     }
543
544     /**
545      * Push a long value onto the stack.
546      * For the values zero and one the LCONST_0 and
547      * LCONST_1 instructions are used.
548      * For values betwee Short.MIN_VALUE and Short.MAX_VALUE
549      * inclusive an byte/short/int value is pushed
550      * using push(int, Type) followed by an I2L instruction.
551      * This saves using a constant pool entry for such values.
552      * All other values use a constant pool entry. For values
553      * in the range of an Integer an integer constant pool
554      * entry is created to allow sharing with integer constants
555      * and to reduce constant pool slot entries.
556      */

557     public void push(long value) {
558         CodeChunk chunk = myCode;
559
560         if (value == 0L || value == 1L) {
561             short opcode = value == 0L ? VMOpcode.LCONST_0 : VMOpcode.LCONST_1;
562             chunk.addInstr(opcode);
563         } else if (value >= Integer.MIN_VALUE && value <= Integer.MAX_VALUE) {
564             // the push(int, Type) method grows the stack for us.
565
push((int) value, Type.LONG);
566             chunk.addInstr(VMOpcode.I2L);
567             return;
568         } else {
569             int cpe = modClass.addConstant(value);
570             chunk.addInstrU2(VMOpcode.LDC2_W, cpe);
571         }
572         growStack(2, Type.LONG);
573     }
574     public void push(float value) {
575
576         CodeChunk chunk = myCode;
577         
578         if (value == 0.0)
579         {
580             chunk.addInstr(VMOpcode.FCONST_0);
581         }
582         else if (value == 1.0)
583         {
584             chunk.addInstr(VMOpcode.FCONST_1);
585         }
586         else if (value == 2.0)
587         {
588             chunk.addInstr(VMOpcode.FCONST_2);
589         }
590         else
591         {
592             int cpe = modClass.addConstant(value);
593             addInstrCPE(VMOpcode.LDC, cpe);
594         }
595         growStack(1, Type.FLOAT);
596     }
597
598     public void push(double value) {
599         CodeChunk chunk = myCode;
600
601         if (value == 0.0) {
602                 chunk.addInstr(VMOpcode.DCONST_0);
603         }
604         else {
605             int cpe = modClass.addConstant(value);
606             chunk.addInstrU2(VMOpcode.LDC2_W, cpe);
607         }
608         growStack(2, Type.DOUBLE);
609     }
610     public void push(String JavaDoc value) {
611         int cpe = modClass.addConstant(value);
612         addInstrCPE(VMOpcode.LDC, cpe);
613         growStack(1, Type.STRING);
614     }
615  
616
617     public void methodReturn() {
618
619         short opcode;
620         if (stackDepth != 0) {
621             Type topType = popStack();
622             opcode = CodeChunk.RETURN_OPCODE[topType.vmType()];
623         } else {
624             opcode = VMOpcode.RETURN;
625         }
626
627         myCode.addInstr(opcode);
628
629         if (SanityManager.DEBUG) {
630             if (stackDepth != 0)
631                 SanityManager.THROWASSERT("items left on stack " + stackDepth);
632         }
633     }
634
635     public Object JavaDoc describeMethod(short opcode, String JavaDoc declaringClass, String JavaDoc methodName, String JavaDoc returnType) {
636
637         Type rt = cb.factory.type(returnType);
638
639         String JavaDoc methodDescriptor = BCMethodDescriptor.get(BCMethodDescriptor.EMPTY, rt.vmName(), cb.factory);
640
641         if ((declaringClass == null) && (opcode != VMOpcode.INVOKESTATIC)) {
642
643             Type dt = stackTypes[stackTypeOffset - 1];
644
645             if (declaringClass == null)
646                 declaringClass = dt.javaName();
647         }
648         
649         int cpi = modClass.addMethodReference(declaringClass, methodName,
650                 methodDescriptor, opcode == VMOpcode.INVOKEINTERFACE);
651
652         return new BCMethodCaller(opcode, rt, cpi);
653     }
654
655     public int callMethod(Object JavaDoc methodDescriptor) {
656
657         // pop the reference off the stack
658
popStack();
659
660         BCMethodCaller mc = (BCMethodCaller) methodDescriptor;
661
662         int cpi = mc.cpi;
663         short opcode = mc.opcode;
664
665         if (opcode == VMOpcode.INVOKEINTERFACE) {
666             myCode.addInstrU2U1U1(opcode, cpi, (short) 1, (short) 0);
667         }
668         else
669             myCode.addInstrU2(opcode, cpi);
670         
671         // this is the return type of the method
672
Type rt = mc.type;
673         int rw = rt.width();
674         if (rw != 0)
675             growStack(rw, rt);
676         else
677         {
678             if (stackDepth == 0)
679                 overflowMethodCheck();
680         }
681         return cpi;
682     }
683
684     public int callMethod(short opcode, String JavaDoc declaringClass, String JavaDoc methodName,
685         String JavaDoc returnType, int numArgs) {
686
687         Type rt = cb.factory.type(returnType);
688
689         int initialStackDepth = stackDepth;
690
691         // get the array of parameter types
692

693         String JavaDoc [] debugParameterTypes = null;
694         String JavaDoc[] vmParameterTypes;
695         if (numArgs == 0) {
696             vmParameterTypes = BCMethodDescriptor.EMPTY;
697         } else {
698             if (SanityManager.DEBUG) {
699                 debugParameterTypes = new String JavaDoc[numArgs];
700             }
701             vmParameterTypes = new String JavaDoc[numArgs];
702             for (int i = numArgs - 1; i >= 0; i--) {
703                 Type at = popStack();
704
705                 vmParameterTypes[i] = at.vmName();
706                 if (SanityManager.DEBUG) {
707                     debugParameterTypes[i] = at.javaName();
708                 }
709             }
710         }
711         
712         String JavaDoc methodDescriptor = BCMethodDescriptor.get(vmParameterTypes, rt.vmName(), cb.factory);
713
714         Type dt = null;
715         if (opcode != VMOpcode.INVOKESTATIC) {
716
717             dt = popStack();
718         }
719         Type dtu = vmNameDeclaringClass(declaringClass);
720         if (dtu != null)
721             dt = dtu;
722         
723         int cpi = modClass.addMethodReference(dt.vmNameSimple, methodName,
724                 methodDescriptor, opcode == VMOpcode.INVOKEINTERFACE);
725
726         if (opcode == VMOpcode.INVOKEINTERFACE) {
727             short callArgCount = (short) (initialStackDepth - stackDepth);
728             myCode.addInstrU2U1U1(opcode, cpi, callArgCount, (short) 0);
729         }
730         else
731             myCode.addInstrU2(opcode, cpi);
732         
733         // this is the return type of the method
734
int rw = rt.width();
735         if (rw != 0)
736             growStack(rw, rt);
737         else
738         {
739             if (stackDepth == 0)
740                 overflowMethodCheck();
741         }
742         // Check the declared type of the method
743
if (SanityManager.DEBUG) {
744
745             d_BCValidate.checkMethod(opcode, dt, methodName, debugParameterTypes, rt);
746         }
747
748         return cpi;
749     }
750
751     private Type vmNameDeclaringClass(String JavaDoc declaringClass) {
752         if (declaringClass == null)
753             return null;
754         return cb.factory.type(declaringClass);
755     }
756
757     public void callSuper() {
758
759         pushThis();
760         callMethod(VMOpcode.INVOKESPECIAL, cb.getSuperClassName(), "<init>", "void", 0);
761     }
762
763     public void pushNewStart(String JavaDoc className) {
764
765         int cpi = modClass.addClassReference(className);
766
767         // Use U2, not CPE, since only wide form exists.
768
myCode.addInstrU2(VMOpcode.NEW, cpi);
769         myCode.addInstr(VMOpcode.DUP);
770
771         // Grow the stack twice as we are pushing
772
// two instances of newly created reference
773
Type nt = cb.factory.type(className);
774         growStack(1, nt);
775         growStack(1, nt);
776     }
777
778     public void pushNewComplete(int numArgs) {
779         callMethod(VMOpcode.INVOKESPECIAL, (String JavaDoc) null, "<init>", "void", numArgs);
780     }
781
782     public void upCast(String JavaDoc className) {
783         Type uct = cb.factory.type(className);
784
785         stackTypes[stackTypeOffset - 1] = uct;
786         //popStack();
787
//growStack(1, uct);
788
}
789
790     public void cast(String JavaDoc className) {
791         
792         // Perform a simple optimization to not
793
// insert a checkcast when the classname
794
// of the cast exactly matches the type name
795
// currently on the stack.
796
// This can reduce the amount of generated code.
797
// This compiler/class generator does not load
798
// classes to check relationships or any other
799
// information. Thus other optimizations where a cast
800
// is not required are not implemented.
801
Type tbc = stackTypes[stackTypeOffset - 1];
802         
803         short sourceType = tbc.vmType();
804         
805         if (sourceType == BCExpr.vm_reference)
806         {
807             // Simple optimize step
808
if (className.equals(tbc.javaName()))
809             {
810                 // do nothing, exact matching type
811
return;
812             }
813         }
814         
815         Type ct = cb.factory.type(className);
816         popStack();
817         
818         short targetType = ct.vmType();
819
820         if (SanityManager.DEBUG) {
821
822             if (!((sourceType == BCExpr.vm_reference &&
823                 targetType == BCExpr.vm_reference) ||
824                 (sourceType != BCExpr.vm_reference &&
825                 targetType != BCExpr.vm_reference))) {
826                 SanityManager.THROWASSERT("Both or neither must be object types " + ct.javaName() + " " + tbc.javaName());
827             }
828         }
829
830         // if it is an object type, do a checkcast on it.
831
if (sourceType == BCExpr.vm_reference) {
832
833             int cpi = modClass.addClassReference(ct.vmNameSimple);
834             myCode.addInstrU2(VMOpcode.CHECKCAST, cpi);
835         }
836         // otherwise, try to convert it.
837
else {
838             short opcode = VMOpcode.NOP;
839
840             // we use the conversionInfo array
841
// to determine how to convert; if
842
// the result type of the conversion
843
// is not our target type, we are not done
844
// yet. Make sure there are no
845
// infinite loop possibilities in the
846
// conversionInfo array!
847
while (sourceType!=targetType && opcode!=VMOpcode.BAD) {
848                 short[] currentConversion =
849                     CodeChunk.CAST_CONVERSION_INFO[sourceType][targetType];
850                 sourceType = currentConversion[1];
851                 opcode = currentConversion[0];
852                 if (opcode != VMOpcode.NOP) {
853                     myCode.addInstr(opcode);
854                 }
855             }
856             if (SanityManager.DEBUG) {
857                 SanityManager.ASSERT(opcode != VMOpcode.BAD,
858                     "BAD VMOpcode not expected in cast");
859             }
860         }
861         growStack(ct);
862     }
863
864     public void isInstanceOf(String JavaDoc className) {
865         int cpi = modClass.addClassReference(className);
866         myCode.addInstrU2(VMOpcode.INSTANCEOF, cpi);
867         popStack();
868         growStack(1, Type.BOOLEAN);
869     }
870
871     public void pushNull(String JavaDoc type) {
872         myCode.addInstr(VMOpcode.ACONST_NULL);
873         growStack(1, cb.factory.type(type));
874     }
875
876
877     public void getField(LocalField field) {
878
879         BCLocalField lf = (BCLocalField) field;
880         Type lt = lf.type;
881
882         pushThis();
883         myCode.addInstrU2(VMOpcode.GETFIELD, lf.cpi);
884
885         popStack();
886         growStack(lt);
887
888     }
889
890     public void getField(String JavaDoc declaringClass, String JavaDoc fieldName, String JavaDoc fieldType) {
891         Type dt = popStack();
892
893         Type dtu = vmNameDeclaringClass(declaringClass);
894         if (dtu != null)
895             dt = dtu;
896
897         getField(VMOpcode.GETFIELD, dt.vmNameSimple, fieldName, fieldType);
898     }
899     /**
900         Push the contents of the described static field onto the stack.
901     */

902     public void getStaticField(String JavaDoc declaringClass, String JavaDoc fieldName, String JavaDoc fieldType) {
903         getField(VMOpcode.GETSTATIC, declaringClass, fieldName, fieldType);
904     }
905
906     private void getField(short opcode, String JavaDoc declaringClass, String JavaDoc fieldName, String JavaDoc fieldType) {
907
908         Type ft = cb.factory.type(fieldType);
909         int cpi = modClass.addFieldReference(vmNameDeclaringClass(declaringClass).vmNameSimple, fieldName, ft.vmName());
910         myCode.addInstrU2(opcode, cpi);
911
912         growStack(ft);
913     }
914     
915     /**
916      * Set the field but don't duplicate its value so
917      * nothing is left on the stack after this call.
918      */

919     public void setField(LocalField field) {
920         BCLocalField lf = (BCLocalField) field;
921         Type lt = lf.type;
922
923         putField(lf.type, lf.cpi, false);
924
925         if (stackDepth == 0)
926             overflowMethodCheck();
927     }
928
929     /**
930         Upon entry the top word(s) on the stack is
931         the value to be put into the field. Ie.
932         we have
933         <PRE>
934         word
935         </PRE>
936
937         Before the call we need
938         <PRE>
939         word
940         this
941         word
942         </PRE>
943         word2,word1 -> word2, word1, word2
944
945         So that we are left with word after the put.
946
947     */

948     public void putField(LocalField field) {
949         BCLocalField lf = (BCLocalField) field;
950         Type lt = lf.type;
951
952         putField(lf.type, lf.cpi, true);
953     }
954
955     /**
956         Pop the top stack value and store it in the instance field of this class.
957     */

958     public void putField(String JavaDoc fieldName, String JavaDoc fieldType) {
959
960         Type ft = cb.factory.type(fieldType);
961         int cpi = modClass.addFieldReference(cb.classType.vmNameSimple, fieldName, ft.vmName());
962
963         putField(ft, cpi, true);
964     }
965
966     private void putField(Type fieldType, int cpi, boolean dup) {
967
968         // now have ...,value
969
if (dup)
970         {
971             myCode.addInstr(fieldType.width() == 2 ? VMOpcode.DUP2 : VMOpcode.DUP);
972             growStack(fieldType);
973         }
974         // now have
975
// dup true: ...,value,value
976
// dup false: ...,value,
977

978         pushThis();
979         // now have
980
// dup true: ...,value,value,this
981
// dup false: ...,value,this
982

983         swap();
984         // now have
985
// dup true: ...,value,this,value
986
// dup false: ...,this,value
987

988         myCode.addInstrU2(VMOpcode.PUTFIELD, cpi);
989         popStack(); // the value
990
popStack(); // this
991

992         // now have
993
// dup true: ...,value
994
// dup false: ...
995
}
996     /**
997         Pop the top stack value and store it in the field.
998         This call requires the instance to be pushed by the caller.
999     */

1000    public void putField(String JavaDoc declaringClass, String JavaDoc fieldName, String JavaDoc fieldType) {
1001        Type vt = popStack();
1002        Type dt = popStack();
1003
1004        if (SanityManager.DEBUG) {
1005            if (dt.width() != 1)
1006                SanityManager.THROWASSERT("reference expected for field access - is " + dt.javaName());
1007        }
1008
1009        // have objectref,value
1010
// need value,objectref,value
1011

1012        myCode.addInstr(vt.width() == 2 ? VMOpcode.DUP2_X1 : VMOpcode.DUP_X1);
1013        growStack(vt);
1014        growStack(dt);
1015        growStack(vt);
1016
1017        Type dtu = vmNameDeclaringClass(declaringClass);
1018        if (dtu != null)
1019            dt = dtu;
1020
1021        Type ft = cb.factory.type(fieldType);
1022        int cpi = modClass.addFieldReference(dt.vmNameSimple, fieldName, ft.vmName());
1023        myCode.addInstrU2(VMOpcode.PUTFIELD, cpi);
1024
1025        popStack(); // value
1026
popStack(); // reference
1027
}
1028
1029    public void conditionalIfNull() {
1030
1031        conditionalIf(VMOpcode.IFNONNULL);
1032    }
1033
1034    public void conditionalIf() {
1035        conditionalIf(VMOpcode.IFEQ);
1036    }
1037
1038    private Conditional condition;
1039
1040    private void conditionalIf(short opcode) {
1041        popStack();
1042        
1043        // Save the stack upon entry to the 'then' block of the
1044
// 'if' so that we can set up the 'else' block with the
1045
// correct stack on entry.
1046

1047        condition = new Conditional(condition, myCode, opcode, copyStack());
1048    }
1049
1050    public void startElseCode() {
1051        
1052        // start the else code
1053
Type[] entryStack = condition.startElse(this, myCode, copyStack());
1054        
1055        for (int i = stackDepth = 0; i < entryStack.length; i++)
1056        {
1057            stackDepth += (stackTypes[i] = entryStack[i]).width();
1058        }
1059        this.stackTypeOffset = entryStack.length;
1060
1061    }
1062    public void completeConditional() {
1063        condition = condition.end(this, myCode, stackTypes, stackTypeOffset);
1064    }
1065    
1066    public void pop() {
1067        if (SanityManager.DEBUG) {
1068            if (stackDepth == 0)
1069                SanityManager.THROWASSERT("pop when stack is empty!");
1070        }
1071        Type toPop = popStack();
1072
1073        myCode.addInstr(toPop.width() == 2 ? VMOpcode.POP2 : VMOpcode.POP);
1074        
1075        if (stackDepth == 0)
1076            overflowMethodCheck();
1077    }
1078
1079    public void endStatement() {
1080        if (stackDepth != 0) {
1081            pop();
1082        }
1083
1084        //if (SanityManager.DEBUG) {
1085
// if (stackDepth != 0)
1086
// SanityManager.THROWASSERT("items left on stack " + stackDepth);
1087
// }
1088
}
1089
1090    /**
1091    */

1092    public void getArrayElement(int element) {
1093
1094        push(element);
1095        popStack(); // int just pushed will be popped by array access
1096

1097        Type arrayType = popStack();
1098
1099        String JavaDoc arrayJava = arrayType.javaName();
1100        String JavaDoc componentString = arrayJava.substring(0,arrayJava.length()-2);
1101
1102        Type componentType = cb.factory.type(componentString);
1103
1104        short typ = componentType.vmType();
1105
1106        // boolean has a type id of integer, here it needs to be byte.
1107
if ((typ == BCExpr.vm_int) && (componentType.vmName().equals("Z")))
1108            typ = BCExpr.vm_byte;
1109        myCode.addInstr(CodeChunk.ARRAY_ACCESS[typ]);
1110
1111        growStack(componentType);
1112
1113    }
1114    // come in with ref, value
1115

1116    public void setArrayElement(int element) {
1117
1118        // ref, value
1119

1120        push(element);
1121
1122        // ref, value, index
1123
swap();
1124        
1125        Type componentType = popStack(); // value
1126
popStack(); // int just pushed will be popped by array access
1127

1128        popStack(); // array ref.
1129

1130        short typ = componentType.vmType();
1131
1132        // boolean has a type id of integer, here it needs to be byte.
1133
if ((typ == BCExpr.vm_int) && (componentType.vmName().equals("Z")))
1134            typ = BCExpr.vm_byte;
1135
1136        myCode.addInstr(CodeChunk.ARRAY_STORE[typ]);
1137    }
1138    /**
1139        this array maps the BCExpr vm_* constants 0..6 to
1140        the expected VM type constants for the newarray instruction.
1141        <p>
1142        Because boolean was mapped to integer for general instructions,
1143        it will have to be specially matched and mapped to its value
1144        directly (4).
1145     */

1146    private static final byte newArrayElementTypeMap[] = { 8, 9, 10, 11, 6, 7, 5 };
1147    static final byte T_BOOLEAN = 4;
1148    /**
1149        Create an array instance
1150
1151        Stack ... =>
1152              ...,arrayref
1153    */

1154    public void pushNewArray(String JavaDoc className, int size) {
1155
1156        push(size);
1157        popStack(); // int just pushed will be popped by array creation
1158

1159        Type elementType = cb.factory.type(className);
1160
1161        // determine the instruction to use based on the element type
1162
if (elementType.vmType() == BCExpr.vm_reference) {
1163
1164            // For an array of Java class/interface elements, generate:
1165
// ANEWARRAY #cpei ; where cpei is a constant pool index for the class
1166

1167            int cpi = modClass.addClassReference(elementType.javaName());
1168            // Use U2, not CPE, since only wide form exists.
1169
myCode.addInstrU2(VMOpcode.ANEWARRAY, cpi);
1170        } else {
1171            byte atype;
1172
1173            // get the argument for the array type
1174
// if the element type is boolean, we can't use the map
1175
// because the type id will say integer.
1176
// but we can use vm_int test to weed out some tests
1177
if (elementType.vmType() == BCExpr.vm_int &&
1178                VMDescriptor.C_BOOLEAN == elementType.vmName().charAt(0))
1179                atype = T_BOOLEAN;
1180            else
1181                atype = newArrayElementTypeMap[elementType.vmType()];
1182
1183            // For an array of Java builtin type elements, generate:
1184
// NEWARRAY #atype ; where atype is a constant for the builtin type
1185

1186            myCode.addInstrU1(VMOpcode.NEWARRAY, atype);
1187        }
1188
1189        // an array reference is an object, hence width of 1
1190
growStack(1, cb.factory.type(className.concat("[]")));
1191    }
1192    
1193    /**
1194     * Write a instruction that uses a constant pool entry
1195     * as an operand, add a limit exceeded message if
1196     * the number of constant pool entries has exceeded
1197     * the limit.
1198     */

1199    private void addInstrCPE(short opcode, int cpe)
1200    {
1201        if (cpe >= VMOpcode.MAX_CONSTANT_POOL_ENTRIES)
1202            cb.addLimitExceeded(this, "constant_pool_count",
1203                    VMOpcode.MAX_CONSTANT_POOL_ENTRIES, cpe);
1204        
1205        myCode.addInstrCPE(opcode, cpe);
1206    }
1207
1208    /**
1209        Tell if statement number in this method builder hits limit. This
1210        method builder keeps a counter of how many statements are added to it.
1211        Caller should call this function every time it tries to add a statement
1212        to this method builder (counter is increased by 1), then the function
1213        returns whether the accumulated statement number hits a limit.
1214        The reason of doing this is that Java compiler has a limit of 64K code
1215        size for each method. We might hit this limit if an extremely long
1216        insert statement is issued, for example (see beetle 4293). Counting
1217        statement number is an approximation without too much overhead.
1218    */

1219    public boolean statementNumHitLimit(int noStatementsAdded)
1220    {
1221        if (statementNum > 2048) // 2K limit
1222
{
1223            return true;
1224        }
1225        else
1226        {
1227            statementNum = statementNum + noStatementsAdded;
1228            return false;
1229        }
1230    }
1231    
1232    /**
1233     * Check to see if the current method byte code is nearing the
1234     * limit of 65535. If it is start overflowing to a new method.
1235     * <P>
1236     * Overflow is handled for a method named e23 as:
1237     * <CODE>
1238     public Object e23()
1239     {
1240       ... existing code
1241       // split point
1242       return e23_0();
1243     }
1244     private Object e23_0()
1245     {
1246        ... first set overflowed code
1247        // split point
1248        return e23_1();
1249     }
1250     private Object e23_1()
1251     {
1252        ... second set overflowed code
1253        // method complete
1254        return result;
1255     }
1256         </CODE>
1257     <P>
1258     
1259     These overflow methods are hidden from the code using this MethodBuilder,
1260     it continues to think that it is building a single method with the
1261     original name.
1262
1263
1264     * <BR> Restrictions:
1265     * <UL>
1266     * <LI> Only handles methods with no arguments
1267     * <LI> Stack depth must be zero
1268     * </UL>
1269     *
1270     *
1271     */

1272    private void overflowMethodCheck()
1273    {
1274        if (handlingOverflow)
1275            return;
1276        
1277        // don't sub method in the middle of a conditional
1278
if (condition != null)
1279            return;
1280        
1281        int currentCodeSize = myCode.getPC();
1282        
1283        // Overflow at >= 55,000 bytes which is someway
1284
// below the limit of 65,535. Ideally overflow
1285
// would occur at 65535 minus the few bytes needed
1286
// to call the sub-method, but the issue is at this level
1287
// we don't know frequently we are called given the restriction
1288
// of only being called when the stack depth is zero.
1289
// Thus split earlier to try ensure most cases are caught.
1290
// Only downside is that we may split into N methods when N-1 would suffice.
1291
if (currentCodeSize < 55000)
1292            return;
1293                
1294        // only handle no-arg methods at the moment.
1295
if (parameters != null)
1296        {
1297            if (parameters.length != 0)
1298                return;
1299        }
1300                
1301        BCMethod subMethod = getNewSubMethod(myReturnType, false);
1302                
1303        // stop any recursion
1304
handlingOverflow = true;
1305        
1306        // in this method make a call to the sub method we will
1307
// be transferring control to.
1308
callSubMethod(subMethod);
1309    
1310        // and return its value, works just as well for a void method!
1311
this.methodReturn();
1312        this.complete();
1313        
1314        handlingOverflow = false;
1315        
1316        // now the tricky bit, make this object take over the
1317
// code etc. from the sub method. This is done so
1318
// that any code that has a reference to this MethodBuilder
1319
// will continue to work. They will be writing code into the
1320
// new sub method.
1321

1322        this.myEntry = subMethod.myEntry;
1323        this.myCode = subMethod.myCode;
1324        this.currentVarNum = subMethod.currentVarNum;
1325        this.statementNum = subMethod.statementNum;
1326        
1327        // copy stack info
1328
this.stackTypes = subMethod.stackTypes;
1329        this.stackTypeOffset = subMethod.stackTypeOffset;
1330        this.maxStack = subMethod.maxStack;
1331        this.stackDepth = subMethod.stackDepth;
1332    }
1333    
1334    /**
1335     * Create a sub-method from this method to allow the code builder to split a
1336     * single logical method into multiple methods to avoid the 64k per-method
1337     * code size limit. The sub method with inherit the thrown exceptions of
1338     * this method.
1339     *
1340     * @param returnType
1341     * Return type of the new method
1342     * @param withParameters
1343     * True to define the method with matching parameters false to
1344     * define it with no parameters.
1345     * @return A valid empty sub method.
1346     */

1347    final BCMethod getNewSubMethod(String JavaDoc returnType, boolean withParameters) {
1348        int modifiers = myEntry.getModifier();
1349
1350        // the sub-method can be private to ensure that no-one
1351
// can call it accidentally from outside the class.
1352
modifiers &= ~(Modifier.PROTECTED | Modifier.PUBLIC);
1353        modifiers |= Modifier.PRIVATE;
1354
1355        String JavaDoc subMethodName = myName + "_s"
1356                + Integer.toString(subMethodCount++);
1357        BCMethod subMethod = (BCMethod) cb.newMethodBuilder(modifiers,
1358                returnType, subMethodName, withParameters ? parameterTypes
1359                        : null);
1360        subMethod.thrownExceptions = this.thrownExceptions;
1361        
1362        return subMethod;
1363    }
1364
1365    /**
1366     * Call a sub-method created by getNewSubMethod handling parameters
1367     * correctly.
1368     */

1369    final void callSubMethod(BCMethod subMethod) {
1370        // in this method make a call to the sub method we will
1371
// be transferring control to.
1372
short op;
1373        if ((myEntry.getModifier() & Modifier.STATIC) == 0) {
1374            op = VMOpcode.INVOKEVIRTUAL;
1375            this.pushThis();
1376        } else {
1377            op = VMOpcode.INVOKESTATIC;
1378        }
1379
1380        int parameterCount = subMethod.parameters == null ? 0
1381                : subMethod.parameters.length;
1382
1383        // push my parameter values for the call.
1384
for (int pi = 0; pi < parameterCount; pi++)
1385            this.getParameter(pi);
1386
1387        this.callMethod(op, modClass.getName(), subMethod.getName(),
1388                subMethod.myReturnType, parameterCount);
1389    }
1390}
1391
1392
Popular Tags