diff options
author | dirkf <fieldhouse@gmx.net> | 2024-12-12 00:26:00 +0000 |
---|---|---|
committer | dirkf <fieldhouse@gmx.net> | 2024-12-16 12:38:51 +0000 |
commit | 5dee6213edddb5ea00775db6b3b73f8355144485 (patch) | |
tree | 4347780a0573831c578c24899f965d479a59b8bf /youtube_dl | |
parent | 81e64cacf2b8c144ec1e9d3258db792f0eb8443e (diff) |
[jsinterp] Fix and improve arithmetic operations
* addition becomes concat with a string operand
* improve handling of edgier cases
* arithmetic in float like JS (more places need cast to int?)
* increase test coverage
Diffstat (limited to 'youtube_dl')
-rw-r--r-- | youtube_dl/jsinterp.py | 53 |
1 files changed, 34 insertions, 19 deletions
diff --git a/youtube_dl/jsinterp.py b/youtube_dl/jsinterp.py index 248210ce4..73934a6af 100644 --- a/youtube_dl/jsinterp.py +++ b/youtube_dl/jsinterp.py @@ -11,6 +11,7 @@ from functools import update_wrapper, wraps from .utils import ( error_to_compat_str, ExtractorError, + float_or_none, js_to_json, remove_quotes, unified_timestamp, @@ -81,35 +82,47 @@ def _js_bit_op(op): return wrapped -def _js_arith_op(op): +def _js_arith_op(op, div=False): @wraps_op(op) def wrapped(a, b): if JS_Undefined in (a, b): return _NaN - return op(a or 0, b or 0) + # null, "" --> 0 + a, b = (float_or_none( + (x.strip() if isinstance(x, compat_basestring) else x) or 0, + default=_NaN) for x in (a, b)) + if _NaN in (a, b): + return _NaN + try: + return op(a, b) + except ZeroDivisionError: + return _NaN if not (div and (a or b)) else _Infinity return wrapped -def _js_div(a, b): - if JS_Undefined in (a, b) or not (a or b): - return _NaN - return operator.truediv(a or 0, b) if b else _Infinity +_js_arith_add = _js_arith_op(operator.add) + + +def _js_add(a, b): + if not (isinstance(a, compat_basestring) or isinstance(b, compat_basestring)): + return _js_arith_add(a, b) + if not isinstance(a, compat_basestring): + a = _js_toString(a) + elif not isinstance(b, compat_basestring): + b = _js_toString(b) + return operator.concat(a, b) -def _js_mod(a, b): - if JS_Undefined in (a, b) or not b: - return _NaN - return (a or 0) % b +_js_mod = _js_arith_op(operator.mod) +__js_exp = _js_arith_op(operator.pow) def _js_exp(a, b): if not b: return 1 # even 0 ** 0 !! - elif JS_Undefined in (a, b): - return _NaN - return (a or 0) ** b + return __js_exp(a, b) def _js_to_primitive(v): @@ -117,7 +130,7 @@ def _js_to_primitive(v): ','.join(map(_js_toString, v)) if isinstance(v, list) else '[object Object]' if isinstance(v, dict) else compat_str(v) if not isinstance(v, ( - compat_numeric_types, compat_basestring, bool)) + compat_numeric_types, compat_basestring)) else v ) @@ -128,7 +141,9 @@ def _js_toString(v): else 'Infinity' if v == _Infinity else 'NaN' if v is _NaN else 'null' if v is None - else compat_str(v) if isinstance(v, compat_numeric_types) + # bool <= int: do this first + else ('false', 'true')[v] if isinstance(v, bool) + else '{0:.7f}'.format(v).rstrip('.0') if isinstance(v, compat_numeric_types) else _js_to_primitive(v)) @@ -240,11 +255,11 @@ def _js_typeof(expr): _OPERATORS = ( ('>>', _js_bit_op(operator.rshift)), ('<<', _js_bit_op(operator.lshift)), - ('+', _js_arith_op(operator.add)), + ('+', _js_add), ('-', _js_arith_op(operator.sub)), ('*', _js_arith_op(operator.mul)), ('%', _js_mod), - ('/', _js_div), + ('/', _js_arith_op(operator.truediv, div=True)), ('**', _js_exp), ) @@ -873,7 +888,7 @@ class JSInterpreter(object): start, end = m.span() sign = m.group('pre_sign') or m.group('post_sign') ret = local_vars[var] - local_vars[var] += 1 if sign[0] == '+' else -1 + local_vars[var] = _js_add(ret, 1 if sign[0] == '+' else -1) if m.group('pre_sign'): ret = local_vars[var] expr = expr[:start] + self._dump(ret, local_vars) + expr[end:] @@ -1023,7 +1038,7 @@ class JSInterpreter(object): if obj is compat_str: if member == 'fromCharCode': assertion(argvals, 'takes one or more arguments') - return ''.join(map(compat_chr, argvals)) + return ''.join(compat_chr(int(n)) for n in argvals) raise self.Exception('Unsupported string method ' + member, expr=expr) elif obj is float: if member == 'pow': |