mirror of
https://github.com/ytdl-org/youtube-dl
synced 2025-04-20 22:14:50 +09:00
[JSInterp] Add tests and relevant functionality from yt-dlp
* thx seproDev, bashonly: yt-dlp/yt-dlp#12760, yt-dlp/yt-dlp#12761: - Improve nested attribute support - Pass global stack when extracting objects - interpret_statement: Match attribute before indexing - Fix assignment to array elements with nested brackets - Add new signature tests - Invalidate JS function cache - Avoid testdata dupes now that we cache by URL * rework nsig function name search * fully fixes #33102 * update cache required versions * update program version
This commit is contained in:
parent
bd2ded59f2
commit
c052a16f72
@ -180,6 +180,7 @@ class TestJSInterpreter(unittest.TestCase):
|
|||||||
self._test('function f(){var x = 20; x = 30 + 1; return x;}', 31)
|
self._test('function f(){var x = 20; x = 30 + 1; return x;}', 31)
|
||||||
self._test('function f(){var x = 20; x += 30 + 1; return x;}', 51)
|
self._test('function f(){var x = 20; x += 30 + 1; return x;}', 51)
|
||||||
self._test('function f(){var x = 20; x -= 30 + 1; return x;}', -11)
|
self._test('function f(){var x = 20; x -= 30 + 1; return x;}', -11)
|
||||||
|
self._test('function f(){var x = 2; var y = ["a", "b"]; y[x%y["length"]]="z"; return y}', ['z', 'b'])
|
||||||
|
|
||||||
def test_comments(self):
|
def test_comments(self):
|
||||||
self._test('''
|
self._test('''
|
||||||
@ -552,6 +553,8 @@ class TestJSInterpreter(unittest.TestCase):
|
|||||||
test_result = list('test')
|
test_result = list('test')
|
||||||
tests = [
|
tests = [
|
||||||
'function f(a, b){return a.split(b)}',
|
'function f(a, b){return a.split(b)}',
|
||||||
|
'function f(a, b){return a["split"](b)}',
|
||||||
|
'function f(a, b){let x = ["split"]; return a[x[0]](b)}',
|
||||||
'function f(a, b){return String.prototype.split.call(a, b)}',
|
'function f(a, b){return String.prototype.split.call(a, b)}',
|
||||||
'function f(a, b){return String.prototype.split.apply(a, [b])}',
|
'function f(a, b){return String.prototype.split.apply(a, [b])}',
|
||||||
]
|
]
|
||||||
@ -602,6 +605,9 @@ class TestJSInterpreter(unittest.TestCase):
|
|||||||
self._test('function f(){return "012345678".slice(-1, 1)}', '')
|
self._test('function f(){return "012345678".slice(-1, 1)}', '')
|
||||||
self._test('function f(){return "012345678".slice(-3, -1)}', '67')
|
self._test('function f(){return "012345678".slice(-3, -1)}', '67')
|
||||||
|
|
||||||
|
def test_splice(self):
|
||||||
|
self._test('function f(){var T = ["0", "1", "2"]; T["splice"](2, 1, "0")[0]; return T }', ['0', '1', '0'])
|
||||||
|
|
||||||
def test_pop(self):
|
def test_pop(self):
|
||||||
# pop
|
# pop
|
||||||
self._test('function f(){var a = [0, 1, 2, 3, 4, 5, 6, 7, 8]; return [a.pop(), a]}',
|
self._test('function f(){var a = [0, 1, 2, 3, 4, 5, 6, 7, 8]; return [a.pop(), a]}',
|
||||||
@ -636,6 +642,16 @@ class TestJSInterpreter(unittest.TestCase):
|
|||||||
'return [ret.length, ret[0][0], ret[1][1], ret[0][2]]}',
|
'return [ret.length, ret[0][0], ret[1][1], ret[0][2]]}',
|
||||||
[2, 4, 1, [4, 2]])
|
[2, 4, 1, [4, 2]])
|
||||||
|
|
||||||
|
def test_extract_function(self):
|
||||||
|
jsi = JSInterpreter('function a(b) { return b + 1; }')
|
||||||
|
func = jsi.extract_function('a')
|
||||||
|
self.assertEqual(func([2]), 3)
|
||||||
|
|
||||||
|
def test_extract_function_with_global_stack(self):
|
||||||
|
jsi = JSInterpreter('function c(d) { return d + e + f + g; }')
|
||||||
|
func = jsi.extract_function('c', {'e': 10}, {'f': 100, 'g': 1000})
|
||||||
|
self.assertEqual(func([1]), 1111)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
@ -94,11 +94,51 @@ _SIG_TESTS = [
|
|||||||
'2aq0aqSyOoJXtK73m-uME_jv7-pT15gOFC02RFkGMqWpzEICs69VdbwQ0LDp1v7j8xx92efCJlYFYb1sUkkBSPOlPmXgIARw8JQ0qOAOAA',
|
'2aq0aqSyOoJXtK73m-uME_jv7-pT15gOFC02RFkGMqWpzEICs69VdbwQ0LDp1v7j8xx92efCJlYFYb1sUkkBSPOlPmXgIARw8JQ0qOAOAA',
|
||||||
'0aqSyOoJXtK73m-uME_jv7-pT15gOFC02RFkGMqWpz2ICs6EVdbwQ0LDp1v7j8xx92efCJlYFYb1sUkkBSPOlPmXgIARw8JQ0qOAOAA',
|
'0aqSyOoJXtK73m-uME_jv7-pT15gOFC02RFkGMqWpz2ICs6EVdbwQ0LDp1v7j8xx92efCJlYFYb1sUkkBSPOlPmXgIARw8JQ0qOAOAA',
|
||||||
),
|
),
|
||||||
|
(
|
||||||
|
'https://www.youtube.com/s/player/363db69b/player_ias_tce.vflset/en_US/base.js',
|
||||||
|
'2aq0aqSyOoJXtK73m-uME_jv7-pT15gOFC02RFkGMqWpzEICs69VdbwQ0LDp1v7j8xx92efCJlYFYb1sUkkBSPOlPmXgIARw8JQ0qOAOAA',
|
||||||
|
'0aqSyOoJXtK73m-uME_jv7-pT15gOFC02RFkGMqWpz2ICs6EVdbwQ0LDp1v7j8xx92efCJlYFYb1sUkkBSPOlPmXgIARw8JQ0qOAOAA',
|
||||||
|
),
|
||||||
(
|
(
|
||||||
'https://www.youtube.com/s/player/4fcd6e4a/player_ias.vflset/en_US/base.js',
|
'https://www.youtube.com/s/player/4fcd6e4a/player_ias.vflset/en_US/base.js',
|
||||||
'2aq0aqSyOoJXtK73m-uME_jv7-pT15gOFC02RFkGMqWpzEICs69VdbwQ0LDp1v7j8xx92efCJlYFYb1sUkkBSPOlPmXgIARw8JQ0qOAOAA',
|
'2aq0aqSyOoJXtK73m-uME_jv7-pT15gOFC02RFkGMqWpzEICs69VdbwQ0LDp1v7j8xx92efCJlYFYb1sUkkBSPOlPmXgIARw8JQ0qOAOAA',
|
||||||
'wAOAOq0QJ8ARAIgXmPlOPSBkkUs1bYFYlJCfe29xx8q7v1pDL0QwbdV96sCIEzpWqMGkFR20CFOg51Tp-7vj_EMu-m37KtXJoOySqa0',
|
'wAOAOq0QJ8ARAIgXmPlOPSBkkUs1bYFYlJCfe29xx8q7v1pDL0QwbdV96sCIEzpWqMGkFR20CFOg51Tp-7vj_EMu-m37KtXJoOySqa0',
|
||||||
),
|
),
|
||||||
|
(
|
||||||
|
'https://www.youtube.com/s/player/4fcd6e4a/player_ias_tce.vflset/en_US/base.js',
|
||||||
|
'2aq0aqSyOoJXtK73m-uME_jv7-pT15gOFC02RFkGMqWpzEICs69VdbwQ0LDp1v7j8xx92efCJlYFYb1sUkkBSPOlPmXgIARw8JQ0qOAOAA',
|
||||||
|
'wAOAOq0QJ8ARAIgXmPlOPSBkkUs1bYFYlJCfe29xx8q7v1pDL0QwbdV96sCIEzpWqMGkFR20CFOg51Tp-7vj_EMu-m37KtXJoOySqa0',
|
||||||
|
),
|
||||||
|
(
|
||||||
|
'https://www.youtube.com/s/player/20830619/player_ias.vflset/en_US/base.js',
|
||||||
|
'2aq0aqSyOoJXtK73m-uME_jv7-pT15gOFC02RFkGMqWpzEICs69VdbwQ0LDp1v7j8xx92efCJlYFYb1sUkkBSPOlPmXgIARw8JQ0qOAOAA',
|
||||||
|
'7AOq0QJ8wRAIgXmPlOPSBkkAs1bYFYlJCfe29xx8jOv1pDL0Q2bdV96sCIEzpWqMGkFR20CFOg51Tp-7vj_EMu-m37KtXJoOySqa0qaw',
|
||||||
|
),
|
||||||
|
(
|
||||||
|
'https://www.youtube.com/s/player/20830619/player_ias_tce.vflset/en_US/base.js',
|
||||||
|
'2aq0aqSyOoJXtK73m-uME_jv7-pT15gOFC02RFkGMqWpzEICs69VdbwQ0LDp1v7j8xx92efCJlYFYb1sUkkBSPOlPmXgIARw8JQ0qOAOAA',
|
||||||
|
'7AOq0QJ8wRAIgXmPlOPSBkkAs1bYFYlJCfe29xx8jOv1pDL0Q2bdV96sCIEzpWqMGkFR20CFOg51Tp-7vj_EMu-m37KtXJoOySqa0qaw',
|
||||||
|
),
|
||||||
|
(
|
||||||
|
'https://www.youtube.com/s/player/20830619/player-plasma-ias-phone-en_US.vflset/base.js',
|
||||||
|
'2aq0aqSyOoJXtK73m-uME_jv7-pT15gOFC02RFkGMqWpzEICs69VdbwQ0LDp1v7j8xx92efCJlYFYb1sUkkBSPOlPmXgIARw8JQ0qOAOAA',
|
||||||
|
'7AOq0QJ8wRAIgXmPlOPSBkkAs1bYFYlJCfe29xx8jOv1pDL0Q2bdV96sCIEzpWqMGkFR20CFOg51Tp-7vj_EMu-m37KtXJoOySqa0qaw',
|
||||||
|
),
|
||||||
|
(
|
||||||
|
'https://www.youtube.com/s/player/20830619/player-plasma-ias-tablet-en_US.vflset/base.js',
|
||||||
|
'2aq0aqSyOoJXtK73m-uME_jv7-pT15gOFC02RFkGMqWpzEICs69VdbwQ0LDp1v7j8xx92efCJlYFYb1sUkkBSPOlPmXgIARw8JQ0qOAOAA',
|
||||||
|
'7AOq0QJ8wRAIgXmPlOPSBkkAs1bYFYlJCfe29xx8jOv1pDL0Q2bdV96sCIEzpWqMGkFR20CFOg51Tp-7vj_EMu-m37KtXJoOySqa0qaw',
|
||||||
|
),
|
||||||
|
(
|
||||||
|
'https://www.youtube.com/s/player/8a8ac953/player_ias_tce.vflset/en_US/base.js',
|
||||||
|
'2aq0aqSyOoJXtK73m-uME_jv7-pT15gOFC02RFkGMqWpzEICs69VdbwQ0LDp1v7j8xx92efCJlYFYb1sUkkBSPOlPmXgIARw8JQ0qOAOAA',
|
||||||
|
'IAOAOq0QJ8wRAAgXmPlOPSBkkUs1bYFYlJCfe29xx8j7v1pDL0QwbdV96sCIEzpWqMGkFR20CFOg51Tp-7vj_E2u-m37KtXJoOySqa0',
|
||||||
|
),
|
||||||
|
(
|
||||||
|
'https://www.youtube.com/s/player/8a8ac953/tv-player-es6.vflset/tv-player-es6.js',
|
||||||
|
'2aq0aqSyOoJXtK73m-uME_jv7-pT15gOFC02RFkGMqWpzEICs69VdbwQ0LDp1v7j8xx92efCJlYFYb1sUkkBSPOlPmXgIARw8JQ0qOAOAA',
|
||||||
|
'IAOAOq0QJ8wRAAgXmPlOPSBkkUs1bYFYlJCfe29xx8j7v1pDL0QwbdV96sCIEzpWqMGkFR20CFOg51Tp-7vj_E2u-m37KtXJoOySqa0',
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
_NSIG_TESTS = [
|
_NSIG_TESTS = [
|
||||||
@ -272,7 +312,7 @@ _NSIG_TESTS = [
|
|||||||
),
|
),
|
||||||
(
|
(
|
||||||
'https://www.youtube.com/s/player/643afba4/player_ias.vflset/en_US/base.js',
|
'https://www.youtube.com/s/player/643afba4/player_ias.vflset/en_US/base.js',
|
||||||
'W9HJZKktxuYoDTqW', 'larxUlagTRAcSw',
|
'ir9-V6cdbCiyKxhr', '2PL7ZDYAALMfmA',
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
'https://www.youtube.com/s/player/363db69b/player_ias.vflset/en_US/base.js',
|
'https://www.youtube.com/s/player/363db69b/player_ias.vflset/en_US/base.js',
|
||||||
@ -286,6 +326,26 @@ _NSIG_TESTS = [
|
|||||||
'https://www.youtube.com/s/player/4fcd6e4a/tv-player-ias.vflset/tv-player-ias.js',
|
'https://www.youtube.com/s/player/4fcd6e4a/tv-player-ias.vflset/tv-player-ias.js',
|
||||||
'o_L251jm8yhZkWtBW', 'lXoxI3XvToqn6A',
|
'o_L251jm8yhZkWtBW', 'lXoxI3XvToqn6A',
|
||||||
),
|
),
|
||||||
|
(
|
||||||
|
'https://www.youtube.com/s/player/20830619/tv-player-ias.vflset/tv-player-ias.js',
|
||||||
|
'ir9-V6cdbCiyKxhr', '9YE85kNjZiS4',
|
||||||
|
),
|
||||||
|
(
|
||||||
|
'https://www.youtube.com/s/player/20830619/player-plasma-ias-phone-en_US.vflset/base.js',
|
||||||
|
'ir9-V6cdbCiyKxhr', '9YE85kNjZiS4',
|
||||||
|
),
|
||||||
|
(
|
||||||
|
'https://www.youtube.com/s/player/20830619/player-plasma-ias-tablet-en_US.vflset/base.js',
|
||||||
|
'ir9-V6cdbCiyKxhr', '9YE85kNjZiS4',
|
||||||
|
),
|
||||||
|
(
|
||||||
|
'https://www.youtube.com/s/player/8a8ac953/player_ias_tce.vflset/en_US/base.js',
|
||||||
|
'MiBYeXx_vRREbiCCmh', 'RtZYMVvmkE0JE',
|
||||||
|
),
|
||||||
|
(
|
||||||
|
'https://www.youtube.com/s/player/8a8ac953/tv-player-es6.vflset/tv-player-es6.js',
|
||||||
|
'MiBYeXx_vRREbiCCmh', 'RtZYMVvmkE0JE',
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@ -335,7 +395,7 @@ def t_factory(name, sig_func, url_pattern):
|
|||||||
test_id = re.sub(r'[/.-]', '_', m.group('id') or m.group('compat_id'))
|
test_id = re.sub(r'[/.-]', '_', m.group('id') or m.group('compat_id'))
|
||||||
|
|
||||||
def test_func(self):
|
def test_func(self):
|
||||||
basename = 'player-{0}-{1}.js'.format(name, test_id)
|
basename = 'player-{0}.js'.format(test_id)
|
||||||
fn = os.path.join(self.TESTDATA_DIR, basename)
|
fn = os.path.join(self.TESTDATA_DIR, basename)
|
||||||
|
|
||||||
if not os.path.exists(fn):
|
if not os.path.exists(fn):
|
||||||
|
@ -1652,7 +1652,7 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
|
|||||||
assert os.path.basename(func_id) == func_id
|
assert os.path.basename(func_id) == func_id
|
||||||
|
|
||||||
self.write_debug('Extracting signature function {0}'.format(func_id))
|
self.write_debug('Extracting signature function {0}'.format(func_id))
|
||||||
cache_spec, code = self.cache.load('youtube-sigfuncs', func_id), None
|
cache_spec, code = self.cache.load('youtube-sigfuncs', func_id, min_ver='2025.04.07'), None
|
||||||
|
|
||||||
if not cache_spec:
|
if not cache_spec:
|
||||||
code = self._load_player(video_id, player_url, player_id)
|
code = self._load_player(video_id, player_url, player_id)
|
||||||
@ -1813,6 +1813,10 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
|
|||||||
return ret
|
return ret
|
||||||
|
|
||||||
def _extract_n_function_name(self, jscode):
|
def _extract_n_function_name(self, jscode):
|
||||||
|
func_name, idx = None, None
|
||||||
|
# these special cases are redundant and probably obsolete (2025-04):
|
||||||
|
# they make the tests run ~10% faster without fallback warnings
|
||||||
|
r"""
|
||||||
func_name, idx = self._search_regex(
|
func_name, idx = self._search_regex(
|
||||||
# (y=NuD(),Mw(k),q=k.Z[y]||null)&&(q=narray[idx](q),k.set(y,q),k.V||NuD(''))}};
|
# (y=NuD(),Mw(k),q=k.Z[y]||null)&&(q=narray[idx](q),k.set(y,q),k.V||NuD(''))}};
|
||||||
# (R="nn"[+J.Z],mW(J),N=J.K[R]||null)&&(N=narray[idx](N),J.set(R,N))}};
|
# (R="nn"[+J.Z],mW(J),N=J.K[R]||null)&&(N=narray[idx](N),J.set(R,N))}};
|
||||||
@ -1839,9 +1843,28 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
|
|||||||
\(\s*[\w$]+\s*\)
|
\(\s*[\w$]+\s*\)
|
||||||
''', jscode, 'Initial JS player n function name', group=('nfunc', 'idx'),
|
''', jscode, 'Initial JS player n function name', group=('nfunc', 'idx'),
|
||||||
default=(None, None))
|
default=(None, None))
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not func_name:
|
||||||
|
# nfunc=function(x){...}|function nfunc(x); ...
|
||||||
|
# ... var y=[nfunc]|y[idx]=nfunc);
|
||||||
|
# obvious REs hang, so use a two-stage tactic
|
||||||
|
for m in re.finditer(r'''(?x)
|
||||||
|
[\n;]var\s(?:(?:(?!,).)+,|\s)*?(?!\d)[\w$]+(?:\[(?P<idx>\d+)\])?\s*=\s*
|
||||||
|
(?(idx)|\[\s*)(?P<nfunc>(?!\d)[\w$]+)(?(idx)|\s*\])
|
||||||
|
\s*?[;\n]
|
||||||
|
''', jscode):
|
||||||
|
func_name = self._search_regex(
|
||||||
|
r'[;,]\s*(function\s+)?({0})(?(1)|\s*=\s*function)\s*\((?!\d)[\w$]+\)\s*\{1}(?!\s*return\s)'.format(
|
||||||
|
re.escape(m.group('nfunc')), '{'),
|
||||||
|
jscode, 'Initial JS player n function name (2)', group=2, default=None)
|
||||||
|
if func_name:
|
||||||
|
idx = m.group('idx')
|
||||||
|
break
|
||||||
|
|
||||||
# thx bashonly: yt-dlp/yt-dlp/pull/10611
|
# thx bashonly: yt-dlp/yt-dlp/pull/10611
|
||||||
if not func_name:
|
if not func_name:
|
||||||
self.report_warning('Falling back to generic n function search')
|
self.report_warning('Falling back to generic n function search', only_once=True)
|
||||||
return self._search_regex(
|
return self._search_regex(
|
||||||
r'''(?xs)
|
r'''(?xs)
|
||||||
(?:(?<=[^\w$])|^) # instead of \b, which ignores $
|
(?:(?<=[^\w$])|^) # instead of \b, which ignores $
|
||||||
@ -1855,14 +1878,14 @@ class YoutubeIE(YoutubeBaseInfoExtractor):
|
|||||||
return func_name
|
return func_name
|
||||||
|
|
||||||
return self._search_json(
|
return self._search_json(
|
||||||
r'var\s+{0}\s*='.format(re.escape(func_name)), jscode,
|
r'(?<![\w-])var\s(?:(?:(?!,).)+,|\s)*?{0}\s*='.format(re.escape(func_name)), jscode,
|
||||||
'Initial JS player n function list ({0}.{1})'.format(func_name, idx),
|
'Initial JS player n function list ({0}.{1})'.format(func_name, idx),
|
||||||
func_name, contains_pattern=r'\[[\s\S]+\]', end_pattern='[,;]',
|
func_name, contains_pattern=r'\[.+\]', end_pattern='[,;]',
|
||||||
transform_source=js_to_json)[int(idx)]
|
transform_source=js_to_json)[int(idx)]
|
||||||
|
|
||||||
def _extract_n_function_code(self, video_id, player_url):
|
def _extract_n_function_code(self, video_id, player_url):
|
||||||
player_id = self._extract_player_info(player_url)
|
player_id = self._extract_player_info(player_url)
|
||||||
func_code = self.cache.load('youtube-nsig', player_id)
|
func_code = self.cache.load('youtube-nsig', player_id, min_ver='2025.04.07')
|
||||||
jscode = func_code or self._load_player(video_id, player_url)
|
jscode = func_code or self._load_player(video_id, player_url)
|
||||||
jsi = JSInterpreter(jscode)
|
jsi = JSInterpreter(jscode)
|
||||||
|
|
||||||
|
@ -303,8 +303,6 @@ _UNARY_OPERATORS_X = (
|
|||||||
('!', _js_unary_op(lambda x: _js_ternary(x, if_true=False, if_false=True))),
|
('!', _js_unary_op(lambda x: _js_ternary(x, if_true=False, if_false=True))),
|
||||||
)
|
)
|
||||||
|
|
||||||
_OPERATOR_RE = '|'.join(map(lambda x: re.escape(x[0]), _OPERATORS + _LOG_OPERATORS))
|
|
||||||
|
|
||||||
_COMP_OPERATORS = (
|
_COMP_OPERATORS = (
|
||||||
('===', _js_id_op(operator.is_)),
|
('===', _js_id_op(operator.is_)),
|
||||||
('!==', _js_id_op(operator.is_not)),
|
('!==', _js_id_op(operator.is_not)),
|
||||||
@ -316,9 +314,12 @@ _COMP_OPERATORS = (
|
|||||||
('>', _js_comp_op(operator.gt)),
|
('>', _js_comp_op(operator.gt)),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
_OPERATOR_RE = '|'.join(map(lambda x: re.escape(x[0]), _OPERATORS + _LOG_OPERATORS + _SC_OPERATORS))
|
||||||
|
|
||||||
_NAME_RE = r'[a-zA-Z_$][\w$]*'
|
_NAME_RE = r'[a-zA-Z_$][\w$]*'
|
||||||
_MATCHING_PARENS = dict(zip(*zip('()', '{}', '[]')))
|
_MATCHING_PARENS = dict(zip(*zip('()', '{}', '[]')))
|
||||||
_QUOTES = '\'"/'
|
_QUOTES = '\'"/'
|
||||||
|
_NESTED_BRACKETS = r'[^[\]]+(?:\[[^[\]]+(?:\[[^\]]+\])?\])?'
|
||||||
|
|
||||||
|
|
||||||
class JS_Break(ExtractorError):
|
class JS_Break(ExtractorError):
|
||||||
@ -1088,15 +1089,18 @@ class JSInterpreter(object):
|
|||||||
|
|
||||||
m = re.match(r'''(?x)
|
m = re.match(r'''(?x)
|
||||||
(?P<assign>
|
(?P<assign>
|
||||||
(?P<out>{_NAME_RE})(?:\[(?P<out_idx>(?:.+?\]\s*\[)*.+?)\])?\s*
|
(?P<out>{_NAME_RE})(?P<out_idx>(?:\[{_NESTED_BRACKETS}\])+)?\s*
|
||||||
(?P<op>{_OPERATOR_RE})?
|
(?P<op>{_OPERATOR_RE})?
|
||||||
=(?!=)(?P<expr>.*)$
|
=(?!=)(?P<expr>.*)$
|
||||||
)|(?P<return>
|
)|(?P<return>
|
||||||
(?!if|return|true|false|null|undefined|NaN|Infinity)(?P<name>{_NAME_RE})$
|
(?!if|return|true|false|null|undefined|NaN|Infinity)(?P<name>{_NAME_RE})$
|
||||||
)|(?P<indexing>
|
|
||||||
(?P<in>{_NAME_RE})\[(?P<in_idx>(?:.+?\]\s*\[)*.+?)\]$
|
|
||||||
)|(?P<attribute>
|
)|(?P<attribute>
|
||||||
(?P<var>{_NAME_RE})(?:(?P<nullish>\?)?\.(?P<member>[^(]+)|\[(?P<member2>[^\]]+)\])\s*
|
(?P<var>{_NAME_RE})(?:
|
||||||
|
(?P<nullish>\?)?\.(?P<member>[^(]+)|
|
||||||
|
\[(?P<member2>{_NESTED_BRACKETS})\]
|
||||||
|
)\s*
|
||||||
|
)|(?P<indexing>
|
||||||
|
(?P<in>{_NAME_RE})(?P<in_idx>\[.+\])$
|
||||||
)|(?P<function>
|
)|(?P<function>
|
||||||
(?P<fname>{_NAME_RE})\((?P<args>.*)\)$
|
(?P<fname>{_NAME_RE})\((?P<args>.*)\)$
|
||||||
)'''.format(**globals()), expr)
|
)'''.format(**globals()), expr)
|
||||||
@ -1111,10 +1115,11 @@ class JSInterpreter(object):
|
|||||||
elif left_val in (None, JS_Undefined):
|
elif left_val in (None, JS_Undefined):
|
||||||
raise self.Exception('Cannot index undefined variable ' + m.group('out'), expr=expr)
|
raise self.Exception('Cannot index undefined variable ' + m.group('out'), expr=expr)
|
||||||
|
|
||||||
indexes = re.split(r'\]\s*\[', m.group('out_idx'))
|
indexes = md['out_idx']
|
||||||
for i, idx in enumerate(indexes, 1):
|
while indexes:
|
||||||
|
idx, indexes = self._separate_at_paren(indexes)
|
||||||
idx = self.interpret_expression(idx, local_vars, allow_recursion)
|
idx = self.interpret_expression(idx, local_vars, allow_recursion)
|
||||||
if i < len(indexes):
|
if indexes:
|
||||||
left_val = self._index(left_val, idx)
|
left_val = self._index(left_val, idx)
|
||||||
if isinstance(idx, float):
|
if isinstance(idx, float):
|
||||||
idx = int(idx)
|
idx = int(idx)
|
||||||
@ -1159,7 +1164,9 @@ class JSInterpreter(object):
|
|||||||
|
|
||||||
if md.get('indexing'):
|
if md.get('indexing'):
|
||||||
val = local_vars[m.group('in')]
|
val = local_vars[m.group('in')]
|
||||||
for idx in re.split(r'\]\s*\[', m.group('in_idx')):
|
indexes = m.group('in_idx')
|
||||||
|
while indexes:
|
||||||
|
idx, indexes = self._separate_at_paren(indexes)
|
||||||
idx = self.interpret_expression(idx, local_vars, allow_recursion)
|
idx = self.interpret_expression(idx, local_vars, allow_recursion)
|
||||||
val = self._index(val, idx)
|
val = self._index(val, idx)
|
||||||
return val, should_return
|
return val, should_return
|
||||||
@ -1204,7 +1211,7 @@ class JSInterpreter(object):
|
|||||||
if obj is JS_Undefined:
|
if obj is JS_Undefined:
|
||||||
try:
|
try:
|
||||||
if variable not in self._objects:
|
if variable not in self._objects:
|
||||||
self._objects[variable] = self.extract_object(variable)
|
self._objects[variable] = self.extract_object(variable, local_vars)
|
||||||
obj = self._objects[variable]
|
obj = self._objects[variable]
|
||||||
except self.Exception:
|
except self.Exception:
|
||||||
if not nullish:
|
if not nullish:
|
||||||
@ -1215,7 +1222,7 @@ class JSInterpreter(object):
|
|||||||
|
|
||||||
# Member access
|
# Member access
|
||||||
if arg_str is None:
|
if arg_str is None:
|
||||||
return self._index(obj, member)
|
return self._index(obj, member, nullish)
|
||||||
|
|
||||||
# Function call
|
# Function call
|
||||||
argvals = [
|
argvals = [
|
||||||
@ -1400,7 +1407,7 @@ class JSInterpreter(object):
|
|||||||
for v in self._separate(list_txt):
|
for v in self._separate(list_txt):
|
||||||
yield self.interpret_expression(v, local_vars, allow_recursion)
|
yield self.interpret_expression(v, local_vars, allow_recursion)
|
||||||
|
|
||||||
def extract_object(self, objname):
|
def extract_object(self, objname, *global_stack):
|
||||||
_FUNC_NAME_RE = r'''(?:{n}|"{n}"|'{n}')'''.format(n=_NAME_RE)
|
_FUNC_NAME_RE = r'''(?:{n}|"{n}"|'{n}')'''.format(n=_NAME_RE)
|
||||||
obj = {}
|
obj = {}
|
||||||
fields = next(filter(None, (
|
fields = next(filter(None, (
|
||||||
@ -1421,7 +1428,8 @@ class JSInterpreter(object):
|
|||||||
fields):
|
fields):
|
||||||
argnames = self.build_arglist(f.group('args'))
|
argnames = self.build_arglist(f.group('args'))
|
||||||
name = remove_quotes(f.group('key'))
|
name = remove_quotes(f.group('key'))
|
||||||
obj[name] = function_with_repr(self.build_function(argnames, f.group('code')), 'F<{0}>'.format(name))
|
obj[name] = function_with_repr(
|
||||||
|
self.build_function(argnames, f.group('code'), *global_stack), 'F<{0}>'.format(name))
|
||||||
|
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
__version__ = '2021.12.17'
|
__version__ = '2025.04.07'
|
||||||
|
Loading…
x
Reference in New Issue
Block a user