aboutsummaryrefslogtreecommitdiff
path: root/stdlib/source/library/lux/tool/compiler/meta/packager/jvm.lux
blob: 6d4535137f2e68c978c78ac870e6e9ae1b15aea8 (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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
(.using
 [library
  [lux (.except Module Definition)
   ["[0]" ffi (.only import do_to)]
   [abstract
    ["[0]" monad (.only Monad do)]]
   [control
    ["[0]" maybe (.open: "[1]#[0]" functor)]
    ["[0]" try (.only Try)]]
   [data
    ["[0]" binary (.only Binary)]
    ["[0]" text (.only)
     ["%" format (.only format)]]
    [collection
     ["[0]" sequence]
     ["[0]" list (.open: "[1]#[0]" functor)]
     ["[0]" dictionary]
     ["[0]" set (.only Set)]]]
   [math
    [number
     ["n" nat]
     ["i" int]]]
   [target
    [jvm
     [encoding
      ["[0]" name]]]]
   [world
    ["[0]" file]]]]
 ["[0]" // (.only Packager)
  [//
   ["[0]" context (.only Context)]
   ["[0]" archive (.only Output)
    ["[0]" artifact]
    ["[0]" unit]
    ["[0]" module (.only)
     ["[0]" descriptor (.only Module)]]]
   ["[0]" cache
    [dependency
     ["[1]/[0]" module]
     ["[1]/[0]" artifact]]]
   ["[0]" io
    ["[1]" archive]]
   [//
    [language
     ["$" lux (.only)
      [phase
       [generation
        [jvm
         ["[0]" runtime (.only Definition)]]]]]]]]])

(import java/lang/Object
  "[1]::[0]")

(import java/lang/String
  "[1]::[0]")

(import java/util/jar/Attributes
  "[1]::[0]"
  (put [java/lang/Object java/lang/Object] "?" java/lang/Object))

(import java/util/jar/Attributes$Name
  "[1]::[0]"
  ("static" MAIN_CLASS java/util/jar/Attributes$Name)
  ("static" MANIFEST_VERSION java/util/jar/Attributes$Name))

(import java/util/jar/Manifest
  "[1]::[0]"
  (new [])
  (getMainAttributes [] java/util/jar/Attributes))

(import java/io/Flushable
  "[1]::[0]"
  (flush [] void))

(import java/io/Closeable
  "[1]::[0]"
  (close [] void))

(import java/io/OutputStream
  "[1]::[0]"
  (write [[byte] int int] void))

(import java/io/ByteArrayOutputStream
  "[1]::[0]"
  (new [int])
  (toByteArray [] [byte]))

(import java/util/zip/ZipEntry
  "[1]::[0]"
  (getName [] java/lang/String)
  (isDirectory [] boolean)
  (getSize [] long))

(import java/util/zip/ZipOutputStream
  "[1]::[0]"
  (write [[byte] int int] void)
  (closeEntry [] void))

(import java/util/jar/JarEntry
  "[1]::[0]"
  (new [java/lang/String]))

(import java/util/jar/JarOutputStream
  "[1]::[0]"
  (new [java/io/OutputStream java/util/jar/Manifest])
  (putNextEntry [java/util/zip/ZipEntry] "try" void))

(import java/io/ByteArrayInputStream
  "[1]::[0]"
  (new [[byte]]))

(import java/io/InputStream
  "[1]::[0]"
  (read [[byte] int int] int))

(import java/util/jar/JarInputStream
  "[1]::[0]"
  (new [java/io/InputStream])
  (getNextJarEntry [] "try" "?" java/util/jar/JarEntry))

(def: byte
  1)

... https://en.wikipedia.org/wiki/Kibibyte
(def: kibi_byte
  (n.* 1,024 byte))

... https://en.wikipedia.org/wiki/Mebibyte
(def: mebi_byte
  (n.* 1,024 kibi_byte))

(def: manifest_version
  "1.0")

(def: (manifest program)
  (-> (Maybe unit.ID) java/util/jar/Manifest)
  (let [manifest (java/util/jar/Manifest::new)
        attrs (do_to (java/util/jar/Manifest::getMainAttributes manifest)
                (java/util/jar/Attributes::put (java/util/jar/Attributes$Name::MANIFEST_VERSION)
                                               (ffi.as_string ..manifest_version)))]
    (exec
      (case program
        {.#Some program}
        (do_to attrs
          (java/util/jar/Attributes::put (java/util/jar/Attributes$Name::MAIN_CLASS)
                                         (|> program
                                             runtime.class_name
                                             name.internal
                                             name.external
                                             ffi.as_string)))
        
        {.#None}
        attrs)
      manifest)))

(def: (write_class static module artifact custom content sink)
  (-> Context module.ID artifact.ID (Maybe Text) Binary java/util/jar/JarOutputStream
      (Try java/util/jar/JarOutputStream))
  (let [class_path (|> custom
                       (maybe#each (|>> name.internal name.read))
                       (maybe.else (runtime.class_name [module artifact]))
                       (text.replaced "." "/")
                       (text.suffix (the context.#artifact_extension static)))]
    (do try.monad
      [_ (java/util/jar/JarOutputStream::putNextEntry (java/util/jar/JarEntry::new (ffi.as_string class_path))
                                                      sink)]
      (in (do_to sink
            (java/util/zip/ZipOutputStream::write content (ffi.as_int +0) (ffi.as_int (.int (binary.size content))))
            (java/io/Flushable::flush)
            (java/util/zip/ZipOutputStream::closeEntry))))))

(def: (write_module static necessary_dependencies [module output] sink)
  (-> Context (Set unit.ID) [module.ID Output] java/util/jar/JarOutputStream
      (Try java/util/jar/JarOutputStream))
  (let [! try.monad]
    (monad.mix try.monad
               (function (_ [artifact custom content] sink)
                 (if (set.member? necessary_dependencies [module artifact])
                   (..write_class static module artifact custom content sink)
                   (at ! in sink)))
               sink
               (sequence.list output))))

(def: (read_jar_entry_with_unknown_size input)
  (-> java/util/jar/JarInputStream [Nat Binary])
  (let [chunk (binary.empty ..mebi_byte)
        chunk_size (.int ..mebi_byte)
        buffer (java/io/ByteArrayOutputStream::new (ffi.as_int chunk_size))]
    (loop (again [so_far 0])
      (case (ffi.of_int (java/io/InputStream::read chunk (ffi.as_int +0) (ffi.as_int chunk_size) input))
        -1
        [so_far
         (java/io/ByteArrayOutputStream::toByteArray buffer)]
        
        bytes_read
        (exec
          (java/io/OutputStream::write chunk (ffi.as_int +0) (ffi.as_int bytes_read) buffer)
          (again (|> bytes_read .nat (n.+ so_far))))))))

(def: (read_jar_entry_with_known_size expected_size input)
  (-> Nat java/util/jar/JarInputStream [Nat Binary])
  (let [buffer (binary.empty expected_size)]
    (loop (again [so_far 0])
      (let [so_far' (|> input
                        (java/io/InputStream::read buffer (ffi.as_int (.int so_far)) (ffi.as_int (.int (n.- so_far expected_size))))
                        ffi.of_int
                        .nat
                        (n.+ so_far))]
        (if (n.= expected_size so_far')
          [expected_size buffer]
          (again so_far'))))))

(def: (read_jar_entry entry input)
  (-> java/util/jar/JarEntry java/util/jar/JarInputStream [Nat Binary])
  (case (ffi.of_long (java/util/zip/ZipEntry::getSize entry))
    -1
    (..read_jar_entry_with_unknown_size input)
    
    entry_size
    (..read_jar_entry_with_known_size (.nat entry_size) input)))

(def: (write_host_dependency jar [entries duplicates sink])
  (-> Binary
      [(Set file.Path) (Set file.Path) java/util/jar/JarOutputStream]
      (Try [(Set file.Path) (Set file.Path) java/util/jar/JarOutputStream]))
  (let [input (|> jar
                  java/io/ByteArrayInputStream::new
                  java/util/jar/JarInputStream::new)]
    (loop (again [entries entries
                  duplicates duplicates
                  sink sink])
      (case (java/util/jar/JarInputStream::getNextJarEntry input)
        {try.#Failure error}
        {try.#Failure error}
        
        {try.#Success ?entry}
        (case ?entry
          {.#None}
          (exec
            (java/io/Closeable::close input)
            {try.#Success [entries duplicates sink]})
          
          {.#Some entry}
          (let [entry_path (ffi.of_string (java/util/zip/ZipEntry::getName entry))
                entry_size (ffi.of_long (java/util/zip/ZipEntry::getSize entry))]
            (if (not (or (ffi.of_boolean (java/util/zip/ZipEntry::isDirectory entry))
                         (or (text.starts_with? "META-INF/maven/" entry_path)
                             (text.starts_with? "META-INF/leiningen/" entry_path))
                         (or (text.ends_with? ".SF" entry_path)
                             (text.ends_with? ".DSA" entry_path))))
              (case (java/util/jar/JarOutputStream::putNextEntry (java/util/jar/JarEntry::new (ffi.as_string entry_path))
                                                                 sink)
                {try.#Failure error}
                (again entries
                       (set.has entry_path duplicates)
                       sink)
                
                {try.#Success _}
                (let [[entry_size entry_data] (read_jar_entry entry input)]
                  (again (set.has entry_path entries)
                         duplicates
                         (do_to sink
                           (java/util/zip/ZipOutputStream::write entry_data (ffi.as_int +0) (ffi.as_int (.int entry_size)))
                           (java/io/Flushable::flush)
                           (java/util/zip/ZipOutputStream::closeEntry)))))
              (again entries
                     duplicates
                     sink))))))))

(def: .public (package static)
  (-> Context Packager)
  (function (_ host_dependencies archive program)
    (do [! try.monad]
      [.let [necessary_dependencies (cache/artifact.necessary_dependencies archive)]
       order (cache/module.load_order $.key archive)
       .let [buffer (java/io/ByteArrayOutputStream::new (ffi.as_int (.int ..mebi_byte)))]
       sink (|> order
                (list#each (function (_ [module [module_id entry]])
                             [module_id (the archive.#output entry)]))
                (monad.mix ! (..write_module static necessary_dependencies)
                           (java/util/jar/JarOutputStream::new buffer (..manifest program))))
       [entries duplicates sink] (|> host_dependencies
                                     dictionary.values
                                     (monad.mix ! ..write_host_dependency
                                                [(set.empty text.hash)
                                                 (set.empty text.hash)
                                                 sink]))
       .let [_ (do_to sink
                 (java/io/Flushable::flush)
                 (java/io/Closeable::close))]]
      (in (|> buffer
              java/io/ByteArrayOutputStream::toByteArray
              {.#Left})))))