source: branches/generic-class-file/abcl/src/org/armedbear/lisp/jvm-class-file.lisp @ 12769

Last change on this file since 12769 was 12769, checked in by ehuelsmann, 13 years ago

README.BRANCH update, pool-management and method finalization.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 21.9 KB
Line 
1;;; jvm-class-file.lisp
2;;;
3;;; Copyright (C) 2010 Erik Huelsmann
4;;; $Id: jvm-class-file.lisp 12769 2010-06-27 19:48:41Z ehuelsmann $
5;;;
6;;; This program is free software; you can redistribute it and/or
7;;; modify it under the terms of the GNU General Public License
8;;; as published by the Free Software Foundation; either version 2
9;;; of the License, or (at your option) any later version.
10;;;
11;;; This program is distributed in the hope that it will be useful,
12;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
13;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14;;; GNU General Public License for more details.
15;;;
16;;; You should have received a copy of the GNU General Public License
17;;; along with this program; if not, write to the Free Software
18;;; Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19;;;
20;;; As a special exception, the copyright holders of this library give you
21;;; permission to link this library with independent modules to produce an
22;;; executable, regardless of the license terms of these independent
23;;; modules, and to copy and distribute the resulting executable under
24;;; terms of your choice, provided that you also meet, for each linked
25;;; independent module, the terms and conditions of the license of that
26;;; module.  An independent module is a module which is not derived from
27;;; or based on this library.  If you modify this library, you may extend
28;;; this exception to your version of the library, but you are not
29;;; obligated to do so.  If you do not wish to do so, delete this
30;;; exception statement from your version.
31
32(in-package "JVM")
33
34#|
35
36The general design of the class-file writer is to have generic
37- human readable - representations of the class being generated
38during the construction and manipulation phases.
39
40After completing the creation/manipulation of the class, all its
41components will be finalized. This process translates readable
42(e.g. string) representations to indices to be stored on disc.
43
44The only thing to be done after finalization is sending the
45output to a stream ("writing").
46
47
48Finalization happens highest-level first. As an example, take a
49method with exception handlers. The exception handlers are stored
50as attributes in the class file structure. They are children of the
51method's Code attribute. In this example, the body of the Code
52attribute (the higher level) gets finalized before the attributes.
53The reason to do so is that the exceptions need to refer to labels
54(offsets) in the Code segment.
55
56
57|#
58
59
60(defun map-primitive-type (type)
61  (case type
62    (:int        "I")
63    (:long       "J")
64    (:float      "F")
65    (:double     "D")
66    (:boolean    "Z")
67    (:char       "C")
68    (:byte       "B")
69    (:short      "S")
70    ((nil :void) "V")))
71
72
73#|
74
75The `class-name' facility helps to abstract from "this instruction takes
76a reference" and "this instruction takes a class name". We simply pass
77the class name around and the instructions themselves know which
78representation to use.
79
80|#
81
82(defstruct (class-name (:conc-name class-)
83                       (:constructor %make-class-name))
84  name-internal
85  ref
86  array-ref)
87
88(defun make-class-name (name)
89  (setf name (substitute #\/ #\. name))
90  (%make-class-name :name-internal name
91                    :ref (concatenate 'string "L" name ";")
92                    :array-ref (concatenate 'string "[L" name ";")))
93
94(defmacro define-class-name (symbol java-dotted-name &optional documentation)
95  `(defconstant ,symbol (make-class-name ,java-dotted-name)
96     ,documentation))
97
98(define-class-name +!java-object+ "java.lang.Object")
99(define-class-name +!java-string+ "java.lang.String")
100(define-class-name +!lisp-object+ "org.armedbear.lisp.LispObject")
101(define-class-name +!lisp-simple-string+ "org.armedbear.lisp.SimpleString")
102(define-class-name +!lisp+ "org.armedbear.lisp.Lisp")
103(define-class-name +!lisp-nil+ "org.armedbear.lisp.Nil")
104(define-class-name +!lisp-class+ "org.armedbear.lisp.LispClass")
105(define-class-name +!lisp-symbol+ "org.armedbear.lisp.Symbol")
106(define-class-name +!lisp-thread+ "org.armedbear.lisp.LispThread")
107(define-class-name +!lisp-closure-binding+ "org.armedbear.lisp.ClosureBinding")
108(define-class-name +!lisp-integer+ "org.armedbear.lisp.Integer")
109(define-class-name +!lisp-fixnum+ "org.armedbear.lisp.Fixnum")
110(define-class-name +!lisp-bignum+ "org.armedbear.lisp.Bignum")
111(define-class-name +!lisp-single-float+ "org.armedbear.lisp.SingleFloat")
112(define-class-name +!lisp-double-float+ "org.armedbear.lisp.DoubleFloat")
113(define-class-name +!lisp-cons+ "org.armedbear.lisp.Cons")
114(define-class-name +!lisp-load+ "org.armedbear.lisp.Load")
115(define-class-name +!lisp-character+ "org.armedbear.lisp.Character")
116(define-class-name +!lisp-simple-vector+ "org.armedbear.lisp.SimpleVector")
117(define-class-name +!lisp-abstract-string+ "org.armedbear.lisp.AbstractString")
118(define-class-name +!lisp-abstract-vector+ "org.armedbear.lisp.AbstractVector")
119(define-class-name +!lisp-abstract-bit-vector+
120    "org.armedbear.lisp.AbstractBitVector")
121(define-class-name +!lisp-environment+ "org.armedbear.lisp.Environment")
122(define-class-name +!lisp-special-binding+ "org.armedbear.lisp.SpecialBinding")
123(define-class-name +!lisp-special-binding-mark+
124    "org.armedbear.lisp.SpecialBindingMark")
125(define-class-name +!lisp-throw+ "org.armedbear.lisp.Throw")
126(define-class-name +!lisp-return+ "org.armedbear.lisp.Return")
127(define-class-name +!lisp-go+ "org.armedbear.lisp.Go")
128(define-class-name +!lisp-primitive+ "org.armedbear.lisp.Primitive")
129(define-class-name +!lisp-compiled-closure+
130    "org.armedbear.lisp.CompiledClosure")
131(define-class-name +!lisp-eql-hash-table+ "org.armedbear.lisp.EqlHashTable")
132(define-class-name +!lisp-package+ "org.armedbear.lisp.Package")
133(define-class-name +!lisp-readtable+ "org.armedbear.lisp.Readtable")
134(define-class-name +!lisp-stream+ "org.armedbear.lisp.Stream")
135(define-class-name +!lisp-closure+ "org.armedbear.lisp.Closure")
136(define-class-name +!lisp-closure-parameter+
137    "org.armedbear.lisp.Closure$Parameter")
138(define-class-name +!fasl-loader+ "org.armedbear.lisp.FaslClassLoader")
139
140
141(defun internal-field-type (field-type)
142  (if (keywordp field-type)
143      (map-primitive-type field-type)
144      (class-name-internal field-type)))
145
146(defun internal-field-ref (field-type)
147  (if (keywordp field-type)
148      (map-primitive-type field-type)
149      (class-ref field-type)))
150
151(defun descriptor (return-type &rest argument-types)
152  (format nil "(~{~A~}~A)" (mapcar #'internal-field-ref argument-types)
153          (internal-field-type return-type)))
154
155
156(defstruct pool
157  (count 1)  ;; "A constant pool entry is considered valid if it has
158             ;; an index greater than 0 (zero) and less than pool-count"
159  entries-list
160  ;; the entries hash stores raw values, except in case of string and
161  ;; utf8, because both are string values
162  (entries (make-hash-table :test #'equal :size 2048 :rehash-size 2.0)))
163
164(defstruct constant
165  tag
166  index)
167
168(defparameter +constant-type-map+
169  '((:class          7 1)
170    (:field-ref      9 1)
171    (:method-ref    10 1)
172    ;; (:interface-method-ref 11)
173    (:string         8 1)
174    (:integer        3 1)
175    (:float          4 1)
176    (:long           5 2)
177    (:double         6 2)
178    (:name-and-type 12 1)
179    (:utf8           1 1)))
180
181(defstruct (constant-class (:include constant
182                                     (tag 7)))
183  name)
184
185(defstruct (constant-member-ref (:include constant))
186  class
187  name/type)
188
189(defstruct (constant-string (:constructor make-constant-string
190                                          (index value-index))
191                            (:include constant
192                                      (tag 8)))
193  value-index) ;;; #### is this the value or the value index???
194
195(defstruct (constant-float/int (:include constant))
196  value)
197
198(defstruct (constant-double/long (:include constant))
199  value)
200
201(defstruct (constant-name/type (:include constant))
202  name-index
203  descriptor-index)
204
205(defstruct (constant-utf8 (:constructor make-constant-utf8 (index value))
206                          (:include constant
207                                    (tag 11)))
208  value)
209
210
211(defun pool-add-string (pool string)
212  (let ((entry (gethash (cons 8 string) ;; 8 == string-tag
213                        (pool-entries pool))))
214    (unless entry
215      (setf entry (make-constant-string (pool-add-utf8 pool string))
216            (gethash (cons 8 string) (pool-entries pool)) entry)
217      (incf (pool-count pool))
218      (push entry (pool-entries-list pool)))
219    (constant-index entry)))
220
221(defun pool-add-utf8 (pool utf8-as-string)
222  (let ((entry (gethash (cons 11 utf8-as-string) ;; 11 == utf8
223                        (pool-entries pool))))
224    (unless entry
225      (setf entry (make-constant-utf8 (pool-count pool) utf8-as-string)
226            (gethash (cons 11 utf8-as-string) (pool-entries pool)) entry)
227      (incf (pool-count pool))
228      (push entry (pool-entries-list pool)))
229    (constant-index entry)))
230
231(defstruct (class-file (:constructor %make-class-file))
232  constants
233  access-flags
234  class
235  superclass
236  ;; interfaces
237  fields
238  methods
239  attributes
240  )
241
242(defun class-add-field (class field)
243  (push field (class-file-fields class)))
244
245(defun class-field (class name)
246  (find name (class-file-fields class)
247        :test #'string= :key #'field-name))
248
249(defun class-add-method (class method)
250  (push method (class-file-methods class)))
251
252(defun class-methods-by-name (class name)
253  (remove (map-method-name name) (class-file-methods class)
254          :test-not #'string= :key #'method-name))
255
256(defun class-method (class descriptor)
257  (find descriptor (class-file-methods class)
258        :test #'string= :key #'method-name))
259
260
261(defun finalize-class-file (class)
262
263  ;; constant pool contains constants finalized on addition;
264  ;; no need for additional finalization
265
266  (setf (class-file-access-flags class)
267        (map-flags (class-file-access-flags class)))
268  ;; (finalize-class-name )
269  ;;  (finalize-interfaces)
270  (dolist (field (class-file-fields class))
271    (finalize-field field class))
272  (dolist (method (class-file-methods class))
273    (finalize-method method class))
274  ;; top-level attributes (no parent attributes to refer to)
275  (finalize-attributes (class-file-attributes class) nil class)
276
277)
278
279(defun !write-class-file (class stream)
280  ;; all components need to finalize themselves:
281  ;;  the constant pool needs to be complete before we start
282  ;;  writing our output.
283
284  ;; header
285  (write-u4 #xCAFEBABE stream)
286  (write-u2 3 stream)
287  (write-u2 45 stream)
288
289   ;; constants pool
290  (write-constants (class-file-constants class) stream)
291  ;; flags
292  (write-u2  (class-file-access-flags class) stream)
293  ;; class name
294  (write-u2 (class-file-class class) stream)
295  ;; superclass
296  (write-u2 (class-file-superclass class) stream)
297
298  ;; interfaces
299  (write-u2 0 stream)
300
301  ;; fields
302  (write-u2 (length (class-file-fields class)) stream)
303  (dolist (field (class-file-fields class))
304    (!write-field field stream))
305
306  ;; methods
307  (write-u2 (length (class-file-methods class)) stream)
308  (dolist (method (class-file-methods class))
309    (!write-method method stream))
310
311  ;; attributes
312  (write-attributes (class-file-attributes class) stream))
313
314(defun write-constants (constants stream)
315  (write-u2 (pool-count constants) stream)
316  (dolist (entry (reverse (pool-entries-list constants)))
317    (let ((tag (constant-tag entry)))
318    (write-u1 tag stream)
319    (case tag
320      (1 ; UTF8
321       (write-utf8 (constant-utf8-value entry) stream))
322      ((3 4) ; int
323       (write-u4 (constant-float/int-value entry) stream))
324      ((5 6) ; long double
325       (write-u4 (second entry) stream)
326       (write-u4 (third entry) stream))
327      ((9 10 11 12) ; fieldref methodref InterfaceMethodref nameAndType
328       (write-u2 (second entry) stream)
329       (write-u2 (third entry) stream))
330      ((7 8) ; class string
331       (write-u2 (constant-class-name entry) stream))
332      (t
333       (error "write-constant-pool-entry unhandled tag ~D~%" tag))))))
334
335#|
336
337ABCL doesn't use interfaces, so don't implement it here at this time
338
339(defstruct interface)
340
341|#
342
343
344(defparameter +access-flags-map+
345  '((:public       #x0001)
346    (:private      #x0002)
347    (:protected    #x0004)
348    (:static       #x0008)
349    (:final        #x0010)
350    (:volatile     #x0040)
351    (:synchronized #x0020)
352    (:transient    #x0080)
353    (:native       #x0100)
354    (:abstract     #x0400)
355    (:strict       #x0800)))
356
357(defun map-flags (flags)
358  (reduce #'(lambda (x y)
359              (logior (or (when (member (car x) flags)
360                            (second x))
361                          0) y)
362              (logior (or )))
363          :initial-value 0))
364
365(defstruct (field (:constructor %make-field))
366  access-flags
367  name
368  descriptor
369  attributes
370  )
371
372(defun make-field (name type &key (flags '(:public)))
373  (%make-field :access-flags flags
374               :name name
375               :descriptor (map-primitive-type type)))
376
377(defun add-field-attribute (field attribute)
378  (push attribute (field-attributes field)))
379
380
381(defun finalize-field (field class)
382  (declare (ignore class field))
383  (error "Not implemented"))
384
385(defun !write-field (field stream)
386  (declare (ignore field stream))
387  (error "Not implemented"))
388
389
390(defstruct (method (:constructor %!make-method))
391  access-flags
392  name
393  descriptor
394  attributes
395  arg-count ;; not in the class file,
396            ;; but required for setting up CODE attribute
397  )
398
399
400(defun map-method-name (name)
401  (cond
402    ((eq name :class-constructor)
403     "<clinit>")
404    ((eq name :constructor)
405     "<init>")
406    (t name)))
407
408(defun !make-method-descriptor (name return &rest args)
409  (apply #'concatenate (append (list 'string (map-method-name name) "(")
410                               (mapcar #'map-primitive-type args)
411                               (list ")" return))))
412
413(defun !make-method (name return args &key (flags '(:public)))
414  (setf name (map-method-name name))
415  (%make-method :descriptor (apply #'make-method-descriptor
416                                   name return args)
417                :access-flags flags
418                :name name
419                :arg-count (if (member :static flags)
420                               (length args)
421                               (1+ (length args))))) ;; implicit 'this'
422
423(defun method-add-attribute (method attribute)
424  (push attribute (method-attributes method)))
425
426(defun method-attribute (method name)
427  (find name (method-attributes method)
428        :test #'string= :key #'attribute-name))
429
430
431(defun finalize-method (method class)
432  (setf (method-access-flags method)
433        (map-flags (method-access-flags method))
434        (method-descriptor method)
435        (pool-add-utf8 (apply #'descriptor (method-descriptor method)))
436        (method-name method)
437        (pool-add-utf8 (map-method-name (method-name method))))
438  (finalize-attributes attributes nil class))
439
440
441(defun !write-method (method stream)
442  (declare (ignore method stream))
443  (error "Not implemented"))
444
445(defstruct attribute
446  name
447
448  ;; not in the class file:
449  finalizer  ;; function of 3 arguments: the attribute, parent and class-file
450  writer     ;; function of 2 arguments: the attribute and the output stream
451  )
452
453(defun finalize-attributes (attributes att class)
454  (dolist (attribute attributes)
455    ;; assure header: make sure 'name' is in the pool
456    (setf (attribute-name attribute)
457          (pool-add-string (class-file-constants class)
458                           (attribute-name attribute)))
459    ;; we're saving "root" attributes: attributes which have no parent
460    (funcall (attribute-finalizer attribute) attribute att class)))
461
462(defun write-attributes (attributes stream)
463  (write-u2 (length attributes) stream)
464  (dolist (attribute attributes)
465    (write-u2 (attribute-name attribute) stream)
466    ;; set up a bulk catcher for (UNSIGNED-BYTE 8)
467    ;; since we need to know the attribute length (excluding the header)
468    (let ((local-stream (sys::%make-byte-array-output-stream)))
469      (funcall (attribute-writer attribute) attribute local-stream)
470      (let ((array (sys::%get-output-stream-array local-stream)))
471        (write-u2 (length array) stream)
472        (write-sequence array stream)))))
473
474
475
476(defstruct (code-attribute (:conc-name code-)
477                           (:include attribute
478                                     (name "Code")
479                                     (finalizer #'!finalize-code)
480                                     (writer #'!write-code))
481                           (:constructor %make-code-attribute))
482  max-stack
483  max-locals
484  code
485  attributes
486  ;; labels contains offsets into the code array after it's finalized
487  (labels (make-hash-table :test #'eq))
488
489  ;; fields not in the class file start here
490  current-local ;; used for handling nested WITH-CODE-TO-METHOD blocks
491  )
492
493
494(defun code-label-offset (code label)
495  (gethash label (code-labels code)))
496
497(defun (setf code-label-offset) (offset code label)
498  (setf (gethash label (code-labels code)) offset))
499
500(defun !finalize-code (code class)
501  (let ((c (coerce (resolve-instructions (code-code code)) 'vector)))
502    (setf (code-max-stack code) (analyze-stack c)
503          (code-code code) (code-bytes c)))
504  (finalize-attributes (code-attributes code) code class))
505
506(defun !write-code (code stream)
507  (write-u2 (code-max-stack code) stream)
508  (write-u2 (code-max-locals code) stream)
509  (let ((code-array (code-code code)))
510    (write-u4 (length code-array) stream)
511    (dotimes (i (length code-array))
512      (write-u1 (svref code-array i) stream)))
513  (write-attributes (code-attributes code) stream))
514
515(defun make-code-attribute (method)
516  (%make-code-attribute :max-locals (method-arg-count method)))
517
518(defun code-add-attribute (code attribute)
519  (push attribute (code-attributes code)))
520
521(defun code-attribute (code name)
522  (find name (code-attributes code)
523        :test #'string= :key #'attribute-name))
524
525
526
527(defvar *current-code-attribute*)
528
529(defun save-code-specials (code)
530  (setf (code-code code) *code*
531        (code-max-locals code) *registers-allocated*
532        (code-exception-handlers code) *handlers*
533        (code-current-local code) *register*))
534
535(defun restore-code-specials (code)
536  (setf *code* (code-code code)
537        *registers-allocated* (code-max-locals code)
538        *register* (code-current-local code)))
539
540(defmacro with-code-to-method ((method &key safe-nesting) &body body)
541  (let ((m (gensym))
542        (c (gensym)))
543    `(progn
544       ,@(when safe-nesting
545           `((when *current-code-attribute*
546               (save-code-specials *current-code-attribute*))))
547       (let* ((,m ,method)
548              (,c (method-attribute ,m "Code"))
549              (*code* (code-code ,c))
550              (*registers-allocated* (code-max-locals ,c))
551              (*register* (code-current-local ,c))
552              (*current-code-attribute* ,c))
553         ,@body
554         (setf (code-code ,c) *code*
555               (code-exception-handlers ,c) *handlers*
556               (code-max-locals ,c) *registers-allocated*))
557       ,@(when safe-nesting
558           `((when *current-code-attribute*
559               (restore-code-specials *current-code-attribute*)))))))
560
561(defstruct (exceptions-attribute (:constructor make-exceptions)
562                                 (:conc-name exceptions-)
563                                 (:include attribute
564                                           (name "Exceptions")
565                                           (finalizer #'finalize-exceptions)
566                                           (writer #'write-exceptions)))
567  exceptions)
568
569(defun finalize-exceptions (exceptions code class)
570  (dolist (exception (exceptions-exceptions exceptions))
571    ;; no need to finalize `catch-type': it's already the index required
572    (setf (exception-start-pc exception)
573          (code-label-offset code (exception-start-pc exception))
574          (exception-end-pc exception)
575          (code-label-offset code (exception-end-pc exception))
576          (exception-handler-pc exception)
577          (code-label-offset code (exception-handler-pc exception))
578          (exception-catch-type exception)
579          (pool-add-string (class-file-constants class)
580                           (exception-catch-type exception))))
581  ;;(finalize-attributes (exceptions-attributes exception) exceptions class)
582  )
583
584
585(defun write-exceptions (exceptions stream)
586  ; number of entries
587  (write-u2 (length (exceptions-exceptions exceptions)) stream)
588  (dolist (exception (exceptions-exceptions exceptions))
589    (write-u2 (exception-start-pc exception) stream)
590    (write-u2 (exception-end-pc exception) stream)
591    (write-u2 (exception-handler-pc exception) stream)
592    (write-u2 (exception-catch-type exception) stream)))
593
594(defun code-add-exception (code start end handler type)
595  (when (null (code-attribute code "Exceptions"))
596    (code-add-attribute code (make-exceptions)))
597  (push (make-exception :start-pc start
598                        :end-pc end
599                        :handler-pc handler
600                        :catch-type type)
601        (exceptions-exceptions (code-attribute code "Exceptions"))))
602
603(defstruct exception
604  start-pc    ;; label target
605  end-pc      ;; label target
606  handler-pc  ;; label target
607  catch-type  ;; a string for a specific type, or NIL for all
608  )
609
610(defstruct (source-file-attribute (:conc-name source-)
611                                  (:include attribute
612                                            (name "SourceFile")))
613  filename)
614
615(defstruct (line-numbers-attribute (:include attribute
616                                             (name "LineNumberTable")))
617  line-numbers)
618
619(defstruct line-number
620  start-pc
621  line)
622
623(defstruct (local-variables-attribute (:conc-name local-var-)
624                                      (:include attribute
625                                                (name "LocalVariableTable")))
626  locals)
627
628(defstruct (local-variable (:conc-name local-))
629  start-pc
630  length
631  name
632  descriptor
633  index)
634
635#|
636
637;; this is the minimal sequence we need to support:
638
639;;  create a class file structure
640;;  add methods
641;;  add code to the methods, switching from one method to the other
642;;  finalize the methods, one by one
643;;  write the class file
644
645to support the sequence above, we probably need to
646be able to
647
648- find methods by signature
649- find the method's code attribute
650- add code to the code attribute
651- finalize the code attribute contents (blocking it for further addition)
652-
653
654
655|#
656
Note: See TracBrowser for help on using the repository browser.