From 0136be4a196895200fe1f754a8de6c759222832f Mon Sep 17 00:00:00 2001 From: sulyi Date: Thu, 2 Mar 2017 21:22:11 +0100 Subject: [PATCH] [jsbuilt-ins] fixing constructors --- youtube_dl/jsinterp/jsbuilt_ins/base.py | 8 ++++-- youtube_dl/jsinterp/jsbuilt_ins/internals.py | 16 +++++++---- youtube_dl/jsinterp/jsbuilt_ins/jsarray.py | 11 ++++++-- youtube_dl/jsinterp/jsbuilt_ins/jsboolean.py | 5 +++- youtube_dl/jsinterp/jsbuilt_ins/jsfunction.py | 28 +++++++++++++------ youtube_dl/jsinterp/jsbuilt_ins/jsnumber.py | 9 ++++++ youtube_dl/jsinterp/jsbuilt_ins/jsobject.py | 6 ++-- youtube_dl/jsinterp/jsbuilt_ins/jsstring.py | 5 +++- 8 files changed, 65 insertions(+), 23 deletions(-) diff --git a/youtube_dl/jsinterp/jsbuilt_ins/base.py b/youtube_dl/jsinterp/jsbuilt_ins/base.py index 9d8099f66..a787da150 100644 --- a/youtube_dl/jsinterp/jsbuilt_ins/base.py +++ b/youtube_dl/jsinterp/jsbuilt_ins/base.py @@ -60,6 +60,10 @@ class JSProtoBase(JSBase): jsclass = '' +def _get_formal_args(func): + return func.__code__.co_varnames[func.__code__.co_argcount - len((func.__defaults__))] + + def to_js(o, name=None): if isinstance(o, JSProtoBase): return o @@ -74,9 +78,9 @@ def to_js(o, name=None): elif isinstance(o, native_object): return JSObjectPrototype(o) elif isinstance(o, native_function): - return JSFunctionPrototype(name, o, []) + return JSFunctionPrototype(name, o, _get_formal_args(o)) elif isinstance(o, JSBase) and hasattr(o, 'call'): - return JSFunctionPrototype(o.name, o, []) + return JSFunctionPrototype(o.name, o, _get_formal_args(o.call)) elif isinstance(o, native_array): return JSArrayPrototype(o) else: diff --git a/youtube_dl/jsinterp/jsbuilt_ins/internals.py b/youtube_dl/jsinterp/jsbuilt_ins/internals.py index e2a56b1b8..18544e04e 100644 --- a/youtube_dl/jsinterp/jsbuilt_ins/internals.py +++ b/youtube_dl/jsinterp/jsbuilt_ins/internals.py @@ -12,6 +12,13 @@ from .jsstring import JSStringPrototype from .jsnumber import JSNumberPrototype from ..jsgrammar import __HEXADECIMAL_RE +undefined_type = object() +null_type = object() +boolean_type = object() +string_type = object() +number_type = object() +object_type = object() + def jstype(o): if o is undefined: @@ -189,9 +196,6 @@ def to_object(o): return o -undefined_type = object() -null_type = object() -boolean_type = object() -string_type = object() -number_type = object() -object_type = object() +def throw_type_error(): + # TODO [[ThrowTypeError]] (13.2.3) + pass diff --git a/youtube_dl/jsinterp/jsbuilt_ins/jsarray.py b/youtube_dl/jsinterp/jsbuilt_ins/jsarray.py index 8e7a4da71..6920ca2ed 100644 --- a/youtube_dl/jsinterp/jsbuilt_ins/jsarray.py +++ b/youtube_dl/jsinterp/jsbuilt_ins/jsarray.py @@ -10,8 +10,13 @@ class JSArrayPrototype(JSObjectPrototype): def __init__(self, value=None): super(JSArrayPrototype, self).__init__() - self.value = [] if value is None else list(value) - self.own = {'length': self._length} + if value is None: + # prototype + self.value = [] + else: + self.value = value + self.own = dict((str(i), v) for i, v in enumerate(value)) + self.own['length'] = self._length def __str__(self): return 'JSArrayPrototype: %s' % self.value @@ -136,7 +141,7 @@ class JSArray(JSObject): if args: return JSArrayPrototype(args) else: - return JSArrayPrototype() + return JSArrayPrototype([]) def _is_array(self, arg): return 'array is array' diff --git a/youtube_dl/jsinterp/jsbuilt_ins/jsboolean.py b/youtube_dl/jsinterp/jsbuilt_ins/jsboolean.py index 4df25578a..be82fd24a 100644 --- a/youtube_dl/jsinterp/jsbuilt_ins/jsboolean.py +++ b/youtube_dl/jsinterp/jsbuilt_ins/jsboolean.py @@ -9,10 +9,13 @@ from .jsobject import JSObject, JSObjectPrototype class JSBooleanPrototype(JSObjectPrototype): def __init__(self, value=None): + super(JSBooleanPrototype, self).__init__() if value is None: # prototype value = False - super(JSBooleanPrototype, self).__init__(value) + else: + self.value = value + self.own = {} @staticmethod def _constructor(value=None): diff --git a/youtube_dl/jsinterp/jsbuilt_ins/jsfunction.py b/youtube_dl/jsinterp/jsbuilt_ins/jsfunction.py index e83cdc308..5802d2625 100644 --- a/youtube_dl/jsinterp/jsbuilt_ins/jsfunction.py +++ b/youtube_dl/jsinterp/jsbuilt_ins/jsfunction.py @@ -1,7 +1,7 @@ from __future__ import unicode_literals from . import undefined, null -from .internals import to_string +from .internals import to_string, throw_type_error from .base import to_js, native_function, JSBase from .jsobject import JSObject, JSObjectPrototype @@ -27,6 +27,17 @@ class JSFunctionPrototype(JSObjectPrototype): self.body = to_string(body) if body is not undefined or body is not null else '' self.f_name = name self.arguments = list(formal_args) + proto = JSObject.construct() + proto.own['constructor'] = self + self.own = {'length': self._length, + 'prototype': proto + } + # TODO Handle strict mode + strict = True + if strict: + thrower = throw_type_error + self.own['caller'] = thrower + self.own['arguments'] = thrower # FIXME: JSProtoBase sets body to '' instead of None # TODO check if self._args can be parsed as formal parameter list # TODO check if self._body can be parsed as function body @@ -36,8 +47,7 @@ class JSFunctionPrototype(JSObjectPrototype): @property def _length(self): - # Yeesh, I dare you to find anything like that in the python specification. - return len([arg for arg, init in self.arguments if init is not None]) + return len(self.arguments) @staticmethod def _constructor(arguments=None): @@ -51,7 +61,7 @@ class JSFunctionPrototype(JSObjectPrototype): body = '' return 'function %s(%s) {%s\n}' % ( self.f_name, - ', '.join(arg if init is None else arg + '=' + init for arg, init in self.arguments), + ', '.join(self.arguments), body) def _apply(self, this_arg, arg_array): @@ -82,12 +92,14 @@ class JSFunction(JSObject): @staticmethod def construct(formal_args=None): - if formal_args is None: + if formal_args is not None and formal_args: + body = formal_args[-1] + formal_args = [] + for arg in formal_args[:-1]: + formal_args.extend(a.strip() for a in arg.split(',')) + else: body = '' formal_args = [] - else: - body = formal_args[-1] if formal_args else '' - formal_args = formal_args[:-1] return JSFunctionPrototype('anonymous', body, formal_args) name = JSFunctionPrototype.jsclass diff --git a/youtube_dl/jsinterp/jsbuilt_ins/jsnumber.py b/youtube_dl/jsinterp/jsbuilt_ins/jsnumber.py index 2ff893ec0..e571f8350 100644 --- a/youtube_dl/jsinterp/jsbuilt_ins/jsnumber.py +++ b/youtube_dl/jsinterp/jsbuilt_ins/jsnumber.py @@ -7,6 +7,15 @@ from .jsobject import JSObject, JSObjectPrototype class JSNumberPrototype(JSObjectPrototype): + def __init__(self, value=None): + super(JSNumberPrototype, self).__init__() + if value is None: + # prototype + value = 0 + else: + self.value = value + self.own = {} + @staticmethod def _constructor(value=None): return JSNumber.construct(value) diff --git a/youtube_dl/jsinterp/jsbuilt_ins/jsobject.py b/youtube_dl/jsinterp/jsbuilt_ins/jsobject.py index 147611eca..da85bd91a 100644 --- a/youtube_dl/jsinterp/jsbuilt_ins/jsobject.py +++ b/youtube_dl/jsinterp/jsbuilt_ins/jsobject.py @@ -12,7 +12,9 @@ class JSObjectPrototype(JSProtoBase): def __init__(self, value=None): super(JSObjectPrototype, self).__init__() - self.value = {} if value is None else value + if value is not None: + self.props.update(self.own) + self.own = self.value = value @staticmethod def _constructor(value=None): @@ -64,7 +66,7 @@ class JSObject(JSBase): value = to_js(value) # TODO set [[Prototype]], [[Class]], [[Extensible]], internal methods if value is undefined or value is null: - return JSObjectPrototype() + return JSObjectPrototype({}) elif isinstance(value, JSObjectPrototype): return value elif isinstance(value, (JSStringPrototype, JSNumberPrototype, JSBooleanPrototype)): diff --git a/youtube_dl/jsinterp/jsbuilt_ins/jsstring.py b/youtube_dl/jsinterp/jsbuilt_ins/jsstring.py index 9243fb98f..bcabe74bd 100644 --- a/youtube_dl/jsinterp/jsbuilt_ins/jsstring.py +++ b/youtube_dl/jsinterp/jsbuilt_ins/jsstring.py @@ -7,10 +7,13 @@ from .jsobject import JSObject, JSObjectPrototype class JSStringPrototype(JSObjectPrototype): def __init__(self, value=None): + super(JSStringPrototype, self).__init__() if value is None: # prototype value = '' - super(JSStringPrototype, self).__init__(value) + else: + self.value = value + self.own = {'length': self._length} @property def _length(self):