restructured manifest and plugins loading; updated plugins
This commit is contained in:
@@ -186,7 +186,7 @@ _OPERATORS = { # None => Defined in JSInterpreter._operator
|
||||
_COMP_OPERATORS = {'===', '!==', '==', '!=', '<=', '>=', '<', '>'}
|
||||
|
||||
_NAME_RE = r'[a-zA-Z_$][\w$]*'
|
||||
_MATCHING_PARENS = dict(zip(*zip('()', '{}', '[]')))
|
||||
_MATCHING_PARENS = dict(zip(*zip('()', '{}', '[]', strict=True), strict=True))
|
||||
_QUOTES = '\'"/'
|
||||
_NESTED_BRACKETS = r'[^[\]]+(?:\[[^[\]]+(?:\[[^\]]+\])?\])?'
|
||||
|
||||
@@ -222,6 +222,14 @@ class LocalNameSpace(collections.ChainMap):
|
||||
def __delitem__(self, key):
|
||||
raise NotImplementedError('Deleting is not supported')
|
||||
|
||||
def set_local(self, key, value):
|
||||
self.maps[0][key] = value
|
||||
|
||||
def get_local(self, key):
|
||||
if key in self.maps[0]:
|
||||
return self.maps[0][key]
|
||||
return JS_Undefined
|
||||
|
||||
|
||||
class Debugger:
|
||||
import sys
|
||||
@@ -271,6 +279,7 @@ class JSInterpreter:
|
||||
def __init__(self, code, objects=None):
|
||||
self.code, self._functions = code, {}
|
||||
self._objects = {} if objects is None else objects
|
||||
self._undefined_varnames = set()
|
||||
|
||||
class Exception(ExtractorError): # noqa: A001
|
||||
def __init__(self, msg, expr=None, *args, **kwargs):
|
||||
@@ -381,7 +390,7 @@ class JSInterpreter:
|
||||
return self._named_object(namespace, obj)
|
||||
|
||||
@Debugger.wrap_interpreter
|
||||
def interpret_statement(self, stmt, local_vars, allow_recursion=100):
|
||||
def interpret_statement(self, stmt, local_vars, allow_recursion=100, _is_var_declaration=False):
|
||||
if allow_recursion < 0:
|
||||
raise self.Exception('Recursion limit reached')
|
||||
allow_recursion -= 1
|
||||
@@ -401,6 +410,7 @@ class JSInterpreter:
|
||||
if m.group('throw'):
|
||||
raise JS_Throw(self.interpret_expression(expr, local_vars, allow_recursion))
|
||||
should_return = not m.group('var')
|
||||
_is_var_declaration = _is_var_declaration or bool(m.group('var'))
|
||||
if not expr:
|
||||
return None, should_return
|
||||
|
||||
@@ -585,11 +595,39 @@ class JSInterpreter:
|
||||
sub_expressions = list(self._separate(expr))
|
||||
if len(sub_expressions) > 1:
|
||||
for sub_expr in sub_expressions:
|
||||
ret, should_abort = self.interpret_statement(sub_expr, local_vars, allow_recursion)
|
||||
ret, should_abort = self.interpret_statement(
|
||||
sub_expr, local_vars, allow_recursion, _is_var_declaration=_is_var_declaration)
|
||||
if should_abort:
|
||||
return ret, True
|
||||
return ret, False
|
||||
|
||||
m = re.match(fr'''(?x)
|
||||
(?P<out>{_NAME_RE})(?:\[(?P<index>{_NESTED_BRACKETS})\])?\s*
|
||||
(?P<op>{"|".join(map(re.escape, set(_OPERATORS) - _COMP_OPERATORS))})?
|
||||
=(?!=)(?P<expr>.*)$
|
||||
''', expr)
|
||||
if m: # We are assigning a value to a variable
|
||||
left_val = local_vars.get(m.group('out'))
|
||||
|
||||
if not m.group('index'):
|
||||
eval_result = self._operator(
|
||||
m.group('op'), left_val, m.group('expr'), expr, local_vars, allow_recursion)
|
||||
if _is_var_declaration:
|
||||
local_vars.set_local(m.group('out'), eval_result)
|
||||
else:
|
||||
local_vars[m.group('out')] = eval_result
|
||||
return local_vars[m.group('out')], should_return
|
||||
elif left_val in (None, JS_Undefined):
|
||||
raise self.Exception(f'Cannot index undefined variable {m.group("out")}', expr)
|
||||
|
||||
idx = self.interpret_expression(m.group('index'), local_vars, allow_recursion)
|
||||
if not isinstance(idx, (int, float)):
|
||||
raise self.Exception(f'List index {idx} must be integer', expr)
|
||||
idx = int(idx)
|
||||
left_val[idx] = self._operator(
|
||||
m.group('op'), self._index(left_val, idx), m.group('expr'), expr, local_vars, allow_recursion)
|
||||
return left_val[idx], should_return
|
||||
|
||||
for m in re.finditer(rf'''(?x)
|
||||
(?P<pre_sign>\+\+|--)(?P<var1>{_NAME_RE})|
|
||||
(?P<var2>{_NAME_RE})(?P<post_sign>\+\+|--)''', expr):
|
||||
@@ -606,11 +644,7 @@ class JSInterpreter:
|
||||
return None, should_return
|
||||
|
||||
m = re.match(fr'''(?x)
|
||||
(?P<assign>
|
||||
(?P<out>{_NAME_RE})(?:\[(?P<index>{_NESTED_BRACKETS})\])?\s*
|
||||
(?P<op>{"|".join(map(re.escape, set(_OPERATORS) - _COMP_OPERATORS))})?
|
||||
=(?!=)(?P<expr>.*)$
|
||||
)|(?P<return>
|
||||
(?P<return>
|
||||
(?!if|return|true|false|null|undefined|NaN)(?P<name>{_NAME_RE})$
|
||||
)|(?P<attribute>
|
||||
(?P<var>{_NAME_RE})(?:
|
||||
@@ -622,25 +656,7 @@ class JSInterpreter:
|
||||
)|(?P<function>
|
||||
(?P<fname>{_NAME_RE})\((?P<args>.*)\)$
|
||||
)''', expr)
|
||||
if m and m.group('assign'):
|
||||
left_val = local_vars.get(m.group('out'))
|
||||
|
||||
if not m.group('index'):
|
||||
local_vars[m.group('out')] = self._operator(
|
||||
m.group('op'), left_val, m.group('expr'), expr, local_vars, allow_recursion)
|
||||
return local_vars[m.group('out')], should_return
|
||||
elif left_val in (None, JS_Undefined):
|
||||
raise self.Exception(f'Cannot index undefined variable {m.group("out")}', expr)
|
||||
|
||||
idx = self.interpret_expression(m.group('index'), local_vars, allow_recursion)
|
||||
if not isinstance(idx, (int, float)):
|
||||
raise self.Exception(f'List index {idx} must be integer', expr)
|
||||
idx = int(idx)
|
||||
left_val[idx] = self._operator(
|
||||
m.group('op'), self._index(left_val, idx), m.group('expr'), expr, local_vars, allow_recursion)
|
||||
return left_val[idx], should_return
|
||||
|
||||
elif expr.isdigit():
|
||||
if expr.isdigit():
|
||||
return int(expr), should_return
|
||||
|
||||
elif expr == 'break':
|
||||
@@ -653,7 +669,19 @@ class JSInterpreter:
|
||||
return float('NaN'), should_return
|
||||
|
||||
elif m and m.group('return'):
|
||||
return local_vars.get(m.group('name'), JS_Undefined), should_return
|
||||
var = m.group('name')
|
||||
# Declared variables
|
||||
if _is_var_declaration:
|
||||
ret = local_vars.get_local(var)
|
||||
# Register varname in local namespace
|
||||
# Set value as JS_Undefined or its pre-existing value
|
||||
local_vars.set_local(var, ret)
|
||||
else:
|
||||
ret = local_vars.get(var, NO_DEFAULT)
|
||||
if ret is NO_DEFAULT:
|
||||
ret = JS_Undefined
|
||||
self._undefined_varnames.add(var)
|
||||
return ret, should_return
|
||||
|
||||
with contextlib.suppress(ValueError):
|
||||
return json.loads(js_to_json(expr, strict=True)), should_return
|
||||
@@ -856,7 +884,7 @@ class JSInterpreter:
|
||||
obj = {}
|
||||
obj_m = re.search(
|
||||
r'''(?x)
|
||||
(?<!\.)%s\s*=\s*{\s*
|
||||
(?<![a-zA-Z$0-9.])%s\s*=\s*{\s*
|
||||
(?P<fields>(%s\s*:\s*function\s*\(.*?\)\s*{.*?}(?:,\s*)?)*)
|
||||
}\s*;
|
||||
''' % (re.escape(objname), _FUNC_NAME_RE),
|
||||
|
||||
Reference in New Issue
Block a user