(module class-file mzscheme
(require (planet "inspector.ss" ("dherman" "inspector.plt" 1)))
(require (planet "hierarchy.ss" ("dherman" "struct.plt" 1)))
(require (planet "io.ss" ("dherman" "io.plt" 1)))
(require (lib "contract.ss"))
(require (lib "match.ss"))
(require (lib "list.ss" "srfi" "1"))
(require (lib "etc.ss"))
(with-public-inspector
(define-hierarchy/provide/contract
(info ()
(class-info ((name-index natural-number/c)))
(ref-info ((class-index natural-number/c)
(name-and-type-index natural-number/c))
(field-ref-info ())
(method-ref-info ())
(interface-method-ref-info ()))
(string-info ((string-index natural-number/c)))
(integer-info ((value integer?)))
(float-info ((bytes bytes?)))
(long-info ((high-bytes bytes?)
(low-bytes bytes?)))
(double-info ((high-bytes bytes?)
(low-bytes bytes?)))
(name-and-type-info ((name-index natural-number/c)
(descriptor-index natural-number/c)))
(utf8-info ((length natural-number/c)
(bytes bytes?)))
(inner-class-entry ((inner-class-info-index natural-number/c)
(outer-class-info-index natural-number/c)
(inner-name-index natural-number/c)
(inner-class-access-flags integer?))) (element-info ((access-flags integer?) (name-index natural-number/c)
(descriptor-index natural-number/c)
(attributes-count natural-number/c)
(attributes (listof attribute-info?))) (field-info ())
(method-info ()))
(attribute-info ()
(unsupported-attribute-info ((length natural-number/c)
(bytes bytes?)))
(constant-value-attribute-info ((value-index natural-number/c)))
(code-attribute-info ()) (exceptions-attribute-info ((count natural-number/c)
(exceptions (listof natural-number/c))))
(inner-classes-attribute-info ())
(synthetic-attribute-info ())
(source-file-attribute-info ())
(line-number-table-attribute-info ())
(local-variable-table-attribute-info ())
(deprecated-attribute-info ()))))
(define-struct class-file (pool flags this super interfaces fields methods attributes))
(provide (struct class-file (pool flags this super interfaces fields methods attributes))))
(define (read-constant in)
(let ([type (read-byte in)])
(cond
[(and (<= 1 type *max-constant-type*)
(vector-ref *read-constant-vector* type))
=> (lambda (reader)
(reader in))]
[else
(error 'read-constant "bad constant type: ~a" type)])))
(define (read-class-info in)
(make-class-info (read-integer 2 #f in #t)))
(define (read-field-ref-info in)
(let ([class-index (read-integer 2 #f in #t)]
[name-and-type-index (read-integer 2 #f in #t)])
(make-field-ref-info class-index name-and-type-index)))
(define (read-method-ref-info in)
(let ([class-index (read-integer 2 #f in #t)]
[name-and-type-index (read-integer 2 #f in #t)])
(make-method-ref-info class-index name-and-type-index)))
(define (read-interface-method-ref-info in)
(let ([class-index (read-integer 2 #f in #t)]
[name-and-type-index (read-integer 2 #f in #t)])
(make-interface-method-ref-info class-index name-and-type-index)))
(define (read-string-info in)
(make-string-info (read-integer 2 #f in #t)))
(define (read-integer-info in)
(make-integer-info (read-integer 4 #f in #t)))
(define (read-float-info in)
(make-float-info (read-bytes 4 in)))
(define (read-long-info in)
(let ([high-bytes (read-bytes 4 in)]
[low-bytes (read-bytes 4 in)])
(make-long-info high-bytes low-bytes)))
(define (read-double-info in)
(let ([high-bytes (read-bytes 4 in)]
[low-bytes (read-bytes 4 in)])
(make-double-info high-bytes low-bytes)))
(define (read-name-and-type-info in)
(let ([name-index (read-integer 2 #f in #t)]
[descriptor-index (read-integer 2 #f in #t)])
(make-name-and-type-info name-index descriptor-index)))
(define (read-utf8-info in)
(let* ([len (read-integer 2 #f in #t)]
[bytes (read-bytes len in)])
(make-utf8-info len bytes)))
(define (constant-entry-count constant)
(if (or (long-info? constant) (double-info? constant)) 2 1))
(define (read-constant-pool count in)
(let ([pool (make-vector count #f)])
(let loop ([i 0])
(when (< i count)
(let ([next-constant (read-constant in)])
(vector-set! pool i next-constant)
(loop (+ i (constant-entry-count next-constant))))))
pool))
(define (read-field-info pool)
(lambda (in)
(let* ([access-flags (read-integer 2 #f in #t)]
[name-index (read-integer 2 #f in #t)]
[descriptor-index (read-integer 2 #f in #t)]
[attributes-count (read-integer 2 #f in #t)]
[attributes (build-list attributes-count
(lambda (i) ((read-attribute-info pool) in)))])
(make-field-info access-flags name-index descriptor-index attributes-count attributes))))
(define (read-method-info pool)
(lambda (in)
(let* ([access-flags (read-integer 2 #f in #t)]
[name-index (read-integer 2 #f in #t)]
[descriptor-index (read-integer 2 #f in #t)]
[attributes-count (read-integer 2 #f in #t)]
[attributes (build-list attributes-count
(lambda (i) ((read-attribute-info pool) in)))])
(make-method-info access-flags name-index descriptor-index attributes-count attributes))))
(define (read-attribute-info pool)
(lambda (in)
(let* ([name-index (read-integer 2 #f in #t)]
[name (utf8-info->string (vector-ref pool (sub1 name-index)))])
(match name
["ConstantValue" (read-constant-value-attribute-info in)]
["Exceptions" (read-exceptions-attribute-info in)]
["Deprecated" (read-deprecated-attribute-info in)]
[_ (read-unsupported-attribute-info in)]))))
(define (read-inner-classes-attribute-info in)
(let* ([attribute-length (read-integer 4 #f in #t)]
[count (read-integer 2 #f in #t)]
[classes (build-list count (lambda (i) (read-inner-class-entry in)))])
(make-inner-classes-attribute-info count classes)))
(define (read-inner-class-entry in)
(let* ([inner-class-info-index (read-integer 2 #f in #t)]
[outer-class-info-index (read-integer 2 #f in #t)]
[inner-name-index (read-integer 2 #f in #t)]
[inner-class-access-flags (read-integer 2 #f in #t)])
(make-inner-class-entry inner-class-info-index outer-class-info-index inner-name-index inner-class-access-flags)))
(define (read-exceptions-attribute-info in)
(let* ([attribute-length (read-integer 4 #f in #t)]
[count (read-integer 2 #f in #t)]
[exceptions (build-list count (lambda (i) (read-integer 2 #f in #t)))])
(make-exceptions-attribute-info count exceptions)))
(define (read-constant-value-attribute-info in)
(let ([attribute-length (read-integer 4 #f in #t)])
(unless (= attribute-length 2)
(error 'read-attribute-info
"attribute ConstantValue: expected 2 bytes, found ~a bytes" attribute-length))
(make-constant-value-attribute-info (read-integer 2 #f in #t))))
(define (read-synthetic-attribute-info in)
(let ([attribute-length (read-integer 4 #f in #t)])
(unless (zero? attribute-length)
(error 'read-attribute-info
"attribute Synthetic: expected 0 bytes, found ~a bytes" attribute-length))
(make-synthetic-attribute-info)))
(define (read-deprecated-attribute-info in)
(let ([attribute-length (read-integer 4 #f in #t)])
(unless (zero? attribute-length)
(error 'read-attribute-info
"attribute Deprecated: expected 0 bytes, found ~a bytes" attribute-length))
(make-deprecated-attribute-info)))
(define (read-unsupported-attribute-info in)
(let* ([attribute-length (read-integer 4 #f in #t)]
[info (read-bytes attribute-length in)])
(make-unsupported-attribute-info attribute-length info)))
(define (read-interfaces count in pool)
(map (lambda (i) (vector-ref pool (sub1 i)))
(build-list count (lambda (i) (read-integer 2 #f in #t)))))
(define (read-array count in reader)
(build-vector count (lambda (i) (reader in))))
(define (read-list count in reader)
(build-list count (lambda (i) (reader in))))
(define read-class-file
(opt-lambda ([in (current-input-port)])
(let ([magic (read-integer 4 #f in #t)])
(unless (= magic #xcafebabe)
(error 'read-class-file "bad class file signature: #x~x" magic))
(let* ([minor (read-integer 2 #f in #t)]
[major (read-integer 2 #f in #t)]
[constant-pool-count (read-integer 2 #f in #t)]
[pool (read-constant-pool (sub1 constant-pool-count) in)]
[access-flags (read-integer 2 #f in #t)]
[this-index (read-integer 2 #f in #t)]
[super-index (read-integer 2 #f in #t)]
[interfaces-count (read-integer 2 #f in #t)]
[interfaces (read-interfaces interfaces-count in pool)]
[fields-count (read-integer 2 #f in #t)]
[fields (read-list fields-count in (read-field-info pool))]
[methods-count (read-integer 2 #f in #t)]
[methods (read-list methods-count in (read-method-info pool))]
[attributes-count (read-integer 2 #f in #t)]
[attributes (read-list attributes-count in (read-attribute-info pool))])
(make-class-file pool access-flags this-index super-index interfaces fields methods attributes)))))
(define (utf8-info->string utf8)
(bytes->string/utf-8 (utf8-info-bytes utf8)))
(define *access-flags*
'(public private protected static final super volatile transient native interface abstract strictfp))
(define *access-flags-vector*
(list->vector *access-flags*))
(define access-flag/c
(apply symbols *access-flags*))
(define *read-constant-vector*
(vector #f
read-utf8-info #f
read-integer-info read-float-info read-long-info read-double-info read-class-info read-string-info read-field-ref-info read-method-ref-info read-interface-method-ref-info read-name-and-type-info))
(define *max-constant-type* (sub1 (vector-length *read-constant-vector*)))
(define (extract-access-flags bits)
(filter-map identity
(build-list (vector-length *access-flags-vector*)
(lambda (i)
(and (bit-set? i bits)
(vector-ref *access-flags-vector* i))))))
(provide/contract
[access-flag/c contract?])
(provide/contract
[extract-access-flags (integer? . -> . (listof access-flag/c))]
[utf8-info->string (utf8-info? . -> . string?)]
[read-class-file (() (input-port?) . opt-> . class-file?)]))