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

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

More work-in-progress. Add file mistakenly not committed with
WIP commit: it's the most important part.

File size: 21.5 KB
Line 
1;;; jvm-class-file.lisp
2;;;
3;;; Copyright (C) 2010 Erik Huelsmann
4;;; $Id: compiler-pass2.lisp 12311 2009-12-28 23:11:35Z 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    (: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 descriptor (method-name return-type &rest argument-types)
142  (format nil "~A(~{~A~}~A)" method-name
143          (mapcar #'(lambda (arg-type)
144                      (if (keywordp arg-type)
145                          (map-primitive-type arg-type)
146                          (class-ref arg-type)))
147                  argument-types)
148          (if (keywordp return-type)
149              (map-primitive-type return-type)
150              (class-name-internal return-type))))
151
152
153
154
155
156(defstruct pool
157  (count 1)  ;; ####  why count 1???
158  entries-list
159  (entries (make-hash-table :test #'equal :size 2048 :rehash-size 2.0)))
160
161(defstruct constant
162  tag
163  index)
164
165(defparameter +constant-type-map+
166  '((:class          7 1)
167    (:field-ref      9 1)
168    (:method-ref    10 1)
169    ;; (:interface-method-ref 11)
170    (:string         8 1)
171    (:integer        3 1)
172    (:float          4 1)
173    (:long           5 2)
174    (:double         6 2)
175    (:name-and-type 12 1)
176    (:utf8           1 1)))
177
178(defstruct (constant-class (:include constant
179                                     (tag 7)))
180  name)
181
182(defstruct (constant-member-ref (:include constant))
183  class
184  name/type)
185
186(defstruct (constant-string (:constructor make-constant-string (value-index))
187                            (:include constant
188                                      (tag 8)))
189  value-index) ;;; #### is this the value or the value index???
190
191(defstruct (constant-float/int (:include constant))
192  value)
193
194(defstruct (constant-double/long (:include constant))
195  value)
196
197(defstruct (constant-name/type (:include constant))
198  name-index
199  descriptor-index)
200
201(defstruct (constant-utf8 (:include constant))
202  value)
203
204
205;; Need to add pool/constant creation addition routines here;
206;; all routines have 2 branches: return existing or push new.
207
208(defun pool-add-string (pool string)
209  (let ((entry (gethash (pool-entries string))))
210    (unless entry
211      (setf entry (make-constant-string (pool-count pool) string))
212      (push entry (pool-entries-list pool))
213      (incf (pool-count pool)))
214    (constant-index entry)))
215
216
217
218(defstruct (class-file (:constructor %make-class-file))
219  constants
220  access-flags
221  class
222  superclass
223  ;; interfaces
224  fields
225  methods
226  attributes
227  )
228
229(defun class-add-field (class field)
230  (push field (class-file-fields class)))
231
232(defun class-field (class name)
233  (find name (class-file-fields class)
234        :test #'string= :key #'field-name))
235
236(defun class-add-method (class method)
237  (push method (class-file-methods class)))
238
239(defun class-methods-by-name (class name)
240  (remove (map-method-name name) (class-file-methods class)
241          :test-not #'string= :key #'method-name))
242
243(defun class-method (class descriptor)
244  (find descriptor (class-file-methods class)
245        :test #'string= :key #'method-name))
246
247
248(defun finalize-class-file (class)
249
250  ;; constant pool contains constants finalized on addition;
251  ;; no need for additional finalization
252
253  (setf (class-file-access-flags class)
254        (map-flags (class-file-access-flags class)))
255  ;; (finalize-class-name )
256  ;;  (finalize-interfaces)
257  (dolist (field (class-file-fields class))
258    (finalize-field field class))
259  (dolist (method (class-file-methods class))
260    (finalize-method method class))
261  ;; top-level attributes (no parent attributes to refer to)
262  (finalize-attributes (class-file-attributes class) nil class)
263
264)
265
266(defun !write-class-file (class stream)
267  ;; all components need to finalize themselves:
268  ;;  the constant pool needs to be complete before we start
269  ;;  writing our output.
270
271  ;; header
272  (write-u4 #xCAFEBABE stream)
273  (write-u2 3 stream)
274  (write-u2 45 stream)
275
276   ;; constants pool
277  (write-constants (class-file-constants class) stream)
278  ;; flags
279  (write-u2  (class-file-access-flags class) stream)
280  ;; class name
281  (write-u2 (class-file-class class) stream)
282  ;; superclass
283  (write-u2 (class-file-superclass class) stream)
284
285  ;; interfaces
286  (write-u2 0 stream)
287
288  ;; fields
289  (write-u2 (length (class-file-fields class)) stream)
290  (dolist (field (class-file-fields class))
291    (!write-field field stream))
292
293  ;; methods
294  (write-u2 (length (class-file-methods class)) stream)
295  (dolist (method (class-file-methods class))
296    (!write-method method stream))
297
298  ;; attributes
299  (write-attributes (class-file-attributes class) stream))
300
301(defun write-constants (constants stream)
302  (write-u2 (pool-count constants) stream)
303  (dolist (entry (reverse (pool-entries-list constants)))
304    (let ((tag (constant-tag entry)))
305    (write-u1 tag stream)
306    (case tag
307      (1 ; UTF8
308       (write-utf8 (constant-utf8-value entry) stream))
309      ((3 4) ; int
310       (write-u4 (constant-float/int-value entry) stream))
311      ((5 6) ; long double
312       (write-u4 (second entry) stream)
313       (write-u4 (third entry) stream))
314      ((9 10 11 12) ; fieldref methodref InterfaceMethodref nameAndType
315       (write-u2 (second entry) stream)
316       (write-u2 (third entry) stream))
317      ((7 8) ; class string
318       (write-u2 (constant-class-name entry) stream))
319      (t
320       (error "write-constant-pool-entry unhandled tag ~D~%" tag))))))
321
322#|
323
324ABCL doesn't use interfaces, so don't implement it here at this time
325
326(defstruct interface)
327
328|#
329
330
331(defparameter +access-flags-map+
332  '((:public       #x0001)
333    (:private      #x0002)
334    (:protected    #x0004)
335    (:static       #x0008)
336    (:final        #x0010)
337    (:volatile     #x0040)
338    (:synchronized #x0020)
339    (:transient    #x0080)
340    (:native       #x0100)
341    (:abstract     #x0400)
342    (:strict       #x0800)))
343
344(defun map-flags (flags)
345  (reduce #'(lambda (x y)
346              (logior (or (when (member (car x) flags)
347                            (second x))
348                          0) y)
349              (logior (or )))
350          :initial-value 0))
351
352(defstruct (field (:constructor %make-field))
353  access-flags
354  name
355  descriptor
356  attributes
357  )
358
359(defun make-field (name type &key (flags '(:public)))
360  (%make-field :access-flags flags
361               :name name
362               :descriptor (map-primitive-type type)))
363
364(defun add-field-attribute (field attribute)
365  (push attribute (field-attributes field)))
366
367
368(defun finalize-field (field class)
369  (declare (ignore class field))
370  (error "Not implemented"))
371
372(defun !write-field (field stream)
373  (declare (ignore field stream))
374  (error "Not implemented"))
375
376
377(defstruct (method (:constructor %!make-method))
378  access-flags
379  name
380  descriptor
381  attributes
382  arg-count ;; not in the class file,
383            ;; but required for setting up CODE attribute
384  )
385
386
387(defun map-method-name (name)
388  (cond
389    ((eq name :class-constructor)
390     "<clinit>")
391    ((eq name :constructor)
392     "<init>")
393    (t name)))
394
395(defun !make-method-descriptor (name return &rest args)
396  (apply #'concatenate (append (list 'string (map-method-name name) "(")
397                               (mapcar #'map-primitive-type args)
398                               (list ")" return))))
399
400(defun !make-method (name return args &key (flags '(:public)))
401  (setf name (map-method-name name))
402  (%make-method :descriptor (apply #'make-method-descriptor
403                                   name return args)
404                :access-flags flags
405                :name name
406                :arg-count (if (member :static flags)
407                               (length args)
408                               (1+ (length args))))) ;; implicit 'this'
409
410(defun method-add-attribute (method attribute)
411  (push attribute (method-attributes method)))
412
413(defun method-attribute (method name)
414  (find name (method-attributes method)
415        :test #'string= :key #'attribute-name))
416
417
418(defun finalize-method (method class)
419  (declare (ignore method class))
420  (error "Not implemented"))
421
422
423(defun !write-method (method stream)
424  (declare (ignore method stream))
425  (error "Not implemented"))
426
427(defstruct attribute
428  name
429
430  ;; not in the class file:
431  finalizer  ;; function of 3 arguments: the attribute, parent and class-file
432  writer     ;; function of 2 arguments: the attribute and the output stream
433  )
434
435(defun finalize-attributes (attributes att class)
436  (dolist (attribute attributes)
437    ;; assure header: make sure 'name' is in the pool
438    (setf (attribute-name attribute)
439          (pool-add-string (class-file-constants class)
440                           (attribute-name attribute)))
441    ;; we're saving "root" attributes: attributes which have no parent
442    (funcall (attribute-finalizer attribute) attribute att class)))
443
444(defun write-attributes (attributes stream)
445  (write-u2 (length attributes) stream)
446  (dolist (attribute attributes)
447    (write-u2 (attribute-name attribute) stream)
448    ;; set up a bulk catcher for (UNSIGNED-BYTE 8)
449    ;; since we need to know the attribute length (excluding the header)
450    (let ((local-stream (sys::%make-byte-array-output-stream)))
451      (funcall (attribute-writer attribute) attribute local-stream)
452      (let ((array (sys::%get-output-stream-array local-stream)))
453        (write-u2 (length array) stream)
454        (write-sequence array stream)))))
455
456
457
458(defstruct (code-attribute (:conc-name code-)
459                           (:include attribute
460                                     (name "Code")
461                                     (finalizer #'!finalize-code)
462                                     (writer #'!write-code))
463                           (:constructor %make-code-attribute))
464  max-stack
465  max-locals
466  code
467  attributes
468  ;; labels contains offsets into the code array after it's finalized
469  (labels (make-hash-table :test #'eq))
470
471  ;; fields not in the class file start here
472  current-local ;; used for handling nested WITH-CODE-TO-METHOD blocks
473  )
474
475
476(defun code-label-offset (code label)
477  (gethash label (code-labels code)))
478
479(defun (setf code-label-offset) (offset code label)
480  (setf (gethash label (code-labels code)) offset))
481
482(defun !finalize-code (code class)
483  (let ((c (coerce (resolve-instructions (code-code code)) 'vector)))
484    (setf (code-max-stack code) (analyze-stack c)
485          (code-code code) (code-bytes c)))
486  (finalize-attributes (code-attributes code) code class))
487
488(defun !write-code (code stream)
489  (write-u2 (code-max-stack code) stream)
490  (write-u2 (code-max-locals code) stream)
491  (let ((code-array (code-code code)))
492    (write-u4 (length code-array) stream)
493    (dotimes (i (length code-array))
494      (write-u1 (svref code-array i) stream)))
495  (write-attributes (code-attributes code) stream))
496
497(defun make-code-attribute (method)
498  (%make-code-attribute :max-locals (method-arg-count method)))
499
500(defun code-add-attribute (code attribute)
501  (push attribute (code-attributes code)))
502
503(defun code-attribute (code name)
504  (find name (code-attributes code)
505        :test #'string= :key #'attribute-name))
506
507
508
509(defvar *current-code-attribute*)
510
511(defun save-code-specials (code)
512  (setf (code-code code) *code*
513        (code-max-locals code) *registers-allocated*
514        (code-exception-handlers code) *handlers*
515        (code-current-local code) *register*))
516
517(defun restore-code-specials (code)
518  (setf *code* (code-code code)
519        *registers-allocated* (code-max-locals code)
520        *register* (code-current-local code)))
521
522(defmacro with-code-to-method ((method &key safe-nesting) &body body)
523  (let ((m (gensym))
524        (c (gensym)))
525    `(progn
526       ,@(when safe-nesting
527           `((when *current-code-attribute*
528               (save-code-specials *current-code-attribute*))))
529       (let* ((,m ,method)
530              (,c (method-attribute ,m "Code"))
531              (*code* (code-code ,c))
532              (*registers-allocated* (code-max-locals ,c))
533              (*register* (code-current-local ,c))
534              (*current-code-attribute* ,c))
535         ,@body
536         (setf (code-code ,c) *code*
537               (code-exception-handlers ,c) *handlers*
538               (code-max-locals ,c) *registers-allocated*))
539       ,@(when safe-nesting
540           `((when *current-code-attribute*
541               (restore-code-specials *current-code-attribute*)))))))
542
543(defstruct (exceptions-attribute (:constructor make-exceptions)
544                                 (:conc-name exceptions-)
545                                 (:include attribute
546                                           (name "Exceptions")
547                                           (finalizer #'finalize-exceptions)
548                                           (writer #'write-exceptions)))
549  exceptions)
550
551(defun finalize-exceptions (exceptions code class)
552  (dolist (exception (exceptions-exceptions exceptions))
553    ;; no need to finalize `catch-type': it's already the index required
554    (setf (exception-start-pc exception)
555          (code-label-offset code (exception-start-pc exception))
556          (exception-end-pc exception)
557          (code-label-offset code (exception-end-pc exception))
558          (exception-handler-pc exception)
559          (code-label-offset code (exception-handler-pc exception))
560          (exception-catch-type exception)
561          (pool-add-string (class-file-constants class)
562                           (exception-catch-type exception))))
563  ;;(finalize-attributes (exceptions-attributes exception) exceptions class)
564  )
565
566
567(defun write-exceptions (exceptions stream)
568  ; number of entries
569  (write-u2 (length (exceptions-exceptions exceptions)) stream)
570  (dolist (exception (exceptions-exceptions exceptions))
571    (write-u2 (exception-start-pc exception) stream)
572    (write-u2 (exception-end-pc exception) stream)
573    (write-u2 (exception-handler-pc exception) stream)
574    (write-u2 (exception-catch-type exception) stream)))
575
576(defun code-add-exception (code start end handler type)
577  (when (null (code-attribute code "Exceptions"))
578    (code-add-attribute code (make-exceptions)))
579  (push (make-exception :start-pc start
580                        :end-pc end
581                        :handler-pc handler
582                        :catch-type type)
583        (exceptions-exceptions (code-attribute code "Exceptions"))))
584
585(defstruct exception
586  start-pc    ;; label target
587  end-pc      ;; label target
588  handler-pc  ;; label target
589  catch-type  ;; a string for a specific type, or NIL for all
590  )
591
592(defstruct (source-file-attribute (:conc-name source-)
593                                  (:include attribute
594                                            (name "SourceFile")))
595  filename)
596
597(defstruct (line-numbers-attribute (:include attribute
598                                             (name "LineNumberTable")))
599  line-numbers)
600
601(defstruct line-number
602  start-pc
603  line)
604
605(defstruct (local-variables-attribute (:conc-name local-var-)
606                                      (:include attribute
607                                                (name "LocalVariableTable")))
608  locals)
609
610(defstruct (local-variable (:conc-name local-))
611  start-pc
612  length
613  name
614  descriptor
615  index)
616
617#|
618
619;; this is the minimal sequence we need to support:
620
621;;  create a class file structure
622;;  add methods
623;;  add code to the methods, switching from one method to the other
624;;  finalize the methods, one by one
625;;  write the class file
626
627to support the sequence above, we probably need to
628be able to
629
630- find methods by signature
631- find the method's code attribute
632- add code to the code attribute
633- finalize the code attribute contents (blocking it for further addition)
634-
635
636
637|#
638
Note: See TracBrowser for help on using the repository browser.