15 import Utilities |
15 import Utilities |
16 import Utilities.ClassBrowsers as ClassBrowsers |
16 import Utilities.ClassBrowsers as ClassBrowsers |
17 from . import ClbrBaseClasses |
17 from . import ClbrBaseClasses |
18 |
18 |
19 SUPPORTED_TYPES = [ClassBrowsers.JS_SOURCE] |
19 SUPPORTED_TYPES = [ClassBrowsers.JS_SOURCE] |
20 |
20 |
21 _modules = {} # cache of modules we've seen |
21 _modules = {} # cache of modules we've seen |
22 |
22 |
23 |
23 |
24 class VisibilityMixin(ClbrBaseClasses.ClbrVisibilityMixinBase): |
24 class VisibilityMixin(ClbrBaseClasses.ClbrVisibilityMixinBase): |
25 """ |
25 """ |
26 Mixin class implementing the notion of visibility. |
26 Mixin class implementing the notion of visibility. |
27 """ |
27 """ |
|
28 |
28 def __init__(self): |
29 def __init__(self): |
29 """ |
30 """ |
30 Constructor |
31 Constructor |
31 """ |
32 """ |
32 if self.name.startswith('__'): |
33 if self.name.startswith("__"): |
33 self.setPrivate() |
34 self.setPrivate() |
34 elif self.name.startswith('_'): |
35 elif self.name.startswith("_"): |
35 self.setProtected() |
36 self.setProtected() |
36 else: |
37 else: |
37 self.setPublic() |
38 self.setPublic() |
38 |
39 |
39 |
40 |
40 class Function(ClbrBaseClasses.Function, VisibilityMixin): |
41 class Function(ClbrBaseClasses.Function, VisibilityMixin): |
41 """ |
42 """ |
42 Class to represent a Python function. |
43 Class to represent a Python function. |
43 """ |
44 """ |
44 def __init__(self, module, name, file, lineno, signature='', |
45 |
45 separator=','): |
46 def __init__(self, module, name, file, lineno, signature="", separator=","): |
46 """ |
47 """ |
47 Constructor |
48 Constructor |
48 |
49 |
49 @param module name of the module containing this function |
50 @param module name of the module containing this function |
50 @param name name of this function |
51 @param name name of this function |
51 @param file filename containing this class |
52 @param file filename containing this class |
52 @param lineno linenumber of the class definition |
53 @param lineno linenumber of the class definition |
53 @param signature parameterlist of the method |
54 @param signature parameterlist of the method |
54 @param separator string separating the parameters |
55 @param separator string separating the parameters |
55 """ |
56 """ |
56 ClbrBaseClasses.Function.__init__(self, module, name, file, lineno, |
57 ClbrBaseClasses.Function.__init__( |
57 signature, separator) |
58 self, module, name, file, lineno, signature, separator |
|
59 ) |
58 VisibilityMixin.__init__(self) |
60 VisibilityMixin.__init__(self) |
59 |
61 |
60 |
62 |
61 class Attribute(ClbrBaseClasses.Attribute, VisibilityMixin): |
63 class Attribute(ClbrBaseClasses.Attribute, VisibilityMixin): |
62 """ |
64 """ |
63 Class to represent a class attribute. |
65 Class to represent a class attribute. |
64 """ |
66 """ |
|
67 |
65 def __init__(self, module, name, file, lineno): |
68 def __init__(self, module, name, file, lineno): |
66 """ |
69 """ |
67 Constructor |
70 Constructor |
68 |
71 |
69 @param module name of the module containing this class |
72 @param module name of the module containing this class |
70 @param name name of this class |
73 @param name name of this class |
71 @param file filename containing this attribute |
74 @param file filename containing this attribute |
72 @param lineno linenumber of the class definition |
75 @param lineno linenumber of the class definition |
73 """ |
76 """ |
116 # ignore syntax errors of the parser |
120 # ignore syntax errors of the parser |
117 pass |
121 pass |
118 except jsTokenizer.ParseError: |
122 except jsTokenizer.ParseError: |
119 # ignore syntax errors of the tokenizer |
123 # ignore syntax errors of the tokenizer |
120 pass |
124 pass |
121 |
125 |
122 return self.__dict |
126 return self.__dict |
123 |
127 |
124 def __visit(self, root): |
128 def __visit(self, root): |
125 """ |
129 """ |
126 Private method implementing the visit logic delegating to interesting |
130 Private method implementing the visit logic delegating to interesting |
127 methods. |
131 methods. |
128 |
132 |
129 @param root root node to visit |
133 @param root root node to visit |
130 """ |
134 """ |
|
135 |
131 def call(n): |
136 def call(n): |
132 getattr(self, "visit_{0}".format(n.type), |
137 getattr(self, "visit_{0}".format(n.type), self.visit_noop)(n) |
133 self.visit_noop)(n) |
138 |
134 |
|
135 call(root) |
139 call(root) |
136 for node in root: |
140 for node in root: |
137 self.__visit(node) |
141 self.__visit(node) |
138 |
142 |
139 def visit_noop(self, node): |
143 def visit_noop(self, node): |
140 """ |
144 """ |
141 Public method to ignore the given node. |
145 Public method to ignore the given node. |
142 |
146 |
143 @param node reference to the node (jasy.script.parse.Node.Node) |
147 @param node reference to the node (jasy.script.parse.Node.Node) |
144 """ |
148 """ |
145 pass |
149 pass |
146 |
150 |
147 def visit_function(self, node): |
151 def visit_function(self, node): |
148 """ |
152 """ |
149 Public method to treat a function node. |
153 Public method to treat a function node. |
150 |
154 |
151 @param node reference to the node (jasy.script.parse.Node.Node) |
155 @param node reference to the node (jasy.script.parse.Node.Node) |
152 """ |
156 """ |
153 if ( |
157 if ( |
154 node.type == "function" and |
158 node.type == "function" |
155 getattr(node, "name", None) and |
159 and getattr(node, "name", None) |
156 node.functionForm == "declared_form" |
160 and node.functionForm == "declared_form" |
157 ): |
161 ): |
158 if self.__stack and self.__stack[-1].endlineno < node.line: |
162 if self.__stack and self.__stack[-1].endlineno < node.line: |
159 del self.__stack[-1] |
163 del self.__stack[-1] |
160 endline = node.line + self.__source.count( |
164 endline = node.line + self.__source.count("\n", node.start, node.end) |
161 '\n', node.start, node.end) |
|
162 if getattr(node, "params", None): |
165 if getattr(node, "params", None): |
163 func_sig = ", ".join([p.value for p in node.params]) |
166 func_sig = ", ".join([p.value for p in node.params]) |
164 else: |
167 else: |
165 func_sig = "" |
168 func_sig = "" |
166 if self.__stack: |
169 if self.__stack: |
167 # it's a nested function |
170 # it's a nested function |
168 cur_func = self.__stack[-1] |
171 cur_func = self.__stack[-1] |
169 f = Function(None, node.name, |
172 f = Function(None, node.name, self.__file, node.line, func_sig) |
170 self.__file, node.line, func_sig) |
|
171 f.setEndLine(endline) |
173 f.setEndLine(endline) |
172 cur_func._addmethod(node.name, f) |
174 cur_func._addmethod(node.name, f) |
173 else: |
175 else: |
174 f = Function(self.__module, node.name, |
176 f = Function(self.__module, node.name, self.__file, node.line, func_sig) |
175 self.__file, node.line, func_sig) |
|
176 f.setEndLine(endline) |
177 f.setEndLine(endline) |
177 func_name = node.name |
178 func_name = node.name |
178 if func_name in self.__dict_counts: |
179 if func_name in self.__dict_counts: |
179 self.__dict_counts[func_name] += 1 |
180 self.__dict_counts[func_name] += 1 |
180 func_name = "{0}_{1:d}".format( |
181 func_name = "{0}_{1:d}".format( |
181 func_name, self.__dict_counts[func_name]) |
182 func_name, self.__dict_counts[func_name] |
|
183 ) |
182 else: |
184 else: |
183 self.__dict_counts[func_name] = 0 |
185 self.__dict_counts[func_name] = 0 |
184 self.__dict[func_name] = f |
186 self.__dict[func_name] = f |
185 self.__stack.append(f) |
187 self.__stack.append(f) |
186 |
188 |
187 def visit_property_init(self, node): |
189 def visit_property_init(self, node): |
188 """ |
190 """ |
189 Public method to treat a property_init node. |
191 Public method to treat a property_init node. |
190 |
192 |
191 @param node reference to the node (jasy.script.parse.Node.Node) |
193 @param node reference to the node (jasy.script.parse.Node.Node) |
192 """ |
194 """ |
193 if node.type == "property_init" and node[1].type == "function": |
195 if node.type == "property_init" and node[1].type == "function": |
194 if self.__stack and self.__stack[-1].endlineno < node[0].line: |
196 if self.__stack and self.__stack[-1].endlineno < node[0].line: |
195 del self.__stack[-1] |
197 del self.__stack[-1] |
196 endline = node[0].line + self.__source.count( |
198 endline = node[0].line + self.__source.count("\n", node.start, node[1].end) |
197 '\n', node.start, node[1].end) |
|
198 if getattr(node[1], "params", None): |
199 if getattr(node[1], "params", None): |
199 func_sig = ", ".join([p.value for p in node[1].params]) |
200 func_sig = ", ".join([p.value for p in node[1].params]) |
200 else: |
201 else: |
201 func_sig = "" |
202 func_sig = "" |
202 if self.__stack: |
203 if self.__stack: |
203 # it's a nested function |
204 # it's a nested function |
204 cur_func = self.__stack[-1] |
205 cur_func = self.__stack[-1] |
205 f = Function(None, node[0].value, |
206 f = Function(None, node[0].value, self.__file, node[0].line, func_sig) |
206 self.__file, node[0].line, func_sig) |
|
207 f.setEndLine(endline) |
207 f.setEndLine(endline) |
208 cur_func._addmethod(node[0].value, f) |
208 cur_func._addmethod(node[0].value, f) |
209 else: |
209 else: |
210 f = Function(self.__module, node[0].value, |
210 f = Function( |
211 self.__file, node[0].line, func_sig) |
211 self.__module, node[0].value, self.__file, node[0].line, func_sig |
|
212 ) |
212 f.setEndLine(endline) |
213 f.setEndLine(endline) |
213 func_name = node[0].value |
214 func_name = node[0].value |
214 if func_name in self.__dict_counts: |
215 if func_name in self.__dict_counts: |
215 self.__dict_counts[func_name] += 1 |
216 self.__dict_counts[func_name] += 1 |
216 func_name = "{0}_{1:d}".format( |
217 func_name = "{0}_{1:d}".format( |
217 func_name, self.__dict_counts[func_name]) |
218 func_name, self.__dict_counts[func_name] |
|
219 ) |
218 else: |
220 else: |
219 self.__dict_counts[func_name] = 0 |
221 self.__dict_counts[func_name] = 0 |
220 self.__dict[func_name] = f |
222 self.__dict[func_name] = f |
221 self.__stack.append(f) |
223 self.__stack.append(f) |
222 |
224 |
223 def visit_var(self, node): |
225 def visit_var(self, node): |
224 """ |
226 """ |
225 Public method to treat a variable node. |
227 Public method to treat a variable node. |
226 |
228 |
227 @param node reference to the node (jasy.script.parse.Node.Node) |
229 @param node reference to the node (jasy.script.parse.Node.Node) |
228 """ |
230 """ |
229 if ( |
231 if ( |
230 node.type == "var" and |
232 node.type == "var" |
231 node.parent.type == "script" and |
233 and node.parent.type == "script" |
232 node.getChildrenLength() |
234 and node.getChildrenLength() |
|
235 ): |
|
236 if self.__stack and self.__stack[-1].endlineno < node[0].line: |
|
237 del self.__stack[-1] |
|
238 if self.__stack: |
|
239 # function variables |
|
240 for var in node: |
|
241 attr = Attribute(self.__module, var.name, self.__file, var.line) |
|
242 self.__stack[-1]._addattribute(attr) |
|
243 else: |
|
244 # global variable |
|
245 if "@@Globals@@" not in self.__dict: |
|
246 self.__dict["@@Globals@@"] = ClbrBaseClasses.ClbrBase( |
|
247 self.__module, "Globals", self.__file, 0 |
|
248 ) |
|
249 for var in node: |
|
250 self.__dict["@@Globals@@"]._addglobal( |
|
251 Attribute(self.__module, var.name, self.__file, var.line) |
|
252 ) |
|
253 |
|
254 def visit_const(self, node): |
|
255 """ |
|
256 Public method to treat a constant node. |
|
257 |
|
258 @param node reference to the node (jasy.script.parse.Node.Node) |
|
259 """ |
|
260 if ( |
|
261 node.type == "const" |
|
262 and node.parent.type == "script" |
|
263 and node.getChildrenLength() |
233 ): |
264 ): |
234 if self.__stack and self.__stack[-1].endlineno < node[0].line: |
265 if self.__stack and self.__stack[-1].endlineno < node[0].line: |
235 del self.__stack[-1] |
266 del self.__stack[-1] |
236 if self.__stack: |
267 if self.__stack: |
237 # function variables |
268 # function variables |
238 for var in node: |
269 for var in node: |
239 attr = Attribute( |
270 attr = Attribute( |
240 self.__module, var.name, self.__file, var.line) |
271 self.__module, "const " + var.name, self.__file, var.line |
|
272 ) |
241 self.__stack[-1]._addattribute(attr) |
273 self.__stack[-1]._addattribute(attr) |
242 else: |
274 else: |
243 # global variable |
275 # global variable |
244 if "@@Globals@@" not in self.__dict: |
276 if "@@Globals@@" not in self.__dict: |
245 self.__dict["@@Globals@@"] = ClbrBaseClasses.ClbrBase( |
277 self.__dict["@@Globals@@"] = ClbrBaseClasses.ClbrBase( |
246 self.__module, "Globals", self.__file, 0) |
278 self.__module, "Globals", self.__file, 0 |
247 for var in node: |
279 ) |
248 self.__dict["@@Globals@@"]._addglobal(Attribute( |
|
249 self.__module, var.name, self.__file, var.line)) |
|
250 |
|
251 def visit_const(self, node): |
|
252 """ |
|
253 Public method to treat a constant node. |
|
254 |
|
255 @param node reference to the node (jasy.script.parse.Node.Node) |
|
256 """ |
|
257 if ( |
|
258 node.type == "const" and |
|
259 node.parent.type == "script" and |
|
260 node.getChildrenLength() |
|
261 ): |
|
262 if self.__stack and self.__stack[-1].endlineno < node[0].line: |
|
263 del self.__stack[-1] |
|
264 if self.__stack: |
|
265 # function variables |
|
266 for var in node: |
|
267 attr = Attribute(self.__module, "const " + var.name, |
|
268 self.__file, var.line) |
|
269 self.__stack[-1]._addattribute(attr) |
|
270 else: |
|
271 # global variable |
|
272 if "@@Globals@@" not in self.__dict: |
|
273 self.__dict["@@Globals@@"] = ClbrBaseClasses.ClbrBase( |
|
274 self.__module, "Globals", self.__file, 0) |
|
275 for var in node: |
280 for var in node: |
276 self.__dict["@@Globals@@"]._addglobal( |
281 self.__dict["@@Globals@@"]._addglobal( |
277 Attribute(self.__module, "const " + var.name, |
282 Attribute( |
278 self.__file, var.line)) |
283 self.__module, "const " + var.name, self.__file, var.line |
|
284 ) |
|
285 ) |
279 |
286 |
280 |
287 |
281 def readmodule_ex(module, path=None): |
288 def readmodule_ex(module, path=None): |
282 """ |
289 """ |
283 Read a JavaScript file and return a dictionary of functions and variables. |
290 Read a JavaScript file and return a dictionary of functions and variables. |