fdlint 0.1.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.
- data/Gemfile +8 -0
- data/Gemfile.lock +14 -0
- data/README.md +68 -0
- data/Rakefile +92 -0
- data/bin/fdlint +17 -0
- data/lib/base_parser.rb +143 -0
- data/lib/cmd_runner.rb +145 -0
- data/lib/context.rb +31 -0
- data/lib/css/parser.rb +186 -0
- data/lib/css/reader.rb +30 -0
- data/lib/css/rule/check_compression_rule.rb +48 -0
- data/lib/css/rule/checklist.rb +45 -0
- data/lib/css/struct.rb +111 -0
- data/lib/encoding_error.rb +6 -0
- data/lib/file_validator.rb +38 -0
- data/lib/helper/code_type.rb +50 -0
- data/lib/helper/color_string.rb +44 -0
- data/lib/helper/file_reader.rb +22 -0
- data/lib/helper/strenc.rb +65 -0
- data/lib/html/parser.rb +212 -0
- data/lib/html/query.rb +96 -0
- data/lib/html/rule/check_tag_rule.rb +80 -0
- data/lib/html/struct.rb +291 -0
- data/lib/js/expr/expr.rb +66 -0
- data/lib/js/expr/left_hand.rb +63 -0
- data/lib/js/expr/operate.rb +92 -0
- data/lib/js/expr/primary.rb +166 -0
- data/lib/js/parser.rb +116 -0
- data/lib/js/rule/all.rb +35 -0
- data/lib/js/rule/checklist.rb +41 -0
- data/lib/js/rule/file_checker.rb +42 -0
- data/lib/js/rule/helper.rb +96 -0
- data/lib/js/rule/no_global.rb +87 -0
- data/lib/js/stat/if.rb +25 -0
- data/lib/js/stat/iter.rb +85 -0
- data/lib/js/stat/stat.rb +117 -0
- data/lib/js/stat/switch.rb +65 -0
- data/lib/js/stat/try.rb +28 -0
- data/lib/js/stat/var.rb +40 -0
- data/lib/js/struct.rb +248 -0
- data/lib/log_entry.rb +49 -0
- data/lib/node.rb +28 -0
- data/lib/parse_error.rb +13 -0
- data/lib/parser_visitable.rb +138 -0
- data/lib/position_info.rb +46 -0
- data/lib/printer/base_printer.rb +24 -0
- data/lib/printer/console_printer.rb +66 -0
- data/lib/printer/nocolor_printer.rb +27 -0
- data/lib/printer/vim_printer.rb +19 -0
- data/lib/rule.rb +241 -0
- data/lib/rule_helper.rb +14 -0
- data/lib/runner.rb +225 -0
- data/rules.d/css.rule +127 -0
- data/rules.d/html.dtd.rule +22 -0
- data/rules.d/html.prop.rule +51 -0
- data/rules.d/html.tag.rule +136 -0
- data/rules.d/js.file.rule +13 -0
- data/rules.d/js.jquery.rule +56 -0
- data/rules.d/js.mergefile.rule +71 -0
- data/rules.d/js.rule +84 -0
- data/test/all_tests.rb +84 -0
- data/test/cli/cli_test.rb +70 -0
- data/test/cli/log_level_test.rb +51 -0
- data/test/cli/output_format_test.rb +47 -0
- data/test/cli/type_test.rb +77 -0
- data/test/css/mac_line_end_support_test.rb +38 -0
- data/test/css/parser_test.rb +276 -0
- data/test/css/rule/check_encoding_test.rb +66 -0
- data/test/css/rule/check_list_rule_test.rb +167 -0
- data/test/css/rule/compression_test.rb +53 -0
- data/test/css/rule/file_name_test.rb +76 -0
- data/test/fixtures/css/broken.css +4 -0
- data/test/fixtures/css/cbu/36.css +52 -0
- data/test/fixtures/css/cbu/china_top.css +324 -0
- data/test/fixtures/css/cbu/default-merge.css +3 -0
- data/test/fixtures/css/cbu/default.css +13 -0
- data/test/fixtures/css/cbu/diy-merge.css +25 -0
- data/test/fixtures/css/cbu/fns-v1.css +27 -0
- data/test/fixtures/css/cbu/index_v0.1.css +12 -0
- data/test/fixtures/css/cbu/merge.css +11 -0
- data/test/fixtures/css/cbu/min.css +2 -0
- data/test/fixtures/css/cbu/my_home_admin.css +126 -0
- data/test/fixtures/css/cbu/nav.css +95 -0
- data/test/fixtures/css/cbu/pic_list.css +386 -0
- data/test/fixtures/css/cbu/quote-edit.css +18 -0
- data/test/fixtures/css/cbu/selloffer.shopwindow.css +1 -0
- data/test/fixtures/css/cbu/v1.css +9 -0
- data/test/fixtures/css/css3.css +30 -0
- data/test/fixtures/css/empty-min.css +0 -0
- data/test/fixtures/css/empty.css +0 -0
- data/test/fixtures/css/font-family.css +4 -0
- data/test/fixtures/css/gb-good.css +14 -0
- data/test/fixtures/css/gb_using_star.css +4 -0
- data/test/fixtures/css/import.css +18 -0
- data/test/fixtures/css/mac-line-sep-err-min.css +1 -0
- data/test/fixtures/css/mac-line-sep-err.css +1 -0
- data/test/fixtures/css/mac-line-sep-good-min.css +1 -0
- data/test/fixtures/css/mac-line-sep-good.css +1 -0
- data/test/fixtures/css/multi-encoding-in-a-file.css +0 -0
- data/test/fixtures/css/simple.css +1 -0
- data/test/fixtures/css/using_expr.css +8 -0
- data/test/fixtures/css/using_hack.css +21 -0
- data/test/fixtures/css/using_id.css +1 -0
- data/test/fixtures/css/using_star.css +4 -0
- data/test/fixtures/css/utf8_good.css +6 -0
- data/test/fixtures/css/utf8_good_declaring_charset.css +7 -0
- data/test/fixtures/css/utf8_using_star.css +5 -0
- data/test/fixtures/html/1-1.html +120 -0
- data/test/fixtures/html/1-2.html +120 -0
- data/test/fixtures/html/cms.html +373 -0
- data/test/fixtures/html/css_out_of_head.html +9 -0
- data/test/fixtures/html/fdev-template.html +22 -0
- data/test/fixtures/html/google.com.html +33 -0
- data/test/fixtures/html/mixed_log_levels.html +4 -0
- data/test/fixtures/html/mixed_types.html +13 -0
- data/test/fixtures/html/no_dtd.html +6 -0
- data/test/fixtures/html/readme.html +94 -0
- data/test/fixtures/html/review.board.html +163 -0
- data/test/fixtures/html/syntax_err.html +3 -0
- data/test/fixtures/html/train/detail/345/233/276/346/226/207/347/273/223/345/220/210.html +208 -0
- data/test/fixtures/html/train/detail/347/232/204Flash.html +212 -0
- data/test/fixtures/html/train/detail/347/232/204Vedio.html +212 -0
- data/test/fixtures/html/train/index.html +37 -0
- data/test/fixtures/html/train/test.html +1 -0
- data/test/fixtures/html/train//344/277/256/346/224/271/344/270/200/347/272/247/345/210/206/347/261/273.html +112 -0
- data/test/fixtures/html/train//344/277/256/346/224/271/345/255/220/345/210/206/347/261/273.html +108 -0
- data/test/fixtures/html/train//344/277/256/346/224/271/350/257/276/347/250/213.html +195 -0
- data/test/fixtures/html/train//345/215/232/345/256/242/350/256/276/347/275/256.html +142 -0
- data/test/fixtures/html/train//346/265/217/350/247/210/350/256/260/345/275/225.html +191 -0
- data/test/fixtures/html/train//346/267/273/345/212/240/344/270/200/347/272/247/345/210/206/347/261/273.html +113 -0
- data/test/fixtures/html/train//346/267/273/345/212/240/345/255/220/345/210/206/347/261/273.html +112 -0
- data/test/fixtures/html/train//346/267/273/345/212/240/350/257/276/347/250/213.html +195 -0
- data/test/fixtures/html/train//347/231/273/345/275/225.html +20 -0
- data/test/fixtures/html/train//347/256/241/347/220/206/345/210/206/347/261/273.html +210 -0
- data/test/fixtures/html/train//347/256/241/347/220/206/345/217/215/351/246/210.html +222 -0
- data/test/fixtures/html/train//347/256/241/347/220/206/350/257/276/347/250/213.html +284 -0
- data/test/fixtures/html/train//347/256/241/347/220/206/350/264/246/346/210/267.html +107 -0
- data/test/fixtures/html/train//347/275/221/344/270/212/345/237/271/350/256/255home/351/241/265.html +354 -0
- data/test/fixtures/html/train//347/275/221/345/225/206/345/237/271/350/256/255list/351/241/265.html +255 -0
- data/test/fixtures/html/train//350/256/276/347/275/256/351/246/226/351/241/265/346/216/250/350/215/220.html +168 -0
- data/test/fixtures/html/train//350/257/264/346/230/216.txt +3 -0
- data/test/fixtures/html/train//351/246/226/351/241/265/345/271/277/345/221/212/350/256/276/347/275/256.html +297 -0
- data/test/fixtures/html/unescaped.html +2 -0
- data/test/fixtures/html/view.vm +916 -0
- data/test/fixtures/js/jquery-1.7.js +9300 -0
- data/test/fixtures/js/scope-test.js +22 -0
- data/test/helper.rb +41 -0
- data/test/html/mixed_type_test.rb +35 -0
- data/test/html/parser/parse_comment_test.rb +47 -0
- data/test/html/parser/parse_dtd_test.rb +46 -0
- data/test/html/parser/parse_script_tag_test.rb +55 -0
- data/test/html/parser/parse_with_auto_close_tag_test.rb +41 -0
- data/test/html/parser/parse_with_diff_case_test.rb +38 -0
- data/test/html/parser/parse_with_emtpy_test.rb +22 -0
- data/test/html/parser/parse_with_multi_children_test.rb +27 -0
- data/test/html/parser/parse_with_multi_line_test.rb +41 -0
- data/test/html/parser/parse_with_prop_test.rb +88 -0
- data/test/html/parser/parse_with_script_tag_test.rb +26 -0
- data/test/html/parser/parse_with_selfclosing_test.rb +39 -0
- data/test/html/parser/parse_with_simple_tag_test.rb +44 -0
- data/test/html/parser/parse_with_simple_tree_test.rb +40 -0
- data/test/html/parser/parse_with_style_tag_test.rb +22 -0
- data/test/html/parser/parse_with_text_test.rb +45 -0
- data/test/html/parser_test.rb +52 -0
- data/test/html/query_test.rb +52 -0
- data/test/html/rule/check_block_level_element_test.rb +52 -0
- data/test/html/rule/check_button_test.rb +45 -0
- data/test/html/rule/check_class_count_test.rb +36 -0
- data/test/html/rule/check_css_in_head_test.rb +53 -0
- data/test/html/rule/check_dtd_test.rb +46 -0
- data/test/html/rule/check_form_element_name_test.rb +49 -0
- data/test/html/rule/check_head_contain_meta_and_title_test.rb +52 -0
- data/test/html/rule/check_html_template_test.rb +103 -0
- data/test/html/rule/check_hyperlink_with_target_test.rb +40 -0
- data/test/html/rule/check_hyperlink_with_title_test.rb +43 -0
- data/test/html/rule/check_id_n_class_downcase_test.rb +40 -0
- data/test/html/rule/check_img_with_alt_prop_test.rb +33 -0
- data/test/html/rule/check_no_import_css_test.rb +36 -0
- data/test/html/rule/check_prop_have_value_test.rb +32 -0
- data/test/html/rule/check_prop_seperator_test.rb +32 -0
- data/test/html/rule/check_style_prop_test.rb +30 -0
- data/test/html/rule/check_tag_closed_test.rb +59 -0
- data/test/html/rule/check_tag_downcase_test.rb +51 -0
- data/test/html/rule/check_unescape_char_test.rb +35 -0
- data/test/html/rule/check_unique_import_test.rb +56 -0
- data/test/html/rule_test.rb +62 -0
- data/test/js/expr/expr.rb +57 -0
- data/test/js/expr/left_hand.rb +25 -0
- data/test/js/expr/operate.rb +145 -0
- data/test/js/expr/primary.rb +89 -0
- data/test/js/parser_test.rb +98 -0
- data/test/js/rule/alert_check_test.rb +37 -0
- data/test/js/rule/all_test.rb +23 -0
- data/test/js/rule/base_test.rb +34 -0
- data/test/js/rule/file_checker_test.rb +131 -0
- data/test/js/rule/jq_check_test.rb +90 -0
- data/test/js/rule/nest_try_catch_test.rb +71 -0
- data/test/js/rule/new_object_and_new_array_test.rb +38 -0
- data/test/js/rule/no_eval_test.rb +34 -0
- data/test/js/rule/no_global_test.rb +88 -0
- data/test/js/rule/private_method_check_test.rb +58 -0
- data/test/js/rule/semicolon_test.rb +63 -0
- data/test/js/rule/stat_if_with_brace_test.rb +68 -0
- data/test/js/rule/stat_if_with_muti_else_test.rb +68 -0
- data/test/js/rule/use_strict_equal_test.rb +44 -0
- data/test/js/rule_test.rb +47 -0
- data/test/js/stat/if.rb +26 -0
- data/test/js/stat/iter.rb +115 -0
- data/test/js/stat/stat.rb +91 -0
- data/test/js/stat/switch.rb +37 -0
- data/test/js/stat/try.rb +32 -0
- data/test/js/stat/var.rb +38 -0
- data/test/parser_visitable_test.rb +102 -0
- data/test/position_info_test.rb +66 -0
- data/test/rule_dsl/dsl_basic_test.rb +91 -0
- data/test/rule_dsl/importing_test.rb +48 -0
- data/test/runner/log_level_test.rb +58 -0
- metadata +317 -0
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
module XRay
|
|
2
|
+
module JS
|
|
3
|
+
module Expr
|
|
4
|
+
|
|
5
|
+
module Primary
|
|
6
|
+
|
|
7
|
+
R_IDENTIFY = /[a-zA-Z_$][\w$]*/
|
|
8
|
+
|
|
9
|
+
RESERVED_WORDS = %w(
|
|
10
|
+
break case catch continue debugger default delete do else
|
|
11
|
+
finally for function if in instanceof new return switch this
|
|
12
|
+
throw try typeof var void while with
|
|
13
|
+
class const enum export extends import super
|
|
14
|
+
implements interface let package private protected public static yield
|
|
15
|
+
null true false
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
R_THIS_NULL_BOOLEAN = /(?:this|null|true|false)\b/
|
|
19
|
+
R_HEX = /0[xX]/
|
|
20
|
+
R_NUMBERIC = /[+-]?(?:\d|(?:[.]\d))/
|
|
21
|
+
R_STRING = /['"]/
|
|
22
|
+
R_REGEXP = /\/[^\/]/
|
|
23
|
+
|
|
24
|
+
def parse_expr_primary
|
|
25
|
+
log "parse expr primary"
|
|
26
|
+
if check /\(/
|
|
27
|
+
parse_expr_parentheses
|
|
28
|
+
|
|
29
|
+
elsif check /\[/
|
|
30
|
+
parse_expr_array
|
|
31
|
+
|
|
32
|
+
elsif check /\{/
|
|
33
|
+
parse_expr_object
|
|
34
|
+
|
|
35
|
+
# literal
|
|
36
|
+
elsif check R_THIS_NULL_BOOLEAN
|
|
37
|
+
parse_expr_literal_this_null_boolean
|
|
38
|
+
|
|
39
|
+
elsif check R_HEX
|
|
40
|
+
parse_expr_literal_hex
|
|
41
|
+
|
|
42
|
+
elsif check R_NUMBERIC
|
|
43
|
+
parse_expr_literal_number
|
|
44
|
+
|
|
45
|
+
elsif check R_STRING
|
|
46
|
+
parse_expr_literal_string
|
|
47
|
+
|
|
48
|
+
elsif check R_REGEXP
|
|
49
|
+
parse_expr_literal_regexp
|
|
50
|
+
#~
|
|
51
|
+
|
|
52
|
+
else
|
|
53
|
+
parse_expr_identifier
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def parse_expr_parentheses
|
|
58
|
+
log 'parse expr parentheses'
|
|
59
|
+
|
|
60
|
+
pos = skip /\(/
|
|
61
|
+
expr = parse_expression
|
|
62
|
+
skip /\)/
|
|
63
|
+
|
|
64
|
+
create_expression 'parentheses', expr, pos
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def parse_expr_array
|
|
68
|
+
log 'parse expr array'
|
|
69
|
+
|
|
70
|
+
pos = skip /\[/
|
|
71
|
+
elms = batch(:parse_expr_assignment, /\]/, /,/)
|
|
72
|
+
skip /\]/
|
|
73
|
+
|
|
74
|
+
create_expression 'array', Elements.new(elms), pos
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def parse_expr_object
|
|
78
|
+
log 'parse expr object'
|
|
79
|
+
|
|
80
|
+
pos = skip /\{/
|
|
81
|
+
elms = batch(:parse_expr_object_item, /\}/, /,/)
|
|
82
|
+
skip /\}/
|
|
83
|
+
|
|
84
|
+
create_expression 'object', Elements.new(elms), pos
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def parse_expr_object_item
|
|
88
|
+
log 'parse expr object item'
|
|
89
|
+
|
|
90
|
+
name = if check R_STRING
|
|
91
|
+
parse_expr_literal_string
|
|
92
|
+
elsif check R_NUMBERIC
|
|
93
|
+
parse_expr_literal_number
|
|
94
|
+
else
|
|
95
|
+
parse_expr_identifier
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
skip /:/
|
|
99
|
+
value = parse_expr_assignment
|
|
100
|
+
|
|
101
|
+
Expression.new ':', name, value
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def parse_expr_identifier
|
|
105
|
+
log 'parse expr identifier'
|
|
106
|
+
|
|
107
|
+
id = scan(R_IDENTIFY)
|
|
108
|
+
RESERVED_WORDS.include?(id.text) ?
|
|
109
|
+
parse_error("identifier can not be reserved word: #{id}") : id
|
|
110
|
+
|
|
111
|
+
create_expression 'id', id
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def parse_expr_literal_this_null_boolean
|
|
115
|
+
log 'parse expr literal this null boolean'
|
|
116
|
+
expr = scan /this|null|true|false/
|
|
117
|
+
type = expr.text.gsub(/true|false/, 'boolean')
|
|
118
|
+
|
|
119
|
+
create_expression type, expr
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def parse_expr_literal_string
|
|
123
|
+
log 'parse expr literal string'
|
|
124
|
+
|
|
125
|
+
expr = if check /'/
|
|
126
|
+
scan /'(?:(?:\\')|(?:\\\n)|[^'\n])*'/
|
|
127
|
+
elsif check /"/
|
|
128
|
+
scan /"(?:(?:\\")|(?:\\\n)|[^"\n])*"/
|
|
129
|
+
else
|
|
130
|
+
raise 'assert false'
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
create_expression 'string', expr
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def parse_expr_literal_hex
|
|
137
|
+
log 'parse expr literal hex'
|
|
138
|
+
expr = scan /0[xX][0-9a-fA-F]+/
|
|
139
|
+
create_expression 'number', expr
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def parse_expr_literal_number
|
|
143
|
+
log 'parse expr literal number'
|
|
144
|
+
expr = scan /[+-]?(?:(?:\d*[.]\d+)|(?:\d+))(?:[eE][+-]?\d+)?/
|
|
145
|
+
create_expression 'number', expr
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def parse_expr_literal_regexp
|
|
149
|
+
log 'parse expr literal regexp'
|
|
150
|
+
expr = scan %r{/(?:(?:\\.)|(?:\[(?:\\.|[^\[\]])+\])|[^/\\])+/[a-z]*}
|
|
151
|
+
create_expression 'regexp', expr
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
private
|
|
155
|
+
|
|
156
|
+
def create_expression(*args)
|
|
157
|
+
expr = PrimaryExpression.new *args
|
|
158
|
+
log " #{expr}"
|
|
159
|
+
expr
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|
data/lib/js/parser.rb
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
require_relative '../base_parser'
|
|
2
|
+
require_relative 'struct'
|
|
3
|
+
|
|
4
|
+
require_relative 'expr/expr'
|
|
5
|
+
require_relative 'stat/stat'
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
module XRay
|
|
9
|
+
module JS
|
|
10
|
+
|
|
11
|
+
class Parser < XRay::BaseParser
|
|
12
|
+
|
|
13
|
+
include Expr::Expr
|
|
14
|
+
include Stat::Stat
|
|
15
|
+
|
|
16
|
+
attr_reader :singleline_comments, :mutiline_comments
|
|
17
|
+
|
|
18
|
+
def initialize(js, logger)
|
|
19
|
+
super js, logger
|
|
20
|
+
@singleline_comments, @mutiline_comments = [], []
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def parse_program
|
|
24
|
+
log 'parse program'
|
|
25
|
+
parse_comments
|
|
26
|
+
Program.new parse_source_elements
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
alias_method :parse, :parse_program
|
|
30
|
+
|
|
31
|
+
def parse_source_element
|
|
32
|
+
log 'parse source_element'
|
|
33
|
+
|
|
34
|
+
check(/function\b/) ? parse_function_declaration : parse_statement
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def parse_function_declaration(skip_name = false)
|
|
38
|
+
log 'parse function declaration'
|
|
39
|
+
|
|
40
|
+
pos = skip /function/
|
|
41
|
+
|
|
42
|
+
name = (skip_name && check(/\(/)) ? nil : parse_function_name
|
|
43
|
+
skip /\(/
|
|
44
|
+
params = parse_function_parameters
|
|
45
|
+
skip /\)\s*\{/
|
|
46
|
+
body = parse_source_elements true
|
|
47
|
+
skip /\}/
|
|
48
|
+
|
|
49
|
+
FunctionDeclaraion.new name, params, body, pos
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def parse_function_name
|
|
53
|
+
parse_expr_identifier
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def parse_function_parameters
|
|
57
|
+
Elements.new batch(:parse_expr_identifier, /\)/, /,/)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def parse_singleline_comment
|
|
61
|
+
log 'parse singleline comment'
|
|
62
|
+
comment = raw_scan /\/\/.*/
|
|
63
|
+
log " #{comment}"
|
|
64
|
+
comment
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def parse_mutiline_comment
|
|
68
|
+
log 'parse mutiline comment'
|
|
69
|
+
comment = raw_scan /\/\*[^*]*\*+([^\/*][^*]*\*+)*\//
|
|
70
|
+
log " #{comment}"
|
|
71
|
+
comment
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
protected
|
|
75
|
+
|
|
76
|
+
def create_element(klass, *args)
|
|
77
|
+
elm = klass.new *args
|
|
78
|
+
log " #{elm.text} #{elm.position}"
|
|
79
|
+
elm
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
#override
|
|
83
|
+
def after_scan(pattern)
|
|
84
|
+
parse_comments
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def after_skip(pattern)
|
|
88
|
+
parse_comments
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
private
|
|
92
|
+
|
|
93
|
+
def parse_source_elements(inner = false)
|
|
94
|
+
elms = batch(:parse_source_element) do
|
|
95
|
+
skip_empty
|
|
96
|
+
inner ? !check(/\}/) : !eos?
|
|
97
|
+
end
|
|
98
|
+
Elements.new elms
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def parse_comments
|
|
102
|
+
while true
|
|
103
|
+
if check /\/\//
|
|
104
|
+
@singleline_comments << parse_singleline_comment
|
|
105
|
+
elsif check /\/\*/
|
|
106
|
+
@mutiline_comments << parse_mutiline_comment
|
|
107
|
+
else
|
|
108
|
+
break
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
end
|
|
116
|
+
end
|
data/lib/js/rule/all.rb
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
require 'delegate'
|
|
2
|
+
|
|
3
|
+
module XRay
|
|
4
|
+
module JS
|
|
5
|
+
module Rule
|
|
6
|
+
|
|
7
|
+
class All < SimpleDelegator
|
|
8
|
+
|
|
9
|
+
attr_reader :rules
|
|
10
|
+
|
|
11
|
+
def initialize(options = {})
|
|
12
|
+
@rules = load_sub_rules.collect do |name|
|
|
13
|
+
klass = Rule.const_get name
|
|
14
|
+
klass.method(:initialize).arity >= 1 ? klass.new(options) :
|
|
15
|
+
klass.new
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
super @rules
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def load_sub_rules
|
|
22
|
+
Dir.glob File.expand_path( '*.rb', File.dirname(__FILE__) ) do |file|
|
|
23
|
+
require file
|
|
24
|
+
end
|
|
25
|
+
XRay::JS::Rule.constants.select { |c| c.to_s.end_with? 'Rule' }
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
require_relative 'helper'
|
|
3
|
+
require_relative '../../rule'
|
|
4
|
+
|
|
5
|
+
module XRay
|
|
6
|
+
module JS
|
|
7
|
+
module Rule
|
|
8
|
+
|
|
9
|
+
class ChecklistRule
|
|
10
|
+
|
|
11
|
+
include XRay::JS::Rule::Helper, XRay::Rule
|
|
12
|
+
|
|
13
|
+
def visit_statement(stat)
|
|
14
|
+
check_js_statement stat
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def visit_stat_if(stat)
|
|
18
|
+
check_js_stat_if stat
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def visit_expr_member(expr)
|
|
22
|
+
check_js_expr_member expr
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def visit_expr_new(expr)
|
|
26
|
+
check_js_expr_new expr
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def visit_expr_equal(expr)
|
|
30
|
+
check_js_expr_equal expr
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def visit_stat_try(stat)
|
|
34
|
+
check_js_stat_try stat
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require_relative '../../helper/file_reader'
|
|
4
|
+
require_relative 'helper'
|
|
5
|
+
require_relative '../../rule'
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
module XRay
|
|
9
|
+
module JS
|
|
10
|
+
module Rule
|
|
11
|
+
|
|
12
|
+
class FileChecker
|
|
13
|
+
|
|
14
|
+
include Helper, XRay::Rule
|
|
15
|
+
|
|
16
|
+
def check_file(path)
|
|
17
|
+
src = readfile(path)
|
|
18
|
+
ret = results_to_logs check_js_file( path, src )
|
|
19
|
+
|
|
20
|
+
if merge_file? path
|
|
21
|
+
pathes = grep_import_js_path( src )
|
|
22
|
+
ret.concat results_to_logs( check_js_merge_file( path, src, pathes ) )
|
|
23
|
+
ret.concat results_to_logs( check_merge_imports( pathes, path ) )
|
|
24
|
+
end
|
|
25
|
+
ret
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def check_merge_imports( pathes, path )
|
|
29
|
+
ret = []
|
|
30
|
+
pathes.each do |import|
|
|
31
|
+
r = check_js_merge_importing import, pathes, path
|
|
32
|
+
ret.concat r
|
|
33
|
+
end
|
|
34
|
+
ret
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
module XRay
|
|
2
|
+
module JS
|
|
3
|
+
module Rule
|
|
4
|
+
|
|
5
|
+
module Helper
|
|
6
|
+
def find_expr_member(expr)
|
|
7
|
+
while expr.is_a?(Expression) && !yield(expr)
|
|
8
|
+
expr = expr.left
|
|
9
|
+
end
|
|
10
|
+
expr && expr.is_a?(Expression) && yield(expr) ? expr : nil
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def has_doc_comment?(body)
|
|
14
|
+
body =~ /^\s*\/\*\*[^*]*\*+([^\/*][^*]*\*+)*\//
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def readfile(path)
|
|
18
|
+
text, encoding = XRay::Helper::FileReader.readfile path
|
|
19
|
+
text
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def merge_file?(path)
|
|
23
|
+
return path =~ /\w+-merge([-_]?\d+)?\.js$/
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def min_file?(path)
|
|
27
|
+
return path =~ /\w+-min\.js$/
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def func_file?(path)
|
|
31
|
+
!merge_file?(path) and !min_file?(path)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def grep_import_js_path(body)
|
|
35
|
+
lines = body.split /\n/
|
|
36
|
+
pattern = /(ImportJavscript\s*\.\s*url\s*\(\s*['"]?([^'"]+)['"]?\s*\)\s*;?)/
|
|
37
|
+
|
|
38
|
+
pathes = []
|
|
39
|
+
lines.each_with_index do |line, index|
|
|
40
|
+
line.scan(pattern) do |text, url|
|
|
41
|
+
pathes << {
|
|
42
|
+
:text => text,
|
|
43
|
+
:url => url,
|
|
44
|
+
:row => index + 1,
|
|
45
|
+
:col => Regexp.last_match.begin(0) + 1
|
|
46
|
+
}
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
pathes
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def scope(path)
|
|
54
|
+
parts = path.split(/\\\//)
|
|
55
|
+
if parts.include? 'global'
|
|
56
|
+
:global
|
|
57
|
+
elsif parts.include? 'page'
|
|
58
|
+
:page
|
|
59
|
+
elsif parts.include? 'lib' or parts.include? 'sys'
|
|
60
|
+
:lib
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def global_scope?( path )
|
|
65
|
+
scope(path) == :global
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def page_scope?( path )
|
|
69
|
+
scope(path) == :page
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def lib_scope?( path )
|
|
73
|
+
scope(path) == :lib
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def relative?( path )
|
|
77
|
+
path !~ %r(^https?://)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def app( path )
|
|
81
|
+
sep = /(?:\\|\/)/
|
|
82
|
+
path[/#{sep}app#{sep}(.*?)#{sep}(?:.+#{sep})*(global|page|module)(?:\\|\/)/, 1]
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def results_to_logs( results )
|
|
86
|
+
results.map do |r|
|
|
87
|
+
msg, level, row, column = *r
|
|
88
|
+
LogEntry.new(msg, level, row || 0, column || 0)
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
module XRay
|
|
4
|
+
module JS
|
|
5
|
+
module Rule
|
|
6
|
+
|
|
7
|
+
class NoGlobalRule
|
|
8
|
+
|
|
9
|
+
def visit_stat_var(stat)
|
|
10
|
+
unless @scope_index > 0
|
|
11
|
+
['不允许使用全局变量', :error]
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def visit_stat_var_declaration(dec)
|
|
16
|
+
scope = current_scope
|
|
17
|
+
scope << dec.left.text
|
|
18
|
+
nil
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def visit_function_name(name)
|
|
22
|
+
current_scope << name.text
|
|
23
|
+
['不允许申明全局函数', :error] if @scope_index == 0
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def visit_function_parameters(params)
|
|
27
|
+
@scope_index = (@scope_index || 0) + 1
|
|
28
|
+
scope = current_scope(true)
|
|
29
|
+
params.each do |param|
|
|
30
|
+
scope << param.text
|
|
31
|
+
end
|
|
32
|
+
nil
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def visit_stat_for(stat)
|
|
36
|
+
first = stat.condition.first
|
|
37
|
+
nil
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def visit_function_declaration(fun)
|
|
41
|
+
@scope_index -= 1
|
|
42
|
+
nil
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def visit_expr_assignment(expr)
|
|
46
|
+
if expr.type == '='
|
|
47
|
+
id = find_assignment_id(expr.left)
|
|
48
|
+
|
|
49
|
+
if id && use_id_global?(id.text)
|
|
50
|
+
['禁止使用未定义的变量(或全局变量)', :error]
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
private
|
|
56
|
+
|
|
57
|
+
def current_scope(empty = false)
|
|
58
|
+
@scopes ||= []
|
|
59
|
+
@scope_index ||= 0
|
|
60
|
+
@scopes[@scope_index] = [] if empty
|
|
61
|
+
@scopes[@scope_index] ||= []
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def find_assignment_id(expr)
|
|
65
|
+
expr.type == 'id' ? expr :
|
|
66
|
+
expr.type == '.' && expr.left.text == 'window' &&
|
|
67
|
+
expr.right.type == 'id' ? expr.right : nil
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def use_id_global?(id)
|
|
71
|
+
white_list = %w(ImportJavscript)
|
|
72
|
+
|
|
73
|
+
return false if white_list.include? id
|
|
74
|
+
return true unless @scopes && @scope_index
|
|
75
|
+
|
|
76
|
+
@scope_index.downto(0) do |index|
|
|
77
|
+
scope = @scopes[index]
|
|
78
|
+
return false if scope && scope.find { |name| name == id}
|
|
79
|
+
end
|
|
80
|
+
true
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
data/lib/js/stat/if.rb
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module XRay
|
|
2
|
+
module JS
|
|
3
|
+
module Stat
|
|
4
|
+
|
|
5
|
+
module If
|
|
6
|
+
def parse_stat_if
|
|
7
|
+
log 'parse stat if'
|
|
8
|
+
|
|
9
|
+
pos = skip /if\s*\(/
|
|
10
|
+
condition = parse_expression
|
|
11
|
+
skip /\)/
|
|
12
|
+
|
|
13
|
+
true_part = parse_statement
|
|
14
|
+
false_part = if check /else\b/
|
|
15
|
+
skip /else/
|
|
16
|
+
parse_statement
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
create_element IfStatement, condition, true_part, false_part, pos
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
data/lib/js/stat/iter.rb
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
module XRay
|
|
2
|
+
module JS
|
|
3
|
+
module Stat
|
|
4
|
+
|
|
5
|
+
module Iter
|
|
6
|
+
|
|
7
|
+
def parse_stat_dowhile
|
|
8
|
+
log 'parse stat dowhile'
|
|
9
|
+
|
|
10
|
+
pos = skip /do/
|
|
11
|
+
body = parse_statement
|
|
12
|
+
|
|
13
|
+
skip /while\s*\(/
|
|
14
|
+
condition = parse_expression
|
|
15
|
+
skip /\)/
|
|
16
|
+
|
|
17
|
+
create_element DowhileStatement, body, condition, pos
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def parse_stat_while
|
|
21
|
+
log 'parse stat while'
|
|
22
|
+
|
|
23
|
+
pos = skip /while\s*\(/
|
|
24
|
+
condition = parse_expression
|
|
25
|
+
skip /\)/
|
|
26
|
+
body = parse_statement
|
|
27
|
+
|
|
28
|
+
create_element WhileStatement, condition, body, pos
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def parse_stat_for
|
|
32
|
+
log 'parse stat for'
|
|
33
|
+
|
|
34
|
+
pos = skip /for/
|
|
35
|
+
|
|
36
|
+
con_pos = skip /\(/
|
|
37
|
+
first, is_var = parse_stat_for_con_first
|
|
38
|
+
type, second, third = parse_stat_for_con_other(first, is_var)
|
|
39
|
+
skip /\)/
|
|
40
|
+
|
|
41
|
+
condition = create_element ForConditionElement, type, first, second, third, con_pos
|
|
42
|
+
body = parse_statement
|
|
43
|
+
create_element ForStatement, condition, body, pos
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
|
|
48
|
+
def parse_stat_for_con_first
|
|
49
|
+
self.expr_operate_not_in = true
|
|
50
|
+
first = if check /var\b/
|
|
51
|
+
skip /var/
|
|
52
|
+
is_var = true
|
|
53
|
+
parse_stat_var_declarationlist
|
|
54
|
+
elsif !check(/;/)
|
|
55
|
+
parse_expression
|
|
56
|
+
end
|
|
57
|
+
self.expr_operate_not_in = false
|
|
58
|
+
[first, is_var]
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def parse_stat_for_con_other(first, is_var)
|
|
62
|
+
if check /in\b/
|
|
63
|
+
if is_var && first.length != 1 || !is_var && !first.left_hand?
|
|
64
|
+
parse_error('first expression of for-condtion error')
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
skip /in/
|
|
68
|
+
type = is_var ? 'forvarin' : 'forin'
|
|
69
|
+
second = parse_expression
|
|
70
|
+
else
|
|
71
|
+
skip /;/
|
|
72
|
+
type = is_var ? 'forvar' : 'fordefault'
|
|
73
|
+
second = check(/;/) ? nil : parse_expression
|
|
74
|
+
skip /;/
|
|
75
|
+
|
|
76
|
+
third = check(/\)/) ? nil : parse_expression
|
|
77
|
+
end
|
|
78
|
+
[type, second, third]
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|