Module: java-vm-code-generation Author: Mark Tillotson Copyright: Original Code is Copyright (c) 1995-2004 Functional Objects, Inc. All rights reserved. License: Functional Objects Library Public License Version 1.0 Dual-license: GNU Lesser General Public License Warranty: Distributed WITHOUT WARRANTY OF ANY KIND // model a Java class as we output it (most of the outputing is defered till // the constants pool is complete) format-out ("initing java-emit-class.dylan\n") ; define constant $java-access-public$ = #x0001 ; define constant $java-access-private$ = #x0002 ; define constant $java-access-protected$ = #x0004 ; // not used define constant $java-access-static$ = #x0008 ; define constant $java-access-final$ = #x0010 ; // not used define constant $java-access-sync$ = #x0020 ; define constant $java-access-sup$ = #x0020 ; define constant $java-access-interface$ = #x0200 ; define constant $java-access-abstract$ = #x0400 ; define open generic java-class (obj) => (object) ; define sealed class () sealed constant slot java-class :: , required-init-keyword: java-class: ; sealed constant slot slot-name :: , required-init-keyword: name: ; sealed constant slot slot-type :: , required-init-keyword: type: ; sealed constant slot static? = #f, init-keyword: static?: ; sealed constant slot public? = #t, init-keyword: public?: ; end; define class () sealed constant slot invoke-op :: , required-init-keyword: invoke-op: ; end; /* these need to take large-number of args cases into account? */ define function total-pushes (meth-spec :: ) => (pushes :: ) let function-type :: = meth-spec.slot-type ; let result :: = function-type.java-function-result-type ; result.java-type-words - total-args (meth-spec) end; define function total-args (meth-spec :: ) => (args :: ) let args = if (meth-spec.invoke-op.pops-instance?) 1 else 0 end; let function-type :: = meth-spec.slot-type ; for (arg-type :: in function-type.java-function-arg-types) args := args + arg-type.java-type-words end; args end; define method pop-list (meth-spec :: ) => (list :: ) let arg-types = meth-spec.slot-type.java-function-arg-types ; if (meth-spec.invoke-op.pops-instance?) arg-types := concatenate (vector (meth-spec.java-class), arg-types) end; reverse (map (method (tipe :: ) make (, type-constraint: tipe) end, arg-types)) end; define method push-list (meth-spec :: ) => (list :: ) let result-type = meth-spec.slot-type.java-function-result-type ; if (result-type == $java-void-type$) #() else list (make (, type-constraint: result-type)) end; end; define function slot-spec (java-class :: , name, type :: , static?) => (spec :: ) make (, java-class: java-class, name: name.ensure-uniq /* .java-name-generate */ , type: type, static?: static?) end; define function meth-spec (java-class :: , name, type :: , invoke-op) => (spec :: ) // if (~ instance?(type, )) // type.examine // end; let static? = (invoke-op == j-invokestatic) ; make (, java-class: java-class, name: name.ensure-uniq /* .java-name-generate */ , type: type, static?: static?, invoke-op: invoke-op) end; define function java-method (meth-spec :: , #key native? = #f, synchronized? = #f, public? = #t) => (meth :: ) let meth-name = meth-spec.slot-name ; let meth-sig = signature-string (meth-spec.slot-type) ; let jclass = meth-spec.java-class ; let concrete = jclass.concrete-implementation ; if (concrete) let meth-name-index = java-name-pool-index (meth-name, jclass) ; let meth-sig-index = java-name-pool-index (meth-sig, jclass) ; let meth = any? (method (meth :: ) if (meth.slot-name = meth-name-index & meth.slot-sig = meth-sig-index) format-out ("###### found duplicate method %s:%s in %s, appending code regardless...\n", meth-name, meth-sig, jclass) ; meth end end, concrete.methods) ; unless (meth) meth := make (, max-locals: meth-spec.total-args, java-class: jclass, name: meth-name-index, sig: meth-sig-index, public?: public?, static?: meth-spec.static?, native?: native?, synchronized?: synchronized?, slots-spec: meth-spec) ; concrete.methods := add! (concrete.methods, meth) ; end; meth else format-out ("Warning: java-method for a non-concrete class %s\n", jclass) ; make (, max-locals: meth-spec.total-args, java-class: jclass, name: meth-name, sig: meth-sig, public?: public?, static?: meth-spec.static?, synchronized?: synchronized?, slots-spec: meth-spec) end end; define function java-interface-method (meth-spec :: , #key public? = #t) => (meth :: ) let meth-name = meth-spec.slot-name ; let meth-sig = signature-string (meth-spec.slot-type) ; let jclass = meth-spec.java-class ; let concrete = jclass.concrete-implementation ; if (concrete) let meth-name-index = java-name-pool-index (meth-name, jclass) ; let meth-sig-index = java-name-pool-index (meth-sig, jclass) ; let meth = any? (method (meth :: ) if (meth.slot-name = meth-name-index & meth.slot-sig = meth-sig-index) format-out ("###### found duplicate method %s:%s in %s, appending code regardless...\n", meth-name, meth-sig, jclass) ; meth end end, concrete.methods) ; unless (meth) meth := make (, java-class: jclass, name: meth-name-index, sig: meth-sig-index, public?: public?, static?: meth-spec.static?, slots-spec: meth-spec) ; concrete.methods := add! (concrete.methods, meth) ; end; meth else make (, java-class: jclass, name: meth-name, sig: meth-sig, public?: public?, static?: meth-spec.static?, slots-spec: meth-spec) ; end end; define function my-break (x) => () foo-break(x); break (x) ; foo-break(x); end; define function foo-break (x) => () if (x == #"Sproing") format-out ("prevent optimization away\n") end end; define function java-field (slot-spec :: , #key public? = #t) => (field :: ) let name = slot-spec.slot-name ; let sig = signature-string (slot-spec.slot-type) ; let jclass :: = slot-spec.java-class ; let concrete = jclass.concrete-implementation ; if (concrete) let slot-name-index = java-name-pool-index (name, jclass) ; let slot-sig-index = java-name-pool-index (sig, jclass) ; let slot = any? (method (slot :: ) if (slot.slot-name = slot-name-index & slot.slot-sig = slot-sig-index) let acc = slot.access-code ; local method check-flag-matches (bool, flags) let flag-set = (logand (acc, flags) = flags) ; if ((~bool) == flag-set) format-out ("###### found duplicate incompatible slot %s:%s in %s\n", name, sig, jclass) ; my-break (slot) end end; check-flag-matches (slot-spec.static?, $java-access-static$) ; check-flag-matches (public?, $java-access-public$) ; slot end end, concrete.slots) ; unless (slot) slot := make (, java-class: jclass, name: slot-name-index, sig: slot-sig-index, slots-spec: slot-spec) ; let acc = slot.access-code ; acc := logior (acc, if (public?) $java-access-public$ else $java-access-private$ end) ; if (slot-spec.static?) acc := logior (acc, $java-access-static$) end; slot.access-code := acc ; concrete.slots := add! (concrete.slots, slot) ; end; slot else let slot :: = make (, java-class: jclass, name: slot-name, sig: slot-sig, slots-spec: slot-spec) ; let acc = slot.access-code ; acc := logior (acc, if (public?) $java-access-public$ else $java-access-private$ end) ; if (slot-spec.static?) acc := logior (acc, $java-access-static$) end; slot.access-code := acc ; slot end end; // model those classes we actually generate //define abstract class () define open abstract class () // accumulate constants here sealed slot constants = make (, size: 1, fill: #f) ; sealed slot slots = make () ; // of sealed slot methods = make () ; // of // sealed slot interfaces = make () ; // see mark-as-implementing sealed slot attrs = make () ; sealed slot access-code = logior ($java-access-public$, $java-access-sup$) ; sealed constant slot outstream, init-keyword: outstream:, init-value: *standard-output* ; sealed slot code-index = #f ; sealed slot constants-hash :: = make () ; sealed slot been-emitted? :: = #f ; /* sealed slot symbol-slots-list :: = #() ; // pairs of string/slot-spec sealed slot been-inited? :: = #f ; sealed constant slot library, required-init-keyword: library: ; sealed slot ep-seqnum :: = 0 ; sealed slot iep-emitted? = #f ; sealed slot mep-emitted? = #f ; sealed slot xep-emitted? = #f ; */ end; define function mark-as-implementing (cls :: , inter :: ) add! (cls.interfaces, make (, java-class: inter)) end; define class () end; define class () end; //define class (, ) // sealed slot interfaces = make () ; // see mark-as-implementing //end; // //define class (, ) // inherited slot access-code = logior ($java-access-public$, $java-access-interface$) ; //end; define open class () /* sealed*/open slot interfaces :: = make () ; // see mark-as-implementing end; define open class () inherited slot access-code = logior ($java-access-public$, $java-access-interface$) ; end; define generic standard-concrete-class-for (jcls :: ) => (cls :: ) ; define method standard-concrete-class-for (jcls :: ) => (cls :: ) end; define method standard-concrete-class-for (jcls :: ) => (cls :: ) end; define function ensure-class-concrete (cls :: , #key class-for :: = standard-concrete-class-for) => (cls :: ) unless (cls.concrete-implementation) upgrade-class-to-concrete (cls, class-for) end; cls end; define generic upgrade-class-to-concrete (cls :: , class-for :: ) => () ; define method upgrade-class-to-concrete (cls :: , class-for :: ) => () // all concrete classes should be queued for output java-emit-class (cls) end; define method upgrade-class-to-concrete (cls :: , class-for :: ) => () cls.concrete-implementation := make (cls.class-for, library: #f, class-or-interface: cls) ; next-method () end; define method upgrade-class-to-concrete (cls :: , class-for :: ) => () cls.concrete-implementation := make (cls.class-for, library: #f, class-or-interface: cls) ; next-method () end; define sealed abstract class () end; define sealed generic type-byte (con :: ) => (byte :: ) ; //// temp hack: //define method type-byte-setter (foo :: , con :: ) => (byte :: ) foo end; define sealed generic con-size (con :: ) => (size :: ) ; define method type-byte (con :: ) => (byte :: ) 0 end; define method con-size (con :: ) => (size :: ) 1 end; // used to find existing matching constant define sealed generic same-java-constant (o1 :: , o2 :: ) => (same? :: ) ; // this matches same-java-constant define sealed generic jcon-hash (jcon :: ) => (hash :: ) ; define function pool-index (thing :: , concrete :: ) block (return) let cons-hash = concrete.constants-hash ; let hashval = jcon-hash (thing) ; // new stuff = hash to a list of indices into the real stretchy-vector let constants-vec = concrete.constants ; let cons :: = element (cons-hash, hashval, default: #()) ; for (ind :: in cons) let cand = constants-vec [ind] ; if (same-java-constant (cand, thing)) return (ind) end end; let copy = pool-copy (thing, concrete) ; let new-index = constants-vec.size ; add! (constants-vec, copy) ; if (thing.con-size = 2) add! (constants-vec, #f) ; end; cons-hash [hashval] := pair (new-index, cons) ; new-index end; end; // default method - probably only called when of different types, // so really returns #f define method same-java-constant (o1 :: , o2 :: ) => (same? :: ) o1 == o2 end ; define sealed generic constant-javatype (con :: ) => (res :: ) ; define class () sealed constant slot value :: , init-keyword: value: ; end ; define method constant-javatype (con :: ) => (res :: ) $java-int-type$ end; define method type-byte (con :: ) => (byte :: ) 3 end ; //define method initialize (con :: , #key) => () // next-method () ; //// con.type-byte := 3 //end; define method print-object (int :: , stream :: ) => () format (stream, "INT(%s)", int.value) end; define method same-java-constant (o1 :: , o2 :: ) => (same? :: ) o1.value = o2.value end; define method jcon-hash (jcon :: ) => (hash :: ) jcon.value end; define sealed generic print-jcon (o :: , cons :: ) => (res :: ) ; define method print-jcon (o :: , cons :: ) => (res :: ) let val :: = o.value ; format-to-string ("%d", val) end; define class () sealed constant slot value :: , init-keyword: value: ; end ; define method constant-javatype (con :: ) => (res :: ) $java-long-type$ end; define method type-byte (con :: ) => (byte :: ) 5 end ; define method con-size (con :: ) => (size :: ) 2 end ; //define method initialize (con :: , #key) => () // next-method () ; //// con.type-byte := 5 ; //// con.con-size := 2 //end; define method print-object (long :: , stream :: ) => () format (stream, "LONG(%s)", long.value) end; define method same-java-constant (o1 :: , o2 :: ) => (same? :: ) o1.value = o2.value end; define method jcon-hash (jcon :: ) => (hash :: ) jcon.value end; define method print-jcon (o :: , cons :: ) => (res :: ) let val :: = o.value ; format-to-string ("%d", val) end; define class () sealed constant slot value :: , init-keyword: value: ; end ; define method constant-javatype (con :: ) => (res :: ) $java-float-type$ end; define method type-byte (con :: ) => (byte :: ) 4 end ; //define method initialize (con :: , #key) => () // next-method () ; //// con.type-byte := 4 //end; define method same-java-constant (o1 :: , o2 :: ) => (same? :: ) o1.value = o2.value end; //define function get-second-value (function :: , value :: ) => (res) // let (a, b) = function (value) ; // b //end; //define constant $dummy-hash-state$ = get-second-value (object-hash, 0) ; define method jcon-hash (jcon :: ) => (hash :: ) // values (object-hash (jcon.value, $dummy-hash-state$)) truncate/ (round (jcon.value), #xffffff) end; define method print-jcon (o :: , cons :: ) => (res :: ) let val :: = o.value ; format-to-string ("%s", val) end; define class () sealed constant slot value :: , init-keyword: value: ; end ; define method constant-javatype (con :: ) => (res :: ) $java-double-type$ end; define method type-byte (con :: ) => (byte :: ) 6 end ; define method con-size (con :: ) => (size :: ) 2 end ; //define method initialize (con :: , #key) => () // next-method () ; //// con.type-byte := 6 ; //// con.con-size := 2 //end; define method same-java-constant (o1 :: , o2 :: ) => (same? :: ) o1.value = o2.value end; define method jcon-hash (jcon :: ) => (hash :: ) // values (object-hash (jcon.value, $dummy-hash-state$)) truncate/ (round (jcon.value), #xffffff) end; define method print-jcon (o :: , cons :: ) => (res :: ) let val :: = o.value ; format-to-string ("%s", val) end; define class () sealed constant slot string :: , init-keyword: string: ; end; define method type-byte (con :: ) => (byte :: ) 1 end ; //define method initialize (con :: , #key) => () // next-method () ; //// con.type-byte := 1 //end; define method print-object (utf :: , stream :: ) => () format (stream, "UTF(%s)", utf.string) end; define method same-java-constant (o1 :: , o2 :: ) => (same? :: ) let s1 = o1.string ; let s2 = o2.string ; if (s1 == s2) #t else let tag1 = s1.unique-tag ; let tag2 = s2.unique-tag ; if (tag1 = -1 | tag2 = -1) format-out ("revoked unique strings picked up by same-java-constant\n") ; my-break (o1) end; if (tag1 = tag2 & s1 ~= s2) format-out ("tags same for different strings %s %s\n", s1, s2) ; my-break (o1) ; end; tag1 = tag2 end end; define sealed generic jcon-string-hash (str) => (hash :: ) ; define method jcon-string-hash (str :: ) => (hash :: ) format-out ("unexpected hashing on byte-string %s\n", str) ; jcon-string-hash (str.uniq) end; define method jcon-string-hash (str :: ) => (hash :: ) str.unique-tag end; define method jcon-hash (jcon :: ) => (hash :: ) // jcon.hash-cache | // (jcon.hash-cache := jcon-string-hash (jcon.string)) jcon.string.unique-tag end; define method print-jcon (o :: , cons :: ) => (res :: ) o.string.the-string end; define sealed generic pool-copy (o :: , concrete :: ) => (c :: ) ; // default doesn't copy for unstructured constants (utf, int, etc) define method pool-copy (o :: , concrete :: ) => (con :: ) o end; define class () sealed constant slot utf :: , required-init-keyword: utf: ; sealed slot utf-index :: = -1, init-keyword: utf-index: ; // slot hash-cache :: false-or () = #f ; end; define method constant-javatype (con :: ) => (res :: ) $java/lang/String$ end; define method type-byte (con :: ) => (byte :: ) 8 end ; //define method initialize (con :: , #key) => () // next-method () ; //// con.type-byte := 8 //end; define method print-object (str :: , stream :: ) => () format (stream, "STR(%s)", str.utf.string.the-string) end; define method same-java-constant (o1 :: , o2 :: ) => (same? :: ) same-java-constant (o1.utf, o2.utf) end; define method jcon-hash (jcon :: ) => (hash :: ) // jcon.hash-cache | // (jcon.hash-cache := 357 + jcon-hash (jcon.utf)) 357 + jcon-hash (jcon.utf) end; define method print-jcon (o :: , cons :: ) => (res :: ) print-jcon (cons[o.utf-index], cons) end; define method pool-copy (o :: , concrete :: ) => (con :: ) let utf-index = pool-index (o.utf, concrete) ; make (, utf: o.utf, utf-index: utf-index) end; // can be an array class too, note define class () sealed constant slot java-class :: , required-init-keyword: java-class: ; sealed slot java-class-index :: = -1, init-keyword: java-class-index: ; // slot hash-cache :: false-or () = #f ; end ; define method constant-javatype (con :: ) => (res :: ) con.java-class end; define method type-byte (con :: ) => (byte :: ) 7 end ; //define method initialize (con :: , #key) => () // next-method () ; //// con.type-byte := 7 //end; define method print-object (cls :: , stream :: ) => () format (stream, "CLASS(%s)", cls.java-class) end; define method same-java-constant (o1 :: , o2 :: ) => (same? :: ) if (o1.java-class ~== o2.java-class & o1.java-class.java-class-name == o2.java-class.java-class-name) format-out ("@@@ two identically named classes! %s", o1) ; // my-break (pair (o1,o2)) ; #t else o1.java-class == o2.java-class // maybe should check names? end end; define method jcon-hash (jcon :: ) => (hash :: ) let clz = jcon.java-class ; 439 + clz.java-class-name.unique-tag end; define method print-jcon (o :: , cons :: ) => (res :: ) print-jcon (cons[o.java-class-index], cons) end; define method pool-copy (o :: , concrete :: ) => (con :: ) let utf-constant = make (, string: o.java-class.java-class-name) ; make (, java-class: o.java-class, java-class-index: pool-index (utf-constant, concrete)) end; define class () sealed constant slot nat-name :: , required-init-keyword: name: ; sealed constant slot nat-type :: , required-init-keyword: type: ; sealed slot nat-name-index :: = -1, init-keyword: name-index: ; sealed slot nat-type-index :: = -1, init-keyword: type-index: ; sealed slot hash-cache :: false-or () = #f ; end ; define method constant-javatype (con :: ) => (res :: ) con.nat-type end; define method type-byte (con :: ) => (byte :: ) 12 end ; define method print-object (nat :: , stream :: ) => () format (stream, "%s :: %s", nat.nat-name.the-string, nat.nat-type) end; define method same-java-constant (o1 :: , o2 :: ) => (same? :: ) if (o1.nat-name ~== o2.nat-name & o1.nat-name.the-string = o2.nat-name.the-string) format-out ("@@ bad uniques for same-java-constant\n") ; my-break (pair (o1,o2)) end; (o1.nat-name == o2.nat-name) & java-type-equivalent? (o1.nat-type, o2.nat-type) end; define method jcon-hash (jcon :: ) => (hash :: ) jcon.hash-cache | (jcon.hash-cache := 49 + jcon.nat-name.unique-tag + jcon.nat-type.signature-string.unique-tag) end; define method print-jcon (o :: , cons :: ) => (res :: ) concatenate (concatenate (print-jcon (cons[o.nat-name-index], cons), ":"), print-jcon (cons[o.nat-type-index], cons)) end; define method pool-copy (o :: , concrete :: ) => (con :: ) unless (instance? (o.nat-name, )) format-out ("WHOOPS! broken java nat(name=%s type=%s)\n", o.nat-name, o.nat-type) ; break (o) end; unless (instance? (o.nat-type, )) format-out ("WHOOPS! broken java nat(name=%s type=%s)\n", o.nat-name, o.nat-type) end; let name-index = pool-index (make (, string: o.nat-name), concrete) ; let type-index = pool-index (make (, string: o.nat-type.signature-string), concrete) ; make (, name: o.nat-name, name-index: name-index, type: o.nat-type, type-index: type-index) end; define class () sealed constant slot java-class :: , required-init-keyword: java-class: ; sealed constant slot nat :: , required-init-keyword: nat: ; sealed slot java-class-index :: = -1, init-keyword: java-class-index: ; sealed slot nat-index :: = -1, init-keyword: nat-index: ; sealed slot hash-cache :: false-or () = #f ; end ; define method constant-javatype (con :: ) => (res :: ) constant-javatype (con.nat) end; define method type-byte (con :: ) => (byte :: ) 9 end ; define method print-object (slot :: , stream :: ) => () let jc = slot.java-class.java-class ; format (stream, "SLOT(%s.%s.%s)", jc.class-package, jc.class-name, slot.nat) end; define method same-java-constant (o1 :: , o2 :: ) => (same? :: ) // if (o1.object-class == o2.object-class) // format-out ("@@ in same-java-constant for slot, %s, %s\n", // same-java-constant (o1.java-class, o2.java-class), // same-java-constant (o1.nat, o2.nat)) // end; (o1.object-class == o2.object-class) & // methods and fields are different type bytes same-java-constant (o1.java-class, o2.java-class) & same-java-constant (o1.nat, o2.nat) end; define method jcon-hash (jcon :: ) => (hash :: ) jcon.hash-cache | (jcon.hash-cache := 49 + jcon-hash (jcon.java-class) + jcon-hash (jcon.nat)) end; define method print-jcon (o :: , cons :: ) => (res :: ) concatenate (concatenate (print-jcon (cons[o.java-class-index], cons), "."), print-jcon (cons[o.nat-index], cons)) ; end; define method pool-copy (o :: , concrete :: ) => (con :: ) let class-index = pool-index (o.java-class, concrete) ; let nat-index = pool-index (o.nat, concrete) ; make (o.object-class, java-class: o.java-class, java-class-index: class-index, nat: o.nat, nat-index: nat-index) end; define class () end ; define method type-byte (con :: ) => (byte :: ) 10 end ; //define method initialize (con :: , #key) => () // next-method () ; //// con.type-byte := 10 //end; define method print-object (slot :: , stream :: ) => () let jc = slot.java-class.java-class ; format (stream, "METH(%s.%s.%s)", jc.class-package, jc.class-name, slot.nat) end; define class () end ; define method type-byte (con :: ) => (byte :: ) 11 end ; //define method initialize (con :: , #key) => () // next-method () ; //// con.type-byte := 11 //end; define method print-object (slot :: , stream :: ) => () format (stream, "IMETH(%s,%s)", slot.java-class, slot.nat) end; define function slot-not-already-present (jc :: , name :: ) let concrete = jc.concrete-implementation ; let name-index = java-name-pool-index (name.uniq, concrete) ; block (return) for (slot :: in concrete.slots) if (slot.slot-name == name-index) return (#f) end end; #t end end; define function java-io-class (classname :: , super :: ) => (cls :: ) make (, class-name: classname, package: $java-io-pack$, super: super) end; // not used? define constant $java-lang-reflect-pack$ = java-package ("reflect", super: $java-lang-pack$) ; define function java-reflect-class (classname :: , super :: ) => (cls :: ) make (, class-name: classname, package: $java-lang-reflect-pack$, super: super) end; define constant $java/lang/Object-array$ = $java/lang/Object$.array-type ; define constant $java/lang/String$ = java-lang-class ("String", $java/lang/Object$) ; define constant $java/lang/Math$ = java-lang-class ("Math", $java/lang/Object$) ; // this should implement Runnable define constant $java/lang/Thread$ = java-lang-class ("Thread", $java/lang/Object$) ; // already in java-opcodes: // define constant $java/lang/Throwable$ = java-lang-class ("Throwable", $java/lang/Object$) ; define constant $java/lang/Exception$ = java-lang-class ("Exception", $java/lang/Throwable$) ; define constant $java/lang/RuntimeException$ = java-lang-class ("RuntimeException", $java/lang/Exception$) ; define constant $java/lang/ClassCastException$ = java-lang-class ("ClassCastException", $java/lang/RuntimeException$) ; define constant $java/lang/System$ = java-lang-class ("System", $java/lang/Object$) ; // this for when don't want a Java String, but a Utf8 thing (typically // a name of a method class or signature) define function java-name-rep (s :: ) => (thing) make (, string: s) end; define function java-name-pool-index (o, jclass :: ) => (index :: ) let concrete = jclass.concrete-implementation ; pool-index (java-name-rep (o), concrete) end ; define function write2 (s, i :: ) write-element (s, as (, logand (#xff, ash (i, -8)))) ; write-element (s, as (, logand (#xff, i))) ; end; define function write4 (s, i :: ) write-element (s, as (, logand (#xff, ash (i, -24)))) ; write-element (s, as (, logand (#xff, ash (i, -16)))) ; write-element (s, as (, logand (#xff, ash (i, -8)))) ; write-element (s, as (, logand (#xff, i))) ; end; define sealed generic java-emit (s :: , java-thing) => () ; define method java-emit (s :: , c :: ) => () write-element (s, as (, c.type-byte)) ; end; define method java-emit (s :: , c :: ) => () next-method (); write4 (s, c.value) ; end; define method java-emit (s :: , c :: ) => () next-method (); // broken // write4 (s, c.value) ; write4 (s, #x595959) ; // Ought to fix this (or at least use the pattern for 42.0)!! end; define method java-emit (s :: , c :: ) => () next-method (); let str = c.string.the-string ; write2 (s, str.size) ; // for (ch in str) // write-element (s, ch) ; // end write (s, str) end; define method java-emit (s :: , c :: ) => () next-method () ; write2 (s, c.utf-index) ; end; define method java-emit (s :: , c :: ) => () next-method (); write2 (s, c.java-class-index) ; end; define method java-emit (s :: , c :: ) => () next-method (); write2 (s, c.java-class-index) ; write2 (s, c.nat-index) ; end ; define method java-emit (s :: , c :: ) => () next-method (); write2 (s, c.nat-name-index) ; write2 (s, c.nat-type-index) ; end; define sealed abstract class () sealed constant slot java-class :: , required-init-keyword: java-class: ; sealed slot public? = #t, init-keyword: public?: ; sealed slot static? = #f, init-keyword: static?: ; sealed slot access-code :: = #x0000 ; // other access bits sealed constant slot slot-name :: , required-init-keyword: name: ; // this is constants-pool offset sealed constant slot slot-sig :: , required-init-keyword: sig: ; // this is constants-pool offset sealed constant slot slots-spec :: , required-init-keyword: slots-spec: ; end; define method initialize (js :: , #key) next-method(); let acc = js.access-code ; acc := logior (acc, if (js.public?) $java-access-public$ else $java-access-private$ end) ; if (js.static?) acc := logior (acc, $java-access-static$) end; js.access-code := acc end; define method print-object (slot :: , stream :: ) => () format (stream, "{java-slot %s}", slot.slots-spec.slot-name) end; define class () end; define method initialize (jam :: , #key) next-method(); jam.access-code := logior (jam.access-code, $java-access-abstract$) end; define abstract sealed class () slot pc :: = 0, init-keyword: pc: ; slot max-stack :: = 0, init-keyword: max-stack: ; slot max-locals :: = 0, init-keyword: max-locals: ; end; define class (, ) sealed slot excep-table = make () ; // not used yet sealed slot basic-blocks = make () ; // of sealed slot bb-list :: = #() ; // the sequence of raw bbs, for label resolution sealed constant slot finally-handlers :: = make () ; sealed slot synchronized? :: = #f, init-keyword: synchronized?: ; sealed slot native? :: = #f, init-keyword: native?: ; sealed constant slot label-table :: = make () ; end; define method initialize (jm :: , #key) next-method(); if (jm.synchronized?) jm.access-code := logior (jm.access-code, $java-access-sync$) end end; define method print-object (slot :: , stream :: ) => () format (stream, "{java-method %s}", slot.slots-spec.slot-name) end; define class () end; // not sure these should be constant? define sealed class () sealed constant slot start-pc :: , init-keyword: start-pc: ; sealed constant slot end-pc :: , init-keyword: end-pc: ; sealed constant slot excep-type :: , init-keyword: excep-type: ; sealed constant slot excep-pc :: , init-keyword: excep-pc: ; end; define method java-emit (s :: , slot :: ) => () let access = logior (slot.access-code, if (slot.public?) $java-access-public$ else $java-access-private$ end) ; if (slot.static?) access := logior (access, $java-access-static$) end; write2 (s, access); write2 (s, slot.slot-name) ; write2 (s, slot.slot-sig) end; define variable *max-max-stack* :: = 0 ; define variable *max-max-locals* :: = 0 ; define method java-emit (s :: , meth :: ) => () if (meth.synchronized?) meth.access-code := logior (meth.access-code, $java-access-sync$) end; next-method(); // do slot stuff write2 (s, 1) ; // one attribute, "Code" write2 (s, meth.java-class.concrete-implementation.code-index) ; write4 (s, meth.pc + 12 + 8 * meth.excep-table.size) ; write2 (s, meth.max-stack) ; write2 (s, meth.max-locals) ; //format-out (" +%d+%d+\n", meth.max-stack, meth.max-locals) ; *max-max-stack* := max (*max-max-stack*, meth.max-stack) ; *max-max-locals* := max (*max-max-locals*, meth.max-locals) ; write4 (s, meth.pc) ; // new byte-vec for byte codes stuff let byte-vec = make (, size: meth.pc) ; //format-out ("byte vector created size %s\n", meth.pc) ; let pc = 0 ; for (jbb :: in meth.basic-blocks) //format-out (" << bb %s\n", pc) ; pc := output-bytecodes (byte-vec, jbb, pc); //format-out (" >> bb %s\n", pc) ; end; // write the vector, single stream op write (s, byte-vec) ; write2 (s, meth.excep-table.size) ; for (excep :: in meth.excep-table) write2 (s, excep.start-pc) ; write2 (s, excep.end-pc) ; write2 (s, excep.excep-pc) ; write2 (s, excep.excep-type) end; write2 (s, 0); // no other attributes! end; define method java-emit (s :: , meth :: ) => () next-method(); // do slot stuff write2 (s, 0) ; // no attributes end; define method java-emit (s :: , meth :: ) => () next-method(); // do slot stuff write2 (s, 0) ; // no attributes end; //define method java-emit (s :: , cls :: ) => () // error ("attempt to generate a non-concrete Java class\n") //end; define constant $SourceFile-name$ = "SourceFile".uniq ; define constant $dummy-file-name$ = "dummy.java".uniq ; define function filename-for-class (cls :: ) => (filename :: ) concatenate (cls.java-class-name.the-string, ".java").uniq end; define method java-emit (s :: , cls :: ) => () let concrete = cls.concrete-implementation ; if (concrete) // various preparatory stuff unless (concrete.code-index) concrete.code-index := java-name-pool-index ($Code-attr-name$, cls) end; // note write4 is not emulator/native portable, so use write2 etc for now // write4 (s, #xcafebabe) ; // magic number for Java class files write2 (s, #xcafe) ; // magic number for Java class files write2 (s, #xbabe) ; // write2 (s, #x0003) ; // minor version number = 3 (JDK 1.0, 1.1) write2 (s, #x002d) ; // major version number = 45 (JDK 1.0, 1.1) let self = make (, java-class: cls) ; let this = pool-index (self, concrete) ; let super = if (instance? (cls, )) 0 elseif (cls.super) pool-index (make (, java-class: cls.super), concrete) else 0 end; if (instance? (cls, )) for (ifc :: in cls.super-interfaces) pool-index (ifc, concrete) end else for (ifc :: in cls.interfaces) // these should be s pool-index (ifc, concrete) ; end end; let src-file-tag = java-name-pool-index ($SourceFile-name$, cls) ; let src-file-name = java-name-pool-index (filename-for-class (cls), cls) ; // no more constants can be created after here! write2 (s, concrete.constants.size) ; let cons :: = concrete.constants ; for (con in cons, i :: from 0) unless (zero? (i)) java-emit (s, con) ; end; end; write2 (s, concrete.access-code) ; write2 (s, this) ; write2 (s, super) ; if (instance? (cls, )) write2 (s, cls.super-interfaces.size) ; for (ifc :: in cls.super-interfaces) write2 (s, pool-index (ifc, concrete)) end else write2 (s, cls.interfaces.size) ; for (ifc :: in cls.interfaces) write2 (s, pool-index (ifc, concrete)) end end; write2 (s, concrete.slots.size) ; for (slot :: in concrete.slots) java-emit (s, slot) end; write2 (s, concrete.methods.size) ; *max-max-stack* := 0 ; *max-max-locals* := 0 ; for (meth :: in concrete.methods) java-emit (s, meth) end; format-out (".. %d constants, %d, %d max stack, locals\n", cons.size, *max-max-stack*, *max-max-locals*) ; // add a sourcefile attribute write2 (s, concrete.attrs.size + 1) ; for (attr in concrete.attrs) java-emit (s, attr) end; write2 (s, src-file-tag) ; write4 (s, 2) ; write2 (s, src-file-name) ; end end; define constant $Code-attr-name$ = "Code".uniq ; // stuff to implement the jar entry - should // put the zip stuff in a module of its own, see if we // already have an API for it. define class () sealed constant slot java-class :: , required-init-keyword: class: ; sealed slot cached-filesize = #f ; sealed slot cached-crc = #f ; end; define class () sealed constant slot zstream :: = make (, stream: make ()) ; end; define method zip-details-upfront? (e :: ) => (upfront? :: ) #f end; define method zip-details-upfront? (e :: ) => (upfront? :: ) #t end; define method filesize (e :: ) => (size :: ) e.cached-filesize | error ("cannot get filesize for - perhaps never wrote it? %s", e) end; define method filesize (e :: ) => (size :: ) e.zstream.get-offset end; define method filesize-setter (size :: , e :: ) => (size :: ) e.cached-filesize := size end; define method filesize-setter (size :: , e :: ) => (size :: ) size // dummy method end; define method file-crc (e :: ) => (crc :: ) e.cached-crc | error ("cannot get file-crc for , perhaps not written? %s", e) end; define method file-crc (e :: ) => (crc :: ) e.zstream.get-crc end; define method file-crc-setter (crc :: , e :: ) => (crc :: ) e.cached-crc := crc end; define method file-crc-setter (crc :: , e :: ) => (crc :: ) crc // dummy method end; define method writer (e :: ) => (writer :: ) method (s :: ) java-emit (s, e.java-class) end end; define method writer (e :: ) => (writer :: ) let raw-stream = e.zstream.the-stream ; method (s :: ) raw-stream.rewind ; let count :: = 0 ; while (available? (raw-stream)) let ch = read-element (raw-stream) ; if (ch) // format-out ("recopy %d\n", as (, ch)) ; write-element (s, ch) ; count := count + 1 end end; format-out ("recopied %d chars\n", count) ; end end; //////////////// end of zip file .jar file tailoring // stuff to track extant java classes and allow the final write-everything-out // phase. define variable *the-pending-java-classes* = make () ; define sealed generic java-emit-class (jc :: ) => (); //define method java-emit-class (jc :: ) => () //end; //define method java-emit-class (jc :: ) => () // unless (jc.been-emitted?) // jc.been-emitted? := #t ; // *the-pending-java-classes* [jc] := #t // end //end; define method java-emit-class (jc :: ) => () let concrete = jc.concrete-implementation ; if (concrete) unless (concrete.been-emitted?) concrete.been-emitted? := #t ; *the-pending-java-classes* [jc] := #t end end end; //define function java-unemit-class (jc :: ) => () // *the-pending-java-classes* [jc] := #f ; // jc.been-emitted? := #f ; //end; define function java-unemit-class (jc :: ) => () let concrete = jc.concrete-implementation ; if (concrete & concrete.been-emitted?) *the-pending-java-classes* [jc] := #f ; concrete.been-emitted? := #f ; end end; format-out ("inited java-emit-class.dylan\n") ; // eof