diff --git a/test/jstests/__init__.py b/test/jstests/__init__.py new file mode 100644 index 000000000..03e51047b --- /dev/null +++ b/test/jstests/__init__.py @@ -0,0 +1,46 @@ +from . import ( + basic, + calc, + empty_return, + morespace, + strange_chars, + operators, + array_access, + parens, + assignments, + comments, + precedence, + call, + getfield, + branch, + switch, + for_loop, + for_empty, + for_in, + do_loop, + while_loop, + label, + func_expr, + object_literal, + try_statement, + with_statement, + debug, + unshift +) + + +modules = [basic, calc, empty_return, morespace, strange_chars, operators, array_access, parens, assignments, comments, + precedence, call, getfield, branch, switch, for_loop, for_empty, for_in, do_loop, while_loop, label, + func_expr, object_literal, try_statement, with_statement, debug, unshift] + + +def gettestcases(): + for module in modules: + if hasattr(module, 'tests'): + case = {'name': module.__name__[len(__name__) + 1:], 'subtests': []} + for test in getattr(module, 'tests'): + if 'code' in test: + case['subtests'].append(test) + if hasattr(module, 'skip'): + case['skip'] = getattr(module, 'skip') + yield case diff --git a/test/jstests/array_access.py b/test/jstests/array_access.py new file mode 100644 index 000000000..12eae6fed --- /dev/null +++ b/test/jstests/array_access.py @@ -0,0 +1,76 @@ +from youtube_dl.jsinterp.jsgrammar import Token +from youtube_dl.jsinterp.tstream import _ASSIGN_OPERATORS + +tests = [ + {'code': 'var x = [1,2,3]; x[0] = 4; x[0] = 5; x[2] = 7; return x;', + 'asserts': [{'value': [5, 2, 7]}], + 'ast': [(Token.VAR, + zip(['x'], + [(Token.ASSIGN, + None, + (Token.OPEXPR, [ + (Token.MEMBER, (Token.ARRAY, [ + (Token.ASSIGN, None, (Token.OPEXPR, [ + (Token.MEMBER, (Token.INT, 1), None, None)]), None), + (Token.ASSIGN, None, (Token.OPEXPR, [ + (Token.MEMBER, (Token.INT, 2), None, None)]), None), + (Token.ASSIGN, None, (Token.OPEXPR, [ + (Token.MEMBER, (Token.INT, 3), None, None)]), None) + ]), None, None), + ]), + None) + ]) + ), + (Token.EXPR, [ + (Token.ASSIGN, + _ASSIGN_OPERATORS['='][1], + (Token.OPEXPR, [ + (Token.MEMBER, (Token.ID, 'x'), + None, + (Token.ELEM, + (Token.EXPR, [ + (Token.ASSIGN, + None, + (Token.OPEXPR, [(Token.MEMBER, (Token.INT, 0), None, None)]), + None) + ]), + None)) + ]), + (Token.ASSIGN, None, (Token.OPEXPR, [(Token.MEMBER, (Token.INT, 4), None, None)]), None) + ) + ]), + (Token.EXPR, [ + (Token.ASSIGN, + _ASSIGN_OPERATORS['='][1], + (Token.OPEXPR, [(Token.MEMBER, (Token.ID, 'x'), + None, + (Token.ELEM, (Token.EXPR, [ + (Token.ASSIGN, + None, + (Token.OPEXPR, [(Token.MEMBER, (Token.INT, 0), None, None)]), + None) + ]), None)) + ]), + (Token.ASSIGN, None, (Token.OPEXPR, [(Token.MEMBER, (Token.INT, 5), None, None)]), None)) + ]), + (Token.EXPR, [ + (Token.ASSIGN, + _ASSIGN_OPERATORS['='][1], + (Token.OPEXPR, [(Token.MEMBER, (Token.ID, 'x'), + None, + (Token.ELEM, (Token.EXPR, [ + (Token.ASSIGN, + None, + (Token.OPEXPR, [(Token.MEMBER, (Token.INT, 2), None, None)]), + None) + ]), None)) + ]), + (Token.ASSIGN, None, (Token.OPEXPR, [(Token.MEMBER, (Token.INT, 7), None, None)]), None)) + ]), + (Token.RETURN, + (Token.EXPR, [ + (Token.ASSIGN, None, (Token.OPEXPR, [(Token.MEMBER, (Token.ID, 'x'), None, None)]), None) + ]) + )] + } +] diff --git a/test/jstests/assignments.py b/test/jstests/assignments.py new file mode 100644 index 000000000..3565b315f --- /dev/null +++ b/test/jstests/assignments.py @@ -0,0 +1,42 @@ +from youtube_dl.jsinterp.jsgrammar import Token +from youtube_dl.jsinterp.tstream import _OPERATORS, _ASSIGN_OPERATORS + +tests = [ + { + 'code': 'var x = 20; x = 30 + 1; return x;', + 'asserts': [{'value': 31}], + 'ast': [ + (Token.VAR, zip( + ['x'], + [(Token.ASSIGN, + None, + (Token.OPEXPR, [(Token.MEMBER, (Token.INT, 20), None, None)]), + None)] + )), + (Token.EXPR, [ + (Token.ASSIGN, + _ASSIGN_OPERATORS['='][1], + (Token.OPEXPR, [(Token.MEMBER, (Token.ID, 'x'), None, None)]), + (Token.ASSIGN, None, + (Token.OPEXPR, [ + (Token.MEMBER, (Token.INT, 30), None, None), + (Token.MEMBER, (Token.INT, 1), None, None), + (Token.OP, _OPERATORS['+'][1])]), + None)) + ]), + + (Token.RETURN, (Token.EXPR, [ + (Token.ASSIGN, None, + (Token.OPEXPR, [ + (Token.MEMBER, (Token.ID, 'x'), None, None) + ]), None) + ])) + ] + }, { + 'code': 'var x = 20; x += 30 + 1; return x;', + 'asserts': [{'value': 51}], + }, { + 'code': 'var x = 20; x -= 30 + 1; return x;', + 'asserts': [{'value': -11}], + } +] diff --git a/test/jstests/basic.py b/test/jstests/basic.py new file mode 100644 index 000000000..3f99528c4 --- /dev/null +++ b/test/jstests/basic.py @@ -0,0 +1,24 @@ +from youtube_dl.jsinterp.jsgrammar import Token + +tests = [ + { + 'code': 'return 42;', + 'asserts': [{'value': 42}], + 'ast': [(Token.RETURN, + (Token.EXPR, [ + (Token.ASSIGN, + None, + (Token.OPEXPR, [(Token.MEMBER, (Token.INT, 42), None, None)]), + None) + ]))] + }, + { + 'code': ';', + 'asserts': [{'value': None}], + 'ast': [None] + }, + { + 'code': 'var x5 = function(){return 42;}', + 'asserts': [{'value': 42, 'call': ('x5',)}] + } +] diff --git a/test/jstests/branch.py b/test/jstests/branch.py new file mode 100644 index 000000000..61a387991 --- /dev/null +++ b/test/jstests/branch.py @@ -0,0 +1,35 @@ +from youtube_dl.jsinterp.jsgrammar import Token +from youtube_dl.jsinterp.tstream import _RELATIONS + +skip = {'i': 'Interpreting if statement not yet implemented'} + +tests = [ + { + 'code': ''' + function a(x) { + if (x > 0) + return true; + else + return false; + } + ''', + 'asserts': [{'value': True, 'call': ('a', 1)}, {'value': False, 'call': ('a', 0)}], + 'ast': [ + (Token.FUNC, 'a', + ['x'], + (Token.BLOCK, [ + (Token.IF, + (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ + (Token.MEMBER, (Token.ID, 'x'), None, None), + (Token.MEMBER, (Token.INT, 0), None, None), + (Token.REL, _RELATIONS['>'][1]) + ]), None)]), + (Token.RETURN, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ + (Token.MEMBER, (Token.BOOL, True), None, None)]), None)])), + (Token.RETURN, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ + (Token.MEMBER, (Token.BOOL, False), None, None)]), None)]))) + + ])) + ] + } +] diff --git a/test/jstests/calc.py b/test/jstests/calc.py new file mode 100644 index 000000000..6e9fd8774 --- /dev/null +++ b/test/jstests/calc.py @@ -0,0 +1,24 @@ +from youtube_dl.jsinterp.jsgrammar import Token +from youtube_dl.jsinterp.tstream import _OPERATORS + +tests = [ + {'code': 'return 2*a+1;', + 'globals': {'a': 3}, + 'asserts': [{'value': 7}], + 'ast': [(Token.RETURN, + (Token.EXPR, [ + (Token.ASSIGN, + None, + (Token.OPEXPR, [ + # Reverse Polish Notation! + (Token.MEMBER, (Token.INT, 2), None, None), + (Token.MEMBER, (Token.ID, 'a'), None, None), + (Token.OP, _OPERATORS['*'][1]), + (Token.MEMBER, (Token.INT, 1), None, None), + (Token.OP, _OPERATORS['+'][1]) + ]), + None) + ]) + )] + } +] diff --git a/test/jstests/call.py b/test/jstests/call.py new file mode 100644 index 000000000..10e11f40b --- /dev/null +++ b/test/jstests/call.py @@ -0,0 +1,111 @@ +from youtube_dl.jsinterp.jsgrammar import Token +from youtube_dl.jsinterp.tstream import _OPERATORS + +skip = {'i': 'Interpreting function call not yet implemented'} + +tests = [ + { + 'code': ''' + function x() { return 2; } + function y(a) { return x() + a; } + function z() { return y(3); } + ''', + 'asserts': [{'value': 5, 'call': ('z',)}], + 'ast': [ + (Token.FUNC, 'x', + [], + (Token.BLOCK, [ + (Token.RETURN, (Token.EXPR, [ + (Token.ASSIGN, None, (Token.OPEXPR, [(Token.MEMBER, (Token.INT, 2), None, None)]), None) + ]) + ) + ])), + (Token.FUNC, 'y', + ['a'], + (Token.BLOCK, [ + (Token.RETURN, (Token.EXPR, [ + (Token.ASSIGN, None, + (Token.OPEXPR, [ + (Token.MEMBER, (Token.ID, 'x'), None, (Token.CALL, [], None)), + (Token.MEMBER, (Token.ID, 'a'), None, None), + (Token.OP, _OPERATORS['+'][1]) + ]), None) + ]) + ) + ])), + (Token.FUNC, 'z', + [], + (Token.BLOCK, [ + (Token.RETURN, (Token.EXPR, [ + (Token.ASSIGN, None, (Token.OPEXPR, [ + (Token.MEMBER, (Token.ID, 'y'), None, (Token.CALL, [ + (Token.ASSIGN, None, (Token.OPEXPR, [(Token.MEMBER, (Token.INT, 3), None, None)]), None) + ], None)) + ]), None) + ]) + ) + ])) + ] + }, { + 'code': 'function x(a) { return a.split(""); }', + 'asserts': [{'value': ["a", "b", "c"], 'call': ('x',)}], + 'ast': [ + (Token.FUNC, 'x', + ['a'], + (Token.BLOCK, [ + (Token.RETURN, (Token.EXPR, [ + (Token.ASSIGN, None, (Token.OPEXPR, [ + (Token.MEMBER, (Token.ID, 'a'), None, + (Token.FIELD, 'split', + (Token.CALL, [ + (Token.ASSIGN, None, (Token.OPEXPR, [(Token.MEMBER, (Token.STR, ''), None, None)]), None) + ], None)) + )]), + None) + ]) + ) + ])) + ] + }, { + 'code': ''' + function a(x) { return x; } + function b(x) { return x; } + function c() { return [a, b][0](0); } + ''', + 'asserts': [{'value': 0}], + 'ast': [ + (Token.FUNC, 'a', ['x'], + (Token.BLOCK, [ + (Token.RETURN, (Token.EXPR, [ + (Token.ASSIGN, None, (Token.OPEXPR, [(Token.MEMBER, (Token.ID, 'x'), None, None)]), None) + ]) + ) + ])), + (Token.FUNC, 'b', ['x'], + (Token.BLOCK, [ + (Token.RETURN, (Token.EXPR, [ + (Token.ASSIGN, None, (Token.OPEXPR, [(Token.MEMBER, (Token.ID, 'x'), None, None)]), None) + ]) + ) + ])), + (Token.FUNC, 'c', [], + (Token.BLOCK, [ + (Token.RETURN, (Token.EXPR, [ + (Token.ASSIGN, None, (Token.OPEXPR, [ + (Token.MEMBER, (Token.ARRAY, [ + (Token.ASSIGN, None, (Token.OPEXPR, [ + (Token.MEMBER, (Token.ID, 'a'), None, None)]), None), + (Token.ASSIGN, None, (Token.OPEXPR, [ + (Token.MEMBER, (Token.ID, 'b'), None, None)]), None) + ]), None, (Token.ELEM, (Token.EXPR, [ + (Token.ASSIGN, None, (Token.OPEXPR, [(Token.MEMBER, (Token.INT, 0), None, None)]), None) + ]), (Token.CALL, [ + (Token.ASSIGN, None, (Token.OPEXPR, [(Token.MEMBER, (Token.INT, 0), None, None)]), None) + ], None))) + ]), None) + ]) + ) + ])) + ] + } +] diff --git a/test/jstests/comments.py b/test/jstests/comments.py new file mode 100644 index 000000000..0f297bcde --- /dev/null +++ b/test/jstests/comments.py @@ -0,0 +1,69 @@ +from youtube_dl.jsinterp.jsgrammar import Token +from youtube_dl.jsinterp.tstream import _OPERATORS + +tests = [ + { + 'code': ''' + var x = /* 1 + */ 2; + var y = /* 30 + * 40 */ 50; + return x + y;''', + 'asserts': [{'value': 52}], + 'ast': [ + (Token.VAR, zip( + ['x'], + [(Token.ASSIGN, + None, + (Token.OPEXPR, [(Token.MEMBER, (Token.INT, 2), None, None)]), + None)] + )), + (Token.VAR, zip( + ['y'], + [(Token.ASSIGN, + None, + (Token.OPEXPR, [(Token.MEMBER, (Token.INT, 50), None, None)]), + None)] + )), + (Token.RETURN, (Token.EXPR, [ + (Token.ASSIGN, None, + (Token.OPEXPR, [ + (Token.MEMBER, (Token.ID, 'x'), None, None), + (Token.MEMBER, (Token.ID, 'y'), None, None), + (Token.OP, _OPERATORS['+'][1]) + ]), None) + ])) + ] + }, { + 'code': ''' + var x = "/*"; + var y = 1 /* comment */ + 2; + return y; + ''', + 'asserts': [{'value': 3}], + 'ast': [ + (Token.VAR, zip( + ['x'], + [(Token.ASSIGN, + None, + (Token.OPEXPR, [(Token.MEMBER, (Token.STR, '/*'), None, None)]), + None)] + )), + (Token.VAR, zip( + ['y'], + [(Token.ASSIGN, + None, + (Token.OPEXPR, [ + (Token.MEMBER, (Token.INT, 1), None, None), + (Token.MEMBER, (Token.INT, 2), None, None), + (Token.OP, _OPERATORS['+'][1]) + ]), + None)] + )), + (Token.RETURN, (Token.EXPR, [ + (Token.ASSIGN, None, + (Token.OPEXPR, [(Token.MEMBER, (Token.ID, 'y'), None, None)]), + None) + ])) + ] + } +] diff --git a/test/jstests/debug.py b/test/jstests/debug.py new file mode 100644 index 000000000..3d6f3ee74 --- /dev/null +++ b/test/jstests/debug.py @@ -0,0 +1,12 @@ +from youtube_dl.jsinterp.jsgrammar import Token + +skip = {'i': 'Interpreting debugger statement not yet implemented', + 'p': 'Test not yet implemented: missing code and ast'} + +tests = [ + { + 'code': '', + 'asserts': [{'value': 0}], + 'ast': [] + } +] diff --git a/test/jstests/do_loop.py b/test/jstests/do_loop.py new file mode 100644 index 000000000..80caff65f --- /dev/null +++ b/test/jstests/do_loop.py @@ -0,0 +1,47 @@ +from youtube_dl.jsinterp.jsgrammar import Token +from youtube_dl.jsinterp.tstream import _ASSIGN_OPERATORS, _UNARY_OPERATORS, _RELATIONS + +skip = {'i': 'Interpreting do loop not yet implemented'} + +tests = [ + { + 'code': ''' + function f(x){ + i = 1; + do{ + i++; + } while (i < x); + return i; + } + ''', + 'asserts': [{'value': 5, 'call': 5}], + 'ast': [ + (Token.FUNC, 'f', ['x'], + (Token.BLOCK, [ + (Token.EXPR, [ + (Token.ASSIGN, _ASSIGN_OPERATORS['='][1], + (Token.OPEXPR, [(Token.MEMBER, (Token.ID, 'i'), None, None)]), + (Token.ASSIGN, None, (Token.OPEXPR, [(Token.MEMBER, (Token.INT, 1), None, None)]), None)) + ]), + (Token.DO, + (Token.EXPR, [ + (Token.ASSIGN, None, (Token.OPEXPR, [ + (Token.MEMBER, (Token.ID, 'i'), None, None), + (Token.MEMBER, (Token.ID, 'x'), None, None), + (Token.REL, _RELATIONS['<'][1]) + ]), None) + ]), + (Token.BLOCK, [ + (Token.EXPR, [ + (Token.ASSIGN, None, (Token.OPEXPR, [ + (Token.MEMBER, (Token.ID, 'i'), None, None), + (Token.UOP, _UNARY_OPERATORS['++'][1]) + ]), None) + ]) + ])), + (Token.RETURN, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ + (Token.MEMBER, (Token.ID, 'i'), None, None)]), None)])) + ])) + ] + } +] diff --git a/test/jstests/empty_return.py b/test/jstests/empty_return.py new file mode 100644 index 000000000..283073fbe --- /dev/null +++ b/test/jstests/empty_return.py @@ -0,0 +1,21 @@ +from youtube_dl.jsinterp.jsgrammar import Token + +tests = [ + {'code': 'return; y()', + 'asserts': [{'value': None}], + 'ast': [ + (Token.RETURN, None), + (Token.EXPR, [ + (Token.ASSIGN, + None, + (Token.OPEXPR, [ + (Token.MEMBER, + (Token.ID, 'y'), + None, + (Token.CALL, [], None) + ) + ]), + None) + ])] + } +] diff --git a/test/jstests/for_empty.py b/test/jstests/for_empty.py new file mode 100644 index 000000000..b3a83c11c --- /dev/null +++ b/test/jstests/for_empty.py @@ -0,0 +1,47 @@ +from youtube_dl.jsinterp.jsgrammar import Token +from youtube_dl.jsinterp.tstream import _ASSIGN_OPERATORS, _UNARY_OPERATORS, _RELATIONS + +skip = {'i': 'Interpreting for empty loop not yet implemented'} + +tests = [ + { + 'code': ''' + function f(x){ + var h = 0; + for (; h <= x; ++h) { + a = h; + } + return a; + } + ''', + 'asserts': [{'value': 5, 'call': ('f', 5)}], + 'ast': [ + (Token.FUNC, 'f', ['x'], + (Token.BLOCK, [ + (Token.VAR, zip(['h'], [ + (Token.ASSIGN, None, (Token.OPEXPR, [(Token.MEMBER, (Token.INT, 0), None, None)]), None) + ])), + (Token.FOR, + None, + (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ + (Token.MEMBER, (Token.ID, 'h'), None, None), + (Token.MEMBER, (Token.ID, 'x'), None, None), + (Token.REL, _RELATIONS['<='][1]) + ]), None)]), + (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ + (Token.MEMBER, (Token.ID, 'h'), None, None), + (Token.UOP, _UNARY_OPERATORS['++'][1]) + ]), None)]), + (Token.BLOCK, [ + (Token.EXPR, [ + (Token.ASSIGN, _ASSIGN_OPERATORS['='][1], + (Token.OPEXPR, [(Token.MEMBER, (Token.ID, 'a'), None, None)]), + (Token.ASSIGN, None, (Token.OPEXPR, [(Token.MEMBER, (Token.ID, 'h'), None, None)]), None)) + ]) + ])), + (Token.RETURN, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ + (Token.MEMBER, (Token.ID, 'a'), None, None)]), None)])) + ])) + ] + } +] diff --git a/test/jstests/for_in.py b/test/jstests/for_in.py new file mode 100644 index 000000000..065b38c35 --- /dev/null +++ b/test/jstests/for_in.py @@ -0,0 +1,40 @@ +from youtube_dl.jsinterp.jsgrammar import Token +from youtube_dl.jsinterp.tstream import _ASSIGN_OPERATORS + +skip = {'i': 'Interpreting for in loop not yet implemented'} + +tests = [ + { + 'code': ''' + function f(z){ + for (h in z) { + a = h; + } + return a; + } + ''', + 'asserts': [{'value': 'c', 'call': ('f', ['a', 'b', 'c'])}], + 'ast': [ + (Token.FUNC, 'f', ['z'], + (Token.BLOCK, [ + (Token.FOR, + (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ + (Token.MEMBER, (Token.ID, 'h'), None, None) + ]), None)]), + (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ + (Token.MEMBER, (Token.ID, 'z'), None, None) + ]), None)]), + None, + (Token.BLOCK, [ + (Token.EXPR, [ + (Token.ASSIGN, _ASSIGN_OPERATORS['='][1], + (Token.OPEXPR, [(Token.MEMBER, (Token.ID, 'a'), None, None)]), + (Token.ASSIGN, None, (Token.OPEXPR, [(Token.MEMBER, (Token.ID, 'h'), None, None)]), None)) + ]) + ])), + (Token.RETURN, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ + (Token.MEMBER, (Token.ID, 'a'), None, None)]), None)])) + ])) + ] + } +] diff --git a/test/jstests/for_loop.py b/test/jstests/for_loop.py new file mode 100644 index 000000000..147a3c8b1 --- /dev/null +++ b/test/jstests/for_loop.py @@ -0,0 +1,45 @@ +from youtube_dl.jsinterp.jsgrammar import Token +from youtube_dl.jsinterp.tstream import _ASSIGN_OPERATORS, _UNARY_OPERATORS, _RELATIONS + +skip = {'i': 'Interpreting for loop not yet implemented'} + +tests = [ + { + 'code': ''' + function f(x){ + for (var h = 0; h <= x; ++h) { + a = h; + } + return a; + } + ''', + 'asserts': [{'value': 5, 'call': ('f', 5)}], + 'ast': [ + (Token.FUNC, 'f', ['x'], + (Token.BLOCK, [ + (Token.FOR, + (Token.VAR, zip(['h'], [ + (Token.ASSIGN, None, (Token.OPEXPR, [(Token.MEMBER, (Token.INT, 0), None, None)]), None) + ])), + (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ + (Token.MEMBER, (Token.ID, 'h'), None, None), + (Token.MEMBER, (Token.ID, 'x'), None, None), + (Token.REL, _RELATIONS['<='][1]) + ]), None)]), + (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ + (Token.MEMBER, (Token.ID, 'h'), None, None), + (Token.UOP, _UNARY_OPERATORS['++'][1]) + ]), None)]), + (Token.BLOCK, [ + (Token.EXPR, [ + (Token.ASSIGN, _ASSIGN_OPERATORS['='][1], + (Token.OPEXPR, [(Token.MEMBER, (Token.ID, 'a'), None, None)]), + (Token.ASSIGN, None, (Token.OPEXPR, [(Token.MEMBER, (Token.ID, 'h'), None, None)]), None)) + ]) + ])), + (Token.RETURN, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ + (Token.MEMBER, (Token.ID, 'a'), None, None)]), None)])) + ])) + ] + } +] diff --git a/test/jstests/func_expr.py b/test/jstests/func_expr.py new file mode 100644 index 000000000..a73f9663b --- /dev/null +++ b/test/jstests/func_expr.py @@ -0,0 +1,58 @@ +from youtube_dl.jsinterp.jsgrammar import Token +from youtube_dl.jsinterp.tstream import _ASSIGN_OPERATORS + +skip = {'i': 'Interpreting function expression not yet implemented'} + +tests = [ + { + 'code': ''' + function f() { + var add = (function () { + var counter = 0; + return function () {return counter += 1;}; + })(); + add(); + add(); + return add(); + } + ''', + 'asserts': [{'value': 3, 'call': ('f',)}], + 'ast': [ + (Token.FUNC, 'f', [], + (Token.BLOCK, [ + (Token.VAR, zip(['add'], [(Token.ASSIGN, None, (Token.OPEXPR, [ + (Token.MEMBER, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ + (Token.MEMBER, (Token.FUNC, None, [], (Token.BLOCK, [ + (Token.VAR, zip( + ['counter'], + [(Token.ASSIGN, None, (Token.OPEXPR, [ + (Token.MEMBER, (Token.INT, 0), None, None) + ]), None)] + )), + (Token.RETURN, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ + (Token.MEMBER, (Token.FUNC, None, [], (Token.BLOCK, [ + (Token.RETURN, (Token.EXPR, [ + (Token.ASSIGN, _ASSIGN_OPERATORS['+='][1], (Token.OPEXPR, [ + (Token.MEMBER, (Token.ID, 'counter'), None, None) + ]), (Token.ASSIGN, None, (Token.OPEXPR, [ + (Token.MEMBER, (Token.INT, 1), None, None) + ]), None)) + ])) + ])), None, None) + ]), None)])) + ])), None, None), + ]), None)]), None, (Token.CALL, [], None)) + ]), None)])), + (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ + (Token.MEMBER, (Token.ID, 'add'), None, (Token.CALL, [], None)) + ]), None)]), + (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ + (Token.MEMBER, (Token.ID, 'add'), None, (Token.CALL, [], None)) + ]), None)]), + (Token.RETURN, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ + (Token.MEMBER, (Token.ID, 'add'), None, (Token.CALL, [], None)) + ]), None)])) + ])) + ] + } +] diff --git a/test/jstests/getfield.py b/test/jstests/getfield.py new file mode 100644 index 000000000..a41f74c49 --- /dev/null +++ b/test/jstests/getfield.py @@ -0,0 +1,25 @@ +from youtube_dl.jsinterp.jsgrammar import Token + +skip = {'i': 'Interpreting get field not yet implemented'} + +tests = [ + { + 'code': 'return a.var;', + 'asserts': [{'value': 3}], + 'globals': {'a': {'var': 3}}, + 'ast': [ + (Token.RETURN, + (Token.EXPR, [ + (Token.ASSIGN, + None, + (Token.OPEXPR, [ + (Token.MEMBER, + (Token.ID, 'a'), + None, + (Token.FIELD, 'var', None)), + ]), + None) + ])) + ] + } +] diff --git a/test/jstests/label.py b/test/jstests/label.py new file mode 100644 index 000000000..1cd9d8164 --- /dev/null +++ b/test/jstests/label.py @@ -0,0 +1,12 @@ +from youtube_dl.jsinterp.jsgrammar import Token + +skip = {'i': 'Interpreting label not yet implemented', + 'p': 'Test not yet implemented: missing code and ast'} + +tests = [ + { + 'code': '', + 'asserts': [{'value': 0}], + 'ast': [] + } +] diff --git a/test/jstests/morespace.py b/test/jstests/morespace.py new file mode 100644 index 000000000..1e238f419 --- /dev/null +++ b/test/jstests/morespace.py @@ -0,0 +1,33 @@ +from youtube_dl.jsinterp.jsgrammar import Token +from youtube_dl.jsinterp.tstream import _ASSIGN_OPERATORS + +skip = {'i': 'Interpreting set field not yet implemented'} + +tests = [ + { + 'code': 'x = 2 ; return x;', + 'asserts': [{'value': 2}], + 'ast': [ + (Token.EXPR, + [(Token.ASSIGN, + _ASSIGN_OPERATORS['='][1], + (Token.OPEXPR, [(Token.MEMBER, (Token.ID, 'x'), None, None)]), + (Token.ASSIGN, + None, + (Token.OPEXPR, [(Token.MEMBER, (Token.INT, 2), None, None)]), + None) + )] + ), + (Token.RETURN, + (Token.EXPR, [ + (Token.ASSIGN, + None, + (Token.OPEXPR, [(Token.MEMBER, (Token.ID, 'x'), None, None)]), + None) + ]) + )] + }, { + 'code': 'function x (a) { return 2 * a + 1 ; }', + 'asserts': [{'value': 7, 'call': ('x', 3)}] + } +] diff --git a/test/jstests/object_literal.py b/test/jstests/object_literal.py new file mode 100644 index 000000000..b566a65c2 --- /dev/null +++ b/test/jstests/object_literal.py @@ -0,0 +1,59 @@ +from youtube_dl.jsinterp.jsgrammar import Token +from youtube_dl.jsinterp.tstream import _ASSIGN_OPERATORS, _OPERATORS + +skip = {'i': 'Interpreting object literals not yet implemented'} + +tests = [ + { + 'code': ''' + function f() { + var o = { + a: 7, + get b() { return this.a + 1; }, + set c(x) { this.a = x / 2; } + }; + return o; + } + ''', + 'ast': [ + (Token.FUNC, 'f', [], + (Token.BLOCK, [ + (Token.VAR, + zip(['o'], + [(Token.ASSIGN, None, (Token.OPEXPR, [ + (Token.MEMBER, (Token.OBJECT, [ + ('a', (Token.PROPVALUE, (Token.ASSIGN, None, (Token.OPEXPR, [ + (Token.MEMBER, (Token.INT, 7), None, None) + ]), None))), + ('b', (Token.PROPGET, (Token.BLOCK, [ + (Token.RETURN, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ + (Token.MEMBER, (Token.RSV, 'this'), None, (Token.FIELD, 'a', None)), + (Token.MEMBER, (Token.INT, 1), None, None), + (Token.OP, _OPERATORS['+'][1]) + ]), None)])) + ]))), + ('c', (Token.PROPSET, 'x', (Token.BLOCK, [ + (Token.EXPR, [ + (Token.ASSIGN, + _ASSIGN_OPERATORS['='][1], + (Token.OPEXPR, [ + (Token.MEMBER, (Token.RSV, 'this'), None, (Token.FIELD, 'a', None)) + ]), + (Token.ASSIGN, None, (Token.OPEXPR, [ + (Token.MEMBER, (Token.ID, 'x'), None, None), + (Token.MEMBER, (Token.INT, 2), None, None), + (Token.OP, _OPERATORS['/'][1]) + ]), None)) + ]) + ]))) + ]), + None, None) + ]), None)] + ) + ), + (Token.RETURN, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ + (Token.MEMBER, (Token.ID, 'o'), None, None)]), None)])) + ])) + ] + } +] diff --git a/test/jstests/operators.py b/test/jstests/operators.py new file mode 100644 index 000000000..c95a8baca --- /dev/null +++ b/test/jstests/operators.py @@ -0,0 +1,43 @@ +from youtube_dl.jsinterp.jsgrammar import Token +from youtube_dl.jsinterp.tstream import _OPERATORS + +tests = [ + { + 'code': 'return 1 << 5;', + 'asserts': [{'value': 32}], + 'ast': [ + (Token.RETURN, + (Token.EXPR, [ + (Token.ASSIGN, None, (Token.OPEXPR, [ + (Token.MEMBER, (Token.INT, 1), None, None), + (Token.MEMBER, (Token.INT, 5), None, None), + (Token.OP, _OPERATORS['<<'][1]) + ]), None) + ]))] + }, { + 'code': 'return 19 & 21;', + 'asserts': [{'value': 17}], + 'ast': [ + (Token.RETURN, + (Token.EXPR, [ + (Token.ASSIGN, None, (Token.OPEXPR, [ + (Token.MEMBER, (Token.INT, 19), None, None), + (Token.MEMBER, (Token.INT, 21), None, None), + (Token.OP, _OPERATORS['&'][1]) + ]), None) + ])) + ] + }, { + 'code': 'return 11 >> 2;', + 'asserts': [{'value': 2}], + 'ast': [ + (Token.RETURN, + (Token.EXPR, [ + (Token.ASSIGN, None, (Token.OPEXPR, [ + (Token.MEMBER, (Token.INT, 11), None, None), + (Token.MEMBER, (Token.INT, 2), None, None), + (Token.OP, _OPERATORS['>>'][1]) + ]), None) + ]))] + } +] diff --git a/test/jstests/parens.py b/test/jstests/parens.py new file mode 100644 index 000000000..52eef903f --- /dev/null +++ b/test/jstests/parens.py @@ -0,0 +1,73 @@ +from youtube_dl.jsinterp.jsgrammar import Token +from youtube_dl.jsinterp.tstream import _OPERATORS + +tests = [ + { + 'code': 'return (1 + 2) * 3;', + 'asserts': [{'value': 9}], + 'ast': [ + (Token.RETURN, (Token.EXPR, [ + (Token.ASSIGN, None, + (Token.OPEXPR, [ + (Token.MEMBER, (Token.EXPR, [ + (Token.ASSIGN, None, + (Token.OPEXPR, [ + (Token.MEMBER, (Token.INT, 1), None, None), + (Token.MEMBER, (Token.INT, 2), None, None), + (Token.OP, _OPERATORS['+'][1]) + ]), None) + ]), None, None), + (Token.MEMBER, (Token.INT, 3), None, None), + (Token.OP, _OPERATORS['*'][1]) + ]), None) + ]))] + }, { + 'code': 'return (1) + (2) * ((( (( (((((3)))))) )) ));', + 'asserts': [{'value': 7}], + 'ast': [ + + (Token.RETURN, (Token.EXPR, [ + (Token.ASSIGN, None, + (Token.OPEXPR, [ + (Token.MEMBER, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ + (Token.MEMBER, (Token.INT, 1), None, None) + ]), None)]), None, None), + + (Token.MEMBER, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ + (Token.MEMBER, (Token.INT, 2), None, None) + ]), None)]), None, None), + + (Token.MEMBER, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ + (Token.MEMBER, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ + (Token.MEMBER, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ + + (Token.MEMBER, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ + (Token.MEMBER, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ + + (Token.MEMBER, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ + (Token.MEMBER, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ + (Token.MEMBER, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ + (Token.MEMBER, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ + (Token.MEMBER, + (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ + (Token.MEMBER, (Token.INT, 3), None, None) + ]), None)]), None, None) + ]), None)]), None, None) + ]), None)]), None, None) + ]), None)]), None, None) + ]), None)]), None, None) + + ]), None)]), None, None) + ]), None)]), None, None) + + ]), None)]), None, None) + ]), None)]), None, None) + ]), None)]), None, None), + + (Token.OP, _OPERATORS['*'][1]), + (Token.OP, _OPERATORS['+'][1]) + ]), None) + ])) + ] + } +] diff --git a/test/jstests/precedence.py b/test/jstests/precedence.py new file mode 100644 index 000000000..7b8bf9bcc --- /dev/null +++ b/test/jstests/precedence.py @@ -0,0 +1,80 @@ +from youtube_dl.jsinterp.jsgrammar import Token +from youtube_dl.jsinterp.tstream import _ASSIGN_OPERATORS, _OPERATORS + +skip = {'i': 'Interpreting get field not yet implemented'} + +tests = [ + { + 'code': ''' + var a = [10, 20, 30, 40, 50]; + var b = 6; + a[0]=a[b%a.length]; + return a; + ''', + 'asserts': [{'value': [20, 20, 30, 40, 50]}], + 'ast': [ + (Token.VAR, + zip(['a'], + [(Token.ASSIGN, + None, + (Token.OPEXPR, [ + (Token.MEMBER, (Token.ARRAY, [ + (Token.ASSIGN, None, (Token.OPEXPR, [ + (Token.MEMBER, (Token.INT, 10), None, None)]), None), + (Token.ASSIGN, None, (Token.OPEXPR, [ + (Token.MEMBER, (Token.INT, 20), None, None)]), None), + (Token.ASSIGN, None, (Token.OPEXPR, [ + (Token.MEMBER, (Token.INT, 30), None, None)]), None), + (Token.ASSIGN, None, (Token.OPEXPR, [ + (Token.MEMBER, (Token.INT, 40), None, None)]), None), + (Token.ASSIGN, None, (Token.OPEXPR, [ + (Token.MEMBER, (Token.INT, 50), None, None)]), None) + ]), None, None), + ]), + None) + ]) + ), + (Token.VAR, + zip(['b'], + [(Token.ASSIGN, None, (Token.OPEXPR, [(Token.MEMBER, (Token.INT, 6), None, None)]), None)] + ) + ), + (Token.EXPR, [ + (Token.ASSIGN, + _ASSIGN_OPERATORS['='][1], + (Token.OPEXPR, [ + (Token.MEMBER, (Token.ID, 'a'), + None, + (Token.ELEM, + (Token.EXPR, [ + (Token.ASSIGN, + None, + (Token.OPEXPR, [(Token.MEMBER, (Token.INT, 0), None, None)]), + None) + ]), + None)) + ]), + (Token.ASSIGN, + None, + (Token.OPEXPR, [ + (Token.MEMBER, (Token.ID, 'a'), + None, + (Token.ELEM, (Token.EXPR, [ + (Token.ASSIGN, None, (Token.OPEXPR, [ + (Token.MEMBER, (Token.ID, 'b'), None, None), + (Token.MEMBER, (Token.ID, 'a'), None, (Token.FIELD, 'length', None)), + (Token.OP, _OPERATORS['%'][1]) + ]), None)]), + None)) + ]), + None) + ) + ]), + (Token.RETURN, + (Token.EXPR, [ + (Token.ASSIGN, None, (Token.OPEXPR, [(Token.MEMBER, (Token.ID, 'a'), None, None)]), None) + ]) + ) + ] + } +] diff --git a/test/jstests/strange_chars.py b/test/jstests/strange_chars.py new file mode 100644 index 000000000..96355eaed --- /dev/null +++ b/test/jstests/strange_chars.py @@ -0,0 +1,31 @@ +from youtube_dl.jsinterp.jsgrammar import Token +from youtube_dl.jsinterp.tstream import _OPERATORS + +tests = [ + { + 'code': 'var $_axY2 = $_xY1 + 1; return $_axY2;', + 'globals': {'$_xY1': 20}, + 'asserts': [{'value': 21}], + 'ast': [ + (Token.VAR, + zip(['$_axY2'], + [(Token.ASSIGN, + None, + (Token.OPEXPR, [ + (Token.MEMBER, (Token.ID, '$_xY1'), None, None), + (Token.MEMBER, (Token.INT, 1), None, None), + (Token.OP, _OPERATORS['+'][1]) + ]), + None) + ]) + ), + (Token.RETURN, + (Token.EXPR, [ + (Token.ASSIGN, + None, + (Token.OPEXPR, [(Token.MEMBER, (Token.ID, '$_axY2'), None, None)]), + None)] + ) + )] + } +] diff --git a/test/jstests/switch.py b/test/jstests/switch.py new file mode 100644 index 000000000..0777bd119 --- /dev/null +++ b/test/jstests/switch.py @@ -0,0 +1,73 @@ +from youtube_dl.jsinterp.jsgrammar import Token +from youtube_dl.jsinterp.tstream import _ASSIGN_OPERATORS, _UNARY_OPERATORS + +skip = {'i': 'Interpreting switch statement not yet implemented'} + +tests = [ + { + 'code': ''' + function a(x) { + switch (x) { + case 6: + break; + case 5: + x++; + case 8: + x--; + break; + default: + x = 0; + } + return x; + } + ''', + 'asserts': [{'value': 4, 'call': ('a', 0)}, + {'value': 5, 'call': ('a', 5)}, + {'value': 6, 'call': ('a', 6)}, + {'value': 8, 'call': ('a', 7)}], + 'ast': [ + (Token.FUNC, 'a', ['x'], + (Token.BLOCK, [ + (Token.SWITCH, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ + (Token.MEMBER, (Token.ID, 'x'), None, None) + ]), None)]), + [ + ((Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ + (Token.MEMBER, (Token.INT, 6), None, None)]), None)]), + [ + (Token.BREAK, None) + ]), + ((Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ + (Token.MEMBER, (Token.INT, 5), None, None)]), None)]), + [ + (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ + (Token.MEMBER, (Token.ID, 'x'), None, None), + (Token.UOP, _UNARY_OPERATORS['++'][1]) + ]), None)]) + ]), + ((Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ + (Token.MEMBER, (Token.INT, 8), None, None)]), None)]), + [ + (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ + (Token.MEMBER, (Token.ID, 'x'), None, None), + (Token.UOP, _UNARY_OPERATORS['--'][1]) + ]), None)]), + (Token.BREAK, None) + ]), + (None, + [ + (Token.EXPR, [ + (Token.ASSIGN, + _ASSIGN_OPERATORS['='][1], + (Token.OPEXPR, [(Token.MEMBER, (Token.ID, 'x'), None, None)]), + (Token.ASSIGN, None, (Token.OPEXPR, [(Token.MEMBER, (Token.INT, 0), None, None)]), None) + ) + ]) + ]) + ] + ), + (Token.RETURN, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ + (Token.MEMBER, (Token.ID, 'x'), None, None)]), None)])) + ]))] + } +] diff --git a/test/jstests/try_statement.py b/test/jstests/try_statement.py new file mode 100644 index 000000000..841bcc524 --- /dev/null +++ b/test/jstests/try_statement.py @@ -0,0 +1,12 @@ +from youtube_dl.jsinterp.jsgrammar import Token + +skip = {'i': 'Interpreting try statement not yet implemented', + 'p': 'Test not yet implemented: missing code and ast'} + +tests = [ + { + 'code': '', + 'asserts': [{'value': 0}], + 'ast': [] + } +] diff --git a/test/jstests/unshift.py b/test/jstests/unshift.py new file mode 100644 index 000000000..de76f2cab --- /dev/null +++ b/test/jstests/unshift.py @@ -0,0 +1,30 @@ + +skip = {'p': 'Signed integers not yet supported'} + +tests = [ + { + 'code': ''' + var MAX_LENGTH = 0xffffffff; + + var a = {}; + a.length = MAX_LENGTH + 1; + assertEq([].unshift.call(a), MAX_LENGTH); + assertEq(a.length, MAX_LENGTH); + + function testGetSet(len, expected) { + var newlen; + var a = { get length() { return len; }, set length(v) { newlen = v; } }; + var res = [].unshift.call(a); + assertEq(res, expected); + assertEq(newlen, expected); + } + + testGetSet(0, 0); + testGetSet(10, 10); + testGetSet("1", 1); + testGetSet(null, 0); + testGetSet(MAX_LENGTH + 2, MAX_LENGTH); + testGetSet(-5, 0); + ''' + } +] diff --git a/test/jstests/while_loop.py b/test/jstests/while_loop.py new file mode 100644 index 000000000..9c8228d23 --- /dev/null +++ b/test/jstests/while_loop.py @@ -0,0 +1,47 @@ +from youtube_dl.jsinterp.jsgrammar import Token +from youtube_dl.jsinterp.tstream import _ASSIGN_OPERATORS, _UNARY_OPERATORS, _RELATIONS + +skip = {'i': 'Interpreting while loop not yet implemented'} + +tests = [ + { + 'code': ''' + function f(x){ + i = 1; + while (i < x) { + i++; + } + return i; + } + ''', + 'asserts': [{'value': 5, 'call': ('f', 5)}], + 'ast': [ + (Token.FUNC, 'f', ['x'], + (Token.BLOCK, [ + (Token.EXPR, [ + (Token.ASSIGN, _ASSIGN_OPERATORS['='][1], + (Token.OPEXPR, [(Token.MEMBER, (Token.ID, 'i'), None, None)]), + (Token.ASSIGN, None, (Token.OPEXPR, [(Token.MEMBER, (Token.INT, 1), None, None)]), None)) + ]), + (Token.WHILE, + (Token.EXPR, [ + (Token.ASSIGN, None, (Token.OPEXPR, [ + (Token.MEMBER, (Token.ID, 'i'), None, None), + (Token.MEMBER, (Token.ID, 'x'), None, None), + (Token.REL, _RELATIONS['<'][1]) + ]), None) + ]), + (Token.BLOCK, [ + (Token.EXPR, [ + (Token.ASSIGN, None, (Token.OPEXPR, [ + (Token.MEMBER, (Token.ID, 'i'), None, None), + (Token.UOP, _UNARY_OPERATORS['++'][1]) + ]), None) + ]) + ])), + (Token.RETURN, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ + (Token.MEMBER, (Token.ID, 'i'), None, None)]), None)])) + ])) + ] + } +] diff --git a/test/jstests/with_statement.py b/test/jstests/with_statement.py new file mode 100644 index 000000000..82c04c099 --- /dev/null +++ b/test/jstests/with_statement.py @@ -0,0 +1,12 @@ +from youtube_dl.jsinterp.jsgrammar import Token + +skip = {'i': 'Interpreting with statement not yet implemented', + 'p': 'Test not yet implemented: missing code and ast'} + +tests = [ + { + 'code': '', + 'asserts': [{'value': 0}], + 'ast': [] + } +] diff --git a/test/test_jsinterp.py b/test/test_jsinterp.py index 734b5507a..3313e40a0 100644 --- a/test/test_jsinterp.py +++ b/test/test_jsinterp.py @@ -2,9 +2,9 @@ from __future__ import unicode_literals -# Allow direct execution import os import sys + if sys.version_info < (2, 7): import unittest2 as unittest else: @@ -12,127 +12,44 @@ else: sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from youtube_dl.jsinterp import JSInterpreter +from test.jstests import gettestcases + +defs = gettestcases() class TestJSInterpreter(unittest.TestCase): - def test_basic(self): - jsi = JSInterpreter('function x(){;}') - self.assertEqual(jsi.call_function('x'), None) + def setUp(self): + self.defs = defs - jsi = JSInterpreter('function x3(){return 42;}') - self.assertEqual(jsi.call_function('x3'), 42) - jsi = JSInterpreter('var x5 = function(){return 42;}') - self.assertEqual(jsi.call_function('x5'), 42) +def generator(test_case): + def test_template(self): + for test in test_case['subtests']: + jsi = JSInterpreter(test['code'], variables=None if 'globals' not in test else test['globals']) + if 'asserts' in test: + for a in test['asserts']: + if 'call' in a: + self.assertEqual(jsi.call_function(*a['call']), a['value']) + else: + self.assertEqual(jsi.run(), a['value']) - def test_calc(self): - jsi = JSInterpreter('function x4(a){return 2*a+1;}') - self.assertEqual(jsi.call_function('x4', 3), 7) + if 'skip' not in test_case or 'i' not in test_case['skip']: + reason = False + else: + reason = test_case['skip']['i'] - def test_empty_return(self): - jsi = JSInterpreter('function f(){return; y()}') - self.assertEqual(jsi.call_function('f'), None) + return test_template if not reason else unittest.skip(reason)(test_template) - @unittest.skip('Interpreting set field not yet implemented') - def test_morespace(self): - jsi = JSInterpreter('function x (a) { return 2 * a + 1 ; }') - self.assertEqual(jsi.call_function('x', 3), 7) - jsi = JSInterpreter('function f () { x = 2 ; return x; }') - self.assertEqual(jsi.call_function('f'), 2) - - def test_strange_chars(self): - jsi = JSInterpreter('function $_xY1 ($_axY1) { var $_axY2 = $_axY1 + 1; return $_axY2; }') - self.assertEqual(jsi.call_function('$_xY1', 20), 21) - - # TODO test prefix and postfix operators - - def test_operators(self): - jsi = JSInterpreter('function f(){return 1 << 5;}') - self.assertEqual(jsi.call_function('f'), 32) - - jsi = JSInterpreter('function f(){return 19 & 21;}') - self.assertEqual(jsi.call_function('f'), 17) - - jsi = JSInterpreter('function f(){return 11 >> 2;}') - self.assertEqual(jsi.call_function('f'), 2) - - def test_array_access(self): - jsi = JSInterpreter('function f(){var x = [1,2,3]; x[0] = 4; x[0] = 5; x[2] = 7; return x;}') - self.assertEqual(jsi.call_function('f'), [5, 2, 7]) - - def test_parens(self): - jsi = JSInterpreter('function f(){return (1) + (2) * ((( (( (((((3)))))) )) ));}') - self.assertEqual(jsi.call_function('f'), 7) - - jsi = JSInterpreter('function f(){return (1 + 2) * 3;}') - self.assertEqual(jsi.call_function('f'), 9) - - def test_assignments(self): - jsi = JSInterpreter('function f(){var x = 20; x = 30 + 1; return x;}') - self.assertEqual(jsi.call_function('f'), 31) - - jsi = JSInterpreter('function f(){var x = 20; x += 30 + 1; return x;}') - self.assertEqual(jsi.call_function('f'), 51) - - jsi = JSInterpreter('function f(){var x = 20; x -= 30 + 1; return x;}') - self.assertEqual(jsi.call_function('f'), -11) - - def test_comments(self): - jsi = JSInterpreter(''' - function x() { - var x = /* 1 + */ 2; - var y = /* 30 - * 40 */ 50; - return x + y; - } - ''') - self.assertEqual(jsi.call_function('x'), 52) - - jsi = JSInterpreter(''' - function f() { - var x = "/*"; - var y = 1 /* comment */ + 2; - return y; - } - ''') - self.assertEqual(jsi.call_function('f'), 3) - - @unittest.skip('Interpreting get field not yet implemented') - def test_precedence(self): - jsi = JSInterpreter(''' - function x() { - var a = [10, 20, 30, 40, 50]; - var b = 6; - a[0]=a[b%a.length]; - return a; - }''') - self.assertEqual(jsi.call_function('x'), [20, 20, 30, 40, 50]) - - @unittest.skip('Interpreting function call not yet implemented') - def test_call(self): - jsi = JSInterpreter(''' - function x() { return 2; } - function y(a) { return x() + a; } - function z() { return y(3); } - ''') - self.assertEqual(jsi.call_function('z'), 5) - jsi = JSInterpreter('function x(a) { return a.split(""); }', variables={'a': 'abc'}) - self.assertEqual(jsi.call_function('x'), ["a", "b", "c"]) - - @unittest.skip('Interpreting function call not yet implemented') - def test_complex_call(self): - jsi = JSInterpreter(''' - function a(x) { return x; } - function b(x) { return x; } - function c() { return [a, b][0](0); } - ''') - self.assertEqual(jsi.call_function('c'), 0) - - @unittest.skip('Interpreting get field not yet implemented') - def test_getfield(self): - jsi = JSInterpreter('function c() { return a.var; }', variables={'a': {'var': 3}}) - self.assertEqual(jsi.call_function('c'), 3) - -if __name__ == '__main__': - unittest.main() +# And add them to TestJSInterpreter +for n, tc in enumerate(defs): + if any('asserts' in test for test in tc['subtests']): + test_method = generator(tc) + tname = 'test_' + str(tc['name']) + i = 1 + while hasattr(TestJSInterpreter, tname): + tname = 'test_%s_%d' % (tc['name'], i) + i += 1 + test_method.__name__ = str(tname) + setattr(TestJSInterpreter, test_method.__name__, test_method) + del test_method diff --git a/test/test_jsinterp_parse.py b/test/test_jsinterp_parse.py new file mode 100644 index 000000000..323bb747c --- /dev/null +++ b/test/test_jsinterp_parse.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python + +from __future__ import unicode_literals + +import os +import sys +import copy + +if sys.version_info < (2, 7): + import unittest2 as unittest +else: + import unittest +sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + +from youtube_dl.jsinterp import JSInterpreter +from test.jstests import gettestcases + + +def traverse(node, tree_types=(list, tuple)): + if type(node) == zip: + node = list(copy.deepcopy(node)) + if isinstance(node, tree_types): + tree = [] + for value in node: + tree.append(traverse(value, tree_types)) + return tree + else: + return node + + +defs = gettestcases() + + +class TestJSInterpreterParse(unittest.TestCase): + def setUp(self): + self.defs = defs + + +def generator(test_case): + def test_template(self): + for a in test_case['subtests']: + jsi = JSInterpreter(a['code'], variables=None if 'globals' not in a else a['globals']) + parsed = list(jsi.statements()) + if 'ast' in a: + self.assertEqual(traverse(parsed), traverse(a['ast'])) + + if 'skip' not in test_case or 'p' not in test_case['skip']: + reason = False + else: + reason = test_case['skip']['p'] + + return test_template if not reason else unittest.skip(reason)(test_template) + + +# And add them to TestJSInterpreter +for n, tc in enumerate(defs): + test_method = generator(tc) + tname = 'test_' + str(tc['name']) + i = 1 + while hasattr(TestJSInterpreterParse, tname): + tname = 'test_%s_%d' % (tc['name'], i) + i += 1 + test_method.__name__ = str(tname) + setattr(TestJSInterpreterParse, test_method.__name__, test_method) + del test_method diff --git a/test/test_jsinterp_parser.py b/test/test_jsinterp_parser.py deleted file mode 100644 index 98accd28b..000000000 --- a/test/test_jsinterp_parser.py +++ /dev/null @@ -1,1160 +0,0 @@ -#!/usr/bin/env python - -from __future__ import unicode_literals - -# Allow direct execution -import os -import sys -import copy - -if sys.version_info < (2, 7): - import unittest2 as unittest -else: - import unittest -sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) - -from youtube_dl.jsinterp import JSInterpreter -from youtube_dl.jsinterp.jsgrammar import Token -from youtube_dl.jsinterp.tstream import ( - _OPERATORS, - _ASSIGN_OPERATORS, - _LOGICAL_OPERATORS, - _UNARY_OPERATORS, - _RELATIONS -) - - -def traverse(node, tree_types=(list, tuple)): - if type(node) == zip: - node = list(copy.deepcopy(node)) - if isinstance(node, tree_types): - for value in node: - for subvalue in traverse(value, tree_types): - yield subvalue - else: - yield node - - -class TestJSInterpreterParser(unittest.TestCase): - def test_basic(self): - jsi = JSInterpreter(';') - ast = [None] - self.assertEqual(list(jsi.statements()), ast) - - jsi = JSInterpreter('return 42;') - ast = [(Token.RETURN, - (Token.EXPR, [ - (Token.ASSIGN, - None, - (Token.OPEXPR, [(Token.MEMBER, (Token.INT, 42), None, None)]), - None) - ]) - )] - self.assertEqual(list(jsi.statements()), ast) - - def test_calc(self): - jsi = JSInterpreter('return 2*a+1;') - ast = [(Token.RETURN, - (Token.EXPR, [ - (Token.ASSIGN, - None, - (Token.OPEXPR, [ - # Reverse Polish Notation! - (Token.MEMBER, (Token.INT, 2), None, None), - (Token.MEMBER, (Token.ID, 'a'), None, None), - (Token.OP, _OPERATORS['*'][1]), - (Token.MEMBER, (Token.INT, 1), None, None), - (Token.OP, _OPERATORS['+'][1]) - ]), - None) - ]) - )] - self.assertEqual(list(jsi.statements()), ast) - - def test_empty_return(self): - jsi = JSInterpreter('return; y()') - ast = [(Token.RETURN, None), - (Token.EXPR, [ - (Token.ASSIGN, - None, - (Token.OPEXPR, [ - (Token.MEMBER, - (Token.ID, 'y'), - None, - (Token.CALL, [], None) - ) - ]), - None) - ])] - self.assertEqual(list(jsi.statements()), ast) - - def test_morespace(self): - jsi = JSInterpreter('x = 2 ; return x;') - ast = [(Token.EXPR, - [(Token.ASSIGN, - _ASSIGN_OPERATORS['='][1], - (Token.OPEXPR, [(Token.MEMBER, (Token.ID, 'x'), None, None)]), - (Token.ASSIGN, - None, - (Token.OPEXPR, [(Token.MEMBER, (Token.INT, 2), None, None)]), - None) - )] - ), - (Token.RETURN, - (Token.EXPR, [ - (Token.ASSIGN, - None, - (Token.OPEXPR, [(Token.MEMBER, (Token.ID, 'x'), None, None)]), - None) - ]) - )] - self.assertEqual(list(jsi.statements()), ast) - - def test_strange_chars(self): - jsi = JSInterpreter('var $_axY2 = $_axY1 + 1; return $_axY2;') - ast = [(Token.VAR, - zip(['$_axY2'], - [(Token.ASSIGN, - None, - (Token.OPEXPR, [ - (Token.MEMBER, (Token.ID, '$_axY1'), None, None), - (Token.MEMBER, (Token.INT, 1), None, None), - (Token.OP, _OPERATORS['+'][1]) - ]), - None) - ]) - ), - (Token.RETURN, - (Token.EXPR, [ - (Token.ASSIGN, - None, - (Token.OPEXPR, [(Token.MEMBER, (Token.ID, '$_axY2'), None, None)]), - None)] - ) - )] - self.assertEqual(list(traverse(list(jsi.statements()))), list(traverse(ast))) - - def test_operators(self): - jsi = JSInterpreter('return 1 << 5;') - ast = [ - (Token.RETURN, - (Token.EXPR, [ - (Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.INT, 1), None, None), - (Token.MEMBER, (Token.INT, 5), None, None), - (Token.OP, _OPERATORS['<<'][1]) - ]), None) - ])) - ] - self.assertEqual(list(jsi.statements()), ast) - - jsi = JSInterpreter('return 19 & 21;') - ast = [ - (Token.RETURN, - (Token.EXPR, [ - (Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.INT, 19), None, None), - (Token.MEMBER, (Token.INT, 21), None, None), - (Token.OP, _OPERATORS['&'][1]) - ]), None) - ])) - ] - self.assertEqual(list(jsi.statements()), ast) - - jsi = JSInterpreter('return 11 >> 2;') - ast = [ - (Token.RETURN, - (Token.EXPR, [ - (Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.INT, 11), None, None), - (Token.MEMBER, (Token.INT, 2), None, None), - (Token.OP, _OPERATORS['>>'][1]) - ]), None) - ])) - ] - self.assertEqual(list(jsi.statements()), ast) - - def test_array_access(self): - jsi = JSInterpreter('var x = [1,2,3]; x[0] = 4; x[0] = 5; x[2] = 7; return x;') - ast = [(Token.VAR, - zip(['x'], - [(Token.ASSIGN, - None, - (Token.OPEXPR, [ - (Token.MEMBER, (Token.ARRAY, [ - (Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.INT, 1), None, None)]), None), - (Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.INT, 2), None, None)]), None), - (Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.INT, 3), None, None)]), None) - ]), None, None), - ]), - None) - ]) - ), - (Token.EXPR, [ - (Token.ASSIGN, - _ASSIGN_OPERATORS['='][1], - (Token.OPEXPR, [ - (Token.MEMBER, (Token.ID, 'x'), - None, - (Token.ELEM, - (Token.EXPR, [ - (Token.ASSIGN, - None, - (Token.OPEXPR, [(Token.MEMBER, (Token.INT, 0), None, None)]), - None) - ]), - None)) - ]), - (Token.ASSIGN, None, (Token.OPEXPR, [(Token.MEMBER, (Token.INT, 4), None, None)]), None) - ) - ]), - (Token.EXPR, [ - (Token.ASSIGN, - _ASSIGN_OPERATORS['='][1], - (Token.OPEXPR, [(Token.MEMBER, (Token.ID, 'x'), - None, - (Token.ELEM, (Token.EXPR, [ - (Token.ASSIGN, - None, - (Token.OPEXPR, [(Token.MEMBER, (Token.INT, 0), None, None)]), - None) - ]), None)) - ]), - (Token.ASSIGN, None, (Token.OPEXPR, [(Token.MEMBER, (Token.INT, 5), None, None)]), None)) - ]), - (Token.EXPR, [ - (Token.ASSIGN, - _ASSIGN_OPERATORS['='][1], - (Token.OPEXPR, [(Token.MEMBER, (Token.ID, 'x'), - None, - (Token.ELEM, (Token.EXPR, [ - (Token.ASSIGN, - None, - (Token.OPEXPR, [(Token.MEMBER, (Token.INT, 2), None, None)]), - None) - ]), None)) - ]), - (Token.ASSIGN, None, (Token.OPEXPR, [(Token.MEMBER, (Token.INT, 7), None, None)]), None)) - ]), - (Token.RETURN, - (Token.EXPR, [ - (Token.ASSIGN, None, (Token.OPEXPR, [(Token.MEMBER, (Token.ID, 'x'), None, None)]), None) - ]) - ) - ] - self.assertEqual(list(traverse(list(jsi.statements()))), list(traverse(ast))) - - def test_parens(self): - jsi = JSInterpreter('return (1 + 2) * 3;') - ast = [ - (Token.RETURN, (Token.EXPR, [ - (Token.ASSIGN, None, - (Token.OPEXPR, [ - (Token.MEMBER, (Token.EXPR, [ - (Token.ASSIGN, None, - (Token.OPEXPR, [ - (Token.MEMBER, (Token.INT, 1), None, None), - (Token.MEMBER, (Token.INT, 2), None, None), - (Token.OP, _OPERATORS['+'][1]) - ]), None) - ]), None, None), - (Token.MEMBER, (Token.INT, 3), None, None), - (Token.OP, _OPERATORS['*'][1]) - ]), None) - ])) - ] - self.assertEqual(list(jsi.statements()), ast) - - jsi = JSInterpreter('return (1) + (2) * ((( (( (((((3)))))) )) ));') - ast = [ - (Token.RETURN, (Token.EXPR, [ - (Token.ASSIGN, None, - (Token.OPEXPR, [ - (Token.MEMBER, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.INT, 1), None, None) - ]), None)]), None, None), - - (Token.MEMBER, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.INT, 2), None, None) - ]), None)]), None, None), - - (Token.MEMBER, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ - - (Token.MEMBER, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ - - (Token.MEMBER, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, - (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.INT, 3), None, None) - ]), None)]), None, None) - ]), None)]), None, None) - ]), None)]), None, None) - ]), None)]), None, None) - ]), None)]), None, None) - - ]), None)]), None, None) - ]), None)]), None, None) - - ]), None)]), None, None) - ]), None)]), None, None) - ]), None)]), None, None), - - (Token.OP, _OPERATORS['*'][1]), - (Token.OP, _OPERATORS['+'][1]) - ]), None) - ])) - ] - self.assertEqual(list(jsi.statements()), ast) - - def test_assignments(self): - jsi = JSInterpreter('var x = 20; x = 30 + 1; return x;') - ast = [ - (Token.VAR, zip( - ['x'], - [(Token.ASSIGN, - None, - (Token.OPEXPR, [(Token.MEMBER, (Token.INT, 20), None, None)]), - None)] - )), - - (Token.EXPR, [ - (Token.ASSIGN, - _ASSIGN_OPERATORS['='][1], - (Token.OPEXPR, [(Token.MEMBER, (Token.ID, 'x'), None, None)]), - (Token.ASSIGN, None, - (Token.OPEXPR, [ - (Token.MEMBER, (Token.INT, 30), None, None), - (Token.MEMBER, (Token.INT, 1), None, None), - (Token.OP, _OPERATORS['+'][1])]), - None)) - ]), - - (Token.RETURN, (Token.EXPR, [ - (Token.ASSIGN, None, - (Token.OPEXPR, [ - (Token.MEMBER, (Token.ID, 'x'), None, None) - ]), None) - ])) - ] - self.assertEqual(list(traverse(list(jsi.statements()))), list(traverse(ast))) - - jsi = JSInterpreter('var x = 20; x += 30 + 1; return x;') - ast[1] = (Token.EXPR, [ - (Token.ASSIGN, - _ASSIGN_OPERATORS['+='][1], - (Token.OPEXPR, [(Token.MEMBER, (Token.ID, 'x'), None, None)]), - (Token.ASSIGN, None, - (Token.OPEXPR, [ - (Token.MEMBER, (Token.INT, 30), None, None), - (Token.MEMBER, (Token.INT, 1), None, None), - (Token.OP, _OPERATORS['+'][1])]), - None)) - ]) - self.assertEqual(list(traverse(list(jsi.statements()))), list(traverse(ast))) - - jsi = JSInterpreter('var x = 20; x -= 30 + 1; return x;') - ast[1] = (Token.EXPR, [ - (Token.ASSIGN, - _ASSIGN_OPERATORS['-='][1], - (Token.OPEXPR, [(Token.MEMBER, (Token.ID, 'x'), None, None)]), - (Token.ASSIGN, None, - (Token.OPEXPR, [ - (Token.MEMBER, (Token.INT, 30), None, None), - (Token.MEMBER, (Token.INT, 1), None, None), - (Token.OP, _OPERATORS['+'][1])]), - None)) - ]) - self.assertEqual(list(traverse(list(jsi.statements()))), list(traverse(ast))) - - def test_comments(self): - # var x = 2; var y = 50; return x + y; - jsi = JSInterpreter('var x = /* 1 + */ 2; var y = /* 30 * 40 */ 50; return x + y;') - ast = [ - (Token.VAR, zip( - ['x'], - [(Token.ASSIGN, - None, - (Token.OPEXPR, [(Token.MEMBER, (Token.INT, 2), None, None)]), - None)] - )), - - (Token.VAR, zip( - ['y'], - [(Token.ASSIGN, - None, - (Token.OPEXPR, [(Token.MEMBER, (Token.INT, 50), None, None)]), - None)] - )), - - (Token.RETURN, (Token.EXPR, [ - (Token.ASSIGN, None, - (Token.OPEXPR, [ - (Token.MEMBER, (Token.ID, 'x'), None, None), - (Token.MEMBER, (Token.ID, 'y'), None, None), - (Token.OP, _OPERATORS['+'][1]) - ]), None) - ])) - ] - self.assertEqual(list(traverse(list(jsi.statements()))), list(traverse(ast))) - - # var x = "/*"; var y = 1 + 2; return y; - jsi = JSInterpreter('var x = "/*"; var y = 1 /* comment */ + 2; return y;') - ast = [ - (Token.VAR, zip( - ['x'], - [(Token.ASSIGN, - None, - (Token.OPEXPR, [(Token.MEMBER, (Token.STR, '/*'), None, None)]), - None)] - )), - - (Token.VAR, zip( - ['y'], - [(Token.ASSIGN, - None, - (Token.OPEXPR, [ - (Token.MEMBER, (Token.INT, 1), None, None), - (Token.MEMBER, (Token.INT, 2), None, None), - (Token.OP, _OPERATORS['+'][1]) - ]), - None)] - )), - - (Token.RETURN, (Token.EXPR, [ - (Token.ASSIGN, None, - (Token.OPEXPR, [(Token.MEMBER, (Token.ID, 'y'), None, None)]), - None) - ])) - ] - self.assertEqual(list(traverse(list(jsi.statements()))), list(traverse(ast))) - - def test_precedence(self): - jsi = JSInterpreter(' var a = [10, 20, 30, 40, 50]; var b = 6; a[0]=a[b%a.length]; return a;') - ast = [ - (Token.VAR, - zip(['a'], - [(Token.ASSIGN, - None, - (Token.OPEXPR, [ - (Token.MEMBER, (Token.ARRAY, [ - (Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.INT, 10), None, None)]), None), - (Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.INT, 20), None, None)]), None), - (Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.INT, 30), None, None)]), None), - (Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.INT, 40), None, None)]), None), - (Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.INT, 50), None, None)]), None) - ]), None, None), - ]), - None) - ]) - ), - (Token.VAR, - zip(['b'], - [(Token.ASSIGN, None, (Token.OPEXPR, [(Token.MEMBER, (Token.INT, 6), None, None)]), None)] - ) - ), - (Token.EXPR, [ - (Token.ASSIGN, - _ASSIGN_OPERATORS['='][1], - (Token.OPEXPR, [ - (Token.MEMBER, (Token.ID, 'a'), - None, - (Token.ELEM, - (Token.EXPR, [ - (Token.ASSIGN, - None, - (Token.OPEXPR, [(Token.MEMBER, (Token.INT, 0), None, None)]), - None) - ]), - None)) - ]), - (Token.ASSIGN, - None, - (Token.OPEXPR, [ - (Token.MEMBER, (Token.ID, 'a'), - None, - (Token.ELEM, (Token.EXPR, [ - (Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.ID, 'b'), None, None), - (Token.MEMBER, (Token.ID, 'a'), None, (Token.FIELD, 'length', None)), - (Token.OP, _OPERATORS['%'][1]) - ]), None)]), - None)) - ]), - None) - ) - ]), - (Token.RETURN, - (Token.EXPR, [ - (Token.ASSIGN, None, (Token.OPEXPR, [(Token.MEMBER, (Token.ID, 'a'), None, None)]), None) - ]) - ) - ] - self.assertEqual(list(traverse(list(jsi.statements()))), list(traverse(ast))) - - def test_call(self): - jsi = JSInterpreter(''' - function x() { return 2; } - function y(a) { return x() + a; } - function z() { return y(3); } - ''') - - ast = [ - (Token.FUNC, 'x', - [], - (Token.BLOCK, [ - (Token.RETURN, (Token.EXPR, [ - (Token.ASSIGN, None, (Token.OPEXPR, [(Token.MEMBER, (Token.INT, 2), None, None)]), None) - ]) - ) - ])), - (Token.FUNC, 'y', - ['a'], - (Token.BLOCK, [ - (Token.RETURN, (Token.EXPR, [ - (Token.ASSIGN, None, - (Token.OPEXPR, [ - (Token.MEMBER, (Token.ID, 'x'), None, (Token.CALL, [], None)), - (Token.MEMBER, (Token.ID, 'a'), None, None), - (Token.OP, _OPERATORS['+'][1]) - ]), None) - ]) - ) - ])), - (Token.FUNC, 'z', - [], - (Token.BLOCK, [ - (Token.RETURN, (Token.EXPR, [ - (Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.ID, 'y'), None, (Token.CALL, [ - (Token.ASSIGN, None, (Token.OPEXPR, [(Token.MEMBER, (Token.INT, 3), None, None)]), None) - ], None)) - ]), None) - ]) - ) - ])) - ] - self.assertEqual(list(jsi.statements()), ast) - jsi = JSInterpreter('function x(a) { return a.split(""); }', variables={'a': 'abc'}) - ast = [ - (Token.FUNC, 'x', - ['a'], - (Token.BLOCK, [ - (Token.RETURN, (Token.EXPR, [ - (Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.ID, 'a'), None, - (Token.FIELD, 'split', - (Token.CALL, [ - (Token.ASSIGN, None, (Token.OPEXPR, [(Token.MEMBER, (Token.STR, ''), None, None)]), None) - ], None)) - )]), - None) - ]) - ) - ])) - ] - self.assertEqual(list(jsi.statements()), ast) - - def test_complex_call(self): - jsi = JSInterpreter(''' - function a(x) { return x; } - function b(x) { return x; } - function c() { return [a, b][0](0); } - ''') - ast = [ - (Token.FUNC, 'a', - ['x'], - (Token.BLOCK, [ - (Token.RETURN, (Token.EXPR, [ - (Token.ASSIGN, None, (Token.OPEXPR, [(Token.MEMBER, (Token.ID, 'x'), None, None)]), None) - ]) - ) - ])), - (Token.FUNC, 'b', - ['x'], - (Token.BLOCK, [ - (Token.RETURN, (Token.EXPR, [ - (Token.ASSIGN, None, (Token.OPEXPR, [(Token.MEMBER, (Token.ID, 'x'), None, None)]), None) - ]) - ) - ])), - (Token.FUNC, 'c', - [], - (Token.BLOCK, [ - (Token.RETURN, (Token.EXPR, [ - (Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.ARRAY, [ - (Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.ID, 'a'), None, None)]), None), - (Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.ID, 'b'), None, None)]), None) - ]), None, (Token.ELEM, (Token.EXPR, [ - (Token.ASSIGN, None, (Token.OPEXPR, [(Token.MEMBER, (Token.INT, 0), None, None)]), None) - ]), (Token.CALL, [ - (Token.ASSIGN, None, (Token.OPEXPR, [(Token.MEMBER, (Token.INT, 0), None, None)]), None) - ], None))) - ]), None) - ]) - ) - ])), - ] - self.assertEqual(list(jsi.statements()), ast) - - def test_getfield(self): - jsi = JSInterpreter('return a.var;', variables={'a': {'var': 3}}) - ast = [(Token.RETURN, - (Token.EXPR, [ - (Token.ASSIGN, - None, - (Token.OPEXPR, [ - (Token.MEMBER, - (Token.ID, 'a'), - None, - (Token.FIELD, 'var', None)), - ]), - None) - ])) - ] - self.assertEqual(list(jsi.statements()), ast) - - def test_if(self): - jsi = JSInterpreter( - ''' - function a(x) { - if (x > 0) - return true; - else - return false; - } - ''' - ) - ast = [ - (Token.FUNC, 'a', - ['x'], - (Token.BLOCK, [ - (Token.IF, - (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.ID, 'x'), None, None), - (Token.MEMBER, (Token.INT, 0), None, None), - (Token.REL, _RELATIONS['>'][1]) - ]), None)]), - (Token.RETURN, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.BOOL, True), None, None)]), None)])), - (Token.RETURN, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.BOOL, False), None, None)]), None)]))) - - ])) - ] - self.assertEqual(list(jsi.statements()), ast) - - jsi = JSInterpreter( - ''' - function a(x) { - if (x > 0) - return true; - return false; - } - ''' - ) - ast = [ - (Token.FUNC, 'a', - ['x'], - (Token.BLOCK, [ - (Token.IF, - (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.ID, 'x'), None, None), - (Token.MEMBER, (Token.INT, 0), None, None), - (Token.REL, _RELATIONS['>'][1]) - ]), None)]), - (Token.RETURN, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.BOOL, True), None, None)]), None)])), - None), - (Token.RETURN, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.BOOL, False), None, None)]), None)])) - ])) - ] - self.assertEqual(list(jsi.statements()), ast) - - jsi = JSInterpreter( - ''' - function a(x) { - if (x > 0) { - x--; - return x; - } else { - x++; - return x; - } - } - ''' - ) - ast = [ - (Token.FUNC, 'a', - ['x'], - (Token.BLOCK, [ - (Token.IF, - (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.ID, 'x'), None, None), - (Token.MEMBER, (Token.INT, 0), None, None), - (Token.REL, _RELATIONS['>'][1]) - ]), None)]), - (Token.BLOCK, [ - (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.ID, 'x'), None, None), - (Token.UOP, _UNARY_OPERATORS['--'][1]) - ]), None)]), - (Token.RETURN, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.ID, 'x'), None, None)]), None)])) - ]), - (Token.BLOCK, [ - (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.ID, 'x'), None, None), - (Token.UOP, _UNARY_OPERATORS['++'][1]) - ]), None)]), - (Token.RETURN, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.ID, 'x'), None, None)]), None)])) - ])) - ])) - ] - self.assertEqual(list(jsi.statements()), ast) - - def test_switch(self): - jsi = JSInterpreter( - ''' - function a(x) { - switch (x) { - case 6: - break; - case 5: - x++; - case 8: - x--; - break; - default: - x = 0; - } - return x; - } - ''' - ) - ast = [ - (Token.FUNC, 'a', ['x'], - (Token.BLOCK, [ - (Token.SWITCH, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.ID, 'x'), None, None) - ]), None)]), - [ - ((Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.INT, 6), None, None)]), None)]), - [ - (Token.BREAK, None) - ]), - ((Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.INT, 5), None, None)]), None)]), - [ - (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.ID, 'x'), None, None), - (Token.UOP, _UNARY_OPERATORS['++'][1]) - ]), None)]) - ]), - ((Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.INT, 8), None, None)]), None)]), - [ - (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.ID, 'x'), None, None), - (Token.UOP, _UNARY_OPERATORS['--'][1]) - ]), None)]), - (Token.BREAK, None) - ]), - (None, - [ - (Token.EXPR, [ - (Token.ASSIGN, - _ASSIGN_OPERATORS['='][1], - (Token.OPEXPR, [(Token.MEMBER, (Token.ID, 'x'), None, None)]), - (Token.ASSIGN, None, (Token.OPEXPR, [(Token.MEMBER, (Token.INT, 0), None, None)]), None) - ) - ]) - ]) - ] - ), - (Token.RETURN, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.ID, 'x'), None, None)]), None)])) - ])) - ] - self.assertEqual(list(jsi.statements()), ast) - - def test_for(self): - jsi = JSInterpreter(''' - function f(x){ - for (var h = 0; h <= x; ++h) { - a = h; - } - return a; - } - ''') - ast = [ - (Token.FUNC, 'f', ['x'], - (Token.BLOCK, [ - (Token.FOR, - (Token.VAR, zip(['h'], [ - (Token.ASSIGN, None, (Token.OPEXPR, [(Token.MEMBER, (Token.INT, 0), None, None)]), None) - ])), - (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.ID, 'h'), None, None), - (Token.MEMBER, (Token.ID, 'x'), None, None), - (Token.REL, _RELATIONS['<='][1]) - ]), None)]), - (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.ID, 'h'), None, None), - (Token.UOP, _UNARY_OPERATORS['++'][1]) - ]), None)]), - (Token.BLOCK, [ - (Token.EXPR, [ - (Token.ASSIGN, _ASSIGN_OPERATORS['='][1], - (Token.OPEXPR, [(Token.MEMBER, (Token.ID, 'a'), None, None)]), - (Token.ASSIGN, None, (Token.OPEXPR, [(Token.MEMBER, (Token.ID, 'h'), None, None)]), None)) - ]) - ])), - (Token.RETURN, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.ID, 'a'), None, None)]), None)])) - ])) - ] - self.assertEqual(list(traverse(list(jsi.statements()))), list(traverse(ast))) - - def test_for_empty(self): - jsi = JSInterpreter(''' - function f(x){ - var h = 0; - for (; h <= x; ++h) { - a = h; - } - return a; - } - ''') - ast = [ - (Token.FUNC, 'f', ['x'], - (Token.BLOCK, [ - (Token.VAR, zip(['h'], [ - (Token.ASSIGN, None, (Token.OPEXPR, [(Token.MEMBER, (Token.INT, 0), None, None)]), None) - ])), - (Token.FOR, - None, - (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.ID, 'h'), None, None), - (Token.MEMBER, (Token.ID, 'x'), None, None), - (Token.REL, _RELATIONS['<='][1]) - ]), None)]), - (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.ID, 'h'), None, None), - (Token.UOP, _UNARY_OPERATORS['++'][1]) - ]), None)]), - (Token.BLOCK, [ - (Token.EXPR, [ - (Token.ASSIGN, _ASSIGN_OPERATORS['='][1], - (Token.OPEXPR, [(Token.MEMBER, (Token.ID, 'a'), None, None)]), - (Token.ASSIGN, None, (Token.OPEXPR, [(Token.MEMBER, (Token.ID, 'h'), None, None)]), None)) - ]) - ])), - (Token.RETURN, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.ID, 'a'), None, None)]), None)])) - ])) - ] - self.assertEqual(list(traverse(list(jsi.statements()))), list(traverse(ast))) - - def test_for_in(self): - jsi = JSInterpreter(''' - function f(z){ - for (h in z) { - a = h; - } - return a; - } - ''') - ast = [ - (Token.FUNC, 'f', ['z'], - (Token.BLOCK, [ - (Token.FOR, - (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.ID, 'h'), None, None) - ]), None)]), - (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.ID, 'z'), None, None) - ]), None)]), - None, - (Token.BLOCK, [ - (Token.EXPR, [ - (Token.ASSIGN, _ASSIGN_OPERATORS['='][1], - (Token.OPEXPR, [(Token.MEMBER, (Token.ID, 'a'), None, None)]), - (Token.ASSIGN, None, (Token.OPEXPR, [(Token.MEMBER, (Token.ID, 'h'), None, None)]), None)) - ]) - ])), - (Token.RETURN, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.ID, 'a'), None, None)]), None)])) - ])) - ] - self.assertEqual(list(jsi.statements()), ast) - - def test_do(self): - jsi = JSInterpreter(''' - function f(x){ - i = 1; - do{ - i++; - } while (i < x); - return i; - } - ''') - ast = [ - (Token.FUNC, 'f', ['x'], - (Token.BLOCK, [ - (Token.EXPR, [ - (Token.ASSIGN, _ASSIGN_OPERATORS['='][1], - (Token.OPEXPR, [(Token.MEMBER, (Token.ID, 'i'), None, None)]), - (Token.ASSIGN, None, (Token.OPEXPR, [(Token.MEMBER, (Token.INT, 1), None, None)]), None)) - ]), - (Token.DO, - (Token.EXPR, [ - (Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.ID, 'i'), None, None), - (Token.MEMBER, (Token.ID, 'x'), None, None), - (Token.REL, _RELATIONS['<'][1]) - ]), None) - ]), - (Token.BLOCK, [ - (Token.EXPR, [ - (Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.ID, 'i'), None, None), - (Token.UOP, _UNARY_OPERATORS['++'][1]) - ]), None) - ]) - ])), - (Token.RETURN, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.ID, 'i'), None, None)]), None)])) - ])) - ] - self.assertEqual(list(jsi.statements()), ast) - - def test_while(self): - jsi = JSInterpreter(''' - function f(x){ - i = 1; - while (i < x) { - i++; - } - return i; - } - ''') - ast = [ - (Token.FUNC, 'f', ['x'], - (Token.BLOCK, [ - (Token.EXPR, [ - (Token.ASSIGN, _ASSIGN_OPERATORS['='][1], - (Token.OPEXPR, [(Token.MEMBER, (Token.ID, 'i'), None, None)]), - (Token.ASSIGN, None, (Token.OPEXPR, [(Token.MEMBER, (Token.INT, 1), None, None)]), None)) - ]), - (Token.WHILE, - (Token.EXPR, [ - (Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.ID, 'i'), None, None), - (Token.MEMBER, (Token.ID, 'x'), None, None), - (Token.REL, _RELATIONS['<'][1]) - ]), None) - ]), - (Token.BLOCK, [ - (Token.EXPR, [ - (Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.ID, 'i'), None, None), - (Token.UOP, _UNARY_OPERATORS['++'][1]) - ]), None) - ]) - ])), - (Token.RETURN, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.ID, 'i'), None, None)]), None)])) - ])) - ] - self.assertEqual(list(jsi.statements()), ast) - - @unittest.skip('Test not yet implemented: missing code and ast') - def test_label(self): - # TODO label (break, continue) statement test - # might be combined with another - jsi = JSInterpreter('') - ast = [] - self.assertEqual(list(jsi.statements()), ast) - - def test_function_expression(self): - jsi = JSInterpreter(''' - function f() { - var add = (function () { - var counter = 0; - return function () {return counter += 1;}; - })(); - add(); - add(); - return add(); - } - ''') - ast = [ - (Token.FUNC, 'f', [], - (Token.BLOCK, [ - (Token.VAR, zip(['add'], [(Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.FUNC, None, [], (Token.BLOCK, [ - (Token.VAR, zip( - ['counter'], - [(Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.INT, 0), None, None) - ]), None)] - )), - (Token.RETURN, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.FUNC, None, [], (Token.BLOCK, [ - (Token.RETURN, (Token.EXPR, [ - (Token.ASSIGN, _ASSIGN_OPERATORS['+='][1], (Token.OPEXPR, [ - (Token.MEMBER, (Token.ID, 'counter'), None, None) - ]), (Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.INT, 1), None, None) - ]), None)) - ])) - ])), None, None) - ]), None)])) - ])), None, None), - ]), None)]), None, (Token.CALL, [], None)) - ]), None)])), - (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.ID, 'add'), None, (Token.CALL, [], None)) - ]), None)]), - (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.ID, 'add'), None, (Token.CALL, [], None)) - ]), None)]), - (Token.RETURN, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.ID, 'add'), None, (Token.CALL, [], None)) - ]), None)])) - ])) - ] - self.assertEqual(list(traverse(list(jsi.statements()))), list(traverse(ast))) - - def test_object(self): - jsi = JSInterpreter(''' - function f() { - var o = { - a: 7, - get b() { return this.a + 1; }, - set c(x) { this.a = x / 2; } - }; - return o; - } - ''') - ast = [ - (Token.FUNC, 'f', [], - (Token.BLOCK, [ - (Token.VAR, - zip(['o'], - [(Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.OBJECT, [ - ('a', (Token.PROPVALUE, (Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.INT, 7), None, None) - ]), None))), - ('b', (Token.PROPGET, (Token.BLOCK, [ - (Token.RETURN, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.RSV, 'this'), None, (Token.FIELD, 'a', None)), - (Token.MEMBER, (Token.INT, 1), None, None), - (Token.OP, _OPERATORS['+'][1]) - ]), None)])) - ]))), - ('c', (Token.PROPSET, 'x', (Token.BLOCK, [ - (Token.EXPR, [ - (Token.ASSIGN, - _ASSIGN_OPERATORS['='][1], - (Token.OPEXPR, [ - (Token.MEMBER, (Token.RSV, 'this'), None, (Token.FIELD, 'a', None)) - ]), - (Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.ID, 'x'), None, None), - (Token.MEMBER, (Token.INT, 2), None, None), - (Token.OP, _OPERATORS['/'][1]) - ]), None)) - ]) - ]))) - ]), - None, None) - ]), None)] - ) - ), - (Token.RETURN, (Token.EXPR, [(Token.ASSIGN, None, (Token.OPEXPR, [ - (Token.MEMBER, (Token.ID, 'o'), None, None)]), None)])) - ])) - ] - self.assertEqual(list(traverse(list(jsi.statements()))), list(traverse(ast))) - - @unittest.skip('Test not yet implemented: missing code and ast') - def test_try(self): - # TODO try statement test - jsi = JSInterpreter('') - ast = [] - self.assertEqual(list(jsi.statements()), ast) - - @unittest.skip('Test not yet implemented: missing code and ast') - def test_throw(self): - # TODO throw statement test - # might be combined with another - jsi = JSInterpreter('') - ast = [] - self.assertEqual(list(jsi.statements()), ast) - - @unittest.skip('Test not yet implemented: missing code and ast') - def test_with(self): - # TODO with statement test - jsi = JSInterpreter('') - ast = [] - self.assertEqual(list(jsi.statements()), ast) - - @unittest.skip('Test not yet implemented: missing code and ast') - def test_debug(self): - # TODO debugger statement test - # might be combined with another - jsi = JSInterpreter('') - ast = [] - self.assertEqual(list(jsi.statements()), ast) - - def test_unshift(self): - # https://hg.mozilla.org/mozilla-central/file/tip/js/src/tests/ecma_5/Array/unshift-01.js - jsi = JSInterpreter( - '''var MAX_LENGTH = 0xffffffff; - - var a = {}; - a.length = MAX_LENGTH + 1; - assertEq([].unshift.call(a), MAX_LENGTH); - assertEq(a.length, MAX_LENGTH); - - function testGetSet(len, expected) { - var newlen; - var a = { get length() { return len; }, set length(v) { newlen = v; } }; - var res = [].unshift.call(a); - assertEq(res, expected); - assertEq(newlen, expected); - } - - testGetSet(0, 0); - testGetSet(10, 10); - testGetSet("1", 1); - testGetSet(null, 0); - testGetSet(MAX_LENGTH + 2, MAX_LENGTH); - testGetSet(-5, 0);''') - jsi.statements() - -if __name__ == '__main__': - unittest.main() diff --git a/youtube_dl/jsinterp/jsgrammar.py b/youtube_dl/jsinterp/jsgrammar.py index 9eb0b64a4..b44714bcb 100644 --- a/youtube_dl/jsinterp/jsgrammar.py +++ b/youtube_dl/jsinterp/jsgrammar.py @@ -48,6 +48,7 @@ _SINGLE_QUOTED_RE = r"""'(?:(?:\\'|\n)|[^'\n])*'""" _DOUBLE_QUOTED_RE = r'''"(?:(?:\\"|\n)|[^"\n])*"''' _STRING_RE = r'(?:%s)|(?:%s)' % (_SINGLE_QUOTED_RE, _DOUBLE_QUOTED_RE) +# FIXME signed values _INTEGER_RE = r'(?:%(hex)s)|(?:%(dec)s)|(?:%(oct)s)' % {'hex': __HEXADECIMAL_RE, 'dec': __DECIMAL_RE, 'oct': __OCTAL_RE} _FLOAT_RE = r'(?:(?:%(dec)s\.[0-9]*)|(?:\.[0-9]+))(?:[eE][+-]?[0-9]+)?' % {'dec': __DECIMAL_RE} diff --git a/youtube_dl/jsinterp/jsinterp.py b/youtube_dl/jsinterp/jsinterp.py index db9f14625..52e3dc2bf 100644 --- a/youtube_dl/jsinterp/jsinterp.py +++ b/youtube_dl/jsinterp/jsinterp.py @@ -55,7 +55,7 @@ class JSInterpreter(object): for k, v in dict(variables).items(): # XXX validate identifiers self.global_vars[k] = Reference(v, (self.global_vars, k)) - self._context = Context(self.global_vars) + self._context = Context() self._context_stack = [] def statements(self, code=None, pos=0, stack_size=100): @@ -920,7 +920,8 @@ class JSInterpreter(object): elif name is Token.ID: # XXX error handling (unknown id) - ref = self._context.local_vars[expr[1]] if expr[1] in self._context.local_vars else self.global_vars[expr[1]] + ref = (self._context.local_vars[expr[1]] if expr[1] in self._context.local_vars else + self.global_vars[expr[1]]) # literal elif name in _token_keys: @@ -939,6 +940,18 @@ class JSInterpreter(object): return ref + def run(self, cx=None): + if cx is not None: + self.push_context(cx) + res = None + for stmt in self.statements(): + res = self.interpret_statement(stmt) + if self._context.ended: + if cx is not None: + self.pop_context() + break + return res + def extract_object(self, objname): obj = {} obj_m = re.search( diff --git a/youtube_dl/jsinterp/tstream.py b/youtube_dl/jsinterp/tstream.py index 4e72774b4..36a22d09e 100644 --- a/youtube_dl/jsinterp/tstream.py +++ b/youtube_dl/jsinterp/tstream.py @@ -124,7 +124,10 @@ class TokenStream(object): elif token_id is Token.STR: yield (token_id, token_value[1:-1], pos) elif token_id is Token.INT: - yield (token_id, int(token_value), pos) + # FIXME signed values + root = ((16 if len(token_value) > 2 and token_value[1] in 'xX' else 8) + if token_value.startswith('0') else 10) + yield (token_id, int(token_value, root), pos) elif token_id is Token.FLOAT: yield (token_id, float(token_value), pos) elif token_id is Token.REGEX: @@ -142,6 +145,8 @@ class TokenStream(object): else: raise ExtractorError('Unexpected token at %d' % pos) pos = feed_m.end() + elif pos >= len(self.code): + self.ended = True else: raise ExtractorError('Unrecognised sequence at %d' % pos) raise StopIteration