aboutsummaryrefslogtreecommitdiffstats
path: root/yt_dlp/jsinterp.py
diff options
context:
space:
mode:
authorpukkandan <pukkandan.ytdlp@gmail.com>2022-08-15 03:20:36 +0530
committerpukkandan <pukkandan.ytdlp@gmail.com>2022-08-15 03:31:49 +0530
commit49b4ceaedf92db85177cfa10542bddbed16529c7 (patch)
tree9c1bcbcb3a980cb0bc4d49415d2676a838f058da /yt_dlp/jsinterp.py
parentd711839760e220e561098cf257de43769049d238 (diff)
downloadhypervideo-pre-49b4ceaedf92db85177cfa10542bddbed16529c7.tar.lz
hypervideo-pre-49b4ceaedf92db85177cfa10542bddbed16529c7.tar.xz
hypervideo-pre-49b4ceaedf92db85177cfa10542bddbed16529c7.zip
[jsinterp] Bring or-par with youtube-dl
Partially cherry-picked from: https://github.com/ytdl-org/youtube-dl/commit/d231b56717c73ee597d2e077d11b69ed48a1b02d Authored by pukkandan, dirkf
Diffstat (limited to 'yt_dlp/jsinterp.py')
-rw-r--r--yt_dlp/jsinterp.py54
1 files changed, 35 insertions, 19 deletions
diff --git a/yt_dlp/jsinterp.py b/yt_dlp/jsinterp.py
index 1af6ee0aa..87f141476 100644
--- a/yt_dlp/jsinterp.py
+++ b/yt_dlp/jsinterp.py
@@ -17,6 +17,8 @@ from .utils import (
)
_NAME_RE = r'[a-zA-Z_$][\w$]*'
+
+# Ref: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence
_OPERATORS = { # None => Defined in JSInterpreter._operator
'?': None,
@@ -26,23 +28,31 @@ _OPERATORS = { # None => Defined in JSInterpreter._operator
'|': operator.or_,
'^': operator.xor,
- # FIXME: This should actually be below comparision
- '>>': operator.rshift,
- '<<': operator.lshift,
+ '===': operator.is_,
+ '!==': operator.is_not,
+ '==': operator.eq,
+ '!=': operator.ne,
'<=': operator.le,
'>=': operator.ge,
'<': operator.lt,
'>': operator.gt,
+ '>>': operator.rshift,
+ '<<': operator.lshift,
+
'+': operator.add,
'-': operator.sub,
'*': operator.mul,
'/': operator.truediv,
'%': operator.mod,
+
+ '**': operator.pow,
}
+_COMP_OPERATORS = {'===', '!==', '==', '!=', '<=', '>=', '<', '>'}
+
_MATCHING_PARENS = dict(zip('({[', ')}]'))
_QUOTES = '\'"'
@@ -81,7 +91,7 @@ class LocalNameSpace(collections.ChainMap):
class Debugger:
import sys
- ENABLED = 'pytest' in sys.modules
+ ENABLED = False and 'pytest' in sys.modules
@staticmethod
def write(*args, level=100):
@@ -200,7 +210,7 @@ class JSInterpreter:
if should_return:
return ret, should_return
- m = re.match(r'(?P<var>var\s)|return(?:\s+|$)', stmt)
+ m = re.match(r'(?P<var>(?:var|const|let)\s)|return(?:\s+|$)', stmt)
if m:
expr = stmt[len(m.group(0)):].strip()
should_return = not m.group('var')
@@ -218,13 +228,18 @@ class JSInterpreter:
obj = expr[4:]
if obj.startswith('Date('):
left, right = self._separate_at_paren(obj[4:], ')')
- expr = unified_timestamp(left[1:-1], False)
+ expr = unified_timestamp(
+ self.interpret_expression(left, local_vars, allow_recursion), False)
if not expr:
raise self.Exception(f'Failed to parse date {left!r}', expr)
expr = self._dump(int(expr * 1000), local_vars) + right
else:
raise self.Exception(f'Unsupported object {obj}', expr)
+ if expr.startswith('void '):
+ left = self.interpret_expression(expr[5:], local_vars, allow_recursion)
+ return None, should_return
+
if expr.startswith('{'):
inner, outer = self._separate_at_paren(expr, '}')
inner, should_abort = self.interpret_statement(inner, local_vars, allow_recursion)
@@ -307,7 +322,8 @@ class JSInterpreter:
if default:
matched = matched or case == 'default'
elif not matched:
- matched = case != 'default' and switch_val == self.interpret_expression(case, local_vars, allow_recursion)
+ matched = (case != 'default'
+ and switch_val == self.interpret_expression(case, local_vars, allow_recursion))
if not matched:
continue
try:
@@ -347,7 +363,7 @@ class JSInterpreter:
m = re.match(fr'''(?x)
(?P<assign>
(?P<out>{_NAME_RE})(?:\[(?P<index>[^\]]+?)\])?\s*
- (?P<op>{"|".join(map(re.escape, _OPERATORS))})?
+ (?P<op>{"|".join(map(re.escape, set(_OPERATORS) - _COMP_OPERATORS))})?
=(?P<expr>.*)$
)|(?P<return>
(?!if|return|true|false|null|undefined)(?P<name>{_NAME_RE})$
@@ -397,12 +413,14 @@ class JSInterpreter:
for op in _OPERATORS:
separated = list(self._separate(expr, op))
- if len(separated) < 2:
- continue
right_expr = separated.pop()
- while op == '-' and len(separated) > 1 and not separated[-1].strip():
- right_expr = f'-{right_expr}'
+ while op in '<>*-' and len(separated) > 1 and not separated[-1].strip():
separated.pop()
+ right_expr = f'{op}{right_expr}'
+ if op != '-':
+ right_expr = f'{separated.pop()}{op}{right_expr}'
+ if not separated:
+ continue
left_val = self.interpret_expression(op.join(separated), local_vars, allow_recursion)
return self._operator(op, 0 if left_val is None else left_val,
right_expr, expr, local_vars, allow_recursion), should_return
@@ -564,8 +582,8 @@ class JSInterpreter:
# Currently, it only supports function definitions
fields_m = re.finditer(
r'''(?x)
- (?P<key>%s)\s*:\s*function\s*\((?P<args>[a-z,]+)\){(?P<code>[^}]+)}
- ''' % _FUNC_NAME_RE,
+ (?P<key>%s)\s*:\s*function\s*\((?P<args>(?:%s|,)*)\){(?P<code>[^}]+)}
+ ''' % (_FUNC_NAME_RE, _NAME_RE),
fields)
for f in fields_m:
argnames = f.group('args').split(',')
@@ -580,7 +598,7 @@ class JSInterpreter:
(?:
function\s+%(name)s|
[{;,]\s*%(name)s\s*=\s*function|
- var\s+%(name)s\s*=\s*function
+ (?:var|const|let)\s+%(name)s\s*=\s*function
)\s*
\((?P<args>[^)]*)\)\s*
(?P<code>{.+})''' % {'name': re.escape(funcname)},
@@ -615,10 +633,8 @@ class JSInterpreter:
argnames = tuple(argnames)
def resf(args, kwargs={}, allow_recursion=100):
- global_stack[0].update({
- **dict(itertools.zip_longest(argnames, args, fillvalue=None)),
- **kwargs
- })
+ global_stack[0].update(itertools.zip_longest(argnames, args, fillvalue=None))
+ global_stack[0].update(kwargs)
var_stack = LocalNameSpace(*global_stack)
ret, should_abort = self.interpret_statement(code.replace('\n', ''), var_stack, allow_recursion - 1)
if should_abort: