Permalink
Browse files

Convert globals to JSGlobal

  • Loading branch information...
1 parent 4be76e9 commit 45d1e621f4ef9edf2528cac4cbb50420da94a333 @mattbasta committed Oct 10, 2013
View
1 .gitignore
@@ -8,3 +8,4 @@
/appvalidator/constants_local.py
/src
/extras/jslibs
+.noseids
View
42 appvalidator/testcases/javascript/call_definitions.py
@@ -15,35 +15,31 @@
# Global object function definitions:
def string_global(wrapper, arguments, traverser):
- if not arguments:
- return JSWrapper("", traverser=traverser)
- arg = traverser.traverse_node(arguments[0])
- value = utils.get_as_str(arg.get_literal_value())
+ if (not arguments or
+ not arguments[0].get_literal_value()):
+ return JSWrapper(JSObject(), traverser=traverser)
+ value = utils.get_as_str(arguments[0].get_literal_value())
return JSWrapper(value, traverser=traverser)
def array_global(wrapper, arguments, traverser):
- output = JSArray()
- if arguments:
- output.elements = map(traverser.traverse_node, arguments)
- return JSWrapper(output, traverser=traverser)
+ return JSWrapper(JSArray(arguments), traverser=traverser)
def number_global(wrapper, arguments, traverser):
if not arguments:
return JSWrapper(0, traverser=traverser)
- arg = traverser.traverse_node(arguments[0])
try:
- return float(arg.get_literal_value())
+ return JSWrapper(float(arguments[0].get_literal_value()), traverser=traverser)
except (ValueError, TypeError):
return utils.get_NaN(traverser)
def boolean_global(wrapper, arguments, traverser):
if not arguments:
return JSWrapper(False, traverser=traverser)
- arg = traverser.traverse_node(arguments[0])
- return JSWrapper(bool(arg.get_literal_value()), traverser=traverser)
+ return JSWrapper(bool(arguments[0].get_literal_value()),
+ traverser=traverser)
def python_wrap(func, args, nargs=False):
@@ -67,15 +63,13 @@ def _process_literal(type_, literal):
return literal
def wrap(wrapper, arguments, traverser):
- passed_args = map(traverser.traverse_node, arguments)
-
params = []
if not nargs:
# Handle definite argument lists.
for type_, def_value in args:
- if passed_args:
- parg = passed_args[0]
- passed_args = passed_args[1:]
+ if arguments:
+ parg = arguments[0]
+ arguments = arguments[1:]
passed_literal = parg.get_literal_value()
passed_literal = _process_literal(type_, passed_literal)
@@ -84,7 +78,7 @@ def wrap(wrapper, arguments, traverser):
params.append(def_value)
else:
# Handle dynamic argument lists.
- for arg in passed_args:
+ for arg in arguments:
literal = arg.get_literal_value()
params.append(_process_literal(args[0], literal))
@@ -103,11 +97,10 @@ def wrap(wrapper, arguments, traverser):
def math_log(wrapper, arguments, traverser):
"""Return a better value than the standard python log function."""
- args = map(traverser.traverse_node, arguments)
- if not args:
+ if not arguments:
return JSWrapper(0, traverser=traverser)
- arg = utils.get_as_num(args[0].get_literal_value())
+ arg = utils.get_as_num(arguments[0].get_literal_value())
if arg == 0:
return JSWrapper(float('-inf'), traverser=traverser)
@@ -120,14 +113,13 @@ def math_log(wrapper, arguments, traverser):
def math_round(wrapper, arguments, traverser):
"""Return a better value than the standard python round function."""
- args = map(traverser.traverse_node, arguments)
- if not args:
+ if not arguments:
return JSWrapper(0, traverser=traverser)
- arg = utils.get_as_num(args[0].get_literal_value())
+ arg = utils.get_as_num(arguments[0].get_literal_value())
# Prevent nasty infinity tracebacks.
if abs(arg) == float("inf"):
- return args[0]
+ return arguments[0]
# Python rounds away from zero, JS rounds "up".
if arg < 0 and int(arg) != arg:
View
45 appvalidator/testcases/javascript/entity_values.py
@@ -1,6 +1,3 @@
-from appvalidator.constants import BUGZILLA_BUG
-
-
ENTITIES = {}
def register_entity(name):
"""Allow an entity's modifier to be registered for use."""
@@ -11,46 +8,45 @@ def wrap(function):
def entity(name, result=None):
- def return_wrap(t):
- output = ENTITIES[name](traverser=t)
- if result is not None:
- return result
- return output or {"value": {}}
- return {"value": return_wrap}
+ ent = ENTITIES[name]
+ if callable(ent):
+ ent = ent()
+ ENTITIES[name] = ent
+ return ent or {"value": {}}
@register_entity("Function")
@register_entity("eval")
-def csp_warning_function(traverser):
+def csp_warning_function():
def call_wrap(*args, **kwargs):
+ traverser = kwargs.get("traverser") or args[-1]
from appvalidator.csp import warn
warn(err=traverser.err,
filename=traverser.filename,
line=traverser.line,
column=traverser.position,
context=traverser.context,
violation_type="script")
- return False
return {
+ "new": call_wrap,
"return": call_wrap,
"value": call_wrap,
}
@register_entity("setTimeout")
@register_entity("setInterval")
-def csp_warning_timeout(traverser):
+def csp_warning_timeout():
def wrap(wrapper, arguments, traverser):
- if arguments and arguments[0]["type"] != "FunctionExpression":
+ if arguments and not arguments[0].callable:
from appvalidator.csp import warn
warn(err=traverser.err,
filename=traverser.filename,
line=traverser.line,
column=traverser.position,
context=traverser.context,
violation_type="set*")
- return False
return {"return": wrap}
@@ -62,18 +58,18 @@ def wrap(wrapper, arguments, traverser):
}
@register_entity("getUserMedia")
-def getUserMedia(traverser):
+def getUserMedia():
def method(wrapper, arguments, traverser):
if not arguments:
return False
- options = traverser.traverse_node(arguments[0])
+ options = arguments[0]
for feature in GUM_FEATURES:
- if (options.has_property(feature) and
+ if (options.has_var(feature) and
options.get(traverser, feature).get_literal_value() == True):
traverser.log_feature(GUM_FEATURES[feature])
- if (options.has_property("video") and
- options.get(traverser, "video").has_property("mandatory") and
+ if (options.has_var("video") and
+ options.get(traverser, "video").has_var("mandatory") and
options.get(traverser, "video").get(traverser, "mandatory") and
options.get(traverser, "video").get(traverser, "mandatory"
).get(traverser, "chromeMediaSource"
@@ -84,10 +80,10 @@ def method(wrapper, arguments, traverser):
@register_entity("XMLHttpRequest")
-def XMLHttpRequest(traverser):
+def XMLHttpRequest():
def return_(wrapper, arguments, traverser):
if (arguments and len(arguments) >= 3 and
- not traverser.traverse_node(arguments[2]).get_literal_value()):
+ not arguments[2].get_literal_value()):
traverser.err.warning(
err_id=("javascript", "xhr", "sync"),
warning="Synchronous XHR should not be used",
@@ -100,14 +96,13 @@ def return_(wrapper, arguments, traverser):
context=traverser.context)
return wrapper
- def new(traverser, node, elem):
+ def new(node, arguments, traverser):
if not node["arguments"]: # Ignore XHR without args
- return elem
+ return
arg = traverser.traverse_node(node["arguments"][0])
- if (arg.has_property("mozSystem") and
+ if (arg.has_var("mozSystem") and
arg.get(traverser, "mozSystem").get_literal_value()):
traverser.log_feature("SYSTEMXHR")
- return elem
return {
"value": {u"open": {"return": return_}},
View
19 appvalidator/testcases/javascript/instanceactions.py
@@ -23,12 +23,10 @@ def createElement(args, traverser, node, wrapper):
if not args:
return
- simple_args = map(traverser.traverse_node, args)
-
- first_as_str = utils.get_as_str(simple_args[0].get_literal_value())
+ first_as_str = utils.get_as_str(args[0].get_literal_value())
if first_as_str.lower() == u"script":
_create_script_tag(traverser)
- elif not simple_args[0].is_literal():
+ elif not args[0].is_literal():
_create_variable_element(traverser)
@@ -38,12 +36,10 @@ def createElementNS(args, traverser, node, wrapper):
if not args or len(args) < 2:
return
- simple_args = map(traverser.traverse_node, args)
-
- second_as_str = utils.get_as_str(simple_args[1].get_literal_value())
+ second_as_str = utils.get_as_str(args[1].get_literal_value())
if "script" in second_as_str.lower():
_create_script_tag(traverser)
- elif not simple_args[1].is_literal():
+ elif not args[1].is_literal():
_create_variable_element(traverser)
@@ -78,8 +74,7 @@ def insertAdjacentHTML(args, traverser, node, wrapper):
if not args or len(args) < 2:
return
- content = traverser.traverse_node(args[1])
- _set_HTML_property("insertAdjacentHTML", content, traverser)
+ _set_HTML_property("insertAdjacentHTML", args[1], traverser)
def setAttribute(args, traverser, node, wrapper):
@@ -88,9 +83,7 @@ def setAttribute(args, traverser, node, wrapper):
if not args:
return
- simple_args = [traverser.traverse_node(a) for a in args]
-
- first_as_str = utils.get_as_str(simple_args[0].get_literal_value())
+ first_as_str = utils.get_as_str(args[0].get_literal_value())
if first_as_str.lower().startswith("on"):
warn(traverser.err,
filename=traverser.filename,
View
6 appvalidator/testcases/javascript/instanceproperties.py
@@ -56,10 +56,8 @@ def _set_HTML_property(function, new_value, traverser):
def set_on_event(new_value, traverser):
"""Ensure that on* properties are not assigned string values."""
- is_literal = new_value.is_literal()
-
- if is_literal and isinstance(new_value.get_literal_value(),
- types.StringTypes):
+ if new_value.is_literal() and isinstance(
+ new_value.get_literal_value(), types.StringTypes):
warn(traverser.err,
filename=traverser.filename,
line=traverser.line,
View
394 appvalidator/testcases/javascript/jstypes.py
@@ -1,4 +1,11 @@
+import types
+
import instanceproperties
+import utils
+
+
+def fake(traverser, **kw):
+ return JSWrapper(JSObject(), traverser=traverser, **kw)
class JSObject(object):
@@ -7,15 +14,19 @@ class JSObject(object):
context to enable static analysis of `with` statements.
"""
- def __init__(self, data=None):
+ TYPEOF = "object"
+
+ def __init__(self, data=None, traverser=None):
+ self.const = False
+ self.traverser = traverser
self.type_ = "object" # For use when an object is pushed as a context.
- self.data = {u"prototype": lambda: JSPrototype()}
+ self.data = {}
if data:
self.data.update(data)
self.recursing = False
- def get(self, name, instantiate=False, traverser=None):
+ def get(self, traverser, name, instantiate=False):
"Returns the value associated with a property name"
name = unicode(name)
output = None
@@ -24,25 +35,21 @@ def get(self, name, instantiate=False, traverser=None):
output = self.data[name]
if callable(output):
output = output()
- elif instantiate:
- output = JSWrapper(JSObject(), dirty=True, traverser=traverser)
+ elif instantiate or name in ('constructor', 'prototype'):
+ output = fake(traverser)
self.set(name, output, traverser=traverser)
if traverser:
modifier = instanceproperties.get_operation("get", name)
if modifier:
modifier(traverser)
- dirty = False
if output is None:
- dirty = True
- output = JSObject()
+ return fake(traverser)
- if dirty or not isinstance(output, JSWrapper):
- output = JSWrapper(output, dirty=dirty, traverser=traverser)
return output
- def get_literal_value(self):
+ def get_literal_value(self, traverser=None):
return u"[object Object]"
def set(self, name, value, traverser=None):
@@ -53,9 +60,9 @@ def set(self, name, value, traverser=None):
if modified_value is not None:
value = modified_value
- self.data[name] = value
+ self.data[unicode(name)] = value
- def has_var(self, name):
+ def has_var(self, name, traverser=None):
return unicode(name) in self.data
def output(self):
@@ -80,24 +87,118 @@ def output(self):
# Pop from the recursion buster.
self.recursing = False
- return str(output_dict)
+ return unicode(output_dict)
+
+ def __str__(self):
+ return self.output()
+
+ def delete(self, member):
+ if member not in self.data:
+ return
+ del self.data[member]
+
+
+class JSGlobal(JSObject):
+
+ def __init__(self, global_data, traverser=None):
+ self.global_data = utils.evaluate_lambdas(traverser, global_data)
+ super(JSGlobal, self).__init__(traverser=traverser)
+
+ if "typeof" in self.global_data:
+ self.TYPEOF = self.global_data["typeof"]
+
+ def _get_contents(self, traverser):
+ if "value" not in self.global_data:
+ return None
+ directory = utils.evaluate_lambdas(
+ traverser, self.global_data["value"])
+ if directory and callable(self.global_data["value"]):
+ self.global_data["value"] = directory
+ return directory
+
+ def get(self, traverser, name, instantiate=False):
+ if name in self.data or instantiate:
+ traverser._debug("Global member found in set data: %s" % name)
+ return super(JSGlobal, self).get(
+ traverser, name, instantiate=instantiate)
+
+ directory = self._get_contents(traverser)
+ if directory and isinstance(directory, dict) and name in directory:
+ traverser._debug("GETTING (%s) FROM GLOBAL" % name)
+ value = utils.evaluate_lambdas(traverser, directory[name])
+ if "literal" in value:
+ lit = utils.evaluate_lambdas(traverser, value["literal"])
+ return JSWrapper(JSLiteral(lit), traverser=traverser)
+ return traverser._build_global(name=name, entity=value)
+
+ traverser._debug("JSObject fallback for member %s in %s" %
+ (name, directory))
+ return super(JSGlobal, self).get(traverser, name)
+
+ def set(self, name, value, traverser=None):
+ directory = self._get_contents(traverser)
+ if directory and isinstance(directory, dict) and name in directory:
+ traverser._debug("Setting global member %s" % name)
+ self.get(traverser, name).value._set_to(value, traverser=traverser)
+ return super(JSGlobal, self).set(name, value, traverser=traverser)
+
+ def _set_to(self, value, traverser=None):
+ "This is called when the value of this glboal node is set directly."
+
+ traverser._debug("Assigning direct global value")
+ self._get_contents(traverser) # We don't care about the output.
+ if self.global_data.get("readonly"):
+ traverser.err.notice(
+ err_id=("js", "global", "readonly"),
+ notice="Read-only JS global modified",
+ description=["A read-only JS global was modified by some "
+ "code. This may cause issues and usually "
+ "indicates other problems with the code.",
+ "Modified global: %s" %
+ self.global_data.get("name", "(unknown)")],
+ filename=traverser.filename,
+ line=traverser.line,
+ column=traverser.position,
+ context=traverser.context)
+
+ def has_var(self, name, traverser=None):
+ directory = self._get_contents(traverser)
+ if directory and name in directory:
+ return True
+ return super(JSGlobal, self).has_var(name, traverser=traverser)
+
+ def get_literal_value(self, traverser=None):
+ if "literal" in self.global_data:
+ lit = self.global_data["literal"]
+ return lit(traverser or self.traverser) if callable(lit) else lit
+
+ directory = self._get_contents(traverser)
+ if directory and not isinstance(directory, dict):
+ return directory.get_literal_value(traverser=traverser)
+
+ return super(JSGlobal, self).get_literal_value(traverser)
+
+ def output(self):
+ return "[global %s]{%s}%s" % (
+ getattr(self, "name", "Unnamed"),
+ ",".join("%s:%s" % (repr(k), repr(v)) for
+ k, v in self.global_data.items()),
+ super(JSGlobal, self).output())
class JSContext(JSObject):
"""A variable context"""
- def __init__(self, context_type):
- super(JSContext, self).__init__()
+ def __init__(self, context_type, traverser=None):
+ super(JSContext, self).__init__(traverser=traverser)
self.type_ = context_type
self.data = {}
class JSWrapper(object):
"""Wraps a JS value and handles contextual functions for it."""
- def __init__(self, value=None, dirty=False, lazy=False,
- is_global=False, traverser=None, callable_=False,
- setter=None, context="chrome"):
+ def __init__(self, value=None, traverser=None, callable_=False):
if traverser is not None:
traverser.debug_level += 1
@@ -109,23 +210,14 @@ def __init__(self, value=None, dirty=False, lazy=False,
self.const = False
self.traverser = traverser
self.value = None # Instantiate the placeholder value
- self.is_global = False # Not yet...
- self.dirty = False # Also not yet...
- self.context = context
- # Used for predetermining set operations
- self.setter = setter
-
- if value is not None and not is_global:
- self.set_value(value, overwrite_const=True)
-
- if not self.is_global:
- self.is_global = is_global # Globals are built seperately
-
- self.dirty = dirty or self.dirty
- self.lazy = lazy
+ self.set_value(value, overwrite_const=True)
self.callable = callable_
+ @property
+ def is_global(self):
+ return isinstance(self.value, JSGlobal)
+
def set_value(self, value, traverser=None, overwrite_const=False):
"""Assigns a value to the wrapper"""
@@ -135,8 +227,7 @@ def set_value(self, value, traverser=None, overwrite_const=False):
if self.const and not overwrite_const:
traverser.err.warning(
- err_id=("testcases_javascript_traverser", "JSWrapper_set_value",
- "const_overwrite"),
+ err_id=("js", "JSWrapper_set_value", "const_overwrite"),
warning="Overwritten constant value",
description="A variable declared as constant has been "
"overwritten in some JS code.",
@@ -145,198 +236,85 @@ def set_value(self, value, traverser=None, overwrite_const=False):
column=traverser.position,
context=traverser.context)
- # 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:
return self
if isinstance(value, (bool, str, int, float, long, unicode)):
- self.inspect_literal(value)
value = JSLiteral(value)
# If the value being assigned is a wrapper as well, copy it in
elif isinstance(value, JSWrapper):
self.value = value.value
- self.lazy = value.lazy
- self.dirty = value.dirty
- self.is_global = value.is_global
- self.context = value.context
- # const does not carry over on reassignment
+ # `const` does not carry over on reassignment.
return self
elif callable(value):
- value = value(traverser)
-
- if not isinstance(value, dict):
- self.is_global = False
- elif "context" in value:
- self.context = value["context"]
+ value = utils.evaluate_lambdas(traverser, value)
self.value = value
return self
- def has_property(self, prop):
+ def has_var(self, prop):
"""Returns a boolean value representing the presence of a property"""
return (getattr(self.value, "has_var") and
self.value.has_var(prop))
def get(self, traverser, name, instantiate=False):
"""Retrieve a property from the variable."""
- value = self.value
- dirty = value is None
- context = self.context
- if self.is_global:
- if "value" not in value:
- output = JSWrapper(JSObject(), traverser=traverser)
- output.value = {}
-
- def apply_value(name):
- if name in self.value:
- output.value[name] = self.value[name]
-
- map(apply_value, ("readonly", "context", "name"))
- output.is_global = True
- output.context = self.context
- return output
-
- def _evaluate_lambdas(node):
- if callable(node):
- return _evaluate_lambdas(node(t=traverser))
- else:
- return node
-
- value_val = value["value"]
- value_val = _evaluate_lambdas(value_val)
-
- if isinstance(value_val, dict):
- if name in value_val:
- value_val = _evaluate_lambdas(value_val[name])
- output = traverser._build_global(name=name,
- entity=value_val)
- if "context" not in value_val:
- output.context = self.context
- return output
- else:
- value = value_val
-
# Process any getters that are present for the current property.
modifier = instanceproperties.get_operation("get", name)
if modifier:
modifier(traverser)
- if value is not None and issubclass(type(value), JSObject):
- output = value.get(name, instantiate=instantiate,
- traverser=traverser)
+ if self.value is not None:
+ output = self.value.get(traverser, name, instantiate=instantiate)
else:
output = None
- if not isinstance(output, JSWrapper):
- output = JSWrapper(
- output, traverser=traverser, dirty=output is None or dirty)
-
- output.context = context
-
- # If we can predetermine the setter for the wrapper, we can save a ton
- # of lookbehinds in the future. This greatly simplifies the
- # MemberExpression support.
- setter = instanceproperties.get_operation("set", name)
- if setter:
- output.setter = setter
return output
def del_value(self, member):
"""The member `member` will be deleted from the value of the wrapper"""
- if self.is_global:
- self.traverser.err.warning(
- err_id=("testcases_js_jstypes", "del_value",
- "global_member_deletion"),
- warning="Global member deletion",
- description="Members of global objects cannot be deleted.",
- filename=self.traverser.filename,
- line=self.traverser.line,
- column=self.traverser.position,
- context=self.traverser.context)
- elif isinstance(self.value, (JSObject, JSPrototype)):
- if member not in self.value.data:
- return
- del self.value.data[member]
-
- def contains(self, value):
- """Serves 'in' for BinaryOperators for lists and dictionaries"""
-
- # Unwrap the rvalue.
- if isinstance(value, JSWrapper):
- value = value.get_literal_value()
-
- if isinstance(self.value, JSArray):
- from actions import _get_as_num
- index = int(_get_as_num(value))
- if len(self.value.elements) > index >= 0:
- return True
-
- if isinstance(self.value, (JSArray, JSObject, JSPrototype)):
- return self.value.has_var(value)
-
- # Nothing else supports "in"
- return False
+ self.value.delete(member)
+
+ def has_var(self, value, traverser=None):
+ return self.value.has_var(value, traverser=traverser or self.traverser)
def is_literal(self):
"""Returns whether the content is a literal"""
return isinstance(self.value, JSLiteral)
- def get_literal_value(self):
+ def get_literal_value(self, traverser=None):
"""Returns the literal value of the wrapper"""
-
- if self.is_global:
- if "literal" in self.value:
- lit = self.value["literal"]
- return lit(self.traverser) if callable(lit) else lit
- else:
- return "[object Object]"
if self.value is None:
return None
- output = self.value.get_literal_value()
- return output
+ return self.value.get_literal_value(self.traverser)
def output(self):
- """Returns a readable version of the object"""
- if self.value is None:
- return "(None)"
- elif self.is_global:
- return "(Global)"
-
- return self.value.output()
-
- def inspect_literal(self, value):
- """
- Inspect the value of a literal to see whether it contains a flagged
- value.
- """
- return # This is currently a noop.
-
- # Don't do any processing if we can't return an error.
- if not self.traverser:
- return
-
- self.traverser._debug("INSPECTING: %s" % value)
-
- # This is a no-op for now.
- pass
+ if self.value is self:
+ return "(recursing)"
+ return self.value.output() if self.value else "(None)"
def __str__(self):
- """Returns a textual version of the object."""
- return unicode(self.get_literal_value())
+ return unicode(self.value)
+
+LITERAL_TYPEOF = {
+ int: "number",
+ float: "number",
+ long: "number",
+ str: "string",
+ unicode: "string",
+ bool: "boolean",
+}
class JSLiteral(JSObject):
"""Represents a literal JavaScript value."""
- def __init__(self, value=None):
- super(JSLiteral, self).__init__()
+ def __init__(self, value=None, traverser=None):
+ super(JSLiteral, self).__init__(traverser=traverser)
self.value = value
+ self.TYPEOF = LITERAL_TYPEOF.get(type(value), self.TYPEOF)
def set_value(self, value):
self.value = value
@@ -349,51 +327,48 @@ def __str__(self):
def output(self):
return self.value
- def get_literal_value(self):
+ def get_literal_value(self, traverser=None):
"Returns the literal value of a this literal. Heh."
return self.value
+ def has_var(self, name, traverser=None):
+ return False
-class JSPrototype(JSObject):
- """
- A lazy JavaScript object that is assumed not to contain any default
- methods.
- """
-
- def __init__(self):
- super(JSPrototype, self).__init__()
- self.data = {}
-
- def get(self, name, instantiate=False, traverser=None):
- name = unicode(name)
- output = super(JSPrototype, self).get(name, instantiate, traverser)
- if output is not None:
- return output
- if name == "prototype":
- prototype = JSWrapper(JSPrototype(), traverser=traverser)
- self.data[name] = prototype
-
- return output
+ def delete(self, member):
+ pass
class JSArray(JSObject):
"""A class that represents both a JS Array and a JS list."""
- def __init__(self, elements=None):
- super(JSArray, self).__init__()
+ def __init__(self, elements=None, traverser=None):
+ super(JSArray, self).__init__(traverser=traverser)
self.elements = elements or []
- def get(self, index, instantiate=False, traverser=None):
+ def get(self, traverser, index, instantiate=False):
if index == "length":
return len(self.elements)
# Courtesy of Ian Bicking: http://bit.ly/hxv6qt
try:
return self.elements[int(index.strip().split()[0])]
except (ValueError, IndexError, KeyError):
- return super(JSArray, self).get(index, instantiate, traverser)
+ return super(JSArray, self).get(traverser, index, instantiate)
+
+ def has_var(self, name, traverser=None):
+ print name, type(name)
+ index = None
+ if isinstance(name, types.StringTypes) and name.isdigit():
+ index = utils.get_as_num(name, traverser)
+ elif isinstance(name, int):
+ index = name
+
+ if index is not None and len(self.elements) > index >= 0:
+ return True
- def get_literal_value(self):
+ return super(JSArray, self).has_var(name, traverser=traverser)
+
+ def get_literal_value(self, traverser=None):
"""Arrays return a comma-delimited version of themselves."""
if self.recursing:
@@ -406,9 +381,10 @@ def get_literal_value(self):
# y = x * 3 // y = 12 since x equals "4"
output = u",".join(
- [unicode(w.get_literal_value() if w is not None else u"") for w in
- self.elements if
- not (isinstance(w, JSWrapper) and w.value == self)])
+ unicode(w.get_literal_value(traverser=self.traverser) if
+ w is not None else u"") for
+ w in self.elements if
+ not (isinstance(w, JSWrapper) and w.value == self))
self.recursing = False
return output
@@ -434,5 +410,15 @@ def set(self, index, value, traverser=None):
self.elements.append(None)
self.elements.append(JSWrapper(value=value, traverser=traverser))
+ def delete(self, member):
+ if member.isdigit() and self.has_var(member):
+ index = int(member)
+ if index == len(self.elements) - 1:
+ self.elements.pop()
+ else:
+ self.elements[member] = None
+ else:
+ super(JSArray, self).delete(member)
+
def output(self):
return u"[%s]" % self.get_literal_value()
View
134 appvalidator/testcases/javascript/nodedefinitions.py
@@ -5,7 +5,8 @@
import instanceactions
import utils
-from jstypes import JSArray, JSContext, JSLiteral, JSObject, JSWrapper
+from jstypes import (JSArray, JSContext, JSGlobal, JSLiteral, JSObject,
+ JSWrapper)
NUMERIC_TYPES = (int, long, float, complex)
@@ -60,7 +61,7 @@ def wrap(traverser, node):
local_context = traverser._peek_context(1)
for param in params:
- var = JSWrapper(lazy=True, traverser=traverser)
+ var = JSWrapper(JSObject(), traverser=traverser)
# We can assume that the params are static because we don't care
# about what calls the function. We want to know whether the
@@ -84,7 +85,7 @@ def wrap(traverser, node):
# Put the function off for traversal at the end of the current block scope.
traverser.function_collection[-1].append(lambda: wrap(traverser, node))
- return JSWrapper(traverser=traverser, callable_=True, dirty=True)
+ return JSWrapper(JSObject(), traverser=traverser, callable_=True)
def FunctionDeclaration(traverser, node):
@@ -121,7 +122,7 @@ def VariableDeclaration(traverser, node):
# Simple instantiation; no initialization
for var in filter(None, vars):
traverser._declare_variable(
- var, JSWrapper(None, traverser=traverser))
+ var, JSWrapper(JSObject(), traverser=traverser))
# The variables are declared inline
elif declaration["init"]["type"] == "ArrayPattern":
@@ -180,10 +181,9 @@ def _proc_objpattern(init_obj, properties):
def ThisExpression(traverser, node):
"Returns the `this` object"
-
if not traverser.this_stack:
from predefinedentities import global_identity
- return traverser._build_global("window", global_identity())
+ return traverser._build_global("window", global_identity)
return traverser.this_stack[-1]
@@ -192,38 +192,31 @@ def ArrayExpression(traverser, node):
def ObjectExpression(traverser, node):
- var = JSObject()
+ var = JSObject(traverser=traverser)
for prop in node["properties"]:
key = prop["key"]
var.set(key["value" if key["type"] == "Literal" else "name"],
traverser.traverse_node(prop["value"]), traverser)
-
# TODO: Observe "kind"
- return JSWrapper(var, lazy=True, traverser=traverser)
+ return var
def _expr_unary_typeof(wrapper):
"""Evaluate the "typeof" value for a JSWrapper object."""
- if (wrapper.callable or
- (wrapper.is_global and "return" in wrapper.value and
- "value" not in wrapper.value)):
+ if wrapper.callable:
return "function"
-
- # import pdb; pdb.set_trace()
- if wrapper.is_global and "undefined" in wrapper.value:
+ elif wrapper.is_global:
+ if "typeof" in wrapper.value.global_data:
+ return wrapper.value.global_data["typeof"]
+ if ("return" in wrapper.value.global_data and
+ "value" not in wrapper.value.global_data):
+ return "function"
+
+ if wrapper.is_global and "undefined" in wrapper.value.global_data:
return "undefined"
- value = wrapper.value
- if isinstance(value, JSLiteral):
- value = value.value
- if isinstance(value, bool):
- return "boolean"
- elif isinstance(value, (int, long, float)):
- return "number"
- elif isinstance(value, types.StringTypes):
- return "string"
- return "object"
+ return wrapper.value.TYPEOF
UNARY_OPERATORS = {
@@ -232,7 +225,6 @@ def _expr_unary_typeof(wrapper):
"!": lambda e: not e.get_literal_value(),
"~": lambda e: -1 * (utils.get_as_num(e.get_literal_value()) + 1),
"typeof": _expr_unary_typeof,
- "delete": lambda e: None,
}
def UnaryExpression(traverser, node):
@@ -333,7 +325,8 @@ def BinaryExpression(traverser, node):
output = BINARY_OPERATORS[operator](left, right, gleft, gright)
elif operator == "in":
- output = right_wrap.contains(left)
+ output = right_wrap.has_var(left, traverser=traverser)
+ #TODO: `delete` operator
# Cap the length of analyzed strings.
if isinstance(output, types.StringTypes) and len(output) > MAX_STR_SIZE:
@@ -387,7 +380,8 @@ def AssignmentExpression(traverser, node):
readonly_value = global_dict.get("readonly", False)
traverser._declare_variable(node_left["name"], right, type_="glob")
-
+
+ # TODO: WTF does this even do?
elif node_left["type"] == "MemberExpression":
member_object = MemberExpression(traverser, node_left["object"],
instantiate=True)
@@ -397,27 +391,10 @@ def AssignmentExpression(traverser, node):
if member_object.value is None:
member_object.value = JSObject()
- # Don't do the assignment if we're facing a global.
- elif (not member_object.is_global and
- member_object.value.get("overwritable")):
-
- if not member_object.is_global:
- member_object.value.set(member_property, right, traverser)
-
- elif "value" in member_object.value:
- member_object_value = traverser.expand_globals(
- member_object).value
- if member_property in member_object_value["value"]:
-
- # If it's a global and the actual member exists, test
- # whether it can be safely overwritten.
- member = member_object_value["value"][member_property]
- if callable(member.get("value")):
- member = member["value"](t=traverser)
- readonly_value = member.get("readonly", True)
+ member_object.value.set(member_property, right, traverser)
if callable(readonly_value):
- readonly_value(t=traverser, r=right, rn=node["right"])
+ readonly_value(traverser, right, node["right"])
return right
@@ -482,31 +459,32 @@ def AssignmentExpression(traverser, node):
def NewExpression(traverser, node):
- # We don't actually process the arguments as part of the flow because of
- # the Angry T-Rex effect. For now, we just traverse them to ensure they
- # don't contain anything dangerous.
- args = node["arguments"]
- if isinstance(args, list):
- for arg in args:
- traverser.traverse_node(arg)
- else:
- traverser.traverse_node(args)
-
+ args = [traverser.traverse_node(arg) for arg in node["arguments"]]
elem = traverser.traverse_node(node["callee"])
if elem.is_global:
traverser._debug("Making overwritable")
-
- elem.value = deepcopy(elem.value)
- elem.value["overwritable"] = True
- elem.value["readonly"] = False
- if "new" in elem.value:
- elem = elem.value["new"](traverser, node, elem)
+ global_data = dict(elem.value.global_data)
+ global_data.update(overwritable=True, readonly=False)
+ temp = JSGlobal(global_data, traverser=traverser)
+ temp.data = deepcopy(elem.value.data) if elem.value.data else {}
+ if "new" in temp.global_data:
+ new_temp = temp.global_data["new"](node, args, traverser)
+ if new_temp is not None:
+ # typeof new Boolean() === "object"
+ traverser._debug("Stripping global typeof")
+ new_temp.value.TYPEOF = "object"
+ return new_temp
+ elif "return" in temp.global_data:
+ new_temp = temp.global_data["return"](
+ wrapper=node, arguments=args, traverser=traverser)
+ if new_temp is not None:
+ return new_temp
+ elem.value = temp
return elem
def CallExpression(traverser, node):
- args = node["arguments"]
- map(traverser.traverse_node, args)
+ args = [traverser.traverse_node(a) for a in node["arguments"]]
member = traverser.traverse_node(node["callee"])
@@ -518,14 +496,18 @@ def CallExpression(traverser, node):
# for additional conditions.
identifier_name = node["callee"]["property"]["name"]
if identifier_name in instanceactions.INSTANCE_DEFINITIONS:
+ traverser._debug('Calling instance action...')
result = instanceactions.INSTANCE_DEFINITIONS[identifier_name](
args, traverser, node, wrapper=member)
return result
- if member.is_global and "return" in member.value:
- return member.value["return"](wrapper=member, arguments=args,
- traverser=traverser)
- return JSWrapper(JSObject(), dirty=True, traverser=traverser)
+ if member.is_global and "return" in member.value.global_data:
+ traverser._debug("EVALUATING RETURN...")
+ output = member.value.global_data["return"](
+ wrapper=member, arguments=args, traverser=traverser)
+ if output is not None:
+ return output
+ return JSObject(traverser=traverser)
def _get_member_exp_property(traverser, node):
@@ -546,13 +528,10 @@ def MemberExpression(traverser, node, instantiate=False):
# x.y or x[y]
# x = base
base = MemberExpression(traverser, node["object"], instantiate)
- base = traverser.expand_globals(base)
-
identifier = _get_member_exp_property(traverser, node)
traverser._debug("MEMBER_EXP>>PROPERTY (%s)" % identifier)
- return base.get(
- traverser=traverser, instantiate=instantiate, name=identifier)
+ return base.get(traverser, identifier, instantiate=instantiate)
elif node["type"] == "Identifier":
traverser._debug("MEMBER_EXP>>ROOT:IDENTIFIER (%s)" % node["name"])
@@ -565,7 +544,7 @@ def MemberExpression(traverser, node, instantiate=False):
else:
output = traverser._seek_variable(node["name"])
- return traverser.expand_globals(output)
+ return output
else:
traverser._debug("MEMBER_EXP>>ROOT:EXPRESSION")
# It's an expression, so just try your damndest.
@@ -579,8 +558,8 @@ def Literal(traverser, node):
"""
value = node["value"]
if isinstance(value, dict):
- value = JSObject()
- return JSWrapper(value, traverser=traverser)
+ return JSObject(traverser=traverser)
+ return JSLiteral(value, traverser=traverser)
def Identifier(traverser, node):
@@ -590,7 +569,7 @@ def Identifier(traverser, node):
if traverser._is_defined(name):
return traverser._seek_variable(name)
- return JSWrapper(JSObject(), traverser=traverser, dirty=True)
+ return JSObject(traverser=traverser)
#(branches,
@@ -635,6 +614,7 @@ def node(branches=None, dynamic=False, action=None, returns=False,
"ForStatement": node(branches=("init", "test", "update", "body"),
is_block=True),
"ForInStatement": node(branches=("left", "right", "body"), is_block=True),
+ "ForOfStatement": node(branches=("left", "right", "body"), is_block=True),
"FunctionDeclaration": node(branches=("body", ), dynamic=True,
action=FunctionDeclaration, is_block=True),
View
65 appvalidator/testcases/javascript/predefinedentities.py
@@ -3,17 +3,16 @@
import call_definitions
from call_definitions import python_wrap
from entity_values import entity
-from jstypes import JSWrapper
+from jstypes import JSGlobal, JSWrapper
# See https://github.com/mattbasta/amo-validator/wiki/JS-Predefined-Entities
# for details on entity properties.
def get_wrapped_global(traverser, *args):
- var = JSWrapper(get_global(*args), traverser=traverser)
- var.is_global = True
- return var
+ var = JSGlobal(get_global(*args), traverser=traverser)
+ return JSWrapper(var, traverser=traverser)
def get_global(*args):
def wrap(t):
@@ -26,13 +25,11 @@ def wrap(t):
return element
return wrap
-
-get_constant = lambda val: lambda t: JSWrapper(val, traverser=t)
+get_constant = lambda v: lambda t: JSWrapper(v, traverser=t)
get_constant_method = lambda val: lambda **kw: JSWrapper(
val, traverser=kw['traverser'])
-global_identity = lambda *args: {"value": GLOBAL_ENTITIES}
+global_identity = {"value": lambda *args: GLOBAL_ENTITIES}
-MUTABLE = {"overwriteable": True, "readonly": False}
READONLY = {"readonly": True}
@@ -42,11 +39,10 @@ def wrap(t):
t._debug("Found feature: %s" % constant)
if fallback:
t._debug("Feature has fallback: %s" % repr(fallback))
- return {"value": fallback} if fallback else {}
+ return lambda *a: fallback if fallback else {}
return {'value': wrap,
- 'return': lambda traverser, *a, **kw:
- traverser.log_feature(constant)}
+ 'return': lambda **kw: kw['traverser'].log_feature(constant)}
MOZAPPS = {
@@ -100,13 +96,12 @@ def wrap(t):
# GLOBAL_ENTITIES is also representative of the `window` object.
GLOBAL_ENTITIES = {
- u"window": {"value": global_identity},
- u"null": {"literal": get_constant(None)},
+ u"window": global_identity,
+ u"null": {"literal": None},
u"document":
{"value":
- {u"title": MUTABLE,
- u"defaultView": {"value": global_identity},
+ {u"defaultView": {"value": global_identity},
u"cancelFullScreen": feature("FULLSCREEN"),
u"mozCancelFullScreen": feature("FULLSCREEN"),
@@ -136,27 +131,33 @@ def wrap(t):
u"eval": entity("eval"),
u"Function": entity("Function"),
u"Object":
- {"value":
- {"constructor": {"value": get_global("Function")}}},
+ {"value": {u"constructor": {"value": get_global("Function")}}},
u"String":
{"value":
{u"constructor": {"value": get_global("Function")}},
- "return": call_definitions.string_global},
+ "return": call_definitions.string_global,
+ "new": call_definitions.string_global,
+ "typeof": "string"},
u"Array":
{"value":
{u"constructor": {"value": get_global("Function")}},
- "return": call_definitions.array_global},
+ "return": call_definitions.array_global,
+ "new": call_definitions.array_global},
u"Number":
{"value":
{u"constructor": {"value": get_global("Function")},
u"POSITIVE_INFINITY": {"value": get_constant(float('inf'))},
u"NEGATIVE_INFINITY": {"value": get_constant(float('-inf'))},
u"isNaN": get_constant("isNaN")},
- "return": call_definitions.number_global},
+ "return": call_definitions.number_global,
+ "new": call_definitions.number_global,
+ "typeof": "number"},
u"Boolean":
{"value":
{u"constructor": {"value": get_global("Function")}},
- "return": call_definitions.boolean_global},
+ "return": call_definitions.boolean_global,
+ "new": call_definitions.boolean_global,
+ "typeof": "boolean"},
u"RegExp":
{"value":
{u"constructor": {"value": get_global("Function")}}},
@@ -169,14 +170,14 @@ def wrap(t):
u"Math":
{"value":
- {u"PI": {"value": get_constant(math.pi)},
- u"E": {"value": get_constant(math.e)},
- u"LN2": {"value": get_constant(math.log(2))},
- u"LN10": {"value": get_constant(math.log(10))},
- u"LOG2E": {"value": get_constant(math.log(math.e, 2))},
- u"LOG10E": {"value": get_constant(math.log10(math.e))},
- u"SQRT2": {"value": get_constant(math.sqrt(2))},
- u"SQRT1_2": {"value": get_constant(math.sqrt(1/2))},
+ {u"PI": {"literal": math.pi},
+ u"E": {"literal": math.e},
+ u"LN2": {"literal": math.log(2)},
+ u"LN10": {"literal": math.log(10)},
+ u"LOG2E": {"literal": math.log(math.e, 2)},
+ u"LOG10E": {"literal": math.log10(math.e)},
+ u"SQRT2": {"literal": math.sqrt(2)},
+ u"SQRT1_2": {"literal": math.sqrt(1/2)},
u"abs": {"return": python_wrap(abs, [("num", 0)])},
u"acos": {"return": python_wrap(math.acos, [("num", 0)])},
u"asin": {"return": python_wrap(math.asin, [("num", 0)])},
@@ -204,14 +205,10 @@ def wrap(t):
u"XMLHttpRequest": entity('XMLHttpRequest'),
# Global properties are inherently read-only, though this formalizes it.
- u"Infinity": {"value": get_global("Number", "POSITIVE_INFINITY")},
+ u"Infinity": get_global("Number", "POSITIVE_INFINITY"),
u"NaN": READONLY,
u"undefined": {"readonly": True, "undefined": True, "literal": None},
- u"innerHeight": MUTABLE,
- u"innerWidth": MUTABLE,
- u"width": MUTABLE,
- u"height": MUTABLE,
u"opener": {"value": global_identity},
u"navigator": {"value": NAVIGATOR},
View
57 appvalidator/testcases/javascript/traverser.py
@@ -78,7 +78,7 @@ def traverse_node(self, node):
"Finds a node's internal blocks and helps manage state."
if node is None:
- return JSWrapper(JSObject(), traverser=self, dirty=True)
+ return JSWrapper(JSObject(), traverser=self)
# Simple caching to prevent retraversal
if "__traversal" in node and node["__traversal"] is not None:
@@ -87,7 +87,7 @@ def traverse_node(self, node):
if isinstance(node, types.StringTypes):
return JSWrapper(JSLiteral(node), traverser=self)
elif "type" not in node or node["type"] not in DEFINITIONS:
- return JSWrapper(JSObject(), traverser=self, dirty=True)
+ return JSWrapper(JSObject(), traverser=self)
self._debug("TRAVERSE>>%s" % node["type"])
self.debug_level += 1
@@ -157,7 +157,7 @@ def traverse_node(self, node):
return action_result
node["__traversal"] = None
- return JSWrapper(JSObject(), traverser=self, dirty=True)
+ return JSWrapper(JSObject(), traverser=self)
def _push_block_context(self):
"Adds a block context to the current interpretation frame"
@@ -187,9 +187,10 @@ def _pop_context(self):
self._debug(popped_context)
def _peek_context(self, depth=1):
- """Returns the most recent context. Note that this should NOT be used
- for variable lookups."""
-
+ """
+ Returns the most recent context. Note that this should NOT be used
+ for variable lookups.
+ """
return self.contexts[len(self.contexts) - depth]
def _seek_variable(self, variable):
@@ -217,73 +218,51 @@ def _is_defined(self, variable):
def _is_local_variable(self, variable):
"""Return whether a variable is defined in the current scope."""
- return any(ctx.has_var(variable) for ctx in self.contexts)
+ return any(ctx.has_var(variable, traverser=self) for
+ ctx in self.contexts)
def _seek_local_variable(self, variable):
# Loop through each context in reverse order looking for the defined
# variable.
for context in reversed(self.contexts):
# If it has the variable, return it
- if context.has_var(variable):
+ if context.has_var(variable, traverser=self):
self._debug("SEEK>>FOUND")
- return context.get(variable, traverser=self)
+ return context.get(self, variable)
def _is_global(self, name):
"Returns whether a name is a global entity"
return not self._is_local_variable(name) and name in GLOBAL_ENTITIES
def _build_global(self, name, entity):
- "Builds an object based on an entity from the predefined entity list"
- entity.setdefault("name", name)
+ if isinstance(entity, dict):
+ entity.setdefault("name", name)
# Build out the wrapper object from the global definition.
- result = JSWrapper(is_global=True, traverser=self, lazy=True)
- result.value = entity
- result = self.expand_globals(result)
-
- if "context" in entity:
- result.context = entity["context"]
-
- self._debug("BUILT_GLOBAL")
-
+ result = JSWrapper(JSGlobal(entity, traverser=self), traverser=self)
return result
def _declare_variable(self, name, value, type_="var"):
context = self.contexts[0]
if type_ == "let":
context = self.contexts[-1]
elif type_ in ("var", "const", ):
- contexts = ([self.contexts[0]] +
- filter(lambda c: c.type_ == "default",
- self.contexts[1:]))
+ contexts = (
+ [self.contexts[0]] + filter(lambda c: c.type_ == "default",
+ self.contexts[1:]))
context = contexts[-1]
elif type_ == "glob":
# Look down through the lexical scope. If the variable being
# assigned is present in one of those objects, use that as the
# target context.
for ctx in reversed(self.contexts[1:]):
- if ctx.has_var(name):
+ if ctx.has_var(name, traverser=self):
context = ctx
break
context.set(name, value)
return value
- def expand_globals(self, node):
- if node.is_global and callable(node.value.get("value")):
- result = node.value["value"](self)
-
- if isinstance(result, dict):
- output = self._build_global("--", result)
- elif isinstance(result, JSWrapper):
- output = result
- else:
- output = JSWrapper(result, self)
-
- return output
-
- return node
-
def log_feature(self, feature):
self.err.feature_profile.add(feature)
self.err.feature_usage[feature].append({
View
7 appvalidator/testcases/javascript/utils.py
@@ -60,3 +60,10 @@ def get_NaN(traverser):
# Cache it so we don't need to do this again.
traverser.NAN_CACHE = ncache
return ncache
+
+
+def evaluate_lambdas(traverser, node):
+ if callable(node):
+ return evaluate_lambdas(traverser, node(traverser))
+ else:
+ return node
View
12 jsmain.py
@@ -36,9 +36,6 @@ def do_inspect(wrapper, arguments, traverser):
a = traverser.traverse_node(arg)
print a.output()
-
- if a.is_global:
- print a.value
print "Context: %s" % a.context
print "<"
print "~" * 50
@@ -59,11 +56,9 @@ def do_exit(wrapper, arguments, traverser):
elif line == "disable bootstrap\n":
err.save_resource("em:bootstrap", False)
continue
- elif line.startswith(("inspect ", "isglobal ")):
- actions = {"inspect": lambda wrap: wrap.value if
- wrap.is_global else
- wrap.output(),
- "isglobal": lambda wrap: wrap.is_global}
+ elif line.startswith(("inspect ", "type ")):
+ actions = {"inspect": lambda wrap: wrap.output(),
+ "type": lambda wrap: type(wrap.value)}
vars = line.split()
final_context = trav.contexts[-1]
for var in vars[1:]:
@@ -82,4 +77,3 @@ def do_exit(wrapper, arguments, traverser):
output = trav.traverse_node(branch)
if output is not None:
print output.output()
-
View
5 tests/js/js_helper.py
@@ -79,7 +79,7 @@ def get_var(self, name):
raise ("Test seeking variable (%s) not found in final context." %
name)
- def assert_var_eq(self, name, value):
+ def assert_var_eq(self, name, value, explanation=None):
"""
Assert that the value of a variable from the final script context
contains the value specified.
@@ -90,4 +90,5 @@ def assert_var_eq(self, name, value):
val = round(val)
val /= 100000
- eq_(val, value)
+ eq_(val, value,
+ explanation or "%s doesn't equal %s" % (val, value))
View
9 tests/js/test_csp.py
@@ -82,6 +82,11 @@ def test_setInterval_pass(self):
self.run_script("var x = setInterval(function() {}, 0);")
self.assert_silent()
+ def test_timeouts_less_noisy(self):
+ self.run_script("var f = function() {};x = setInterval(f, 0);")
+ self.run_script("var f = function() {};x = setTimeout(f, 0);")
+ self.assert_silent()
+
class TestCreateElement(JSTestCase):
@@ -98,6 +103,10 @@ def test_createElement_pass(self):
self.run_script("var x = document.createElement('b');")
self.assert_silent()
+ def test_createElement_var_pass(self):
+ self.run_script("var a = 'asdf', x = document.createElement(a);")
+ self.assert_silent()
+
def test_createElement(self):
self.run_script("var x = document.createElement('script');")
self.assert_failed(with_warnings=True)
View
2 tests/js/test_features.py
@@ -42,7 +42,7 @@ class TestWindowFeatures(FeatureTester):
("WEBAUDIO", "var x = new mozAudioContext();"),
("QUOTA", "var x = new mozPersistentStorage();"),
("QUOTA", "var x = new StorageInfo();"),
- ("WEBRTC_MEDIA", "var x = MediaStream;"),
+ ("WEBRTC_MEDIA", "var x = new MediaStream();"),
("WEBRTC_DATA", "var x = new DataChannel();"),
("WEBRTC_PEER", "var x = new RTCPeerConnection();"),
("SPEECH_SYN", "var x = speechSynthesis.foo();"),
View
9 tests/js/test_jstypes.py
@@ -83,7 +83,8 @@ def test_jsobject_set_get(self):
JSWrappers.
"""
jso = jstypes.JSObject()
- jso.set('foo', 123)
- assert isinstance(jso.get('foo'), jstypes.JSWrapper)
- assert isinstance(jso.get('prototype'), jstypes.JSWrapper)
- assert isinstance(jso.get('prototype').value, jstypes.JSPrototype)
+ jso.set('foo', jstypes.JSWrapper(jstypes.JSLiteral(123)))
+ assert isinstance(jso.get(None, 'foo'), jstypes.JSWrapper)
+ assert isinstance(jso.get(None, 'foo').value, jstypes.JSLiteral)
+ assert isinstance(jso.get(None, 'prototype'), jstypes.JSWrapper)
+ assert isinstance(jso.get(None, 'prototype').value, jstypes.JSObject)
View
3 tests/js/test_math.py
@@ -17,7 +17,8 @@ def wrap(params, output):
def do_expr(self, expr, output):
self.setUp()
self.run_script("var x = %s" % expr)
- self.assert_var_eq("x", output)
+ self.assert_var_eq(
+ "x", output)
def test_abs(self):
"""Test that the abs() function works properly."""

0 comments on commit 45d1e62

Please sign in to comment.