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.
Files changed (183) hide show
  1. data/.travis.yml +0 -1
  2. data/README.md +32 -6
  3. data/Rakefile +10 -18
  4. data/bin/compare +5 -5
  5. data/bin/jsduck +2 -3
  6. data/jsduck.gemspec +3 -4
  7. data/lib/jsduck/aggregator.rb +21 -80
  8. data/lib/jsduck/app.rb +7 -14
  9. data/lib/jsduck/app_data.rb +4 -5
  10. data/lib/jsduck/assets.rb +4 -7
  11. data/lib/jsduck/base_type.rb +53 -0
  12. data/lib/jsduck/batch_parser.rb +8 -87
  13. data/lib/jsduck/batch_processor.rb +77 -0
  14. data/lib/jsduck/categories/auto.rb +83 -0
  15. data/lib/jsduck/categories/class_name.rb +63 -0
  16. data/lib/jsduck/categories/factory.rb +113 -0
  17. data/lib/jsduck/categories/file.rb +75 -0
  18. data/lib/jsduck/class.rb +3 -9
  19. data/lib/jsduck/class_doc_expander.rb +1 -1
  20. data/lib/jsduck/css/lexer.rb +203 -0
  21. data/lib/jsduck/css/parser.rb +121 -0
  22. data/lib/jsduck/doc/comment.rb +40 -0
  23. data/lib/jsduck/doc/map.rb +23 -0
  24. data/lib/jsduck/doc/parser.rb +128 -0
  25. data/lib/jsduck/doc/processor.rb +52 -0
  26. data/lib/jsduck/doc/scanner.rb +76 -0
  27. data/lib/jsduck/doc/standard_tag_parser.rb +154 -0
  28. data/lib/jsduck/doc/subproperties.rb +64 -0
  29. data/lib/jsduck/docs_code_comparer.rb +31 -0
  30. data/lib/jsduck/export_writer.rb +2 -2
  31. data/lib/jsduck/exporter/app.rb +16 -4
  32. data/lib/jsduck/exporter/full.rb +2 -2
  33. data/lib/jsduck/format/batch.rb +58 -0
  34. data/lib/jsduck/format/class.rb +62 -0
  35. data/lib/jsduck/format/doc.rb +172 -0
  36. data/lib/jsduck/format/html_stack.rb +109 -0
  37. data/lib/jsduck/format/shortener.rb +55 -0
  38. data/lib/jsduck/format/subproperties.rb +64 -0
  39. data/lib/jsduck/guides.rb +32 -14
  40. data/lib/jsduck/index_html.rb +3 -1
  41. data/lib/jsduck/inline/auto_link.rb +2 -2
  42. data/lib/jsduck/inline/link.rb +4 -3
  43. data/lib/jsduck/inline/link_renderer.rb +2 -2
  44. data/lib/jsduck/inline/video.rb +8 -2
  45. data/lib/jsduck/js/ast.rb +361 -0
  46. data/lib/jsduck/js/esprima.rb +39 -0
  47. data/lib/jsduck/{esprima → js/esprima}/esprima.js +0 -0
  48. data/lib/jsduck/js/evaluator.rb +70 -0
  49. data/lib/jsduck/js/ext_patterns.rb +70 -0
  50. data/lib/jsduck/js/function.rb +206 -0
  51. data/lib/jsduck/js/node.rb +194 -0
  52. data/lib/jsduck/js/node_array.rb +36 -0
  53. data/lib/jsduck/js/parser.rb +223 -0
  54. data/lib/jsduck/js/serializer.rb +263 -0
  55. data/lib/jsduck/js/utils.rb +21 -0
  56. data/lib/jsduck/logger.rb +3 -13
  57. data/lib/jsduck/members_index.rb +3 -4
  58. data/lib/jsduck/merger.rb +25 -145
  59. data/lib/jsduck/options.rb +29 -132
  60. data/lib/jsduck/parser.rb +76 -0
  61. data/lib/jsduck/process/accessors.rb +133 -0
  62. data/lib/jsduck/process/circular_deps.rb +58 -0
  63. data/lib/jsduck/process/enums.rb +91 -0
  64. data/lib/jsduck/process/ext4_events.rb +43 -0
  65. data/lib/jsduck/process/global_members.rb +36 -0
  66. data/lib/jsduck/process/ignored_classes.rb +16 -0
  67. data/lib/jsduck/process/importer.rb +58 -0
  68. data/lib/jsduck/process/inherit_doc.rb +197 -0
  69. data/lib/jsduck/process/lint.rb +135 -0
  70. data/lib/jsduck/process/overrides.rb +99 -0
  71. data/lib/jsduck/process/return_values.rb +72 -0
  72. data/lib/jsduck/process/versions.rb +102 -0
  73. data/lib/jsduck/relations.rb +5 -0
  74. data/lib/jsduck/render/class.rb +144 -0
  75. data/lib/jsduck/render/sidebar.rb +97 -0
  76. data/lib/jsduck/render/signature.rb +94 -0
  77. data/lib/jsduck/render/subproperties.rb +99 -0
  78. data/lib/jsduck/render/tags.rb +38 -0
  79. data/lib/jsduck/search_data.rb +19 -13
  80. data/lib/jsduck/source/file.rb +8 -17
  81. data/lib/jsduck/tag/abstract.rb +4 -7
  82. data/lib/jsduck/tag/accessor.rb +10 -0
  83. data/lib/jsduck/tag/alias.rb +61 -0
  84. data/lib/jsduck/tag/alternate_class_names.rb +17 -0
  85. data/lib/jsduck/tag/aside.rb +28 -31
  86. data/lib/jsduck/tag/author.rb +9 -5
  87. data/lib/jsduck/tag/boolean_tag.rb +24 -0
  88. data/lib/jsduck/tag/cfg.rb +45 -0
  89. data/lib/jsduck/tag/chainable.rb +5 -7
  90. data/lib/jsduck/tag/class.rb +28 -0
  91. data/lib/jsduck/tag/class_list_tag.rb +40 -0
  92. data/lib/jsduck/tag/constructor.rb +24 -0
  93. data/lib/jsduck/tag/css_mixin.rb +17 -0
  94. data/lib/jsduck/tag/css_var.rb +29 -0
  95. data/lib/jsduck/tag/default.rb +31 -0
  96. data/lib/jsduck/tag/deprecated.rb +13 -27
  97. data/lib/jsduck/tag/deprecated_tag.rb +58 -0
  98. data/lib/jsduck/tag/doc.rb +32 -0
  99. data/lib/jsduck/tag/docauthor.rb +4 -5
  100. data/lib/jsduck/tag/enum.rb +70 -0
  101. data/lib/jsduck/tag/event.rb +28 -0
  102. data/lib/jsduck/tag/evented.rb +10 -0
  103. data/lib/jsduck/tag/extends.rb +45 -0
  104. data/lib/jsduck/tag/ftype.rb +18 -0
  105. data/lib/jsduck/tag/hide.rb +4 -11
  106. data/lib/jsduck/tag/ignore.rb +6 -7
  107. data/lib/jsduck/tag/inheritable.rb +10 -0
  108. data/lib/jsduck/tag/inheritdoc.rb +48 -0
  109. data/lib/jsduck/tag/markdown.rb +8 -6
  110. data/lib/jsduck/tag/member.rb +24 -0
  111. data/lib/jsduck/tag/method.rb +35 -0
  112. data/lib/jsduck/tag/mixins.rb +26 -0
  113. data/lib/jsduck/tag/name.rb +36 -0
  114. data/lib/jsduck/tag/new.rb +13 -27
  115. data/lib/jsduck/tag/override.rb +37 -0
  116. data/lib/jsduck/tag/overrides.rb +29 -0
  117. data/lib/jsduck/tag/param.rb +87 -0
  118. data/lib/jsduck/tag/preventable.rb +19 -10
  119. data/lib/jsduck/tag/private.rb +28 -13
  120. data/lib/jsduck/tag/property.rb +39 -0
  121. data/lib/jsduck/tag/protected.rb +5 -7
  122. data/lib/jsduck/tag/ptype.rb +18 -0
  123. data/lib/jsduck/tag/readonly.rb +4 -7
  124. data/lib/jsduck/tag/removed.rb +21 -29
  125. data/lib/jsduck/tag/required.rb +11 -9
  126. data/lib/jsduck/tag/requires.rb +12 -0
  127. data/lib/jsduck/tag/return.rb +47 -0
  128. data/lib/jsduck/tag/since.rb +19 -11
  129. data/lib/jsduck/tag/singleton.rb +15 -0
  130. data/lib/jsduck/tag/static.rb +5 -7
  131. data/lib/jsduck/tag/subproperties.rb +23 -0
  132. data/lib/jsduck/tag/tag.rb +208 -0
  133. data/lib/jsduck/tag/template.rb +14 -9
  134. data/lib/jsduck/tag/throws.rb +38 -0
  135. data/lib/jsduck/tag/type.rb +48 -0
  136. data/lib/jsduck/tag/uses.rb +12 -0
  137. data/lib/jsduck/tag/xtype.rb +30 -0
  138. data/lib/jsduck/tag_loader.rb +39 -0
  139. data/lib/jsduck/tag_registry.rb +189 -0
  140. data/lib/jsduck/type_parser.rb +3 -3
  141. data/lib/jsduck/web_writer.rb +2 -2
  142. data/lib/jsduck/welcome.rb +1 -1
  143. metadata +578 -538
  144. data/lib/jsduck/accessors.rb +0 -136
  145. data/lib/jsduck/ast.rb +0 -524
  146. data/lib/jsduck/auto_categories.rb +0 -80
  147. data/lib/jsduck/batch_formatter.rb +0 -60
  148. data/lib/jsduck/categories.rb +0 -73
  149. data/lib/jsduck/categories_class_name.rb +0 -37
  150. data/lib/jsduck/circular_deps.rb +0 -56
  151. data/lib/jsduck/class_formatter.rb +0 -102
  152. data/lib/jsduck/columns.rb +0 -56
  153. data/lib/jsduck/css_lexer.rb +0 -201
  154. data/lib/jsduck/css_parser.rb +0 -119
  155. data/lib/jsduck/doc_ast.rb +0 -319
  156. data/lib/jsduck/doc_formatter.rb +0 -142
  157. data/lib/jsduck/doc_parser.rb +0 -611
  158. data/lib/jsduck/doc_type.rb +0 -59
  159. data/lib/jsduck/enum.rb +0 -73
  160. data/lib/jsduck/esprima.rb +0 -51
  161. data/lib/jsduck/evaluator.rb +0 -69
  162. data/lib/jsduck/ext_patterns.rb +0 -58
  163. data/lib/jsduck/file_categories.rb +0 -76
  164. data/lib/jsduck/function_ast.rb +0 -206
  165. data/lib/jsduck/guide_anchors.rb +0 -32
  166. data/lib/jsduck/guide_toc.rb +0 -49
  167. data/lib/jsduck/html_stack.rb +0 -105
  168. data/lib/jsduck/importer.rb +0 -121
  169. data/lib/jsduck/inherit_doc.rb +0 -193
  170. data/lib/jsduck/js_parser.rb +0 -221
  171. data/lib/jsduck/lint.rb +0 -133
  172. data/lib/jsduck/meta_tag.rb +0 -88
  173. data/lib/jsduck/meta_tag_loader.rb +0 -67
  174. data/lib/jsduck/meta_tag_registry.rb +0 -111
  175. data/lib/jsduck/meta_tag_renderer.rb +0 -34
  176. data/lib/jsduck/news.rb +0 -128
  177. data/lib/jsduck/override.rb +0 -87
  178. data/lib/jsduck/renderer.rb +0 -361
  179. data/lib/jsduck/return_values.rb +0 -72
  180. data/lib/jsduck/serializer.rb +0 -262
  181. data/lib/jsduck/shortener.rb +0 -58
  182. data/lib/jsduck/signature_renderer.rb +0 -91
  183. data/lib/jsduck/source/file_parser.rb +0 -72
