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

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

Fix typo to restore build.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 33.3 KB
Line 
1;;; jvm-class-file.lisp
2;;;
3;;; Copyright (C) 2010 Erik Huelsmann
4;;; $Id: jvm-class-file.lisp 12788 2010-07-06 22:36:46Z 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  "Maps a symbolic primitive type name to its Java string representation."
62  (case type
63    (:int        "I")
64    (:long       "J")
65    (:float      "F")
66    (:double     "D")
67    (:boolean    "Z")
68    (:char       "C")
69    (:byte       "B")
70    (:short      "S")
71    ((nil :void) "V")))
72
73
74#|
75
76The `class-name' facility helps to abstract from "this instruction takes
77a reference" and "this instruction takes a class name". We simply pass
78the class name around and the instructions themselves know which
79representation to use.
80
81|#
82
83(defstruct (class-name (:conc-name class-)
84                       (:constructor %make-class-name))
85  name-internal
86  ref
87  array-ref)
88
89(defun make-class-name (name)
90  "Creates a `class-name' structure for the class or interface `name'.
91
92`name' should be specified using Java representation, which is converted
93to 'internal' (JVM) representation by this function."
94  (setf name (substitute #\/ #\. name))
95  (%make-class-name :name-internal name
96                    :ref (concatenate 'string "L" name ";")
97                    :array-ref (concatenate 'string "[L" name ";")))
98
99(defmacro define-class-name (symbol java-dotted-name &optional documentation)
100  "Convenience macro to define constants for `class-name' structures,
101initialized from the `java-dotted-name'."
102  `(defconstant ,symbol (make-class-name ,java-dotted-name)
103     ,documentation))
104
105(define-class-name +java-object+ "java.lang.Object")
106(define-class-name +java-string+ "java.lang.String")
107(define-class-name +!lisp-object+ "org.armedbear.lisp.LispObject")
108(define-class-name +!lisp-simple-string+ "org.armedbear.lisp.SimpleString")
109(define-class-name +lisp+ "org.armedbear.lisp.Lisp")
110(define-class-name +lisp-nil+ "org.armedbear.lisp.Nil")
111(define-class-name +lisp-class+ "org.armedbear.lisp.LispClass")
112(define-class-name +!lisp-symbol+ "org.armedbear.lisp.Symbol")
113(define-class-name +!lisp-thread+ "org.armedbear.lisp.LispThread")
114(define-class-name +lisp-closure-binding+ "org.armedbear.lisp.ClosureBinding")
115(define-class-name +!lisp-integer+ "org.armedbear.lisp.Integer")
116(define-class-name +!lisp-fixnum+ "org.armedbear.lisp.Fixnum")
117(define-class-name +!lisp-bignum+ "org.armedbear.lisp.Bignum")
118(define-class-name +!lisp-single-float+ "org.armedbear.lisp.SingleFloat")
119(define-class-name +!lisp-double-float+ "org.armedbear.lisp.DoubleFloat")
120(define-class-name +!lisp-cons+ "org.armedbear.lisp.Cons")
121(define-class-name +lisp-load+ "org.armedbear.lisp.Load")
122(define-class-name +!lisp-character+ "org.armedbear.lisp.Character")
123(define-class-name +!lisp-simple-vector+ "org.armedbear.lisp.SimpleVector")
124(define-class-name +!lisp-abstract-string+ "org.armedbear.lisp.AbstractString")
125(define-class-name +!lisp-abstract-vector+ "org.armedbear.lisp.AbstractVector")
126(define-class-name +!lisp-abstract-bit-vector+
127    "org.armedbear.lisp.AbstractBitVector")
128(define-class-name +!lisp-environment+ "org.armedbear.lisp.Environment")
129(define-class-name +!lisp-special-binding+ "org.armedbear.lisp.SpecialBinding")
130(define-class-name +lisp-special-bindings-mark+
131    "org.armedbear.lisp.SpecialBindingsMark")
132(define-class-name +!lisp-throw+ "org.armedbear.lisp.Throw")
133(define-class-name +!lisp-return+ "org.armedbear.lisp.Return")
134(define-class-name +!lisp-go+ "org.armedbear.lisp.Go")
135(define-class-name +!lisp-primitive+ "org.armedbear.lisp.Primitive")
136(define-class-name +!lisp-eql-hash-table+ "org.armedbear.lisp.EqlHashTable")
137(define-class-name +lisp-package+ "org.armedbear.lisp.Package")
138(define-class-name +lisp-readtable+ "org.armedbear.lisp.Readtable")
139(define-class-name +lisp-stream+ "org.armedbear.lisp.Stream")
140(define-class-name +lisp-closure+ "org.armedbear.lisp.Closure")
141(define-class-name +lisp-compiled-closure+ "org.armedbear.lisp.CompiledClosure")
142(define-class-name +!lisp-closure-parameter+
143    "org.armedbear.lisp.Closure$Parameter")
144(define-class-name +!fasl-loader+ "org.armedbear.lisp.FaslClassLoader")
145
146#|
147
148Lisp-side descriptor representation:
149
150 - list: a list starting with a method return value, followed by
151     the argument types
152 - keyword: the primitive type associated with that keyword
153 - class-name structure instance: the class-ref value
154
155The latter two can be converted to a Java representation using
156the `internal-field-ref' function, the former is to be fed to
157`descriptor'.
158
159|#
160
161(defun internal-field-type (field-type)
162  "Returns a string containing the JVM-internal representation
163of `field-type', which should either be a symbol identifying a primitive
164type, or a `class-name' structure identifying a class or interface."
165  (if (symbolp field-type)
166      (map-primitive-type field-type)
167      (class-name-internal field-type)))
168
169(defun internal-field-ref (field-type)
170  "Returns a string containing the JVM-internal representation of a reference
171to `field-type', which should either be a symbol identifying a primitive
172type, or a `class-name' structure identifying a class or interface."
173  (if (symbolp field-type)
174      (map-primitive-type field-type)
175      (class-ref field-type)))
176
177(defun descriptor (return-type &rest argument-types)
178  "Returns a string describing the `return-type' and `argument-types'
179in JVM-internal representation."
180  (format nil "(~{~A~})~A" (mapcar #'internal-field-ref argument-types)
181          (internal-field-type return-type)))
182
183
184(defstruct pool
185  ;; `count' contains a reference to the last-used slot (0 being empty)
186  ;; "A constant pool entry is considered valid if it has
187  ;; an index greater than 0 (zero) and less than pool-count"
188  (count 0)
189  entries-list
190  ;; the entries hash stores raw values, except in case of string and
191  ;; utf8, because both are string values
192  (entries (make-hash-table :test #'equal :size 2048 :rehash-size 2.0)))
193
194
195(defstruct constant
196  "Structure to be included in all constant sub-types."
197  tag
198  index)
199
200(defparameter +constant-type-map+
201  '((:class          7 1)
202    (:field-ref      9 1)
203    (:method-ref    10 1)
204    ;; (:interface-method-ref 11)
205    (:string         8 1)
206    (:integer        3 1)
207    (:float          4 1)
208    (:long           5 2)
209    (:double         6 2)
210    (:name-and-type 12 1)
211    (:utf8           1 1)))
212
213(defstruct (constant-class (:constructor make-constant-class (index name-index))
214                           (:include constant
215                                     (tag 7)))
216  name-index)
217
218(defstruct (constant-member-ref (:constructor
219                                 %make-constant-member-ref
220                                     (tag index class-index name/type-index))
221                                (:include constant))
222  class-index
223  name/type-index)
224
225(declaim (inline make-constant-field-ref make-constant-method-ref
226                 make-constant-interface-method-ref))
227(defun make-constant-field-ref (index class-index name/type-index)
228  "Creates a `constant-member-ref' instance containing a field reference."
229  (%make-constant-member-ref 9 index class-index name/type-index))
230
231(defun make-constant-method-ref (index class-index name/type-index)
232  "Creates a `constant-member-ref' instance containing a method reference."
233  (%make-constant-member-ref 10 index class-index name/type-index))
234
235(defun make-constant-interface-method-ref (index class-index name/type-index)
236  "Creates a `constant-member-ref' instance containing an
237interface-method reference."
238  (%make-constant-member-ref 11 index class-index name/type-index))
239
240(defstruct (constant-string (:constructor
241                             make-constant-string (index value-index))
242                            (:include constant
243                                      (tag 8)))
244  value-index)
245
246(defstruct (constant-float/int (:constructor
247                                %make-constant-float/int (tag index value))
248                               (:include constant))
249  value)
250
251(declaim (inline make-constant-float make-constant-int))
252(defun make-constant-float (index value)
253  "Creates a `constant-float/int' structure instance containing a float."
254  (%make-constant-float/int 4 index value))
255
256(defun make-constant-int (index value)
257  "Creates a `constant-float/int' structure instance containing an int."
258  (%make-constant-float/int 3 index value))
259
260(defstruct (constant-double/long (:constructor
261                                  %make-constant-double/long (tag index value))
262                                 (:include constant))
263  value)
264
265(declaim (inline make-constant-double make-constant-float))
266(defun make-constant-double (index value)
267  "Creates a `constant-double/long' structure instance containing a double."
268  (%make-constant-double/long 6 index value))
269
270(defun make-constant-long (index value)
271  "Creates a `constant-double/long' structure instance containing a long."
272  (%make-constant-double/long 5 index value))
273
274(defstruct (constant-name/type (:constructor
275                                make-constant-name/type (index
276                                                         name-index
277                                                         descriptor-index))
278                               (:include constant
279                                         (tag 12)))
280  name-index
281  descriptor-index)
282
283(defstruct (constant-utf8 (:constructor make-constant-utf8 (index value))
284                          (:include constant
285                                    (tag 11)))
286  value)
287
288
289(defun pool-add-class (pool class)
290  "Returns the index of the constant-pool class item for `class'.
291
292`class' must be an instance of `class-name'."
293  (let ((entry (gethash class (pool-entries pool))))
294    (unless entry
295      (setf entry
296            (make-constant-class (incf (pool-count pool))
297                                 (pool-add-utf8 pool
298                                                (class-name-internal class)))
299            (gethash class (pool-entries pool)) entry)
300      (push entry (pool-entries-list pool)))
301    (constant-index entry)))
302
303(defun pool-add-field-ref (pool class name type)
304  "Returns the index of the constant-pool item which denotes a reference
305to the `name' field of the `class', being of `type'.
306
307`class' should be an instance of `class-name'.
308`name' is a string.
309`type' is a field-type (see `internal-field-type')"
310  (let ((entry (gethash (acons name type class) (pool-entries pool))))
311    (unless entry
312      (setf entry (make-constant-field-ref (incf (pool-count pool))
313                                           (pool-add-class pool class)
314                                           (pool-add-name/type pool name type))
315            (gethash (acons name type class) (pool-entries pool)) entry)
316      (push entry (pool-entries-list pool)))
317    (constant-index entry)))
318
319(defun pool-add-method-ref (pool class name type)
320  "Returns the index of the constant-pool item which denotes a reference
321to the method with `name' in `class', which is of `type'.
322
323Here, `type' is a method descriptor, which defines the argument types
324and return type. `class' is an instance of `class-name'."
325  (let ((entry (gethash (acons name type class) (pool-entries pool))))
326    (unless entry
327      (setf entry (make-constant-method-ref (incf (pool-count pool))
328                                            (pool-add-class pool class)
329                                            (pool-add-name/type pool name type))
330            (gethash (acons name type class) (pool-entries pool)) entry)
331      (push entry (pool-entries-list pool)))
332    (constant-index entry)))
333
334(defun pool-add-interface-method-ref (pool class name type)
335  "Returns the index of the constant-pool item which denotes a reference to
336the method `name' in the interface `class', which is of `type'.
337
338See `pool-add-method-ref' for remarks."
339  (let ((entry (gethash (acons name type class) (pool-entries pool))))
340    (unless entry
341      (setf entry
342            (make-constant-interface-method-ref (incf (pool-count pool))
343                                                (pool-add-class pool class)
344                                                (pool-add-name/type pool
345                                                                    name type))
346            (gethash (acons name type class) (pool-entries pool)) entry)
347      (push entry (pool-entries-list pool)))
348    (constant-index entry)))
349
350(defun pool-add-string (pool string)
351  "Returns the index of the constant-pool item denoting the string."
352  (let ((entry (gethash (cons 8 string) ;; 8 == string-tag
353                        (pool-entries pool))))
354    (unless entry
355      (setf entry (make-constant-string (incf (pool-count pool))
356                                        (pool-add-utf8 pool string))
357            (gethash (cons 8 string) (pool-entries pool)) entry)
358      (push entry (pool-entries-list pool)))
359    (constant-index entry)))
360
361(defun pool-add-int (pool int)
362  "Returns the index of the constant-pool item denoting the int."
363  (let ((entry (gethash (cons 3 int) (pool-entries pool))))
364    (unless entry
365      (setf entry (make-constant-int (incf (pool-count pool)) int)
366            (gethash (cons 3 int) (pool-entries pool)) entry)
367      (push entry (pool-entries-list pool)))
368    (constant-index entry)))
369
370(defun pool-add-float (pool float)
371  "Returns the index of the constant-pool item denoting the float."
372  (let ((entry (gethash (cons 4 float) (pool-entries pool))))
373    (unless entry
374      (setf entry (make-constant-float (incf (pool-count pool)) float)
375            (gethash (cons 4 float) (pool-entries pool)) entry)
376      (push entry (pool-entries-list pool)))
377    (constant-index entry)))
378
379(defun pool-add-long (pool long)
380  "Returns the index of the constant-pool item denoting the long."
381  (let ((entry (gethash (cons 5 long) (pool-entries pool))))
382    (unless entry
383      (setf entry (make-constant-long (incf (pool-count pool)) long)
384            (gethash (cons 5 long) (pool-entries pool)) entry)
385      (push entry (pool-entries-list pool))
386      (incf (pool-count pool))) ;; double index increase; long takes 2 slots
387    (constant-index entry)))
388
389(defun pool-add-double (pool double)
390  "Returns the index of the constant-pool item denoting the double."
391  (let ((entry (gethash (cons 6 double) (pool-entries pool))))
392    (unless entry
393      (setf entry (make-constant-double (incf (pool-count pool)) double)
394            (gethash (cons 6 double) (pool-entries pool)) entry)
395      (push entry (pool-entries-list pool))
396      (incf (pool-count pool))) ;; double index increase; 'double' takes 2 slots
397    (constant-index entry)))
398
399(defun pool-add-name/type (pool name type)
400  "Returns the index of the constant-pool item denoting
401the name/type identifier."
402  (let ((entry (gethash (cons name type) (pool-entries pool)))
403        (internal-type (if (listp type)
404                           (apply #'descriptor type)
405                           (internal-field-ref type))))
406    (unless entry
407      (setf entry (make-constant-name/type (incf (pool-count pool))
408                                           (pool-add-utf8 pool name)
409                                           (pool-add-utf8 pool internal-type))
410            (gethash (cons name type) (pool-entries pool)) entry)
411      (push entry (pool-entries-list pool)))
412    (constant-index entry)))
413
414(defun pool-add-utf8 (pool utf8-as-string)
415  "Returns the index of the textual value that will be stored in the
416class file as UTF-8 encoded data."
417  (let ((entry (gethash (cons 11 utf8-as-string) ;; 11 == utf8
418                        (pool-entries pool))))
419    (unless entry
420      (setf entry (make-constant-utf8 (incf (pool-count pool)) utf8-as-string)
421            (gethash (cons 11 utf8-as-string) (pool-entries pool)) entry)
422      (push entry (pool-entries-list pool)))
423    (constant-index entry)))
424
425(defstruct (class-file (:constructor
426                        !make-class-file (class superclass access-flags)))
427  (constants (make-pool))
428  access-flags
429  class
430  superclass
431  ;; interfaces
432  fields
433  methods
434  attributes)
435
436(defun class-add-field (class field)
437  "Adds a `field' created by `make-field'."
438  (push field (class-file-fields class)))
439
440(defun class-field (class name)
441  "Finds a field by name." ;; ### strictly speaking, a field is uniquely
442  ;; identified by its name and type, not by the name alone.
443  (find name (class-file-fields class)
444        :test #'string= :key #'field-name))
445
446(defun class-add-method (class method)
447  "Adds a `method' to `class'; the method must have been created using
448`make-method'."
449  (push method (class-file-methods class)))
450
451(defun class-methods-by-name (class name)
452  "Returns all methods which have `name'."
453  (remove name (class-file-methods class)
454          :test-not #'string= :key #'method-name))
455
456(defun class-method (class name return &rest args)
457  "Return the method which is (uniquely) identified by its name AND descriptor."
458  (let ((return-and-args (cons return args)))
459    (find-if #'(lambda (c)
460                 (and (string= (method-name c) name)
461                      (equal (method-descriptor c) return-and-args)))
462             (class-file-methods class))))
463
464(defun class-add-attribute (class attribute)
465  "Adds `attribute' to the class; attributes must be instances of
466structure classes which include the `attribute' structure class."
467  (push attribute (class-file-attributes class)))
468
469(defun class-attribute (class name)
470  "Returns the attribute which is named `name'."
471  (find name (class-file-attributes class)
472        :test #'string= :key #'attribute-name))
473
474
475(defun finalize-class-file (class)
476  "Transforms the representation of the class-file from one
477which allows easy modification to one which works best for serialization.
478
479The class can't be modified after serialization."
480  ;; constant pool contains constants finalized on addition;
481  ;; no need for additional finalization
482
483  (setf (class-file-access-flags class)
484        (map-flags (class-file-access-flags class)))
485  (setf (class-file-class class)
486        (pool-add-class (class-file-constants class)
487                        (class-file-class class)))
488  ;;  (finalize-interfaces)
489  (dolist (field (class-file-fields class))
490    (finalize-field field class))
491  (dolist (method (class-file-methods class))
492    (finalize-method method class))
493  ;; top-level attributes (no parent attributes to refer to)
494  (finalize-attributes (class-file-attributes class) nil class))
495
496(defun !write-class-file (class stream)
497  "Serializes `class' to `stream', after it has been finalized."
498
499  ;; header
500  (write-u4 #xCAFEBABE stream)
501  (write-u2 3 stream)
502  (write-u2 45 stream)
503
504   ;; constants pool
505  (write-constants (class-file-constants class) stream)
506  ;; flags
507  (write-u2  (class-file-access-flags class) stream)
508  ;; class name
509  (write-u2 (class-file-class class) stream)
510  ;; superclass
511  (write-u2 (class-file-superclass class) stream)
512
513  ;; interfaces
514  (write-u2 0 stream)
515
516  ;; fields
517  (write-u2 (length (class-file-fields class)) stream)
518  (dolist (field (class-file-fields class))
519    (!write-field field stream))
520
521  ;; methods
522  (write-u2 (length (class-file-methods class)) stream)
523  (dolist (method (class-file-methods class))
524    (!write-method method stream))
525
526  ;; attributes
527  (write-attributes (class-file-attributes class) stream))
528
529(defun write-constants (constants stream)
530  (write-u2 (pool-count constants) stream)
531  (dolist (entry (reverse (pool-entries-list constants)))
532    (let ((tag (constant-tag entry)))
533    (write-u1 tag stream)
534    (case tag
535      (1         ; UTF8
536       (write-utf8 (constant-utf8-value entry) stream))
537      ((3 4)     ; int
538       (write-u4 (constant-float/int-value entry) stream))
539      ((5 6)     ; long double
540       (write-u4 (logand (ash (constant-double/long-value entry) -32)
541                         #xFFFFffff) stream)
542       (write-u4 (logand (constant-double/long-value entry) #xFFFFffff) stream))
543      ((9 10 11) ; fieldref methodref InterfaceMethodref
544       (write-u2 (constant-member-ref-class-index entry) stream)
545       (write-u2 (constant-member-ref-name/type-index entry) stream))
546      (12        ; nameAndType
547       (write-u2 (constant-name/type-name-index entry) stream)
548       (write-u2 (constant-name/type-descriptor-index entry) stream))
549      (7         ; class
550       (write-u2 (constant-class-name-index entry) stream))
551      (8         ; string
552       (write-u2 (constant-string-value-index entry) stream))
553      (t
554       (error "write-constant-pool-entry unhandled tag ~D~%" tag))))))
555
556#|
557
558ABCL doesn't use interfaces, so don't implement it here at this time
559
560(defstruct interface)
561
562|#
563
564
565(defparameter +access-flags-map+
566  '((:public       #x0001)
567    (:private      #x0002)
568    (:protected    #x0004)
569    (:static       #x0008)
570    (:final        #x0010)
571    (:volatile     #x0040)
572    (:synchronized #x0020)
573    (:transient    #x0080)
574    (:native       #x0100)
575    (:abstract     #x0400)
576    (:strict       #x0800)))
577
578(defun map-flags (flags)
579  "Calculates the bitmap of the flags from a list of symbols."
580  (reduce #'(lambda (y x)
581              (logior (or (when (member (car x) flags)
582                            (second x))
583                          0) y))
584          +access-flags-map+
585          :initial-value 0))
586
587(defstruct (field (:constructor %make-field))
588  access-flags
589  name
590  descriptor
591  attributes)
592
593(defun make-field (name type &key (flags '(:public)))
594  (%make-field :access-flags flags
595               :name name
596               :descriptor type))
597
598(defun field-add-attribute (field attribute)
599  (push attribute (field-attributes field)))
600
601(defun field-attribute (field name)
602  (find name (field-attributes field)
603        :test #'string= :key #'attribute-name))
604
605(defun finalize-field (field class)
606  (let ((pool (class-file-constants class)))
607    (setf (field-access-flags field)
608          (map-flags (field-access-flags field))
609          (field-descriptor field)
610          (pool-add-utf8 pool (internal-field-type (field-descriptor field)))
611          (field-name field)
612          (pool-add-utf8 pool (field-name field))))
613  (finalize-attributes (field-attributes field) nil class))
614
615(defun !write-field (field stream)
616  (write-u2 (field-access-flags field) stream)
617  (write-u2 (field-name field) stream)
618  (write-u2 (field-descriptor field) stream)
619  (write-attributes (field-attributes field) stream))
620
621
622(defstruct (method (:constructor %!make-method))
623  access-flags
624  name
625  descriptor
626  attributes)
627
628
629(defun map-method-name (name)
630  "Methods should be identified by strings containing their names, or,
631be one of two keyword identifiers to identify special methods:
632
633 * :class-constructor
634 * :constructor
635"
636  (cond
637    ((eq name :class-constructor)
638     "<clinit>")
639    ((eq name :constructor)
640     "<init>")
641    (t name)))
642
643(defun !make-method (name return args &key (flags '(:public)))
644  (%make-method :descriptor (cons return args)
645                :access-flags flags
646                :name name))
647
648(defun method-add-attribute (method attribute)
649  (push attribute (method-attributes method)))
650
651(defun method-add-code (method)
652  "Creates an (empty) 'Code' attribute for the method."
653  (method-add-attribute
654   (make-code-attribute (+ (length (cdr (method-descriptor method)))
655                           (if (member :static (method-access-flags method))
656                               0 1))))) ;; 1 == implicit 'this'
657
658(defun method-attribute (method name)
659  (find name (method-attributes method)
660        :test #'string= :key #'attribute-name))
661
662
663(defun finalize-method (method class)
664  (let ((pool (class-file-constants class)))
665    (setf (method-access-flags method)
666          (map-flags (method-access-flags method))
667          (method-descriptor method)
668          (pool-add-utf8 pool (apply #'descriptor (method-descriptor method)))
669          (method-name method)
670          (pool-add-utf8 pool (map-method-name (method-name method)))))
671  (finalize-attributes (method-attributes method) nil class))
672
673
674(defun !write-method (method stream)
675  (write-u2 (method-access-flags method) stream)
676  (write-u2 (method-name method) stream)
677  (write-u2 (method-descriptor method) stream)
678  (write-attributes (method-attributes method) stream))
679
680(defstruct attribute
681  name
682
683  ;; not in the class file:
684  finalizer  ;; function of 3 arguments: the attribute, parent and class-file
685  writer     ;; function of 2 arguments: the attribute and the output stream
686  )
687
688(defun finalize-attributes (attributes att class)
689  (dolist (attribute attributes)
690    ;; assure header: make sure 'name' is in the pool
691    (setf (attribute-name attribute)
692          (pool-add-string (class-file-constants class)
693                           (attribute-name attribute)))
694    ;; we're saving "root" attributes: attributes which have no parent
695    (funcall (attribute-finalizer attribute) attribute att class)))
696
697(defun write-attributes (attributes stream)
698  (write-u2 (length attributes) stream)
699  (dolist (attribute attributes)
700    (write-u2 (attribute-name attribute) stream)
701    ;; set up a bulk catcher for (UNSIGNED-BYTE 8)
702    ;; since we need to know the attribute length (excluding the header)
703    (let ((local-stream (sys::%make-byte-array-output-stream)))
704      (funcall (attribute-writer attribute) attribute local-stream)
705      (let ((array (sys::%get-output-stream-array local-stream)))
706        (write-u2 (length array) stream)
707        (write-sequence array stream)))))
708
709
710
711(defstruct (code-attribute (:conc-name code-)
712                           (:include attribute
713                                     (name "Code")
714                                     (finalizer #'!finalize-code)
715                                     (writer #'!write-code))
716                           (:constructor %make-code-attribute))
717  max-stack
718  max-locals
719  code
720  attributes
721  ;; labels contains offsets into the code array after it's finalized
722  (labels (make-hash-table :test #'eq))
723
724  ;; fields not in the class file start here
725  current-local ;; used for handling nested WITH-CODE-TO-METHOD blocks
726  )
727
728
729(defun code-label-offset (code label)
730  (gethash label (code-labels code)))
731
732(defun (setf code-label-offset) (offset code label)
733  (setf (gethash label (code-labels code)) offset))
734
735(defun !finalize-code (code class)
736  (let ((c (coerce (resolve-instructions (code-code code)) 'vector)))
737    (setf (code-max-stack code) (analyze-stack c)
738          (code-code code) (code-bytes c)))
739  (finalize-attributes (code-attributes code) code class))
740
741(defun !write-code (code stream)
742  (write-u2 (code-max-stack code) stream)
743  (write-u2 (code-max-locals code) stream)
744  (let ((code-array (code-code code)))
745    (write-u4 (length code-array) stream)
746    (dotimes (i (length code-array))
747      (write-u1 (svref code-array i) stream)))
748  (write-attributes (code-attributes code) stream))
749
750(defun make-code-attribute (arg-count)
751  "Creates an empty 'Code' attribute for a method which takes
752`arg-count` parameters, including the implicit `this` parameter."
753  (%make-code-attribute :max-locals arg-count))
754
755(defun code-add-attribute (code attribute)
756  (push attribute (code-attributes code)))
757
758(defun code-attribute (code name)
759  (find name (code-attributes code)
760        :test #'string= :key #'attribute-name))
761
762
763
764(defvar *current-code-attribute*)
765
766(defun save-code-specials (code)
767  (setf (code-code code) *code*
768        (code-max-locals code) *registers-allocated*
769        (code-exception-handlers code) *handlers*
770        (code-current-local code) *register*))
771
772(defun restore-code-specials (code)
773  (setf *code* (code-code code)
774        *registers-allocated* (code-max-locals code)
775        *register* (code-current-local code)))
776
777(defmacro with-code-to-method ((method &key safe-nesting) &body body)
778  (let ((m (gensym))
779        (c (gensym)))
780    `(progn
781       ,@(when safe-nesting
782           `((when *current-code-attribute*
783               (save-code-specials *current-code-attribute*))))
784       (let* ((,m ,method)
785              (,c (method-attribute ,m "Code"))
786              (*code* (code-code ,c))
787              (*registers-allocated* (code-max-locals ,c))
788              (*register* (code-current-local ,c))
789              (*current-code-attribute* ,c))
790         ,@body
791         (setf (code-code ,c) *code*
792               (code-exception-handlers ,c) *handlers*
793               (code-max-locals ,c) *registers-allocated*))
794       ,@(when safe-nesting
795           `((when *current-code-attribute*
796               (restore-code-specials *current-code-attribute*)))))))
797
798(defstruct (exceptions-attribute (:constructor make-exceptions)
799                                 (:conc-name exceptions-)
800                                 (:include attribute
801                                           (name "Exceptions")
802                                           (finalizer #'finalize-exceptions)
803                                           (writer #'write-exceptions)))
804  exceptions)
805
806(defun finalize-exceptions (exceptions code class)
807  (dolist (exception (exceptions-exceptions exceptions))
808    ;; no need to finalize `catch-type': it's already the index required
809    (setf (exception-start-pc exception)
810          (code-label-offset code (exception-start-pc exception))
811          (exception-end-pc exception)
812          (code-label-offset code (exception-end-pc exception))
813          (exception-handler-pc exception)
814          (code-label-offset code (exception-handler-pc exception))
815          (exception-catch-type exception)
816          (pool-add-string (class-file-constants class)
817                           (exception-catch-type exception))))
818  ;;(finalize-attributes (exceptions-attributes exception) exceptions class)
819  )
820
821
822(defun write-exceptions (exceptions stream)
823  ; number of entries
824  (write-u2 (length (exceptions-exceptions exceptions)) stream)
825  (dolist (exception (exceptions-exceptions exceptions))
826    (write-u2 (exception-start-pc exception) stream)
827    (write-u2 (exception-end-pc exception) stream)
828    (write-u2 (exception-handler-pc exception) stream)
829    (write-u2 (exception-catch-type exception) stream)))
830
831(defun code-add-exception (code start end handler type)
832  (when (null (code-attribute code "Exceptions"))
833    (code-add-attribute code (make-exceptions)))
834  (push (make-exception :start-pc start
835                        :end-pc end
836                        :handler-pc handler
837                        :catch-type type)
838        (exceptions-exceptions (code-attribute code "Exceptions"))))
839
840(defstruct exception
841  start-pc    ;; label target
842  end-pc      ;; label target
843  handler-pc  ;; label target
844  catch-type  ;; a string for a specific type, or NIL for all
845  )
846
847(defstruct (source-file-attribute (:conc-name source-)
848                                  (:include attribute
849                                            (name "SourceFile")))
850  filename)
851
852(defstruct (line-numbers-attribute (:include attribute
853                                             (name "LineNumberTable")))
854  line-numbers)
855
856(defstruct line-number
857  start-pc
858  line)
859
860(defstruct (local-variables-attribute (:conc-name local-var-)
861                                      (:include attribute
862                                                (name "LocalVariableTable")))
863  locals)
864
865(defstruct (local-variable (:conc-name local-))
866  start-pc
867  length
868  name
869  descriptor
870  index)
871
872#|
873
874;; this is the minimal sequence we need to support:
875
876;;  create a class file structure
877;;  add methods
878;;  add code to the methods, switching from one method to the other
879;;  finalize the methods, one by one
880;;  write the class file
881
882to support the sequence above, we probably need to
883be able to
884
885- find methods by signature
886- find the method's code attribute
887- add code to the code attribute
888- finalize the code attribute contents (blocking it for further addition)
889-
890
891
892|#
893
Note: See TracBrowser for help on using the repository browser.