jsduck 4.10.4 → 5.0.0.beta01
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +0 -1
- data/README.md +32 -6
- data/Rakefile +10 -18
- data/bin/compare +5 -5
- data/bin/jsduck +2 -3
- data/jsduck.gemspec +3 -4
- data/lib/jsduck/aggregator.rb +21 -80
- data/lib/jsduck/app.rb +7 -14
- data/lib/jsduck/app_data.rb +4 -5
- data/lib/jsduck/assets.rb +4 -7
- data/lib/jsduck/base_type.rb +53 -0
- data/lib/jsduck/batch_parser.rb +8 -87
- data/lib/jsduck/batch_processor.rb +77 -0
- data/lib/jsduck/categories/auto.rb +83 -0
- data/lib/jsduck/categories/class_name.rb +63 -0
- data/lib/jsduck/categories/factory.rb +113 -0
- data/lib/jsduck/categories/file.rb +75 -0
- data/lib/jsduck/class.rb +3 -9
- data/lib/jsduck/class_doc_expander.rb +1 -1
- data/lib/jsduck/css/lexer.rb +203 -0
- data/lib/jsduck/css/parser.rb +121 -0
- data/lib/jsduck/doc/comment.rb +40 -0
- data/lib/jsduck/doc/map.rb +23 -0
- data/lib/jsduck/doc/parser.rb +128 -0
- data/lib/jsduck/doc/processor.rb +52 -0
- data/lib/jsduck/doc/scanner.rb +76 -0
- data/lib/jsduck/doc/standard_tag_parser.rb +154 -0
- data/lib/jsduck/doc/subproperties.rb +64 -0
- data/lib/jsduck/docs_code_comparer.rb +31 -0
- data/lib/jsduck/export_writer.rb +2 -2
- data/lib/jsduck/exporter/app.rb +16 -4
- data/lib/jsduck/exporter/full.rb +2 -2
- data/lib/jsduck/format/batch.rb +58 -0
- data/lib/jsduck/format/class.rb +62 -0
- data/lib/jsduck/format/doc.rb +172 -0
- data/lib/jsduck/format/html_stack.rb +109 -0
- data/lib/jsduck/format/shortener.rb +55 -0
- data/lib/jsduck/format/subproperties.rb +64 -0
- data/lib/jsduck/guides.rb +32 -14
- data/lib/jsduck/index_html.rb +3 -1
- data/lib/jsduck/inline/auto_link.rb +2 -2
- data/lib/jsduck/inline/link.rb +4 -3
- data/lib/jsduck/inline/link_renderer.rb +2 -2
- data/lib/jsduck/inline/video.rb +8 -2
- data/lib/jsduck/js/ast.rb +361 -0
- data/lib/jsduck/js/esprima.rb +39 -0
- data/lib/jsduck/{esprima → js/esprima}/esprima.js +0 -0
- data/lib/jsduck/js/evaluator.rb +70 -0
- data/lib/jsduck/js/ext_patterns.rb +70 -0
- data/lib/jsduck/js/function.rb +206 -0
- data/lib/jsduck/js/node.rb +194 -0
- data/lib/jsduck/js/node_array.rb +36 -0
- data/lib/jsduck/js/parser.rb +223 -0
- data/lib/jsduck/js/serializer.rb +263 -0
- data/lib/jsduck/js/utils.rb +21 -0
- data/lib/jsduck/logger.rb +3 -13
- data/lib/jsduck/members_index.rb +3 -4
- data/lib/jsduck/merger.rb +25 -145
- data/lib/jsduck/options.rb +29 -132
- data/lib/jsduck/parser.rb +76 -0
- data/lib/jsduck/process/accessors.rb +133 -0
- data/lib/jsduck/process/circular_deps.rb +58 -0
- data/lib/jsduck/process/enums.rb +91 -0
- data/lib/jsduck/process/ext4_events.rb +43 -0
- data/lib/jsduck/process/global_members.rb +36 -0
- data/lib/jsduck/process/ignored_classes.rb +16 -0
- data/lib/jsduck/process/importer.rb +58 -0
- data/lib/jsduck/process/inherit_doc.rb +197 -0
- data/lib/jsduck/process/lint.rb +135 -0
- data/lib/jsduck/process/overrides.rb +99 -0
- data/lib/jsduck/process/return_values.rb +72 -0
- data/lib/jsduck/process/versions.rb +102 -0
- data/lib/jsduck/relations.rb +5 -0
- data/lib/jsduck/render/class.rb +144 -0
- data/lib/jsduck/render/sidebar.rb +97 -0
- data/lib/jsduck/render/signature.rb +94 -0
- data/lib/jsduck/render/subproperties.rb +99 -0
- data/lib/jsduck/render/tags.rb +38 -0
- data/lib/jsduck/search_data.rb +19 -13
- data/lib/jsduck/source/file.rb +8 -17
- data/lib/jsduck/tag/abstract.rb +4 -7
- data/lib/jsduck/tag/accessor.rb +10 -0
- data/lib/jsduck/tag/alias.rb +61 -0
- data/lib/jsduck/tag/alternate_class_names.rb +17 -0
- data/lib/jsduck/tag/aside.rb +28 -31
- data/lib/jsduck/tag/author.rb +9 -5
- data/lib/jsduck/tag/boolean_tag.rb +24 -0
- data/lib/jsduck/tag/cfg.rb +45 -0
- data/lib/jsduck/tag/chainable.rb +5 -7
- data/lib/jsduck/tag/class.rb +28 -0
- data/lib/jsduck/tag/class_list_tag.rb +40 -0
- data/lib/jsduck/tag/constructor.rb +24 -0
- data/lib/jsduck/tag/css_mixin.rb +17 -0
- data/lib/jsduck/tag/css_var.rb +29 -0
- data/lib/jsduck/tag/default.rb +31 -0
- data/lib/jsduck/tag/deprecated.rb +13 -27
- data/lib/jsduck/tag/deprecated_tag.rb +58 -0
- data/lib/jsduck/tag/doc.rb +32 -0
- data/lib/jsduck/tag/docauthor.rb +4 -5
- data/lib/jsduck/tag/enum.rb +70 -0
- data/lib/jsduck/tag/event.rb +28 -0
- data/lib/jsduck/tag/evented.rb +10 -0
- data/lib/jsduck/tag/extends.rb +45 -0
- data/lib/jsduck/tag/ftype.rb +18 -0
- data/lib/jsduck/tag/hide.rb +4 -11
- data/lib/jsduck/tag/ignore.rb +6 -7
- data/lib/jsduck/tag/inheritable.rb +10 -0
- data/lib/jsduck/tag/inheritdoc.rb +48 -0
- data/lib/jsduck/tag/markdown.rb +8 -6
- data/lib/jsduck/tag/member.rb +24 -0
- data/lib/jsduck/tag/method.rb +35 -0
- data/lib/jsduck/tag/mixins.rb +26 -0
- data/lib/jsduck/tag/name.rb +36 -0
- data/lib/jsduck/tag/new.rb +13 -27
- data/lib/jsduck/tag/override.rb +37 -0
- data/lib/jsduck/tag/overrides.rb +29 -0
- data/lib/jsduck/tag/param.rb +87 -0
- data/lib/jsduck/tag/preventable.rb +19 -10
- data/lib/jsduck/tag/private.rb +28 -13
- data/lib/jsduck/tag/property.rb +39 -0
- data/lib/jsduck/tag/protected.rb +5 -7
- data/lib/jsduck/tag/ptype.rb +18 -0
- data/lib/jsduck/tag/readonly.rb +4 -7
- data/lib/jsduck/tag/removed.rb +21 -29
- data/lib/jsduck/tag/required.rb +11 -9
- data/lib/jsduck/tag/requires.rb +12 -0
- data/lib/jsduck/tag/return.rb +47 -0
- data/lib/jsduck/tag/since.rb +19 -11
- data/lib/jsduck/tag/singleton.rb +15 -0
- data/lib/jsduck/tag/static.rb +5 -7
- data/lib/jsduck/tag/subproperties.rb +23 -0
- data/lib/jsduck/tag/tag.rb +208 -0
- data/lib/jsduck/tag/template.rb +14 -9
- data/lib/jsduck/tag/throws.rb +38 -0
- data/lib/jsduck/tag/type.rb +48 -0
- data/lib/jsduck/tag/uses.rb +12 -0
- data/lib/jsduck/tag/xtype.rb +30 -0
- data/lib/jsduck/tag_loader.rb +39 -0
- data/lib/jsduck/tag_registry.rb +189 -0
- data/lib/jsduck/type_parser.rb +3 -3
- data/lib/jsduck/web_writer.rb +2 -2
- data/lib/jsduck/welcome.rb +1 -1
- metadata +578 -538
- data/lib/jsduck/accessors.rb +0 -136
- data/lib/jsduck/ast.rb +0 -524
- data/lib/jsduck/auto_categories.rb +0 -80
- data/lib/jsduck/batch_formatter.rb +0 -60
- data/lib/jsduck/categories.rb +0 -73
- data/lib/jsduck/categories_class_name.rb +0 -37
- data/lib/jsduck/circular_deps.rb +0 -56
- data/lib/jsduck/class_formatter.rb +0 -102
- data/lib/jsduck/columns.rb +0 -56
- data/lib/jsduck/css_lexer.rb +0 -201
- data/lib/jsduck/css_parser.rb +0 -119
- data/lib/jsduck/doc_ast.rb +0 -319
- data/lib/jsduck/doc_formatter.rb +0 -142
- data/lib/jsduck/doc_parser.rb +0 -611
- data/lib/jsduck/doc_type.rb +0 -59
- data/lib/jsduck/enum.rb +0 -73
- data/lib/jsduck/esprima.rb +0 -51
- data/lib/jsduck/evaluator.rb +0 -69
- data/lib/jsduck/ext_patterns.rb +0 -58
- data/lib/jsduck/file_categories.rb +0 -76
- data/lib/jsduck/function_ast.rb +0 -206
- data/lib/jsduck/guide_anchors.rb +0 -32
- data/lib/jsduck/guide_toc.rb +0 -49
- data/lib/jsduck/html_stack.rb +0 -105
- data/lib/jsduck/importer.rb +0 -121
- data/lib/jsduck/inherit_doc.rb +0 -193
- data/lib/jsduck/js_parser.rb +0 -221
- data/lib/jsduck/lint.rb +0 -133
- data/lib/jsduck/meta_tag.rb +0 -88
- data/lib/jsduck/meta_tag_loader.rb +0 -67
- data/lib/jsduck/meta_tag_registry.rb +0 -111
- data/lib/jsduck/meta_tag_renderer.rb +0 -34
- data/lib/jsduck/news.rb +0 -128
- data/lib/jsduck/override.rb +0 -87
- data/lib/jsduck/renderer.rb +0 -361
- data/lib/jsduck/return_values.rb +0 -72
- data/lib/jsduck/serializer.rb +0 -262
- data/lib/jsduck/shortener.rb +0 -58
- data/lib/jsduck/signature_renderer.rb +0 -91
- data/lib/jsduck/source/file_parser.rb +0 -72
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'v8'
|
2
|
+
require 'jsduck/util/json'
|
3
|
+
require 'jsduck/util/singleton'
|
4
|
+
|
5
|
+
module JsDuck
|
6
|
+
module Js
|
7
|
+
|
8
|
+
# Runs Esprima.js through V8.
|
9
|
+
#
|
10
|
+
# Initialized as singleton to avoid loading the esprima.js more
|
11
|
+
# than once - otherwise performace will severely suffer.
|
12
|
+
class Esprima
|
13
|
+
include Util::Singleton
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
@v8 = V8::Context.new
|
17
|
+
esprima = File.dirname(File.expand_path(__FILE__))+"/esprima/esprima.js";
|
18
|
+
|
19
|
+
# Esprima attempts to assign to window.esprima, but our v8
|
20
|
+
# engine has no global window variable defined. So define our
|
21
|
+
# own and then grab esprima out from it again.
|
22
|
+
@v8.eval("var window = {};")
|
23
|
+
@v8.load(esprima)
|
24
|
+
@v8.eval("var esprima = window.esprima;")
|
25
|
+
end
|
26
|
+
|
27
|
+
# Parses JavaScript source code using Esprima.js
|
28
|
+
#
|
29
|
+
# Returns the resulting AST
|
30
|
+
def parse(input)
|
31
|
+
@v8['js'] = input
|
32
|
+
json = @v8.eval("JSON.stringify(esprima.parse(js, {comment: true, range: true, raw: true}))")
|
33
|
+
return Util::Json.parse(json, :max_nesting => false)
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
File without changes
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module JsDuck
|
2
|
+
module Js
|
3
|
+
|
4
|
+
# Evaluates Esprima AST node into Ruby object
|
5
|
+
class Evaluator
|
6
|
+
|
7
|
+
# Converts AST node into a value.
|
8
|
+
#
|
9
|
+
# - String literals become Ruby strings
|
10
|
+
# - Number literals become Ruby numbers
|
11
|
+
# - Regex literals become :regexp symbols
|
12
|
+
# - Array expressions become Ruby arrays
|
13
|
+
# - etc
|
14
|
+
#
|
15
|
+
# For anything it doesn't know how to evaluate (like a function
|
16
|
+
# expression) it throws exception.
|
17
|
+
#
|
18
|
+
def to_value(ast)
|
19
|
+
case ast["type"]
|
20
|
+
when "ArrayExpression"
|
21
|
+
ast["elements"].map {|e| to_value(e) }
|
22
|
+
when "ObjectExpression"
|
23
|
+
h = {}
|
24
|
+
ast["properties"].each do |p|
|
25
|
+
key = key_value(p["key"])
|
26
|
+
value = to_value(p["value"])
|
27
|
+
h[key] = value
|
28
|
+
end
|
29
|
+
h
|
30
|
+
when "BinaryExpression"
|
31
|
+
if ast["operator"] == "+"
|
32
|
+
to_value(ast["left"]) + to_value(ast["right"])
|
33
|
+
else
|
34
|
+
throw "Unable to handle operator: " + ast["operator"]
|
35
|
+
end
|
36
|
+
when "MemberExpression"
|
37
|
+
if base_css_prefix?(ast)
|
38
|
+
"x-"
|
39
|
+
else
|
40
|
+
throw "Unable to handle this MemberExpression"
|
41
|
+
end
|
42
|
+
when "Literal"
|
43
|
+
if ast["raw"] =~ /\A\//
|
44
|
+
:regexp
|
45
|
+
else
|
46
|
+
ast["value"]
|
47
|
+
end
|
48
|
+
else
|
49
|
+
throw "Unknown node type: " + ast["type"]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Turns object property key into string value
|
54
|
+
def key_value(key)
|
55
|
+
key["type"] == "Identifier" ? key["name"] : key["value"]
|
56
|
+
end
|
57
|
+
|
58
|
+
# True when MemberExpression == Ext.baseCSSPrefix
|
59
|
+
def base_css_prefix?(ast)
|
60
|
+
ast["computed"] == false &&
|
61
|
+
ast["object"]["type"] == "Identifier" &&
|
62
|
+
ast["object"]["name"] == "Ext" &&
|
63
|
+
ast["property"]["type"] == "Identifier" &&
|
64
|
+
ast["property"]["name"] == "baseCSSPrefix"
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require "jsduck/util/singleton"
|
2
|
+
|
3
|
+
module JsDuck
|
4
|
+
module Js
|
5
|
+
|
6
|
+
# Identifies Ext JS builtins like Ext.define and Ext.extend, taking
|
7
|
+
# also into account the possibility of aliasing the Ext namespace.
|
8
|
+
#
|
9
|
+
# For example when the following command line option is used:
|
10
|
+
#
|
11
|
+
# --ext-namespaces=Ext,MyApp
|
12
|
+
#
|
13
|
+
# we need to identify both Ext.define and MyApp.define, but
|
14
|
+
# Ext.define is additionally aliased withing ExtJS as
|
15
|
+
# Ext.ClassManager.create, so we also need to recognize
|
16
|
+
# Ext.ClassManager.create and MyApp.ClassManager.create.
|
17
|
+
#
|
18
|
+
# The matches? method will take care of identifying all these four
|
19
|
+
# cases:
|
20
|
+
#
|
21
|
+
# ExtPatterns.set(["Ext", "MyApp"])
|
22
|
+
# ExtPatterns.matches?("Ext.define", "MyApp.define") --> true
|
23
|
+
#
|
24
|
+
class ExtPatterns
|
25
|
+
include Util::Singleton
|
26
|
+
|
27
|
+
def initialize
|
28
|
+
set(["Ext"])
|
29
|
+
end
|
30
|
+
|
31
|
+
# True when string matches the given pattern type.
|
32
|
+
#
|
33
|
+
# Pattern type is one of: "Ext.define", "Ext.extend",
|
34
|
+
# "Ext.override", "Ext.emptyFn"
|
35
|
+
def matches?(pattern, string)
|
36
|
+
@patterns[pattern].include?(string)
|
37
|
+
end
|
38
|
+
|
39
|
+
# Reconfigures ExtPatterns with different set of namespaces.
|
40
|
+
# Called when --ext-namespaces option is passed to JSDuck.
|
41
|
+
def set(namespaces)
|
42
|
+
@patterns = {
|
43
|
+
"Ext.define" => build_patterns(namespaces, [".define", ".ClassManager.create"]),
|
44
|
+
"Ext.extend" => build_patterns(namespaces, [".extend"]),
|
45
|
+
"Ext.override" => build_patterns(namespaces, [".override"]),
|
46
|
+
"Ext.emptyFn" => build_patterns(namespaces, [".emptyFn"]),
|
47
|
+
}
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
# Given Array of alternate Ext namespaces builds list of patterns
|
53
|
+
# for detecting Ext.define or some other construct:
|
54
|
+
#
|
55
|
+
# build_patterns(["Ext", "Foo"], [".define"]) --> ["Ext.define", "Foo.define"]
|
56
|
+
#
|
57
|
+
def build_patterns(namespaces, suffixes)
|
58
|
+
patterns = []
|
59
|
+
namespaces.each do |ns|
|
60
|
+
suffixes.each do |suffix|
|
61
|
+
patterns << ns + suffix
|
62
|
+
end
|
63
|
+
end
|
64
|
+
patterns
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,206 @@
|
|
1
|
+
require "jsduck/util/singleton"
|
2
|
+
|
3
|
+
module JsDuck
|
4
|
+
module Js
|
5
|
+
|
6
|
+
# Analyzes the AST of a FunctionDeclaration or FunctionExpression.
|
7
|
+
class Function
|
8
|
+
include Util::Singleton
|
9
|
+
|
10
|
+
# Detects possible return types of the given function.
|
11
|
+
#
|
12
|
+
# For now there are three possible detected return values:
|
13
|
+
#
|
14
|
+
# * :this - the code contins 'return this;'
|
15
|
+
#
|
16
|
+
# * "undefined" - the code finishes by returning undefined or
|
17
|
+
# without explicitly returning anything
|
18
|
+
#
|
19
|
+
# * :other - some other value is returned.
|
20
|
+
#
|
21
|
+
def return_types(ast)
|
22
|
+
h = return_types_hash(ast["body"]["body"])
|
23
|
+
|
24
|
+
# Replace the special :void value that signifies possibility of
|
25
|
+
# exiting without explicitly returning anything
|
26
|
+
if h[:void]
|
27
|
+
h["undefined"] = true
|
28
|
+
h.delete(:void)
|
29
|
+
end
|
30
|
+
|
31
|
+
h.keys
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def return_types_hash(body)
|
37
|
+
rvalues = {}
|
38
|
+
body.each do |ast|
|
39
|
+
if return?(ast)
|
40
|
+
type = value_type(ast["argument"])
|
41
|
+
rvalues[type] = true
|
42
|
+
return rvalues
|
43
|
+
elsif possibly_blocking?(ast)
|
44
|
+
extract_bodies(ast).each do |b|
|
45
|
+
rvalues.merge!(return_types_hash(b))
|
46
|
+
end
|
47
|
+
if !rvalues[:void]
|
48
|
+
return rvalues
|
49
|
+
else
|
50
|
+
rvalues.delete(:void)
|
51
|
+
end
|
52
|
+
elsif control_flow?(ast)
|
53
|
+
extract_bodies(ast).each do |b|
|
54
|
+
rvalues.merge!(return_types_hash(b))
|
55
|
+
end
|
56
|
+
rvalues.delete(:void)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
rvalues[:void] = true
|
61
|
+
return rvalues
|
62
|
+
end
|
63
|
+
|
64
|
+
def return?(ast)
|
65
|
+
ast["type"] == "ReturnStatement"
|
66
|
+
end
|
67
|
+
|
68
|
+
def value_type(ast)
|
69
|
+
if !ast
|
70
|
+
:void
|
71
|
+
elsif undefined?(ast) || void?(ast)
|
72
|
+
"undefined"
|
73
|
+
elsif this?(ast)
|
74
|
+
:this
|
75
|
+
elsif boolean?(ast)
|
76
|
+
"Boolean"
|
77
|
+
elsif string?(ast)
|
78
|
+
"String"
|
79
|
+
elsif regexp?(ast)
|
80
|
+
"RegExp"
|
81
|
+
else
|
82
|
+
:other
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def undefined?(ast)
|
87
|
+
ast["type"] == "Identifier" && ast["name"] == "undefined"
|
88
|
+
end
|
89
|
+
|
90
|
+
def void?(ast)
|
91
|
+
ast["type"] == "UnaryExpression" && ast["operator"] == "void"
|
92
|
+
end
|
93
|
+
|
94
|
+
def this?(ast)
|
95
|
+
ast["type"] == "ThisExpression"
|
96
|
+
end
|
97
|
+
|
98
|
+
def boolean?(ast)
|
99
|
+
if boolean_literal?(ast)
|
100
|
+
true
|
101
|
+
elsif ast["type"] == "UnaryExpression" || ast["type"] == "BinaryExpression"
|
102
|
+
!!BOOLEAN_RETURNING_OPERATORS[ast["operator"]]
|
103
|
+
elsif ast["type"] == "LogicalExpression"
|
104
|
+
boolean?(ast["left"]) && boolean?(ast["right"])
|
105
|
+
elsif ast["type"] == "ConditionalExpression"
|
106
|
+
boolean?(ast["consequent"]) && boolean?(ast["alternate"])
|
107
|
+
elsif ast["type"] == "AssignmentExpression" && ast["operator"] == "="
|
108
|
+
boolean?(ast["right"])
|
109
|
+
else
|
110
|
+
false
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def boolean_literal?(ast)
|
115
|
+
ast["type"] == "Literal" && (ast["value"] == true || ast["value"] == false)
|
116
|
+
end
|
117
|
+
|
118
|
+
def string?(ast)
|
119
|
+
if string_literal?(ast)
|
120
|
+
true
|
121
|
+
elsif ast["type"] == "BinaryExpression" && ast["operator"] == "+"
|
122
|
+
string?(ast["left"]) || string?(ast["right"])
|
123
|
+
elsif ast["type"] == "UnaryExpression" && ast["operator"] == "typeof"
|
124
|
+
true
|
125
|
+
else
|
126
|
+
false
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def string_literal?(ast)
|
131
|
+
ast["type"] == "Literal" && ast["value"].is_a?(String)
|
132
|
+
end
|
133
|
+
|
134
|
+
def regexp?(ast)
|
135
|
+
ast["type"] == "Literal" && ast["raw"] =~ /^\//
|
136
|
+
end
|
137
|
+
|
138
|
+
def control_flow?(ast)
|
139
|
+
CONTROL_FLOW[ast["type"]]
|
140
|
+
end
|
141
|
+
|
142
|
+
def extract_bodies(ast)
|
143
|
+
body = []
|
144
|
+
CONTROL_FLOW[ast["type"]].each do |name|
|
145
|
+
statements = ast[name]
|
146
|
+
if statements.is_a?(Hash)
|
147
|
+
body << [statements]
|
148
|
+
else
|
149
|
+
body << Array(statements)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
body
|
153
|
+
end
|
154
|
+
|
155
|
+
# True if the node is a control structure which will block further
|
156
|
+
# program flow when all its branches finish with a return
|
157
|
+
# statement.
|
158
|
+
def possibly_blocking?(ast)
|
159
|
+
if POSSIBLY_BLOCKING[ast["type"]]
|
160
|
+
CONTROL_FLOW[ast["type"]].all? {|key| ast[key] }
|
161
|
+
else
|
162
|
+
false
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
BOOLEAN_RETURNING_OPERATORS = {
|
167
|
+
"!" => true,
|
168
|
+
">" => true,
|
169
|
+
">=" => true,
|
170
|
+
"<" => true,
|
171
|
+
"<=" => true,
|
172
|
+
"==" => true,
|
173
|
+
"!=" => true,
|
174
|
+
"===" => true,
|
175
|
+
"!==" => true,
|
176
|
+
"in" => true,
|
177
|
+
"instanceof" => true,
|
178
|
+
"delete" => true,
|
179
|
+
}
|
180
|
+
|
181
|
+
POSSIBLY_BLOCKING = {
|
182
|
+
"IfStatement" => true,
|
183
|
+
"DoWhileStatement" => true,
|
184
|
+
"WithStatement" => true,
|
185
|
+
"LabeledStatement" => true,
|
186
|
+
"BlockStatement" => true,
|
187
|
+
}
|
188
|
+
|
189
|
+
CONTROL_FLOW = {
|
190
|
+
"IfStatement" => ["consequent", "alternate"],
|
191
|
+
"SwitchStatement" => ["cases"],
|
192
|
+
"SwitchCase" => ["consequent"],
|
193
|
+
"ForStatement" => ["body"],
|
194
|
+
"ForInStatement" => ["body"],
|
195
|
+
"WhileStatement" => ["body"],
|
196
|
+
"DoWhileStatement" => ["body"],
|
197
|
+
"TryStatement" => ["block", "handlers", "finalizer"],
|
198
|
+
"CatchClause" => ["body"],
|
199
|
+
"WithStatement" => ["body"],
|
200
|
+
"LabeledStatement" => ["body"],
|
201
|
+
"BlockStatement" => ["body"],
|
202
|
+
}
|
203
|
+
end
|
204
|
+
|
205
|
+
end
|
206
|
+
end
|
@@ -0,0 +1,194 @@
|
|
1
|
+
require "jsduck/js/serializer"
|
2
|
+
require "jsduck/js/evaluator"
|
3
|
+
require "jsduck/js/ext_patterns"
|
4
|
+
require "jsduck/js/node_array"
|
5
|
+
|
6
|
+
module JsDuck
|
7
|
+
module Js
|
8
|
+
|
9
|
+
# Wraps around AST node returned from Esprima, providing methods for
|
10
|
+
# investigating it.
|
11
|
+
class Node
|
12
|
+
# Factor method that creates either Node or NodeArray.
|
13
|
+
def self.create(node)
|
14
|
+
if node.is_a? Array
|
15
|
+
Js::NodeArray.new(node)
|
16
|
+
else
|
17
|
+
Js::Node.new(node)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# Initialized with a AST Hash from Esprima.
|
22
|
+
def initialize(node)
|
23
|
+
@node = node || {}
|
24
|
+
end
|
25
|
+
|
26
|
+
# Returns a child AST node as Node class.
|
27
|
+
def child(name)
|
28
|
+
Js::Node.create(@node[name])
|
29
|
+
end
|
30
|
+
# Shorthand for #child method
|
31
|
+
def [](name)
|
32
|
+
child(name)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Returns the raw Exprima AST node this class wraps.
|
36
|
+
def raw
|
37
|
+
@node
|
38
|
+
end
|
39
|
+
|
40
|
+
# Serializes the node into string
|
41
|
+
def to_s
|
42
|
+
begin
|
43
|
+
Js::Serializer.new.to_s(@node)
|
44
|
+
rescue
|
45
|
+
nil
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Evaluates the node into basic JavaScript value.
|
50
|
+
def to_value
|
51
|
+
begin
|
52
|
+
Js::Evaluator.new.to_value(@node)
|
53
|
+
rescue
|
54
|
+
nil
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Converts object expression property key to string value
|
59
|
+
def key_value
|
60
|
+
Js::Evaluator.new.key_value(@node)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Returns the type of node value.
|
64
|
+
def value_type
|
65
|
+
v = to_value
|
66
|
+
if v.is_a?(String)
|
67
|
+
"String"
|
68
|
+
elsif v.is_a?(Numeric)
|
69
|
+
"Number"
|
70
|
+
elsif v.is_a?(TrueClass) || v.is_a?(FalseClass)
|
71
|
+
"Boolean"
|
72
|
+
elsif v.is_a?(Array)
|
73
|
+
"Array"
|
74
|
+
elsif v.is_a?(Hash)
|
75
|
+
"Object"
|
76
|
+
elsif v == :regexp
|
77
|
+
"RegExp"
|
78
|
+
else
|
79
|
+
nil
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# Iterates over keys and values in ObjectExpression. The keys
|
84
|
+
# are turned into strings, but values are left as is for further
|
85
|
+
# processing.
|
86
|
+
def each_property
|
87
|
+
return unless object_expression?
|
88
|
+
|
89
|
+
child("properties").each do |ast|
|
90
|
+
yield(ast["key"].key_value, ast["value"], ast)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# Returns line number in parsed source where the Node resides.
|
95
|
+
def linenr
|
96
|
+
# Get line number from third place at range array.
|
97
|
+
# This third item exists in forked EsprimaJS at
|
98
|
+
# https://github.com/nene/esprima/tree/linenr-in-range
|
99
|
+
@node["range"][2]
|
100
|
+
end
|
101
|
+
|
102
|
+
# Tests for higher level types which don't correspond directly to
|
103
|
+
# Esprima AST types.
|
104
|
+
|
105
|
+
def function?
|
106
|
+
function_declaration? || function_expression? || ext_empty_fn?
|
107
|
+
end
|
108
|
+
|
109
|
+
def fire_event?
|
110
|
+
call_expression? && child("callee").to_s == "this.fireEvent"
|
111
|
+
end
|
112
|
+
|
113
|
+
def string?
|
114
|
+
literal? && @node["value"].is_a?(String)
|
115
|
+
end
|
116
|
+
|
117
|
+
# Checks dependent on Ext namespace,
|
118
|
+
# which may not always be "Ext" but also something user-defined.
|
119
|
+
|
120
|
+
def ext_empty_fn?
|
121
|
+
member_expression? && ext_pattern?("Ext.emptyFn")
|
122
|
+
end
|
123
|
+
|
124
|
+
def ext_define?
|
125
|
+
call_expression? && child("callee").ext_pattern?("Ext.define")
|
126
|
+
end
|
127
|
+
|
128
|
+
def ext_extend?
|
129
|
+
call_expression? && child("callee").ext_pattern?("Ext.extend")
|
130
|
+
end
|
131
|
+
|
132
|
+
def ext_override?
|
133
|
+
call_expression? && child("callee").ext_pattern?("Ext.override")
|
134
|
+
end
|
135
|
+
|
136
|
+
def ext_pattern?(pattern)
|
137
|
+
Js::ExtPatterns.matches?(pattern, to_s)
|
138
|
+
end
|
139
|
+
|
140
|
+
# Simple shorthands for testing the type of node
|
141
|
+
# These have one-to-one mapping to Esprima node types.
|
142
|
+
|
143
|
+
def call_expression?
|
144
|
+
@node["type"] == "CallExpression"
|
145
|
+
end
|
146
|
+
|
147
|
+
def assignment_expression?
|
148
|
+
@node["type"] == "AssignmentExpression"
|
149
|
+
end
|
150
|
+
|
151
|
+
def object_expression?
|
152
|
+
@node["type"] == "ObjectExpression"
|
153
|
+
end
|
154
|
+
|
155
|
+
def array_expression?
|
156
|
+
@node["type"] == "ArrayExpression"
|
157
|
+
end
|
158
|
+
|
159
|
+
def function_expression?
|
160
|
+
@node["type"] == "FunctionExpression"
|
161
|
+
end
|
162
|
+
|
163
|
+
def member_expression?
|
164
|
+
@node["type"] == "MemberExpression"
|
165
|
+
end
|
166
|
+
|
167
|
+
def expression_statement?
|
168
|
+
@node["type"] == "ExpressionStatement"
|
169
|
+
end
|
170
|
+
|
171
|
+
def variable_declaration?
|
172
|
+
@node["type"] == "VariableDeclaration"
|
173
|
+
end
|
174
|
+
|
175
|
+
def function_declaration?
|
176
|
+
@node["type"] == "FunctionDeclaration"
|
177
|
+
end
|
178
|
+
|
179
|
+
def property?
|
180
|
+
@node["type"] == "Property"
|
181
|
+
end
|
182
|
+
|
183
|
+
def identifier?
|
184
|
+
@node["type"] == "Identifier"
|
185
|
+
end
|
186
|
+
|
187
|
+
def literal?
|
188
|
+
@node["type"] == "Literal"
|
189
|
+
end
|
190
|
+
|
191
|
+
end
|
192
|
+
|
193
|
+
end
|
194
|
+
end
|