aboutsummaryrefslogtreecommitdiff
path: root/stdlib/source/lux/host/jvm/loader.lux
blob: b4d5089d42d054179c0c0c938cf6d77946274a58 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
(.module:
  [lux #*
   [control
    [monad (#+ do)]
    ["ex" exception (#+ exception:)]
    [concurrency
     ["." atom (#+ Atom)]]]
   [data
    ["." error (#+ Error)]
    ["." text
     format]
    [collection
     ["." array]
     ["." list ("list/." Functor<List>)]
     ["." dictionary (#+ Dictionary)]]]
   ["." io (#+ IO)]
   [world
    ["." binary (#+ Binary)]]
   ["." host (#+ import: object do-to)]])

(type: #export Library
  (Atom (Dictionary Text Binary)))

(exception: #export (already-stored {class Text})
  (ex.report ["Class" class]))

(exception: #export (unknown {class Text} {known-classes (List Text)})
  (ex.report ["Class" class]
             ["Known classes" (|> known-classes
                                  (list/map (|>> (format text.new-line text.tab)))
                                  (text.join-with ""))]))

(exception: #export (cannot-define {class Text} {error Text})
  (ex.report ["Class" class]
             ["Error" error]))

(import: #long java/lang/Object
  (getClass [] (java/lang/Class java/lang/Object)))

(import: #long java/lang/String)

(import: #long java/lang/reflect/Method
  (invoke [java/lang/Object (Array java/lang/Object)]
          #try java/lang/Object))

(import: #long (java/lang/Class a)
  (getDeclaredMethod [java/lang/String (Array (java/lang/Class java/lang/Object))]
                     #try java/lang/reflect/Method))

(import: #long java/lang/Integer
  (#static TYPE (java/lang/Class java/lang/Integer)))

(import: #long java/lang/reflect/AccessibleObject
  (setAccessible [boolean] void))

(import: #long java/lang/ClassLoader
  (loadClass [java/lang/String]
             #io #try (java/lang/Class java/lang/Object)))

(def: java/lang/ClassLoader::defineClass
  java/lang/reflect/Method
  (let [signature (|> (host.array (java/lang/Class java/lang/Object) 4)
                      (host.array-write 0 (:coerce (java/lang/Class java/lang/Object)
                                                   (host.class-for java/lang/String)))
                      (host.array-write 1 (java/lang/Object::getClass (host.array byte 0)))
                      (host.array-write 2 (:coerce (java/lang/Class java/lang/Object)
                                                   (java/lang/Integer::TYPE)))
                      (host.array-write 3 (:coerce (java/lang/Class java/lang/Object)
                                                   (java/lang/Integer::TYPE))))]
    (do-to (error.assume
            (java/lang/Class::getDeclaredMethod "defineClass"
                                                signature
                                                (host.class-for java/lang/ClassLoader)))
      (java/lang/reflect/AccessibleObject::setAccessible true))))

(def: #export (define class-name bytecode loader)
  (-> Text Binary java/lang/ClassLoader (Error java/lang/Object))
  (let [signature (array.from-list (list (:coerce java/lang/Object
                                                  class-name)
                                         (:coerce java/lang/Object
                                                  bytecode)
                                         (:coerce java/lang/Object
                                                  (host.long-to-int +0))
                                         (:coerce java/lang/Object
                                                  (host.long-to-int (.int (binary.size bytecode))))))]
    (java/lang/reflect/Method::invoke loader signature java/lang/ClassLoader::defineClass)))

(def: #export (new-library _)
  (-> Any Library)
  (atom.atom (dictionary.new text.Hash<Text>)))

(def: #export (memory library)
  (-> Library java/lang/ClassLoader)
  (object [] java/lang/ClassLoader []
    []
    (java/lang/ClassLoader (findClass {class-name java/lang/String}) java/lang/Class
                           (let [classes (|> library atom.read io.run)]
                             (case (dictionary.get class-name classes)
                               (#.Some bytecode)
                               (case (|> _jvm_this
                                         (..define class-name bytecode))
                                 (#error.Success class)
                                 (:assume class)

                                 (#error.Failure error)
                                 (error! (ex.construct ..cannot-define [class-name error])))

                               #.None
                               (error! (ex.construct ..unknown [class-name (dictionary.keys classes)])))))))

(def: #export (store name bytecode library)
  (-> Text Binary Library (IO (Error Any)))
  (do io.Monad<IO>
    [library' (atom.read library)]
    (if (dictionary.contains? name library')
      (wrap (ex.throw ..already-stored name))
      (do @
        [_ (atom.update (dictionary.put name bytecode) library)]
        (wrap (#error.Success []))))))

(def: #export (load name loader)
  (-> Text java/lang/ClassLoader
      (IO (Error (java/lang/Class java/lang/Object))))
  (java/lang/ClassLoader::loadClass name loader))