Bug fixes and updates to satiate functional test.
This commit is contained in:
Родитель
6e2e1fd6b4
Коммит
f87e4757b3
|
@ -9,9 +9,11 @@ def _do_test(path):
|
|||
|
||||
def _do_test_raw(script, path="foo"):
|
||||
"Performs a test on a JS file"
|
||||
|
||||
|
||||
err = validator.testcases.scripting.traverser.MockBundler()
|
||||
validator.testcases.scripting.test_js_file(err, path, script)
|
||||
if err.final_context is not None:
|
||||
print err.final_context.output()
|
||||
|
||||
return err
|
||||
|
||||
|
|
|
@ -14,9 +14,14 @@ def test_innerHTML():
|
|||
x.innerHTML = "<div onclick=\\"foo\\"></div>";
|
||||
""").failed()
|
||||
|
||||
# Test without declaration
|
||||
assert _do_test_raw("""
|
||||
x.innerHTML = "<div onclick=\\"foo\\"></div>";
|
||||
""").failed()
|
||||
|
||||
assert _do_test_raw("""
|
||||
var x = foo();
|
||||
x.innerHTML = "x" + y + "z";
|
||||
x.innerHTML = "x" + y;
|
||||
""").failed()
|
||||
|
||||
|
||||
|
|
|
@ -8,8 +8,11 @@ def test_basic_math():
|
|||
var y = 2;
|
||||
var z = x + y;
|
||||
|
||||
var dbz = 1 / 0; // Should return 0, not break the world.
|
||||
var dbz1 = 1 % 0;
|
||||
var dbz = 1;
|
||||
var dbz1 = 1;
|
||||
dbz = dbz / 0;
|
||||
dbz1 = dbz1 % 0;
|
||||
|
||||
var dbz2 = 1;
|
||||
var dbz3 = 1;
|
||||
dbz2 /= 0;
|
||||
|
@ -25,10 +28,10 @@ def test_basic_math():
|
|||
assert _get_var(err, "y") == 2
|
||||
assert _get_var(err, "z") == 3
|
||||
|
||||
assert _get_var(err, "dbz") is None
|
||||
assert _get_var(err, "dbz1") is None
|
||||
assert _get_var(err, "dbz2") is None
|
||||
assert _get_var(err, "dbz3") is None
|
||||
assert _get_var(err, "dbz") == 0 # Spidermonkey does this.
|
||||
assert _get_var(err, "dbz1") == 0 # ...and this.
|
||||
assert _get_var(err, "dbz2") == 0
|
||||
assert _get_var(err, "dbz3") == 0
|
||||
|
||||
assert _get_var(err, "a") == 5
|
||||
assert _get_var(err, "b") == 4
|
||||
|
@ -50,11 +53,10 @@ def test_in_operator():
|
|||
var b = "asdf" in dict;
|
||||
""")
|
||||
assert err.message_count == 0
|
||||
print err.final_context.output()
|
||||
|
||||
print _get_var(err, "x"), "<<<"
|
||||
assert _get_var(err, "x") == True
|
||||
assert _get_var(err, "y") == True
|
||||
print _get_var(err, "a"), "<<<"
|
||||
assert _get_var(err, "a") == False
|
||||
assert _get_var(err, "b") == False
|
||||
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import math
|
||||
import types
|
||||
|
||||
import spidermonkey
|
||||
|
@ -432,14 +433,14 @@ def _expr_assignment(traverser, node):
|
|||
"+=": lambda: lit_left + lit_right,
|
||||
"-=": lambda: gleft - gright,
|
||||
"*=": lambda: gleft * gright,
|
||||
"/=": lambda: None if gright == 0 else (gleft / gright),
|
||||
"%=": lambda: None if gright == 0 else (gleft % gright),
|
||||
"<<=": lambda: gleft << gright,
|
||||
">>=": lambda: gleft >> lit_right,
|
||||
">>>=": lambda: math.fabs(gleft) >> gright,
|
||||
"|=": lambda: gleft | gright,
|
||||
"^=": lambda: gleft ^ gright,
|
||||
"&=": lambda: gleft & gright}
|
||||
"/=": lambda: 0 if gright == 0 else (gleft / gright),
|
||||
"%=": lambda: 0 if gright == 0 else (gleft % gright),
|
||||
"<<=": lambda: int(gleft) << int(gright),
|
||||
">>=": lambda: int(gleft) >> int(gright),
|
||||
">>>=": lambda: float(abs(int(gleft)) >> gright),
|
||||
"|=": lambda: int(gleft) | int(gright),
|
||||
"^=": lambda: int(gleft) ^ int(gright),
|
||||
"&=": lambda: int(gleft) & int(gright)}
|
||||
|
||||
token = node["operator"]
|
||||
traverser._debug("ASSIGNMENT>>OPERATION:%s" % token)
|
||||
|
@ -448,9 +449,11 @@ def _expr_assignment(traverser, node):
|
|||
traverser.debug_level -= 1
|
||||
return left
|
||||
|
||||
traverser._debug("ASSIGNMENT::LEFT>>%s" % unicode(left.is_global))
|
||||
traverser._debug("ASSIGNMENT::RIGHT>>%s" % unicode(operators[token]()))
|
||||
left.set_value(operators[token](), traverser=traverser)
|
||||
traverser._debug("ASSIGNMENT::L-value global? (%s)" %
|
||||
("Y" if left.is_global else "N"))
|
||||
new_value = operators[token]()
|
||||
traverser._debug("ASSIGNMENT::New value >> %s" % new_value)
|
||||
left.set_value(new_value, traverser=traverser)
|
||||
traverser.debug_level -= 1
|
||||
return left
|
||||
|
||||
|
@ -465,24 +468,29 @@ def _expr_binary(traverser, node):
|
|||
|
||||
traverser.debug_level += 1
|
||||
|
||||
traverser._debug("BIN_EXP>>LEFT")
|
||||
# Traverse the left half of the binary expression.
|
||||
traverser._debug("BIN_EXP>>l-value")
|
||||
traverser.debug_level += 1
|
||||
|
||||
left = traverser._traverse_node(node["left"])
|
||||
if not isinstance(left, JSWrapper):
|
||||
left = JSWrapper(left, traverser=traverser)
|
||||
traverser._debug(unicode(left.dirty))
|
||||
traverser._debug("Is dirty? %r" % left.dirty)
|
||||
|
||||
traverser.debug_level -= 1
|
||||
|
||||
traverser._debug("BIN_EXP>>RIGHT")
|
||||
# Traverse the right half of the binary expression.
|
||||
traverser._debug("BIN_EXP>>r-value")
|
||||
traverser.debug_level += 1
|
||||
|
||||
right = traverser._traverse_node(node["right"])
|
||||
if not isinstance(right, JSWrapper):
|
||||
right = JSWrapper(right, traverser=traverser)
|
||||
traverser._debug(unicode(right.dirty))
|
||||
traverser._debug("Is dirty? %r" % right.dirty)
|
||||
|
||||
# Dirty l or r values mean we can skip the expression. A dirty value
|
||||
# indicates that a lazy operation took place that introduced some
|
||||
# nondeterminacy.
|
||||
if left.dirty:
|
||||
return left
|
||||
elif right.dirty:
|
||||
|
@ -490,17 +498,22 @@ def _expr_binary(traverser, node):
|
|||
|
||||
traverser.debug_level -= 1
|
||||
|
||||
# Binary expressions are only executed on literals.
|
||||
left_wrap = left
|
||||
left = left.get_literal_value()
|
||||
right_wrap = right
|
||||
right = right.get_literal_value()
|
||||
|
||||
# Select the proper operator.
|
||||
operator = node["operator"]
|
||||
traverser._debug("BIN_OPERATOR>>%s" % operator)
|
||||
|
||||
type_operators = (">>", "<<", ">>>")
|
||||
|
||||
# Coerce the literals to numbers for numeric operations.
|
||||
gleft = _get_as_num(left)
|
||||
gright = _get_as_num(right)
|
||||
|
||||
operators = {
|
||||
"==": lambda: left == right or gleft == gright,
|
||||
"!=": lambda: left != right,
|
||||
|
@ -510,13 +523,14 @@ def _expr_binary(traverser, node):
|
|||
"<": lambda: left < right,
|
||||
"<=": lambda: left <= right,
|
||||
">=": lambda: left >= right,
|
||||
"<<": lambda: gleft << gright,
|
||||
">>": lambda: gleft >> gright,
|
||||
">>>": lambda: math.fabs(gleft) >> gright,
|
||||
"<<": lambda: int(gleft) << int(gright),
|
||||
">>": lambda: int(gleft) >> int(gright),
|
||||
">>>": lambda: float(abs(int(gleft)) >> int(gright)),
|
||||
"+": lambda: left + right,
|
||||
"-": lambda: gleft - gright,
|
||||
"*": lambda: gleft * gright,
|
||||
"/": lambda: 0 if gright == 0 else (gleft / gright),
|
||||
"%": lambda: 0 if gright == 0 else (gleft % gright),
|
||||
"in": lambda: right_wrap.contains(left),
|
||||
# TODO : implement instanceof
|
||||
}
|
||||
|
@ -529,7 +543,8 @@ def _expr_binary(traverser, node):
|
|||
left is None or right is None):
|
||||
output = False
|
||||
elif operator in operators:
|
||||
|
||||
# Concatenation can be silly, so always turn undefineds into empty
|
||||
# strings and if there are strings, make everything strings.
|
||||
if operator == "+":
|
||||
if left is None:
|
||||
left = ""
|
||||
|
@ -593,7 +608,7 @@ def _get_as_num(value):
|
|||
try:
|
||||
if isinstance(value, types.StringTypes):
|
||||
return float(value)
|
||||
elif isinstance(value, int) or isinstance(value, float):
|
||||
elif isinstance(value, (int, float, long)):
|
||||
return value
|
||||
else:
|
||||
return int(value)
|
||||
|
|
|
@ -19,13 +19,11 @@ def createElement(args, traverser, node):
|
|||
if not args:
|
||||
return
|
||||
|
||||
simple_args = [traverser._traverse_node(a) for a in args]
|
||||
simple_args = map(traverser._traverse_node, args)
|
||||
|
||||
if unicode(simple_args[0].get_literal_value()).lower() == u"script":
|
||||
_create_script_tag(traverser)
|
||||
elif not (simple_args[0].is_literal() or
|
||||
isinstance(simple_args[0].get_literal_value(),
|
||||
types.StringTypes)):
|
||||
elif not simple_args[0].is_literal():
|
||||
_create_variable_element(traverser)
|
||||
|
||||
|
||||
|
@ -35,13 +33,11 @@ def createElementNS(args, traverser, node):
|
|||
if not args or len(args) < 2:
|
||||
return
|
||||
|
||||
simple_args = [traverser._traverse_node(a) for a in args]
|
||||
simple_args = map(traverser._traverse_node, args)
|
||||
|
||||
if "script" in unicode(simple_args[1].get_literal_value()).lower():
|
||||
_create_script_tag(traverser)
|
||||
elif not (simple_args[1].is_literal() or
|
||||
isinstance(simple_args[1].get_literal_value(),
|
||||
types.StringTypes)):
|
||||
elif not simple_args[1].is_literal():
|
||||
_create_variable_element(traverser)
|
||||
|
||||
|
||||
|
|
|
@ -4,37 +4,40 @@ import types
|
|||
import jstypes
|
||||
|
||||
def set_innerHTML(new_value, traverser):
|
||||
"Tests that values being assigned to innerHTML are not dangerous"
|
||||
"""Tests that values being assigned to innerHTML are not dangerous."""
|
||||
|
||||
if not isinstance(new_value, jstypes.JSWrapper):
|
||||
new_value = jstypes.JSWrapper(new_value, traverser=traverser)
|
||||
literal_value = new_value.get_literal_value()
|
||||
if isinstance(literal_value, types.StringTypes):
|
||||
# Static string assignments
|
||||
|
||||
# Test for on* attributes
|
||||
event_assignment = re.compile("<.+ on[a-z]+=")
|
||||
if event_assignment.search(literal_value.lower()):
|
||||
traverser.err.warning(
|
||||
err_id=("testcases_javascript_instancetypes", "set_innerHTML",
|
||||
"event_assignment"),
|
||||
warning="Event handler assignment via innerHTML",
|
||||
description=["When assigning event handlers, innerHTML "
|
||||
"should never be used. Rather, use a "
|
||||
"proper technique, like addEventListener.",
|
||||
"Event handler code: %s" %
|
||||
literal_value.encode("ascii", "replace")],
|
||||
filename=traverser.filename,
|
||||
line=traverser.line,
|
||||
column=traverser.position,
|
||||
context=traverser.context)
|
||||
else:
|
||||
# Everything checks out, but we still want to pass it through the
|
||||
# markup validator. Turn off strict mode so we don't get warnings
|
||||
# about malformed HTML.
|
||||
from validator.testcases.markup.markuptester import MarkupParser
|
||||
parser = MarkupParser(traverser.err, strict=False, debug=True)
|
||||
parser.process(traverser.filename, literal_value, "xul")
|
||||
if new_value.is_literal():
|
||||
literal_value = new_value.get_literal_value()
|
||||
if isinstance(literal_value, types.StringTypes):
|
||||
# Static string assignments
|
||||
|
||||
# Test for on* attributes
|
||||
event_assignment = re.compile("<.+ on[a-z]+=")
|
||||
if event_assignment.search(literal_value.lower()):
|
||||
traverser.err.warning(
|
||||
err_id=("testcases_javascript_instancetypes",
|
||||
"set_innerHTML", "event_assignment"),
|
||||
warning="Event handler assignment via innerHTML",
|
||||
description=["When assigning event handlers, innerHTML "
|
||||
"should never be used. Rather, use a "
|
||||
"proper technique, like addEventListener.",
|
||||
"Event handler code: %s" %
|
||||
literal_value.encode("ascii", "replace")],
|
||||
filename=traverser.filename,
|
||||
line=traverser.line,
|
||||
column=traverser.position,
|
||||
context=traverser.context)
|
||||
else:
|
||||
# Everything checks out, but we still want to pass it through
|
||||
# the markup validator. Turn off strict mode so we don't get
|
||||
# warnings about malformed HTML.
|
||||
from validator.testcases.markup.markuptester import \
|
||||
MarkupParser
|
||||
parser = MarkupParser(traverser.err, strict=False, debug=True)
|
||||
parser.process(traverser.filename, literal_value, "xul")
|
||||
|
||||
else:
|
||||
# Variable assignments
|
||||
|
@ -85,7 +88,7 @@ def get_operation(mode, property):
|
|||
|
||||
return OBJECT_DEFINITIONS[property][mode]
|
||||
|
||||
elif mode == "set" and property.startswith("on"):
|
||||
elif mode == "set" and unicode(property).startswith("on"):
|
||||
# We can't match all of them manually, so grab all the "on*" properties
|
||||
# and funnel them through the set_on_event function.
|
||||
|
||||
|
|
|
@ -13,8 +13,7 @@ class JSObject(object):
|
|||
|
||||
def __init__(self):
|
||||
self.data = {
|
||||
"prototype": JSPrototype(),
|
||||
"constructor": lambda **keys: JSObject(keys["anon"])
|
||||
u"prototype": JSPrototype()
|
||||
}
|
||||
|
||||
def get(self, name):
|
||||
|
@ -40,7 +39,9 @@ class JSObject(object):
|
|||
return name in self.data
|
||||
|
||||
def output(self):
|
||||
return unicode(self.data)
|
||||
return json.dumps(dict(zip(self.data.keys(),
|
||||
map(lambda v:v.output(),
|
||||
self.data.values()))))
|
||||
|
||||
|
||||
class JSContext(JSObject):
|
||||
|
@ -53,12 +54,6 @@ class JSContext(JSObject):
|
|||
def set(self, name, value):
|
||||
JSObject.set(self, name, value, None)
|
||||
|
||||
def output(self):
|
||||
output = {}
|
||||
for (name, item) in self.data.items():
|
||||
output[name] = unicode(item)
|
||||
return json.dumps(output)
|
||||
|
||||
|
||||
class JSWrapper(object):
|
||||
"""Wraps a JS value and handles contextual functions for it."""
|
||||
|
@ -114,6 +109,7 @@ class JSWrapper(object):
|
|||
|
||||
# Process any setter/modifier
|
||||
if self.setter:
|
||||
traverser._debug("Running setter on JSWrapper...");
|
||||
value = self.setter(value, traverser) or value or None
|
||||
|
||||
if value == self.value:
|
||||
|
@ -136,7 +132,7 @@ class JSWrapper(object):
|
|||
context=traverser.context)
|
||||
return self
|
||||
|
||||
if isinstance(value, (bool, str, int, float, unicode)):
|
||||
if isinstance(value, (bool, str, int, float, long, unicode)):
|
||||
value = JSLiteral(value)
|
||||
# If the value being assigned is a wrapper as well, copy it in
|
||||
elif isinstance(value, JSWrapper):
|
||||
|
@ -146,6 +142,8 @@ class JSWrapper(object):
|
|||
self.is_global = value.is_global
|
||||
# const does not carry over on reassignment
|
||||
return self
|
||||
elif isinstance(value, types.LambdaType):
|
||||
value = value()
|
||||
|
||||
self.value = value
|
||||
return self
|
||||
|
@ -172,10 +170,8 @@ class JSWrapper(object):
|
|||
def get(self, traverser, name):
|
||||
"""Retrieves a property from the variable"""
|
||||
|
||||
if self.value is None:
|
||||
return JSWrapper(traverser=traverser, dirty=True)
|
||||
|
||||
value = self.value
|
||||
dirty = value is None
|
||||
if self.is_global:
|
||||
if "value" not in value:
|
||||
return JSWrapper(traverser=traverser)
|
||||
|
@ -202,10 +198,17 @@ class JSWrapper(object):
|
|||
if modifier:
|
||||
modifier(traverser)
|
||||
|
||||
output = value.get(name) if issubclass(type(value), JSObject) else None
|
||||
if value is not None:
|
||||
output = (value.get(name) if
|
||||
issubclass(type(value), JSObject) else
|
||||
None)
|
||||
else:
|
||||
output = None
|
||||
|
||||
if not isinstance(output, JSWrapper):
|
||||
output = JSWrapper(output, traverser=traverser, dirty=output is None)
|
||||
output = JSWrapper(output,
|
||||
traverser=traverser,
|
||||
dirty=output is None or dirty)
|
||||
|
||||
# If we can predetermine the setter for the wrapper, we can save a ton
|
||||
# of lookbehinds in the future. This greatly simplifies the
|
||||
|
@ -258,16 +261,19 @@ class JSWrapper(object):
|
|||
"""Returns the literal value of the wrapper"""
|
||||
|
||||
if self.is_global:
|
||||
return None
|
||||
return ""
|
||||
if self.value is None:
|
||||
return None
|
||||
return ""
|
||||
|
||||
return self.value.get_literal_value()
|
||||
output = self.value.get_literal_value()
|
||||
return output if output is not None else ""
|
||||
|
||||
def output(self):
|
||||
"""Returns a readable version of the object"""
|
||||
if self.value is None or self.is_global:
|
||||
return ""
|
||||
if self.value is None:
|
||||
return "(None)"
|
||||
elif self.is_global:
|
||||
return "(Global)"
|
||||
|
||||
return self.value.output()
|
||||
|
||||
|
@ -288,10 +294,10 @@ class JSLiteral(JSObject):
|
|||
|
||||
def __str__(self):
|
||||
"Returns a human-readable version of the variable's contents"
|
||||
return json.dumps(self.value)
|
||||
return self.output()
|
||||
|
||||
def output(self):
|
||||
return self.__str__()
|
||||
return self.value
|
||||
|
||||
def get_literal_value(self):
|
||||
"Returns the literal value of a this literal. Heh."
|
||||
|
@ -351,7 +357,9 @@ class JSArray(JSObject):
|
|||
# Interestingly enough, this allows for things like:
|
||||
# x = [4]
|
||||
# y = x * 3 // y = 12 since x equals "4"
|
||||
return u",".join([unicode(w.get_literal_value()) for w in self.elements])
|
||||
return u",".join([unicode(w.get_literal_value()) for
|
||||
w in self.elements if
|
||||
not (isinstance(w, JSWrapper) and w.value == self)])
|
||||
|
||||
def set(self, index, value, traverser=None):
|
||||
"""Follow the rules of JS for creating an array"""
|
||||
|
@ -379,5 +387,5 @@ class JSArray(JSObject):
|
|||
self.elements.append(JSWrapper(value=value, traverser=traverser))
|
||||
|
||||
def output(self):
|
||||
return self.get_literal_value()
|
||||
return [x.output() for x in self.elements]
|
||||
|
||||
|
|
|
@ -166,6 +166,7 @@ class Traverser:
|
|||
return wrapper
|
||||
|
||||
self._debug("TRAVERSE>>%s" % (node["type"]))
|
||||
self.debug_level += 1
|
||||
|
||||
# Extract location information if it's available
|
||||
if "loc" in node and node["loc"] is not None:
|
||||
|
@ -192,11 +193,15 @@ class Traverser:
|
|||
action_result = None
|
||||
if action is not None:
|
||||
action_result = action(self, node)
|
||||
self._debug("ACTION>>%s (%s)" %
|
||||
("halt>>%s" % unicode(action_result) if
|
||||
action_result else
|
||||
"continue",
|
||||
node["type"]))
|
||||
|
||||
if DEBUG:
|
||||
action_debug = "continue"
|
||||
if action_result is not None:
|
||||
action_debug = (action_result.output() if
|
||||
isinstance(action_result, JSWrapper) else
|
||||
action_result)
|
||||
self._debug("ACTION>>%s (%s)" % (action_debug,
|
||||
node["type"]))
|
||||
|
||||
# print node["type"], branches
|
||||
if action_result is None:
|
||||
|
@ -219,6 +224,8 @@ class Traverser:
|
|||
if establish_context or block_level:
|
||||
self._pop_context()
|
||||
|
||||
self.debug_level -= 1
|
||||
|
||||
# If there is an action and the action returned a value, it should be
|
||||
# returned to the node traversal that initiated this node's traversal.
|
||||
if returns:
|
||||
|
|
Загрузка…
Ссылка в новой задаче