Added stronger debug support for JS.

This commit is contained in:
mattbasta 2010-10-11 12:05:41 -04:00
Родитель 8a78d5da69
Коммит 3376598811
2 изменённых файлов: 69 добавлений и 10 удалений

Просмотреть файл

@ -1,11 +1,11 @@
import copy
import traverser
import traverser as js_traverser
def _function(traverser, node):
"Prevents code duplication"
me = traverser.JSObject()
me = js_traverser.JSObject()
traverser._pop_context()
traverser._push_context(me)
@ -71,7 +71,7 @@ def _define_var(traverser, node):
def _define_obj(traverser, node):
"Creates a local context object"
var = traverser.JSObject()
var = js_traverser.JSObject()
for prop in node["properties"]:
var_name = ""
if prop["type"] == "Literal":
@ -88,7 +88,7 @@ def _define_obj(traverser, node):
def _define_array(traverser, node):
"Instantiates an array object"
arr = traverser.JSArray()
arr = js_traverser.JSArray()
for elem in node["elements"]:
arr.elements.append(traverser._traverse_node(elem))
@ -96,12 +96,15 @@ def _define_array(traverser, node):
def _define_literal(traverser, node):
"Creates a JSVariable object based on a literal"
var = traverser.JSVariable()
var.set_value(node["value"])
var = js_traverser.JSVariable()
var.set_value(traverser, node["value"])
return var
def _call_settimeout(traverser, *args):
# TODO : Analyze args[0]. If it's a function, return false.
if js_traverser.DEBUG:
print "ACTIONS>>TIMEOUT>>>"
print args
return True
def _expression(traverser, node):

Просмотреть файл

@ -6,11 +6,30 @@ from validator.testcases.javascript.predefinedentities import GLOBAL_ENTITIES
DEBUG = True
class MockBundler:
def error(self, id, title, description, file="", line=1):
"Represents a mock error"
print "-" * 30
print title
print "~" * len(title)
print description
print "in %s:line %d" % (file, line)
def warning(self, id, title, description, file="", line=1):
self.error(id, title, description, file, line)
def info(self, id, title, description, file="", line=1):
self.error(id, title, description, file, line)
class Traverser:
"Traverses the AST Tree and determines problems with a chunk of JS."
def __init__(self, err, filename, start_line=0):
self.err = err
if err is not None:
self.err = err
else:
self.err = MockBundler()
self.contexts = []
self.block_contexts = []
self.filename = filename
@ -21,10 +40,21 @@ class Traverser:
# Can use the `this` object
self.can_use_this = False
self.this_stack = []
# For debugging
self.debug_level = 0
def _debug(self, data):
"Writes a message to the console if debugging is enabled."
if DEBUG:
print ". " * self.debug_level + str(data)
def run(self, data):
if "type" not in data or not self._can_handle_node(data["type"]):
self._debug("ERR>>Cannot handle node type %s" % (data["type"] if
"type" in data
else "<unknown>"))
self.err.error(("testcases_javascript_traverser",
"run",
"cannot_interpret_js"),
@ -35,8 +65,13 @@ class Traverser:
self.start_line)
return None
self._debug("START>>")
self._push_context()
self._traverse_node(data)
self._debug("END>>")
if self.contexts:
# This performs the namespace pollution test.
# {prefix, count}
@ -60,6 +95,8 @@ class Traverser:
if "type" not in node or not self._can_handle_node(node["type"]):
return JSObject()
self._debug("TRAVERSE>>%s" % (node["type"]))
self.line = self.start_line + int(node["loc"]["start"]["line"])
(branches,
@ -74,18 +111,24 @@ class Traverser:
elif block_level:
self._push_block_context()
docontinue = None
docontinue = True
if action is not None:
docontinue = action(self, node)
self._debug("ACTION>>%s" % ("continue" if docontinue else "halt"))
if docontinue is not None:
self.debug_level += 1
for branch in branches:
if branch in node:
self._debug("BRANCH>>%s" % branch)
self.debug_level += 1
b = node[branch]
if isinstance(b, list):
self._interpret_block(b)
else:
self._traverse_node(b)
self.debug_level -= 1
self.debug_level -= 1
if establish_context or block_level:
self._pop_context()
@ -109,13 +152,22 @@ class Traverser:
if default is None:
default = JSContext("default")
self.contexts.append(default)
self.debug_level += 1
self._debug("CONTEXT>>%d" % len(self.contexts))
def _pop_context(self):
"Adds a variable context to the current interpretation frame"
# Keep the global scope on the stack.
if len(self.contexts) == 1:
self._debug("CONTEXT>>ROOT POP ABORTED")
return
self.contexts.pop()
popped_context = self.contexts.pop()
self.debug_level -= 1
self._debug("POP_CONTEXT>>%d" % len(self.contexts))
self._debug(popped_context.__dict__)
def _peek_context(self, depth=1):
"""Returns the most recent context. Note that this should NOT be used
@ -126,6 +178,8 @@ class Traverser:
def _seek_variable(self, variable, depth=-1):
"Returns the value of a variable that has been declared in a context"
self._debug("SEEK>>%s>>%d" % (variable, depth))
for c in range(len(self.contexts) - 1, 0):
context = self.contexts[c]
if context.has_var(variable):
@ -192,7 +246,7 @@ class JSContext(object):
"A variable context"
def __init__(self, context_type):
self.type_ = context_type
self._type = context_type
def __getattr__(self, name):
if name in ("_type", ):
@ -219,6 +273,7 @@ class JSVariable(object):
def set_value(self, traverser, value, line=0):
if self.const:
self._debug("LITERAL>>CONSTANT REASSIGNMENT")
traverser.err.error(("testcases_javascript_traverser",
"set_value",
"const_assignment"),
@ -228,6 +283,7 @@ class JSVariable(object):
been initialized.""",
traverser.filename,
traverser.line)
traverser._debug("LITERAL::%s" % json.dumps(value))
self.value = value
class JSObject(object):