mirror of
https://codeberg.org/polarisfm/youtube-dl
synced 2024-11-22 16:44:32 +01:00
[jsinterp] Adding delete and void operators
- Refractors `Context` and `Reference` classes into their own module named `environment` (saves local import in `tstream`)
This commit is contained in:
parent
93c0bb53a6
commit
c0ef911bc6
63
youtube_dl/jsinterp2/environment.py
Normal file
63
youtube_dl/jsinterp2/environment.py
Normal file
@ -0,0 +1,63 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from ..utils import ExtractorError
|
||||
from .jsbuilt_ins.base import isprimitive
|
||||
|
||||
|
||||
class Context(object):
|
||||
def __init__(self, variables=None, ended=False):
|
||||
super(Context, self).__init__()
|
||||
self.ended = ended
|
||||
self.no_in = True
|
||||
self.local_vars = {}
|
||||
if variables is not None:
|
||||
for k, v in dict(variables).items():
|
||||
# XXX validate identifiers
|
||||
self.local_vars[k] = Reference(v, (self.local_vars, k))
|
||||
|
||||
|
||||
class Reference(object):
|
||||
def __init__(self, value, parent_key=None):
|
||||
super(Reference, self).__init__()
|
||||
self.value = value
|
||||
if parent_key is not None:
|
||||
self.parent, self.name = parent_key
|
||||
else:
|
||||
self.parent = self.name = None
|
||||
|
||||
def getvalue(self, deep=False):
|
||||
value = self.value
|
||||
if deep:
|
||||
if isinstance(self.value, (list, tuple)):
|
||||
# TODO test nested arrays
|
||||
value = [elem if isprimitive(elem) else elem.getvalue() for elem in self.value]
|
||||
elif isinstance(self.value, dict):
|
||||
value = {}
|
||||
for key, prop in self.value.items():
|
||||
value[key] = prop.getvalue()
|
||||
|
||||
return value
|
||||
|
||||
def putvalue(self, value):
|
||||
if self.parent is None:
|
||||
raise ExtractorError('Trying to set a read-only reference')
|
||||
if not hasattr(self.parent, '__setitem__'):
|
||||
raise ExtractorError('Unknown reference')
|
||||
self.parent.__setitem__(self.name, Reference(value, (self.parent, self.name)))
|
||||
self.value = value
|
||||
return value
|
||||
|
||||
def __repr__(self):
|
||||
if self.parent is not None:
|
||||
parent, key = self.parent
|
||||
return '<Reference value: %s, parent: %s@(0x%x), key: %s>' % (
|
||||
str(self.value), parent.__class__.__name__, id(parent), key)
|
||||
return '<Reference value: %s, parent: %s>' % (self.value, None)
|
||||
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, Reference):
|
||||
return self.parent is other.parent and self.name == other.name
|
||||
return False
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self.__eq__(other)
|
@ -4,6 +4,7 @@ import re
|
||||
|
||||
from ..compat import compat_str
|
||||
from ..utils import ExtractorError
|
||||
from .environment import Context, Reference
|
||||
from .jsparser import Parser
|
||||
from .jsgrammar import TokenTypes, token_keys
|
||||
from .jsbuilt_ins import global_obj
|
||||
@ -11,64 +12,7 @@ from .jsbuilt_ins.base import isprimitive
|
||||
from .jsbuilt_ins.internals import to_string
|
||||
from .jsbuilt_ins.utils import to_js
|
||||
|
||||
|
||||
class Context(object):
|
||||
def __init__(self, variables=None, ended=False):
|
||||
super(Context, self).__init__()
|
||||
self.ended = ended
|
||||
self.no_in = True
|
||||
self.local_vars = {}
|
||||
if variables is not None:
|
||||
for k, v in dict(variables).items():
|
||||
# XXX validate identifiers
|
||||
self.local_vars[k] = Reference(v, (self.local_vars, k))
|
||||
|
||||
|
||||
class Reference(object):
|
||||
def __init__(self, value, parent_key=None):
|
||||
super(Reference, self).__init__()
|
||||
self._value = value
|
||||
if parent_key is not None:
|
||||
self._parent, self._name = parent_key
|
||||
else:
|
||||
self._parent = self._name = None
|
||||
|
||||
def getvalue(self, deep=False):
|
||||
value = self._value
|
||||
if deep:
|
||||
if isinstance(self._value, (list, tuple)):
|
||||
# TODO test nested arrays
|
||||
value = [elem if isprimitive(elem) else elem.getvalue() for elem in self._value]
|
||||
elif isinstance(self._value, dict):
|
||||
value = {}
|
||||
for key, prop in self._value.items():
|
||||
value[key] = prop.getvalue()
|
||||
|
||||
return value
|
||||
|
||||
def putvalue(self, value):
|
||||
if self._parent is None:
|
||||
raise ExtractorError('Trying to set a read-only reference')
|
||||
if not hasattr(self._parent, '__setitem__'):
|
||||
raise ExtractorError('Unknown reference')
|
||||
self._parent.__setitem__(self._name, Reference(value, (self._parent, self._name)))
|
||||
self._value = value
|
||||
return value
|
||||
|
||||
def __repr__(self):
|
||||
if self._parent is not None:
|
||||
parent, key = self._parent
|
||||
return '<Reference value: %s, parent: %s@(0x%x), key: %s>' % (
|
||||
str(self._value), parent.__class__.__name__, id(parent), key)
|
||||
return '<Reference value: %s, parent: %s>' % (self._value, None)
|
||||
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, Reference):
|
||||
return self._parent is other._parent and self._name == other._name
|
||||
return False
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self.__eq__(other)
|
||||
# TODO use JSObject for Contex.local_vars and JSInterpreter.global_vars
|
||||
|
||||
|
||||
class JSInterpreter(object):
|
||||
|
@ -16,7 +16,8 @@ from .jsgrammar import (
|
||||
UNARY_OPERATORS_RE,
|
||||
TokenTypes
|
||||
)
|
||||
from .jsbuilt_ins import false, true, nan
|
||||
from .environment import Reference
|
||||
from .jsbuilt_ins import false, true, nan, undefined
|
||||
from .jsbuilt_ins.internals import jstype, undefined_type, null_type, number_type, boolean_type, string_type
|
||||
|
||||
|
||||
@ -25,8 +26,6 @@ def convert_to_unary(token_value):
|
||||
|
||||
|
||||
def strict_equal(x, y):
|
||||
from .jsinterp import Reference
|
||||
|
||||
if jstype(x) != jstype(y):
|
||||
return False
|
||||
if jstype(x) in (undefined_type, null_type):
|
||||
@ -46,6 +45,22 @@ def strict_equal(x, y):
|
||||
return False
|
||||
|
||||
|
||||
def delete(ref):
|
||||
if not isinstance(ref, Reference):
|
||||
return True
|
||||
if ref.value not in (None, undefined):
|
||||
# XXX raise SyntaxError if ref is strict reference
|
||||
return True
|
||||
# XXX handle if ref is property reference
|
||||
else:
|
||||
# XXX raise SyntaxError if ref is strict reference (again)
|
||||
if ref.name not in ref.parent:
|
||||
return False
|
||||
# FIXME `JSInterperter.global_vars` will be changed from `dict` to `JSObjectPrototype`
|
||||
ref.parent.remove(ref.name)
|
||||
return True
|
||||
|
||||
|
||||
_PUNCTUATIONS = {
|
||||
'{': TokenTypes.COPEN,
|
||||
'}': TokenTypes.CCLOSE,
|
||||
@ -70,9 +85,8 @@ _UNARY_OPERATORS = {
|
||||
'--': (TokenTypes.DEC, lambda cur: cur - 1),
|
||||
'!': (TokenTypes.NOT, operator.not_),
|
||||
'~': (TokenTypes.BNOT, operator.inv),
|
||||
# XXX define these operators
|
||||
'delete': (TokenTypes.DEL, None),
|
||||
'void': (TokenTypes.VOID, None),
|
||||
'delete': (TokenTypes.DEL, delete),
|
||||
'void': (TokenTypes.VOID, lambda cur: undefined),
|
||||
'typeof': (TokenTypes.TYPE, lambda cur: _type_strings[jstype(cur)])
|
||||
}
|
||||
_RELATIONS = {
|
||||
|
Loading…
Reference in New Issue
Block a user