1
0
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:
sulyi 2018-06-11 12:27:02 +02:00
parent 93c0bb53a6
commit c0ef911bc6
3 changed files with 85 additions and 64 deletions

View 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)

View File

@ -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):

View File

@ -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 = {