]> gcc.gnu.org Git - gcc.git/blob - gcc/java/verify.c
[gcc.git] / gcc / java / verify.c
1 /* Handle verification of bytecoded methods for the GNU compiler for
2 the Java(TM) language.
3 Copyright (C) 1997 Free Software Foundation, Inc.
4
5 This file is part of GNU CC.
6
7 GNU CC is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2, or (at your option)
10 any later version.
11
12 GNU CC is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GNU CC; see the file COPYING. If not, write to
19 the Free Software Foundation, 59 Temple Place - Suite 330,
20 Boston, MA 02111-1307, USA.
21
22 Java and all Java-based marks are trademarks or registered trademarks
23 of Sun Microsystems, Inc. in the United States and other countries.
24 The Free Software Foundation is independent of Sun Microsystems, Inc. */
25
26 #include "config.h"
27 #include "system.h"
28 #include "tree.h"
29 #include "java-tree.h"
30 #include "javaop.h"
31 #include "java-opcodes.h"
32 #include "jcf.h"
33 #include "java-except.h"
34 #include "toplev.h"
35
36 extern int stack_pointer;
37
38 /* During verification, start of the current subroutine (jsr target). */
39 tree current_subr;
40
41 /* A list of pending blocks, chained using LABEL_PENDING_CHAIN.
42 A pending block is one that has LABEL_CHANGED set, which means
43 it requires (re-) verification. */
44 tree pending_blocks;
45
46 /* Append TARGET_LABEL to the pending_block stack unless already in it. */
47
48 void
49 push_pending_label (target_label)
50 tree target_label;
51 {
52 if (! LABEL_CHANGED (target_label))
53 {
54 LABEL_PENDING_CHAIN (target_label) = pending_blocks;
55 pending_blocks = target_label;
56 LABEL_CHANGED (target_label) = 1;
57 }
58 }
59
60 /* Note that TARGET_LABEL is a possible successor instruction.
61 Merge the type state etc.
62 Return NULL on sucess, or an error message on failure. */
63
64 static char *
65 check_pending_block (target_label)
66 tree target_label;
67 {
68 int changed = merge_type_state (target_label);
69
70 if (changed)
71 {
72 if (changed < 0)
73 return "types could not be merged";
74 push_pending_label (target_label);
75 }
76
77 if (current_subr == NULL)
78 {
79 if (LABEL_IN_SUBR (target_label))
80 return "might transfer control into subroutine";
81 }
82 else
83 {
84 if (LABEL_IN_SUBR (target_label))
85 {
86 if (LABEL_SUBR_START (target_label) != current_subr)
87 return "transfer out of subroutine";
88 }
89 else if (! LABEL_VERIFIED (target_label))
90 {
91 LABEL_IN_SUBR (target_label) = 1;
92 LABEL_SUBR_START (target_label) = current_subr;
93 }
94 else
95 return "transfer out of subroutine";
96 }
97 return NULL;
98 }
99
100 /* Return the "merged" types of TYPE1 and TYPE2.
101 If either is primitive, the other must match (after promotion to int).
102 For reference types, return the common super-class.
103 Return TYPE_UNKNOWN if the types cannot be merged. */
104
105 tree
106 merge_types (type1, type2)
107 tree type1, type2;
108 {
109 if (type1 == type2)
110 return type1;
111 if (type1 == TYPE_UNKNOWN || type2 == TYPE_UNKNOWN
112 || type1 == TYPE_RETURN_ADDR || type2 == TYPE_RETURN_ADDR)
113 return TYPE_UNKNOWN;
114 if (TREE_CODE (type1) == POINTER_TYPE && TREE_CODE (type2) == POINTER_TYPE)
115 {
116 int depth1, depth2;
117 tree tt1, tt2;
118 /* ptr_type_node is only used for a null reference,
119 which is compatible with any reference type. */
120 if (type1 == ptr_type_node || type2 == object_ptr_type_node)
121 return type2;
122 if (type2 == ptr_type_node || type1 == object_ptr_type_node)
123 return type1;
124
125 tt1 = HANDLE_TO_CLASS_TYPE (TREE_TYPE (type1));
126 tt2 = HANDLE_TO_CLASS_TYPE (TREE_TYPE (type2));
127
128 if (TYPE_ARRAY_P (tt1) || TYPE_ARRAY_P (tt2))
129 {
130 if (TYPE_ARRAY_P (tt1) == TYPE_ARRAY_P (tt2))
131 {
132 tree el_type1 = TYPE_ARRAY_ELEMENT (tt1);
133 tree el_type2 = TYPE_ARRAY_ELEMENT (tt2);
134 tree el_type = NULL_TREE;
135 if (el_type1 == el_type2)
136 el_type = el_type1;
137 else if (TREE_CODE (el_type1) == POINTER_TYPE
138 && TREE_CODE (el_type2) == POINTER_TYPE)
139 el_type = merge_types (el_type1, el_type2);
140 if (el_type != NULL_TREE)
141 {
142 HOST_WIDE_INT len1 = java_array_type_length (tt1);
143 HOST_WIDE_INT len2 = java_array_type_length (tt2);
144 if (len1 != len2)
145 len1 = -1;
146 else if (el_type1 == el_type2)
147 return type1;
148 return promote_type (build_java_array_type (el_type, len1));
149 }
150 }
151 return object_ptr_type_node;
152 }
153 type1 = tt1;
154 type2 = tt2;
155
156 depth1 = class_depth (type1);
157 depth2 = class_depth (type2);
158 for ( ; depth1 > depth2; depth1--)
159 type1 = TYPE_BINFO_BASETYPE (type1, 0);
160 for ( ; depth2 > depth1; depth2--)
161 type2 = TYPE_BINFO_BASETYPE (type2, 0);
162 while (type1 != type2)
163 {
164 type1 = TYPE_BINFO_BASETYPE (type1, 0);
165 type2 = TYPE_BINFO_BASETYPE (type2, 0);
166 }
167 return promote_type (type1);
168 }
169 if (INTEGRAL_TYPE_P (type1) && INTEGRAL_TYPE_P (type2)
170 && TYPE_PRECISION (type1) <= 32 && TYPE_PRECISION (type2) <= 32)
171 return int_type_node;
172 return TYPE_UNKNOWN;
173 }
174
175 /* Merge the current type state with that at LABEL.
176 Return -1 the the states are incompatible (i.e. on error),
177 0 if there was no change, and 1 if there was a change. */
178
179 int
180 merge_type_state (label)
181 tree label;
182 {
183 int nlocals = DECL_MAX_LOCALS(current_function_decl);
184 int cur_length = stack_pointer + nlocals;
185 tree vec = LABEL_TYPE_STATE (label);
186 tree return_map;
187 if (vec == NULL_TREE)
188 {
189 vec = make_tree_vec (cur_length);
190 LABEL_TYPE_STATE (label) = vec;
191 while (--cur_length >= 0)
192 TREE_VEC_ELT (vec, cur_length) = type_map [cur_length];
193 return 1;
194 }
195 else
196 {
197 int i;
198 int changed = 0;
199 if (LABEL_IS_SUBR_START (label) && LABEL_VERIFIED (label)
200 && current_subr != label)
201 return_map = LABEL_RETURN_TYPE_STATE (label);
202 else
203 return_map = NULL_TREE;
204 if (TREE_VEC_LENGTH (vec) != cur_length)
205 {
206 return -1;
207 }
208 for (i = 0; i < cur_length; i++)
209 {
210 tree old_type = TREE_VEC_ELT (vec, i);
211 tree new_type = merge_types (old_type, type_map [i]);
212 if (TREE_VEC_ELT (vec, i) != new_type)
213 {
214 /* If there has been a change, note that since we must re-verify.
215 However, if the label is the start of a subroutine,
216 we don't care about local variables that are neither
217 set nor used in the sub-routine. */
218 if (return_map == NULL_TREE || i >= nlocals
219 || TREE_VEC_ELT (return_map, i) != TYPE_UNUSED
220 || (TYPE_IS_WIDE (new_type)
221 && TREE_VEC_ELT (return_map, i+1) != TYPE_UNUSED))
222 changed = 1;
223 }
224 TREE_VEC_ELT (vec, i) = new_type;
225 if (new_type == TYPE_UNKNOWN)
226 {
227 if (i >= nlocals)
228 return -1;
229 }
230 else if (TYPE_IS_WIDE (new_type))
231 i++;
232 }
233 return changed;
234 }
235 }
236
237 /* Handle dup-like operations. */
238
239 static void
240 type_stack_dup (size, offset)
241 int size, offset;
242 {
243 tree type[4];
244 int index;
245 if (size + offset > stack_pointer)
246 error ("stack underflow - dup* operation");
247 for (index = 0; index < size + offset; index++)
248 {
249 type[index] = stack_type_map[stack_pointer - 1];
250 if (type[index] == void_type_node)
251 {
252 index++;
253 type[index] = stack_type_map[stack_pointer - 2];
254 if (! TYPE_IS_WIDE (type[index]))
255 fatal ("internal error - dup operation");
256 if (index == size || index == size + offset)
257 fatal ("dup operation splits 64-bit number");
258 }
259 pop_type (type[index]);
260 }
261 for (index = size; --index >= 0; )
262 {
263 if (type[index] != void_type_node)
264 push_type (type[index]);
265 }
266
267 for (index = size + offset; --index >= 0; )
268 {
269 if (type[index] != void_type_node)
270 push_type (type[index]);
271 }
272 }
273
274 /* This causes the next iteration to ignore the next instruction
275 and look for some other unhandled instruction. */
276 #define INVALIDATE_PC (prevpc = -1, oldpc = PC, PC = INVALID_PC)
277 #define INVALID_PC (-1)
278
279 #define VERIFICATION_ERROR(MESSAGE) \
280 do { message = MESSAGE; goto verify_error; } while (0)
281
282 #define PUSH_PENDING(LABEL) \
283 do { if ((message = check_pending_block (LABEL)) != NULL) \
284 goto verify_error; } while (0)
285
286 #ifdef __GNUC__
287 #define CHECK_PC_IN_RANGE(PC) ({if (PC < 0 || PC > length) goto bad_pc; 1;})
288 #else
289 #define CHECK_PC_IN_RANGE(PC) (PC < 0 || PC > length ? \
290 (fatal("Bad byte codes.\n"), 0) : 1)
291 #endif
292
293 #define BCODE byte_ops
294
295 /* Verify the bytecodes of the current method.
296 Return 1 on sucess, 0 on failure. */
297 int
298 verify_jvm_instructions (jcf, byte_ops, length)
299 JCF* jcf;
300 unsigned char* byte_ops;
301 long length;
302 {
303 tree label;
304 int wide = 0;
305 int op_code;
306 int PC;
307 int oldpc; /* PC of start of instruction. */
308 int prevpc; /* If >= 0, PC of previous instruction. */
309 char *message;
310 int i;
311 register unsigned char *p;
312 struct eh_range *prev_eh_ranges = NULL_EH_RANGE;
313 struct eh_range *eh_ranges;
314
315 jint int_value = -1;
316
317 pending_blocks = NULL_TREE;
318
319 /* Handle the exception table. */
320 method_init_exceptions ();
321 JCF_SEEK (jcf, DECL_CODE_OFFSET (current_function_decl) + length);
322 i = JCF_readu2 (jcf);
323
324 /* We read the exception backwards. */
325 p = jcf->read_ptr + 8 * i;
326 while (--i >= 0)
327 {
328 int start_pc = GET_u2 (p-8);
329 int end_pc = GET_u2 (p-6);
330 int handler_pc = GET_u2 (p-4);
331 int catch_type = GET_u2 (p-2);
332 p -= 8;
333
334 if (start_pc < 0 || start_pc >= length
335 || end_pc < 0 || end_pc > length || start_pc >= end_pc
336 || handler_pc < 0 || handler_pc >= length
337 || (handler_pc >= start_pc && handler_pc < end_pc)
338 || ! (instruction_bits [start_pc] & BCODE_INSTRUCTION_START)
339 || ! (instruction_bits [end_pc] & BCODE_INSTRUCTION_START)
340 || ! (instruction_bits [handler_pc] & BCODE_INSTRUCTION_START))
341 {
342 error ("bad pc in exception_table");
343 return 0;
344 }
345
346 if (! add_handler (start_pc, end_pc,
347 lookup_label (handler_pc),
348 catch_type == 0 ? NULL_TREE
349 : get_class_constant (jcf, catch_type)))
350 {
351 error ("overlapping exception ranges are not supported");
352 return 0;
353 }
354
355 instruction_bits [handler_pc] |= BCODE_EXCEPTION_TARGET;
356 }
357
358 for (PC = 0;;)
359 {
360 int index;
361 tree type, tmp;
362 if (((PC != INVALID_PC
363 && instruction_bits [PC] & BCODE_TARGET) != 0)
364 || PC == 0)
365 {
366 PUSH_PENDING (lookup_label (PC));
367 INVALIDATE_PC;
368 }
369 if (PC == INVALID_PC)
370 {
371 label = pending_blocks;
372 if (label == NULL_TREE)
373 break; /* We're done! */
374 pending_blocks = LABEL_PENDING_CHAIN (label);
375 LABEL_CHANGED (label) = 0;
376
377 if (LABEL_IN_SUBR (label))
378 current_subr = LABEL_SUBR_START (label);
379 else
380 current_subr = NULL_TREE;
381
382 /* Restore type_map and stack_pointer from
383 LABEL_TYPE_STATE (label), and continue
384 compiling from there. */
385 load_type_state (label);
386 PC = LABEL_PC (label);
387 }
388 else if (PC >= length)
389 VERIFICATION_ERROR ("falling through end of method");
390
391 oldpc = PC;
392
393 if (!(instruction_bits [PC] & BCODE_INSTRUCTION_START) && ! wide)
394 VERIFICATION_ERROR ("PC not at instruction start");
395
396 instruction_bits[PC] |= BCODE_VERIFIED;
397
398 eh_ranges = find_handler (oldpc);
399
400 op_code = byte_ops[PC++];
401 switch (op_code)
402 {
403 int is_static, is_putting;
404 case OPCODE_nop:
405 break;
406 case OPCODE_iconst_m1:
407 case OPCODE_iconst_0: case OPCODE_iconst_1: case OPCODE_iconst_2:
408 case OPCODE_iconst_3: case OPCODE_iconst_4: case OPCODE_iconst_5:
409 i = op_code - OPCODE_iconst_0;
410 goto push_int;
411 push_int:
412 if (byte_ops[PC] == OPCODE_newarray
413 || byte_ops[PC] == OPCODE_newarray)
414 int_value = i;
415 push_type (int_type_node); break;
416 case OPCODE_lconst_0: case OPCODE_lconst_1:
417 push_type (long_type_node); break;
418 case OPCODE_fconst_0: case OPCODE_fconst_1: case OPCODE_fconst_2:
419 push_type (float_type_node); break;
420 case OPCODE_dconst_0: case OPCODE_dconst_1:
421 push_type (double_type_node); break;
422 case OPCODE_bipush:
423 i = IMMEDIATE_s1;
424 goto push_int;
425 case OPCODE_sipush:
426 i = IMMEDIATE_s2;
427 goto push_int;
428 case OPCODE_iload: type = int_type_node; goto general_load;
429 case OPCODE_lload: type = long_type_node; goto general_load;
430 case OPCODE_fload: type = float_type_node; goto general_load;
431 case OPCODE_dload: type = double_type_node; goto general_load;
432 case OPCODE_aload: type = ptr_type_node; goto general_load;
433 general_load:
434 index = wide ? IMMEDIATE_u2 : IMMEDIATE_u1;
435 wide = 0;
436 goto load;
437 case OPCODE_iload_0: type = int_type_node; index = 0; goto load;
438 case OPCODE_iload_1: type = int_type_node; index = 1; goto load;
439 case OPCODE_iload_2: type = int_type_node; index = 2; goto load;
440 case OPCODE_iload_3: type = int_type_node; index = 3; goto load;
441 case OPCODE_lload_0: type = long_type_node; index = 0; goto load;
442 case OPCODE_lload_1: type = long_type_node; index = 1; goto load;
443 case OPCODE_lload_2: type = long_type_node; index = 2; goto load;
444 case OPCODE_lload_3: type = long_type_node; index = 3; goto load;
445 case OPCODE_fload_0: type = float_type_node; index = 0; goto load;
446 case OPCODE_fload_1: type = float_type_node; index = 1; goto load;
447 case OPCODE_fload_2: type = float_type_node; index = 2; goto load;
448 case OPCODE_fload_3: type = float_type_node; index = 3; goto load;
449 case OPCODE_dload_0: type = double_type_node; index = 0; goto load;
450 case OPCODE_dload_1: type = double_type_node; index = 1; goto load;
451 case OPCODE_dload_2: type = double_type_node; index = 2; goto load;
452 case OPCODE_dload_3: type = double_type_node; index = 3; goto load;
453 case OPCODE_aload_0: type = ptr_type_node; index = 0; goto load;
454 case OPCODE_aload_1: type = ptr_type_node; index = 1; goto load;
455 case OPCODE_aload_2: type = ptr_type_node; index = 2; goto load;
456 case OPCODE_aload_3: type = ptr_type_node; index = 3; goto load;
457 load:
458 if (index < 0
459 || (index + TYPE_IS_WIDE (type)
460 >= DECL_MAX_LOCALS (current_function_decl)))
461 VERIFICATION_ERROR ("invalid local variable index in load");
462 tmp = type_map[index];
463 if (tmp == TYPE_UNKNOWN || tmp == TYPE_SECOND
464 || (TYPE_IS_WIDE (type)
465 && type_map[index+1] != void_type_node)
466 || (type == ptr_type_node
467 ? TREE_CODE (tmp) != POINTER_TYPE
468 : type == int_type_node
469 ? (! INTEGRAL_TYPE_P (tmp) || TYPE_PRECISION (tmp) > 32)
470 : type != tmp))
471 VERIFICATION_ERROR("invalid local variable type in load");
472 push_type (tmp);
473 goto note_used;
474 case OPCODE_istore: type = int_type_node; goto general_store;
475 case OPCODE_lstore: type = long_type_node; goto general_store;
476 case OPCODE_fstore: type = float_type_node; goto general_store;
477 case OPCODE_dstore: type = double_type_node; goto general_store;
478 case OPCODE_astore: type = ptr_type_node; goto general_store;
479 general_store:
480 index = wide ? IMMEDIATE_u2 : IMMEDIATE_u1;
481 wide = 0;
482 goto store;
483 case OPCODE_istore_0: type = int_type_node; index = 0; goto store;
484 case OPCODE_istore_1: type = int_type_node; index = 1; goto store;
485 case OPCODE_istore_2: type = int_type_node; index = 2; goto store;
486 case OPCODE_istore_3: type = int_type_node; index = 3; goto store;
487 case OPCODE_lstore_0: type = long_type_node; index=0; goto store;
488 case OPCODE_lstore_1: type = long_type_node; index=1; goto store;
489 case OPCODE_lstore_2: type = long_type_node; index=2; goto store;
490 case OPCODE_lstore_3: type = long_type_node; index=3; goto store;
491 case OPCODE_fstore_0: type=float_type_node; index=0; goto store;
492 case OPCODE_fstore_1: type=float_type_node; index=1; goto store;
493 case OPCODE_fstore_2: type=float_type_node; index=2; goto store;
494 case OPCODE_fstore_3: type=float_type_node; index=3; goto store;
495 case OPCODE_dstore_0: type=double_type_node; index=0; goto store;
496 case OPCODE_dstore_1: type=double_type_node; index=1; goto store;
497 case OPCODE_dstore_2: type=double_type_node; index=2; goto store;
498 case OPCODE_dstore_3: type=double_type_node; index=3; goto store;
499 case OPCODE_astore_0: type = ptr_type_node; index = 0; goto store;
500 case OPCODE_astore_1: type = ptr_type_node; index = 1; goto store;
501 case OPCODE_astore_2: type = ptr_type_node; index = 2; goto store;
502 case OPCODE_astore_3: type = ptr_type_node; index = 3; goto store;
503 store:
504 if (index < 0
505 || (index + TYPE_IS_WIDE (type)
506 >= DECL_MAX_LOCALS (current_function_decl)))
507 {
508 VERIFICATION_ERROR ("invalid local variable index in store");
509 return 0;
510 }
511 type = pop_type (type);
512 type_map[index] = type;
513
514 /* If local variable changed, we need to reconsider eh handlers. */
515 prev_eh_ranges = NULL_EH_RANGE;
516
517 /* Allocate decl and rtx for this variable now, so if we're not
518 optmizing, we get a temporary that survives the whole method. */
519 find_local_variable (index, type, oldpc);
520
521 if (TYPE_IS_WIDE (type))
522 type_map[index+1] = TYPE_SECOND;
523 /* ... fall through to note_used ... */
524 note_used:
525 /* For store or load, note that local variable INDEX is used.
526 This is needed to verify try-finally sub-routines. */
527 if (current_subr)
528 {
529 tree vec = LABEL_RETURN_TYPE_STATE (current_subr);
530 tree subr_vec = LABEL_TYPE_STATE (current_subr);
531 int len = 1 + TYPE_IS_WIDE (type);
532 while (--len >= 0)
533 {
534 if (TREE_VEC_ELT (vec, index) == TYPE_UNUSED)
535 TREE_VEC_ELT (vec, index) = TREE_VEC_ELT (subr_vec, index);
536 }
537 }
538 break;
539 case OPCODE_iadd:
540 case OPCODE_iand:
541 case OPCODE_idiv:
542 case OPCODE_imul:
543 case OPCODE_ior:
544 case OPCODE_irem:
545 case OPCODE_ishl:
546 case OPCODE_ishr:
547 case OPCODE_isub:
548 case OPCODE_iushr:
549 case OPCODE_ixor:
550 type = int_type_node; goto binop;
551 case OPCODE_ineg:
552 case OPCODE_i2c:
553 case OPCODE_i2b:
554 case OPCODE_i2s:
555 type = int_type_node; goto unop;
556 case OPCODE_ladd:
557 case OPCODE_land:
558 case OPCODE_ldiv:
559 case OPCODE_lsub:
560 case OPCODE_lmul:
561 case OPCODE_lrem:
562 case OPCODE_lor:
563 case OPCODE_lxor:
564 type = long_type_node; goto binop;
565 case OPCODE_lneg:
566 type = long_type_node; goto unop;
567 case OPCODE_fadd: case OPCODE_fsub:
568 case OPCODE_fmul: case OPCODE_fdiv: case OPCODE_frem:
569 type = float_type_node; goto binop;
570 case OPCODE_fneg:
571 type = float_type_node; goto unop;
572 case OPCODE_dadd: case OPCODE_dsub:
573 case OPCODE_dmul: case OPCODE_ddiv: case OPCODE_drem:
574 type = double_type_node; goto binop;
575 case OPCODE_dneg:
576 type = double_type_node; goto unop;
577 unop:
578 pop_type (type);
579 push_type (type);
580 break;
581 binop:
582 pop_type (type);
583 pop_type (type);
584 push_type (type);
585 break;
586 case OPCODE_lshl:
587 case OPCODE_lshr:
588 case OPCODE_lushr:
589 pop_type (int_type_node);
590 pop_type (long_type_node);
591 push_type (long_type_node);
592 break;
593 case OPCODE_iinc:
594 index = wide ? IMMEDIATE_u2 : IMMEDIATE_u1;
595 PC += wide + 1;
596 wide = 0;
597 if (index < 0 || index >= DECL_MAX_LOCALS (current_function_decl))
598 VERIFICATION_ERROR ("invalid local variable index in iinc");
599 tmp = type_map[index];
600 if (! INTEGRAL_TYPE_P (tmp) || TYPE_PRECISION (tmp) > 32)
601 VERIFICATION_ERROR ("invalid local variable type in iinc");
602 break;
603 case OPCODE_i2l:
604 pop_type (int_type_node); push_type (long_type_node); break;
605 case OPCODE_i2f:
606 pop_type (int_type_node); push_type (float_type_node); break;
607 case OPCODE_i2d:
608 pop_type (int_type_node); push_type (double_type_node); break;
609 case OPCODE_l2i:
610 pop_type (long_type_node); push_type (int_type_node); break;
611 case OPCODE_l2f:
612 pop_type (long_type_node); push_type (float_type_node); break;
613 case OPCODE_l2d:
614 pop_type (long_type_node); push_type (double_type_node); break;
615 case OPCODE_f2i:
616 pop_type (float_type_node); push_type (int_type_node); break;
617 case OPCODE_f2l:
618 pop_type (float_type_node); push_type (long_type_node); break;
619 case OPCODE_f2d:
620 pop_type (float_type_node); push_type (double_type_node); break;
621 case OPCODE_d2i:
622 pop_type (double_type_node); push_type (int_type_node); break;
623 case OPCODE_d2l:
624 pop_type (double_type_node); push_type (long_type_node); break;
625 case OPCODE_d2f:
626 pop_type (double_type_node); push_type (float_type_node); break;
627 case OPCODE_lcmp:
628 type = long_type_node; goto compare;
629 case OPCODE_fcmpl:
630 case OPCODE_fcmpg:
631 type = float_type_node; goto compare;
632 case OPCODE_dcmpl:
633 case OPCODE_dcmpg:
634 type = double_type_node; goto compare;
635 compare:
636 pop_type (type); pop_type (type);
637 push_type (int_type_node); break;
638 case OPCODE_ifeq:
639 case OPCODE_ifne:
640 case OPCODE_iflt:
641 case OPCODE_ifge:
642 case OPCODE_ifgt:
643 case OPCODE_ifle:
644 pop_type (int_type_node); goto cond;
645 case OPCODE_ifnull:
646 case OPCODE_ifnonnull:
647 pop_type (ptr_type_node ); goto cond;
648 case OPCODE_if_icmpeq:
649 case OPCODE_if_icmpne:
650 case OPCODE_if_icmplt:
651 case OPCODE_if_icmpge:
652 case OPCODE_if_icmpgt:
653 case OPCODE_if_icmple:
654 pop_type (int_type_node); pop_type (int_type_node); goto cond;
655 case OPCODE_if_acmpeq:
656 case OPCODE_if_acmpne:
657 pop_type (object_ptr_type_node); pop_type (object_ptr_type_node);
658 goto cond;
659 cond:
660 PUSH_PENDING (lookup_label (oldpc + IMMEDIATE_s2));
661 break;
662 case OPCODE_goto:
663 PUSH_PENDING (lookup_label (oldpc + IMMEDIATE_s2));
664 INVALIDATE_PC;
665 break;
666 case OPCODE_wide:
667 switch (byte_ops[PC])
668 {
669 case OPCODE_iload: case OPCODE_lload:
670 case OPCODE_fload: case OPCODE_dload: case OPCODE_aload:
671 case OPCODE_istore: case OPCODE_lstore:
672 case OPCODE_fstore: case OPCODE_dstore: case OPCODE_astore:
673 case OPCODE_iinc:
674 case OPCODE_ret:
675 wide = 1;
676 break;
677 default:
678 VERIFICATION_ERROR ("invalid use of wide instruction");
679 }
680 break;
681 case OPCODE_ireturn: type = int_type_node; goto ret;
682 case OPCODE_lreturn: type = long_type_node; goto ret;
683 case OPCODE_freturn: type = float_type_node; goto ret;
684 case OPCODE_dreturn: type = double_type_node; goto ret;
685 case OPCODE_areturn: type = ptr_type_node; goto ret;
686 ret:
687 pop_type (type);
688 /* ... fall through ... */
689 case OPCODE_return:
690 INVALIDATE_PC;
691 break;
692 case OPCODE_getstatic: is_putting = 0; is_static = 1; goto field;
693 case OPCODE_putstatic: is_putting = 1; is_static = 1; goto field;
694 case OPCODE_getfield: is_putting = 0; is_static = 0; goto field;
695 case OPCODE_putfield: is_putting = 1; is_static = 0; goto field;
696 field:
697 {
698 int index = IMMEDIATE_u2;
699 tree self_type = get_class_constant
700 (jcf, COMPONENT_REF_CLASS_INDEX (&current_jcf->cpool, index));
701 tree field_name = COMPONENT_REF_NAME (&current_jcf->cpool, index);
702 tree field_signature = COMPONENT_REF_SIGNATURE (&current_jcf->cpool, index);
703 tree field_type = get_type_from_signature (field_signature);
704 if (is_putting)
705 pop_type (field_type);
706 if (! is_static)
707 {
708 /* Defer actual checking until next pass. */
709 pop_type (ptr_type_node);
710 }
711 if (! is_putting)
712 push_type (field_type);
713 break;
714 }
715 case OPCODE_new:
716 push_type (get_class_constant (jcf, IMMEDIATE_u2));
717 break;
718 case OPCODE_dup: type_stack_dup (1, 0); break;
719 case OPCODE_dup_x1: type_stack_dup (1, 1); break;
720 case OPCODE_dup_x2: type_stack_dup (1, 2); break;
721 case OPCODE_dup2: type_stack_dup (2, 0); break;
722 case OPCODE_dup2_x1: type_stack_dup (2, 1); break;
723 case OPCODE_dup2_x2: type_stack_dup (2, 2); break;
724 case OPCODE_pop: index = 1; goto pop;
725 case OPCODE_pop2: index = 2; goto pop;
726 pop:
727 if (stack_pointer < index)
728 VERIFICATION_ERROR ("stack underflow");
729 stack_pointer -= index;
730 break;
731 case OPCODE_swap:
732 if (stack_pointer < 2)
733 VERIFICATION_ERROR ("stack underflow (in swap)");
734 else
735 {
736 tree type1 = stack_type_map[stack_pointer - 1];
737 tree type2 = stack_type_map[stack_pointer - 2];
738 if (type1 == void_type_node || type2 == void_type_node)
739 VERIFICATION_ERROR ("verifier (swap): double or long value");
740 stack_type_map[stack_pointer - 2] = type1;
741 stack_type_map[stack_pointer - 1] = type2;
742 }
743 break;
744 case OPCODE_ldc: index = IMMEDIATE_u1; goto ldc;
745 case OPCODE_ldc2_w:
746 case OPCODE_ldc_w:
747 index = IMMEDIATE_u2; goto ldc;
748 ldc:
749 if (index <= 0 || index >= JPOOL_SIZE(current_jcf))
750 VERIFICATION_ERROR ("bad constant pool index in ldc");
751 int_value = -1;
752 switch (JPOOL_TAG (current_jcf, index) & ~CONSTANT_ResolvedFlag)
753 {
754 case CONSTANT_Integer: type = int_type_node; goto check_ldc;
755 case CONSTANT_Float: type = float_type_node; goto check_ldc;
756 case CONSTANT_String: type = string_type_node; goto check_ldc;
757 case CONSTANT_Long: type = long_type_node; goto check_ldc;
758 case CONSTANT_Double: type = double_type_node; goto check_ldc;
759 check_ldc:
760 if (TYPE_IS_WIDE (type) == (op_code == OPCODE_ldc2_w))
761 break;
762 /* ... else fall through ... */
763 default:
764 bad_ldc:
765 VERIFICATION_ERROR ("bad constant pool tag in ldc");
766 }
767 if (type == int_type_node)
768 {
769 i = TREE_INT_CST_LOW (get_constant (current_jcf, index));
770 goto push_int;
771 }
772 push_type (type);
773 break;
774
775 case OPCODE_invokevirtual:
776 case OPCODE_invokespecial:
777 case OPCODE_invokestatic:
778 case OPCODE_invokeinterface:
779 {
780 int index = IMMEDIATE_u2;
781 tree sig = COMPONENT_REF_SIGNATURE (&current_jcf->cpool, index);
782 tree self_type = get_class_constant
783 (current_jcf, COMPONENT_REF_CLASS_INDEX (&current_jcf->cpool,
784 index));
785 tree method_name = COMPONENT_REF_NAME (&current_jcf->cpool, index);
786 tree method_type;
787 method_type = parse_signature_string (IDENTIFIER_POINTER (sig),
788 IDENTIFIER_LENGTH (sig));
789 if (TREE_CODE (method_type) != FUNCTION_TYPE)
790 VERIFICATION_ERROR ("bad method signature");
791 pop_argument_types (TYPE_ARG_TYPES (method_type));
792
793 /* Can't invoke <clinit> */
794 if (method_name == clinit_identifier_node)
795 VERIFICATION_ERROR ("invoke opcode can't invoke <clinit>");
796 /* Apart invokespecial, can't invoke <init> */
797 if (op_code != OPCODE_invokespecial
798 && method_name == init_identifier_node)
799 VERIFICATION_ERROR ("invoke opcode can't invoke <init>");
800
801 if (op_code != OPCODE_invokestatic)
802 pop_type (self_type);
803
804 switch (op_code)
805 {
806 case OPCODE_invokeinterface:
807 {
808 int nargs = IMMEDIATE_u1;
809 int notZero = IMMEDIATE_u1;
810
811 if (!nargs || notZero)
812 VERIFICATION_ERROR
813 ("invalid argument number in invokeinterface");
814 break;
815 }
816 }
817
818 if (TREE_TYPE (method_type) != void_type_node)
819 push_type (TREE_TYPE (method_type));
820 break;
821 }
822
823 case OPCODE_arraylength:
824 /* Type checking actually made during code generation */
825 pop_type( ptr_type_node );
826 push_type( int_type_node );
827 break;
828
829 /* Q&D verification *or* more checking done during code generation
830 for byte/boolean/char/short, the value popped is a int coerced
831 into the right type before being stored. */
832 case OPCODE_iastore: type = int_type_node; goto astore;
833 case OPCODE_lastore: type = long_type_node; goto astore;
834 case OPCODE_fastore: type = float_type_node; goto astore;
835 case OPCODE_dastore: type = double_type_node; goto astore;
836 case OPCODE_aastore: type = ptr_type_node; goto astore;
837 case OPCODE_bastore: type = int_type_node; goto astore;
838 case OPCODE_castore: type = int_type_node; goto astore;
839 case OPCODE_sastore: type = int_type_node; goto astore;
840 astore:
841 /* FIXME - need better verification here */
842 pop_type (type); /* new value */
843 pop_type (int_type_node); /* index */
844 pop_type (ptr_type_node); /* array */
845 break;
846
847 /* Q&D verification *or* more checking done during code generation
848 for byte/boolean/char/short, the value pushed is a int. */
849 case OPCODE_iaload: type = int_type_node; goto aload;
850 case OPCODE_laload: type = long_type_node; goto aload;
851 case OPCODE_faload: type = float_type_node; goto aload;
852 case OPCODE_daload: type = double_type_node; goto aload;
853 case OPCODE_aaload: type = ptr_type_node; goto aload;
854 case OPCODE_baload: type = promote_type (byte_type_node); goto aload;
855 case OPCODE_caload: type = promote_type (char_type_node); goto aload;
856 case OPCODE_saload: type = promote_type (short_type_node); goto aload;
857 aload:
858 pop_type (int_type_node);
859 type = pop_type (ptr_type_node);
860 if (! is_array_type_p (type))
861 VERIFICATION_ERROR ("array load from non-array type");
862 push_type (TYPE_ARRAY_ELEMENT (TREE_TYPE (type)));
863 break;
864
865 case OPCODE_anewarray:
866 type = get_class_constant (current_jcf, IMMEDIATE_u2);
867 type = promote_type (type);
868 goto newarray;
869
870 case OPCODE_newarray:
871 index = IMMEDIATE_u1;
872 type = decode_newarray_type (index);
873 if (type == NULL_TREE)
874 VERIFICATION_ERROR ("invalid type code in newarray opcode");
875 goto newarray;
876
877 newarray:
878 if (int_value >= 0 && prevpc >= 0)
879 {
880 /* If previous instruction pushed int constant,
881 we want to use it. */
882 switch (byte_ops[prevpc])
883 {
884 case OPCODE_iconst_0: case OPCODE_iconst_1:
885 case OPCODE_iconst_2: case OPCODE_iconst_3:
886 case OPCODE_iconst_4: case OPCODE_iconst_5:
887 case OPCODE_bipush: case OPCODE_sipush:
888 case OPCODE_ldc: case OPCODE_ldc_w:
889 break;
890 default:
891 int_value = -1;
892 }
893 }
894 else
895 int_value = -1;
896 type = build_java_array_type (type, int_value);
897 pop_type (int_type_node);
898 push_type (type);
899 break;
900
901 case OPCODE_multianewarray:
902 {
903 int ndim, i;
904 index = IMMEDIATE_u2;
905 ndim = IMMEDIATE_u1;
906
907 if( ndim < 1 )
908 VERIFICATION_ERROR ("number of dimension lower that 1 in multianewarray" );
909
910 for( i = 0; i < ndim; i++ )
911 pop_type (int_type_node);
912 push_type (get_class_constant (current_jcf, index));
913 break;
914 }
915
916 case OPCODE_aconst_null:
917 push_type (ptr_type_node);
918 break;
919
920 case OPCODE_athrow:
921 pop_type (throwable_type_node);
922 INVALIDATE_PC;
923 break;
924
925 case OPCODE_checkcast:
926 pop_type (ptr_type_node);
927 type = get_class_constant (current_jcf, IMMEDIATE_u2);
928 push_type (type);
929 break;
930 case OPCODE_instanceof:
931 pop_type (ptr_type_node);
932 get_class_constant (current_jcf, IMMEDIATE_u2);
933 push_type (int_type_node);
934 break;
935
936 case OPCODE_tableswitch:
937 {
938 jint default_val, low, high;
939
940 pop_type (int_type_node);
941 while (PC%4)
942 {
943 if (byte_ops[PC++])
944 VERIFICATION_ERROR ("bad alignment in tableswitch pad");
945 }
946 PUSH_PENDING (lookup_label (oldpc+IMMEDIATE_s4));
947 low = IMMEDIATE_s4;
948 high = IMMEDIATE_s4;
949
950 if (low > high)
951 VERIFICATION_ERROR ("unsorted low/high value in tableswitch");
952
953 while (low++ <= high)
954 PUSH_PENDING (lookup_label (oldpc + IMMEDIATE_s4));
955 break;
956 }
957
958 case OPCODE_lookupswitch:
959 {
960 jint npairs, last, not_registered = 1;
961
962 pop_type (int_type_node);
963 while (PC%4)
964 {
965 if (byte_ops[PC++])
966 VERIFICATION_ERROR ("bad alignment in lookupswitch pad");
967 }
968
969 PUSH_PENDING (lookup_label (oldpc+IMMEDIATE_s4));
970 npairs = IMMEDIATE_s4;
971
972 if (npairs < 0)
973 VERIFICATION_ERROR ("invalid number of targets in lookupswitch");
974
975 while (npairs--)
976 {
977 int match = IMMEDIATE_s4;
978 if (not_registered)
979 not_registered = 0;
980 else if (last >= match)
981 VERIFICATION_ERROR ("unsorted match value in lookupswitch");
982
983 last = match;
984 PUSH_PENDING (lookup_label (oldpc + IMMEDIATE_s4));
985 }
986 break;
987 }
988
989 case OPCODE_monitorenter:
990 /* fall thru */
991 case OPCODE_monitorexit:
992 pop_type (ptr_type_node);
993 break;
994
995 case OPCODE_goto_w:
996 PUSH_PENDING (lookup_label (oldpc + IMMEDIATE_s4));
997 INVALIDATE_PC;
998 break;
999
1000 case OPCODE_jsr:
1001 {
1002 tree target = lookup_label (oldpc + IMMEDIATE_s2);
1003 tree return_label = lookup_label (PC);
1004 push_type (return_address_type_node);
1005 if (! LABEL_VERIFIED (target))
1006 {
1007 /* first time seen */
1008 tree return_type_map;
1009 int nlocals = DECL_MAX_LOCALS (current_function_decl);
1010 index = nlocals + DECL_MAX_STACK (current_function_decl);
1011 return_type_map = make_tree_vec (index);
1012 while (--index >= nlocals)
1013 TREE_VEC_ELT (return_type_map, index) = TYPE_UNKNOWN;
1014 while (--index >= 0)
1015 TREE_VEC_ELT (return_type_map, index) = TYPE_UNUSED;
1016 LABEL_RETURN_LABEL (target)
1017 = build_decl (LABEL_DECL, NULL_TREE, TREE_TYPE (target));
1018 LABEL_PC (LABEL_RETURN_LABEL (target)) = -1;
1019 LABEL_RETURN_TYPE_STATE (target) = return_type_map;
1020 LABEL_IS_SUBR_START (target) = 1;
1021 LABEL_IN_SUBR (target) = 1;
1022 LABEL_SUBR_START (target) = target;
1023 LABEL_SUBR_CONTEXT (target) = current_subr;
1024 }
1025 else if (! LABEL_IS_SUBR_START (target)
1026 || LABEL_SUBR_CONTEXT (target) != current_subr)
1027 VERIFICATION_ERROR ("label part of different subroutines");
1028
1029 i = merge_type_state (target);
1030 if (i != 0)
1031 {
1032 if (i < 0)
1033 VERIFICATION_ERROR ("types could not be merged at jsr");
1034 push_pending_label (target);
1035 }
1036 current_subr = target;
1037
1038 /* Chain return_pc onto LABEL_RETURN_LABELS (target) if needed. */
1039 if (! value_member (return_label, LABEL_RETURN_LABELS (target)))
1040 {
1041 LABEL_RETURN_LABELS (target)
1042 = tree_cons (NULL_TREE, return_label,
1043 LABEL_RETURN_LABELS (target));
1044 }
1045
1046 if (LABEL_VERIFIED (target))
1047 {
1048 tree return_map = LABEL_RETURN_TYPE_STATE (target);
1049 int len = TREE_VEC_LENGTH (return_map);
1050 stack_pointer = len - DECL_MAX_LOCALS (current_function_decl);
1051 while (--len >= 0)
1052 {
1053 if (TREE_VEC_ELT (return_map, len) != TYPE_UNUSED)
1054 type_map[len] = TREE_VEC_ELT (return_map, len);
1055 }
1056 current_subr = LABEL_SUBR_CONTEXT (target);
1057 PUSH_PENDING (return_label);
1058 }
1059
1060 INVALIDATE_PC;
1061 }
1062 break;
1063 case OPCODE_ret:
1064 if (current_subr == NULL)
1065 VERIFICATION_ERROR ("ret instruction not in a jsr subroutine");
1066 else
1067 {
1068 tree ret_map = LABEL_RETURN_TYPE_STATE (current_subr);
1069 tree caller = LABEL_SUBR_CONTEXT (current_subr);
1070 int size = DECL_MAX_LOCALS(current_function_decl)+stack_pointer;
1071 index = wide ? IMMEDIATE_u2 : IMMEDIATE_u1;
1072 wide = 0;
1073 INVALIDATE_PC;
1074 if (index < 0 || index >= DECL_MAX_LOCALS (current_function_decl)
1075 || type_map[index] != TYPE_RETURN_ADDR)
1076 VERIFICATION_ERROR ("invalid ret index");
1077
1078 /* The next chunk of code is similar to an inlined version of
1079 * merge_type_state (LABEL_RETURN_LABEL (current_subr)).
1080 * The main differences are that LABEL_RETURN_LABEL is
1081 * pre-allocated by the jsr (but we don't know the size then);
1082 * and that we have to handle TYPE_UNUSED. */
1083
1084 if (! RETURN_MAP_ADJUSTED (ret_map))
1085 { /* First return from this subroutine - fix stack pointer. */
1086 TREE_VEC_LENGTH (ret_map) = size;
1087 for (index = size; --index >= 0; )
1088 {
1089 if (TREE_VEC_ELT (ret_map, index) != TYPE_UNUSED)
1090 TREE_VEC_ELT (ret_map, index) = type_map[index];
1091 }
1092 RETURN_MAP_ADJUSTED (ret_map) = 1;
1093 }
1094 else
1095 {
1096 if (TREE_VEC_LENGTH (ret_map) != size)
1097 VERIFICATION_ERROR ("inconsistent stack size on ret");
1098 for (index = 0; index < size; index++)
1099 {
1100 tree type = TREE_VEC_ELT (ret_map, index);
1101 if (type != TYPE_UNUSED)
1102 {
1103 type = merge_types (type, type_map [index]);
1104 TREE_VEC_ELT (ret_map, index) = type;
1105 if (type == TYPE_UNKNOWN)
1106 {
1107 if (index >= size - stack_pointer)
1108 VERIFICATION_ERROR
1109 ("inconsistent types on ret from jsr");
1110 }
1111 else if (TYPE_IS_WIDE (type))
1112 index++;
1113 }
1114 }
1115 }
1116
1117 /* Check if there are any more pending blocks in this subroutine.
1118 Because we push pending blocks in a last-in-first-out order,
1119 and because we don't push anything from our caller until we
1120 are done with this subroutine or anything nested in it,
1121 then we are done if the top of the pending_blocks stack is
1122 not in a subroutine, or it is in our caller. */
1123 if (pending_blocks == NULL_TREE
1124 || ! LABEL_IN_SUBR (pending_blocks)
1125 || LABEL_SUBR_START (pending_blocks) == caller)
1126 {
1127 /* Since we are done with this subroutine (i.e. this is the
1128 last ret from it), set up the (so far known) return
1129 address as pending - with the merged type state. */
1130 tmp = LABEL_RETURN_LABELS (current_subr);
1131 current_subr = caller;
1132 for ( ; tmp != NULL_TREE; tmp = TREE_CHAIN (tmp))
1133 {
1134 tree return_label = TREE_VALUE (tmp);
1135 tree return_state = LABEL_TYPE_STATE (return_label);
1136 if (return_state == NULL_TREE)
1137 {
1138 /* This means means we had not verified the
1139 subroutine earlier, so this is the first jsr to
1140 call it. In this case, the type_map of the return
1141 address is just the current type_map - and that
1142 is handled by the following PUSH_PENDING. */
1143 }
1144 else
1145 {
1146 /* In this case we have to do a merge. But first
1147 restore the type_map for unused slots to those
1148 that were in effect at the jsr. */
1149 for (index = size; --index >= 0; )
1150 {
1151 type_map[index] = TREE_VEC_ELT (ret_map, index);
1152 if (type_map[index] == TYPE_UNUSED)
1153 type_map[index]
1154 = TREE_VEC_ELT (return_state, index);
1155 }
1156 }
1157 PUSH_PENDING (return_label);
1158 }
1159 }
1160 }
1161 break;
1162 case OPCODE_jsr_w:
1163 case OPCODE_ret_w:
1164 default:
1165 error ("unknown opcode %d@pc=%d during verification", op_code, PC-1);
1166 return 0;
1167 }
1168
1169 prevpc = oldpc;
1170
1171 /* The following test is true if we have entered or exited an exception
1172 handler range *or* we have done a store to a local variable.
1173 In either case we need to consider any exception handlers that
1174 might "follow" this instruction. */
1175
1176 if (eh_ranges != prev_eh_ranges)
1177 {
1178 int save_stack_pointer = stack_pointer;
1179 int index = DECL_MAX_LOCALS (current_function_decl);
1180 tree save_type = type_map[index];
1181 tree save_current_subr = current_subr;
1182 struct eh_range *ranges = find_handler (oldpc);
1183 stack_pointer = 1;
1184 for (; ranges != NULL_EH_RANGE; ranges = ranges->outer)
1185 {
1186 tree chain = ranges->handlers;
1187
1188 /* We need to determine if the handler is part of current_subr.
1189 The are two cases: (1) The exception catch range
1190 is entirely within current_subr. In that case the handler
1191 is also part of current_subr.
1192 (2) Some of the catch range is not in current_subr.
1193 In that case, the handler is *not* part of current_subr.
1194
1195 Figuring out which is the case is not necessarily obvious,
1196 in the presence of clever code generators (and obfuscators).
1197 We make a simplifying assumption that in case (2) we
1198 have that the current_subr is entirely within the catch range.
1199 In that case we can assume if that if a caller (the jsr) of
1200 a subroutine is within the catch range, then the handler is
1201 *not* part of the subroutine, and vice versa. */
1202
1203 current_subr = save_current_subr;
1204 for ( ; current_subr != NULL_TREE;
1205 current_subr = LABEL_SUBR_CONTEXT (current_subr))
1206 {
1207 tree return_labels = LABEL_RETURN_LABELS (current_subr);
1208 /* There could be multiple return_labels, but
1209 we only need to check one. */
1210 int return_pc = LABEL_PC (TREE_VALUE (return_labels));
1211 if (return_pc <= ranges->start_pc
1212 || return_pc > ranges->end_pc)
1213 break;
1214 }
1215
1216 for ( ; chain != NULL_TREE; chain = TREE_CHAIN (chain))
1217 {
1218 tree handler = TREE_VALUE (chain);
1219 tree type = TREE_PURPOSE (chain);
1220 if (type == NULL_TREE) /* a finally handler */
1221 type = throwable_type_node;
1222 type_map[index] = promote_type (type);
1223
1224 PUSH_PENDING (handler);
1225 }
1226 }
1227 stack_pointer = save_stack_pointer;
1228 current_subr = save_current_subr;
1229 type_map[index] = save_type;
1230 prev_eh_ranges = eh_ranges;
1231 }
1232 }
1233 return 1;
1234 bad_pc:
1235 message = "program counter out of range";
1236 goto verify_error;
1237 verify_error:
1238 error ("verification error at PC=%d: %s", oldpc, message);
1239 return 0;
1240 }
This page took 0.098886 seconds and 6 git commands to generate.