[jsbuilt-ins] a riddle wrapped in mystery inside an enigma

This commit is contained in:
sulyi 2017-01-23 01:37:50 +01:00
parent 6f2ac27695
commit 0eef083da6

View File

@ -1,40 +1,65 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from types import FunctionType
class JSBase(object): class JSBase(object):
_name = '' def __init__(self, name, value):
self.props = self.__class__.props.copy()
def __init__(self): self.name = name
self._props = self.__class__._props.copy() self.value = value
def __str__(self): def __str__(self):
return '[native code]' return '[native code]'
_props = {} props = {}
def js(func):
def py2js(o):
if isinstance(o, (FunctionType, JSBase)):
return JSFunctionPrototype(o)
elif isinstance(o, dict):
return JSObjectPrototype(o)
elif isinstance(o, (list, tuple)):
return JSArrayPrototype(o)
else:
raise NotImplementedError
def wrapper(*args, **kwargs):
return py2js(func(*args, **kwargs))
return wrapper
class JSProtoBase(JSBase): class JSProtoBase(JSBase):
def __init__(self): def __init__(self):
super(JSProtoBase, self).__init__()
self._value = {}
cls = self.__class__ cls = self.__class__
while cls is not JSProtoBase: while cls is not JSProtoBase:
cls = cls.__base__ cls = cls.__base__
props = cls._props.copy() props = cls.props.copy()
props.update(self._props) props.update(self.props)
self._props = props self.props = props
super(JSProtoBase, self).__init__('', self.props)
def __str__(self): def __str__(self):
return '' return ''
def get_prop(self, prop): def _get_prop(self, prop):
result = self._value.get(prop) result = self.value.get(prop)
return result if result is not None else self._props.get(prop) if result is None:
result = self.props.get(prop)
return result
@js
def get_prop(self, prop):
return self._get_prop(prop)
@js
def call_prop(self, prop, *args): def call_prop(self, prop, *args):
return self.get_prop(prop)(self, *args) return self._get_prop(prop)(self, *args)
class JSObjectPrototype(JSProtoBase): class JSObjectPrototype(JSProtoBase):
@ -42,7 +67,7 @@ class JSObjectPrototype(JSProtoBase):
def __init__(self, value=None): def __init__(self, value=None):
super(JSObjectPrototype, self).__init__() super(JSObjectPrototype, self).__init__()
if value is not None: if value is not None:
self._value = value self.value = value
def _to_string(self): def _to_string(self):
return 'object to string' return 'object to string'
@ -54,7 +79,7 @@ class JSObjectPrototype(JSProtoBase):
return 'object value of' return 'object value of'
def _has_own_property(self, v): def _has_own_property(self, v):
return v in self._value return v in self.value
def _is_prototype_of(self, v): def _is_prototype_of(self, v):
return 'object has own prop' return 'object has own prop'
@ -62,7 +87,7 @@ class JSObjectPrototype(JSProtoBase):
def _is_property_enumerable(self, v): def _is_property_enumerable(self, v):
return 'object is property enumerable' return 'object is property enumerable'
_props = { props = {
'constructor': __init__, 'constructor': __init__,
'toString': _to_string, 'toString': _to_string,
'toLocaleString': _to_locale_string, 'toLocaleString': _to_locale_string,
@ -75,7 +100,8 @@ class JSObjectPrototype(JSProtoBase):
class JSObject(JSBase): class JSObject(JSBase):
_name = 'Object' def __init__(self):
super(JSObject, self).__init__(self.name, self.props)
def _get_prototype_of(self, o): def _get_prototype_of(self, o):
return 'object get prototype of' return 'object get prototype of'
@ -116,9 +142,10 @@ class JSObject(JSBase):
def _keys(self, o): def _keys(self, o):
return 'object keys' return 'object keys'
_props = { name = 'Object'
props = {
'length': 1, 'length': 1,
'prototype': JSObjectPrototype(JSObjectPrototype._props), 'prototype': JSObjectPrototype.props,
'getPrototypeOf': _get_prototype_of, 'getPrototypeOf': _get_prototype_of,
'getOwnPropertyDescriptor': _get_own_property_descriptor, 'getOwnPropertyDescriptor': _get_own_property_descriptor,
'getOwnPropertyNames': _get_own_property_names, 'getOwnPropertyNames': _get_own_property_names,
@ -140,15 +167,15 @@ class JSFunctionPrototype(JSObjectPrototype):
def __init__(self, *args): def __init__(self, *args):
body = args[-1] if args else '' body = args[-1] if args else ''
if isinstance(body, JSBase): if isinstance(body, JSBase):
super(JSFunctionPrototype, self).__init__(value=body._props) super(JSFunctionPrototype, self).__init__(body.props)
self._fname = body._name self.fname = body.name
else: else:
super(JSFunctionPrototype, self).__init__() super(JSFunctionPrototype, self).__init__()
self._fname = 'anonymous' self.fname = 'anonymous'
# FIXME: JSProtoBase sets body to '' instead of None # FIXME: JSProtoBase sets body to '' instead of None
self._body = str(body) self.body = str(body)
self._args = [sarg.strip() for arg in args[:-1] for sarg in str(arg).split(',')] self.args = [sarg.strip() for arg in args[:-1] for sarg in str(arg).split(',')]
# TODO check if self._args can be parsed as formal parameter list # TODO check if self._args can be parsed as formal parameter list
# TODO check if self._body can be parsed as function body # TODO check if self._body can be parsed as function body
# TODO set strict # TODO set strict
@ -159,15 +186,15 @@ class JSFunctionPrototype(JSObjectPrototype):
def _length(self): def _length(self):
# FIXME: returns maximum instead of "typical" number of arguments # FIXME: returns maximum instead of "typical" number of arguments
# Yeesh, I dare you to find anything like that in the python specification. # Yeesh, I dare you to find anything like that in the python specification.
return len(self._args) return len(self.args)
def _to_string(self): def _to_string(self):
if self._body is not None: if self.body is not None:
body = '\n' body = '\n'
body += '\t' + self._body if self._body else self._body body += '\t' + self.body if self.body else self.body
else: else:
body = '' body = ''
return 'function %s(%s) {%s\n}' % (self._fname, ', '.join(self._args), body) return 'function %s(%s) {%s\n}' % (self.fname, ', '.join(self.args), body)
def _apply(self, this_arg, arg_array): def _apply(self, this_arg, arg_array):
return 'function apply' return 'function apply'
@ -178,7 +205,7 @@ class JSFunctionPrototype(JSObjectPrototype):
def _bind(self, this_arg, *args): def _bind(self, this_arg, *args):
return 'function bind' return 'function bind'
_props = { props = {
'length': 0, 'length': 0,
'constructor': __init__, 'constructor': __init__,
'toString': _to_string, 'toString': _to_string,
@ -190,11 +217,11 @@ class JSFunctionPrototype(JSObjectPrototype):
class JSFuction(JSObject): class JSFuction(JSObject):
_name = 'Function' name = 'Function'
_props = { props = {
'length': 1, 'length': 1,
'prototype': JSFunctionPrototype(JSFunctionPrototype()) 'prototype': JSFunctionPrototype()
} }
@ -202,13 +229,19 @@ class JSArrayPrototype(JSObjectPrototype):
def __init__(self, value=None, length=0): def __init__(self, value=None, length=0):
super(JSArrayPrototype, self).__init__() super(JSArrayPrototype, self).__init__()
self.list = [] self.list = [] if value is None else value
self._value['length'] = self._length self.value['length'] = self._length
@property @property
def _length(self): def _length(self):
return len(self.list) return len(self.list)
def __str__(self):
return 'JSArrayPrototype: %s' % self.list
def __repr__(self):
return 'JSArrayPrototype(%s, %s)' % (self.list, self._length)
def _to_string(self): def _to_string(self):
return 'array to string' return 'array to string'
@ -250,7 +283,7 @@ class JSArrayPrototype(JSObjectPrototype):
def _last_index_of(self, elem, from_index=None): def _last_index_of(self, elem, from_index=None):
if from_index is None: if from_index is None:
from_index = len(self._value) - 1 from_index = len(self.value) - 1
return 'array index of' return 'array index of'
def _every(self, callback, this_arg=None): def _every(self, callback, this_arg=None):
@ -274,7 +307,7 @@ class JSArrayPrototype(JSObjectPrototype):
def _reduce_right(self, callback, init=None): def _reduce_right(self, callback, init=None):
return 'array reduce right' return 'array reduce right'
_props = { props = {
'length': 0, 'length': 0,
'constructor': __init__, 'constructor': __init__,
'toString': _to_string, 'toString': _to_string,
@ -303,17 +336,17 @@ class JSArrayPrototype(JSObjectPrototype):
class JSArray(JSObject): class JSArray(JSObject):
_name = 'Array' name = 'Array'
def _is_array(self, arg): def _is_array(self, arg):
return 'array is array' return 'array is array'
_props = { props = {
'length': 1, 'length': 1,
'prototype': JSObjectPrototype(JSArrayPrototype._props), 'prototype': JSArrayPrototype.props,
'isArray': _is_array 'isArray': _is_array
} }
global_obj = JSObjectPrototype({'Object': JSFunctionPrototype(JSObject()), global_obj = JSObjectPrototype({'Object': JSObject(),
'Array': JSFunctionPrototype(JSArray()), 'Array': JSArray(),
'Function': JSFunctionPrototype(JSFuction())}) 'Function': JSFuction()})