diff options
author | Matth?us G. Chajdas <dev@anteru.net> | 2019-11-10 13:56:53 +0100 |
---|---|---|
committer | Matth?us G. Chajdas <dev@anteru.net> | 2019-11-10 13:56:53 +0100 |
commit | 1dd3124a9770e11b6684e5dd1e6bc15a0aa3bc67 (patch) | |
tree | 87a171383266dd1f64196589af081bc2f8e497c3 /tests/examplefiles/genclass.clj | |
parent | f1c080e184dc1bbc36eaa7cd729ff3a499de568a (diff) | |
download | pygments-master.tar.gz |
Diffstat (limited to 'tests/examplefiles/genclass.clj')
-rw-r--r-- | tests/examplefiles/genclass.clj | 510 |
1 files changed, 0 insertions, 510 deletions
diff --git a/tests/examplefiles/genclass.clj b/tests/examplefiles/genclass.clj deleted file mode 100644 index c63da8fd..00000000 --- a/tests/examplefiles/genclass.clj +++ /dev/null @@ -1,510 +0,0 @@ -; Copyright (c) Rich Hickey. All rights reserved. -; The use and distribution terms for this software are covered by the -; Common Public License 1.0 (http://opensource.org/licenses/cpl.php) -; which can be found in the file CPL.TXT at the root of this distribution. -; By using this software in any fashion, you are agreeing to be bound by -; the terms of this license. -; You must not remove this notice, or any other, from this software. - -(in-ns 'clojure) - -(import '(java.lang.reflect Modifier Constructor) - '(clojure.asm ClassWriter ClassVisitor Opcodes Type) - '(clojure.asm.commons Method GeneratorAdapter) - '(clojure.lang IPersistentMap)) - -;(defn method-sig [#^java.lang.reflect.Method meth] -; [(. meth (getName)) (seq (. meth (getParameterTypes)))]) - -(defn- non-private-methods [#^Class c] - (loop [mm {} - considered #{} - c c] - (if c - (let [[mm considered] - (loop [mm mm - considered considered - meths (concat - (seq (. c (getDeclaredMethods))) - (seq (. c (getMethods))))] - (if meths - (let [#^Method meth (first meths) - mods (. meth (getModifiers)) - mk (method-sig meth)] - (if (or (considered mk) - (. Modifier (isPrivate mods)) - (. Modifier (isStatic mods)) - (. Modifier (isFinal mods))) - (recur mm (conj considered mk) (rest meths)) - (recur (assoc mm mk meth) (conj considered mk) (rest meths)))) - [mm considered]))] - (recur mm considered (. c (getSuperclass)))) - mm))) - -(defn- ctor-sigs [super] - (for [#^Constructor ctor (. super (getDeclaredConstructors)) - :when (not (. Modifier (isPrivate (. ctor (getModifiers)))))] - (apply vector (. ctor (getParameterTypes))))) - -(defn- escape-class-name [c] - (.. (.getSimpleName c) - (replace "[]" "<>"))) - -(defn- overload-name [mname pclasses] - (if (seq pclasses) - (apply str mname (interleave (repeat \-) - (map escape-class-name pclasses))) - (str mname "-void"))) - -;(distinct (map first(keys (mapcat non-private-methods [Object IPersistentMap])))) - -(defn gen-class - "Generates compiled bytecode for a class with the given - package-qualified cname (which, as all names in these parameters, can - be a string or symbol). The gen-class construct contains no - implementation, as the implementation will be dynamically sought by - the generated class in functions in a corresponding Clojure - namespace. Given a generated class org.mydomain.MyClass, methods - will be implemented that look for same-named functions in a Clojure - namespace called org.domain.MyClass. The init and main - functions (see below) will be found similarly. The static - initializer for the generated class will attempt to load the Clojure - support code for the class as a resource from the claspath, e.g. in - the example case, org/mydomain/MyClass.clj - - Returns a map containing :name and :bytecode. Most uses will be - satisfied by the higher-level gen-and-load-class and - gen-and-store-class functions, which generate and immediately load, - or generate and store to disk, respectively. - - Options should be a set of key/value pairs, all of which are optional: - - :extends aclass - - Specifies the superclass, the non-private methods of which will be - overridden by the class. If not provided, defaults to Object. - - :implements [interface ...] - - One or more interfaces, the methods of which will be implemented by the class. - - :init name - - If supplied, names a function that will be called with the arguments - to the constructor. Must return [[superclass-constructor-args] state] - If not supplied, the constructor args are passed directly to - the superclass constructor and the state will be nil - - :constructors {[param-types] [super-param-types], ...} - - By default, constructors are created for the generated class which - match the signature(s) of the constructors for the superclass. This - parameter may be used to explicitly specify constructors, each entry - providing a mapping from a constructor signature to a superclass - constructor signature. When you supply this, you must supply an :init - specifier. - - :methods [[name [param-types] return-type], ...] - - The generated class automatically defines all of the non-private - methods of its superclasses/interfaces. This parameter can be used - to specify the signatures of additional methods of the generated - class. Do not repeat superclass/interface signatures here. - - :main boolean - - If supplied and true, a static public main function will be - generated. It will pass each string of the String[] argument as a - separate argument to a function called 'main. - - :factory name - - If supplied, a (set of) public static factory function(s) will be - created with the given name, and the same signature(s) as the - constructor(s). - - :state name - - If supplied, a public final instance field with the given name will be - created. You must supply an :init function in order to provide a - value for the state. Note that, though final, the state can be a ref - or agent, supporting the creation of Java objects with transactional - or asynchronous mutation semantics. - - :exposes {protected-field-name {:get name :set name}, ...} - - Since the implementations of the methods of the generated class - occur in Clojure functions, they have no access to the inherited - protected fields of the superclass. This parameter can be used to - generate public getter/setter methods exposing the protected field(s) - for use in the implementation." - - [cname & options] - (let [name (str cname) - {:keys [extends implements constructors methods main factory state init exposes]} (apply hash-map options) - super (or extends Object) - interfaces implements - supers (cons super (seq interfaces)) - ctor-sig-map (or constructors (zipmap (ctor-sigs super) (ctor-sigs super))) - cv (new ClassWriter (. ClassWriter COMPUTE_MAXS)) - cname (. name (replace "." "/")) - ctype (. Type (getObjectType cname)) - iname (fn [c] (.. Type (getType c) (getInternalName))) - totype (fn [c] (. Type (getType c))) - to-types (fn [cs] (if (pos? (count cs)) - (into-array (map totype cs)) - (make-array Type 0))) - obj-type (totype Object) - arg-types (fn [n] (if (pos? n) - (into-array (replicate n obj-type)) - (make-array Type 0))) - super-type (totype super) - init-name (str init) - factory-name (str factory) - state-name (str state) - main-name "main" - var-name (fn [s] (str s "__var")) - rt-type (totype clojure.lang.RT) - var-type (totype clojure.lang.Var) - ifn-type (totype clojure.lang.IFn) - iseq-type (totype clojure.lang.ISeq) - ex-type (totype java.lang.UnsupportedOperationException) - all-sigs (distinct (concat (map #(let[[m p] (key %)] {m [p]}) (mapcat non-private-methods supers)) - (map (fn [[m p]] {(str m) [p]}) methods))) - sigs-by-name (apply merge-with concat {} all-sigs) - overloads (into {} (filter (fn [[m s]] (rest s)) sigs-by-name)) - var-fields (concat (and init [init-name]) - (and main [main-name]) - (distinct (concat (keys sigs-by-name) - (mapcat (fn [[m s]] (map #(overload-name m %) s)) overloads) - (mapcat (comp (partial map str) vals val) exposes)))) - emit-get-var (fn [gen v] - (let [false-label (. gen newLabel) - end-label (. gen newLabel)] - (. gen getStatic ctype (var-name v) var-type) - (. gen dup) - (. gen invokeVirtual var-type (. Method (getMethod "boolean isBound()"))) - (. gen ifZCmp (. GeneratorAdapter EQ) false-label) - (. gen invokeVirtual var-type (. Method (getMethod "Object get()"))) - (. gen goTo end-label) - (. gen mark false-label) - (. gen pop) - (. gen visitInsn (. Opcodes ACONST_NULL)) - (. gen mark end-label))) - emit-forwarding-method - (fn [mname pclasses rclass else-gen] - (let [ptypes (to-types pclasses) - rtype (totype rclass) - m (new Method mname rtype ptypes) - is-overload (overloads mname) - gen (new GeneratorAdapter (. Opcodes ACC_PUBLIC) m nil nil cv) - found-label (. gen (newLabel)) - else-label (. gen (newLabel)) - end-label (. gen (newLabel))] - (. gen (visitCode)) - (when is-overload - (emit-get-var gen (overload-name mname pclasses)) - (. gen (dup)) - (. gen (ifNonNull found-label)) - (. gen (pop))) - (emit-get-var gen mname) - (. gen (dup)) - (. gen (ifNull else-label)) - (when is-overload - (. gen (mark found-label))) - ;if found - (. gen (loadThis)) - ;box args - (dotimes i (count ptypes) - (. gen (loadArg i)) - (. clojure.lang.Compiler$HostExpr (emitBoxReturn nil gen (nth pclasses i)))) - ;call fn - (. gen (invokeInterface ifn-type (new Method "invoke" obj-type - (into-array (cons obj-type - (replicate (count ptypes) obj-type)))))) - ;unbox return - (. gen (unbox rtype)) - (when (= (. rtype (getSort)) (. Type VOID)) - (. gen (pop))) - (. gen (goTo end-label)) - - ;else call supplied alternative generator - (. gen (mark else-label)) - (. gen (pop)) - - (else-gen gen m) - - (. gen (mark end-label)) - (. gen (returnValue)) - (. gen (endMethod)))) - ] - ;start class definition - (. cv (visit (. Opcodes V1_5) (. Opcodes ACC_PUBLIC) - cname nil (iname super) - (when interfaces - (into-array (map iname interfaces))))) - - ;static fields for vars - (doseq v var-fields - (. cv (visitField (+ (. Opcodes ACC_PUBLIC) (. Opcodes ACC_FINAL) (. Opcodes ACC_STATIC)) - (var-name v) - (. var-type getDescriptor) - nil nil))) - - ;instance field for state - (when state - (. cv (visitField (+ (. Opcodes ACC_PUBLIC) (. Opcodes ACC_FINAL)) - state-name - (. obj-type getDescriptor) - nil nil))) - - ;static init to set up var fields and load clj - (let [gen (new GeneratorAdapter (+ (. Opcodes ACC_PUBLIC) (. Opcodes ACC_STATIC)) - (. Method getMethod "void <clinit> ()") - nil nil cv)] - (. gen (visitCode)) - (doseq v var-fields - (. gen push name) - (. gen push v) - (. gen (invokeStatic rt-type (. Method (getMethod "clojure.lang.Var var(String,String)")))) - (. gen putStatic ctype (var-name v) var-type)) - - (. gen push ctype) - (. gen push (str (. name replace \. (. java.io.File separatorChar)) ".clj")) - (. gen (invokeStatic rt-type (. Method (getMethod "void loadResourceScript(Class,String)")))) - - (. gen (returnValue)) - (. gen (endMethod))) - - ;ctors - (doseq [pclasses super-pclasses] ctor-sig-map - (let [ptypes (to-types pclasses) - super-ptypes (to-types super-pclasses) - m (new Method "<init>" (. Type VOID_TYPE) ptypes) - super-m (new Method "<init>" (. Type VOID_TYPE) super-ptypes) - gen (new GeneratorAdapter (. Opcodes ACC_PUBLIC) m nil nil cv) - no-init-label (. gen newLabel) - end-label (. gen newLabel) - nth-method (. Method (getMethod "Object nth(Object,int)")) - local (. gen newLocal obj-type)] - (. gen (visitCode)) - - (if init - (do - (emit-get-var gen init-name) - (. gen dup) - (. gen ifNull no-init-label) - ;box init args - (dotimes i (count pclasses) - (. gen (loadArg i)) - (. clojure.lang.Compiler$HostExpr (emitBoxReturn nil gen (nth pclasses i)))) - ;call init fn - (. gen (invokeInterface ifn-type (new Method "invoke" obj-type - (arg-types (count ptypes))))) - ;expecting [[super-ctor-args] state] returned - (. gen dup) - (. gen push 0) - (. gen (invokeStatic rt-type nth-method)) - (. gen storeLocal local) - - (. gen (loadThis)) - (. gen dupX1) - (dotimes i (count super-pclasses) - (. gen loadLocal local) - (. gen push i) - (. gen (invokeStatic rt-type nth-method)) - (. clojure.lang.Compiler$HostExpr (emitUnboxArg nil gen (nth super-pclasses i)))) - (. gen (invokeConstructor super-type super-m)) - - (if state - (do - (. gen push 1) - (. gen (invokeStatic rt-type nth-method)) - (. gen (putField ctype state-name obj-type))) - (. gen pop)) - - (. gen goTo end-label) - ;no init found - (. gen mark no-init-label) - (. gen (throwException ex-type (str init-name " not defined"))) - (. gen mark end-label)) - (if (= pclasses super-pclasses) - (do - (. gen (loadThis)) - (. gen (loadArgs)) - (. gen (invokeConstructor super-type super-m))) - (throw (new Exception ":init not specified, but ctor and super ctor args differ")))) - - (. gen (returnValue)) - (. gen (endMethod)) - ;factory - (when factory - (let [fm (new Method factory-name ctype ptypes) - gen (new GeneratorAdapter (+ (. Opcodes ACC_PUBLIC) (. Opcodes ACC_STATIC)) - fm nil nil cv)] - (. gen (visitCode)) - (. gen newInstance ctype) - (. gen dup) - (. gen (loadArgs)) - (. gen (invokeConstructor ctype m)) - (. gen (returnValue)) - (. gen (endMethod)))))) - - ;add methods matching supers', if no fn -> call super - (let [mm (non-private-methods super)] - (doseq #^java.lang.reflect.Method meth (vals mm) - (emit-forwarding-method (.getName meth) (.getParameterTypes meth) (.getReturnType meth) - (fn [gen m] - (. gen (loadThis)) - ;push args - (. gen (loadArgs)) - ;call super - (. gen (visitMethodInsn (. Opcodes INVOKESPECIAL) - (. super-type (getInternalName)) - (. m (getName)) - (. m (getDescriptor))))))) - ;add methods matching interfaces', if no fn -> throw - (doseq #^Class iface interfaces - (doseq #^java.lang.reflect.Method meth (. iface (getMethods)) - (when-not (contains? mm (method-sig meth)) - (emit-forwarding-method (.getName meth) (.getParameterTypes meth) (.getReturnType meth) - (fn [gen m] - (. gen (throwException ex-type (. m (getName))))))))) - ;extra methods - (doseq [mname pclasses rclass :as msig] methods - (emit-forwarding-method (str mname) pclasses rclass - (fn [gen m] - (. gen (throwException ex-type (. m (getName)))))))) - - ;main - (when main - (let [m (. Method getMethod "void main (String[])") - gen (new GeneratorAdapter (+ (. Opcodes ACC_PUBLIC) (. Opcodes ACC_STATIC)) - m nil nil cv) - no-main-label (. gen newLabel) - end-label (. gen newLabel)] - (. gen (visitCode)) - - (emit-get-var gen main-name) - (. gen dup) - (. gen ifNull no-main-label) - (. gen loadArgs) - (. gen (invokeStatic rt-type (. Method (getMethod "clojure.lang.ISeq seq(Object)")))) - (. gen (invokeInterface ifn-type (new Method "applyTo" obj-type - (into-array [iseq-type])))) - (. gen pop) - (. gen goTo end-label) - ;no main found - (. gen mark no-main-label) - (. gen (throwException ex-type (str main-name " not defined"))) - (. gen mark end-label) - (. gen (returnValue)) - (. gen (endMethod)))) - ;field exposers - (doseq [f {getter :get setter :set}] exposes - (let [fld (.getField super (str f)) - ftype (totype (.getType fld))] - (when getter - (let [m (new Method (str getter) ftype (to-types [])) - gen (new GeneratorAdapter (. Opcodes ACC_PUBLIC) m nil nil cv)] - (. gen (visitCode)) - (. gen loadThis) - (. gen getField ctype (str f) ftype) - (. gen (returnValue)) - (. gen (endMethod)))) - (when setter - (let [m (new Method (str setter) (. Type VOID_TYPE) (into-array [ftype])) - gen (new GeneratorAdapter (. Opcodes ACC_PUBLIC) m nil nil cv)] - (. gen (visitCode)) - (. gen loadThis) - (. gen loadArgs) - (. gen putField ctype (str f) ftype) - (. gen (returnValue)) - (. gen (endMethod)))))) - ;finish class def - (. cv (visitEnd)) - {:name name :bytecode (. cv (toByteArray))})) - -(defn gen-and-load-class - "Generates and immediately loads the bytecode for the specified - class. Note that a class generated this way can be loaded only once - - the JVM supports only one class with a given name per - classloader. Subsequent to generation you can import it into any - desired namespaces just like any other class. See gen-class for a - description of the options." - - [name & options] - (let [{:keys [name bytecode]} - (apply gen-class (str name) options)] - (.. clojure.lang.RT ROOT_CLASSLOADER (defineClass (str name) bytecode)))) - -(defn gen-and-save-class - "Generates the bytecode for the named class and stores in a .class - file in a subpath of the supplied path, the directories for which - must already exist. See gen-class for a description of the options" - - [path name & options] - (let [{:keys [name bytecode]} (apply gen-class (str name) options) - file (java.io.File. path (str (. name replace \. (. java.io.File separatorChar)) ".class"))] - (.createNewFile file) - (with-open f (java.io.FileOutputStream. file) - (.write f bytecode)))) - -(comment -;usage -(gen-class - package-qualified-name - ;all below are optional - :extends aclass - :implements [interface ...] - :constructors {[param-types] [super-param-types], } - :methods [[name [param-types] return-type], ] - :main boolean - :factory name - :state name - :init name - :exposes {protected-field {:get name :set name}, }) - -;(gen-and-load-class -(clojure/gen-and-save-class - "/Users/rich/Downloads" - 'fred.lucy.Ethel - :extends clojure.lang.Box ;APersistentMap - :implements [clojure.lang.IPersistentMap] - :state 'state - ;:constructors {[Object] [Object]} - ;:init 'init - :main true - :factory 'create - :methods [['foo [Object] Object] - ['foo [] Object]] - :exposes {'val {:get 'getVal :set 'setVal}}) - -(in-ns 'fred.lucy.Ethel__2276) -(clojure/refer 'clojure :exclude '(assoc seq count cons)) -(defn init [n] [[] n]) -(defn foo - ([this] :foo) - ([this x] x)) -(defn main [x y] (println x y)) -(in-ns 'user) -(def ethel (new fred.lucy.Ethel__2276 42)) -(def ethel (fred.lucy.Ethel__2276.create 21)) -(fred.lucy.Ethel__2276.main (into-array ["lucy" "ricky"])) -(.state ethel) -(.foo ethel 7) -(.foo ethel) -(.getVal ethel) -(.setVal ethel 12) - -(gen-class org.clojure.MyComparator :implements [Comparator]) -(in-ns 'org.clojure.MyComparator) -(defn compare [this x y] ...) - -(load-file "/Users/rich/dev/clojure/src/genclass.clj") - -(clojure/gen-and-save-class "/Users/rich/dev/clojure/gen/" - 'org.clojure.ClojureServlet - :extends javax.servlet.http.HttpServlet) - -) |