@@ -1,59 +0,0 @@
1
- module JsDuck
2
-
3
- # Detects the type of documentation object: class, method, cfg, etc
4
- class DocType
5
- # Given parsed documentation and code, returns the tagname for
6
- # documentation item.
7
- #
8
- # @param docs Result from DocParser
9
- # @param code Result from Ast#detect or CssParser#parse
10
- # @returns One of: :class, :method, :event, :cfg, :property, :css_var, :css_mixin
11
- #
12
- def detect(docs, code)
13
- doc_map = build_doc_map(docs)
14
-
15
- if doc_map[:class] || doc_map[:override]
16
- :class
17
- elsif doc_map[:event]
18
- :event
19
- elsif doc_map[:method]
20
- :method
21
- elsif doc_map[:property] || doc_map[:type]
22
- :property
23
- elsif doc_map[:css_var]
24
- :css_var
25
- elsif doc_map[:cfg] && doc_map[:cfg].length == 1
26
- # When just one @cfg, avoid treating it as @class
27
- :cfg
28
- elsif code[:tagname] == :class
29
- :class
30
- elsif code[:tagname] == :css_mixin
31
- :css_mixin
32
- elsif doc_map[:cfg]
33
- :cfg
34
- elsif doc_map[:constructor]
35
- :method
36
- elsif doc_map[:param] || doc_map[:return]
37
- :method
38
- else
39
- code[:tagname]
40
- end
41
- end
42
-
43
- private
44
-
45
- # Build map of at-tags for quick lookup
46
- def build_doc_map(docs)
47
- map = {}
48
- docs.each do |tag|
49
- if map[tag[:tagname]]
50
- map[tag[:tagname]] << tag
51
- else
52
- map[tag[:tagname]] = [tag]
53
- end
54
- end
55
- map
56
- end
57
- end
58
-
59
- end
data/lib/jsduck/enum.rb DELETED
@@ -1,73 +0,0 @@
1
- module JsDuck
2
-
3
- class Enum
4
- def initialize(classes)
5
- @classes = classes
6
- end
7
-
8
- # Applies additional processing to all enum-classes.
9
- def process_all!
10
- @classes.each_value do |cls|
11
- if cls[:enum]
12
- process(cls)
13
- end
14
- end
15
- end
16
-
17
- private
18
-
19
- # processes single class
20
- def process(cls)
21
- expand_default(cls)
22
- strip_inheritdoc(cls)
23
- cls[:enum][:type] = infer_type(cls) unless cls[:enum][:type]
24
- end
25
-
26
- # Given an enum class, returns the type infered from its values.
27
- def infer_type(cls)
28
- if cls[:members].length > 0
29
- types = cls[:members].map {|p| p[:type] }
30
- types.sort.uniq.join("/")
31
- else
32
- "Object"
33
- end
34
- end
35
-
36
- # Expands default value like widget.* into list of properties
37
- def expand_default(cls)
38
- if cls[:enum][:default] =~ /\A(.*)\.\*\Z/
39
- each_alias($1) do |name, owner|
40
- cls[:members] << {
41
- :tagname => :property,
42
- :id => 'property-' + name,
43
- :name => name,
44
- :default => "'" + name + "'",
45
- :type => "String",
46
- :meta => {:private => owner[:private]},
47
- :private => owner[:private],
48
- :files => cls[:files],
49
- :owner => cls[:name],
50
- :doc => "Alias for {@link #{owner[:name]}}.",
51
- }
52
- end
53
- end
54
- end
55
-
56
- def each_alias(prefix)
57
- @classes.each_value do |cls|
58
- if cls[:aliases] && cls[:aliases][prefix]
59
- cls[:aliases][prefix].each {|name| yield(name, cls) }
60
- end
61
- end
62
- end
63
-
64
- # Remove the auto-inserted inheritdoc tag so the auto-detected enum
65
- # values default to being public.
66
- def strip_inheritdoc(cls)
67
- cls[:members].each do |p|
68
- p[:inheritdoc] = nil if p[:autodetected]
69
- end
70
- end
71
- end
72
-
73
- end
@@ -1,51 +0,0 @@
1
- require 'execjs'
2
- require 'jsduck/util/json'
3
- require 'jsduck/util/singleton'
4
-
5
- module JsDuck
6
-
7
- # Runs Esprima.js through JavaScript runtime selected by ExecJS.
8
- # Normally this will be V8 engine within therubyracer gem, but when
9
- # JSDuck is installed through some other means than rubygems, then
10
- # one could use any of the runtimes supported by ExecJS. (NodeJS
11
- # for example.)
12
- #
13
- # Initialized as singleton to avoid loading the esprima.js more
14
- # than once - otherwise performace will severely suffer.
15
- class Esprima
16
- include Util::Singleton
17
-
18
- def initialize
19
- esprima_path = File.dirname(File.expand_path(__FILE__)) + "/esprima/esprima.js"
20
- esprima = IO.read(esprima_path)
21
-
22
- # Esprima attempts to assign to window.esprima, but our v8
23
- # engine has no global window variable defined. So define our
24
- # own and then grab esprima out from it again.
25
- source = <<-EOJS
26
- if (typeof window === "undefined") {
27
- var window = {};
28
- }
29
-
30
- #{esprima}
31
-
32
- var esprima = window.esprima;
33
-
34
- function runEsprima(js) {
35
- return JSON.stringify(esprima.parse(js, {comment: true, range: true, raw: true}));
36
- }
37
- EOJS
38
-
39
- @context = ExecJS.compile(source)
40
- end
41
-
42
- # Parses JavaScript source code using Esprima.js
43
- #
44
- # Returns the resulting AST
45
- def parse(input)
46
- json = @context.call("runEsprima", input)
47
- return Util::Json.parse(json, :max_nesting => false)
48
- end
49
-
50
- end
51
- end
@@ -1,69 +0,0 @@
1
- module JsDuck
2
-
3
- # Evaluates Esprima AST node into Ruby object
4
- class Evaluator
5
-
6
- # Converts AST node into a value.
7
- #
8
- # - String literals become Ruby strings
9
- # - Number literals become Ruby numbers
10
- # - Regex literals become :regexp symbols
11
- # - Array expressions become Ruby arrays
12
- # - etc
13
- #
14
- # For anything it doesn't know how to evaluate (like a function
15
- # expression) it throws exception.
16
- #
17
- def to_value(ast)
18
- case ast["type"]
19
- when "ArrayExpression"
20
- ast["elements"].map {|e| to_value(e) }
21
- when "ObjectExpression"
22
- h = {}
23
- ast["properties"].each do |p|
24
- key = key_value(p["key"])
25
- value = to_value(p["value"])
26
- h[key] = value
27
- end
28
- h
29
- when "BinaryExpression"
30
- if ast["operator"] == "+"
31
- to_value(ast["left"]) + to_value(ast["right"])
32
- else
33
- throw "Unable to handle operator: " + ast["operator"]
34
- end
35
- when "MemberExpression"
36
- if base_css_prefix?(ast)
37
- "x-"
38
- else
39
- throw "Unable to handle this MemberExpression"
40
- end
41
- when "Literal"
42
- if ast["raw"] =~ /\A\//
43
- :regexp
44
- else
45
- ast["value"]
46
- end
47
- else
48
- throw "Unknown node type: " + ast["type"]
49
- end
50
- end
51
-
52
- # Turns object property key into string value
53
- def key_value(key)
54
- key["type"] == "Identifier" ? key["name"] : key["value"]
55
- end
56
-
57
- # True when MemberExpression == Ext.baseCSSPrefix
58
- def base_css_prefix?(ast)
59
- ast["computed"] == false &&
60
- ast["object"]["type"] == "Identifier" &&
61
- ast["object"]["name"] == "Ext" &&
62
- ast["property"]["type"] == "Identifier" &&
63
- ast["property"]["name"] == "baseCSSPrefix"
64
- end
65
-
66
- end
67
-
68
- end
69
-
@@ -1,58 +0,0 @@
1
- module JsDuck
2
-
3
- # Identifies Ext JS builtins like Ext.define and Ext.extend, taking
4
- # also into account the possibility of aliasing the Ext namespace.
5
- #
6
- # For example when the following command line option is used:
7
- #
8
- # --ext-namespaces=Ext,MyApp
9
- #
10
- # we need to identify both Ext.define and MyApp.define, but
11
- # Ext.define is additionally aliased withing ExtJS as
12
- # Ext.ClassManager.create, so we also need to recognize
13
- # Ext.ClassManager.create and MyApp.ClassManager.create.
14
- #
15
- # The matches? method will take care of identifying all these four
16
- # cases:
17
- #
18
- # ps = ExtPatterns.new(["Ext", "MyApp"])
19
- # matches?("Ext.define", "MyApp.define") --> true
20
- #
21
- class ExtPatterns
22
- def initialize(namespaces)
23
- @patterns = {
24
- "Ext.define" => build_patterns(namespaces, [".define", ".ClassManager.create"]),
25
- "Ext.extend" => build_patterns(namespaces, [".extend"]),
26
- "Ext.override" => build_patterns(namespaces, [".override"]),
27
- "Ext.emptyFn" => build_patterns(namespaces, [".emptyFn"]),
28
- }
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
- private
40
-
41
- # Given Array of alternate Ext namespaces builds list of patterns
42
- # for detecting Ext.define or some other construct:
43
- #
44
- # build_patterns(["Ext", "Foo"], [".define"]) --> ["Ext.define", "Foo.define"]
45
- #
46
- def build_patterns(namespaces, suffixes)
47
- patterns = []
48
- namespaces.each do |ns|
49
- suffixes.each do |suffix|
50
- patterns << ns + suffix
51
- end
52
- end
53
- patterns
54
- end
55
-
56
- end
57
-
58
- end
@@ -1,76 +0,0 @@
1
- require 'jsduck/logger'
2
- require 'jsduck/util/json'
3
-
4
- module JsDuck
5
-
6
- # Reads categories info from config file
7
- class FileCategories
8
- def initialize(filename, relations)
9
- @filename = filename
10
- @relations = relations
11
- end
12
-
13
- # Parses categories in JSON file
14
- def generate
15
- @categories = Util::Json.read(@filename)
16
-
17
- # Don't crash if old syntax is used.
18
- if @categories.is_a?(Hash) && @categories["categories"]
19
- Logger.warn(nil, 'Update categories file to contain just the array inside {"categories": [...]}', @filename)
20
- @categories = @categories["categories"]
21
- end
22
-
23
- # Perform expansion on all class names containing * wildcard
24
- @categories.each do |cat|
25
- cat["groups"].each do |group|
26
- group["classes"] = group["classes"].map do |name|
27
- expand(name)
28
- end.flatten
29
- end
30
- end
31
-
32
- validate
33
-
34
- @categories
35
- end
36
-
37
- # Expands class name like 'Foo.*' into multiple class names.
38
- def expand(name)
39
- re = Regexp.new("^" + name.split(/\*/, -1).map {|part| Regexp.escape(part) }.join('.*') + "$")
40
-
41
- classes = @relations.to_a.find_all do |cls|
42
- re =~ cls[:name] && !cls[:private] && !deprecated?(cls)
43
- end.map {|cls| cls[:name] }.sort
44
-
45
- if classes.length == 0
46
- Logger.warn(:cat_no_match, "No class found matching a pattern '#{name}' in categories file", @filename)
47
- end
48
- classes
49
- end
50
-
51
- # Prints warnings for missing classes in categories file
52
- def validate
53
- # Build a map of all classes listed in categories
54
- listed_classes = {}
55
- @categories.each do |cat|
56
- cat["groups"].each do |group|
57
- group["classes"].each do |cls_name|
58
- listed_classes[cls_name] = true
59
- end
60
- end
61
- end
62
-
63
- # Check that each existing non-private & non-deprecated class is listed
64
- @relations.each do |cls|
65
- unless listed_classes[cls[:name]] || cls[:private] || deprecated?(cls)
66
- Logger.warn(:cat_class_missing, "Class '#{cls[:name]}' not found in categories file", @filename)
67
- end
68
- end
69
- end
70
-
71
- def deprecated?(cls)
72
- cls[:meta] && cls[:meta][:deprecated]
73
- end
74
- end
75
-
76
- end
@@ -1,206 +0,0 @@
1
- require "jsduck/util/singleton"
2
- require "jsduck/serializer"
3
- require "jsduck/evaluator"
4
-
5
- module JsDuck
6
-
7
- # Analyzes the AST of a FunctionDeclaration or FunctionExpression.
8
- class FunctionAst
9
- include Util::Singleton
10
-
11
- # Detects possible return types of the given function.
12
- #
13
- # For now there are three possible detected return values:
14
- #
15
- # * :this - the code contins 'return this;'
16
- #
17
- # * "undefined" - the code finishes by returning undefined or
18
- # without explicitly returning anything
19
- #
20
- # * :other - some other value is returned.
21
- #
22
- def return_types(ast)
23
- h = return_types_hash(ast["body"]["body"])
24
-
25
- # Replace the special :void value that signifies possibility of
26
- # exiting without explicitly returning anything
27
- if h[:void]
28
- h["undefined"] = true
29
- h.delete(:void)
30
- end
31
-
32
- h.keys
33
- end
34
-
35
- private
36
-
37
- def return_types_hash(body)
38
- rvalues = {}
39
- body.each do |ast|
40
- if return?(ast)
41
- type = value_type(ast["argument"])
42
- rvalues[type] = true
43
- return rvalues
44
- elsif possibly_blocking?(ast)
45
- extract_bodies(ast).each do |b|
46
- rvalues.merge!(return_types_hash(b))
47
- end
48
- if !rvalues[:void]
49
- return rvalues
50
- else
51
- rvalues.delete(:void)
52
- end
53
- elsif control_flow?(ast)
54
- extract_bodies(ast).each do |b|
55
- rvalues.merge!(return_types_hash(b))
56
- end
57
- rvalues.delete(:void)
58
- end
59
- end
60
-
61
- rvalues[:void] = true
62
- return rvalues
63
- end
64
-
65
- def return?(ast)
66
- ast["type"] == "ReturnStatement"
67
- end
68
-
69
- def value_type(ast)
70
- if !ast
71
- :void
72
- elsif undefined?(ast) || void?(ast)
73
- "undefined"
74
- elsif this?(ast)
75
- :this
76
- elsif boolean?(ast)
77
- "Boolean"
78
- elsif string?(ast)
79
- "String"
80
- elsif regexp?(ast)
81
- "RegExp"
82
- else
83
- :other
84
- end
85
- end
86
-
87
- def undefined?(ast)
88
- ast["type"] == "Identifier" && ast["name"] == "undefined"
89
- end
90
-
91
- def void?(ast)
92
- ast["type"] == "UnaryExpression" && ast["operator"] == "void"
93
- end
94
-
95
- def this?(ast)
96
- ast["type"] == "ThisExpression"
97
- end
98
-
99
- def boolean?(ast)
100
- if boolean_literal?(ast)
101
- true
102
- elsif ast["type"] == "UnaryExpression" || ast["type"] == "BinaryExpression"
103
- !!BOOLEAN_RETURNING_OPERATORS[ast["operator"]]
104
- elsif ast["type"] == "LogicalExpression"
105
- boolean?(ast["left"]) && boolean?(ast["right"])
106
- elsif ast["type"] == "ConditionalExpression"
107
- boolean?(ast["consequent"]) && boolean?(ast["alternate"])
108
- elsif ast["type"] == "AssignmentExpression" && ast["operator"] == "="
109
- boolean?(ast["right"])
110
- else
111
- false
112
- end
113
- end
114
-
115
- def boolean_literal?(ast)
116
- ast["type"] == "Literal" && (ast["value"] == true || ast["value"] == false)
117
- end
118
-
119
- def string?(ast)
120
- if string_literal?(ast)
121
- true
122
- elsif ast["type"] == "BinaryExpression" && ast["operator"] == "+"
123
- string?(ast["left"]) || string?(ast["right"])
124
- elsif ast["type"] == "UnaryExpression" && ast["operator"] == "typeof"
125
- true
126
- else
127
- false
128
- end
129
- end
130
-
131
- def string_literal?(ast)
132
- ast["type"] == "Literal" && ast["value"].is_a?(String)
133
- end
134
-
135
- def regexp?(ast)
136
- ast["type"] == "Literal" && ast["raw"] =~ /^\//
137
- end
138
-
139
- def control_flow?(ast)
140
- CONTROL_FLOW[ast["type"]]
141
- end
142
-
143
- def extract_bodies(ast)
144
- body = []
145
- CONTROL_FLOW[ast["type"]].each do |name|
146
- statements = ast[name]
147
- if statements.is_a?(Hash)
148
- body << [statements]
149
- else
150
- body << Array(statements)
151
- end
152
- end
153
- body
154
- end
155
-
156
- # True if the node is a control structure which will block further
157
- # program flow when all its branches finish with a return
158
- # statement.
159
- def possibly_blocking?(ast)
160
- if POSSIBLY_BLOCKING[ast["type"]]
161
- CONTROL_FLOW[ast["type"]].all? {|key| ast[key] }
162
- else
163
- false
164
- end
165
- end
166
-
167
- BOOLEAN_RETURNING_OPERATORS = {
168
- "!" => true,
169
- ">" => true,
170
- ">=" => true,
171
- "<" => true,
172
- "<=" => true,
173
- "==" => true,
174
- "!=" => true,
175
- "===" => true,
176
- "!==" => true,
177
- "in" => true,
178
- "instanceof" => true,
179
- "delete" => true,
180
- }
181
-
182
- POSSIBLY_BLOCKING = {
183
- "IfStatement" => true,
184
- "DoWhileStatement" => true,
185
- "WithStatement" => true,
186
- "LabeledStatement" => true,
187
- "BlockStatement" => true,
188
- }
189
-
190
- CONTROL_FLOW = {
191
- "IfStatement" => ["consequent", "alternate"],
192
- "SwitchStatement" => ["cases"],
193
- "SwitchCase" => ["consequent"],
194
- "ForStatement" => ["body"],
195
- "ForInStatement" => ["body"],
196
- "WhileStatement" => ["body"],
197
- "DoWhileStatement" => ["body"],
198
- "TryStatement" => ["block", "handlers", "finalizer"],
199
- "CatchClause" => ["body"],
200
- "WithStatement" => ["body"],
201
- "LabeledStatement" => ["body"],
202
- "BlockStatement" => ["body"],
203
- }
204
- end
205
-
206
- end