PageRenderTime 55ms CodeModel.GetById 26ms RepoModel.GetById 1ms app.codeStats 0ms

/Clojure/Clojure.Source/clojure/genclass.clj

https://github.com/brycobat/clojure-clr
Clojure | 296 lines | 215 code | 66 blank | 15 comment | 9 complexity | f6f2a127b653fb272f61a4a45e0d2182 MD5 | raw file
  1. ; Copyright (c) David Miller. All rights reserved.
  2. ; The use and distribution terms for this software are covered by the
  3. ; Eclipse Public License 1.0 (http://opensource.org/licenses/eclipse-1.0.php)
  4. ; which can be found in the file epl-v10.html at the root of this distribution.
  5. ; By using this software in any fashion, you are agreeing to be bound by
  6. ; the terms of this license.
  7. ; You must not remove this notice, or any other, from this software.
  8. ; DM: This is one of the few bootstrap *.clj files where I did not even try to do a line-by-line
  9. ; modification of the JVM version. Too many differences.
  10. ; I put more of the support into C# rather than in Clojure, just so I could bang out the code quicker.
  11. ; This could be redone eventually.
  12. (in-ns 'clojure.core)
  13. (import '(System.Reflection ConstructorInfo))
  14. ;;; The options-handling code here is taken from the JVM version.
  15. (defn- ctor-sigs [^Type super]
  16. (for [^ConstructorInfo ctor (.GetConstructors super)
  17. :when (not (.IsPrivate ctor))]
  18. (apply vector (map #(.ParameterType %) (.GetParameters ctor)))))
  19. (def ^{:private true} prim->class
  20. {'int Int32
  21. 'ints (Type/GetType "System.Int32[]")
  22. 'long Int64
  23. 'longs (Type/GetType "System.Int64[]")
  24. 'float Single
  25. 'floats (Type/GetType "System.Single[]")
  26. 'double Double
  27. 'doubles (Type/GetType "System.Double[]")
  28. 'void System.Void
  29. 'short Int16
  30. 'shorts (Type/GetType "System.Int16[]")
  31. 'boolean Boolean
  32. 'booleans (Type/GetType "System.Boolean[]")
  33. 'byte Byte
  34. 'bytes (Type/GetType "System.Byte[]")
  35. 'sbyte SByte
  36. 'sbytes (Type/GetType "System.SByte[]")
  37. 'ushort UInt16
  38. 'ushorts (Type/GetType "System.UInt16[]")
  39. 'uint UInt32
  40. 'uints (Type/GetType "System.UInt32[]")
  41. 'ulong UInt64
  42. 'ulongs (Type/GetType "System.UInt64[]")
  43. 'char Char
  44. 'chars (Type/GetType "System.Char[]")})
  45. (defn- ^Type the-class [x] ;;; ^Class
  46. (cond
  47. (class? x) x
  48. (contains? prim->class x) (prim->class x)
  49. :else (let [strx (str x)]
  50. (clojure.lang.RT/classForName
  51. (if (some #{\. \[} strx)
  52. strx
  53. (str "System." strx)))))) ;;;(str "java.lang." strx))))))
  54. (defn- the-class-maybe-by-ref [x]
  55. (cond
  56. (seq? x) (list (first x) (the-class (second x))) ; (by-ref v)
  57. :else (the-class x)))
  58. ;; someday this can be made codepoint aware
  59. (defn- valid-java-method-name
  60. [^String s]
  61. (= s (clojure.lang.Compiler/munge s)))
  62. (defn- validate-generate-class-options
  63. [{:keys [methods]}]
  64. (let [[mname] (remove valid-java-method-name (map (comp str first) methods))]
  65. (when mname (throw (ArgumentException. (str "Not a valid method name: " mname)))))) ;;; IllegalArgumentException.
  66. (defn- generate-class [options-map]
  67. (validate-generate-class-options options-map)
  68. (let [default-options {:prefix "-" :load-impl-ns true :impl-ns (ns-name *ns*)}
  69. {:keys [name extends implements constructors methods main factory state init exposes
  70. exposes-methods prefix load-impl-ns impl-ns post-init]}
  71. (merge default-options options-map)
  72. name (str name)
  73. super (if extends (the-class extends) Object)
  74. interfaces (map the-class implements)
  75. supers (cons super interfaces)
  76. ctor-sig-map (doall (or constructors (zipmap (ctor-sigs super) (ctor-sigs super))))
  77. class-mapper (fn [coll] (doall (map the-class coll)))
  78. ctor-sig-type-map (doall (zipmap (doall (map class-mapper (keys ctor-sig-map))) (doall (map class-mapper (vals ctor-sig-map)))))
  79. cname (. name (Replace "." "/"))
  80. pkg-name name
  81. impl-pkg-name (str impl-ns)
  82. impl-cname (.. impl-pkg-name (Replace "." "/") (Replace \- \_))
  83. init-name (str init)
  84. post-init-name (str post-init)
  85. factory-name (str factory)
  86. state-name (str state)
  87. main-name "main"
  88. methods (map (fn [x] [(nth x 0)
  89. (map the-class (nth x 1))
  90. (the-class (nth x 2))
  91. (:static (meta x))])
  92. methods)
  93. ]
  94. (clojure.lang.GenClass/GenerateClass
  95. name super (seq interfaces)
  96. (seq ctor-sig-map) (seq ctor-sig-type-map) (seq methods)
  97. exposes exposes-methods
  98. prefix (. clojure.lang.RT booleanCast main)
  99. factory-name state-name
  100. init-name post-init-name
  101. impl-cname impl-pkg-name
  102. (. clojure.lang.RT booleanCast load-impl-ns))))
  103. (defmacro gen-class
  104. "When compiling, generates compiled bytecode for a class with the
  105. given package-qualified :name (which, as all names in these
  106. parameters, can be a string or symbol), and writes the .class file
  107. to the *compile-path* directory. When not compiling, does
  108. nothing. The gen-class construct contains no implementation, as the
  109. implementation will be dynamically sought by the generated class in
  110. functions in an implementing Clojure namespace. Given a generated
  111. class org.mydomain.MyClass with a method named mymethod, gen-class
  112. will generate an implementation that looks for a function named by
  113. (str prefix mymethod) (default prefix: \"-\") in a
  114. Clojure namespace specified by :impl-ns
  115. (defaults to the current namespace). All inherited methods,
  116. generated methods, and init and main functions (see :methods, :init,
  117. and :main below) will be found similarly prefixed. By default, the
  118. static initializer for the generated class will attempt to load the
  119. Clojure support code for the class as a resource from the classpath,
  120. e.g. in the example case, ``org/mydomain/MyClass__init.class``. This
  121. behavior can be controlled by :load-impl-ns
  122. Note that methods with a maximum of 18 parameters are supported.
  123. In all subsequent sections taking types, the primitive types can be
  124. referred to by their Java names (int, float etc), and classes in the
  125. java.lang package can be used without a package qualifier. All other
  126. classes must be fully qualified.
  127. Options should be a set of key/value pairs, all except for :name are optional:
  128. :name aname
  129. The package-qualified name of the class to be generated
  130. :extends aclass
  131. Specifies the superclass, the non-private methods of which will be
  132. overridden by the class. If not provided, defaults to Object.
  133. :implements [interface ...]
  134. One or more interfaces, the methods of which will be implemented by the class.
  135. :init name
  136. If supplied, names a function that will be called with the arguments
  137. to the constructor. Must return [ [superclass-constructor-args] state]
  138. If not supplied, the constructor args are passed directly to
  139. the superclass constructor and the state will be nil
  140. :constructors {[param-types] [super-param-types], ...}
  141. By default, constructors are created for the generated class which
  142. match the signature(s) of the constructors for the superclass. This
  143. parameter may be used to explicitly specify constructors, each entry
  144. providing a mapping from a constructor signature to a superclass
  145. constructor signature. When you supply this, you must supply an :init
  146. specifier.
  147. :post-init name
  148. If supplied, names a function that will be called with the object as
  149. the first argument, followed by the arguments to the constructor.
  150. It will be called every time an object of this class is created,
  151. immediately after all the inherited constructors have completed.
  152. It's return value is ignored.
  153. :methods [ [name [param-types] return-type], ...]
  154. The generated class automatically defines all of the non-private
  155. methods of its superclasses/interfaces. This parameter can be used
  156. to specify the signatures of additional methods of the generated
  157. class. Static methods can be specified with ^{:static true} in the
  158. signature's metadata. Do not repeat superclass/interface signatures
  159. here.
  160. :main boolean
  161. If supplied and true, a static public main function will be generated. It will
  162. pass each string of the String[] argument as a separate argument to
  163. a function called (str prefix main).
  164. :factory name
  165. If supplied, a (set of) public static factory function(s) will be
  166. created with the given name, and the same signature(s) as the
  167. constructor(s).
  168. :state name
  169. If supplied, a public final instance field with the given name will be
  170. created. You must supply an :init function in order to provide a
  171. value for the state. Note that, though final, the state can be a ref
  172. or agent, supporting the creation of Java objects with transactional
  173. or asynchronous mutation semantics.
  174. :exposes {protected-field-name {:get name :set name}, ...}
  175. Since the implementations of the methods of the generated class
  176. occur in Clojure functions, they have no access to the inherited
  177. protected fields of the superclass. This parameter can be used to
  178. generate public getter/setter methods exposing the protected field(s)
  179. for use in the implementation.
  180. :exposes-methods {super-method-name exposed-name, ...}
  181. It is sometimes necessary to call the superclass' implementation of an
  182. overridden method. Those methods may be exposed and referred in
  183. the new method implementation by a local name.
  184. :prefix string
  185. Default: \"-\" Methods called e.g. Foo will be looked up in vars called
  186. prefixFoo in the implementing ns.
  187. :impl-ns name
  188. Default: the name of the current ns. Implementations of methods will be
  189. looked up in this namespace.
  190. :load-impl-ns boolean
  191. Default: true. Causes the static initializer for the generated class
  192. to reference the load code for the implementing namespace. Should be
  193. true when implementing-ns is the default, false if you intend to
  194. load the code via some other method."
  195. {:added "1.0"}
  196. [& options]
  197. (let [x *compile-files*]
  198. (when *compile-files*
  199. (let [options-map (into1 {} (map vec (partition 2 options)))]
  200. `'~(generate-class options-map)))))
  201. ;;;;;;;;;;;;;;;;;;;; gen-interface ;;;;;;;;;;;;;;;;;;;;;;
  202. ;; based on original contribution by Chris Houser
  203. (defn- generate-interface
  204. [{:keys [name extends methods]}]
  205. (let [extendTypes (map the-class extends)
  206. methodSigs (map (fn [[mname pclasses rclass pmetas]] [mname (map the-class-maybe-by-ref pclasses) (the-class rclass) pmetas]) methods)]
  207. (clojure.lang.GenInterface/GenerateInterface (str name) (extract-attributes (meta name)) extendTypes methodSigs)))
  208. (defmacro gen-interface
  209. "When compiling, generates compiled bytecode for an interface with
  210. the given package-qualified :name (which, as all names in these
  211. parameters, can be a string or symbol), and writes the .class file
  212. to the *compile-path* directory. When not compiling, does nothing.
  213. In all subsequent sections taking types, the primitive types can be
  214. referred to by their Java names (int, float etc), and classes in the
  215. java.lang package can be used without a package qualifier. All other
  216. classes must be fully qualified.
  217. Options should be a set of key/value pairs, all except for :name are
  218. optional:
  219. :name aname
  220. The package-qualified name of the class to be generated
  221. :extends [interface ...]
  222. One or more interfaces, which will be extended by this interface.
  223. :methods [ [name [param-types] return-type], ...]
  224. This parameter is used to specify the signatures of the methods of
  225. the generated interface. Do not repeat superinterface signatures
  226. here."
  227. {:added "1.0"}
  228. [& options]
  229. (let [options-map (into1 {} (map vec (partition 2 options))) ]
  230. `'~(generate-interface options-map)))