[jsbuilt-ins] adding Function and Array constructors
This commit is contained in:
parent
484a7d21ed
commit
65e9b0b5a4
@ -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,29 +240,46 @@ 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:
|
||||||
if isinstance(body, JSBase):
|
# prototype
|
||||||
super(JSFunctionPrototype, self).__init__(body.props)
|
|
||||||
self.fname = body.name
|
|
||||||
else:
|
|
||||||
super(JSFunctionPrototype, self).__init__()
|
super(JSFunctionPrototype, self).__init__()
|
||||||
self.fname = 'anonymous'
|
self.f_name = ''
|
||||||
|
self.body = ''
|
||||||
# FIXME: JSProtoBase sets body to '' instead of None
|
else:
|
||||||
self.body = str(body)
|
if isinstance(body, JSBase):
|
||||||
self.args = [sarg.strip() for arg in args[:-1] for sarg in str(arg).split(',')]
|
super(JSFunctionPrototype, self).__init__(body.props)
|
||||||
# TODO check if self._args can be parsed as formal parameter list
|
self.body = '[native code]'
|
||||||
# TODO check if self._body can be parsed as function body
|
elif isinstance(body, _native_function):
|
||||||
# TODO set strict
|
super(JSFunctionPrototype, self).__init__()
|
||||||
# TODO throw strict mode exceptions
|
self.body = '[native code]'
|
||||||
# (double argument, "eval" or "arguments" in arguments, function identifier is "eval" or "arguments")
|
else:
|
||||||
|
super(JSFunctionPrototype, self).__init__()
|
||||||
|
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
|
||||||
|
# TODO check if self._args can be parsed as formal parameter list
|
||||||
|
# TODO check if self._body can be parsed as function body
|
||||||
|
# TODO set strict
|
||||||
|
# TODO throw strict mode exceptions
|
||||||
|
# (double argument, "eval" or "arguments" in arguments, function identifier is "eval" or "arguments")
|
||||||
|
|
||||||
@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,7 +468,10 @@ class JSBooleanPrototype(JSObjectPrototype):
|
|||||||
|
|
||||||
|
|
||||||
class JSBoolean(JSObject):
|
class JSBoolean(JSObject):
|
||||||
pass
|
@staticmethod
|
||||||
|
def construct(value=None):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class JSNumberPrototype(JSObjectPrototype):
|
class JSNumberPrototype(JSObjectPrototype):
|
||||||
@ -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()})
|
||||||
|
Loading…
x
Reference in New Issue
Block a user