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 [Conditional.java]

nameclass, %method, %block, %line, %
Conditional.java100% (1/1)100% (4/4)78%  (80/103)92%  (22/24)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class Conditional100% (1/1)100% (4/4)78%  (80/103)92%  (22/24)
Conditional (Conditional, CodeChunk, short, int): void 100% (1/1)100% (17/17)100% (6/6)
end (CodeChunk, int): Conditional 100% (1/1)39%  (15/38)67%  (4/6)
fillIn (CodeChunk, int): void 100% (1/1)100% (27/27)100% (6/6)
startElse (CodeChunk, int): int 100% (1/1)100% (21/21)100% (6/6)

1/*
2 
3   Derby - Class org.apache.derby.impl.services.bytecode.Conditional
4 
5   Copyright 2000, 2006 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;
22import org.apache.derby.iapi.services.classfile.VMOpcode;
23import org.apache.derby.iapi.services.sanity.SanityManager;
24 
25/**
26        A Conditional represents an if/then/else block.
27        When this is created the code  will already have
28        the conditional check code. The code is optimized for branch
29        offsets that fit in 2 bytes, though will handle 4 byte offsets.
30<code>
31     if condition
32         then code
33         else code
34</code>
35     what actually gets built is
36<code>
37     if !condition branch to eb:
38          then code
39          goto end:  // skip else
40         eb:
41          else code
42         end:
43</code>
44 
45    If no else condition was provided then the code is:
46    
47<code>
48     if !condition branch to end:
49          then code
50         end:
51</code>
52 
53Note all branches here are using relative offsets, not absolute program counters.
54 
55If the then code leads to the conditional branch offset being too big (>32k)
56because the then code is larger than 32767 bytes then this is built:
57<code>
58     // when else code is present
59     if condition branch to tb: (relative offset +8)
60     goto_w eb: // indirect for else block (5 bytes)
61     tb:
62            then code (> 32767 bytes)
63            goto end:
64         eb:
65          else code
66         end:
67</code>
68 
69<code>
70     // when only then code is present
71     if condition branch to tb: (relative offset +8)
72     goto_w end: // indirect for else block (5 bytes)
73     tb:
74            then code (> 32767 bytes)
75         end:
76</code>
77 
78If there is an else branch and only it is larger than 32767 bytes then
79the code is:
80 
81<code>
82     if !condition branch to eb: (offset increased by two over previous value)
83          then code
84          goto_w end:  // skip else
85         eb:
86          else code (> 32767 bytes)
87         end:
88</code>
89 
90This has one special case where the size of conditional branch to eb:
91now must change from a 16bit value to a 32 bit value. The generated code
92for this is the same as when both the then code and the else code require
9332bit offsets for the branches. This code is:
94 
95<code>
96     if condition branch to tb: (relative offset +8)
97     goto_w eb: // indirect for else block (5 bytes)
98     tb:
99            then code (> 32767 bytes)
100            goto_w end:
101         eb:
102          else code (> 32767 bytes)
103         end:
104</code>
105 
106In theory, at the moment this should not happen as this would mean a total
107code size that exceeds the limit on the code size for a method (64k). This
108code handles this case as it does occur if the limit for a branch is lowered
109for testing purposes, to ensure the complete set of branch re-write code works.
110This lowering of the limit can be done by changing the constant BRANCH16LIMIT.
111  
112*/
113class Conditional {
114        
115        /**
116         * Limit of a 16 bit branch.
117         * <P>
118         * If broad testing of the switch from 16bit to 32bit
119         * offsets is required then this constant can be reduced
120         * to a lower value, say 50 and run complete tests. This
121         * will cover all the combinations. This works because the
122         * GOTO_W instruction works with any offset value.
123         */
124        private static final int BRANCH16LIMIT = 32767;
125 
126 
127        private final Conditional parent;
128        /**
129         * pc of the 'if' opcode.
130         */
131        private final int   if_pc;
132        
133        private Type[]        stack;
134        
135        /**
136         * pc of the GOTO added at the end of the then block
137         * to transfer control to the end of this conditional.
138         * That is at the end of the else block.
139         */
140        private int thenGoto_pc;
141 
142        /**
143         * Start a conditional block.
144         * @param parent Current conditional block, null if no nesting is going on.
145         * @param chunk CodeChunk this conditional lives in
146         * @param ifOpcode Opcode for the if check.
147         * @param entryStack Type stack on entering the conditional then block.
148         */
149        Conditional(Conditional parent, CodeChunk chunk, short ifOpcode, Type[] entryStack) {
150                this.parent = parent;
151                if_pc = chunk.getPC();
152                this.stack = entryStack;
153                
154                // reserve the space for the branch, will overwrite later
155                // with the correct branch offset.
156                chunk.addInstrU2(ifOpcode, 0);
157        }
158 
159        /**
160         * Complete the 'then' block and start the 'else' block for this conditional
161         * @param chunk CodeChunk this conditional lives in
162         * @param thenStack Type stack on completing the conditional then block.
163         * @return the type stack on entering the then block
164         */
165        Type[] startElse(BCMethod mb, CodeChunk chunk, Type[] thenStack) {
166                
167                // reserve space for the goto end we will be adding
168                chunk.addInstrU2(VMOpcode.GOTO, 0);
169 
170                // fill in the branch opcode to branch to
171                // the code after the goto, which is the current pc.
172                fillIn(mb, chunk, if_pc, chunk.getPC());
173                
174                // Cannot use the pc before adding the GOTO above
175                // as the fillIn may insert bytes that move the GOTO,
176                // thus calculate at the end, and subtract the number of
177                // instructions in a goto to get its pc.
178                thenGoto_pc = chunk.getPC() - 3;
179                
180                Type[] entryStack = stack;
181                stack = thenStack;
182                
183                return entryStack;
184        }
185 
186 
187        /**
188         * Complete the conditional and patch up any jump instructions.
189         * @param chunk CodeChunk this conditional lives in
190         * @param elseStack Current stack, which is the stack at the end of the else
191         * @param stackNumber Current number of valid elements in elseStack
192         * @return The conditional this conditional was nested in, if any.
193         */
194        Conditional end(BCMethod mb, CodeChunk chunk, Type[] elseStack, int stackNumber) {
195                int branch_pc;
196                if (thenGoto_pc == 0) {
197                        // no else condition, make the conditional branch to the end
198                        branch_pc = if_pc;
199                } else {
200                        // otherwise make the goto branch to the end
201                        branch_pc = thenGoto_pc;
202                }
203                
204                fillIn(mb, chunk, branch_pc, chunk.getPC());
205                
206                if (SanityManager.DEBUG)
207                {
208                        if (stackNumber != stack.length)
209                                SanityManager.THROWASSERT("ByteCode Conditional then/else stack depths differ then:"
210                                                + stack.length + " else: " + stackNumber);
211                        
212                        for (int i = 0; i < stackNumber; i++)
213                        {
214                                if (!stack[i].vmName().equals(elseStack[i].vmName()))
215                                        SanityManager.THROWASSERT("ByteCode Conditional then/else stack mismatch: then: "
216                                                        + stack[i].vmName() + 
217                                                        " else: " + elseStack[i].vmName());
218                        }
219                }
220                
221                return parent;
222        }
223 
224        /**
225         * Fill in the offsets for a conditional or goto instruction that
226         * were dummied up as zero during code generation. Handles modifying
227         * branch logic when the offset for the branch is greater than can
228         * fit in 16 bits. In this case a GOTO_W with a 32 bit offset will
229         * be used, see details within the method for how this is acheived
230         * in all situations. This method might insert instructions in the
231         * already generated byte code, thus increasing the program counter.
232         * 
233         * @param mb Method this conditional is for
234         * @param chunk Our code chunk
235         * @param branch_pc pc of the branch or goto opcode in the code stream
236         * @param target_pc pc where we want to jump to.
237         */
238        private void fillIn(BCMethod mb, CodeChunk chunk,
239                        int branch_pc, int target_pc) {
240 
241                int offset = target_pc - branch_pc;
242 
243                // Following code assumes that this class only
244                // generates forward jumps. Jump of zero is
245                // wrong as well, would be infinite loop or stack problems.
246                if (SanityManager.DEBUG)
247                {
248                        if (offset <= 0)
249                                SanityManager.THROWASSERT("Conditional branch zero or negative " + offset);
250                }
251 
252                // Original opcode written.
253                short branchOpcode = chunk.getOpcode(branch_pc);
254                
255                // Handle 16bit offsets, two byte.
256                if (offset <= BRANCH16LIMIT)
257                {
258                        // Code was already setup for two byte offsets,
259                        // branch or goto instruction was written with
260                        // offset zero, ready to be overwritten by this code.
261                        CodeChunk mod = chunk.insertCodeSpace(branch_pc, 0);
262                        mod.addInstrU2(branchOpcode, offset);
263                        return;
264                }
265                
266                if (branchOpcode == VMOpcode.GOTO)
267                {
268        
269            // The goto could be beyond the code length
270            // supported by the virtual machine: VMOpcode.MAX_CODE_LENGTH
271            // We allow this because later splits may bring the goto
272            // offset to within the required limits. If the goto
273            // still points outside the limits of the JVM then
274            // building the class will fail anyway since the code
275            // size will be too large. So no need to flag an error here.
276                    
277                        // Change the GOTO to a GOTO_W, which means
278                        // inserting 2 bytes into the stream.
279                        CodeChunk mod = chunk.insertCodeSpace(branch_pc, 2);
280                        
281                        // Offset we are jumping to is now two bytes futher away
282                        offset += 2;
283                        
284                        // replace the original GOTO with a GOTO_W
285                        mod.addInstrU4(VMOpcode.GOTO_W, offset);
286                        
287                        // Now need to patch up the original conditional
288                        // as the else code it was branching to is now
289                        // another two bytes away.
290                        // There are three cases, given the original branch_offset:
291                        //
292                        // 1) branch_offset 16bit, branch_offset+2 16 bit
293                        // 2) branch_offset 16bit, branch_offset+2 32 bit
294                        // 3) branch_offset 32bit, branch_offset+2 32 bit
295                        //
296                        int startElse_pc = mod.getPC();
297                        
298                        int branchOffset = startElse_pc - if_pc;
299                                                        
300                        if (branchOffset <= BRANCH16LIMIT + 2)
301                        {                                        
302                                // case 1) branch_offset 16bit, branch_offset+2 16 bit
303                // case 2) branch_offset 16bit, branch_offset+2 32 bit
304                                //
305                                // Branch to the else code is on the original conditional
306                                
307                                // both handled by the standard fillIn method.
308                                fillIn(mb, chunk, if_pc, mod.getPC());
309                                return;
310 
311                        }
312 
313                        // branch to the else code was changed from the conditional
314                        // to a GOTO_W as the branch was out of the range of the
315                        // conditional.
316                        
317                        // Overwrite the offset of the existing GOTO_W, the instruction
318                        // after the conditional instruction, which is three bytes long
319                        mod = chunk.insertCodeSpace(if_pc + 3, 0);
320                        
321                        // Above branchOffset was calculated from the conditional
322                        // but we need to branch from the GOTO_W that was inserted
323                        // which is three bytes after the conditional.
324                        branchOffset -= 3;
325                        
326                        mod.addInstrU4(VMOpcode.GOTO_W, branchOffset);
327                        return;
328 
329                }
330                else
331                {
332                        // Ensure the pc we are jumping to (the current pc)
333                        // is within bounds of a valid method *after*
334                        // we have added the extra bytes.
335                        if ((target_pc + 5) >= VMOpcode.MAX_CODE_LENGTH)
336                        {
337                            mb.cb.addLimitExceeded(mb,
338                                            "branch_target", VMOpcode.MAX_CODE_LENGTH, target_pc + 5);
339                                // even if we fail continue to generate the correct code
340                                // so that the assumptions in the patch up code are not broken.
341                        }
342 
343                        // Conditional branch
344                        // branch on the conditional, need to add
345                        // indirection. Basically changing
346                        // (actual conditional might be different)
347                        // Note branch inverting.
348                        //
349                        // IFNONNULL branch offset (to else code)
350                        //   <then code>
351                        // GOTO end:
352                        //   <else code>
353                        // end:
354                        // to
355                        //
356                        // IFNULL branch +8  (to then code, 3 bytes in stream)
357                        // GOTO_W offset* (to else code, 5 new bytes in stream)
358                        //    <then code>
359                        // GOTO end:
360                        //    <else code>
361                                        
362                        // Invert branch.
363                        switch (branchOpcode)
364                        {
365                        case VMOpcode.IFNONNULL:
366                                branchOpcode = VMOpcode.IFNULL;
367                                break;
368                        case VMOpcode.IFEQ:
369                                branchOpcode = VMOpcode.IFNE;
370                                break;
371                        default:
372                                if (SanityManager.DEBUG)
373                                        SanityManager.THROWASSERT("Conditional does not handle opcode " + branchOpcode);
374                                
375                        }
376                                                                                                
377                        // Thus we need to insert 5 bytes
378                        //
379                        CodeChunk mod = chunk.insertCodeSpace(branch_pc, 5);
380                        
381                        // mod is positioned at the current branch.
382                        mod.addInstrU2(branchOpcode, 8);
383                                                                                        
384                        // Indirect goto for the conditional else block or end.
385                        // Offset was from the comparision instruction to the
386                        // start of the real code. Now the branch location
387                        // is an additional two bytes away, because this
388                        // GOTO_W instruction occupies 5 bytes, and the original
389                        // branch 3.
390                        offset += 2;
391                        
392                        mod.addInstrU4(VMOpcode.GOTO_W, offset);
393        
394                        return;        
395                }
396        }
397 
398 
399}

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