1 ;; JDE-PARSE-CLASS.EL --- Parse a Java class file.
2 ;; $Revision: 1.9 $ $Date: 2005/01/20 04:53:21 $
4 ;; Copyright (C) 2002, 2004, 2005 Andrew Hyatt
6 ;; Author: Andrew Hyatt <andy_jde@thehyatts.net>
7 ;; Maintainers: Andrew Hyatt and Paul Kinnucan
9 ;; Keywords: java, tools
11 ;; This program is free software; you can redistribute it and/or modify
12 ;; it under the terms of the GNU General Public License as published by
13 ;; the Free Software Foundation; either version 2, or (at your option)
16 ;; This program is distributed in the hope that it will be useful,
17 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 ;; GNU General Public License for more details.
21 ;; A copy of the GNU General Public License can be obtained from this
22 ;; program's author (send electronic mail to
23 ;; ) or from the Free Software
24 ;; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27 ;; jde-parse-class|Andrew Hyatt|
28 ;; |Parse a Java class file to get xref info for the JDEE.
29 ;; |$Date: 2005/01/20 04:53:21 $|$Revision: 1.9 $|~/packages/jde-parse-class.el
33 ;; This file parses Java .class files to get information about what
34 ;; methods call what other methods. On the way there, we happen to
35 ;; pick up all sorts of useful information. If someone needs the
36 ;; functionality, contact me and I could easiliy put in more
39 ;; Also, we could return a semantic-compatible structure, but at the
40 ;; moment there is no point in doing this.
42 ;; There are two public functions, `jde-parse-class', which returns
43 ;; everything we have parsed about the class. The resultant structures
44 ;; can be passed to `jde-parse-class-extract-method-calls' which
45 ;; returns the method calls a particular class uses, and
46 ;; `jde-parse-class-extract-interfaces' to retrieve the interfaces a
47 ;; class uses, and `jde-parse-class-extract-classname' to get the
48 ;; fully qualified classname the class represents. This is used in
51 ;; There is one other functions that is generally useful, and one
52 ;; macro. The function is `get-bit-flags-for-byte', which, given a
53 ;; single byte in decimal (0 - 255), and a vector of flags
54 ;; corresponding to bytes, will return a list of which flags are on.
55 ;; The macro `do-and-advance-chars' wraps code, and after executing the
56 ;; code, advances the current point a certain number of chars (here
57 ;; equivalent to bytes). This is useful for the kind of parsing we are
60 ;; for XEmacs compatibilty
61 (unless (fboundp 'char-int)
62 (defalias 'char-int 'identity))
64 (require 'jde-xemacs))
66 (defmacro do-and-advance-chars (num &rest body)
67 "Execute BODY, and then advance the point NUM bytes, called like
68 (do-and-advance-chars 2 ...)"
69 (let ((result-sym (gensym)))
70 `(let ((,result-sym (progn ,@body)))
71 (goto-char (+ (point) ,num))
74 (defsubst jde-parse-class-get-next-const-val (constants)
75 (cadr (jde-parse-class-lookup-constant
76 (jde-parse-class-get-next-2-bytes) constants)))
78 ;; xemacs without mule can't do utf-8
79 (setq jde-parse-class-encoding
80 (if (member 'utf-8 (coding-system-list))
83 (defun jde-parse-class-slash-to-dot (string)
84 (subst-char-in-string ?/ ?. string))
86 (defun jde-parse-class (class-file)
87 "Parse the class in CLASS-FILE, and return an alist, with the following
88 keys: version, this-class, interfaces, fields, and methods."
89 ;; we must visit the file in such a way as to not use any encoding,
90 ;; else our parsing could be severely messed up
91 (unless (file-exists-p class-file)
92 (error (concat "Class file " class-file " does not exist")))
93 (let ((buf (find-file-noselect class-file nil t)))
95 (let* ((constants (jde-parse-class-get-constants))
96 (version (jde-parse-class-get-version))
97 (access (jde-parse-class-get-access-flags))
100 ?/ ?. (cadr (jde-parse-class-lookup-constant
101 (jde-parse-class-get-next-const-val constants)
103 (superclass (jde-parse-class-slash-to-dot
104 (do-and-advance-chars 2
105 (let ((val (jde-parse-class-get-const-ref
107 (if (eq (cadr val) 0)
108 nil ;; only Object can have no superclass
109 (cadr (jde-parse-class-lookup-constant
110 (cadr val) constants)))))))
111 (interfaces (jde-parse-class-get-interfaces constants))
112 (fields (jde-parse-class-get-fields constants))
113 (methods (jde-parse-class-get-methods constants))
114 (attributes (jde-parse-class-get-attributes constants)))
116 `((version . ,version) (access . ,access)
117 (this-class . ,this-class) (superclass . ,superclass)
118 (interfaces . ,interfaces)
121 (attributes . ,attributes)))))
123 (defun jde-parse-class-extract-caught-exception-types (info)
124 "Returns a list of a two-element of list of method signatures to
125 caught exception types for each method"
126 (mapcar (lambda (method)
127 (list `(,(cdr (nth 2 info))
128 ,(cdr (assoc 'name method))
129 ,@(cdr (assoc 'descriptor method)))
130 (mapcar (lambda (class) (when class
131 (jde-parse-class-slash-to-dot class)))
139 (cdr (assoc 'methods info))))
141 (defun jde-parse-class-extract-thrown-exception-types (info)
142 "Returns a two element list of method signatures to thrown exception
143 types for each method"
144 (mapcar (lambda (method)
145 (list `(,(cdr (nth 2 info))
146 ,(cdr (assoc 'name method))
147 ,@(cdr (assoc 'descriptor method)))
153 (cdr (assoc 'methods info))))
155 (defun jde-parse-class-extract-method-calls (info)
156 "Return a cons of a method signature, and a list of the methods it
157 calls. Each method in the list is a list of the calling method or
158 line number if available, the Class, method, and return value, and
159 arguments. INFO is the result of `jde-parse-class'"
160 (mapcan (lambda (method) (mapcar (lambda (attr)
162 `(,(cdr (nth 2 info))
163 ,(cdr (assoc 'name method))
164 ,@(cdr (assoc 'descriptor method))
165 ,(car attr)) (cdr attr)))
173 (cdr (assoc 'methods info))))
175 (defun jde-parse-class-extract-interfaces (info)
176 "Returns a list of fully qualified interface names that the class
177 implements. INFO is the result of `jde-parse-class'"
178 (mapcar 'jde-parse-class-slash-to-dot
179 (cdr (assoc 'interfaces info))))
181 (defun jde-parse-class-extract-superclass (info)
182 "Returns a list of fully qualified class names that are superclasses
184 (jde-parse-class-slash-to-dot (cdr (assoc 'superclass info))))
186 (defun jde-parse-class-extract-method-signatures (info)
187 "Returns a list of method names that the class implements"
188 (mapcar (lambda (method-info) (cons (cdr (assoc 'name method-info))
189 (cdr (assoc 'descriptor method-info))))
190 (cdr (assoc 'methods info))))
192 (defun jde-parse-class-extract-field-signatures (info)
193 "Return a list of field names that the class defines"
194 (mapcar (lambda (field-info)
195 (list (cdr (assoc 'name field-info))
196 (cdr (assoc 'descriptor field-info))))
197 (cdr (assoc 'fields info))))
199 (defun jde-parse-class-extract-classname (info)
200 "Returns the fully qualified classname that the class implements.
201 INFO is the result of `jde-parse-class'"
202 (cdr (assoc 'this-class info)))
204 (defun jde-parse-class-extract-sourcefile (info)
205 (cdr (assoc 'source-file (cdr (assoc 'attributes info)))))
207 (defun jde-parse-class-get-const-ref (point constants)
208 "Look at point in the class file and read it as a reference to the array.
209 Returns the constant information contained at the reference"
210 ;; we have to subtract one since the array index starts at 1
211 ;; (according to the class file, not us
212 (jde-parse-class-lookup-constant (jde-parse-class-get-2byte point)
215 (defun jde-parse-class-lookup-constant (num constants)
216 "From an index value, get the constant for that index"
217 (aref constants (- num 1)))
219 (defsubst jde-parse-class-get-next-length-val ()
220 (jde-parse-class-get-next-2-bytes))
222 (defun jde-parse-class-get-interfaces (constants)
223 (let ((num (jde-parse-class-get-next-length-val))
225 (dotimes (i num interfaces)
226 (add-to-list 'interfaces (cadr (jde-parse-class-lookup-constant
227 (jde-parse-class-get-next-const-val
228 constants) constants))))))
230 (defun jde-parse-class-get-methods (constants)
231 (let ((num (jde-parse-class-get-next-length-val))
233 (dotimes (i num (nreverse methods))
234 (add-to-list 'methods (jde-parse-class-get-method constants)))))
236 (defun jde-parse-class-get-method (constants)
237 (list (cons 'access-flags (jde-parse-class-get-access-flags))
238 (cons 'name (jde-parse-class-get-next-const-val constants))
239 (cons 'descriptor (jde-parse-class-parse-complete-arg-signature
240 (jde-parse-class-get-next-const-val constants)))
241 (cons 'attributes (jde-parse-class-get-attributes constants))))
243 (defun jde-parse-class-get-fields (constants)
244 (let ((num (jde-parse-class-get-next-length-val))
246 (dotimes (i num (nreverse fields))
247 (add-to-list 'fields (jde-parse-class-get-field constants)))))
249 (defun jde-parse-class-get-field (constants)
250 (list (cons 'access-flags (jde-parse-class-get-access-flags))
251 (cons 'name (jde-parse-class-get-next-const-val constants))
252 (cons 'descriptor (car (jde-parse-class-parse-first-arg
253 (jde-parse-class-get-next-const-val constants))))
254 (cons 'attributes (jde-parse-class-get-attributes constants))))
256 (defun jde-parse-class-get-attributes (constants)
257 (let ((num (jde-parse-class-get-next-length-val))
259 (dotimes (i num attributes)
260 (add-to-list 'attributes (jde-parse-class-get-attribute constants)))))
262 (defun jde-parse-class-get-attribute (constants)
263 (let ((attribute-type (jde-parse-class-get-next-const-val constants))
264 (numbytes (jde-parse-class-get-next-4-bytes (point))))
265 ;; TODO: implement the rest of the common attribute types
266 (cond ((equal attribute-type "Code")
268 (jde-parse-class-get-code-attribute numbytes constants)))
269 ((equal attribute-type "LineNumberTable")
270 (cons 'line-number-table
271 (jde-parse-class-get-line-number-attribute)))
272 ((equal attribute-type "Exceptions")
274 (jde-parse-class-get-exception-attribute constants)))
275 ((equal attribute-type "SourceFile")
277 (jde-parse-class-get-source-file-attribute constants)))
278 (t (forward-char numbytes)))))
280 (defun jde-parse-class-get-source-file-attribute (constants)
281 (jde-parse-class-get-next-const-val constants))
283 (defun jde-parse-class-get-exception-attribute (constants)
284 (let ((num (jde-parse-class-get-next-length-val))
286 (dotimes (i num classes)
287 (add-to-list 'classes
288 (cadr (jde-parse-class-lookup-constant
289 (jde-parse-class-get-next-const-val constants)
292 (defun jde-parse-class-get-line-number-attribute ()
293 (let ((num (jde-parse-class-get-next-length-val))
294 (line-number-infos '()))
295 (dotimes (i num (nreverse line-number-infos))
298 (cons (jde-parse-class-get-next-2-bytes)
299 (jde-parse-class-get-next-2-bytes))))))
301 (defun jde-parse-class-get-code-attribute (numbytes constants)
302 (goto-char (+ (point) 4)) ;; no one cares about max_stack and max_locals, right?
303 (let* ((code-numbytes (jde-parse-class-get-next-4-bytes (point)))
304 (end-point (+ (point) code-numbytes))
305 (begin-point (point))
308 (while (< (point) end-point)
309 (let* ((opcode-info (aref jde-parse-class-opcode-vec
310 (char-int (char-after))))
311 (opcode-val (car opcode-info))
312 (opcode-length (cdr opcode-info)))
314 (cond ((member opcode-val '(invokevirtual invokestatic invokespecial
315 invokeinterface getstatic putstatic
317 (do-and-advance-chars (- opcode-length 1)
320 (cons (- (point) begin-point)
321 (jde-parse-class-parse-method-signature
322 (jde-parse-class-lookup-method
323 (jde-parse-class-get-const-ref (point) constants)
325 ((eq opcode-val 'tableswitch)
326 ;; skip padding to go to a multiple of 4 from the begin-point.
327 ;; The second mod is to make sure on an offset of 4 we really don't skip anything
328 (forward-char (mod (- 4 (mod (- (point) begin-point) 4)) 4))
329 (forward-char 4) ;; we don't care about default
330 (let ((low (jde-parse-class-get-next-4-bytes-as-signed))
331 (high (jde-parse-class-get-next-4-bytes-as-signed)))
332 (forward-char (* 4 (+ (- high low) 1)))))
333 ((eq opcode-val 'lookupswitch)
334 (forward-char (mod (- 4 (mod (- (point) begin-point) 4)) 4))
336 (forward-char (* 8 (jde-parse-class-get-next-4-bytes-as-signed))))
337 ((eq opcode-val 'wide)
338 (let ((opcode2 (char-int (char-after))))
339 (if (eq opcode2 'iinc)
342 (t (forward-char (- opcode-length 1))))))
343 (let ((num-exceptions (jde-parse-class-get-next-length-val)))
344 (dotimes (i num-exceptions)
345 (let ((type (cdr (assoc 'catch-type
346 (jde-parse-class-get-exception-handler
349 (add-to-list 'catch-types type)))))
350 (let ((attributes (jde-parse-class-get-attributes constants)))
351 ;; Get the line numbers if there, if not use -1's to iondicate no line number
352 (let ((table (when (assoc 'line-number-table attributes)
353 (cdr (assoc 'line-number-table attributes)))))
356 (mapcar (lambda (call)
358 ;; The line numbers describe the instruction at
359 ;; the start of the line, not every instruction
361 ;; Advance in the table when the next byte
362 ;; described is higher than the current one
363 (progn (while (and (cadr table)
364 (>= (car call) (caadr table)))
365 (setq table (cdr table)))
366 (cons (cdar table) (cdr call)))
367 (cons -1 (cdr call)))) (nreverse calls)))
368 (cons 'catch-types catch-types))))))
370 (defun jde-parse-class-get-exception-handler (constants)
371 (list (cons 'start-pc (jde-parse-class-get-next-2-bytes))
372 (cons 'end-pc (jde-parse-class-get-next-2-bytes))
373 (cons 'handler-pc (jde-parse-class-get-next-2-bytes))
374 (cons 'catch-type (let ((val (jde-parse-class-get-next-2-bytes)))
376 (cadr (jde-parse-class-lookup-constant
377 (cadr (jde-parse-class-lookup-constant val constants))
380 (defun jde-parse-class-parse-first-arg (sig)
381 (let ((char (char-to-string (string-to-char sig))))
382 (cond ((equal char "B") `("byte" . 1))
383 ((equal char "C") `("char" . 1))
384 ((equal char "D") `("double" . 1))
385 ((equal char "F") `("float" . 1))
386 ((equal char "I") `("int" . 1))
387 ((equal char "J") `("long" . 1))
388 ((equal char "L") (let ((endpos (string-match ";" sig)))
389 (cons (jde-parse-class-slash-to-dot
393 ((equal char "S") `("short" . 1))
394 ((equal char "Z") `("boolean" . 1))
395 ((equal char "[") (let ((rest (jde-parse-class-parse-first-arg
397 (cons (concat (car rest) "[]") (+ (cdr rest) 1))))
398 (t (error (concat "Could not find char " char))))))
400 (defun jde-parse-class-parse-arg-signature (sig)
401 (when (> (length sig) 0)
402 (let ((arg (jde-parse-class-parse-first-arg sig)))
403 (if (> (cdr arg) (length sig))
405 (cons (car arg) (jde-parse-class-parse-arg-signature
406 (substring sig (cdr arg))))))))
409 (defun jde-parse-class-parse-complete-arg-signature (sig)
410 (let* ((match (string-match "(\\(.*\\))\\(.*\\)" sig)))
412 (let ((args (match-string 1 sig))
413 (return-val (match-string 2 sig)))
414 (list (unless (equal "V" return-val)
415 (car (jde-parse-class-parse-arg-signature return-val)))
416 (jde-parse-class-parse-arg-signature args)))
417 (list nil (jde-parse-class-parse-arg-signature sig)))))
420 (defun jde-parse-class-parse-method-signature (siglist)
421 `(,(jde-parse-class-slash-to-dot (car siglist))
422 ,(cadr siglist) ,@(jde-parse-class-parse-complete-arg-signature
425 (defun jde-parse-class-lookup-method (method constants)
426 (list (cadr (jde-parse-class-lookup-constant
427 (cadr (jde-parse-class-lookup-constant
428 (cadr method) constants))
430 (cadr (jde-parse-class-lookup-constant
432 (jde-parse-class-lookup-constant (nth 2 method) constants))
434 (cadr (jde-parse-class-lookup-constant
436 (jde-parse-class-lookup-constant (nth 2 method) constants))
439 (defun get-bit-flags-for-byte (byte flag-vec)
440 "Gets the bit flags for BYTE, given the flags that apply to each bit,
441 a vector of length 8 (one for each bit). Nulls in the FLAG-VEC are
442 taken to mean there is no flag for that byte, which causes the byte to be
445 For example: (get-bit-flags-for-byte 6 ['a 'b 'c 'd 'e 'f 'g 'h])
449 (when (and (aref flag-vec (- 7 i))
450 (> (logand (expt 2 i) byte) 0))
451 (add-to-list 'flags (aref flag-vec (- 7 i)))))))
453 (defun jde-parse-class-get-access-flags ()
454 (do-and-advance-chars 2
455 (let ((raw0 (char-int (char-after (point))))
456 (raw1 (char-int (char-after (+ (point) 1)))))
458 (get-bit-flags-for-byte raw0
459 [nil nil nil nil 'string 'abstract
461 (get-bit-flags-for-byte raw1
462 ['transient 'volatile 'synchronized 'final
464 'private 'public])))))
466 (defun jde-parse-class-get-version ()
467 "Return a list - (major-version minor-version)"
468 (list (jde-parse-class-get-2byte 5)
469 (jde-parse-class-get-2byte 7)))
471 (defun jde-parse-class-get-2byte (point)
472 "Gets the value of two bytes (0 - 65535) as an int"
473 (let ((b1 (char-int (char-after point)))
474 (b2 (char-int (char-after (+ 1 point)))))
477 (defun jde-parse-class-get-next-2-bytes ()
478 (do-and-advance-chars 2
479 (jde-parse-class-get-2byte (point))))
481 (defun jde-parse-class-get-4byte (point &optional ignore-large-val)
482 (let ((db1 (jde-parse-class-get-2byte point))
483 (db2 (jde-parse-class-get-2byte (+ 2 point))))
485 ;; don't go over the maxint in elisp (2047, since we have 1 more db1 could be 65536)
486 (+ (* 65536 db1) db2)
489 (error "Class file has a larger 4 byte value then emacs can handle")))))
491 (defun jde-parse-class-get-next-4-bytes-as-signed (&optional ignore-large-val)
492 (let ((db1 (jde-parse-class-get-next-2-bytes))
493 (db2 (jde-parse-class-get-next-2-bytes)))
494 (if (> (logand 32768 db1) 0) ;; if it's high-bit is set, then it's negative.
496 (- (+ 1 (+ (* (- 65535 db1) 65536) (- 65535 db2))))
499 (error "Class file has an unsigned int who is smaller than emacs can handle")))
500 (jde-parse-class-get-4byte (- (point) 4)))))
502 (defun jde-parse-class-get-next-4-bytes (&optional ignore-large-val)
503 (do-and-advance-chars 4
504 (jde-parse-class-get-4byte (point) ignore-large-val)))
506 (defun jde-parse-class-get-constants ()
507 "Returns a list of the constant ending location (not inclusive of
508 of the constants) and a vector of all constants"
509 (let* ((count (- (jde-parse-class-get-2byte 9) 1))
510 (const-vec (make-vector count '())))
511 (goto-char 11) ;; start of constants
512 (dotimes (i count const-vec)
513 (let ((const (jde-parse-class-get-next-constant)))
514 (aset const-vec i const)
515 ;; doubles and longs take up two places on the const table.
516 (when (or (eq (car const) 'double)
517 (eq (car const) 'long))
518 (aset const-vec (+ 1 i) nil)
519 (setq i (+ 1 i)))))))
521 (defsubst jde-parse-class-get-long-constant (&optional ignore-large-val)
522 (let ((qb1 (jde-parse-class-get-next-4-bytes ignore-large-val))
523 (qb2 (jde-parse-class-get-next-4-bytes ignore-large-val)))
527 (error "Class file has a large 8 byte value than emacs can handle"))
530 (defsubst jde-parse-class-get-double-constant ()
531 "NOT IMPLEMENTED YET"
532 (do-and-advance-chars 8
535 (defsubst jde-parse-class-get-ref-constant ()
536 (list (jde-parse-class-get-next-2-bytes) (jde-parse-class-get-next-2-bytes)))
538 (defsubst jde-parse-class-get-float-constant ()
539 "NOT IMPLEMENTED YET"
540 (do-and-advance-chars 4
543 (defsubst jde-parse-class-get-nameandtype-constant ()
544 (list (jde-parse-class-get-next-2-bytes) (jde-parse-class-get-next-2-bytes)))
546 (defsubst jde-parse-class-get-utf8-constant ()
547 (let* ((len (jde-parse-class-get-next-2-bytes))
548 (result (encode-coding-string (buffer-substring (point)
550 jde-parse-class-encoding)))
551 (goto-char (+ len (point)))
554 (defun jde-parse-class-get-next-constant ()
555 (let ((const-type (char-int (char-after (point)))))
557 (cond ((eq const-type 7)
558 `(class ,(jde-parse-class-get-next-2-bytes)))
560 `(field ,@(jde-parse-class-get-ref-constant)))
562 `(method ,@(jde-parse-class-get-ref-constant)))
564 `(interface-method ,@(jde-parse-class-get-ref-constant)))
566 `(string ,(jde-parse-class-get-next-2-bytes)))
568 `(integer ,(jde-parse-class-get-next-4-bytes t)))
570 `(float ,(jde-parse-class-get-float-constant)))
572 `(long ,(jde-parse-class-get-long-constant t)))
574 `(double ,(jde-parse-class-get-double-constant)))
576 `(name-and-type ,@(jde-parse-class-get-nameandtype-constant)))
578 `(utf8 ,(jde-parse-class-get-utf8-constant))))))
580 (defconst jde-parse-class-opcode-vec
582 (aconst_null . 1) ;; 1
740 (if_icmpeq . 3) ;; 159
741 (if_icmpne . 3) ;; 160
742 (if_icmplt . 3) ;; 161
743 (if_icmpge . 3) ;; 162
744 (if_icmpgt . 3) ;; 163
745 (if_icmple . 3) ;; 164
746 (if_acmpeq . 3) ;; 165
747 (if_acmpne . 3) ;; 166
751 (tableswitch . -1) ;; 170 - variable length instruction
752 (lookupswitch . -1) ;; 171 - variable length instruction
759 (getstatic . 3) ;; 178
760 (putstatic . 3) ;; 179
761 (getfield . 3) ;; 180
762 (putfield . 3) ;; 181
763 (invokevirtual . 3) ;; 182
764 (invokespecial . 3) ;; 183
765 (invokestatic . 3) ;; 184
766 (invokeinterface . 5) ;; 185
769 (newarray . 2) ;; 188
770 (anewarray . 3) ;; 189
771 (arraylength . 1) ;; 190
773 (checkcast . 3) ;; 192
774 (instanceof . 3) ;; 193
775 (monitorcenter . 1) ;; 194
776 (monitorexit . 1) ;; 195
777 (wide . -1) ;; 196 - variable length instruction
778 (multianewarray . 4) ;; 197
780 (ifnonnull . 3) ;; 199
784 "A vector storing the java opcodes. Each position in the vector is
785 in the position of it's bytecode number. For example, if, when
786 reading a class file we come across bytecode 0, we can just look at
787 this vector to see both the name of the instruction, and the size of
788 the operation in bytes. A few opcodes have variable length, so those
789 must be calculated at runtime.")
791 (provide 'jde-parse-class)
793 ;; $Log: jde-parse-class.el,v $
794 ;; Revision 1.9 2005/01/20 04:53:21 paulk
795 ;; Fix bug in jde-parse-class-get-code-attribute that caused it to compute the length of iinc JVM instructions
796 ;; as 5 bytes when they are in fact 6 bytes long. Thanks to Jack Klebanoff.
798 ;; Revision 1.8 2004/07/09 05:17:20 paulk
799 ;; Eliminated initialization of loop variable i to 0 in
800 ;; jde-parse-class-get-constants. It does not seem necessssary as dotimes
801 ;; initializes the variable itself to 0. Further, the initialization
802 ;; turned i into a free variable, causing a compiler warning. I tested
803 ;; jde-parse-class-get-constants before and after this change and the
804 ;; change made no difference.
806 ;; Revision 1.7 2003/06/19 17:01:39 ahyatt
807 ;; Now compatible with emacs and xemacs versions without MULE
809 ;; Revision 1.5 2003/03/08 06:32:45 ahyatt
810 ;; Now parses exception information
812 ;; Revision 1.4 2002/12/01 02:39:58 ahyatt
813 ;; Support for jde-xref's latest improvements, which require more
814 ;; information from each class.
816 ;; Revision 1.3 2002/11/21 04:26:33 ahyatt
817 ;; Changed my e-mail address to andy_jde@thehyatts.net
819 ;; Revision 1.2 2002/11/21 04:03:47 ahyatt
820 ;; Fixed a bug in jde-parse-class where two functions had the same
821 ;; definition. The fix involved renamed a few classes to keep
822 ;; consistency. jde-xref had to change as well.
824 ;; Removed a (newline) function call in jde-xref and replaced it with a "\n"
826 ;; In jde-xref, rewrote the parts of jde-xref-make-db-from-path which
827 ;; dealt with keeping track of the already parsed classes. Previous
828 ;; solution was really kludgey, this solution is only somewhat kludgey.
829 ;; I'm still thinking about this problem.
831 ;; Revision 1.1 2002/11/18 07:02:18 paulk
835 ;; end of jde-xref.el