def Literal(traverser, node): """ Convert a literal node in the parse tree to its corresponding interpreted value. """ value = node["value"] if isinstance(value, dict): return JSObject(traverser=traverser) return JSLiteral(value, traverser=traverser)
def _define_literal(traverser, node): """ Convert a literal node in the parse tree to its corresponding interpreted value. """ value = node['value'] if isinstance(value, dict): return JSWrapper(JSObject(), traverser=traverser, dirty=True) wrapper = JSWrapper(value if value is not None else JSLiteral(None), traverser=traverser) test_literal(traverser, wrapper) return wrapper
def UnaryExpression(traverser, node): operator = node["operator"] arg = traverser.traverse_node(node["argument"]) if (isinstance(arg, JSGlobal) and "literal" in arg.global_data and not (operator == "typeof" and "undefined" in arg.global_data)): arg = JSLiteral(utils.evaluate_lambdas(traverser, arg.global_data["literal"]), traverser=traverser) if operator in UNARY_OPERATORS: traverser._debug("Defined unary operator (%s)" % operator) return JSLiteral(UNARY_OPERATORS[node["operator"]](arg, traverser), traverser=traverser) elif operator == "void": traverser._debug("Void unary operator") from predefinedentities import resolve_entity return JSGlobal(resolve_entity(traverser, "undefined"), traverser=traverser) elif operator == "typeof": traverser._debug("Typeof unary operator") return JSLiteral(_expr_unary_typeof(arg)) else: traverser._debug("Undefined unary operator") return JSObject(traverser=traverser)
def _call_create_pref(a, t, e): """ Handler for pref() and user_pref() calls in defaults/preferences/*.js files to ensure that they don't touch preferences outside of the "extensions." branch. """ # We really need to clean up the arguments passed to these functions. traverser = t.im_self if not traverser.filename.startswith('defaults/preferences/') or not a: return instanceactions.set_preference( JSWrapper(JSLiteral(None), traverser=traverser), a, traverser) value = _get_as_str(t(a[0])) return test_preference(value)
}, u"log": { "return": call_definitions.math_log }, u"max": { "return": python_wrap(max, [("num", 0)], nargs=True) }, u"min": { "return": python_wrap(min, [("num", 0)], nargs=True) }, u"pow": { "return": python_wrap(math.pow, [("num", 0), ("num", 0)]) }, # Random always returns 0.5 in our fantasy land. u"random": { "return": lambda **kw: JSLiteral(0.5) }, u"round": { "return": call_definitions.math_round }, u"sin": { "return": python_wrap(math.sin, [("num", 0)]) }, u"sqrt": { "return": python_wrap(math.sqrt, [("num", 1)]) }, u"tan": { "return": python_wrap(math.tan, [("num", 0)]) }, }, },
u"acos": {"return": python_wrap(math.acos, [("num", 0)])}, u"asin": {"return": python_wrap(math.asin, [("num", 0)])}, u"atan": {"return": python_wrap(math.atan, [("num", 0)])}, u"atan2": {"return": python_wrap(math.atan2, [("num", 0), ("num", 1)])}, u"ceil": {"return": python_wrap(math.ceil, [("num", 0)])}, u"cos": {"return": python_wrap(math.cos, [("num", 0)])}, u"exp": {"return": python_wrap(math.exp, [("num", 0)])}, u"floor": {"return": python_wrap(math.floor, [("num", 0)])}, u"log": {"return": call_definitions.math_log}, u"max": {"return": python_wrap(max, [("num", 0)], nargs=True)}, u"min": {"return": python_wrap(min, [("num", 0)], nargs=True)}, u"pow": {"return": python_wrap(math.pow, [("num", 0), ("num", 0)])}, # Random always returns 0.5 in our fantasy land. u"random": {"return": lambda **kw: JSLiteral(0.5)}, u"round": {"return": call_definitions.math_round}, u"sin": {"return": python_wrap(math.sin, [("num", 0)])}, u"sqrt": {"return": python_wrap(math.sqrt, [("num", 1)])}, u"tan": {"return": python_wrap(math.tan, [("num", 0)])}, }, }, u"XMLHttpRequest": entity('XMLHttpRequest'), # Global properties are inherently read-only, though this formalizes it. u"Infinity": get_global("Number", "POSITIVE_INFINITY"), u"NaN": READONLY, u"undefined": {"readonly": True, "undefined": True, "literal": None}, u"opener": global_identity,
def AssignmentExpression(traverser, node): traverser._debug("ASSIGNMENT_EXPRESSION") traverser.debug_level += 1 traverser._debug("ASSIGNMENT>>PARSING RIGHT") right = traverser.traverse_node(node["right"]) traverser._debug("ASSIGNMENT>>PARSING LEFT") orig_left = left = traverser.traverse_node(node["left"]) operator = node["operator"] def set_lvalue(value): node_left = node["left"] traverser._debug("ASSIGNING:DIRECT(%s)" % node_left["type"]) global_overwrite = False readonly_value = True if node_left["type"] == "Identifier": # Identifiers just need the ID name and a value to push. # Raise a global overwrite issue if the identifier is global. global_overwrite = traverser._is_global(node_left["name"]) # Get the readonly attribute and store its value if is_global if global_overwrite: from predefinedentities import GLOBAL_ENTITIES global_dict = GLOBAL_ENTITIES[node_left["name"]] readonly_value = global_dict.get("readonly", False) traverser._declare_variable(node_left["name"], value, type_="glob") elif node_left["type"] == "MemberExpression": member_object = MemberExpression(traverser, node_left["object"], instantiate=True) member_property = _get_member_exp_property(traverser, node_left) traverser._debug("ASSIGNMENT:MEMBER_PROPERTY(%s)" % member_property) if member_object is None: member_object = JSObject() member_object.set(member_property, value, traverser) # Treat direct assignment different than augmented assignment. if operator == "=": set_lvalue(right) return right elif operator not in ASSIGNMENT_OPERATORS: # We don't support that operator. (yet?) traverser._debug("ASSIGNMENT>>OPERATOR NOT FOUND", 1) return left if left.const: traverser.err.warning( err_id=("js", "AssignmentExpression", "aug_const_overwrite"), warning="Overwritten constant value", description="A variable declared as constant has been " "overwritten in some JS code by an augmented " "assignment.", filename=traverser.filename, line=traverser.line, column=traverser.position, context=traverser.context) return JSObject(traverser=traverser) traverser._debug("ASSIGNMENT>>DONE PARSING LEFT") traverser.debug_level -= 1 # If we're modifying a non-numeric type with a numeric operator, return # NaN. if (operator in NUMERIC_OPERATORS and not isinstance( left.get_literal_value(traverser) or 0, NUMERIC_TYPES)): set_lvalue(utils.get_NaN(traverser)) return left gleft, gright = utils.get_as_num(left), utils.get_as_num(right) traverser._debug("ASSIGNMENT>>OPERATION:%s" % operator) if operator in ("<<=", ">>=", ">>>=") and gright < 0: # The user is doing weird bitshifting that will return 0 in JS but # not in Python. set_lvalue(JSLiteral(0, traverser=traverser)) return left elif (operator in ("<<=", ">>=", ">>>=", "|=", "^=", "&=") and (abs(gleft) == float('inf') or abs(gright) == float('inf'))): # Don't bother handling infinity for integer-converted operations. set_lvalue(utils.get_NaN(traverser)) return left if operator == '+=': lit_left = left.get_literal_value(traverser) lit_right = right.get_literal_value(traverser) # Don't perform an operation on None. Python freaks out. if lit_left is None: lit_left = 0 if lit_right is None: lit_right = 0 # If either side of the assignment operator is a string, both sides # need to be cast to strings first. if (isinstance(lit_left, types.StringTypes) or isinstance(lit_right, types.StringTypes)): left = utils.get_as_str(lit_left) right = utils.get_as_str(lit_right) else: left, right = lit_left, lit_right output = ASSIGNMENT_OPERATORS[operator](left, right, gleft, gright) # Cap the length of analyzed strings. if isinstance(output, types.StringTypes) and len(output) > MAX_STR_SIZE: output = output[:MAX_STR_SIZE] traverser._debug("ASSIGNMENT::New value >> %s" % output, 1) set_lvalue(JSLiteral(output, traverser=traverser)) return orig_left
def BinaryExpression(traverser, node): traverser.debug_level += 1 # Select the proper operator. operator = node["operator"] traverser._debug("BIN_OPERATOR>>%s" % operator) # Traverse the left half of the binary expression. traverser._debug("BIN_EXP>>l-value") traverser.debug_level += 1 if (node["left"]["type"] == "BinaryExpression" and "__traversal" not in node["left"]): # Process the left branch of the binary expression directly. This keeps # the recursion cap in line and speeds up processing of large chains # of binary expressions. left = BinaryExpression(traverser, node["left"]) node["left"]["__traversal"] = left else: left = traverser.traverse_node(node["left"]) # Traverse the right half of the binary expression. traverser._debug("BIN_EXP>>r-value", -1) if (operator == "instanceof" and node["right"]["type"] == "Identifier" and node["right"]["name"] == "Function"): # We make an exception for instanceof's r-value if it's a dangerous # global, specifically Function. traverser.debug_level -= 1 return JSLiteral(True, traverser=traverser) else: right = traverser.traverse_node(node["right"]) traverser.debug_level -= 1 # Binary expressions are only executed on literals. left = left.get_literal_value(traverser) right_wrap = right right = right.get_literal_value(traverser) # Coerce the literals to numbers for numeric operations. gleft = utils.get_as_num(left) gright = utils.get_as_num(right) if operator in (">>", "<<", ">>>"): if left is None or right is None or gright < 0: return JSLiteral(False, traverser=traverser) elif abs(gleft) == float('inf') or abs(gright) == float('inf'): return utils.get_NaN(traverser) output = None if operator in BINARY_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 = "" if right is None: right = "" if (isinstance(left, types.StringTypes) or isinstance(right, types.StringTypes)): left = utils.get_as_str(left) right = utils.get_as_str(right) output = BINARY_OPERATORS[operator](left, right, gleft, gright) elif operator == "in": return JSLiteral(right_wrap.has_var(left, traverser=traverser), traverser=traverser) #TODO: `delete` operator # Cap the length of analyzed strings. if isinstance(output, types.StringTypes) and len(output) > MAX_STR_SIZE: output = output[:MAX_STR_SIZE] return JSLiteral(output, traverser=traverser)