Commit c00e0394 authored by Rene Saarsoo's avatar Rene Saarsoo
Browse files

Merge branch 'esprima-parser'

parents af63dd84 eb29b737
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -144,6 +144,9 @@ Thanks to [Ondřej Jirman](https://github.com/megous),
Katherine Chu,
[Rob Dougan](https://github.com/rdougan),
[Dave Thompson](https://github.com/limscoder),
[burnnat](https://github.com/burnnat),
[vjetteam](https://github.com/vjetteam),
[Chris Westbrook](https://github.com/cnstaging),
and many-many others who reported bugs, submitted patches, and
provided a lot of useful input.

+4 −4
Original line number Diff line number Diff line
# Script for benchmarking the lexer.
# Script for benchmarking JavaScript parser.
#
# Takes bunch of filenames as arguments and runs them all through lexer.
# Takes bunch of filenames as arguments and runs them all through JsParser.
#
$:.unshift File.dirname(File.dirname(__FILE__)) + "/lib"
require 'jsduck/lexer'
require 'jsduck/js_parser'

ARGV.each do |fname|
  JsDuck::Lexer.new(IO.read(fname))
  JsDuck::JsParser.new(IO.read(fname)).parse
end

benchmark/serialize.rb

0 → 100644
+14 −0
Original line number Diff line number Diff line
# Script for benchmarking the Serializer.
#
# Takes bunch of filenames as arguments, runs them all through esprima
# parser and serializes the resulting syntax trees.
#
$:.unshift File.dirname(File.dirname(__FILE__)) + "/lib"
require 'jsduck/esprima'
require 'jsduck/serializer'

ARGV.each do |fname|
  ast = JsDuck::Esprima.instance.parse(IO.read(fname))
  JsDuck::Serializer.new.to_s(ast)
end
+199 −37
Original line number Diff line number Diff line
#!/usr/bin/env ruby
# Compare .json files of different ExtJS versions
# Produces diff of two JSDuck exports.

# For running when gem not installed
$:.unshift File.dirname(File.dirname(__FILE__)) + "/lib"

require "rubygems"
require "cgi"
require "optparse"
require "jsduck/json_duck"

options = {
  :title => "Comparison of Ext 4.0.7 and Ext 4.1.1",
}

input_files = OptionParser.new do |opts|
  opts.banner = "Produces diff of two JSDuck exports.\n\n" +
    "Usage: compare [options] old/classes/ new/classes/ output.html\n\n"

  opts.on("--ignore=FILE", "A file listing members to be ignored") do |file|
    options[:ignore] = file
  end

  opts.on("--type=NAME", "Only produce diff of cfg, property, method or event.") do |name|
    options[:type] = name
  end

  opts.on("--title=TEXT", "Title for the generated HTML page.",
    "Defaults to: 'Comparison of Ext 4.0.7 and Ext 4.1.1'") do |text|
    options[:title] = text
  end

  opts.on("-h", "--help", "Show this help message") do
    puts opts
    exit
  end
end.parse!

if input_files.length == 3
  options[:old_classes] = input_files[0]
  options[:new_classes] = input_files[1]
  options[:out_file] = input_files[2]
else
  puts "You must specify exactly 3 filenames: old/classes/ new/classes/ output.html"
  exit 1
end


def read_class(filename)
  JsDuck::JsonDuck.read(filename)
end

# loops through all members of class
def each_member(cls)
  ["members", "statics"].each do |category|
    cls[category].each_pair do |k, members|
      members.each do |m|
        yield m
      end
    end
  end
end

def discard_docs(cls)
  each_member(cls) do |m|
    m["doc"] = nil
    m["params"] = nil
  end
  cls
end

def read_all_classes(dir, ignore_private=false)
  map = {}
  Dir[dir+"/*.json"].each do |filename|
    print "."
    STDOUT.flush
    cls = read_class(filename)
    cls = discard_docs(read_class(filename))
    unless ignore_private && cls["private"]
      map[cls["name"]] = cls
      cls["alternateClassNames"].each do |name|
@@ -40,24 +98,86 @@ def normal_public_member?(m)
  !m["private"] && !m["meta"]["protected"] && !m["meta"]["deprecated"] && !m["meta"]["removed"]
end

# Gathers class members that are in cls1, but are missing in cls2
# Ignoring members listed in ignored_members hash.
def compare_classes(cls1, cls2, ignored_members)
def unify_default(v)
  (v || "undefined").gsub(/"/, "'").sub(/^Ext\.baseCSSPrefix \+ '/, "'x-")
end

def visibility(m)
  if m["private"]
    "private"
  elsif m["meta"]["protected"]
    "protected"
  else
    "public"
  end
end

# Finds equivalent member to given member from class.
# Returns the member found or nil.
def find_member(cls, member)
  cls[member["meta"]["static"] ? "statics" : "members"][member["tagname"]].find do |m|
    member["name"] == m["name"]
  end
end


# Compares members of cls1 to cls2.
# Returns diff array of added, removed and changed members.
def compare_classes(cls1, cls2)
  diff = []
  cls1["members"].each_pair do |group_name, group_items|
    group_items.find_all {|m1| normal_public_member?(m1) && m1["owner"] == cls1["name"] }.each do |m1|
      match = cls2["members"][group_name].find do |m2|
        m2["name"] == m1["name"] && !m2["meta"]["protected"] && !m2["private"]

  checked_members = {}

  each_member(cls1) do |m1|
    next unless normal_public_member?(m1) && m1["owner"] == cls1["name"]

    checked_members[m1["id"]] = true

    m2 = find_member(cls2, m1)

    if m2
      changes = []
      if m1["type"] != m2["type"]
        changes << {
          :what => "type",
          :a => m1["type"],
          :b => m2["type"],
        }
      end
      if unify_default(m1["default"]) != unify_default(m2["default"])
        changes << {
          :what => "default",
          :a => unify_default(m1["default"]),
          :b => unify_default(m2["default"]),
        }
      end
      if visibility(m1) != visibility(m2)
        changes << {
          :what => "visibility",
          :a => visibility(m1),
          :b => visibility(m2),
        }
      end
      if !match && m1["name"] != "constructor" && m1["name"] != "" && !ignored_members[cls1["name"]+"#"+m1["name"]]

      if changes.length > 0
        diff << {
          :type => m1["tagname"],
          :name => m1["name"],
          :what => "changed",
          :changes => changes,
        }
      end

    elsif !m2 && m1["name"] != "constructor" && m1["name"] != ""
      other = nil
      ["cfg", "property", "method", "event"].each do |g|
        other = other || cls2["members"][g].find {|m2| m2["name"] == m1["name"] }
        other = other || cls2["statics"][g].find {|m2| m2["name"] == m1["name"] }
      end
      diff << {
          :type => group_name,
        :type => m1["tagname"],
        :name => m1["name"],
        :what => "removed",
        :other => other ? {
          :type => other["tagname"],
          :static => other["meta"]["static"],
@@ -67,15 +187,47 @@ def compare_classes(cls1, cls2, ignored_members)
      }
    end
  end

  each_member(cls2) do |m2|
    next unless normal_public_member?(m2) && m2["owner"] == cls2["name"] && !checked_members[m2["id"]]

    m1 = find_member(cls1, m2)

    if m1
      changes = []
      if visibility(m1) != visibility(m2)
        changes << {
          :what => "visibility",
          :a => visibility(m1),
          :b => visibility(m2),
        }
      end

      if changes.length > 0
        diff << {
          :type => m2["tagname"],
          :name => m2["name"],
          :what => "changed",
          :changes => changes,
        }
      end

    elsif !m1 && m2["name"] != "constructor" && m2["name"] != ""
      diff << {
        :type => m2["tagname"],
        :name => m2["name"],
        :what => "added",
      }
    end
  end

  diff
end


old_classes = read_all_classes(ARGV[0], :ignore_private)
new_classes = read_all_classes(ARGV[1])
out_file = ARGV[2]
ignored_members = ARGV[3] ? read_ignored_members(ARGV[3]) : {}
old_classes = read_all_classes(options[:old_classes], :ignore_private)
new_classes = read_all_classes(options[:new_classes])
ignored_members = options[:ignore] ? read_ignored_members(options[:ignore]) : {}

# Explicit remapping of classes
remap = {
@@ -101,16 +253,23 @@ old_classes.each_pair do |name, cls|
      :name => cls["name"],
      :found => !!new_cls || ignored_members[name],
      :new_name => new_cls && new_cls["name"],
      :diff => new_cls ? compare_classes(cls, new_cls, ignored_members) : [],
      :diff => new_cls ? compare_classes(cls, new_cls) : [],
    }
  end
end

# Throw away ignored members and everything except configs
diff_data.each do |cls|
  cls[:diff] = cls[:diff].find_all do |m|
    !ignored_members[cls[:name]+"#"+m[:name]] && (!options[:type] || m[:type] == options[:type])
  end
end

# Choose title based on filename
if out_file =~ /touch/
if options[:out_file] =~ /touch/
  title = "Comparison of Touch 1.1.1 and Touch 2.0.0"
else
  title = "Comparison of Ext 4.0.7 and Ext 4.1.0"
  title = "Comparison of Ext 4.0.7 and Ext 4.1.1"
end

# do HTML output
@@ -119,7 +278,7 @@ html << <<-EOHTML
<!DOCTYPE html>
<html>
<head>
<title>#{title}</title>
<title>#{options[:title]}</title>
<style type="text/css">
body { font-family: Georgia, serif; }
li h2 { font-size: medium; font-weight: normal; }
@@ -136,7 +295,7 @@ $(function() {
</script>
</head>
<body>
<h1>#{title}</h1>
<h1>#{options[:title]}</h1>
EOHTML

html << "<ul>"
@@ -146,20 +305,24 @@ diff_data.each do |cls|
    html << "<li>"
    if cls[:found]
      new_name = (cls[:new_name] == cls[:name] ? "" : " --> " + cls[:new_name])
      diff_count = cls[:diff].length > 0 ? "(#{cls[:diff].length} missing members)" : ""
      diff_count = cls[:diff].length > 0 ? "(#{cls[:diff].length} changes)" : ""
      link = cls[:diff].length > 0 ? "<a href='#expand'>#{cls[:name]}</a>" : cls[:name]
      html << "<h2>#{link} #{new_name} #{diff_count}</h2>"
      if cls[:diff].length > 0
        html << "<ul>"
        cls[:diff].each do |m|
          html << "<li>"
          html << m[:type] + " " + m[:name]
          html << m[:what] + " " + m[:type] + " " + m[:name]
          if m[:other]
            o = m[:other]
            stat = o[:static] ? 'static' : ''
            priv = o[:private] ? 'private' : ''
            prot = o[:protected] ? 'protected' : ''
            html << " (found #{stat} #{priv} #{prot} #{o[:type]} with the same name)"
          elsif m[:changes]
            m[:changes].each do |c|
              html << " (#{c[:what]} changed from #{CGI.escapeHTML(c[:a])} to #{CGI.escapeHTML(c[:b])})"
            end
          end
          html << "</li>"
        end
@@ -178,12 +341,11 @@ dd = diff_data
html << "<p>" + dd.find_all {|c| !c[:found] }.length.to_s + " classes not found</p>"
html << "<p>" + dd.find_all {|c| c[:found] && c[:name] == c[:new_name] }.length.to_s + " classes with same name</p>"
html << "<p>" + dd.find_all {|c| c[:found] && c[:name] != c[:new_name] }.length.to_s + " renamed classes</p>"
html << "<p>" + dd.find_all {|c| c[:diff].length > 0 }.length.to_s + " classes with missing members</p>"
html << "<p>" + dd.map {|c| c[:diff].length }.inject {|sum,x| sum + x }.to_s + " missing members</p>"
html << "<p>" + dd.map {|c| c[:diff].find_all {|m| !m[:other]}.length }.inject {|sum,x| sum + x }.to_s + " completely missing members</p>"
html << "<p>" + dd.find_all {|c| c[:diff].length > 0 }.length.to_s + " classes with changed members</p>"
html << "<p>" + dd.map {|c| c[:diff].length }.inject {|sum,x| sum + x }.to_s + " changed members</p>"


html << "</body>"
html << "</html>"

File.open(out_file, 'w') {|f| f.write(html.join("\n")) }
File.open(options[:out_file], 'w') {|f| f.write(html.join("\n")) }

bin/graph

0 → 100755
+52 −0
Original line number Diff line number Diff line
#!/usr/bin/env ruby

# For running when gem not installed
$:.unshift File.dirname(File.dirname(__FILE__)) + "/lib"

require "rubygems"
require "pp"
require "jsduck/json_duck"

def with_each_class(dir)
  Dir[dir+"/*.json"].each do |filename|
    yield JsDuck::JsonDuck.read(filename)
  end
end

def quote(a)
  '"'+a+'"'
end

def arrow(a, b, opts="")
  "  #{quote(a)}->#{quote(b)} #{opts};"
end

input_dir = ARGV[0]

# Build a map that links each classname or alternate classname to its
# canonical form
$canonical_map = {}
with_each_class(input_dir) do |cls|
  $canonical_map[cls["name"]] = cls["name"]
  cls["alternateClassNames"].each do |name|
    $canonical_map[name] = cls["name"]
  end
end

def canonical(name)
  $canonical_map[name] || name
end

# Print out the graph description
puts 'digraph G {'
puts 'rankdir=LR;'
with_each_class(input_dir) do |cls|
  if cls["extends"] && cls["extends"] != "Object"
    puts arrow(canonical(cls["extends"]), cls['name'], '[style=bold,weight=10]')
  end
  if cls["mixins"]
    cls["mixins"].each {|mx| puts arrow(canonical(mx), cls['name'], '[weight=1,style=dashed]') }
  end
end
puts '}'
Loading