aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authordirkf <[email protected]>2024-12-12 04:16:07 +0000
committerdirkf <[email protected]>2024-12-16 12:38:51 +0000
commit60835ca16c052eb00bb9bccd44f8843edac66a2e (patch)
tree26662aa112d0c99fcf1bed983ce32ddc80381b74
parent94fd7746084d87a43e34b094c5db1325f91ce053 (diff)
downloadyoutube-dl-60835ca16c052eb00bb9bccd44f8843edac66a2e.tar.gz
youtube-dl-60835ca16c052eb00bb9bccd44f8843edac66a2e.zip
[jsinterp] Fix and improve "methods"
* push, unshift return new length * impove edge cases for push/pop, shift/unshift, forEach, indexOf, charCodeAt * increase test coverage
-rw-r--r--test/test_jsinterp.py34
-rw-r--r--youtube_dl/jsinterp.py36
2 files changed, 49 insertions, 21 deletions
diff --git a/test/test_jsinterp.py b/test/test_jsinterp.py
index b6e87e9f1..07ed481d8 100644
--- a/test/test_jsinterp.py
+++ b/test/test_jsinterp.py
@@ -544,6 +544,40 @@ class TestJSInterpreter(unittest.TestCase):
self._test('function f(){return "012345678".slice(-1, 1)}', '')
self._test('function f(){return "012345678".slice(-3, -1)}', '67')
+ def test_pop(self):
+ # pop
+ self._test('function f(){var a = [0, 1, 2, 3, 4, 5, 6, 7, 8]; return [a.pop(), a]}',
+ [8, [0, 1, 2, 3, 4, 5, 6, 7]])
+ self._test('function f(){return [].pop()}', JS_Undefined)
+ # push
+ self._test('function f(){var a = [0, 1, 2]; return [a.push(3, 4), a]}',
+ [5, [0, 1, 2, 3, 4]])
+ self._test('function f(){var a = [0, 1, 2]; return [a.push(), a]}',
+ [3, [0, 1, 2]])
+
+ def test_shift(self):
+ # shift
+ self._test('function f(){var a = [0, 1, 2, 3, 4, 5, 6, 7, 8]; return [a.shift(), a]}',
+ [0, [1, 2, 3, 4, 5, 6, 7, 8]])
+ self._test('function f(){return [].shift()}', JS_Undefined)
+ # unshift
+ self._test('function f(){var a = [0, 1, 2]; return [a.unshift(3, 4), a]}',
+ [5, [3, 4, 0, 1, 2]])
+ self._test('function f(){var a = [0, 1, 2]; return [a.unshift(), a]}',
+ [3, [0, 1, 2]])
+
+ def test_forEach(self):
+ self._test('function f(){var ret = []; var l = [4, 2]; '
+ 'var log = function(e,i,a){ret.push([e,i,a]);}; '
+ 'l.forEach(log); '
+ 'return [ret.length, ret[0][0], ret[1][1], ret[0][2]]}',
+ [2, 4, 1, [4, 2]])
+ self._test('function f(){var ret = []; var l = [4, 2]; '
+ 'var log = function(e,i,a){this.push([e,i,a]);}; '
+ 'l.forEach(log, ret); '
+ 'return [ret.length, ret[0][0], ret[1][1], ret[0][2]]}',
+ [2, 4, 1, [4, 2]])
+
if __name__ == '__main__':
unittest.main()
diff --git a/youtube_dl/jsinterp.py b/youtube_dl/jsinterp.py
index bec959946..0cfae4b28 100644
--- a/youtube_dl/jsinterp.py
+++ b/youtube_dl/jsinterp.py
@@ -1113,37 +1113,31 @@ class JSInterpreter(object):
index, how_many = map(int, (argvals + [len(obj)])[:2])
if index < 0:
index += len(obj)
- add_items = argvals[2:]
- res = []
- for _ in range(index, min(index + how_many, len(obj))):
- res.append(obj.pop(index))
- for i, item in enumerate(add_items):
- obj.insert(index + i, item)
+ res = [obj.pop(index)
+ for _ in range(index, min(index + how_many, len(obj)))]
+ obj[index:index] = argvals[2:]
return res
- elif member == 'unshift':
- assertion(isinstance(obj, list), 'must be applied on a list')
- assertion(argvals, 'takes one or more arguments')
- for item in reversed(argvals):
- obj.insert(0, item)
- return obj
- elif member == 'pop':
+ elif member in ('shift', 'pop'):
assertion(isinstance(obj, list), 'must be applied on a list')
assertion(not argvals, 'does not take any arguments')
- if not obj:
- return
- return obj.pop()
+ return obj.pop(0 if member == 'shift' else -1) if len(obj) > 0 else JS_Undefined
+ elif member == 'unshift':
+ assertion(isinstance(obj, list), 'must be applied on a list')
+ # not enforced: assertion(argvals, 'takes one or more arguments')
+ obj[0:0] = argvals
+ return len(obj)
elif member == 'push':
- assertion(argvals, 'takes one or more arguments')
+ # not enforced: assertion(argvals, 'takes one or more arguments')
obj.extend(argvals)
- return obj
+ return len(obj)
elif member == 'forEach':
assertion(argvals, 'takes one or more arguments')
- assertion(len(argvals) <= 2, 'takes at-most 2 arguments')
+ assertion(len(argvals) <= 2, 'takes at most 2 arguments')
f, this = (argvals + [''])[:2]
return [f((item, idx, obj), {'this': this}, allow_recursion) for idx, item in enumerate(obj)]
elif member == 'indexOf':
assertion(argvals, 'takes one or more arguments')
- assertion(len(argvals) <= 2, 'takes at-most 2 arguments')
+ assertion(len(argvals) <= 2, 'takes at most 2 arguments')
idx, start = (argvals + [0])[:2]
try:
return obj.index(idx, start)
@@ -1152,7 +1146,7 @@ class JSInterpreter(object):
elif member == 'charCodeAt':
assertion(isinstance(obj, compat_str), 'must be applied on a string')
# assertion(len(argvals) == 1, 'takes exactly one argument') # but not enforced
- idx = argvals[0] if isinstance(argvals[0], int) else 0
+ idx = argvals[0] if len(argvals) > 0 and isinstance(argvals[0], int) else 0
if idx >= len(obj):
return None
return ord(obj[idx])