minjs 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: cca8d33a6b1776169d7635194a73f0368260a6e3
4
- data.tar.gz: ff6f20d474b1f9a2cdf4459cf40fef1b6f1fc118
3
+ metadata.gz: cf7918e2d61056e016f1903050b7866612ab4dc0
4
+ data.tar.gz: 0576989a9e9705cb43a696d10532d848ee83d27c
5
5
  SHA512:
6
- metadata.gz: f84fb2055af241c9e50ef4a0cce50b20740d5e11b3d1c92bf1485e3adc3dab55e9de793e2d6605d9decf0d620bd0153309b092ed65729ea248a9ee5c95341e98
7
- data.tar.gz: 2f90ba5cb9d4aeb4cd62ef74123fdb0e1cba07f8adfa6bb72a982c63832210849ddea47bb45cdc913a57b10f5a25d46a69f5f2f912749a690ba2880ac3b3defb
6
+ metadata.gz: ccf02697889e825b9c5bb7fdb80d91e235980753d3f8aa1c8598cf1512e06b638e8365d23197fa774812d7d5efbcc8c66805872a34072cefc5c81645a2bbbcf9
7
+ data.tar.gz: 373dd8eed54811bfae0a1498224fb775e47075d00a5c71087f1644063a2d697241101418cffac5aa028e2829758c429f8acf584c1b33b32ad783aca0a1519cff
data/Rakefile CHANGED
@@ -1,7 +1,14 @@
1
1
  require "bundler/gem_tasks"
2
2
  require "rspec/core/rake_task"
3
+ require "yard"
4
+ require "yard/rake/yardoc_task"
3
5
 
4
6
  RSpec::Core::RakeTask.new(:spec)
7
+ YARD::Rake::YardocTask.new do |t|
8
+ t.files = FileList["lib/**/*.rb"]
9
+ t.options = ['--embed-mixins']
10
+ # t.stats_options = ['--list-undoc']
11
+ end
5
12
 
6
13
  task :default => :spec
7
14
 
data/exe/minjs CHANGED
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require 'minjs'
4
+ require 'minjs/compressor/compressor'
4
5
 
5
6
  argv = ARGV.dup
6
7
  f = []
@@ -17,6 +18,6 @@ argv.each do |x|
17
18
  end
18
19
  end
19
20
 
20
- prog = Minjs::Compressor.new(:debug => false)
21
+ prog = Minjs::Compressor::Compressor.new(:debug => false)
21
22
  prog.compress(f.join("\n"), options)
22
23
  puts prog.to_js(options)
@@ -1,15 +1,11 @@
1
1
  require "minjs/version"
2
- require "minjs/expression"
3
- require "minjs/func"
4
2
  require "minjs/lex"
5
- require "minjs/program"
6
- require "minjs/statement"
7
3
  require "minjs/compressor"
8
4
  require "minjs/ecma262"
9
5
  require "minjs/minjs_compressor"
10
6
  require "minjs/ctype"
11
- require "minjs/exceptions"
12
7
 
8
+ # Minjs
13
9
  module Minjs
14
10
  # Your code goes here...
15
11
  end
@@ -1,1144 +1,7 @@
1
- #!/usr/bin/env ruby
2
- # coding: utf-8
3
- require 'minjs/lex'
4
- require 'minjs/ecma262'
5
- require 'minjs/statement'
6
- require 'minjs/expression'
7
- require 'minjs/func'
8
- require 'minjs/program'
9
- require 'minjs/exceptions'
10
- require 'minjs/version'
11
- require 'logger'
12
-
13
1
  module Minjs
