lib/minjs/compressor.rb in minjs-0.2.0 vs lib/minjs/compressor.rb in minjs-0.2.1

- old
+ new

@@ -71,10 +71,11 @@ :reduce_exp, :grouping_statement, :block_to_statement, :if_to_cond, :optimize_if_return2, + :optimize_if_return3, :remove_paren, ] algo.each do |a| if (options.empty? || options[:all] || options[a]) && !options[("no_" + a.to_s).to_sym] @logger.info "* #{a}" @@ -179,11 +180,16 @@ end end } flist.reverse.each do |st, parent| parent.remove(st) - parent.statement_list.unshift(st) + sl = parent.statement_list + if sl[0].kind_of? ECMA262::StExp and sl[0].exp.kind_of? ECMA262::ECMA262String and sl[0].exp.val == "use strict" + sl[1,0] = st + else + sl.unshift(st) + end end self end def reorder_var(node = @prog) @@ -236,14 +242,15 @@ end ) idx = 0 elems.each do |e| - next if e.kind_of? ECMA262::StFunc and e.decl? found = false if e.kind_of? ECMA262::StFunc and e.decl? ; + elsif e.kind_of? ECMA262::StExp and e.exp.kind_of? ECMA262::ECMA262String and e.exp.val == "use strict" + ; else e.traverse(nil){|ee, pp| if ee.kind_of? ECMA262::IdentifierName and var_vars[ee.val.to_sym] found = true break @@ -466,191 +473,252 @@ end end } self end - - def compress_var(node = @prog) - #compress_var_sub(@prog, :longer => true) - compress_var_sub + # + # feature + # + # if(a)b;else return c; + # => + # if(!a)return c;b; + # + def optimize_if_return3(node = @prog) + node.traverse(nil) {|st, parent| + if st.kind_of? ECMA262::StIf and st.else_st and parent.kind_of? ECMA262::StatementList + st.remove_empty_statement + if (st.else_st.kind_of? ECMA262::StBlock and st.else_st[-1].kind_of? ECMA262::StReturn) or + st.else_st.kind_of? ECMA262::StReturn + idx = parent.index(st) + parent[idx+1..0] = st.then_st + st.instance_eval{ + @then_st = @else_st + @else_st = nil + @cond = ECMA262::ExpLogicalNot.new(@cond) + } + end + end + } + self end - def compress_var_sub(node = @prog, options = {}) - # - #traverse all statemtns and expression - # - scopes = [] + def compress_var(node = @prog, options = {}) + func_scopes = [] + catch_scopes = [] node.traverse(nil) {|st, parent| if st.kind_of? ECMA262::StFunc - _context = st.context - _parent = parent - _st = st - scopes.push([st, parent, _parent, _context, _st]) + func_scopes.push([st, parent]) + elsif st.kind_of? ECMA262::StTry + catch_scopes.push([st, parent]) + elsif st.kind_of? ECMA262::StWith + #TODO end } - scopes.reverse! - scopes.each {|st, parent, _parent, _context, _st| + #10.2 + # + # Catch clause of a TryStatement and a new Lexical Environment is + # created each time such code is evaluated. + # + catch_scopes.each{|st, parent| + catch_context = ECMA262::Context.new + catch_context.lex_env = st.context.lex_env.new_declarative_env() + catch_context.var_env = st.context.var_env + catch_context.lex_env.record.create_mutable_binding(st.catch[0], nil) + catch_context.lex_env.record.set_mutable_binding(st.catch[0], :undefined, nil) + st.catch[0].context = catch_context + + st.catch[1].traverse(parent){|st2| + if st2.kind_of? ECMA262::IdentifierName and st2 == st.catch[0] and + st2.binding_env == st.catch[0].binding_env + st2.context = catch_context + end + } + func_scopes.unshift([st, parent]) + } + func_scopes.reverse! + func_scopes.each {|st, parent| + if st.kind_of? ECMA262::StFunc + context = st.context + elsif st.kind_of? ECMA262::StTry + context = st.catch[0].context + end var_sym = :a - if _parent and _context and _st + # + # collect and counting all variables under this function + # + all_vars = {} + var_vars = {} + var_vars_list = [] + outer_vars = {} + nesting_vars = {} + nesting_vars_list = [] + + st.traverse(parent) {|st2| # - # collect and counting all variables under this function/catch - # collect and counting all var-variables under this function/catch + # Next, tring to rename var_vars(see bellow) to + # new name. # - all_vars = {} - var_vars = {} - var_vars_list = [] - outer_vars = {} - nesting_vars = {} - nesting_vars_list = [] - - _st.traverse(_parent) {|st2| - # - # In this function, - # - # 1. outer_vars: - # Variables which locate out of this function(or global variable) - # Them name cannot be renamed - # 2. nesting_vars: - # Variables which locate in the function of this function. - # Them name cannot be renamed - # 3. var_vars: - # Variables which have same scope in this function. - # Them name can be renamed under the following conditions - # - # a. If the new name is not used, the name can be renamed to it. - # b. If the new name belongs to var_vars, the name cannot be renamed. - # c. If the new name belongs to outer_vars the name cannot be renamed. - # d. If the new name belongs to nesting_vars, the name can be rename - # to it after rename nesting_vars's name to another name. - # - if st2.kind_of? ECMA262::IdentifierName - var_name = st2.val.to_sym - st2_env = st2.binding_env - all_vars[var_name] ||= 0 - all_vars[var_name] += 1 - if st2_env == nil #global - outer_vars[var_name] ||= 0 - outer_vars[var_name] += 1 - elsif st2_env == @global_context.var_env #global - outer_vars[var_name] ||= 0 - outer_vars[var_name] += 1 - elsif st2_env == st.context.var_env - var_vars[var_name] ||= 0 - var_vars[var_name] += 1 - var_vars_list.push(st2) - else - e = st2.binding_env - while e - e = e.outer - if e == st.context.var_env - nesting_vars[var_name] ||= 0 - nesting_vars[var_name] += 1 - nesting_vars_list.push(st2) - break - end - if e.nil? - outer_vars[var_name] ||= 0 - outer_vars[var_name] += 1 - break - end + # 1. outer_vars: + # Variables which locate out of this function(or global variable) + # Them name cannot be renamed + # 2. nesting_vars: + # Variables which locate in the function of this function. + # Them name cannot be renamed + # 3. var_vars: + # Variables which have same scope in this function. + # 4. all_vars: + # All variables under this function. + # + # a. If the new name is not in all_vars, the name can be renamed to it. + # b. If the new name belongs to var_vars, the name cannot be renamed. + # c. If the new name belongs to outer_vars the name cannot be renamed. + # d. If the new name belongs to nesting_vars, the name can be rename + # to it after renaming nesting_vars's name to another name. + # + if st2.kind_of? ECMA262::IdentifierName + var_name = st2.val.to_sym + #st2_var_env = st2.binding_env + st2_lex_env = st2.binding_env(:lex) + all_vars[var_name] ||= 0 + all_vars[var_name] += 1 + if st2_lex_env == nil #global + outer_vars[var_name] ||= 0 + outer_vars[var_name] += 1 + elsif st2_lex_env == @global_context.lex_env #global + outer_vars[var_name] ||= 0 + outer_vars[var_name] += 1 +# elsif st2_lex_env == context.lex_env and st2_lex_env != context.lex_env +# nesting_vars[var_name] ||= 0 +# nesting_vars[var_name] += 1 +# nesting_vars_list.push(st2) + elsif st2_lex_env == context.lex_env + var_vars[var_name] ||= 0 + var_vars[var_name] += 1 + var_vars_list.push(st2) + else + e = st2.binding_env(:lex) + while e + e = e.outer + if e == context.lex_env + nesting_vars[var_name] ||= 0 + nesting_vars[var_name] += 1 + nesting_vars_list.push(st2) + break end + if e.nil? + outer_vars[var_name] ||= 0 + outer_vars[var_name] += 1 + break + end end end + end + } + unless var_vars[:eval] + eval_flag = false + st.traverse(parent) {|st2| + if st2.kind_of? ECMA262::ExpCall and st2.name.to_js({}) == "eval" + eval_flag = true + break + end } - unless var_vars[:eval] - eval_flag = false - _st.traverse(_parent) {|st2| - if st2.kind_of? ECMA262::ExpCall and st2.name.to_js({}) == "eval" - eval_flag = true - break - end - } - if eval_flag - next + if eval_flag + next + end + end + # + # sort var_vars + # + var_vars_array = var_vars.sort {|(k1,v1), (k2,v2)| v2 <=> v1} + # + # create renaming table + # + rename_table = {} + var_vars_array.each {|name, count| + if name.nil? + next #bug? + end + #STDERR.puts "trying to rename #{name}" + while true + #condition b + if var_vars[var_sym] + #STDERR.puts "var_vars has #{var_sym}" + #condigion c + elsif outer_vars[var_sym] + #STDERR.puts "outer_vars has #{var_sym}" + else #condition a&d + #STDERR.puts "->#{var_sym}" + break end + var_sym = next_sym(var_sym) end - # - # sort var_vars - # - var_vars_array = var_vars.sort {|(k1,v1), (k2,v2)| v2 <=> v1} - # - # create renaming table - # - rename_table = {} - var_vars_array.each {|name, count| - if name.nil? - next #bug? + #rename nesting_vars + if nesting_vars[var_sym] + #STDERR.puts "nesting_vars has #{var_sym}" + nesting_vars_list.each do |x| + #raise 'error' if x.binding_env(:var).nil? + raise 'error' if x.binding_env(:lex).nil? end - while outer_vars[var_sym] or var_vars[var_sym] - var_sym = next_sym(var_sym) + + var_sym2 = "XXX#{var_sym.to_s}".to_sym + while all_vars[var_sym2] + var_sym2 = next_sym(var_sym2) end - #rename nesting_vars - if nesting_vars[var_sym] - nesting_vars_list.each do |x| - raise 'error' if x.binding_env(:var).nil? - raise 'error' if x.binding_env(:lex).nil? + #STDERR.puts "#{var_sym}->#{var_sym2}" + rl = {} + nesting_vars_list.each do |x| + if x.val.to_sym == var_sym + _var_env = x.binding_env(:var) + _lex_env = x.binding_env(:lex) + rl[_var_env] = true + rl[_lex_env] = true end - - var_sym2 = "abc#{var_sym.to_s}".to_sym - while all_vars[var_sym2] - var_sym2 = next_sym(var_sym2) + end + rl.keys.each do |_env| + if _env && _env.record.binding[var_sym] + _env.record.binding[var_sym2] = _env.record.binding[var_sym] + _env.record.binding.delete var_sym end - rl = {} - nesting_vars_list.each do |x| - if x.val.to_sym == var_sym - _var_env = x.binding_env(:var) - _lex_env = x.binding_env(:lex) - rl[_var_env] = true - rl[_lex_env] = true - end - end - rl.keys.each do |_env| - if _env && _env.record.binding[var_sym] - _env.record.binding[var_sym2] = _env.record.binding[var_sym] - _env.record.binding.delete var_sym - end - end + end - nesting_vars_list.each do |x| - if x.val.to_sym == var_sym - x.instance_eval{ - @val = var_sym2 - } - end - raise 'error' if x.binding_env(:var).nil? - raise 'error' if x.binding_env(:lex).nil? + nesting_vars_list.each do |x| + if x.val.to_sym == var_sym + x.instance_eval{ + @val = var_sym2 + } end + #raise 'error' if x.binding_env(:var).nil? + raise 'error' if x.binding_env(:lex).nil? end - rename_table[name] = var_sym - var_sym = next_sym(var_sym) - } - var_vars_list.each {|st2| - raise 'error' if st2.binding_env(:var).nil? - raise 'error' if st2.binding_env(:lex).nil? - } + end + rename_table[name] = var_sym + var_sym = next_sym(var_sym) + } + var_vars_list.each {|st2| + raise st2.to_js if st2.binding_env(:lex).nil? + } - rename_table.each do |name, new_name| - if name != new_name - if st.context.var_env.record.binding[name] - st.context.var_env.record.binding[new_name] = st.context.var_env.record.binding[name] - st.context.var_env.record.binding.delete(name) - end - if st.context.lex_env.record.binding[name] - st.context.lex_env.record.binding[new_name] = st.context.lex_env.record.binding[name] - st.context.lex_env.record.binding.delete(name) - end + rename_table.each do |name, new_name| + if name != new_name + if context.var_env.record.binding[name] + context.var_env.record.binding[new_name] = context.var_env.record.binding[name] + context.var_env.record.binding.delete(name) end + if context.lex_env.record.binding[name] + context.lex_env.record.binding[new_name] = context.lex_env.record.binding[name] + context.lex_env.record.binding.delete(name) + end end + end - var_vars_list.each {|st2| - st2.instance_eval{ - @val = rename_table[@val] - } - raise 'error' if st2.binding_env(:var).nil? - raise 'error' if st2.binding_env(:lex).nil? + var_vars_list.each {|st2| + st2.instance_eval{ + @val = rename_table[@val] } - end + #raise 'error' if st2.binding_env(:var).nil? + raise 'error' if st2.binding_env(:lex).nil? + } } self end def reduce_exp(node = @prog) @@ -674,28 +742,33 @@ parent.replace(st, ECMA262::ExpParen.new(ECMA262::ExpLogicalNot.new(ECMA262::ECMA262Numeric.new(0)))) else parent.replace(st, ECMA262::ExpParen.new(ECMA262::ExpLogicalNot.new(ECMA262::ECMA262Numeric.new(1)))) end # - #if(true){<then>}else{<else>} => then + #if(true){<then>}else{<else>} => <then> + #if(false){<then>}else{<else>} => <else> # elsif st.kind_of? ECMA262::StIf if st.cond.respond_to? :to_ecma262_boolean - if st.cond.to_ecma262_boolean + if st.cond.to_ecma262_boolean.nil? + ; + elsif st.cond.to_ecma262_boolean == true parent.replace(st, st.then_st) - elsif st.else_st + elsif st.cond.to_ecma262_boolean == false and st.else_st parent.replace(st, st.else_st) - else - parent.replace(st, ECMA262::StEmpty.new()) + elsif st.cond.to_ecma262_boolean == false + parent.replace(st, ECMA262::StEmpty.new) end end # # while(true) => for(;;) # while(false) => remove # elsif st.kind_of? ECMA262::StWhile and st.exp.respond_to? :to_ecma262_boolean - if st.exp.to_ecma262_boolean + if st.exp.to_ecma262_boolean.nil? + ; + elsif st.exp.to_ecma262_boolean parent.replace(st, ECMA262::StFor.new(nil,nil,nil, st.statement)) else parent.replace(st, ECMA262::StEmpty.new) end # @@ -754,9 +827,38 @@ st.replace(st.else_st, nil) st.replace(st.then_st, else_st) parent.replace(st, ECMA262::StBlock.new([st])) retry_flag = true end +=begin +#feature + # + #if(!(a&&b)) + #=> + #if(!a||!b) + # + #if(!(a||b)) + #=> + #if(!a&&!b) + # + if st.cond.kind_of? ECMA262::ExpLogicalNot and st.cond.val.kind_of? ECMA262::ExpParen and + st.cond.val.val.kind_of? ECMA262::ExpLogicalAnd + a = ECMA262::ExpLogicalNot.new(st.cond.val.val.val) + b = ECMA262::ExpLogicalNot.new(st.cond.val.val.val2) + r = ECMA262::ExpLogicalOr.new(a,b).add_paren.remove_paren + if r.to_js.length < st.cond.to_js.length + st.replace(st.cond, r) + end + elsif st.cond.kind_of? ECMA262::ExpLogicalNot and st.cond.val.kind_of? ECMA262::ExpParen and + st.cond.val.val.kind_of? ECMA262::ExpLogicalOr + a = ECMA262::ExpLogicalNot.new(st.cond.val.val.val) + b = ECMA262::ExpLogicalNot.new(st.cond.val.val.val2) + r = ECMA262::ExpLogicalAnd.new(a,b).add_paren.remove_paren + if r.to_js.length < st.cond.to_js.length + st.replace(st.cond, r) + end + end +=end end } block_to_statement if retry_flag end self