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