[jsbuilt-ins] adding Function and Array constructors

This commit is contained in:
sulyi 2017-01-24 18:46:04 +01:00
parent 484a7d21ed
commit 65e9b0b5a4

View File

@ -5,21 +5,23 @@ from types import FunctionType
from ..compat import compat_str from ..compat import compat_str
def _to_js(o): def _to_js(o, name=None):
if isinstance(o, JSProtoBase): if isinstance(o, JSProtoBase):
return o return o
elif o is None: elif o is None:
return undefined return undefined
elif isinstance(o, _native_bool): elif isinstance(o, _native_bool):
return JSBooleanPrototype(o) return JSBoolean.construct(o)
elif isinstance(o, _native_string): elif isinstance(o, _native_string):
return JSStringPrototype(o) return JSStringPrototype(o)
elif isinstance(o, _native_number): elif isinstance(o, _native_number):
return JSNumberPrototype(o) return JSNumberPrototype(o)
elif isinstance(o, _native_object): elif isinstance(o, _native_object):
return JSObjectPrototype(o) return JSObjectPrototype(o)
elif isinstance(o, _native_function) or (isinstance(o, JSBase) and hasattr(o, 'call')): elif isinstance(o, _native_function):
return JSFunctionPrototype(o) return JSFunctionPrototype(name, o, [])
elif isinstance(o, JSBase) and hasattr(o, 'call'):
return JSFunctionPrototype(o.name, o, [])
elif isinstance(o, _native_array): elif isinstance(o, _native_array):
return JSArrayPrototype(o) return JSArrayPrototype(o)
else: else:
@ -28,7 +30,7 @@ def _to_js(o):
def js(func): def js(func):
def wrapper(*args, **kwargs): def wrapper(*args, **kwargs):
return _to_js(func(*args, **kwargs)) return _to_js(*func(*args, **kwargs))
return wrapper return wrapper
@ -63,10 +65,10 @@ def to_object(o):
class JSBase(object): class JSBase(object):
def __init__(self, name, value): def __init__(self, name, own):
self.props = self.__class__.props.copy() self.props = self.__class__.props.copy()
self.name = name self.name = name
self.value = value self.own = own
def __str__(self): def __str__(self):
return '[native code]' return '[native code]'
@ -83,32 +85,35 @@ class JSProtoBase(JSBase):
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.value = {}
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)
if result is None:
result = self.own.get(prop)
if result is None: if result is None:
result = self.props.get(prop) result = self.props.get(prop)
return result return result
@js @js
def get_prop(self, prop): def get_prop(self, prop):
return self.__get_prop(prop) return self.__get_prop(prop), prop
@js @js
def call_prop(self, prop, *args, **kwargs): def call_prop(self, prop, *args, **kwargs):
func = self.__get_prop(prop) func = self.__get_prop(prop)
if isinstance(func, FunctionType): if isinstance(func, FunctionType):
return func(self, *args, **kwargs) return func(self, *args, **kwargs), prop
elif isinstance(func, staticmethod): elif isinstance(func, staticmethod):
return func.__func__(*args, **kwargs) return func.__func__(*args, **kwargs), prop
elif isinstance(func, classmethod): elif isinstance(func, classmethod):
return func.__func__(self.__class__, *args, **kwargs) return func.__func__(self.__class__, *args, **kwargs), prop
elif isinstance(func, JSBase) and hasattr(func, 'call'): elif isinstance(func, JSBase) and hasattr(func, 'call'):
return func.call(*args, **kwargs) return func.call(*args, **kwargs), prop
else: else:
# FIXME instead of prop should return the whole expression # FIXME instead of prop should return the whole expression
# needs to use internal exception # needs to use internal exception
@ -120,8 +125,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: self.value = {} if value is None else value
self.value = value
@staticmethod @staticmethod
def _constructor(value=None): def _constructor(value=None):
@ -143,7 +147,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.own
def _is_prototype_of(self, v): def _is_prototype_of(self, v):
return 'object has own prop' return 'object has own prop'
@ -182,7 +186,7 @@ class JSObject(JSBase):
return 'object desc' return 'object desc'
def _get_own_property_names(self, o): def _get_own_property_names(self, o):
return list(o.value.keys()) return list(o.own.keys())
def _create(self, o, props=None): def _create(self, o, props=None):
return 'object create' return 'object create'
@ -236,18 +240,26 @@ class JSObject(JSBase):
class JSFunctionPrototype(JSObjectPrototype): class JSFunctionPrototype(JSObjectPrototype):
def __init__(self, *args): def __init__(self, name, body, arguments):
body = args[-1] if args else '' if name is None and body is None and arguments is None:
# prototype
super(JSFunctionPrototype, self).__init__()
self.f_name = ''
self.body = ''
else:
if isinstance(body, JSBase): if isinstance(body, JSBase):
super(JSFunctionPrototype, self).__init__(body.props) super(JSFunctionPrototype, self).__init__(body.props)
self.fname = body.name self.body = '[native code]'
elif isinstance(body, _native_function):
super(JSFunctionPrototype, self).__init__()
self.body = '[native code]'
else: else:
super(JSFunctionPrototype, self).__init__() super(JSFunctionPrototype, self).__init__()
self.fname = 'anonymous' body = _to_js(name, body)
self.body = body.call_prop('toString') if body is not undefined or body is not null else ''
self.f_name = name
self.arguments = list(arguments)
# FIXME: JSProtoBase sets body to '' instead of None # FIXME: JSProtoBase sets body to '' instead of None
self.body = str(body)
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
@ -256,9 +268,18 @@ class JSFunctionPrototype(JSObjectPrototype):
@property @property
def _length(self): def _length(self):
# 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([arg for arg, init in self.arguments if init is not None])
@staticmethod
def _constructor(arguments=None):
if arguments is None:
body = ''
arguments = []
else:
body = arguments[-1] if arguments else ''
arguments = arguments[:-1]
return JSFunctionPrototype('anonymous', body, arguments)
def _to_string(self): def _to_string(self):
if self.body is not None: if self.body is not None:
@ -266,7 +287,10 @@ class JSFunctionPrototype(JSObjectPrototype):
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.f_name,
', '.join(arg if init is None else arg + '=' + init for arg, init in self.arguments),
body)
def _apply(self, this_arg, arg_array): def _apply(self, this_arg, arg_array):
return 'function apply' return 'function apply'
@ -279,7 +303,7 @@ class JSFunctionPrototype(JSObjectPrototype):
props = { props = {
'length': 0, 'length': 0,
'constructor': __init__, 'constructor': _constructor,
'toString': _to_string, 'toString': _to_string,
'apply': _apply, 'apply': _apply,
'call': _call, 'call': _call,
@ -287,12 +311,20 @@ class JSFunctionPrototype(JSObjectPrototype):
} }
class JSFuction(JSObject): class JSFunction(JSObject):
@staticmethod
def construct(*args, **kwargs):
return JSFunctionPrototype._constructor(*args)
@staticmethod
def call(*args, **kwargs):
return JSFunction.construct(*args, **kwargs)
name = 'Function' name = 'Function'
props = { props = {
'length': 1, 'length': 1,
'prototype': JSFunctionPrototype() 'prototype': JSFunctionPrototype(None, None, None)
} }
@ -300,18 +332,23 @@ 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 = [] if value is None else value self.value = [] if value is None else value
self.value['length'] = self._length
@property @property
def _length(self): def _length(self):
return len(self.list) return len(self.value)
def __str__(self): def __str__(self):
return 'JSArrayPrototype: %s' % self.list return 'JSArrayPrototype: %s' % self.value
def __repr__(self): def __repr__(self):
return 'JSArrayPrototype(%s, %s)' % (self.list, self._length) return 'JSArrayPrototype(%s, %s)' % (self.value, self._length)
@staticmethod
def _constructor(value=None):
array = JSArrayPrototype(value)
array.own = {'length': array._length}
return array
def _to_string(self): def _to_string(self):
return 'array to string' return 'array to string'
@ -380,7 +417,7 @@ class JSArrayPrototype(JSObjectPrototype):
props = { props = {
'length': 0, 'length': 0,
'constructor': __init__, 'constructor': _constructor,
'toString': _to_string, 'toString': _to_string,
'toLocaleString': _to_locale_string, 'toLocaleString': _to_locale_string,
'concat': _concat, 'concat': _concat,
@ -413,7 +450,7 @@ class JSArray(JSObject):
name = 'Array' name = 'Array'
props = { props = {
'length': 1, 'length': 1,
'prototype': JSArrayPrototype.props, 'prototype': JSArrayPrototype(),
'isArray': _is_array 'isArray': _is_array
} }
@ -431,9 +468,12 @@ class JSBooleanPrototype(JSObjectPrototype):
class JSBoolean(JSObject): class JSBoolean(JSObject):
@staticmethod
def construct(value=None):
pass pass
class JSNumberPrototype(JSObjectPrototype): class JSNumberPrototype(JSObjectPrototype):
pass pass
@ -444,8 +484,8 @@ class JSNumber(JSObject):
undefined = object() undefined = object()
null = object() null = object()
true = JSBooleanPrototype(True) true = JSBoolean.construct(True)
false = JSBooleanPrototype(False) false = JSBoolean.construct(False)
_native_bool = bool _native_bool = bool
_native_string = compat_str _native_string = compat_str
@ -461,6 +501,6 @@ _string_type = object()
_number_type = object() _number_type = object()
_object_type = object() _object_type = object()
global_obj = JSObjectPrototype({'Object': JSObject(), global_obj = JSObject.construct({'Object': JSObject(),
'Array': JSArray(), 'Array': JSArray(),
'Function': JSFuction()}) 'Function': JSFunction()})