14
- class Compressor
15
- include Statement
16
- include Exp
17
- include Func
18
- include Program
19
- include Ctype
20
-
21
- attr_reader :prog
22
-
23
- def initialize(options = {})
24
- @logger = options[:logger]
25
- if !@logger
26
- @logger = Logger.new(STDERR)
27
- @logger.level = (options[:debug_level] || Logger::WARN)
28
- @logger.formatter = proc{|severity, datetime, progname, message|
29
- "#{message}\n"
30
- }
31
- end
32
- end
33
-
34
- def debug
35
- puts @prog.to_js()
36
- end
37
-
38
- def to_js(options = {})
39
- remove_empty_statement
40
- @prog.to_js(options).sub(/;;\Z/, ";")
41
- end
42
-
43
- def remove_empty_statement(node = @prog)
44
- node.traverse(nil) {|st, parent|
45
- if st.kind_of? ECMA262::StatementList
46
- st.remove_empty_statement
47
- end
48
- }
49
- self
50
- end
51
-
52
- def compress(data, options = {})
53
- @logger.info '* parse'
54
- parse(data)
55
-
56
- if options[:only_parse]
57
- return
58
- end
59
-
60
- algo = [
61
- :reorder_function_decl,
62
- :simple_replacement,
63
- :reorder_var,
64
- :assignment_after_var,
65
- :grouping_statement,
66
- :block_to_statement,
67
- :reduce_if,
68
- :if_to_cond,
69
- :optimize_if_return,
70
- :compress_var,
71
- :reduce_exp,
72
- :grouping_statement,
73
- :block_to_statement,
74
- :if_to_cond,
75
- :optimize_if_return2,
76
- :block_to_statement,
77
- :add_remove_paren,
78
- ]
79
- algo.each do |a|
80
- if (options.empty? || options[:all] || options[a]) && !options[("no_" + a.to_s).to_sym]
81
- @logger.info "* #{a}"
82
- __send__(a, @prog)
83
- end
84
- end
85
-
86
- @heading_comments.reverse.each do |c|
87
- @prog.source_elements.source_elements.unshift(c)
88
- end
89
- self
90
- end
91
-
92
- def parse(data)
93
- @lex = Minjs::Lex.new(data, :logger => @logger)
94
- @global_context = ECMA262::Context.new
95
- @heading_comments = []
96
-
97
- while a = (@lex.comment || @lex.line_terminator || @lex.white_space)
98
- @heading_comments.push(a)
99
- end
100
- while @heading_comments.last == ECMA262::LIT_LINE_FEED and
101
- !(@heading_comments[-2].kind_of?(ECMA262::SingleLineComment))
102
- @heading_comments.pop
103
- end
104
- @prog = program(@lex, @global_context)
105
-
106
- remove_empty_statement
107
- @lex.clear_cache
108
- self
109
- end
110
-
111
- def next_sym(s)
112
- def c2i(c)
113
- c = c.ord
114
- if c >= 0x30 and c <= 0x39
115
- c = c - 0x30
116
- elsif c >= 0x61 and c <= 0x7a
117
- c = c - 0x61 + 10
118
- elsif c >= 0x41 and c <= 0x5a
119
- c = c - 0x41 + 10 + 26
120
- elsif c == 0x5f
121
- c = 62
122
- elsif c == 0x24
123
- c = 63
124
- end
125
- end
126
- def i2c(c)
127
- if c < 10
128
- c = "%c" % (0x30 + c)
129
- elsif c < 10 + 26
130
- c = "%c" % (0x61 + c - 10)
131
- elsif c < 10 + 26 + 26
132
- c = "%c" % (0x41 + c - 10 - 26)
133
- elsif c < 63
134
- c = "_"
135
- elsif c < 64
136
- c = "$"
137
- end
138
- end
139
-
140
- v = 0
141
- s.to_s.split("").each do |x|
142
- v *= 64
143
- v += c2i(x)
144
- end
145
-
146
- while true
147
- v += 1
148
- ret = []
149
- vv = v
150
- while vv > 0
151
- ret.unshift(i2c(vv % 64))
152
- vv /= 64
153
- end
154
- ret = ret.join("")
155
- if ECMA262::IdentifierName.reserved?(ret.to_sym)
156
- ;
157
- elsif ret.to_s.match(/^\d/)
158
- ;
159
- else
160
- break
161
- end
162
- end
163
- ret.to_sym
164
-
165
- end
166
-
167
- def grouping_statement(node = @prog)
168
- node.traverse(nil) {|st, parent|
169
- if st.kind_of? ECMA262::StatementList
170
- st.grouping
171
- end
172
- }
173
- add_remove_paren
174
- self
175
- end
176
-
177
- def reorder_function_decl(node = @prog)
178
- flist = []
179
- node.traverse(nil) {|st, parent|
180
- if st.kind_of? ECMA262::StFunc and parent.kind_of? ECMA262::StatementList and st.decl?
181
- if parent.index(st)
182
- flist.push([st, parent])
183
- end
184
- end
185
- }
186
- flist.reverse.each do |st, parent|
187
- parent.remove(st)
188
- sl = parent.statement_list
189
- if sl[0].kind_of? ECMA262::StExp and sl[0].exp.kind_of? ECMA262::ECMA262String and sl[0].exp.val == "use strict"
190
- sl[1,0] = st
191
- else
192
- sl.unshift(st)
193
- end
194
- end
195
- self
196
- end
197
-
198
- def reorder_var(node = @prog)
199
- node.traverse(nil) {|st, parent|
200
- if st.kind_of? ECMA262::Prog
201
- vars = nil
202
- context = st.context
203
- #
204
- # collect all of var variable in this function
205
- #
206
- var_vars = {}
207
- context.var_env.record.binding.each do|k, v|
208
- if v and v[:_parameter_list].nil? and !v[:value].kind_of?(ECMA262::StFunc)
209
- var_vars[k] = true
210
- end
211
- end
212
- #
213
- # traverse block and convert var statement to assignment expression
214
- # if variable has initializer
215
- #
216
- st.traverse(parent){|st2, parent2|
217
- if st2.kind_of? ECMA262::StVar and st2.context.var_env == context.var_env
218
- exp = nil
219
- st2.vars.each do |name, initializer|
220
- if initializer
221
- if exp.nil?
222
- exp = ECMA262::ExpAssign.new(name, initializer)
223
- else
224
- exp = ECMA262::ExpComma.new(exp, ECMA262::ExpAssign.new(name, initializer))
225
- end
226
- end
227
- end
228
- if exp
229
- parent2.replace(st2, ECMA262::StExp.new(exp))
230
- else
231
- parent2.replace(st2, ECMA262::StEmpty.new())
232
- end
233
- elsif st2.kind_of? ECMA262::StForVar and st2.context.var_env == context.var_env
234
- parent2.replace(st2, st2.to_st_for)
235
- elsif st2.kind_of? ECMA262::StForInVar and st2.context.var_env == context.var_env
236
- parent2.replace(st2, st2.to_st_for_in)
237
- end
238
- }
239
- if var_vars.length > 0
240
- elems = st.source_elements.source_elements
241
- v = ECMA262::StVar.new(
242
- context,
243
- var_vars.collect do |k, v|
244
- [ECMA262::IdentifierName.new(context, k)]
245
- end
246
- )
247
-
248
- idx = 0
249
- elems.each do |e|
250
- found = false
251
- if e.kind_of? ECMA262::StFunc and e.decl?
252
- ;
253
- elsif e.kind_of? ECMA262::StExp and e.exp.kind_of? ECMA262::ECMA262String and e.exp.val == "use strict"
254
- ;
255
- else
256
- e.traverse(nil){|ee, pp|
257
- if ee.kind_of? ECMA262::IdentifierName and var_vars[ee.val.to_sym]
258
- found = true
259
- break
260
- end
261
- }
262
- end
263
- break if found
264
- idx += 1
265
- end
266
-
267
- if idx == 0
268
- elems.unshift(v)
269
- else
270
- elems[idx..0] = v
271
- end
272
- st.source_elements.remove_empty_statement
273
- end
274
- end
275
- self
276
- }
277
- self
278
- end
279
-
280
- def add_remove_paren(node = @prog)
281
- node.traverse(nil) {|st, parent|
282
- if st.respond_to? :remove_paren
283
- st.remove_paren
284
- st.add_paren
285
- end
286
- }
287
- self
288
- end
289
-
290
- def remove_paren(node = @prog)
291
- add_remove_paren(node)
292
- end
293
-
294
- #
295
- # To determine removing "block" is available or not is difficult.
296
- # For example, next code's if-block must not be removed, because
297
- # "else" cluase combined to second "if" statement.
298
- #
299
- # if(a){ //<= this block must not be removed
300
- # while(true)
301
- # if(b){
302
- # ;
303
- # }
304
- # }
305
- # else{
306
- # ;
307
- # }
308
- #
309
- # The next code's while-block must not be removed, because
310
- # "else" cluase combined to second "if" statement.
311
- #
312
- # if(a)
313
- # while(true){ //<= this block must not be removed
314
- # if(b){
315
- # ;
316
- # }
317
- # }
318
- # else{
319
- # ;
320
- # }
321
- #
322
- # To solve this problem, first, every then-clause without block
323
- # converts to block statement. After converted, all blocks
324
- # except then-clause can be removed safety.
325
- #
326
- def then_to_block(node = @prog)
327
- node.traverse(nil) {|st, parent|
328
- if st.kind_of? ECMA262::StIf
329
- if !st.then_st.kind_of?(ECMA262::StBlock)
330
- st.replace(st.then_st, ECMA262::StBlock.new([st.then_st]))
331
- end
332
- end
333
- }
334
- end
335
-
336
- def block_to_statement(node = @prog)
337
- remove_empty_statement
338
- then_to_block
339
- node.traverse(nil) {|st, parent|
340
- if st.kind_of? ECMA262::StBlock and !parent.kind_of?(ECMA262::StTry) and !parent.kind_of?(ECMA262::StIf)
341
- if st.to_statement?
342
- parent.replace(st, st.to_statement)
343
- end
344
- end
345
- }
346
- if_block_to_statement
347
- end
348
-
349
- def if_block_to_statement(node = @prog)
350
- remove_empty_statement
351
- # The "else" cluase's block can be removed always
352
- node.traverse(nil) {|st, parent|
353
- if st.kind_of? ECMA262::StIf
354
- if st.else_st and st.else_st.kind_of? ECMA262::StBlock
355
- st.else_st.remove_empty_statement
356
- end
357
-
358
- if st.else_st and st.else_st.kind_of? ECMA262::StBlock and st.else_st.to_statement?
359
- st.replace(st.else_st, st.else_st.to_statement)
360
- end
361
- end
362
- }
363
- node.traverse(nil) {|st, parent|
364
- if st.kind_of? ECMA262::StIf
365
- if st.then_st and st.then_st.kind_of? ECMA262::StBlock
366
- st.then_st.remove_empty_statement
367
- end
368
- if !st.else_st and st.then_st.kind_of? ECMA262::StBlock and st.then_st.to_statement?
369
- st.replace(st.then_st, st.then_st.to_statement)
370
- elsif st.then_st.kind_of? ECMA262::StBlock and st.then_st.to_statement?
371
- _st = st.then_st
372
- st2 = st.then_st.to_statement
373
- while true
374
- if st2.kind_of? ECMA262::StVar or st2.kind_of? ECMA262::StEmpty or
375
- st2.kind_of? ECMA262::StExp or st2.kind_of? ECMA262::StBlock or
376
- st2.kind_of? ECMA262::StDoWhile or st2.kind_of? ECMA262::StSwitch or
377
- st2.kind_of? ECMA262::StContinue or st2.kind_of? ECMA262::StBreak or
378
- st2.kind_of? ECMA262::StReturn or st2.kind_of? ECMA262::StThrow or
379
- st2.kind_of? ECMA262::StTry or st2.kind_of? ECMA262::StDebugger
380
- st.replace(st.then_st, st.then_st.to_statement)
381
- break;
382
- elsif st2.kind_of? ECMA262::StWhile or
383
- st2.kind_of? ECMA262::StFor or
384
- st2.kind_of? ECMA262::StForIn or
385
- st2.kind_of? ECMA262::StForVar or
386
- st2.kind_of? ECMA262::StForInVar or
387
- st2.kind_of? ECMA262::StWith or
388
- st2.kind_of? ECMA262::StLabelled
389
- st2 = st2.statement
390
- elsif st2.kind_of? ECMA262::StIf
391
- if st2.else_st
392
- st2 = st2.else_st
393
- else
394
- break
395
- end
396
- else #?
397
- break
398
- end
399
- end
400
- end
401
- end
402
- }
403
- =begin
404
- node.traverse(nil) {|st0, parent|
405
- st = st0.deep_dup
406
- if st.kind_of? ECMA262::StIf
407
- if st.then_st and st.then_st.kind_of? ECMA262::StBlock
408
- st.then_st.remove_empty_statement
409
- end
410
-
411
- if st.then_st and st.then_st.kind_of? ECMA262::StBlock and st.then_st.to_statement?
412
- st.replace(st.then_st, st.then_st.to_statement)
413
- end
414
-
415
- _lex = Minjs::Lex.new(st.to_js)
416
- _context = ECMA262::Context.new
417
- _if = if_statement(_lex, _context)
418
- reduce_exp(_if)
419
- if _if == st #
420
- if st0.then_st and st0.then_st.kind_of? ECMA262::StBlock
421
- st0.then_st.remove_empty_statement
422
- end
423
- if st0.then_st and st0.then_st.kind_of? ECMA262::StBlock and st0.then_st.to_statement?
424
- st0.replace(st0.then_st, st0.then_st.to_statement)
425
- end
426
- else
427
- p '!='
428
- puts st.to_js
429
- puts _if.to_js
430
- end
431
- end
432
- }
433
- =end
434
- self
435
- end
436
-
437
- #
438
- # if(a)b;else c;
439
- # =>
440
- # a?b:c
441
- #
442
- # if(a)b
443
- # =>
444
- # a&&b; or a?b:0;
445
- #
446
- # NOTE:
447
- # Sometimes, "conditional operator" will be shorter than
448
- # "logical and operator", because "conditional operator"'s
449
- # priority is lower than almost all other expressions.
450
- #
451
- def if_to_cond(node = @prog)
452
- node.traverse(nil) {|st, parent|
453
- if st.kind_of? ECMA262::StIf
454
- if st.to_exp?
455
- t = ECMA262::StExp.new(st.to_exp({}))
456
- t2 = ECMA262::StExp.new(st.to_exp({cond: true}))
457
- if t2.to_js.length < t.to_js.length
458
- t = t2
459
- end
460
- add_remove_paren(t)
461
- simple_replacement(t)
462
-
463
- if t.to_js.length <= st.to_js.length
464
- parent.replace(st, t)
465
- end
466
- end
467
- end
468
- }
469
- if_to_return(node)
470
- self
471
- end
472
- #
473
- # if(a)return b;else return c;
474
- # => return a?b:c;
475
- #
476
- def if_to_return(node = @prog)
477
- node.traverse(nil) {|st, parent|
478
- if st.kind_of? ECMA262::StIf
479
- if st.to_return?
480
- t = st.to_return
481
- add_remove_paren(t)
482
- simple_replacement(t)
483
- if t.to_js.length <= st.to_js.length
484
- parent.replace(st, t)
485
- end
486
- end
487
- end
488
- }
489
- self
490
- end
491
-
492
- #
493
- # if(a)return b;
494
- # return c;
495
- #
496
- # => return a?b:c;
497
- #
498
- def optimize_if_return(node = @prog)
499
- node.traverse(nil) {|st0, parent0|
500
- if st0.kind_of? ECMA262::StatementList
501
- st0.remove_empty_statement
502
- st = st0.deep_dup
503
- while true
504
- #check last statement
505
- ls = st.statement_list[-1]
506
- ls2 = st.statement_list[-2]
507
- if st.kind_of? ECMA262::SourceElements and !(ls.kind_of? ECMA262::StReturn)
508
- ls2 = ls
509
- ls = ECMA262::StReturn.new(ECMA262::ExpVoid.new(ECMA262::ECMA262Numeric.new(0)))
510
- end
511
- break if ls.nil?
512
- break if ls2.nil?
513
- break if !ls.to_return?
514
- break if !ls2.kind_of?(ECMA262::StIf)
515
- break if ls2.else_st
516
- break if !ls2.then_st.to_return?
517
-
518
- # if !ls2.then_st.kind_of? ECMA262::StIf and !ls2.then_st.to_return?
519
- # break
520
- # end
521
- # if ls2.then_st.kind_of? ECMA262::StIf and !ls2.then_to_return?
522
- # break
523
- # end
524
-
525
- then_exp = ls2.then_st.to_return.exp
526
- else_exp = ls.to_return.exp
527
- then_exp = ECMA262::ExpVoid.new(ECMA262::ECMA262Numeric.new(0)) if then_exp.nil?
528
- else_exp = ECMA262::ExpVoid.new(ECMA262::ECMA262Numeric.new(0)) if else_exp.nil?
529
- if ls2.cond.kind_of? ECMA262::ExpLogicalNot
530
- cond = ECMA262::ExpCond.new(ls2.cond.val, else_exp, then_exp)
531
- else
532
- cond = ECMA262::ExpCond.new(ls2.cond, then_exp, else_exp)
533
- end
534
- ret = ECMA262::StReturn.new(cond)
535
- #puts ret.to_js
536
- #puts ls2.to_js
537
- st.replace(ls2, ret)
538
- st.remove(ls)
539
- end
540
- if st0.to_js.length > st.to_js.length
541
- parent0.replace(st0, st)
542
- end
543
- end
544
- }
545
- self
546
- end
547
-
548
- #
549
- # if(a)return b;else c;
550
- # =>
551
- # if(a)return b;c;
552
- #
553
- def optimize_if_return2(node = @prog)
554
- node.traverse(nil) {|st, parent|
555
- if st.kind_of? ECMA262::StIf and st.else_st and parent.kind_of? ECMA262::StatementList
556
- st.remove_empty_statement
557
- if (st.then_st.kind_of? ECMA262::StBlock and st.then_st[-1].kind_of? ECMA262::StReturn) or
558
- st.then_st.kind_of? ECMA262::StReturn
559
- idx = parent.index(st)
560
- parent[idx+1..0] = st.else_st
561
- st.replace(st.else_st, nil)
562
- elsif (st.else_st.kind_of? ECMA262::StBlock and st.else_st[-1].kind_of? ECMA262::StReturn) or
563
- st.else_st.kind_of? ECMA262::StReturn
564
- idx = parent.index(st)
565
- parent[idx+1..0] = st.then_st
566
- st.instance_eval{
567
- @then_st = @else_st
568
- @else_st = nil
569
- @cond = ECMA262::ExpLogicalNot.new(@cond)
570
- }
571
- end
572
- end
573
- }
574
- self
575
- end
576
-
577
- def compress_var(node = @prog, options = {})
578
- func_scopes = []
579
- catch_scopes = []
580
- with_scopes = []
581
- #
582
- # ECMA262 10.2:
583
- #
584
- # Usually a Lexical Environment is associated with some
585
- # specific syntactic structure of ECMAScript code such as a
586
- # FunctionDeclaration, a WithStatement, or a Catch clause of a
587
- # TryStatement and a new Lexical Environment is created each
588
- # time such code is evaluated.
589
- #
590
- node.traverse(nil) {|st, parent|
591
- if st.kind_of? ECMA262::StFunc
592
- func_scopes.push([st, parent])
593
- elsif st.kind_of? ECMA262::StTry
594
- catch_scopes.push([st, parent])
595
- elsif st.kind_of? ECMA262::StWith
596
- with_scopes.push([st, parent])
597
- end
598
- }
599
- #
600
- # 10.2, 12.14
601
- #
602
- #eee = 'global';
603
- #function test()
604
- #{
605
- # /*
606
- # "eee" is local variable(belongs to this function)
607
- # because var declaration is exist in this function.
608
- # (see also catch's scope comment)
609
- # So, global variable 'eee' is not changed.
610
- # */
611
- # eee = 'function';
612
- # try{
613
- # console.log(eee); //=>function
614
- # throw "exception";
615
- # }
616
- # catch(eee){
617
- # /*
618
- # The catch's variable scope will be created at execution time.
619
- # so next var declaration should belong to "test" function.
620
- # */
621
- # var eee;
622
- # /*
623
- # In execution time, "eee" belongs to this
624
- # catch-clause's scope.
625
- # */
626
- # console.log(eee); //=>exception
627
- # /*
628
- # Next function has its own scope and 'eee' belongs to its.
629
- # */
630
- # (function(){
631
- # var eee;
632
- # console.log(eee); //=>undefined
633
- # })();
634
- # }
635
- #}
636
- #console.log(eee); //=>global
637
- #test();
638
- #
639
- catch_scopes.each{|st, parent|
640
- if st.catch
641
- catch_context = ECMA262::Context.new
642
- catch_context.lex_env = st.context.lex_env.new_declarative_env()
643
- catch_context.var_env = st.context.var_env
644
- catch_context.lex_env.record.create_mutable_binding(st.catch[0], nil)
645
- catch_context.lex_env.record.set_mutable_binding(st.catch[0], :undefined, nil)
646
- st.catch[0].context = catch_context
647
-
648
- st.catch[1].traverse(parent){|st2|
649
- if st2.kind_of? ECMA262::IdentifierName and st2 == st.catch[0] and st2.binding_env == st.catch[0].binding_env
650
- st2.context = catch_context
651
- end
652
- }
653
- func_scopes.unshift([st, parent])
654
- end
655
- }
656
- # with_scopes.each{|st, parent|
657
- # with_context = ECMA262::Context.new
658
- # with_context.lex_env = st.context.lex_env.new_declarative_env()
659
- # with_context.var_env = st.context.var_env
660
- # st.statement.traverse(st) {|st2|
661
- # if st2.kind_of? ECMA262::IdentifierName and st2.binding_env == st.context.var_env
662
- # st2.context = with_context
663
- # with_context.lex_env.record.create_mutable_binding(st2, nil)
664
- # with_context.lex_env.record.set_mutable_binding(st2, :undefined, nil)
665
- # end
666
- # }
667
- # }
668
- func_scopes.reverse!
669
- func_scopes.each {|st, parent|
670
- if st.kind_of? ECMA262::StFunc
671
- context = st.context
672
- elsif st.kind_of? ECMA262::StTry
673
- context = st.catch[0].context
674
- end
675
- var_sym = :a
676
- #
677
- # collect and counting all variables under this function
678
- #
679
- all_vars = {}
680
- var_vars = {}
681
- var_vars_list = []
682
- outer_vars = {}
683
- nesting_vars = {}
684
- nesting_vars_list = []
685
-
686
- st.traverse(parent) {|st2|
687
- #
688
- # Next, tring to rename var_vars(see bellow) to
689
- # new name.
690
- #
691
- # 1. outer_vars:
692
- # Variables which locate out of this function(or global variable)
693
- # Them name cannot be renamed
694
- # 2. nesting_vars:
695
- # Variables which locate in the function of this function.
696
- # Them name cannot be renamed
697
- # 3. var_vars:
698
- # Variables which have same scope in this function.
699
- # 4. all_vars:
700
- # All variables under this function.
701
- #
702
- # a. If the new name is not in all_vars, the name can be renamed to it.
703
- # b. If the new name belongs to var_vars, the name cannot be renamed.
704
- # c. If the new name belongs to outer_vars the name cannot be renamed.
705
- # d. If the new name belongs to nesting_vars, the name can be rename
706
- # to it after renaming nesting_vars's name to another name.
707
- #
708
- if st2.kind_of? ECMA262::IdentifierName
709
- var_name = st2.val.to_sym
710
- #st2_var_env = st2.binding_env
711
- st2_lex_env = st2.binding_env(:lex)
712
- all_vars[var_name] ||= 0
713
- all_vars[var_name] += 1
714
- if st2_lex_env == nil #global
715
- outer_vars[var_name] ||= 0
716
- outer_vars[var_name] += 1
717
- elsif st2_lex_env == @global_context.lex_env #global
718
- outer_vars[var_name] ||= 0
719
- outer_vars[var_name] += 1
720
- elsif st2_lex_env == context.lex_env
721
- var_vars[var_name] ||= 0
722
- var_vars[var_name] += 1
723
- var_vars_list.push(st2)
724
- else
725
- e = st2.binding_env(:lex)
726
- while e
727
- e = e.outer
728
- if e == context.lex_env
729
- nesting_vars[var_name] ||= 0
730
- nesting_vars[var_name] += 1
731
- nesting_vars_list.push(st2)
732
- break
733
- end
734
- if e.nil?
735
- outer_vars[var_name] ||= 0
736
- outer_vars[var_name] += 1
737
- break
738
- end
739
- end
740
- end
741
- end
742
- }
743
- unless var_vars[:eval]
744
- eval_flag = false
745
- st.traverse(parent) {|st2|
746
- if st2.kind_of? ECMA262::ExpCall and st2.name.to_js({}) == "eval"
747
- eval_flag = true
748
- break
749
- end
750
- if st2.kind_of? ECMA262::StWith
751
- eval_flag = true
752
- break
753
- end
754
- }
755
- if eval_flag
756
- next
757
- end
758
- end
759
- #
760
- # sort var_vars
761
- #
762
- var_vars_array = var_vars.sort {|(k1,v1), (k2,v2)| v2 <=> v1}
763
- #
764
- # create renaming table
765
- #
766
- rename_table = {}
767
- var_vars_array.each {|name, count|
768
- if name.nil?
769
- next #bug?
770
- end
771
- if name.length == 1
772
- #STDERR.puts "#{name}=>#{count}"
773
- next
774
- end
775
- #STDERR.puts "trying to rename #{name}(#{count})"
776
- while true
777
- #condition b
778
- if outer_vars[var_sym]
779
- #STDERR.puts "outer_vars has #{var_sym}"
780
- elsif var_vars[var_sym]
781
- #STDERR.puts "var_vars has #{var_sym}(#{var_vars[var_sym]})"
782
- #condigion c
783
- else #condition a&d
784
- #STDERR.puts "->#{var_sym}"
785
- break
786
- end
787
- var_sym = next_sym(var_sym)
788
- end
789
- #rename nesting_vars
790
- if nesting_vars[var_sym]
791
- #STDERR.puts "nesting_vars has #{var_sym}"
792
- nesting_vars_list.each do |x|
793
- #raise 'error' if x.binding_env(:var).nil?
794
- raise 'error' if x.binding_env(:lex).nil?
795
- end
796
-
797
- var_sym2 = "XXX#{var_sym.to_s}".to_sym
798
- while all_vars[var_sym2]
799
- var_sym2 = next_sym(var_sym2)
800
- end
801
- #STDERR.puts "#{var_sym}->#{var_sym2}"
802
- rl = {}
803
- nesting_vars_list.each do |x|
804
- if x.val.to_sym == var_sym
805
- _var_env = x.binding_env(:var)
806
- _lex_env = x.binding_env(:lex)
807
- rl[_var_env] = true
808
- rl[_lex_env] = true
809
- end
810
- end
811
- rl.keys.each do |_env|
812
- if _env && _env.record.binding[var_sym]
813
- _env.record.binding[var_sym2] = _env.record.binding[var_sym]
814
- _env.record.binding.delete var_sym
815
- end
816
- end
817
-
818
- nesting_vars_list.each do |x|
819
- if x.val.to_sym == var_sym
820
- x.instance_eval{
821
- @val = var_sym2
822
- }
823
- end
824
- #raise 'error' if x.binding_env(:var).nil?
825
- raise x.to_js if x.binding_env(:lex).nil?
826
- end
827
- end
828
- rename_table[name] = var_sym
829
- var_sym = next_sym(var_sym)
830
- }
831
- var_vars_list.each {|st2|
832
- raise st2.to_js if st2.binding_env(:lex).nil?
833
- }
834
-
835
- rename_table.each do |name, new_name|
836
- if name != new_name
837
- if context.var_env.record.binding[name]
838
- context.var_env.record.binding[new_name] = context.var_env.record.binding[name]
839
- context.var_env.record.binding.delete(name)
840
- end
841
- if context.lex_env.record.binding[name]
842
- context.lex_env.record.binding[new_name] = context.lex_env.record.binding[name]
843
- context.lex_env.record.binding.delete(name)
844
- end
845
- end
846
- end
847
-
848
- var_vars_list.each {|st2|
849
- st2.instance_eval{
850
- if rename_table[@val]
851
- @val = rename_table[@val]
852
- #raise 'error' if st2.binding_env(:var).nil?
853
- raise st2.to_js if st2.binding_env(:lex).nil?
854
- end
855
- }
856
- }
857
- }
858
- self
859
- end
860
-
861
- def reduce_exp(node = @prog)
862
- node.traverse(nil) {|st, parent|
863
- if st.kind_of? ECMA262::Exp
864
- st.reduce(parent)
865
- end
866
- }
867
- self
868
- end
869
-
870
- def simple_replacement(node = @prog)
871
- node.traverse(nil) {|st, parent|
872
- #
873
- #true => !0
874
- #false => !1
875
- #
876
- if st.kind_of? ECMA262::Boolean
877
- if st.true?
878
- parent.replace(st, ECMA262::ExpParen.new(ECMA262::ExpLogicalNot.new(ECMA262::ECMA262Numeric.new(0))))
879
- else
880
- parent.replace(st, ECMA262::ExpParen.new(ECMA262::ExpLogicalNot.new(ECMA262::ECMA262Numeric.new(1))))
881
- end
882
- #
883
- #if(true){<then>}else{<else>} => <then>
884
- #if(false){<then>}else{<else>} => <else>
885
- #
886
- elsif st.kind_of? ECMA262::StIf
887
- if st.cond.respond_to? :to_ecma262_boolean
888
- if st.cond.to_ecma262_boolean.nil?
889
- ;
890
- elsif st.cond.to_ecma262_boolean == true
891
- parent.replace(st, st.then_st)
892
- elsif st.cond.to_ecma262_boolean == false and st.else_st
893
- parent.replace(st, st.else_st)
894
- elsif st.cond.to_ecma262_boolean == false
895
- parent.replace(st, ECMA262::StEmpty.new)
896
- end
897
- end
898
- #
899
- # while(true) => for(;;)
900
- # while(false) => remove
901
- #
902
- elsif st.kind_of? ECMA262::StWhile and st.exp.respond_to? :to_ecma262_boolean
903
- if st.exp.to_ecma262_boolean.nil?
904
- ;
905
- elsif st.exp.to_ecma262_boolean
906
- parent.replace(st, ECMA262::StFor.new(nil,nil,nil, st.statement))
907
- else
908
- parent.replace(st, ECMA262::StEmpty.new)
909
- end
910
- #
911
- # new A() => (new A)
912
- #
913
- elsif st.kind_of? ECMA262::ExpNew and st.args and st.args.length == 0
914
- st.replace(st.args, nil)
915
- parent.add_paren.remove_paren
916
- #
917
- # !c?a:b => c?b:a
918
- # true?a:b => a
919
- # false?a:b => b
920
- #
921
- elsif st.kind_of? ECMA262::ExpCond
922
- if st.val.kind_of? ECMA262::ExpLogicalNot
923
- st.instance_eval{
924
- @val = @val.val
925
- t = @val2
926
- @val2 = @val3
927
- @val3 = t
928
- }
929
- simple_replacement(st)
930
- end
931
-
932
- if st.val.respond_to? :to_ecma262_boolean
933
- if st.val.to_ecma262_boolean.nil?
934
- ;
935
- elsif st.val.to_ecma262_boolean
936
- parent.replace(st, st.val2)
937
- else
938
- parent.replace(st, st.val3)
939
- end
940
- end
941
- #
942
- # A["B"] => A.N
943
- #
944
- elsif st.kind_of? ECMA262::ExpPropBrac and st.val2.kind_of? ECMA262::ECMA262String
945
- if idname?(st.val2.val)
946
- parent.replace(st, ECMA262::ExpProp.new(st.val, st.val2))
947
- elsif !st.val2.to_ecma262_number.nil? and (v=ECMA262::ECMA262Numeric.new(st.val2.to_ecma262_number)).to_ecma262_string == st.val2.to_ecma262_string
948
- st.replace(st.val2, v)
949
- end
950
- end
951
- }
952
- self
953
- end
954
-
955
- #
956
- # reduce_if
957
- #
958
- def reduce_if(node = @prog)
959
- retry_flag = true
960
- while retry_flag
961
- retry_flag = false
962
- node.traverse(nil) {|st, parent|
963
- if st.kind_of? ECMA262::StIf
964
- # if(a)
965
- # if(b) ...;
966
- # if(a && b) ...;
967
- #
968
- if st.else_st.nil? and
969
- st.then_st.kind_of? ECMA262::StIf and st.then_st.else_st.nil?
970
- st.replace(st.cond, ECMA262::ExpLogicalAnd.new(st.cond, st.then_st.cond))
971
- st.replace(st.then_st, st.then_st.then_st)
972
- end
973
- #if(a)z;else;
974
- #if(a)z;else{}
975
- # => {if(a)z;}
976
- if st.else_st and st.else_st.empty?
977
- st.replace(st.else_st, nil)
978
- parent.replace(st, ECMA262::StBlock.new([st]))
979
- retry_flag = true
980
- break
981
- end
982
- #if(a);else z;
983
- #=>{if(!a)z};
984
- #if(a){}else z;
985
- #=>{if(!a)z};
986
- if st.then_st.empty? and st.else_st
987
- st.replace(st.cond, ECMA262::ExpLogicalNot.new(st.cond));
988
- else_st = st.else_st
989
- st.replace(st.else_st, nil)
990
- st.replace(st.then_st, else_st)
991
- parent.replace(st, ECMA262::StBlock.new([st]))
992
- retry_flag = true
993
- break
994
- end
995
- #if(a);
996
- # => a
997
- #if(a){}
998
- # => a
999
- if st.then_st.empty? and st.else_st.nil?
1000
- parent.replace(st, ECMA262::StExp.new(st.cond))
1001
- end
1002
- =begin
1003
- #if(!(a&&b))
1004
- #=>
1005
- #if(!a||!b)
1006
- if st.cond.kind_of? ECMA262::ExpLogicalNot and st.cond.val.kind_of? ECMA262::ExpParen and
1007
- st.cond.val.val.kind_of? ECMA262::ExpLogicalAnd
1008
- a = ECMA262::ExpLogicalNot.new(st.cond.val.val.val)
1009
- b = ECMA262::ExpLogicalNot.new(st.cond.val.val.val2)
1010
- r = ECMA262::ExpLogicalOr.new(a,b).add_remove_paren
1011
- if r.to_js.length <= st.cond.to_js.length
1012
- st.replace(st.cond, r)
1013
- end
1014
- end
1015
- #if(!(a||b))
1016
- #=>
1017
- #if(!a&&!b)
1018
- if st.cond.kind_of? ECMA262::ExpLogicalNot and st.cond.val.kind_of? ECMA262::ExpParen and
1019
- st.cond.val.val.kind_of? ECMA262::ExpLogicalOr
1020
- a = ECMA262::ExpLogicalNot.new(st.cond.val.val.val)
1021
- b = ECMA262::ExpLogicalNot.new(st.cond.val.val.val2)
1022
- r = ECMA262::ExpLogicalAnd.new(a,b).add_remove_paren
1023
- if r.to_js.length <= st.cond.to_js.length
1024
- st.replace(st.cond, r)
1025
- end
1026
- end
1027
- =end
1028
- #if((a))
1029
- if st.cond.kind_of? ECMA262::ExpParen
1030
- st.replace(st.cond, st.cond.val)
1031
- end
1032
- #if(!!a)
1033
- if st.cond.kind_of? ECMA262::ExpLogicalNot and st.cond.val.kind_of? ECMA262::ExpLogicalNot
1034
- st.replace(st.cond, st.cond.val.val)
1035
- end
1036
- end
1037
- }
1038
- end
1039
- block_to_statement
1040
- self
1041
- end
1042
-
1043
- def assignment_after_var(node = @prog)
1044
- def rewrite_var(var_st, name, initializer)
1045
- var_st.normalization
1046
- i = 0
1047
- var_st.vars.each do |_name, _initializer|
1048
- if _name == name and _initializer.nil?
1049
- var_st.vars[i] = [name, initializer]
1050
- var_st.normalization
1051
- return true
1052
- end
1053
- i += 1
1054
- end
1055
- false
1056
- end
1057
-
1058
- retry_flag = true
1059
- while retry_flag
1060
- retry_flag = false
1061
- node.traverse(nil) {|st, parent|
1062
- if st.kind_of? ECMA262::StVar and parent.kind_of? ECMA262::SourceElements
1063
- catch(:break){
1064
- idx = parent.index(st) + 1
1065
- while true
1066
- st2 = parent[idx]
1067
- if st2.kind_of? ECMA262::StEmpty or (st2.kind_of? ECMA262::StFunc and st2.decl?)
1068
- idx +=1
1069
- next
1070
- elsif st2.kind_of? ECMA262::StExp and st2.exp.kind_of? ECMA262::ExpAssign
1071
- if rewrite_var(st, st2.exp.val, st2.exp.val2)
1072
- parent.replace(st2, ECMA262::StEmpty.new())
1073
- retry_flag = true
1074
- else
1075
- throw :break
1076
- end
1077
- idx += 1
1078
- next
1079
- elsif st2.kind_of? ECMA262::StFor and st2.exp1.kind_of? ECMA262::ExpAssign
1080
- if rewrite_var(st, st2.exp1.val, st2.exp1.val2)
1081
- st2.replace(st2.exp1, nil)
1082
- retry_flag = true
1083
- else
1084
- throw :break
1085
- end
1086
- throw :break
1087
- elsif st2.kind_of? ECMA262::StExp and st2.exp.kind_of? ECMA262::ExpComma
1088
- exp_parent = st2
1089
- exp = st2.exp
1090
-
1091
- while exp.val.kind_of? ECMA262::ExpComma
1092
- exp_parent = exp
1093
- exp = exp.val
1094
- end
1095
-
1096
- if exp.val.kind_of? ECMA262::ExpAssign
1097
- if rewrite_var(st, exp.val.val, exp.val.val2)
1098
- exp_parent.replace(exp, exp.val2)
1099
- retry_flag = true
1100
- else
1101
- throw :break
1102
- end
1103
- else
1104
- throw :break
1105
- end
1106
- else
1107
- throw :break
1108
- end
1109
- end
1110
- }
1111
- end
1112
- }
1113
- end
1114
- self
1115
- end
2
+ # Compressor
3
+ module Compressor
1116
4
  end
1117
5
  end
1118
6
 
1119
- if $0 == __FILE__
1120
- argv = ARGV.dup
1121
- f = []
1122
- options = {}
1123
- argv.each do |x|
1124
- if x.match(/^--?version/)
1125
- puts Minjs::VERSION
1126
- exit(0)
1127
- elsif x.match(/^--?/)
1128
- opt = $'.gsub(/-/, '_').to_sym
1129
- options[opt] = true
1130
- else
1131
- f.push(open(x.to_s).read())
1132
- end
1133
- end
1134
-
1135
- js = f.join("\n")
1136
-
1137
- comp = Minjs::Compressor.new(:debug => false)
1138
- comp.compress(js, options)
1139
- comp_js = comp.to_js(options)
1140
- #p comp_js.length
1141
- js = comp_js
1142
- puts js
1143
-
1144
- end
7
+ require 'minjs/compressor/compressor'