Test - CF per XBOX
This commit is contained in:
@@ -1,929 +0,0 @@
|
||||
from __future__ import unicode_literals
|
||||
import re
|
||||
|
||||
import datetime
|
||||
|
||||
from .desc import *
|
||||
from .simplex import *
|
||||
from .conversions import *
|
||||
|
||||
from pyjsparser import PyJsParser
|
||||
|
||||
import six
|
||||
if six.PY2:
|
||||
from itertools import izip
|
||||
else:
|
||||
izip = zip
|
||||
|
||||
|
||||
|
||||
|
||||
def Type(obj):
|
||||
return obj.TYPE
|
||||
|
||||
|
||||
# 8.6.2
|
||||
class PyJs(object):
|
||||
TYPE = 'Object'
|
||||
IS_CONSTRUCTOR = False
|
||||
|
||||
prototype = None
|
||||
Class = None
|
||||
extensible = True
|
||||
value = None
|
||||
|
||||
own = {}
|
||||
|
||||
def get_member(self, unconverted_prop):
|
||||
return self.get(to_string(unconverted_prop))
|
||||
|
||||
def put_member(self, unconverted_prop, val):
|
||||
return self.put(to_string(unconverted_prop), val)
|
||||
|
||||
def get(self, prop):
|
||||
assert type(prop) == unicode
|
||||
cand = self.get_property(prop)
|
||||
if cand is None:
|
||||
return undefined
|
||||
if is_data_descriptor(cand):
|
||||
return cand['value']
|
||||
if is_undefined(cand['get']):
|
||||
return undefined
|
||||
return cand['get'].call(self)
|
||||
|
||||
def get_own_property(self, prop):
|
||||
assert type(prop) == unicode
|
||||
# takes py returns py
|
||||
return self.own.get(prop)
|
||||
|
||||
def get_property(self, prop):
|
||||
assert type(prop) == unicode
|
||||
# take py returns py
|
||||
cand = self.get_own_property(prop)
|
||||
if cand:
|
||||
return cand
|
||||
if self.prototype is not None:
|
||||
return self.prototype.get_property(prop)
|
||||
|
||||
def put(self, prop, val, throw=False):
|
||||
assert type(prop) == unicode
|
||||
# takes py, returns none
|
||||
if not self.can_put(prop):
|
||||
if throw:
|
||||
raise MakeError('TypeError', 'Could not define own property')
|
||||
return
|
||||
own_desc = self.get_own_property(prop)
|
||||
if is_data_descriptor(own_desc):
|
||||
self.own[prop]['value'] = val
|
||||
return
|
||||
desc = self.get_property(prop)
|
||||
if is_accessor_descriptor(desc):
|
||||
desc['set'].call(
|
||||
self, (val, )) # calling setter on own or inherited element
|
||||
else: # new property
|
||||
self.own[prop] = {
|
||||
'value': val,
|
||||
'writable': True,
|
||||
'configurable': True,
|
||||
'enumerable': True
|
||||
}
|
||||
|
||||
def can_put(self, prop): # to check
|
||||
assert type(prop) == unicode, type(prop)
|
||||
# takes py returns py
|
||||
desc = self.get_own_property(prop)
|
||||
if desc: # if we have this property
|
||||
if is_accessor_descriptor(desc):
|
||||
return is_callable(
|
||||
desc['set']) # Check if setter method is defined
|
||||
else: # data desc
|
||||
return desc['writable']
|
||||
if self.prototype is None:
|
||||
return self.extensible
|
||||
inherited = self.prototype.get_property(prop)
|
||||
if inherited is None:
|
||||
return self.extensible
|
||||
if is_accessor_descriptor(inherited):
|
||||
return not is_undefined(inherited['set'])
|
||||
elif self.extensible:
|
||||
return inherited['writable'] # weird...
|
||||
return False
|
||||
|
||||
def has_property(self, prop):
|
||||
assert type(prop) == unicode
|
||||
# takes py returns Py
|
||||
return self.get_property(prop) is not None
|
||||
|
||||
def delete(self, prop, throw=False):
|
||||
assert type(prop) == unicode
|
||||
# takes py, returns py
|
||||
desc = self.get_own_property(prop)
|
||||
if desc is None:
|
||||
return True
|
||||
if desc['configurable']:
|
||||
del self.own[prop]
|
||||
return True
|
||||
if throw:
|
||||
raise MakeError('TypeError', 'Could not define own property')
|
||||
return False
|
||||
|
||||
def default_value(self, hint=None):
|
||||
order = ('valueOf', 'toString')
|
||||
if hint == 'String' or (hint is None and self.Class == 'Date'):
|
||||
order = ('toString', 'valueOf')
|
||||
for meth_name in order:
|
||||
method = self.get(meth_name)
|
||||
if method is not None and is_callable(method):
|
||||
cand = method.call(self, ())
|
||||
if is_primitive(cand):
|
||||
return cand
|
||||
raise MakeError('TypeError',
|
||||
'Cannot convert object to primitive value')
|
||||
|
||||
def define_own_property(
|
||||
self, prop, desc,
|
||||
throw): # Internal use only. External through Object
|
||||
assert type(prop) == unicode
|
||||
# takes Py, returns Py
|
||||
# prop must be a Py string. Desc is either a descriptor or accessor.
|
||||
# Messy method - raw translation from Ecma spec to prevent any bugs. # todo check this
|
||||
current = self.get_own_property(prop)
|
||||
|
||||
extensible = self.extensible
|
||||
if not current: # We are creating a new OWN property
|
||||
if not extensible:
|
||||
if throw:
|
||||
raise MakeError('TypeError',
|
||||
'Could not define own property')
|
||||
return False
|
||||
# extensible must be True
|
||||
if is_data_descriptor(desc) or is_generic_descriptor(desc):
|
||||
DEFAULT_DATA_DESC = {
|
||||
'value': undefined, # undefined
|
||||
'writable': False,
|
||||
'enumerable': False,
|
||||
'configurable': False
|
||||
}
|
||||
DEFAULT_DATA_DESC.update(desc)
|
||||
self.own[prop] = DEFAULT_DATA_DESC
|
||||
else:
|
||||
DEFAULT_ACCESSOR_DESC = {
|
||||
'get': undefined, # undefined
|
||||
'set': undefined, # undefined
|
||||
'enumerable': False,
|
||||
'configurable': False
|
||||
}
|
||||
DEFAULT_ACCESSOR_DESC.update(desc)
|
||||
self.own[prop] = DEFAULT_ACCESSOR_DESC
|
||||
return True
|
||||
# therefore current exists!
|
||||
if not desc or desc == current: # We don't need to change anything.
|
||||
return True
|
||||
configurable = current['configurable']
|
||||
if not configurable: # Prevent changing params
|
||||
if desc.get('configurable'):
|
||||
if throw:
|
||||
raise MakeError('TypeError',
|
||||
'Could not define own property')
|
||||
return False
|
||||
if 'enumerable' in desc and desc['enumerable'] != current[
|
||||
'enumerable']:
|
||||
if throw:
|
||||
raise MakeError('TypeError',
|
||||
'Could not define own property')
|
||||
return False
|
||||
if is_generic_descriptor(desc):
|
||||
pass
|
||||
elif is_data_descriptor(current) != is_data_descriptor(desc):
|
||||
# we want to change the current type of property
|
||||
if not configurable:
|
||||
if throw:
|
||||
raise MakeError('TypeError',
|
||||
'Could not define own property')
|
||||
return False
|
||||
if is_data_descriptor(current): # from data to setter
|
||||
del current['value']
|
||||
del current['writable']
|
||||
current['set'] = undefined # undefined
|
||||
current['get'] = undefined # undefined
|
||||
else: # from setter to data
|
||||
del current['set']
|
||||
del current['get']
|
||||
current['value'] = undefined # undefined
|
||||
current['writable'] = False
|
||||
elif is_data_descriptor(current) and is_data_descriptor(desc):
|
||||
if not configurable:
|
||||
if not current['writable'] and desc.get('writable'):
|
||||
if throw:
|
||||
raise MakeError('TypeError',
|
||||
'Could not define own property')
|
||||
return False
|
||||
if not current['writable'] and 'value' in desc and current[
|
||||
'value'] != desc['value']:
|
||||
if throw:
|
||||
raise MakeError('TypeError',
|
||||
'Could not define own property')
|
||||
return False
|
||||
elif is_accessor_descriptor(current) and is_accessor_descriptor(desc):
|
||||
if not configurable:
|
||||
if 'set' in desc and desc['set'] != current['set']:
|
||||
if throw:
|
||||
raise MakeError('TypeError',
|
||||
'Could not define own property')
|
||||
return False
|
||||
if 'get' in desc and desc['get'] != current['get']:
|
||||
if throw:
|
||||
raise MakeError('TypeError',
|
||||
'Could not define own property')
|
||||
return False
|
||||
current.update(desc)
|
||||
return True
|
||||
|
||||
def create(self, args, space):
|
||||
'''Generally not a constructor, raise an error'''
|
||||
raise MakeError('TypeError', '%s is not a constructor' % self.Class)
|
||||
|
||||
|
||||
def get_member(
|
||||
self, prop, space
|
||||
): # general member getter, prop has to be unconverted prop. it is it can be any value
|
||||
typ = type(self)
|
||||
if typ not in PRIMITIVES: # most likely getter for object
|
||||
return self.get_member(
|
||||
prop
|
||||
) # <- object can implement this to support faster prop getting. ex array.
|
||||
elif typ == unicode: # then probably a String
|
||||
if type(prop) == float and is_finite(prop):
|
||||
index = int(prop)
|
||||
if index == prop and 0 <= index < len(self):
|
||||
return self[index]
|
||||
s_prop = to_string(prop)
|
||||
if s_prop == 'length':
|
||||
return float(len(self))
|
||||
elif s_prop.isdigit():
|
||||
index = int(s_prop)
|
||||
if 0 <= index < len(self):
|
||||
return self[index]
|
||||
# use standard string prototype
|
||||
return space.StringPrototype.get(s_prop)
|
||||
# maybe an index
|
||||
elif typ == float:
|
||||
# use standard number prototype
|
||||
return space.NumberPrototype.get(to_string(prop))
|
||||
elif typ == bool:
|
||||
return space.BooleanPrototype.get(to_string(prop))
|
||||
elif typ is UNDEFINED_TYPE:
|
||||
raise MakeError('TypeError',
|
||||
"Cannot read property '%s' of undefined" % prop)
|
||||
elif typ is NULL_TYPE:
|
||||
raise MakeError('TypeError',
|
||||
"Cannot read property '%s' of null" % prop)
|
||||
else:
|
||||
raise RuntimeError('Unknown type! - ' + repr(typ))
|
||||
|
||||
|
||||
def get_member_dot(self, prop, space):
|
||||
# dot member getter, prop has to be unicode
|
||||
typ = type(self)
|
||||
if typ not in PRIMITIVES: # most likely getter for object
|
||||
return self.get(prop)
|
||||
elif typ == unicode: # then probably a String
|
||||
if prop == 'length':
|
||||
return float(len(self))
|
||||
elif prop.isdigit():
|
||||
index = int(prop)
|
||||
if 0 <= index < len(self):
|
||||
return self[index]
|
||||
else:
|
||||
# use standard string prototype
|
||||
return space.StringPrototype.get(prop)
|
||||
# maybe an index
|
||||
elif typ == float:
|
||||
# use standard number prototype
|
||||
return space.NumberPrototype.get(prop)
|
||||
elif typ == bool:
|
||||
return space.BooleanPrototype.get(prop)
|
||||
elif typ in (UNDEFINED_TYPE, NULL_TYPE):
|
||||
raise MakeError('TypeError',
|
||||
"Cannot read property '%s' of undefined" % prop)
|
||||
else:
|
||||
raise RuntimeError('Unknown type! - ' + repr(typ))
|
||||
|
||||
|
||||
# Object
|
||||
|
||||
|
||||
class PyJsObject(PyJs):
|
||||
TYPE = 'Object'
|
||||
Class = 'Object'
|
||||
|
||||
def __init__(self, prototype=None):
|
||||
self.prototype = prototype
|
||||
self.own = {}
|
||||
|
||||
def _init(self, props, vals):
|
||||
i = 0
|
||||
for prop, kind in props:
|
||||
if prop in self.own: # just check... probably will not happen very often.
|
||||
if is_data_descriptor(self.own[prop]):
|
||||
if kind != 'i':
|
||||
raise MakeError(
|
||||
'SyntaxError',
|
||||
'Invalid object initializer! Duplicate property name "%s"'
|
||||
% prop)
|
||||
else:
|
||||
if kind == 'i' or (kind == 'g' and 'get' in self.own[prop]
|
||||
) or (kind == 's'
|
||||
and 'set' in self.own[prop]):
|
||||
raise MakeError(
|
||||
'SyntaxError',
|
||||
'Invalid object initializer! Duplicate setter/getter of prop: "%s"'
|
||||
% prop)
|
||||
|
||||
if kind == 'i': # init
|
||||
self.own[prop] = {
|
||||
'value': vals[i],
|
||||
'writable': True,
|
||||
'enumerable': True,
|
||||
'configurable': True
|
||||
}
|
||||
elif kind == 'g': # get
|
||||
self.define_own_property(prop, {
|
||||
'get': vals[i],
|
||||
'enumerable': True,
|
||||
'configurable': True
|
||||
}, False)
|
||||
elif kind == 's': # get
|
||||
self.define_own_property(prop, {
|
||||
'get': vals[i],
|
||||
'enumerable': True,
|
||||
'configurable': True
|
||||
}, False)
|
||||
else:
|
||||
raise RuntimeError(
|
||||
'Invalid property kind - %s. Expected one of i, g, s.' %
|
||||
repr(kind))
|
||||
i += 1
|
||||
|
||||
def _set_props(self, prop_descs):
|
||||
for prop, desc in six.iteritems(prop_descs):
|
||||
self.define_own_property(prop, desc)
|
||||
|
||||
|
||||
# Array
|
||||
|
||||
|
||||
# todo Optimise Array - extremely slow due to index conversions from str to int and back etc.
|
||||
# solution - make get and put methods callable with any type of prop and handle conversions from inside
|
||||
# if not array then use to_string(prop). In array if prop is integer then just use it
|
||||
# also consider removal of these stupid writable, enumerable etc for ints.
|
||||
class PyJsArray(PyJs):
|
||||
Class = 'Array'
|
||||
|
||||
def __init__(self, length, prototype=None):
|
||||
self.prototype = prototype
|
||||
self.own = {
|
||||
'length': {
|
||||
'value': float(length),
|
||||
'writable': True,
|
||||
'enumerable': False,
|
||||
'configurable': False
|
||||
}
|
||||
}
|
||||
|
||||
def _init(self, elements):
|
||||
for i, ele in enumerate(elements):
|
||||
if ele is None: continue
|
||||
self.own[unicode(i)] = {
|
||||
'value': ele,
|
||||
'writable': True,
|
||||
'enumerable': True,
|
||||
'configurable': True
|
||||
}
|
||||
|
||||
def put(self, prop, val, throw=False):
|
||||
assert type(val) != int
|
||||
# takes py, returns none
|
||||
if not self.can_put(prop):
|
||||
if throw:
|
||||
raise MakeError('TypeError', 'Could not define own property')
|
||||
return
|
||||
own_desc = self.get_own_property(prop)
|
||||
if is_data_descriptor(own_desc):
|
||||
self.define_own_property(prop, {'value': val}, False)
|
||||
return
|
||||
desc = self.get_property(prop)
|
||||
if is_accessor_descriptor(desc):
|
||||
desc['set'].call(
|
||||
self, (val, )) # calling setter on own or inherited element
|
||||
else: # new property
|
||||
self.define_own_property(
|
||||
prop, {
|
||||
'value': val,
|
||||
'writable': True,
|
||||
'configurable': True,
|
||||
'enumerable': True
|
||||
}, False)
|
||||
|
||||
def define_own_property(self, prop, desc, throw):
|
||||
assert type(desc.get('value')) != int
|
||||
old_len_desc = self.get_own_property('length')
|
||||
old_len = old_len_desc['value'] # value is js type so convert to py.
|
||||
if prop == 'length':
|
||||
if 'value' not in desc:
|
||||
return PyJs.define_own_property(self, prop, desc, False)
|
||||
new_len = to_uint32(desc['value'])
|
||||
if new_len != to_number(desc['value']):
|
||||
raise MakeError('RangeError', 'Invalid range!')
|
||||
new_desc = dict((k, v) for k, v in six.iteritems(desc))
|
||||
new_desc['value'] = float(new_len)
|
||||
if new_len >= old_len:
|
||||
return PyJs.define_own_property(self, prop, new_desc, False)
|
||||
if not old_len_desc['writable']:
|
||||
return False
|
||||
if 'writable' not in new_desc or new_desc['writable'] == True:
|
||||
new_writable = True
|
||||
else:
|
||||
new_writable = False
|
||||
new_desc['writable'] = True
|
||||
if not PyJs.define_own_property(self, prop, new_desc, False):
|
||||
return False
|
||||
if new_len < old_len:
|
||||
# not very efficient for sparse arrays, so using different method for sparse:
|
||||
if old_len > 30 * len(self.own):
|
||||
for ele in self.own.keys():
|
||||
if ele.isdigit() and int(ele) >= new_len:
|
||||
if not self.delete(
|
||||
ele
|
||||
): # if failed to delete set len to current len and reject.
|
||||
new_desc['value'] = old_len + 1.
|
||||
if not new_writable:
|
||||
new_desc['writable'] = False
|
||||
PyJs.define_own_property(
|
||||
self, prop, new_desc, False)
|
||||
return False
|
||||
old_len = new_len
|
||||
else: # standard method
|
||||
while new_len < old_len:
|
||||
old_len -= 1
|
||||
if not self.delete(
|
||||
unicode(int(old_len))
|
||||
): # if failed to delete set len to current len and reject.
|
||||
new_desc['value'] = old_len + 1.
|
||||
if not new_writable:
|
||||
new_desc['writable'] = False
|
||||
PyJs.define_own_property(self, prop, new_desc,
|
||||
False)
|
||||
return False
|
||||
if not new_writable:
|
||||
self.own['length']['writable'] = False
|
||||
return True
|
||||
|
||||
elif prop.isdigit():
|
||||
index = to_uint32(prop)
|
||||
if index >= old_len and not old_len_desc['writable']:
|
||||
return False
|
||||
if not PyJs.define_own_property(self, prop, desc, False):
|
||||
return False
|
||||
if index >= old_len:
|
||||
old_len_desc['value'] = index + 1.
|
||||
return True
|
||||
else:
|
||||
return PyJs.define_own_property(self, prop, desc, False)
|
||||
|
||||
def to_list(self):
|
||||
return [
|
||||
self.get(str(e)) for e in xrange(self.get('length').to_uint32())
|
||||
]
|
||||
|
||||
|
||||
# database with compiled patterns. Js pattern -> Py pattern.
|
||||
REGEXP_DB = {}
|
||||
|
||||
|
||||
class PyJsRegExp(PyJs):
|
||||
Class = 'RegExp'
|
||||
|
||||
def __init__(self, body, flags, prototype=None):
|
||||
self.prototype = prototype
|
||||
self.glob = True if 'g' in flags else False
|
||||
self.ignore_case = re.IGNORECASE if 'i' in flags else 0
|
||||
self.multiline = re.MULTILINE if 'm' in flags else 0
|
||||
self.value = body
|
||||
|
||||
if (body, flags) in REGEXP_DB:
|
||||
self.pat = REGEXP_DB[body, flags]
|
||||
else:
|
||||
comp = None
|
||||
try:
|
||||
# converting JS regexp pattern to Py pattern.
|
||||
possible_fixes = [(u'[]', u'[\0]'), (u'[^]', u'[^\0]'),
|
||||
(u'nofix1791', u'nofix1791')]
|
||||
reg = self.value
|
||||
for fix, rep in possible_fixes:
|
||||
comp = PyJsParser()._interpret_regexp(reg, flags)
|
||||
#print 'reg -> comp', reg, '->', comp
|
||||
try:
|
||||
self.pat = re.compile(
|
||||
comp, self.ignore_case | self.multiline)
|
||||
#print reg, '->', comp
|
||||
break
|
||||
except:
|
||||
reg = reg.replace(fix, rep)
|
||||
# print 'Fix', fix, '->', rep, '=', reg
|
||||
else:
|
||||
raise Exception()
|
||||
REGEXP_DB[body, flags] = self.pat
|
||||
except:
|
||||
#print 'Invalid pattern...', self.value, comp
|
||||
raise MakeError(
|
||||
'SyntaxError',
|
||||
'Invalid RegExp pattern: %s -> %s' % (repr(self.value),
|
||||
repr(comp)))
|
||||
# now set own properties:
|
||||
self.own = {
|
||||
'source': {
|
||||
'value': self.value,
|
||||
'enumerable': False,
|
||||
'writable': False,
|
||||
'configurable': False
|
||||
},
|
||||
'global': {
|
||||
'value': self.glob,
|
||||
'enumerable': False,
|
||||
'writable': False,
|
||||
'configurable': False
|
||||
},
|
||||
'ignoreCase': {
|
||||
'value': bool(self.ignore_case),
|
||||
'enumerable': False,
|
||||
'writable': False,
|
||||
'configurable': False
|
||||
},
|
||||
'multiline': {
|
||||
'value': bool(self.multiline),
|
||||
'enumerable': False,
|
||||
'writable': False,
|
||||
'configurable': False
|
||||
},
|
||||
'lastIndex': {
|
||||
'value': 0.,
|
||||
'enumerable': False,
|
||||
'writable': True,
|
||||
'configurable': False
|
||||
}
|
||||
}
|
||||
|
||||
def match(self, string, pos):
|
||||
'''string is of course a py string'''
|
||||
return self.pat.match(string, int(pos))
|
||||
|
||||
|
||||
class PyJsError(PyJs):
|
||||
Class = 'Error'
|
||||
extensible = True
|
||||
|
||||
def __init__(self, message=None, prototype=None):
|
||||
self.prototype = prototype
|
||||
self.own = {}
|
||||
if message is not None:
|
||||
self.put('message', to_string(message))
|
||||
self.own['message']['enumerable'] = False
|
||||
|
||||
|
||||
class PyJsDate(PyJs):
|
||||
Class = 'Date'
|
||||
UTCToLocal = None # todo UTC to local should be imported!
|
||||
|
||||
def __init__(self, value, prototype=None):
|
||||
self.value = value
|
||||
self.own = {}
|
||||
self.prototype = prototype
|
||||
|
||||
# todo fix this problematic datetime part
|
||||
def to_local_dt(self):
|
||||
return datetime.datetime.utcfromtimestamp(
|
||||
self.UTCToLocal(self.value) // 1000)
|
||||
|
||||
def to_utc_dt(self):
|
||||
return datetime.datetime.utcfromtimestamp(self.value // 1000)
|
||||
|
||||
def local_strftime(self, pattern):
|
||||
if self.value is NaN:
|
||||
return 'Invalid Date'
|
||||
try:
|
||||
dt = self.to_local_dt()
|
||||
except:
|
||||
raise MakeError(
|
||||
'TypeError',
|
||||
'unsupported date range. Will fix in future versions')
|
||||
try:
|
||||
return dt.strftime(pattern)
|
||||
except:
|
||||
raise MakeError(
|
||||
'TypeError',
|
||||
'Could not generate date string from this date (limitations of python.datetime)'
|
||||
)
|
||||
|
||||
def utc_strftime(self, pattern):
|
||||
if self.value is NaN:
|
||||
return 'Invalid Date'
|
||||
try:
|
||||
dt = self.to_utc_dt()
|
||||
except:
|
||||
raise MakeError(
|
||||
'TypeError',
|
||||
'unsupported date range. Will fix in future versions')
|
||||
try:
|
||||
return dt.strftime(pattern)
|
||||
except:
|
||||
raise MakeError(
|
||||
'TypeError',
|
||||
'Could not generate date string from this date (limitations of python.datetime)'
|
||||
)
|
||||
|
||||
|
||||
# Scope class it will hold all the variables accessible to user
|
||||
class Scope(PyJs):
|
||||
Class = 'Global'
|
||||
extensible = True
|
||||
IS_CHILD_SCOPE = True
|
||||
THIS_BINDING = None
|
||||
space = None
|
||||
exe = None
|
||||
|
||||
# todo speed up!
|
||||
# in order to speed up this very important class the top scope should behave differently than
|
||||
# child scopes, child scope should not have this property descriptor thing because they cant be changed anyway
|
||||
# they are all confugurable= False
|
||||
|
||||
def __init__(self, scope, space, parent=None):
|
||||
"""Doc"""
|
||||
self.space = space
|
||||
self.prototype = parent
|
||||
if type(scope) is not dict:
|
||||
assert parent is not None, 'You initialised the WITH_SCOPE without a parent scope.'
|
||||
self.own = scope
|
||||
self.is_with_scope = True
|
||||
else:
|
||||
self.is_with_scope = False
|
||||
if parent is None:
|
||||
# global, top level scope
|
||||
self.own = {}
|
||||
for k, v in six.iteritems(scope):
|
||||
# set all the global items
|
||||
self.define_own_property(
|
||||
k, {
|
||||
'value': v,
|
||||
'configurable': False,
|
||||
'writable': False,
|
||||
'enumerable': False
|
||||
}, False)
|
||||
else:
|
||||
# not global, less powerful but faster closure.
|
||||
self.own = scope # simple dictionary which maps name directly to js object.
|
||||
|
||||
self.par = super(Scope, self)
|
||||
self.stack = []
|
||||
|
||||
def register(self, var):
|
||||
# registered keeps only global registered variables
|
||||
if self.prototype is None:
|
||||
# define in global scope
|
||||
if var in self.own:
|
||||
self.own[var]['configurable'] = False
|
||||
else:
|
||||
self.define_own_property(
|
||||
var, {
|
||||
'value': undefined,
|
||||
'configurable': False,
|
||||
'writable': True,
|
||||
'enumerable': True
|
||||
}, False)
|
||||
elif var not in self.own:
|
||||
# define in local scope since it has not been defined yet
|
||||
self.own[var] = undefined # default value
|
||||
|
||||
def registers(self, vars):
|
||||
"""register multiple variables"""
|
||||
for var in vars:
|
||||
self.register(var)
|
||||
|
||||
def put(self, var, val, throw=False):
|
||||
if self.prototype is None:
|
||||
desc = self.own.get(var) # global scope
|
||||
if desc is None:
|
||||
self.par.put(var, val, False)
|
||||
else:
|
||||
if desc['writable']: # todo consider getters/setters
|
||||
desc['value'] = val
|
||||
else:
|
||||
if self.is_with_scope:
|
||||
if self.own.has_property(var):
|
||||
return self.own.put(var, val, throw=throw)
|
||||
else:
|
||||
return self.prototype.put(var, val)
|
||||
# trying to put in local scope
|
||||
# we dont know yet in which scope we should place this var
|
||||
elif var in self.own:
|
||||
self.own[var] = val
|
||||
return val
|
||||
else:
|
||||
# try to put in the lower scope since we cant put in this one (var wasn't registered)
|
||||
return self.prototype.put(var, val)
|
||||
|
||||
def get(self, var, throw=False):
|
||||
if self.prototype is not None:
|
||||
if self.is_with_scope:
|
||||
cand = None if not self.own.has_property(
|
||||
var) else self.own.get(var)
|
||||
else:
|
||||
# fast local scope
|
||||
cand = self.own.get(var)
|
||||
if cand is None:
|
||||
return self.prototype.get(var, throw)
|
||||
return cand
|
||||
# slow, global scope
|
||||
if var not in self.own:
|
||||
# try in ObjectPrototype...
|
||||
if var in self.space.ObjectPrototype.own:
|
||||
return self.space.ObjectPrototype.get(var)
|
||||
if throw:
|
||||
raise MakeError('ReferenceError', '%s is not defined' % var)
|
||||
return undefined
|
||||
cand = self.own[var].get('value')
|
||||
return cand if cand is not None else self.own[var]['get'].call(self)
|
||||
|
||||
def delete(self, var, throw=False):
|
||||
if self.prototype is not None:
|
||||
if self.is_with_scope:
|
||||
if self.own.has_property(var):
|
||||
return self.own.delete(var)
|
||||
elif var in self.own:
|
||||
return False
|
||||
return self.prototype.delete(var)
|
||||
# we are in global scope here. Must exist and be configurable to delete
|
||||
if var not in self.own:
|
||||
# this var does not exist, why do you want to delete it???
|
||||
return True
|
||||
if self.own[var]['configurable']:
|
||||
del self.own[var]
|
||||
return True
|
||||
# not configurable, cant delete
|
||||
return False
|
||||
|
||||
|
||||
def get_new_arguments_obj(args, space):
|
||||
obj = space.NewObject()
|
||||
obj.Class = 'Arguments'
|
||||
obj.define_own_property(
|
||||
'length', {
|
||||
'value': float(len(args)),
|
||||
'writable': True,
|
||||
'enumerable': False,
|
||||
'configurable': True
|
||||
}, False)
|
||||
for i, e in enumerate(args):
|
||||
obj.put(unicode(i), e)
|
||||
return obj
|
||||
|
||||
|
||||
#Function
|
||||
class PyJsFunction(PyJs):
|
||||
Class = 'Function'
|
||||
source = '{ [native code] }'
|
||||
IS_CONSTRUCTOR = True
|
||||
|
||||
def __init__(self,
|
||||
code,
|
||||
ctx,
|
||||
params,
|
||||
name,
|
||||
space,
|
||||
is_declaration,
|
||||
definitions,
|
||||
prototype=None):
|
||||
self.prototype = prototype
|
||||
self.own = {}
|
||||
|
||||
self.code = code
|
||||
if type(
|
||||
self.code
|
||||
) == int: # just a label pointing to the beginning of the code.
|
||||
self.is_native = False
|
||||
else:
|
||||
self.is_native = True # python function
|
||||
|
||||
self.ctx = ctx
|
||||
|
||||
self.params = params
|
||||
self.arguments_in_params = 'arguments' in params
|
||||
self.definitions = definitions
|
||||
|
||||
# todo remove this check later
|
||||
for p in params:
|
||||
assert p in self.definitions
|
||||
|
||||
self.name = name
|
||||
self.space = space
|
||||
self.is_declaration = is_declaration
|
||||
|
||||
#set own property length to the number of arguments
|
||||
self.own['length'] = {
|
||||
'value': float(len(params)),
|
||||
'writable': False,
|
||||
'enumerable': False,
|
||||
'configurable': False
|
||||
}
|
||||
|
||||
if name:
|
||||
self.own['name'] = {
|
||||
'value': name,
|
||||
'writable': False,
|
||||
'enumerable': False,
|
||||
'configurable': True
|
||||
}
|
||||
|
||||
if not self.is_native: # set prototype for user defined functions
|
||||
# constructor points to this function
|
||||
proto = space.NewObject()
|
||||
proto.own['constructor'] = {
|
||||
'value': self,
|
||||
'writable': True,
|
||||
'enumerable': False,
|
||||
'configurable': True
|
||||
}
|
||||
self.own['prototype'] = {
|
||||
'value': proto,
|
||||
'writable': True,
|
||||
'enumerable': False,
|
||||
'configurable': False
|
||||
}
|
||||
# todo set up throwers on callee and arguments if in strict mode
|
||||
|
||||
def call(self, this, args=()):
|
||||
''' Dont use this method from inside bytecode to call other bytecode. '''
|
||||
if self.is_native:
|
||||
_args = SpaceTuple(
|
||||
args
|
||||
) # we have to do that unfortunately to pass all the necessary info to the funcs
|
||||
_args.space = self.space
|
||||
return self.code(
|
||||
this, _args
|
||||
) # must return valid js object - undefined, null, float, unicode, bool, or PyJs
|
||||
else:
|
||||
return self.space.exe._call(self, this,
|
||||
args) # will run inside bytecode
|
||||
|
||||
def has_instance(self, other):
|
||||
# I am not sure here so instanceof may not work lol.
|
||||
if not is_object(other):
|
||||
return False
|
||||
proto = self.get('prototype')
|
||||
if not is_object(proto):
|
||||
raise MakeError(
|
||||
'TypeError',
|
||||
'Function has non-object prototype in instanceof check')
|
||||
while True:
|
||||
other = other.prototype
|
||||
if not other: # todo make sure that the condition is not None or null
|
||||
return False
|
||||
if other is proto:
|
||||
return True
|
||||
|
||||
def create(self, args, space):
|
||||
proto = self.get('prototype')
|
||||
if not is_object(proto):
|
||||
proto = space.ObjectPrototype
|
||||
new = PyJsObject(prototype=proto)
|
||||
res = self.call(new, args)
|
||||
if is_object(res):
|
||||
return res
|
||||
return new
|
||||
|
||||
def _generate_my_context(self, this, args):
|
||||
my_ctx = Scope(
|
||||
dict(izip(self.params, args)), self.space, parent=self.ctx)
|
||||
my_ctx.registers(self.definitions)
|
||||
my_ctx.THIS_BINDING = this
|
||||
if not self.arguments_in_params:
|
||||
my_ctx.own['arguments'] = get_new_arguments_obj(args, self.space)
|
||||
if not self.is_declaration and self.name and self.name not in my_ctx.own:
|
||||
my_ctx.own[
|
||||
self.
|
||||
name] = self # this should be immutable binding but come on!
|
||||
return my_ctx
|
||||
|
||||
|
||||
class SpaceTuple:
|
||||
def __init__(self, tup):
|
||||
self.tup = tup
|
||||
|
||||
def __len__(self):
|
||||
return len(self.tup)
|
||||
|
||||
def __getitem__(self, item):
|
||||
return self.tup[item]
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self.tup)
|
||||
@@ -1,753 +0,0 @@
|
||||
from .code import Code
|
||||
from .simplex import MakeError
|
||||
from .opcodes import *
|
||||
from .operations import *
|
||||
from .trans_utils import *
|
||||
|
||||
SPECIAL_IDENTIFIERS = {'true', 'false', 'this'}
|
||||
|
||||
|
||||
class ByteCodeGenerator:
|
||||
def __init__(self, exe):
|
||||
self.exe = exe
|
||||
|
||||
self.declared_continue_labels = {}
|
||||
self.declared_break_labels = {}
|
||||
|
||||
self.implicit_breaks = []
|
||||
self.implicit_continues = []
|
||||
|
||||
self.declared_vars = []
|
||||
|
||||
self.function_declaration_tape = []
|
||||
|
||||
self.states = []
|
||||
|
||||
def record_state(self):
|
||||
self.states.append(
|
||||
(self.declared_continue_labels, self.declared_break_labels,
|
||||
self.implicit_breaks, self.implicit_continues, self.declared_vars,
|
||||
self.function_declaration_tape))
|
||||
self.declared_continue_labels, self.declared_break_labels, \
|
||||
self.implicit_breaks, self.implicit_continues, \
|
||||
self.declared_vars, self.function_declaration_tape = {}, {}, [], [], [], []
|
||||
|
||||
def restore_state(self):
|
||||
self.declared_continue_labels, self.declared_break_labels, \
|
||||
self.implicit_breaks, self.implicit_continues, \
|
||||
self.declared_vars, self.function_declaration_tape = self.states.pop()
|
||||
|
||||
def ArrayExpression(self, elements, **kwargs):
|
||||
for e in elements:
|
||||
if e is None:
|
||||
self.emit('LOAD_NONE')
|
||||
else:
|
||||
self.emit(e)
|
||||
self.emit('LOAD_ARRAY', len(elements))
|
||||
|
||||
def AssignmentExpression(self, operator, left, right, **kwargs):
|
||||
operator = operator[:-1]
|
||||
if left['type'] == 'MemberExpression':
|
||||
self.emit(left['object'])
|
||||
if left['computed']:
|
||||
self.emit(left['property'])
|
||||
self.emit(right)
|
||||
if operator:
|
||||
self.emit('STORE_MEMBER_OP', operator)
|
||||
else:
|
||||
self.emit('STORE_MEMBER')
|
||||
else:
|
||||
self.emit(right)
|
||||
if operator:
|
||||
self.emit('STORE_MEMBER_DOT_OP', left['property']['name'],
|
||||
operator)
|
||||
else:
|
||||
self.emit('STORE_MEMBER_DOT', left['property']['name'])
|
||||
elif left['type'] == 'Identifier':
|
||||
if left['name'] in SPECIAL_IDENTIFIERS:
|
||||
raise MakeError('SyntaxError',
|
||||
'Invalid left-hand side in assignment')
|
||||
self.emit(right)
|
||||
if operator:
|
||||
self.emit('STORE_OP', left['name'], operator)
|
||||
else:
|
||||
self.emit('STORE', left['name'])
|
||||
else:
|
||||
raise MakeError('SyntaxError',
|
||||
'Invalid left-hand side in assignment')
|
||||
|
||||
def BinaryExpression(self, operator, left, right, **kwargs):
|
||||
self.emit(left)
|
||||
self.emit(right)
|
||||
self.emit('BINARY_OP', operator)
|
||||
|
||||
def BlockStatement(self, body, **kwargs):
|
||||
self._emit_statement_list(body)
|
||||
|
||||
def BreakStatement(self, label, **kwargs):
|
||||
if label is None:
|
||||
self.emit('JUMP', self.implicit_breaks[-1])
|
||||
else:
|
||||
label = label.get('name')
|
||||
if label not in self.declared_break_labels:
|
||||
raise MakeError('SyntaxError',
|
||||
'Undefined label \'%s\'' % label)
|
||||
else:
|
||||
self.emit('JUMP', self.declared_break_labels[label])
|
||||
|
||||
def CallExpression(self, callee, arguments, **kwargs):
|
||||
if callee['type'] == 'MemberExpression':
|
||||
self.emit(callee['object'])
|
||||
if callee['computed']:
|
||||
self.emit(callee['property'])
|
||||
if arguments:
|
||||
for e in arguments:
|
||||
self.emit(e)
|
||||
self.emit('LOAD_N_TUPLE', len(arguments))
|
||||
self.emit('CALL_METHOD')
|
||||
else:
|
||||
self.emit('CALL_METHOD_NO_ARGS')
|
||||
else:
|
||||
prop_name = to_key(callee['property'])
|
||||
if arguments:
|
||||
for e in arguments:
|
||||
self.emit(e)
|
||||
self.emit('LOAD_N_TUPLE', len(arguments))
|
||||
self.emit('CALL_METHOD_DOT', prop_name)
|
||||
else:
|
||||
self.emit('CALL_METHOD_DOT_NO_ARGS', prop_name)
|
||||
else:
|
||||
self.emit(callee)
|
||||
if arguments:
|
||||
for e in arguments:
|
||||
self.emit(e)
|
||||
self.emit('LOAD_N_TUPLE', len(arguments))
|
||||
self.emit('CALL')
|
||||
else:
|
||||
self.emit('CALL_NO_ARGS')
|
||||
|
||||
def ClassBody(self, body, **kwargs):
|
||||
raise NotImplementedError('Not available in ECMA 5.1')
|
||||
|
||||
def ClassDeclaration(self, id, superClass, body, **kwargs):
|
||||
raise NotImplementedError('Not available in ECMA 5.1')
|
||||
|
||||
def ClassExpression(self, id, superClass, body, **kwargs):
|
||||
raise NotImplementedError('Classes not available in ECMA 5.1')
|
||||
|
||||
def ConditionalExpression(self, test, consequent, alternate, **kwargs):
|
||||
alt = self.exe.get_new_label()
|
||||
end = self.exe.get_new_label()
|
||||
# ?
|
||||
self.emit(test)
|
||||
self.emit('JUMP_IF_FALSE', alt)
|
||||
# first val
|
||||
self.emit(consequent)
|
||||
self.emit('JUMP', end)
|
||||
# second val
|
||||
self.emit('LABEL', alt)
|
||||
self.emit(alternate)
|
||||
# end of ?: statement
|
||||
self.emit('LABEL', end)
|
||||
|
||||
def ContinueStatement(self, label, **kwargs):
|
||||
if label is None:
|
||||
self.emit('JUMP', self.implicit_continues[-1])
|
||||
else:
|
||||
label = label.get('name')
|
||||
if label not in self.declared_continue_labels:
|
||||
raise MakeError('SyntaxError',
|
||||
'Undefined label \'%s\'' % label)
|
||||
else:
|
||||
self.emit('JUMP', self.declared_continue_labels[label])
|
||||
|
||||
def DebuggerStatement(self, **kwargs):
|
||||
self.EmptyStatement(**kwargs)
|
||||
|
||||
def DoWhileStatement(self, body, test, **kwargs):
|
||||
continue_label = self.exe.get_new_label()
|
||||
break_label = self.exe.get_new_label()
|
||||
initial_do = self.exe.get_new_label()
|
||||
|
||||
self.emit('JUMP', initial_do)
|
||||
self.emit('LABEL', continue_label)
|
||||
self.emit(test)
|
||||
self.emit('JUMP_IF_FALSE', break_label)
|
||||
self.emit('LABEL', initial_do)
|
||||
|
||||
# translate the body, remember to add and afterwards remove implicit break/continue labels
|
||||
|
||||
self.implicit_continues.append(continue_label)
|
||||
self.implicit_breaks.append(break_label)
|
||||
self.emit(body)
|
||||
self.implicit_continues.pop()
|
||||
self.implicit_breaks.pop()
|
||||
|
||||
self.emit('JUMP', continue_label) # loop back
|
||||
self.emit('LABEL', break_label)
|
||||
|
||||
def EmptyStatement(self, **kwargs):
|
||||
# do nothing
|
||||
pass
|
||||
|
||||
def ExpressionStatement(self, expression, **kwargs):
|
||||
# change the final stack value
|
||||
# pop the previous value and execute expression
|
||||
self.emit('POP')
|
||||
self.emit(expression)
|
||||
|
||||
def ForStatement(self, init, test, update, body, **kwargs):
|
||||
continue_label = self.exe.get_new_label()
|
||||
break_label = self.exe.get_new_label()
|
||||
first_start = self.exe.get_new_label()
|
||||
|
||||
if init is not None:
|
||||
self.emit(init)
|
||||
if init['type'] != 'VariableDeclaration':
|
||||
self.emit('POP')
|
||||
|
||||
# skip first update and go straight to test
|
||||
self.emit('JUMP', first_start)
|
||||
|
||||
self.emit('LABEL', continue_label)
|
||||
if update:
|
||||
self.emit(update)
|
||||
self.emit('POP')
|
||||
self.emit('LABEL', first_start)
|
||||
if test:
|
||||
self.emit(test)
|
||||
self.emit('JUMP_IF_FALSE', break_label)
|
||||
|
||||
# translate the body, remember to add and afterwards to remove implicit break/continue labels
|
||||
|
||||
self.implicit_continues.append(continue_label)
|
||||
self.implicit_breaks.append(break_label)
|
||||
self.emit(body)
|
||||
self.implicit_continues.pop()
|
||||
self.implicit_breaks.pop()
|
||||
|
||||
self.emit('JUMP', continue_label) # loop back
|
||||
self.emit('LABEL', break_label)
|
||||
|
||||
def ForInStatement(self, left, right, body, **kwargs):
|
||||
# prepare the needed labels
|
||||
body_start_label = self.exe.get_new_label()
|
||||
continue_label = self.exe.get_new_label()
|
||||
break_label = self.exe.get_new_label()
|
||||
|
||||
# prepare the name
|
||||
if left['type'] == 'VariableDeclaration':
|
||||
if len(left['declarations']) != 1:
|
||||
raise MakeError(
|
||||
'SyntaxError',
|
||||
' Invalid left-hand side in for-in loop: Must have a single binding.'
|
||||
)
|
||||
self.emit(left)
|
||||
name = left['declarations'][0]['id']['name']
|
||||
elif left['type'] == 'Identifier':
|
||||
name = left['name']
|
||||
else:
|
||||
raise MakeError('SyntaxError',
|
||||
'Invalid left-hand side in for-loop')
|
||||
|
||||
# prepare the iterable
|
||||
self.emit(right)
|
||||
|
||||
# emit ForIn Opcode
|
||||
self.emit('FOR_IN', name, body_start_label, continue_label,
|
||||
break_label)
|
||||
|
||||
# a special continue position
|
||||
self.emit('LABEL', continue_label)
|
||||
self.emit('NOP')
|
||||
|
||||
self.emit('LABEL', body_start_label)
|
||||
self.implicit_continues.append(continue_label)
|
||||
self.implicit_breaks.append(break_label)
|
||||
self.emit('LOAD_UNDEFINED')
|
||||
self.emit(body)
|
||||
self.implicit_continues.pop()
|
||||
self.implicit_breaks.pop()
|
||||
self.emit('NOP')
|
||||
self.emit('LABEL', break_label)
|
||||
self.emit('NOP')
|
||||
|
||||
def FunctionDeclaration(self, id, params, defaults, body, **kwargs):
|
||||
if defaults:
|
||||
raise NotImplementedError('Defaults not available in ECMA 5.1')
|
||||
|
||||
# compile function
|
||||
self.record_state(
|
||||
) # cleans translator state and appends it to the stack so that it can be later restored
|
||||
function_start = self.exe.get_new_label()
|
||||
function_declarations = self.exe.get_new_label()
|
||||
declarations_done = self.exe.get_new_label(
|
||||
) # put jump to this place at the and of function tape!
|
||||
function_end = self.exe.get_new_label()
|
||||
|
||||
# skip the function if encountered externally
|
||||
self.emit('JUMP', function_end)
|
||||
|
||||
self.emit('LABEL', function_start)
|
||||
# call is made with empty stack so load undefined to fill it
|
||||
self.emit('LOAD_UNDEFINED')
|
||||
# declare all functions
|
||||
self.emit('JUMP', function_declarations)
|
||||
self.emit('LABEL', declarations_done)
|
||||
self.function_declaration_tape.append(LABEL(function_declarations))
|
||||
|
||||
self.emit(body)
|
||||
self.ReturnStatement(None)
|
||||
|
||||
self.function_declaration_tape.append(JUMP(declarations_done))
|
||||
self.exe.tape.extend(self.function_declaration_tape)
|
||||
|
||||
self.emit('LABEL', function_end)
|
||||
declared_vars = self.declared_vars
|
||||
self.restore_state()
|
||||
|
||||
# create function object and append to stack
|
||||
name = id.get('name')
|
||||
assert name is not None
|
||||
self.declared_vars.append(name)
|
||||
self.function_declaration_tape.append(
|
||||
LOAD_FUNCTION(function_start, tuple(p['name'] for p in params),
|
||||
name, True, tuple(declared_vars)))
|
||||
self.function_declaration_tape.append(STORE(name))
|
||||
self.function_declaration_tape.append(POP())
|
||||
|
||||
def FunctionExpression(self, id, params, defaults, body, **kwargs):
|
||||
if defaults:
|
||||
raise NotImplementedError('Defaults not available in ECMA 5.1')
|
||||
|
||||
# compile function
|
||||
self.record_state(
|
||||
) # cleans translator state and appends it to the stack so that it can be later restored
|
||||
function_start = self.exe.get_new_label()
|
||||
function_declarations = self.exe.get_new_label()
|
||||
declarations_done = self.exe.get_new_label(
|
||||
) # put jump to this place at the and of function tape!
|
||||
function_end = self.exe.get_new_label()
|
||||
|
||||
# skip the function if encountered externally
|
||||
self.emit('JUMP', function_end)
|
||||
|
||||
self.emit('LABEL', function_start)
|
||||
# call is made with empty stack so load undefined to fill it
|
||||
self.emit('LOAD_UNDEFINED')
|
||||
# declare all functions
|
||||
self.emit('JUMP', function_declarations)
|
||||
self.emit('LABEL', declarations_done)
|
||||
self.function_declaration_tape.append(LABEL(function_declarations))
|
||||
|
||||
self.emit(body)
|
||||
self.ReturnStatement(None)
|
||||
|
||||
self.function_declaration_tape.append(JUMP(declarations_done))
|
||||
self.exe.tape.extend(self.function_declaration_tape)
|
||||
|
||||
self.emit('LABEL', function_end)
|
||||
declared_vars = self.declared_vars
|
||||
self.restore_state()
|
||||
|
||||
# create function object and append to stack
|
||||
name = id.get('name') if id else None
|
||||
self.emit('LOAD_FUNCTION', function_start,
|
||||
tuple(p['name'] for p in params), name, False,
|
||||
tuple(declared_vars))
|
||||
|
||||
def Identifier(self, name, **kwargs):
|
||||
if name == 'true':
|
||||
self.emit('LOAD_BOOLEAN', 1)
|
||||
elif name == 'false':
|
||||
self.emit('LOAD_BOOLEAN', 0)
|
||||
elif name == 'undefined':
|
||||
self.emit('LOAD_UNDEFINED')
|
||||
else:
|
||||
self.emit('LOAD', name)
|
||||
|
||||
def IfStatement(self, test, consequent, alternate, **kwargs):
|
||||
alt = self.exe.get_new_label()
|
||||
end = self.exe.get_new_label()
|
||||
# if
|
||||
self.emit(test)
|
||||
self.emit('JUMP_IF_FALSE', alt)
|
||||
# consequent
|
||||
self.emit(consequent)
|
||||
self.emit('JUMP', end)
|
||||
# alternate
|
||||
self.emit('LABEL', alt)
|
||||
if alternate is not None:
|
||||
self.emit(alternate)
|
||||
# end of if statement
|
||||
self.emit('LABEL', end)
|
||||
|
||||
def LabeledStatement(self, label, body, **kwargs):
|
||||
label = label['name']
|
||||
if body['type'] in ('WhileStatement', 'DoWhileStatement',
|
||||
'ForStatement', 'ForInStatement'):
|
||||
# Continue label available... Simply take labels defined by the loop.
|
||||
# It is important that they request continue label first
|
||||
self.declared_continue_labels[label] = self.exe._label_count + 1
|
||||
self.declared_break_labels[label] = self.exe._label_count + 2
|
||||
self.emit(body)
|
||||
del self.declared_break_labels[label]
|
||||
del self.declared_continue_labels[label]
|
||||
else:
|
||||
# only break label available
|
||||
lbl = self.exe.get_new_label()
|
||||
self.declared_break_labels[label] = lbl
|
||||
self.emit(body)
|
||||
self.emit('LABEL', lbl)
|
||||
del self.declared_break_labels[label]
|
||||
|
||||
def Literal(self, value, **kwargs):
|
||||
if value is None:
|
||||
self.emit('LOAD_NULL')
|
||||
elif isinstance(value, bool):
|
||||
self.emit('LOAD_BOOLEAN', int(value))
|
||||
elif isinstance(value, basestring):
|
||||
self.emit('LOAD_STRING', unicode(value))
|
||||
elif isinstance(value, (float, int, long)):
|
||||
self.emit('LOAD_NUMBER', float(value))
|
||||
elif isinstance(value, tuple):
|
||||
self.emit('LOAD_REGEXP', *value)
|
||||
else:
|
||||
raise RuntimeError('Unsupported literal')
|
||||
|
||||
def LogicalExpression(self, left, right, operator, **kwargs):
|
||||
end = self.exe.get_new_label()
|
||||
if operator == '&&':
|
||||
# AND
|
||||
self.emit(left)
|
||||
self.emit('JUMP_IF_FALSE_WITHOUT_POP', end)
|
||||
self.emit('POP')
|
||||
self.emit(right)
|
||||
self.emit('LABEL', end)
|
||||
elif operator == '||':
|
||||
# OR
|
||||
self.emit(left)
|
||||
self.emit('JUMP_IF_TRUE_WITHOUT_POP', end)
|
||||
self.emit('POP')
|
||||
self.emit(right)
|
||||
self.emit('LABEL', end)
|
||||
else:
|
||||
raise RuntimeError("Unknown logical expression: %s" % operator)
|
||||
|
||||
def MemberExpression(self, computed, object, property, **kwargs):
|
||||
if computed:
|
||||
self.emit(object)
|
||||
self.emit(property)
|
||||
self.emit('LOAD_MEMBER')
|
||||
else:
|
||||
self.emit(object)
|
||||
self.emit('LOAD_MEMBER_DOT', property['name'])
|
||||
|
||||
def NewExpression(self, callee, arguments, **kwargs):
|
||||
self.emit(callee)
|
||||
if arguments:
|
||||
n = len(arguments)
|
||||
for e in arguments:
|
||||
self.emit(e)
|
||||
self.emit('LOAD_N_TUPLE', n)
|
||||
self.emit('NEW')
|
||||
else:
|
||||
self.emit('NEW_NO_ARGS')
|
||||
|
||||
def ObjectExpression(self, properties, **kwargs):
|
||||
data = []
|
||||
for prop in properties:
|
||||
self.emit(prop['value'])
|
||||
if prop['computed']:
|
||||
raise NotImplementedError(
|
||||
'ECMA 5.1 does not support computed object properties!')
|
||||
data.append((to_key(prop['key']), prop['kind'][0]))
|
||||
self.emit('LOAD_OBJECT', tuple(data))
|
||||
|
||||
def Program(self, body, **kwargs):
|
||||
old_tape_len = len(self.exe.tape)
|
||||
self.emit('LOAD_UNDEFINED')
|
||||
self.emit(body)
|
||||
# add function tape !
|
||||
self.exe.tape = self.exe.tape[:old_tape_len] + self.function_declaration_tape + self.exe.tape[old_tape_len:]
|
||||
|
||||
def Pyimport(self, imp, **kwargs):
|
||||
raise NotImplementedError(
|
||||
'Not available for bytecode interpreter yet, use the Js2Py translator.'
|
||||
)
|
||||
|
||||
def Property(self, kind, key, computed, value, method, shorthand,
|
||||
**kwargs):
|
||||
raise NotImplementedError('Not available in ECMA 5.1')
|
||||
|
||||
def RestElement(self, argument, **kwargs):
|
||||
raise NotImplementedError('Not available in ECMA 5.1')
|
||||
|
||||
def ReturnStatement(self, argument, **kwargs):
|
||||
self.emit('POP') # pop result of expression statements
|
||||
if argument is None:
|
||||
self.emit('LOAD_UNDEFINED')
|
||||
else:
|
||||
self.emit(argument)
|
||||
self.emit('RETURN')
|
||||
|
||||
def SequenceExpression(self, expressions, **kwargs):
|
||||
for e in expressions:
|
||||
self.emit(e)
|
||||
self.emit('POP')
|
||||
del self.exe.tape[-1]
|
||||
|
||||
def SwitchCase(self, test, consequent, **kwargs):
|
||||
raise NotImplementedError('Already implemented in SwitchStatement')
|
||||
|
||||
def SwitchStatement(self, discriminant, cases, **kwargs):
|
||||
self.emit(discriminant)
|
||||
labels = [self.exe.get_new_label() for case in cases]
|
||||
tests = [case['test'] for case in cases]
|
||||
consequents = [case['consequent'] for case in cases]
|
||||
end_of_switch = self.exe.get_new_label()
|
||||
|
||||
# translate test cases
|
||||
for test, label in zip(tests, labels):
|
||||
if test is not None:
|
||||
self.emit(test)
|
||||
self.emit('JUMP_IF_EQ', label)
|
||||
else:
|
||||
self.emit('POP')
|
||||
self.emit('JUMP', label)
|
||||
# this will be executed if none of the cases worked
|
||||
self.emit('POP')
|
||||
self.emit('JUMP', end_of_switch)
|
||||
|
||||
# translate consequents
|
||||
self.implicit_breaks.append(end_of_switch)
|
||||
for consequent, label in zip(consequents, labels):
|
||||
self.emit('LABEL', label)
|
||||
self._emit_statement_list(consequent)
|
||||
self.implicit_breaks.pop()
|
||||
|
||||
self.emit('LABEL', end_of_switch)
|
||||
|
||||
def ThisExpression(self, **kwargs):
|
||||
self.emit('LOAD_THIS')
|
||||
|
||||
def ThrowStatement(self, argument, **kwargs):
|
||||
# throw with the empty stack
|
||||
self.emit('POP')
|
||||
self.emit(argument)
|
||||
self.emit('THROW')
|
||||
|
||||
def TryStatement(self, block, handler, finalizer, **kwargs):
|
||||
try_label = self.exe.get_new_label()
|
||||
catch_label = self.exe.get_new_label()
|
||||
finally_label = self.exe.get_new_label()
|
||||
end_label = self.exe.get_new_label()
|
||||
|
||||
self.emit('JUMP', end_label)
|
||||
|
||||
# try block
|
||||
self.emit('LABEL', try_label)
|
||||
self.emit('LOAD_UNDEFINED')
|
||||
self.emit(block)
|
||||
self.emit(
|
||||
'NOP'
|
||||
) # needed to distinguish from break/continue vs some internal jumps
|
||||
|
||||
# catch block
|
||||
self.emit('LABEL', catch_label)
|
||||
self.emit('LOAD_UNDEFINED')
|
||||
if handler:
|
||||
self.emit(handler['body'])
|
||||
self.emit('NOP')
|
||||
|
||||
# finally block
|
||||
self.emit('LABEL', finally_label)
|
||||
self.emit('LOAD_UNDEFINED')
|
||||
if finalizer:
|
||||
self.emit(finalizer)
|
||||
self.emit('NOP')
|
||||
|
||||
self.emit('LABEL', end_label)
|
||||
|
||||
# give life to the code
|
||||
self.emit('TRY_CATCH_FINALLY', try_label, catch_label,
|
||||
handler['param']['name'] if handler else None, finally_label,
|
||||
bool(finalizer), end_label)
|
||||
|
||||
def UnaryExpression(self, operator, argument, **kwargs):
|
||||
if operator == 'typeof' and argument[
|
||||
'type'] == 'Identifier': # todo fix typeof
|
||||
self.emit('TYPEOF', argument['name'])
|
||||
elif operator == 'delete':
|
||||
if argument['type'] == 'MemberExpression':
|
||||
self.emit(argument['object'])
|
||||
if argument['property']['type'] == 'Identifier':
|
||||
self.emit('LOAD_STRING',
|
||||
unicode(argument['property']['name']))
|
||||
else:
|
||||
self.emit(argument['property'])
|
||||
self.emit('DELETE_MEMBER')
|
||||
elif argument['type'] == 'Identifier':
|
||||
self.emit('DELETE', argument['name'])
|
||||
else:
|
||||
self.emit('LOAD_BOOLEAN', 1)
|
||||
elif operator in UNARY_OPERATIONS:
|
||||
self.emit(argument)
|
||||
self.emit('UNARY_OP', operator)
|
||||
else:
|
||||
raise MakeError('SyntaxError',
|
||||
'Unknown unary operator %s' % operator)
|
||||
|
||||
def UpdateExpression(self, operator, argument, prefix, **kwargs):
|
||||
incr = int(operator == "++")
|
||||
post = int(not prefix)
|
||||
if argument['type'] == 'MemberExpression':
|
||||
if argument['computed']:
|
||||
self.emit(argument['object'])
|
||||
self.emit(argument['property'])
|
||||
self.emit('POSTFIX_MEMBER', post, incr)
|
||||
else:
|
||||
self.emit(argument['object'])
|
||||
name = to_key(argument['property'])
|
||||
self.emit('POSTFIX_MEMBER_DOT', post, incr, name)
|
||||
elif argument['type'] == 'Identifier':
|
||||
name = to_key(argument)
|
||||
self.emit('POSTFIX', post, incr, name)
|
||||
else:
|
||||
raise MakeError('SyntaxError',
|
||||
'Invalid left-hand side in assignment')
|
||||
|
||||
def VariableDeclaration(self, declarations, kind, **kwargs):
|
||||
if kind != 'var':
|
||||
raise NotImplementedError(
|
||||
'Only var variable declaration is supported by ECMA 5.1')
|
||||
for d in declarations:
|
||||
self.emit(d)
|
||||
|
||||
def LexicalDeclaration(self, declarations, kind, **kwargs):
|
||||
raise NotImplementedError('Not supported by ECMA 5.1')
|
||||
|
||||
def VariableDeclarator(self, id, init, **kwargs):
|
||||
name = id['name']
|
||||
if name in SPECIAL_IDENTIFIERS:
|
||||
raise MakeError('Invalid left-hand side in assignment')
|
||||
self.declared_vars.append(name)
|
||||
if init is not None:
|
||||
self.emit(init)
|
||||
self.emit('STORE', name)
|
||||
self.emit('POP')
|
||||
|
||||
def WhileStatement(self, test, body, **kwargs):
|
||||
continue_label = self.exe.get_new_label()
|
||||
break_label = self.exe.get_new_label()
|
||||
|
||||
self.emit('LABEL', continue_label)
|
||||
self.emit(test)
|
||||
self.emit('JUMP_IF_FALSE', break_label)
|
||||
|
||||
# translate the body, remember to add and afterwards remove implicit break/continue labels
|
||||
|
||||
self.implicit_continues.append(continue_label)
|
||||
self.implicit_breaks.append(break_label)
|
||||
self.emit(body)
|
||||
self.implicit_continues.pop()
|
||||
self.implicit_breaks.pop()
|
||||
|
||||
self.emit('JUMP', continue_label) # loop back
|
||||
self.emit('LABEL', break_label)
|
||||
|
||||
def WithStatement(self, object, body, **kwargs):
|
||||
beg_label = self.exe.get_new_label()
|
||||
end_label = self.exe.get_new_label()
|
||||
# scope
|
||||
self.emit(object)
|
||||
|
||||
# now the body
|
||||
self.emit('JUMP', end_label)
|
||||
self.emit('LABEL', beg_label)
|
||||
self.emit('LOAD_UNDEFINED')
|
||||
self.emit(body)
|
||||
self.emit('NOP')
|
||||
self.emit('LABEL', end_label)
|
||||
|
||||
# with statement implementation
|
||||
self.emit('WITH', beg_label, end_label)
|
||||
|
||||
def _emit_statement_list(self, statements):
|
||||
for statement in statements:
|
||||
self.emit(statement)
|
||||
|
||||
def emit(self, what, *args):
|
||||
''' what can be either name of the op, or node, or a list of statements.'''
|
||||
if isinstance(what, basestring):
|
||||
return self.exe.emit(what, *args)
|
||||
elif isinstance(what, list):
|
||||
self._emit_statement_list(what)
|
||||
else:
|
||||
return getattr(self, what['type'])(**what)
|
||||
|
||||
|
||||
import os, codecs
|
||||
|
||||
|
||||
def path_as_local(path):
|
||||
if os.path.isabs(path):
|
||||
return path
|
||||
# relative to cwd
|
||||
return os.path.join(os.getcwd(), path)
|
||||
|
||||
|
||||
def get_file_contents(path_or_file):
|
||||
if hasattr(path_or_file, 'read'):
|
||||
js = path_or_file.read()
|
||||
else:
|
||||
with codecs.open(path_as_local(path_or_file), "r", "utf-8") as f:
|
||||
js = f.read()
|
||||
return js
|
||||
|
||||
|
||||
def main():
|
||||
from space import Space
|
||||
import fill_space
|
||||
|
||||
from pyjsparser import parse
|
||||
import json
|
||||
a = ByteCodeGenerator(Code())
|
||||
|
||||
s = Space()
|
||||
fill_space.fill_space(s, a)
|
||||
|
||||
a.exe.space = s
|
||||
s.exe = a.exe
|
||||
con = get_file_contents('internals/esprima.js')
|
||||
d = parse(con + (
|
||||
''';JSON.stringify(exports.parse(%s), 4, 4)''' % json.dumps(con)))
|
||||
# d = parse('''
|
||||
# function x(n) {
|
||||
# log(n)
|
||||
# return x(n+1)
|
||||
# }
|
||||
# x(0)
|
||||
# ''')
|
||||
|
||||
# var v = 333333;
|
||||
# while (v) {
|
||||
# v--
|
||||
#
|
||||
# }
|
||||
a.emit(d)
|
||||
print(a.declared_vars)
|
||||
print(a.exe.tape)
|
||||
print(len(a.exe.tape))
|
||||
|
||||
a.exe.compile()
|
||||
|
||||
def log(this, args):
|
||||
print(args[0])
|
||||
return 999
|
||||
|
||||
print(a.exe.run(a.exe.space.GlobalObj))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1,227 +0,0 @@
|
||||
from .opcodes import *
|
||||
from .space import *
|
||||
from .base import *
|
||||
|
||||
|
||||
class Code:
|
||||
'''Can generate, store and run sequence of ops representing js code'''
|
||||
|
||||
def __init__(self, is_strict=False, debug_mode=False):
|
||||
self.tape = []
|
||||
self.compiled = False
|
||||
self.label_locs = None
|
||||
self.is_strict = is_strict
|
||||
self.debug_mode = debug_mode
|
||||
|
||||
self.contexts = []
|
||||
self.current_ctx = None
|
||||
self.return_locs = []
|
||||
self._label_count = 0
|
||||
self.label_locs = None
|
||||
|
||||
# useful references
|
||||
self.GLOBAL_THIS = None
|
||||
self.space = None
|
||||
|
||||
# dbg
|
||||
self.ctx_depth = 0
|
||||
|
||||
|
||||
def get_new_label(self):
|
||||
self._label_count += 1
|
||||
return self._label_count
|
||||
|
||||
def emit(self, op_code, *args):
|
||||
''' Adds op_code with specified args to tape '''
|
||||
self.tape.append(OP_CODES[op_code](*args))
|
||||
|
||||
def compile(self, start_loc=0):
|
||||
''' Records locations of labels and compiles the code '''
|
||||
self.label_locs = {} if self.label_locs is None else self.label_locs
|
||||
loc = start_loc
|
||||
while loc < len(self.tape):
|
||||
if type(self.tape[loc]) == LABEL:
|
||||
self.label_locs[self.tape[loc].num] = loc
|
||||
del self.tape[loc]
|
||||
continue
|
||||
loc += 1
|
||||
self.compiled = True
|
||||
|
||||
def _call(self, func, this, args):
|
||||
''' Calls a bytecode function func
|
||||
NOTE: use !ONLY! when calling functions from native methods! '''
|
||||
assert not func.is_native
|
||||
# fake call - the the runner to return to the end of the file
|
||||
old_contexts = self.contexts
|
||||
old_return_locs = self.return_locs
|
||||
old_curr_ctx = self.current_ctx
|
||||
|
||||
self.contexts = [FakeCtx()]
|
||||
self.return_locs = [len(self.tape)] # target line after return
|
||||
|
||||
# prepare my ctx
|
||||
my_ctx = func._generate_my_context(this, args)
|
||||
self.current_ctx = my_ctx
|
||||
|
||||
# execute dunction
|
||||
ret = self.run(my_ctx, starting_loc=self.label_locs[func.code])
|
||||
|
||||
# bring back old execution
|
||||
self.current_ctx = old_curr_ctx
|
||||
self.contexts = old_contexts
|
||||
self.return_locs = old_return_locs
|
||||
|
||||
return ret
|
||||
|
||||
def execute_fragment_under_context(self, ctx, start_label, end_label):
|
||||
''' just like run but returns if moved outside of the specified fragment
|
||||
# 4 different exectution results
|
||||
# 0=normal, 1=return, 2=jump_outside, 3=errors
|
||||
# execute_fragment_under_context returns:
|
||||
# (return_value, typ, return_value/jump_loc/py_error)
|
||||
# IMPARTANT: It is guaranteed that the length of the ctx.stack is unchanged.
|
||||
'''
|
||||
old_curr_ctx = self.current_ctx
|
||||
self.ctx_depth += 1
|
||||
old_stack_len = len(ctx.stack)
|
||||
old_ret_len = len(self.return_locs)
|
||||
old_ctx_len = len(self.contexts)
|
||||
try:
|
||||
self.current_ctx = ctx
|
||||
return self._execute_fragment_under_context(
|
||||
ctx, start_label, end_label)
|
||||
except JsException as err:
|
||||
if self.debug_mode:
|
||||
self._on_fragment_exit("js errors")
|
||||
# undo the things that were put on the stack (if any) to ensure a proper error recovery
|
||||
del ctx.stack[old_stack_len:]
|
||||
del self.return_locs[old_ret_len:]
|
||||
del self.contexts[old_ctx_len :]
|
||||
return undefined, 3, err
|
||||
finally:
|
||||
self.ctx_depth -= 1
|
||||
self.current_ctx = old_curr_ctx
|
||||
assert old_stack_len == len(ctx.stack)
|
||||
|
||||
def _get_dbg_indent(self):
|
||||
return self.ctx_depth * ' '
|
||||
|
||||
def _on_fragment_exit(self, mode):
|
||||
print(self._get_dbg_indent() + 'ctx exit (%s)' % mode)
|
||||
|
||||
def _execute_fragment_under_context(self, ctx, start_label, end_label):
|
||||
start, end = self.label_locs[start_label], self.label_locs[end_label]
|
||||
initial_len = len(ctx.stack)
|
||||
loc = start
|
||||
entry_level = len(self.contexts)
|
||||
# for e in self.tape[start:end]:
|
||||
# print e
|
||||
if self.debug_mode:
|
||||
print(self._get_dbg_indent() + 'ctx entry (from:%d, to:%d)' % (start, end))
|
||||
while loc < len(self.tape):
|
||||
if len(self.contexts) == entry_level and loc >= end:
|
||||
if self.debug_mode:
|
||||
self._on_fragment_exit('normal')
|
||||
assert loc == end
|
||||
delta_stack = len(ctx.stack) - initial_len
|
||||
assert delta_stack == +1, 'Stack change must be equal to +1! got %d' % delta_stack
|
||||
return ctx.stack.pop(), 0, None # means normal return
|
||||
|
||||
# execute instruction
|
||||
if self.debug_mode:
|
||||
print(self._get_dbg_indent() + str(loc), self.tape[loc])
|
||||
status = self.tape[loc].eval(ctx)
|
||||
|
||||
# check status for special actions
|
||||
if status is not None:
|
||||
if type(status) == int: # jump to label
|
||||
loc = self.label_locs[status]
|
||||
if len(self.contexts) == entry_level:
|
||||
# check if jumped outside of the fragment and break if so
|
||||
if not start <= loc < end:
|
||||
if self.debug_mode:
|
||||
self._on_fragment_exit('jump outside loc:%d label:%d' % (loc, status))
|
||||
delta_stack = len(ctx.stack) - initial_len
|
||||
assert delta_stack == +1, 'Stack change must be equal to +1! got %d' % delta_stack
|
||||
return ctx.stack.pop(), 2, status # jump outside
|
||||
continue
|
||||
|
||||
elif len(status) == 2: # a call or a return!
|
||||
# call: (new_ctx, func_loc_label_num)
|
||||
if status[0] is not None:
|
||||
# append old state to the stack
|
||||
self.contexts.append(ctx)
|
||||
self.return_locs.append(loc + 1)
|
||||
# set new state
|
||||
loc = self.label_locs[status[1]]
|
||||
ctx = status[0]
|
||||
self.current_ctx = ctx
|
||||
continue
|
||||
|
||||
# return: (None, None)
|
||||
else:
|
||||
if len(self.contexts) == entry_level:
|
||||
if self.debug_mode:
|
||||
self._on_fragment_exit('return')
|
||||
delta_stack = len(ctx.stack) - initial_len
|
||||
assert delta_stack == +1, 'Stack change must be equal to +1! got %d' % delta_stack
|
||||
return undefined, 1, ctx.stack.pop(
|
||||
) # return signal
|
||||
return_value = ctx.stack.pop()
|
||||
ctx = self.contexts.pop()
|
||||
self.current_ctx = ctx
|
||||
ctx.stack.append(return_value)
|
||||
|
||||
loc = self.return_locs.pop()
|
||||
continue
|
||||
# next instruction
|
||||
loc += 1
|
||||
if self.debug_mode:
|
||||
self._on_fragment_exit('internal error - unexpected end of tape, will crash')
|
||||
assert False, 'Remember to add NOP at the end!'
|
||||
|
||||
def run(self, ctx, starting_loc=0):
|
||||
loc = starting_loc
|
||||
self.current_ctx = ctx
|
||||
while loc < len(self.tape):
|
||||
# execute instruction
|
||||
if self.debug_mode:
|
||||
print(loc, self.tape[loc])
|
||||
status = self.tape[loc].eval(ctx)
|
||||
|
||||
# check status for special actions
|
||||
if status is not None:
|
||||
if type(status) == int: # jump to label
|
||||
loc = self.label_locs[status]
|
||||
continue
|
||||
|
||||
elif len(status) == 2: # a call or a return!
|
||||
# call: (new_ctx, func_loc_label_num)
|
||||
if status[0] is not None:
|
||||
# append old state to the stack
|
||||
self.contexts.append(ctx)
|
||||
self.return_locs.append(loc + 1)
|
||||
# set new state
|
||||
loc = self.label_locs[status[1]]
|
||||
ctx = status[0]
|
||||
self.current_ctx = ctx
|
||||
continue
|
||||
|
||||
# return: (None, None)
|
||||
else:
|
||||
return_value = ctx.stack.pop()
|
||||
ctx = self.contexts.pop()
|
||||
self.current_ctx = ctx
|
||||
ctx.stack.append(return_value)
|
||||
|
||||
loc = self.return_locs.pop()
|
||||
continue
|
||||
# next instruction
|
||||
loc += 1
|
||||
assert len(ctx.stack) == 1, ctx.stack
|
||||
return ctx.stack.pop()
|
||||
|
||||
|
||||
class FakeCtx(object):
|
||||
def __init__(self):
|
||||
self.stack = []
|
||||
@@ -1 +0,0 @@
|
||||
__author__ = 'Piotr Dabkowski'
|
||||
@@ -1,28 +0,0 @@
|
||||
from ..conversions import *
|
||||
from ..func_utils import *
|
||||
|
||||
|
||||
def Array(this, args):
|
||||
return ArrayConstructor(args, args.space)
|
||||
|
||||
|
||||
def ArrayConstructor(args, space):
|
||||
if len(args) == 1:
|
||||
l = get_arg(args, 0)
|
||||
if type(l) == float:
|
||||
if to_uint32(l) == l:
|
||||
return space.NewArray(l)
|
||||
else:
|
||||
raise MakeError(
|
||||
'RangeError',
|
||||
'Invalid length specified for Array constructor (must be uint32)'
|
||||
)
|
||||
else:
|
||||
return space.ConstructArray([l])
|
||||
else:
|
||||
return space.ConstructArray(list(args))
|
||||
|
||||
|
||||
def isArray(this, args):
|
||||
x = get_arg(args, 0)
|
||||
return is_object(x) and x.Class == u'Array'
|
||||
@@ -1,14 +0,0 @@
|
||||
from ..conversions import *
|
||||
from ..func_utils import *
|
||||
|
||||
|
||||
def Boolean(this, args):
|
||||
return to_boolean(get_arg(args, 0))
|
||||
|
||||
|
||||
def BooleanConstructor(args, space):
|
||||
temp = space.NewObject()
|
||||
temp.prototype = space.BooleanPrototype
|
||||
temp.Class = 'Boolean'
|
||||
temp.value = to_boolean(get_arg(args, 0))
|
||||
return temp
|
||||
@@ -1,11 +0,0 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from js2py.internals.conversions import *
|
||||
from js2py.internals.func_utils import *
|
||||
|
||||
|
||||
class ConsoleMethods:
|
||||
def log(this, args):
|
||||
x = ' '.join(to_string(e) for e in args)
|
||||
print(x)
|
||||
return undefined
|
||||
@@ -1,405 +0,0 @@
|
||||
from ..base import *
|
||||
from .time_helpers import *
|
||||
|
||||
TZ_OFFSET = (time.altzone // 3600)
|
||||
ABS_OFFSET = abs(TZ_OFFSET)
|
||||
TZ_NAME = time.tzname[1]
|
||||
ISO_FORMAT = '%s-%s-%sT%s:%s:%s.%sZ'
|
||||
|
||||
|
||||
@Js
|
||||
def Date(year, month, date, hours, minutes, seconds, ms):
|
||||
return now().to_string()
|
||||
|
||||
|
||||
Date.Class = 'Date'
|
||||
|
||||
|
||||
def now():
|
||||
return PyJsDate(int(time.time() * 1000), prototype=DatePrototype)
|
||||
|
||||
|
||||
@Js
|
||||
def UTC(year, month, date, hours, minutes, seconds, ms): # todo complete this
|
||||
args = arguments
|
||||
y = args[0].to_number()
|
||||
m = args[1].to_number()
|
||||
l = len(args)
|
||||
dt = args[2].to_number() if l > 2 else Js(1)
|
||||
h = args[3].to_number() if l > 3 else Js(0)
|
||||
mi = args[4].to_number() if l > 4 else Js(0)
|
||||
sec = args[5].to_number() if l > 5 else Js(0)
|
||||
mili = args[6].to_number() if l > 6 else Js(0)
|
||||
if not y.is_nan() and 0 <= y.value <= 99:
|
||||
y = y + Js(1900)
|
||||
t = TimeClip(MakeDate(MakeDay(y, m, dt), MakeTime(h, mi, sec, mili)))
|
||||
return PyJsDate(t, prototype=DatePrototype)
|
||||
|
||||
|
||||
@Js
|
||||
def parse(string):
|
||||
return PyJsDate(
|
||||
TimeClip(parse_date(string.to_string().value)),
|
||||
prototype=DatePrototype)
|
||||
|
||||
|
||||
Date.define_own_property('now', {
|
||||
'value': Js(now),
|
||||
'enumerable': False,
|
||||
'writable': True,
|
||||
'configurable': True
|
||||
})
|
||||
|
||||
Date.define_own_property('parse', {
|
||||
'value': parse,
|
||||
'enumerable': False,
|
||||
'writable': True,
|
||||
'configurable': True
|
||||
})
|
||||
|
||||
Date.define_own_property('UTC', {
|
||||
'value': UTC,
|
||||
'enumerable': False,
|
||||
'writable': True,
|
||||
'configurable': True
|
||||
})
|
||||
|
||||
|
||||
class PyJsDate(PyJs):
|
||||
Class = 'Date'
|
||||
extensible = True
|
||||
|
||||
def __init__(self, value, prototype=None):
|
||||
self.value = value
|
||||
self.own = {}
|
||||
self.prototype = prototype
|
||||
|
||||
# todo fix this problematic datetime part
|
||||
def to_local_dt(self):
|
||||
return datetime.datetime.utcfromtimestamp(
|
||||
UTCToLocal(self.value) // 1000)
|
||||
|
||||
def to_utc_dt(self):
|
||||
return datetime.datetime.utcfromtimestamp(self.value // 1000)
|
||||
|
||||
def local_strftime(self, pattern):
|
||||
if self.value is NaN:
|
||||
return 'Invalid Date'
|
||||
try:
|
||||
dt = self.to_local_dt()
|
||||
except:
|
||||
raise MakeError(
|
||||
'TypeError',
|
||||
'unsupported date range. Will fix in future versions')
|
||||
try:
|
||||
return dt.strftime(pattern)
|
||||
except:
|
||||
raise MakeError(
|
||||
'TypeError',
|
||||
'Could not generate date string from this date (limitations of python.datetime)'
|
||||
)
|
||||
|
||||
def utc_strftime(self, pattern):
|
||||
if self.value is NaN:
|
||||
return 'Invalid Date'
|
||||
try:
|
||||
dt = self.to_utc_dt()
|
||||
except:
|
||||
raise MakeError(
|
||||
'TypeError',
|
||||
'unsupported date range. Will fix in future versions')
|
||||
try:
|
||||
return dt.strftime(pattern)
|
||||
except:
|
||||
raise MakeError(
|
||||
'TypeError',
|
||||
'Could not generate date string from this date (limitations of python.datetime)'
|
||||
)
|
||||
|
||||
|
||||
def parse_date(py_string): # todo support all date string formats
|
||||
try:
|
||||
try:
|
||||
dt = datetime.datetime.strptime(py_string, "%Y-%m-%dT%H:%M:%S.%fZ")
|
||||
except:
|
||||
dt = datetime.datetime.strptime(py_string, "%Y-%m-%dT%H:%M:%SZ")
|
||||
return MakeDate(
|
||||
MakeDay(Js(dt.year), Js(dt.month - 1), Js(dt.day)),
|
||||
MakeTime(
|
||||
Js(dt.hour), Js(dt.minute), Js(dt.second),
|
||||
Js(dt.microsecond // 1000)))
|
||||
except:
|
||||
raise MakeError(
|
||||
'TypeError',
|
||||
'Could not parse date %s - unsupported date format. Currently only supported format is RFC3339 utc. Sorry!'
|
||||
% py_string)
|
||||
|
||||
|
||||
def date_constructor(*args):
|
||||
if len(args) >= 2:
|
||||
return date_constructor2(*args)
|
||||
elif len(args) == 1:
|
||||
return date_constructor1(args[0])
|
||||
else:
|
||||
return date_constructor0()
|
||||
|
||||
|
||||
def date_constructor0():
|
||||
return now()
|
||||
|
||||
|
||||
def date_constructor1(value):
|
||||
v = value.to_primitive()
|
||||
if v._type() == 'String':
|
||||
v = parse_date(v.value)
|
||||
else:
|
||||
v = v.to_int()
|
||||
return PyJsDate(TimeClip(v), prototype=DatePrototype)
|
||||
|
||||
|
||||
def date_constructor2(*args):
|
||||
y = args[0].to_number()
|
||||
m = args[1].to_number()
|
||||
l = len(args)
|
||||
dt = args[2].to_number() if l > 2 else Js(1)
|
||||
h = args[3].to_number() if l > 3 else Js(0)
|
||||
mi = args[4].to_number() if l > 4 else Js(0)
|
||||
sec = args[5].to_number() if l > 5 else Js(0)
|
||||
mili = args[6].to_number() if l > 6 else Js(0)
|
||||
if not y.is_nan() and 0 <= y.value <= 99:
|
||||
y = y + Js(1900)
|
||||
t = TimeClip(
|
||||
LocalToUTC(MakeDate(MakeDay(y, m, dt), MakeTime(h, mi, sec, mili))))
|
||||
return PyJsDate(t, prototype=DatePrototype)
|
||||
|
||||
|
||||
Date.create = date_constructor
|
||||
|
||||
DatePrototype = PyJsDate(float('nan'), prototype=ObjectPrototype)
|
||||
|
||||
|
||||
def check_date(obj):
|
||||
if obj.Class != 'Date':
|
||||
raise MakeError('TypeError', 'this is not a Date object')
|
||||
|
||||
|
||||
class DateProto:
|
||||
def toString():
|
||||
check_date(this)
|
||||
if this.value is NaN:
|
||||
return 'Invalid Date'
|
||||
offset = (UTCToLocal(this.value) - this.value) // msPerHour
|
||||
return this.local_strftime(
|
||||
'%a %b %d %Y %H:%M:%S GMT') + '%s00 (%s)' % (pad(
|
||||
offset, 2, True), GetTimeZoneName(this.value))
|
||||
|
||||
def toDateString():
|
||||
check_date(this)
|
||||
return this.local_strftime('%d %B %Y')
|
||||
|
||||
def toTimeString():
|
||||
check_date(this)
|
||||
return this.local_strftime('%H:%M:%S')
|
||||
|
||||
def toLocaleString():
|
||||
check_date(this)
|
||||
return this.local_strftime('%d %B %Y %H:%M:%S')
|
||||
|
||||
def toLocaleDateString():
|
||||
check_date(this)
|
||||
return this.local_strftime('%d %B %Y')
|
||||
|
||||
def toLocaleTimeString():
|
||||
check_date(this)
|
||||
return this.local_strftime('%H:%M:%S')
|
||||
|
||||
def valueOf():
|
||||
check_date(this)
|
||||
return this.value
|
||||
|
||||
def getTime():
|
||||
check_date(this)
|
||||
return this.value
|
||||
|
||||
def getFullYear():
|
||||
check_date(this)
|
||||
if this.value is NaN:
|
||||
return NaN
|
||||
return YearFromTime(UTCToLocal(this.value))
|
||||
|
||||
def getUTCFullYear():
|
||||
check_date(this)
|
||||
if this.value is NaN:
|
||||
return NaN
|
||||
return YearFromTime(this.value)
|
||||
|
||||
def getMonth():
|
||||
check_date(this)
|
||||
if this.value is NaN:
|
||||
return NaN
|
||||
return MonthFromTime(UTCToLocal(this.value))
|
||||
|
||||
def getDate():
|
||||
check_date(this)
|
||||
if this.value is NaN:
|
||||
return NaN
|
||||
return DateFromTime(UTCToLocal(this.value))
|
||||
|
||||
def getUTCMonth():
|
||||
check_date(this)
|
||||
if this.value is NaN:
|
||||
return NaN
|
||||
return MonthFromTime(this.value)
|
||||
|
||||
def getUTCDate():
|
||||
check_date(this)
|
||||
if this.value is NaN:
|
||||
return NaN
|
||||
return DateFromTime(this.value)
|
||||
|
||||
def getDay():
|
||||
check_date(this)
|
||||
if this.value is NaN:
|
||||
return NaN
|
||||
return WeekDay(UTCToLocal(this.value))
|
||||
|
||||
def getUTCDay():
|
||||
check_date(this)
|
||||
if this.value is NaN:
|
||||
return NaN
|
||||
return WeekDay(this.value)
|
||||
|
||||
def getHours():
|
||||
check_date(this)
|
||||
if this.value is NaN:
|
||||
return NaN
|
||||
return HourFromTime(UTCToLocal(this.value))
|
||||
|
||||
def getUTCHours():
|
||||
check_date(this)
|
||||
if this.value is NaN:
|
||||
return NaN
|
||||
return HourFromTime(this.value)
|
||||
|
||||
def getMinutes():
|
||||
check_date(this)
|
||||
if this.value is NaN:
|
||||
return NaN
|
||||
return MinFromTime(UTCToLocal(this.value))
|
||||
|
||||
def getUTCMinutes():
|
||||
check_date(this)
|
||||
if this.value is NaN:
|
||||
return NaN
|
||||
return MinFromTime(this.value)
|
||||
|
||||
def getSeconds():
|
||||
check_date(this)
|
||||
if this.value is NaN:
|
||||
return NaN
|
||||
return SecFromTime(UTCToLocal(this.value))
|
||||
|
||||
def getUTCSeconds():
|
||||
check_date(this)
|
||||
if this.value is NaN:
|
||||
return NaN
|
||||
return SecFromTime(this.value)
|
||||
|
||||
def getMilliseconds():
|
||||
check_date(this)
|
||||
if this.value is NaN:
|
||||
return NaN
|
||||
return msFromTime(UTCToLocal(this.value))
|
||||
|
||||
def getUTCMilliseconds():
|
||||
check_date(this)
|
||||
if this.value is NaN:
|
||||
return NaN
|
||||
return msFromTime(this.value)
|
||||
|
||||
def getTimezoneOffset():
|
||||
check_date(this)
|
||||
if this.value is NaN:
|
||||
return NaN
|
||||
return (this.value - UTCToLocal(this.value)) // 60000
|
||||
|
||||
def setTime(time):
|
||||
check_date(this)
|
||||
this.value = TimeClip(time.to_number().to_int())
|
||||
return this.value
|
||||
|
||||
def setMilliseconds(ms):
|
||||
check_date(this)
|
||||
t = UTCToLocal(this.value)
|
||||
tim = MakeTime(
|
||||
HourFromTime(t), MinFromTime(t), SecFromTime(t), ms.to_int())
|
||||
u = TimeClip(LocalToUTC(MakeDate(Day(t), tim)))
|
||||
this.value = u
|
||||
return u
|
||||
|
||||
def setUTCMilliseconds(ms):
|
||||
check_date(this)
|
||||
t = this.value
|
||||
tim = MakeTime(
|
||||
HourFromTime(t), MinFromTime(t), SecFromTime(t), ms.to_int())
|
||||
u = TimeClip(MakeDate(Day(t), tim))
|
||||
this.value = u
|
||||
return u
|
||||
|
||||
# todo Complete all setters!
|
||||
|
||||
def toUTCString():
|
||||
check_date(this)
|
||||
return this.utc_strftime('%d %B %Y %H:%M:%S')
|
||||
|
||||
def toISOString():
|
||||
check_date(this)
|
||||
t = this.value
|
||||
year = YearFromTime(t)
|
||||
month, day, hour, minute, second, milli = pad(
|
||||
MonthFromTime(t) + 1), pad(DateFromTime(t)), pad(
|
||||
HourFromTime(t)), pad(MinFromTime(t)), pad(
|
||||
SecFromTime(t)), pad(msFromTime(t))
|
||||
return ISO_FORMAT % (unicode(year) if 0 <= year <= 9999 else pad(
|
||||
year, 6, True), month, day, hour, minute, second, milli)
|
||||
|
||||
def toJSON(key):
|
||||
o = this.to_object()
|
||||
tv = o.to_primitive('Number')
|
||||
if tv.Class == 'Number' and not tv.is_finite():
|
||||
return this.null
|
||||
toISO = o.get('toISOString')
|
||||
if not toISO.is_callable():
|
||||
raise this.MakeError('TypeError', 'toISOString is not callable')
|
||||
return toISO.call(o, ())
|
||||
|
||||
|
||||
def pad(num, n=2, sign=False):
|
||||
'''returns n digit string representation of the num'''
|
||||
s = unicode(abs(num))
|
||||
if len(s) < n:
|
||||
s = '0' * (n - len(s)) + s
|
||||
if not sign:
|
||||
return s
|
||||
if num >= 0:
|
||||
return '+' + s
|
||||
else:
|
||||
return '-' + s
|
||||
|
||||
|
||||
fill_prototype(DatePrototype, DateProto, default_attrs)
|
||||
|
||||
Date.define_own_property(
|
||||
'prototype', {
|
||||
'value': DatePrototype,
|
||||
'enumerable': False,
|
||||
'writable': False,
|
||||
'configurable': False
|
||||
})
|
||||
|
||||
DatePrototype.define_own_property('constructor', {
|
||||
'value': Date,
|
||||
'enumerable': False,
|
||||
'writable': True,
|
||||
'configurable': True
|
||||
})
|
||||
@@ -1,75 +0,0 @@
|
||||
from ..base import *
|
||||
from ..conversions import *
|
||||
from ..func_utils import *
|
||||
from pyjsparser import parse
|
||||
from ..byte_trans import ByteCodeGenerator, Code
|
||||
|
||||
|
||||
def Function(this, args):
|
||||
# convert arguments to python list of strings
|
||||
a = map(to_string, tuple(args))
|
||||
_body = u';'
|
||||
_args = ()
|
||||
if len(a):
|
||||
_body = u'%s;' % a[-1]
|
||||
_args = a[:-1]
|
||||
return executable_function(_body, _args, args.space, global_context=True)
|
||||
|
||||
|
||||
def executable_function(_body, _args, space, global_context=True):
|
||||
func_str = u'(function (%s) { ; %s ; });' % (u', '.join(_args), _body)
|
||||
|
||||
co = executable_code(
|
||||
code_str=func_str, space=space, global_context=global_context)
|
||||
return co()
|
||||
|
||||
|
||||
# you can use this one lovely piece of function to compile and execute code on the fly! Watch out though as it may generate lots of code.
|
||||
# todo tape cleanup? we dont know which pieces are needed and which are not so rather impossible without smarter machinery something like GC,
|
||||
# a one solution would be to have a separate tape for functions
|
||||
def executable_code(code_str, space, global_context=True):
|
||||
# parse first to check if any SyntaxErrors
|
||||
parsed = parse(code_str)
|
||||
|
||||
old_tape_len = len(space.byte_generator.exe.tape)
|
||||
space.byte_generator.record_state()
|
||||
start = space.byte_generator.exe.get_new_label()
|
||||
skip = space.byte_generator.exe.get_new_label()
|
||||
space.byte_generator.emit('JUMP', skip)
|
||||
space.byte_generator.emit('LABEL', start)
|
||||
space.byte_generator.emit(parsed)
|
||||
space.byte_generator.emit('NOP')
|
||||
space.byte_generator.emit('LABEL', skip)
|
||||
space.byte_generator.emit('NOP')
|
||||
space.byte_generator.restore_state()
|
||||
space.byte_generator.exe.compile(
|
||||
start_loc=old_tape_len
|
||||
) # dont read the code from the beginning, dont be stupid!
|
||||
|
||||
ctx = space.GlobalObj if global_context else space.exe.current_ctx
|
||||
|
||||
def ex_code():
|
||||
ret, status, token = space.byte_generator.exe.execute_fragment_under_context(
|
||||
ctx, start, skip)
|
||||
# todo Clean up the tape!
|
||||
# this is NOT a way to do that because the fragment may contain the executable code! We dont want to remove it
|
||||
#del space.byte_generator.exe.tape[old_tape_len:]
|
||||
if status == 0:
|
||||
return ret
|
||||
elif status == 3:
|
||||
raise token
|
||||
else:
|
||||
raise RuntimeError(
|
||||
'Unexpected return status during JIT execution: %d' % status)
|
||||
|
||||
return ex_code
|
||||
|
||||
|
||||
def _eval(this, args):
|
||||
code_str = to_string(get_arg(args, 0))
|
||||
return executable_code(code_str, args.space, global_context=True)()
|
||||
|
||||
|
||||
def log(this, args):
|
||||
print ' '.join(map(to_string, args))
|
||||
return undefined
|
||||
@@ -1,157 +0,0 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from ..conversions import *
|
||||
from ..func_utils import *
|
||||
|
||||
import math
|
||||
import random
|
||||
|
||||
CONSTANTS = {
|
||||
'E': 2.7182818284590452354,
|
||||
'LN10': 2.302585092994046,
|
||||
'LN2': 0.6931471805599453,
|
||||
'LOG2E': 1.4426950408889634,
|
||||
'LOG10E': 0.4342944819032518,
|
||||
'PI': 3.1415926535897932,
|
||||
'SQRT1_2': 0.7071067811865476,
|
||||
'SQRT2': 1.4142135623730951
|
||||
}
|
||||
|
||||
|
||||
class MathFunctions:
|
||||
def abs(this, args):
|
||||
x = get_arg(args, 0)
|
||||
a = to_number(x)
|
||||
if a != a: # it must be a nan
|
||||
return NaN
|
||||
return abs(a)
|
||||
|
||||
def acos(this, args):
|
||||
x = get_arg(args, 0)
|
||||
a = to_number(x)
|
||||
if a != a: # it must be a nan
|
||||
return NaN
|
||||
try:
|
||||
return math.acos(a)
|
||||
except:
|
||||
return NaN
|
||||
|
||||
def asin(this, args):
|
||||
x = get_arg(args, 0)
|
||||
a = to_number(x)
|
||||
if a != a: # it must be a nan
|
||||
return NaN
|
||||
try:
|
||||
return math.asin(a)
|
||||
except:
|
||||
return NaN
|
||||
|
||||
def atan(this, args):
|
||||
x = get_arg(args, 0)
|
||||
a = to_number(x)
|
||||
if a != a: # it must be a nan
|
||||
return NaN
|
||||
return math.atan(a)
|
||||
|
||||
def atan2(this, args):
|
||||
x = get_arg(args, 0)
|
||||
y = get_arg(args, 1)
|
||||
a = to_number(x)
|
||||
b = to_number(y)
|
||||
if a != a or b != b: # it must be a nan
|
||||
return NaN
|
||||
return math.atan2(a, b)
|
||||
|
||||
def ceil(this, args):
|
||||
x = get_arg(args, 0)
|
||||
a = to_number(x)
|
||||
if a != a: # it must be a nan
|
||||
return NaN
|
||||
return float(math.ceil(a))
|
||||
|
||||
def floor(this, args):
|
||||
x = get_arg(args, 0)
|
||||
a = to_number(x)
|
||||
if a != a: # it must be a nan
|
||||
return NaN
|
||||
return float(math.floor(a))
|
||||
|
||||
def round(this, args):
|
||||
x = get_arg(args, 0)
|
||||
a = to_number(x)
|
||||
if a != a: # it must be a nan
|
||||
return NaN
|
||||
return float(round(a))
|
||||
|
||||
def sin(this, args):
|
||||
x = get_arg(args, 0)
|
||||
a = to_number(x)
|
||||
if not is_finite(a): # it must be a nan
|
||||
return NaN
|
||||
return math.sin(a)
|
||||
|
||||
def cos(this, args):
|
||||
x = get_arg(args, 0)
|
||||
a = to_number(x)
|
||||
if not is_finite(a): # it must be a nan
|
||||
return NaN
|
||||
return math.cos(a)
|
||||
|
||||
def tan(this, args):
|
||||
x = get_arg(args, 0)
|
||||
a = to_number(x)
|
||||
if not is_finite(a): # it must be a nan
|
||||
return NaN
|
||||
return math.tan(a)
|
||||
|
||||
def log(this, args):
|
||||
x = get_arg(args, 0)
|
||||
a = to_number(x)
|
||||
if a != a: # it must be a nan
|
||||
return NaN
|
||||
try:
|
||||
return math.log(a)
|
||||
except:
|
||||
return NaN
|
||||
|
||||
def exp(this, args):
|
||||
x = get_arg(args, 0)
|
||||
a = to_number(x)
|
||||
if a != a: # it must be a nan
|
||||
return NaN
|
||||
return math.exp(a)
|
||||
|
||||
def pow(this, args):
|
||||
x = get_arg(args, 0)
|
||||
y = get_arg(args, 1)
|
||||
a = to_number(x)
|
||||
b = to_number(y)
|
||||
if a != a or b != b: # it must be a nan
|
||||
return NaN
|
||||
try:
|
||||
return a**b
|
||||
except:
|
||||
return NaN
|
||||
|
||||
def sqrt(this, args):
|
||||
x = get_arg(args, 0)
|
||||
a = to_number(x)
|
||||
if a != a: # it must be a nan
|
||||
return NaN
|
||||
try:
|
||||
return a**0.5
|
||||
except:
|
||||
return NaN
|
||||
|
||||
def min(this, args):
|
||||
if len(args) == 0:
|
||||
return Infinity
|
||||
return min(map(to_number, tuple(args)))
|
||||
|
||||
def max(this, args):
|
||||
if len(args) == 0:
|
||||
return -Infinity
|
||||
return max(map(to_number, tuple(args)))
|
||||
|
||||
def random(this, args):
|
||||
return random.random()
|
||||
@@ -1,27 +0,0 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from ..conversions import *
|
||||
from ..func_utils import *
|
||||
|
||||
|
||||
def Number(this, args):
|
||||
if len(args) == 0:
|
||||
return 0.
|
||||
return to_number(args[0])
|
||||
|
||||
|
||||
def NumberConstructor(args, space):
|
||||
temp = space.NewObject()
|
||||
temp.prototype = space.NumberPrototype
|
||||
temp.Class = 'Number'
|
||||
temp.value = float(to_number(get_arg(args, 0)) if len(args) > 0 else 0.)
|
||||
return temp
|
||||
|
||||
|
||||
CONSTS = {
|
||||
'MAX_VALUE': 1.7976931348623157e308,
|
||||
'MIN_VALUE': 5.0e-324,
|
||||
'NaN': NaN,
|
||||
'NEGATIVE_INFINITY': Infinity,
|
||||
'POSITIVE_INFINITY': -Infinity
|
||||
}
|
||||
@@ -1,204 +0,0 @@
|
||||
from __future__ import unicode_literals
|
||||
from ..conversions import *
|
||||
from ..func_utils import *
|
||||
from ..base import is_data_descriptor
|
||||
import six
|
||||
|
||||
|
||||
def Object(this, args):
|
||||
val = get_arg(args, 0)
|
||||
if is_null(val) or is_undefined(val):
|
||||
return args.space.NewObject()
|
||||
return to_object(val, args.space)
|
||||
|
||||
|
||||
def ObjectCreate(args, space):
|
||||
if len(args):
|
||||
val = get_arg(args, 0)
|
||||
if is_object(val):
|
||||
# Implementation dependent, but my will simply return :)
|
||||
return val
|
||||
elif type(val) in (NUMBER_TYPE, STRING_TYPE, BOOLEAN_TYPE):
|
||||
return to_object(val, space)
|
||||
return space.NewObject()
|
||||
|
||||
|
||||
class ObjectMethods:
|
||||
def getPrototypeOf(this, args):
|
||||
obj = get_arg(args, 0)
|
||||
if not is_object(obj):
|
||||
raise MakeError('TypeError',
|
||||
'Object.getPrototypeOf called on non-object')
|
||||
return null if obj.prototype is None else obj.prototype
|
||||
|
||||
def getOwnPropertyDescriptor(this, args):
|
||||
obj = get_arg(args, 0)
|
||||
prop = get_arg(args, 1)
|
||||
if not is_object(obj):
|
||||
raise MakeError(
|
||||
'TypeError',
|
||||
'Object.getOwnPropertyDescriptor called on non-object')
|
||||
desc = obj.own.get(to_string(prop))
|
||||
return convert_to_js_type(desc, args.space)
|
||||
|
||||
def getOwnPropertyNames(this, args):
|
||||
obj = get_arg(args, 0)
|
||||
if not is_object(obj):
|
||||
raise MakeError(
|
||||
'TypeError',
|
||||
'Object.getOwnPropertyDescriptor called on non-object')
|
||||
return args.space.ConstructArray(obj.own.keys())
|
||||
|
||||
def create(this, args):
|
||||
obj = get_arg(args, 0)
|
||||
if not (is_object(obj) or is_null(obj)):
|
||||
raise MakeError('TypeError',
|
||||
'Object prototype may only be an Object or null')
|
||||
temp = args.space.NewObject()
|
||||
temp.prototype = None if is_null(obj) else obj
|
||||
if len(args) > 1 and not is_undefined(args[1]):
|
||||
if six.PY2:
|
||||
args.tup = (args[1], )
|
||||
ObjectMethods.defineProperties.__func__(temp, args)
|
||||
else:
|
||||
args.tup = (args[1], )
|
||||
ObjectMethods.defineProperties(temp, args)
|
||||
return temp
|
||||
|
||||
def defineProperty(this, args):
|
||||
obj = get_arg(args, 0)
|
||||
prop = get_arg(args, 1)
|
||||
attrs = get_arg(args, 2)
|
||||
if not is_object(obj):
|
||||
raise MakeError('TypeError',
|
||||
'Object.defineProperty called on non-object')
|
||||
name = to_string(prop)
|
||||
if not obj.define_own_property(name, ToPropertyDescriptor(attrs),
|
||||
False):
|
||||
raise MakeError('TypeError', 'Cannot redefine property: %s' % name)
|
||||
return obj
|
||||
|
||||
def defineProperties(this, args):
|
||||
obj = get_arg(args, 0)
|
||||
properties = get_arg(args, 1)
|
||||
if not is_object(obj):
|
||||
raise MakeError('TypeError',
|
||||
'Object.defineProperties called on non-object')
|
||||
props = to_object(properties, args.space)
|
||||
for k, v in props.own.items():
|
||||
if not v.get('enumerable'):
|
||||
continue
|
||||
desc = ToPropertyDescriptor(props.get(unicode(k)))
|
||||
if not obj.define_own_property(unicode(k), desc, False):
|
||||
raise MakeError('TypeError',
|
||||
'Failed to define own property: %s' % k)
|
||||
return obj
|
||||
|
||||
def seal(this, args):
|
||||
obj = get_arg(args, 0)
|
||||
if not is_object(obj):
|
||||
raise MakeError('TypeError', 'Object.seal called on non-object')
|
||||
for desc in obj.own.values():
|
||||
desc['configurable'] = False
|
||||
obj.extensible = False
|
||||
return obj
|
||||
|
||||
def freeze(this, args):
|
||||
obj = get_arg(args, 0)
|
||||
if not is_object(obj):
|
||||
raise MakeError('TypeError', 'Object.freeze called on non-object')
|
||||
for desc in obj.own.values():
|
||||
desc['configurable'] = False
|
||||
if is_data_descriptor(desc):
|
||||
desc['writable'] = False
|
||||
obj.extensible = False
|
||||
return obj
|
||||
|
||||
def preventExtensions(this, args):
|
||||
obj = get_arg(args, 0)
|
||||
if not is_object(obj):
|
||||
raise MakeError('TypeError',
|
||||
'Object.preventExtensions on non-object')
|
||||
obj.extensible = False
|
||||
return obj
|
||||
|
||||
def isSealed(this, args):
|
||||
obj = get_arg(args, 0)
|
||||
if not is_object(obj):
|
||||
raise MakeError('TypeError',
|
||||
'Object.isSealed called on non-object')
|
||||
if obj.extensible:
|
||||
return False
|
||||
for desc in obj.own.values():
|
||||
if desc.get('configurable'):
|
||||
return False
|
||||
return True
|
||||
|
||||
def isFrozen(this, args):
|
||||
obj = get_arg(args, 0)
|
||||
if not is_object(obj):
|
||||
raise MakeError('TypeError',
|
||||
'Object.isFrozen called on non-object')
|
||||
if obj.extensible:
|
||||
return False
|
||||
for desc in obj.own.values():
|
||||
if desc.get('configurable'):
|
||||
return False
|
||||
if is_data_descriptor(desc) and desc.get('writable'):
|
||||
return False
|
||||
return True
|
||||
|
||||
def isExtensible(this, args):
|
||||
obj = get_arg(args, 0)
|
||||
if not is_object(obj):
|
||||
raise MakeError('TypeError',
|
||||
'Object.isExtensible called on non-object')
|
||||
return obj.extensible
|
||||
|
||||
def keys(this, args):
|
||||
obj = get_arg(args, 0)
|
||||
if not is_object(obj):
|
||||
raise MakeError('TypeError', 'Object.keys called on non-object')
|
||||
return args.space.ConstructArray([
|
||||
unicode(e) for e, d in six.iteritems(obj.own)
|
||||
if d.get('enumerable')
|
||||
])
|
||||
|
||||
|
||||
# some utility functions:
|
||||
|
||||
|
||||
def ToPropertyDescriptor(obj): # page 38 (50 absolute)
|
||||
if not is_object(obj):
|
||||
raise MakeError('TypeError',
|
||||
'Can\'t convert non-object to property descriptor')
|
||||
desc = {}
|
||||
if obj.has_property('enumerable'):
|
||||
desc['enumerable'] = to_boolean(obj.get('enumerable'))
|
||||
if obj.has_property('configurable'):
|
||||
desc['configurable'] = to_boolean(obj.get('configurable'))
|
||||
if obj.has_property('value'):
|
||||
desc['value'] = obj.get('value')
|
||||
if obj.has_property('writable'):
|
||||
desc['writable'] = to_boolean(obj.get('writable'))
|
||||
if obj.has_property('get'):
|
||||
cand = obj.get('get')
|
||||
if not (is_undefined(cand) or is_callable(cand)):
|
||||
raise MakeError(
|
||||
'TypeError',
|
||||
'Invalid getter (it has to be a function or undefined)')
|
||||
desc['get'] = cand
|
||||
if obj.has_property('set'):
|
||||
cand = obj.get('set')
|
||||
if not (is_undefined(cand) or is_callable(cand)):
|
||||
raise MakeError(
|
||||
'TypeError',
|
||||
'Invalid setter (it has to be a function or undefined)')
|
||||
desc['set'] = cand
|
||||
if ('get' in desc or 'set' in desc) and ('value' in desc
|
||||
or 'writable' in desc):
|
||||
raise MakeError(
|
||||
'TypeError',
|
||||
'Invalid property. A property cannot both have accessors and be writable or have a value.'
|
||||
)
|
||||
return desc
|
||||
@@ -1,41 +0,0 @@
|
||||
from __future__ import unicode_literals
|
||||
from ..conversions import *
|
||||
from ..func_utils import *
|
||||
from ..base import SpaceTuple
|
||||
|
||||
REG_EXP_FLAGS = ('g', 'i', 'm')
|
||||
|
||||
|
||||
def RegExp(this, args):
|
||||
pattern = get_arg(args, 0)
|
||||
flags = get_arg(args, 1)
|
||||
if GetClass(pattern) == 'RegExp':
|
||||
if not is_undefined(flags):
|
||||
raise MakeError(
|
||||
'TypeError',
|
||||
'Cannot supply flags when constructing one RegExp from another'
|
||||
)
|
||||
# return unchanged
|
||||
return pattern
|
||||
#pattern is not a regexp
|
||||
if is_undefined(pattern):
|
||||
pattern = u''
|
||||
else:
|
||||
pattern = to_string(pattern)
|
||||
flags = to_string(flags) if not is_undefined(flags) else u''
|
||||
for flag in flags:
|
||||
if flag not in REG_EXP_FLAGS:
|
||||
raise MakeError(
|
||||
'SyntaxError',
|
||||
'Invalid flags supplied to RegExp constructor "%s"' % flag)
|
||||
if len(set(flags)) != len(flags):
|
||||
raise MakeError(
|
||||
'SyntaxError',
|
||||
'Invalid flags supplied to RegExp constructor "%s"' % flags)
|
||||
return args.space.NewRegExp(pattern, flags)
|
||||
|
||||
|
||||
def RegExpCreate(args, space):
|
||||
_args = SpaceTuple(args)
|
||||
_args.space = space
|
||||
return RegExp(undefined, _args)
|
||||
@@ -1,23 +0,0 @@
|
||||
from ..conversions import *
|
||||
from ..func_utils import *
|
||||
|
||||
|
||||
def fromCharCode(this, args):
|
||||
res = u''
|
||||
for e in args:
|
||||
res += unichr(to_uint16(e))
|
||||
return res
|
||||
|
||||
|
||||
def String(this, args):
|
||||
if len(args) == 0:
|
||||
return u''
|
||||
return to_string(args[0])
|
||||
|
||||
|
||||
def StringConstructor(args, space):
|
||||
temp = space.NewObject()
|
||||
temp.prototype = space.StringPrototype
|
||||
temp.Class = 'String'
|
||||
temp.value = to_string(get_arg(args, 0)) if len(args) > 0 else u''
|
||||
return temp
|
||||
@@ -1,209 +0,0 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
# NOTE: t must be INT!!!
|
||||
import time
|
||||
import datetime
|
||||
import warnings
|
||||
|
||||
try:
|
||||
from tzlocal import get_localzone
|
||||
LOCAL_ZONE = get_localzone()
|
||||
except: # except all problems...
|
||||
warnings.warn(
|
||||
'Please install or fix tzlocal library (pip install tzlocal) in order to make Date object work better. Otherwise I will assume DST is in effect all the time'
|
||||
)
|
||||
|
||||
class LOCAL_ZONE:
|
||||
@staticmethod
|
||||
def dst(*args):
|
||||
return 1
|
||||
|
||||
|
||||
from js2py.base import MakeError
|
||||
CUM = (0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365)
|
||||
msPerDay = 86400000
|
||||
msPerYear = int(86400000 * 365.242)
|
||||
msPerSecond = 1000
|
||||
msPerMinute = 60000
|
||||
msPerHour = 3600000
|
||||
HoursPerDay = 24
|
||||
MinutesPerHour = 60
|
||||
SecondsPerMinute = 60
|
||||
NaN = float('nan')
|
||||
LocalTZA = -time.timezone * msPerSecond
|
||||
|
||||
|
||||
def DaylightSavingTA(t):
|
||||
if t is NaN:
|
||||
return t
|
||||
try:
|
||||
return int(
|
||||
LOCAL_ZONE.dst(datetime.datetime.utcfromtimestamp(
|
||||
t // 1000)).seconds) * 1000
|
||||
except:
|
||||
warnings.warn(
|
||||
'Invalid datetime date, assumed DST time, may be inaccurate...',
|
||||
Warning)
|
||||
return 1
|
||||
#raise MakeError('TypeError', 'date not supported by python.datetime. I will solve it in future versions')
|
||||
|
||||
|
||||
def GetTimeZoneName(t):
|
||||
return time.tzname[DaylightSavingTA(t) > 0]
|
||||
|
||||
|
||||
def LocalToUTC(t):
|
||||
return t - LocalTZA - DaylightSavingTA(t - LocalTZA)
|
||||
|
||||
|
||||
def UTCToLocal(t):
|
||||
return t + LocalTZA + DaylightSavingTA(t)
|
||||
|
||||
|
||||
def Day(t):
|
||||
return t // 86400000
|
||||
|
||||
|
||||
def TimeWithinDay(t):
|
||||
return t % 86400000
|
||||
|
||||
|
||||
def DaysInYear(y):
|
||||
if y % 4:
|
||||
return 365
|
||||
elif y % 100:
|
||||
return 366
|
||||
elif y % 400:
|
||||
return 365
|
||||
else:
|
||||
return 366
|
||||
|
||||
|
||||
def DayFromYear(y):
|
||||
return 365 * (y - 1970) + (y - 1969) // 4 - (y - 1901) // 100 + (
|
||||
y - 1601) // 400
|
||||
|
||||
|
||||
def TimeFromYear(y):
|
||||
return 86400000 * DayFromYear(y)
|
||||
|
||||
|
||||
def YearFromTime(t):
|
||||
guess = 1970 - t // 31556908800 # msPerYear
|
||||
gt = TimeFromYear(guess)
|
||||
if gt <= t:
|
||||
while gt <= t:
|
||||
guess += 1
|
||||
gt = TimeFromYear(guess)
|
||||
return guess - 1
|
||||
else:
|
||||
while gt > t:
|
||||
guess -= 1
|
||||
gt = TimeFromYear(guess)
|
||||
return guess
|
||||
|
||||
|
||||
def DayWithinYear(t):
|
||||
return Day(t) - DayFromYear(YearFromTime(t))
|
||||
|
||||
|
||||
def InLeapYear(t):
|
||||
y = YearFromTime(t)
|
||||
if y % 4:
|
||||
return 0
|
||||
elif y % 100:
|
||||
return 1
|
||||
elif y % 400:
|
||||
return 0
|
||||
else:
|
||||
return 1
|
||||
|
||||
|
||||
def MonthFromTime(t):
|
||||
day = DayWithinYear(t)
|
||||
leap = InLeapYear(t)
|
||||
if day < 31:
|
||||
return 0
|
||||
day -= leap
|
||||
if day < 59:
|
||||
return 1
|
||||
elif day < 90:
|
||||
return 2
|
||||
elif day < 120:
|
||||
return 3
|
||||
elif day < 151:
|
||||
return 4
|
||||
elif day < 181:
|
||||
return 5
|
||||
elif day < 212:
|
||||
return 6
|
||||
elif day < 243:
|
||||
return 7
|
||||
elif day < 273:
|
||||
return 8
|
||||
elif day < 304:
|
||||
return 9
|
||||
elif day < 334:
|
||||
return 10
|
||||
else:
|
||||
return 11
|
||||
|
||||
|
||||
def DateFromTime(t):
|
||||
mon = MonthFromTime(t)
|
||||
day = DayWithinYear(t)
|
||||
return day - CUM[mon] - (1 if InLeapYear(t) and mon >= 2 else 0) + 1
|
||||
|
||||
|
||||
def WeekDay(t):
|
||||
# 0 == sunday
|
||||
return (Day(t) + 4) % 7
|
||||
|
||||
|
||||
def msFromTime(t):
|
||||
return t % 1000
|
||||
|
||||
|
||||
def SecFromTime(t):
|
||||
return (t // 1000) % 60
|
||||
|
||||
|
||||
def MinFromTime(t):
|
||||
return (t // 60000) % 60
|
||||
|
||||
|
||||
def HourFromTime(t):
|
||||
return (t // 3600000) % 24
|
||||
|
||||
|
||||
def MakeTime(hour, Min, sec, ms):
|
||||
# takes PyJs objects and returns t
|
||||
if not (hour.is_finite() and Min.is_finite() and sec.is_finite()
|
||||
and ms.is_finite()):
|
||||
return NaN
|
||||
h, m, s, milli = hour.to_int(), Min.to_int(), sec.to_int(), ms.to_int()
|
||||
return h * 3600000 + m * 60000 + s * 1000 + milli
|
||||
|
||||
|
||||
def MakeDay(year, month, date):
|
||||
# takes PyJs objects and returns t
|
||||
if not (year.is_finite() and month.is_finite() and date.is_finite()):
|
||||
return NaN
|
||||
y, m, dt = year.to_int(), month.to_int(), date.to_int()
|
||||
y += m // 12
|
||||
mn = m % 12
|
||||
d = DayFromYear(y) + CUM[mn] + dt - 1 + (1 if DaysInYear(y) == 366
|
||||
and mn >= 2 else 0)
|
||||
return d # ms per day
|
||||
|
||||
|
||||
def MakeDate(day, time):
|
||||
return 86400000 * day + time
|
||||
|
||||
|
||||
def TimeClip(t):
|
||||
if t != t or abs(t) == float('inf'):
|
||||
return NaN
|
||||
if abs(t) > 8.64 * 10**15:
|
||||
return NaN
|
||||
return int(t)
|
||||
@@ -1,141 +0,0 @@
|
||||
from __future__ import unicode_literals
|
||||
# Type Conversions. to_type. All must return PyJs subclass instance
|
||||
from .simplex import *
|
||||
|
||||
|
||||
def to_primitive(self, hint=None):
|
||||
if is_primitive(self):
|
||||
return self
|
||||
if hint is None and (self.Class == 'Number' or self.Class == 'Boolean'):
|
||||
# favour number for Class== Number or Boolean default = String
|
||||
hint = 'Number'
|
||||
return self.default_value(hint)
|
||||
|
||||
|
||||
def to_boolean(self):
|
||||
typ = Type(self)
|
||||
if typ == 'Boolean': # no need to convert
|
||||
return self
|
||||
elif typ == 'Null' or typ == 'Undefined': # they are both always false
|
||||
return False
|
||||
elif typ == 'Number': # false only for 0, and NaN
|
||||
return self and self == self # test for nan (nan -> flase)
|
||||
elif typ == 'String':
|
||||
return bool(self)
|
||||
else: # object - always True
|
||||
return True
|
||||
|
||||
|
||||
def to_number(self):
|
||||
typ = Type(self)
|
||||
if typ == 'Number': # or self.Class=='Number': # no need to convert
|
||||
return self
|
||||
elif typ == 'Null': # null is 0
|
||||
return 0.
|
||||
elif typ == 'Undefined': # undefined is NaN
|
||||
return NaN
|
||||
elif typ == 'Boolean': # 1 for True 0 for false
|
||||
return float(self)
|
||||
elif typ == 'String':
|
||||
s = self.strip() # Strip white space
|
||||
if not s: # '' is simply 0
|
||||
return 0.
|
||||
if 'x' in s or 'X' in s[:3]: # hex (positive only)
|
||||
try: # try to convert
|
||||
num = int(s, 16)
|
||||
except ValueError: # could not convert -> NaN
|
||||
return NaN
|
||||
return float(num)
|
||||
sign = 1 # get sign
|
||||
if s[0] in '+-':
|
||||
if s[0] == '-':
|
||||
sign = -1
|
||||
s = s[1:]
|
||||
if s == 'Infinity': # Check for infinity keyword. 'NaN' will be NaN anyway.
|
||||
return sign * Infinity
|
||||
try: # decimal try
|
||||
num = sign * float(s) # Converted
|
||||
except ValueError:
|
||||
return NaN # could not convert to decimal > return NaN
|
||||
return float(num)
|
||||
else: # object - most likely it will be NaN.
|
||||
return to_number(to_primitive(self, 'Number'))
|
||||
|
||||
|
||||
def to_string(self):
|
||||
typ = Type(self)
|
||||
if typ == 'String':
|
||||
return self
|
||||
elif typ == 'Null':
|
||||
return 'null'
|
||||
elif typ == 'Undefined':
|
||||
return 'undefined'
|
||||
elif typ == 'Boolean':
|
||||
return 'true' if self else 'false'
|
||||
elif typ == 'Number': # or self.Class=='Number':
|
||||
return js_dtoa(self)
|
||||
else: # object
|
||||
return to_string(to_primitive(self, 'String'))
|
||||
|
||||
|
||||
def to_object(self, space):
|
||||
typ = Type(self)
|
||||
if typ == 'Object':
|
||||
return self
|
||||
elif typ == 'Boolean': # Unsure ... todo check here
|
||||
return space.Boolean.create((self, ), space)
|
||||
elif typ == 'Number': # ?
|
||||
return space.Number.create((self, ), space)
|
||||
elif typ == 'String': # ?
|
||||
return space.String.create((self, ), space)
|
||||
elif typ == 'Null' or typ == 'Undefined':
|
||||
raise MakeError('TypeError',
|
||||
'undefined or null can\'t be converted to object')
|
||||
else:
|
||||
raise RuntimeError()
|
||||
|
||||
|
||||
def to_int32(self):
|
||||
num = to_number(self)
|
||||
if is_nan(num) or is_infinity(num):
|
||||
return 0
|
||||
int32 = int(num) % 2**32
|
||||
return int(int32 - 2**32 if int32 >= 2**31 else int32)
|
||||
|
||||
|
||||
def to_int(self):
|
||||
num = to_number(self)
|
||||
if is_nan(num):
|
||||
return 0
|
||||
elif is_infinity(num):
|
||||
return 10**20 if num > 0 else -10**20
|
||||
return int(num)
|
||||
|
||||
|
||||
def to_uint32(self):
|
||||
num = to_number(self)
|
||||
if is_nan(num) or is_infinity(num):
|
||||
return 0
|
||||
return int(num) % 2**32
|
||||
|
||||
|
||||
def to_uint16(self):
|
||||
num = to_number(self)
|
||||
if is_nan(num) or is_infinity(num):
|
||||
return 0
|
||||
return int(num) % 2**16
|
||||
|
||||
|
||||
def to_int16(self):
|
||||
num = to_number(self)
|
||||
if is_nan(num) or is_infinity(num):
|
||||
return 0
|
||||
int16 = int(num) % 2**16
|
||||
return int(int16 - 2**16 if int16 >= 2**15 else int16)
|
||||
|
||||
|
||||
def cok(self):
|
||||
"""Check object coercible"""
|
||||
if type(self) in (UNDEFINED_TYPE, NULL_TYPE):
|
||||
raise MakeError('TypeError',
|
||||
'undefined or null can\'t be converted to object')
|
||||
@@ -1,90 +0,0 @@
|
||||
# todo make sure what they mean by desc undefined? None or empty? Answer: None :) it can never be empty but None is sometimes returned.
|
||||
|
||||
# I am implementing everything as dicts to speed up property creation
|
||||
|
||||
# Warning: value, get, set props of dest are PyJs types. Rest is Py!
|
||||
|
||||
|
||||
def is_data_descriptor(desc):
|
||||
return desc and ('value' in desc or 'writable' in desc)
|
||||
|
||||
|
||||
def is_accessor_descriptor(desc):
|
||||
return desc and ('get' in desc or 'set' in desc)
|
||||
|
||||
|
||||
def is_generic_descriptor(
|
||||
desc
|
||||
): # generic means not the data and not the setter - therefore it must be one that changes only enum and conf
|
||||
return desc and not (is_data_descriptor(desc)
|
||||
or is_accessor_descriptor(desc))
|
||||
|
||||
|
||||
def from_property_descriptor(desc, space):
|
||||
if not desc:
|
||||
return {}
|
||||
obj = space.NewObject()
|
||||
if is_data_descriptor(desc):
|
||||
obj.define_own_property(
|
||||
'value', {
|
||||
'value': desc['value'],
|
||||
'writable': True,
|
||||
'enumerable': True,
|
||||
'configurable': True
|
||||
}, False)
|
||||
obj.define_own_property(
|
||||
'writable', {
|
||||
'value': desc['writable'],
|
||||
'writable': True,
|
||||
'enumerable': True,
|
||||
'configurable': True
|
||||
}, False)
|
||||
else:
|
||||
obj.define_own_property(
|
||||
'get', {
|
||||
'value': desc['get'],
|
||||
'writable': True,
|
||||
'enumerable': True,
|
||||
'configurable': True
|
||||
}, False)
|
||||
obj.define_own_property(
|
||||
'set', {
|
||||
'value': desc['set'],
|
||||
'writable': True,
|
||||
'enumerable': True,
|
||||
'configurable': True
|
||||
}, False)
|
||||
obj.define_own_property(
|
||||
'writable', {
|
||||
'value': desc['writable'],
|
||||
'writable': True,
|
||||
'enumerable': True,
|
||||
'configurable': True
|
||||
}, False)
|
||||
obj.define_own_property(
|
||||
'enumerable', {
|
||||
'value': desc['enumerable'],
|
||||
'writable': True,
|
||||
'enumerable': True,
|
||||
'configurable': True
|
||||
}, False)
|
||||
return obj
|
||||
|
||||
|
||||
def to_property_descriptor(obj):
|
||||
if obj._type() != 'Object':
|
||||
raise TypeError()
|
||||
desc = {}
|
||||
for e in ('enumerable', 'configurable', 'writable'):
|
||||
if obj.has_property(e):
|
||||
desc[e] = obj.get(e).to_boolean().value
|
||||
if obj.has_property('value'):
|
||||
desc['value'] = obj.get('value')
|
||||
for e in ('get', 'set'):
|
||||
if obj.has_property(e):
|
||||
cand = obj.get(e)
|
||||
if not (cand.is_callable() or cand.is_undefined()):
|
||||
raise TypeError()
|
||||
if ('get' in desc or 'set' in desc) and ('value' in desc
|
||||
or 'writable' in desc):
|
||||
raise TypeError()
|
||||
@@ -1,281 +0,0 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from .base import Scope
|
||||
from .func_utils import *
|
||||
from .conversions import *
|
||||
import six
|
||||
from .prototypes.jsboolean import BooleanPrototype
|
||||
from .prototypes.jserror import ErrorPrototype
|
||||
from .prototypes.jsfunction import FunctionPrototype
|
||||
from .prototypes.jsnumber import NumberPrototype
|
||||
from .prototypes.jsobject import ObjectPrototype
|
||||
from .prototypes.jsregexp import RegExpPrototype
|
||||
from .prototypes.jsstring import StringPrototype
|
||||
from .prototypes.jsarray import ArrayPrototype
|
||||
from .prototypes import jsjson
|
||||
from .prototypes import jsutils
|
||||
|
||||
from .constructors import jsnumber, jsstring, jsarray, jsboolean, jsregexp, jsmath, jsobject, jsfunction, jsconsole
|
||||
|
||||
|
||||
|
||||
def fill_proto(proto, proto_class, space):
|
||||
for i in dir(proto_class):
|
||||
e = getattr(proto_class, i)
|
||||
if six.PY2:
|
||||
if hasattr(e, '__func__'):
|
||||
meth = e.__func__
|
||||
else:
|
||||
continue
|
||||
else:
|
||||
if hasattr(e, '__call__') and not i.startswith('__'):
|
||||
meth = e
|
||||
else:
|
||||
continue
|
||||
meth_name = meth.__name__.strip('_') # RexExp._exec -> RegExp.exec
|
||||
js_meth = space.NewFunction(meth, space.ctx, (), meth_name, False, ())
|
||||
set_non_enumerable(proto, meth_name, js_meth)
|
||||
return proto
|
||||
|
||||
|
||||
def easy_func(f, space):
|
||||
return space.NewFunction(f, space.ctx, (), f.__name__, False, ())
|
||||
|
||||
|
||||
def Empty(this, args):
|
||||
return undefined
|
||||
|
||||
|
||||
def set_non_enumerable(obj, name, prop):
|
||||
obj.define_own_property(
|
||||
unicode(name), {
|
||||
'value': prop,
|
||||
'writable': True,
|
||||
'enumerable': False,
|
||||
'configurable': True
|
||||
}, True)
|
||||
|
||||
|
||||
def set_protected(obj, name, prop):
|
||||
obj.define_own_property(
|
||||
unicode(name), {
|
||||
'value': prop,
|
||||
'writable': False,
|
||||
'enumerable': False,
|
||||
'configurable': False
|
||||
}, True)
|
||||
|
||||
|
||||
def fill_space(space, byte_generator):
|
||||
# set global scope
|
||||
global_scope = Scope({}, space, parent=None)
|
||||
global_scope.THIS_BINDING = global_scope
|
||||
global_scope.registers(byte_generator.declared_vars)
|
||||
space.GlobalObj = global_scope
|
||||
|
||||
space.byte_generator = byte_generator
|
||||
|
||||
# first init all protos, later take care of constructors and details
|
||||
|
||||
# Function must be first obviously, we have to use a small trick to do that...
|
||||
function_proto = space.NewFunction(Empty, space.ctx, (), 'Empty', False,
|
||||
())
|
||||
space.FunctionPrototype = function_proto # this will fill the prototypes of the methods!
|
||||
fill_proto(function_proto, FunctionPrototype, space)
|
||||
|
||||
# Object next
|
||||
object_proto = space.NewObject() # no proto
|
||||
fill_proto(object_proto, ObjectPrototype, space)
|
||||
space.ObjectPrototype = object_proto
|
||||
function_proto.prototype = object_proto
|
||||
|
||||
# Number
|
||||
number_proto = space.NewObject()
|
||||
number_proto.prototype = object_proto
|
||||
fill_proto(number_proto, NumberPrototype, space)
|
||||
number_proto.value = 0.
|
||||
number_proto.Class = 'Number'
|
||||
space.NumberPrototype = number_proto
|
||||
|
||||
# String
|
||||
string_proto = space.NewObject()
|
||||
string_proto.prototype = object_proto
|
||||
fill_proto(string_proto, StringPrototype, space)
|
||||
string_proto.value = u''
|
||||
string_proto.Class = 'String'
|
||||
space.StringPrototype = string_proto
|
||||
|
||||
# Boolean
|
||||
boolean_proto = space.NewObject()
|
||||
boolean_proto.prototype = object_proto
|
||||
fill_proto(boolean_proto, BooleanPrototype, space)
|
||||
boolean_proto.value = False
|
||||
boolean_proto.Class = 'Boolean'
|
||||
space.BooleanPrototype = boolean_proto
|
||||
|
||||
# Array
|
||||
array_proto = space.NewArray(0)
|
||||
array_proto.prototype = object_proto
|
||||
fill_proto(array_proto, ArrayPrototype, space)
|
||||
space.ArrayPrototype = array_proto
|
||||
|
||||
# JSON
|
||||
json = space.NewObject()
|
||||
json.put(u'stringify', easy_func(jsjson.stringify, space))
|
||||
json.put(u'parse', easy_func(jsjson.parse, space))
|
||||
|
||||
# Utils
|
||||
parseFloat = easy_func(jsutils.parseFloat, space)
|
||||
parseInt = easy_func(jsutils.parseInt, space)
|
||||
isNaN = easy_func(jsutils.isNaN, space)
|
||||
isFinite = easy_func(jsutils.isFinite, space)
|
||||
|
||||
# Error
|
||||
error_proto = space.NewError(u'Error', u'')
|
||||
error_proto.prototype = object_proto
|
||||
error_proto.put(u'name', u'Error')
|
||||
fill_proto(error_proto, ErrorPrototype, space)
|
||||
space.ErrorPrototype = error_proto
|
||||
|
||||
def construct_constructor(typ):
|
||||
def creator(this, args):
|
||||
message = get_arg(args, 0)
|
||||
if not is_undefined(message):
|
||||
msg = to_string(message)
|
||||
else:
|
||||
msg = u''
|
||||
return space.NewError(typ, msg)
|
||||
|
||||
j = easy_func(creator, space)
|
||||
j.name = unicode(typ)
|
||||
|
||||
set_protected(j, 'prototype', space.ERROR_TYPES[typ])
|
||||
|
||||
set_non_enumerable(space.ERROR_TYPES[typ], 'constructor', j)
|
||||
|
||||
def new_create(args, space):
|
||||
message = get_arg(args, 0)
|
||||
if not is_undefined(message):
|
||||
msg = to_string(message)
|
||||
else:
|
||||
msg = u''
|
||||
return space.NewError(typ, msg)
|
||||
|
||||
j.create = new_create
|
||||
return j
|
||||
|
||||
# fill remaining error types
|
||||
error_constructors = {}
|
||||
for err_type_name in (u'Error', u'EvalError', u'RangeError',
|
||||
u'ReferenceError', u'SyntaxError', u'TypeError',
|
||||
u'URIError'):
|
||||
extra_err = space.NewError(u'Error', u'')
|
||||
extra_err.put(u'name', err_type_name)
|
||||
setattr(space, err_type_name + u'Prototype', extra_err)
|
||||
error_constructors[err_type_name] = construct_constructor(
|
||||
err_type_name)
|
||||
|
||||
assert space.TypeErrorPrototype is not None
|
||||
|
||||
# RegExp
|
||||
regexp_proto = space.NewRegExp(u'(?:)', u'')
|
||||
regexp_proto.prototype = object_proto
|
||||
fill_proto(regexp_proto, RegExpPrototype, space)
|
||||
space.RegExpPrototype = regexp_proto
|
||||
|
||||
# Json
|
||||
|
||||
# now all these boring constructors...
|
||||
|
||||
# Number
|
||||
number = easy_func(jsnumber.Number, space)
|
||||
space.Number = number
|
||||
number.create = jsnumber.NumberConstructor
|
||||
set_non_enumerable(number_proto, 'constructor', number)
|
||||
set_protected(number, 'prototype', number_proto)
|
||||
# number has some extra constants
|
||||
for k, v in jsnumber.CONSTS.items():
|
||||
set_protected(number, k, v)
|
||||
|
||||
# String
|
||||
string = easy_func(jsstring.String, space)
|
||||
space.String = string
|
||||
string.create = jsstring.StringConstructor
|
||||
set_non_enumerable(string_proto, 'constructor', string)
|
||||
set_protected(string, 'prototype', string_proto)
|
||||
# string has an extra function
|
||||
set_non_enumerable(string, 'fromCharCode',
|
||||
easy_func(jsstring.fromCharCode, space))
|
||||
|
||||
# Boolean
|
||||
boolean = easy_func(jsboolean.Boolean, space)
|
||||
space.Boolean = boolean
|
||||
boolean.create = jsboolean.BooleanConstructor
|
||||
set_non_enumerable(boolean_proto, 'constructor', boolean)
|
||||
set_protected(boolean, 'prototype', boolean_proto)
|
||||
|
||||
# Array
|
||||
array = easy_func(jsarray.Array, space)
|
||||
space.Array = array
|
||||
array.create = jsarray.ArrayConstructor
|
||||
set_non_enumerable(array_proto, 'constructor', array)
|
||||
set_protected(array, 'prototype', array_proto)
|
||||
array.put(u'isArray', easy_func(jsarray.isArray, space))
|
||||
|
||||
# RegExp
|
||||
regexp = easy_func(jsregexp.RegExp, space)
|
||||
space.RegExp = regexp
|
||||
regexp.create = jsregexp.RegExpCreate
|
||||
set_non_enumerable(regexp_proto, 'constructor', regexp)
|
||||
set_protected(regexp, 'prototype', regexp_proto)
|
||||
|
||||
# Object
|
||||
_object = easy_func(jsobject.Object, space)
|
||||
space.Object = _object
|
||||
_object.create = jsobject.ObjectCreate
|
||||
set_non_enumerable(object_proto, 'constructor', _object)
|
||||
set_protected(_object, 'prototype', object_proto)
|
||||
fill_proto(_object, jsobject.ObjectMethods, space)
|
||||
|
||||
# Function
|
||||
function = easy_func(jsfunction.Function, space)
|
||||
space.Function = function
|
||||
|
||||
# Math
|
||||
math = space.NewObject()
|
||||
math.Class = 'Math'
|
||||
fill_proto(math, jsmath.MathFunctions, space)
|
||||
for k, v in jsmath.CONSTANTS.items():
|
||||
set_protected(math, k, v)
|
||||
|
||||
console = space.NewObject()
|
||||
fill_proto(console, jsconsole.ConsoleMethods, space)
|
||||
|
||||
# set global object
|
||||
builtins = {
|
||||
'String': string,
|
||||
'Number': number,
|
||||
'Boolean': boolean,
|
||||
'RegExp': regexp,
|
||||
'exports': convert_to_js_type({}, space),
|
||||
'Math': math,
|
||||
#'Date',
|
||||
'Object': _object,
|
||||
'Function': function,
|
||||
'JSON': json,
|
||||
'Array': array,
|
||||
'parseFloat': parseFloat,
|
||||
'parseInt': parseInt,
|
||||
'isFinite': isFinite,
|
||||
'isNaN': isNaN,
|
||||
'eval': easy_func(jsfunction._eval, space),
|
||||
'console': console,
|
||||
'log': console.get(u'log'),
|
||||
}
|
||||
|
||||
builtins.update(error_constructors)
|
||||
|
||||
set_protected(global_scope, 'NaN', NaN)
|
||||
set_protected(global_scope, 'Infinity', Infinity)
|
||||
for k, v in builtins.items():
|
||||
set_non_enumerable(global_scope, k, v)
|
||||
@@ -1,73 +0,0 @@
|
||||
from .simplex import *
|
||||
from .conversions import *
|
||||
|
||||
import six
|
||||
if six.PY3:
|
||||
basestring = str
|
||||
long = int
|
||||
xrange = range
|
||||
unicode = str
|
||||
|
||||
|
||||
def get_arg(arguments, n):
|
||||
if len(arguments) <= n:
|
||||
return undefined
|
||||
return arguments[n]
|
||||
|
||||
|
||||
def ensure_js_types(args, space=None):
|
||||
return tuple(convert_to_js_type(e, space=space) for e in args)
|
||||
|
||||
|
||||
def convert_to_js_type(e, space=None):
|
||||
t = type(e)
|
||||
if is_js_type(e):
|
||||
return e
|
||||
if t in (int, long, float):
|
||||
return float(e)
|
||||
elif isinstance(t, basestring):
|
||||
return unicode(t)
|
||||
elif t in (list, tuple):
|
||||
if space is None:
|
||||
raise MakeError(
|
||||
'TypeError',
|
||||
'Actually an internal error, could not convert to js type because space not specified'
|
||||
)
|
||||
return space.ConstructArray(ensure_js_types(e, space=space))
|
||||
elif t == dict:
|
||||
if space is None:
|
||||
raise MakeError(
|
||||
'TypeError',
|
||||
'Actually an internal error, could not convert to js type because space not specified'
|
||||
)
|
||||
new = {}
|
||||
for k, v in e.items():
|
||||
new[to_string(convert_to_js_type(k, space))] = convert_to_js_type(
|
||||
v, space)
|
||||
return space.ConstructObject(new)
|
||||
else:
|
||||
raise MakeError('TypeError', 'Could not convert to js type!')
|
||||
|
||||
|
||||
def is_js_type(e):
|
||||
if type(e) in PRIMITIVES:
|
||||
return True
|
||||
elif hasattr(e, 'Class') and hasattr(e, 'value'): # not perfect but works
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
# todo optimise these 2!
|
||||
def js_array_to_tuple(arr):
|
||||
length = to_uint32(arr.get(u'length'))
|
||||
return tuple(arr.get(unicode(e)) for e in xrange(length))
|
||||
|
||||
|
||||
def js_array_to_list(arr):
|
||||
length = to_uint32(arr.get(u'length'))
|
||||
return [arr.get(unicode(e)) for e in xrange(length)]
|
||||
|
||||
|
||||
def js_arr_length(arr):
|
||||
return to_uint32(arr.get(u'length'))
|
||||
@@ -1,805 +0,0 @@
|
||||
from .operations import *
|
||||
from .base import get_member, get_member_dot, PyJsFunction, Scope
|
||||
|
||||
|
||||
class OP_CODE(object):
|
||||
_params = []
|
||||
|
||||
# def eval(self, ctx):
|
||||
# raise
|
||||
|
||||
def __repr__(self):
|
||||
return self.__class__.__name__ + str(
|
||||
tuple([getattr(self, e) for e in self._params]))
|
||||
|
||||
|
||||
# --------------------- UNARY ----------------------
|
||||
|
||||
|
||||
class UNARY_OP(OP_CODE):
|
||||
_params = ['operator']
|
||||
|
||||
def __init__(self, operator):
|
||||
self.operator = operator
|
||||
|
||||
def eval(self, ctx):
|
||||
val = ctx.stack.pop()
|
||||
ctx.stack.append(UNARY_OPERATIONS[self.operator](val))
|
||||
|
||||
|
||||
# special unary operations
|
||||
|
||||
|
||||
class TYPEOF(OP_CODE):
|
||||
_params = ['identifier']
|
||||
|
||||
def __init__(self, identifier):
|
||||
self.identifier = identifier
|
||||
|
||||
def eval(self, ctx):
|
||||
# typeof something_undefined does not throw reference error
|
||||
val = ctx.get(self.identifier,
|
||||
False) # <= this makes it slightly different!
|
||||
ctx.stack.append(typeof_uop(val))
|
||||
|
||||
|
||||
class POSTFIX(OP_CODE):
|
||||
_params = ['cb', 'ca', 'identifier']
|
||||
|
||||
def __init__(self, post, incr, identifier):
|
||||
self.identifier = identifier
|
||||
self.cb = 1 if incr else -1
|
||||
self.ca = -self.cb if post else 0
|
||||
|
||||
def eval(self, ctx):
|
||||
target = to_number(ctx.get(self.identifier)) + self.cb
|
||||
ctx.put(self.identifier, target)
|
||||
ctx.stack.append(target + self.ca)
|
||||
|
||||
|
||||
class POSTFIX_MEMBER(OP_CODE):
|
||||
_params = ['cb', 'ca']
|
||||
|
||||
def __init__(self, post, incr):
|
||||
self.cb = 1 if incr else -1
|
||||
self.ca = -self.cb if post else 0
|
||||
|
||||
def eval(self, ctx):
|
||||
name = ctx.stack.pop()
|
||||
left = ctx.stack.pop()
|
||||
|
||||
target = to_number(get_member(left, name, ctx.space)) + self.cb
|
||||
if type(left) not in PRIMITIVES:
|
||||
left.put_member(name, target)
|
||||
|
||||
ctx.stack.append(target + self.ca)
|
||||
|
||||
|
||||
class POSTFIX_MEMBER_DOT(OP_CODE):
|
||||
_params = ['cb', 'ca', 'prop']
|
||||
|
||||
def __init__(self, post, incr, prop):
|
||||
self.cb = 1 if incr else -1
|
||||
self.ca = -self.cb if post else 0
|
||||
self.prop = prop
|
||||
|
||||
def eval(self, ctx):
|
||||
left = ctx.stack.pop()
|
||||
|
||||
target = to_number(get_member_dot(left, self.prop,
|
||||
ctx.space)) + self.cb
|
||||
if type(left) not in PRIMITIVES:
|
||||
left.put(self.prop, target)
|
||||
|
||||
ctx.stack.append(target + self.ca)
|
||||
|
||||
|
||||
class DELETE(OP_CODE):
|
||||
_params = ['name']
|
||||
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
|
||||
def eval(self, ctx):
|
||||
ctx.stack.append(ctx.delete(self.name))
|
||||
|
||||
|
||||
class DELETE_MEMBER(OP_CODE):
|
||||
def eval(self, ctx):
|
||||
prop = to_string(ctx.stack.pop())
|
||||
obj = to_object(ctx.stack.pop(), ctx)
|
||||
ctx.stack.append(obj.delete(prop, False))
|
||||
|
||||
|
||||
# --------------------- BITWISE ----------------------
|
||||
|
||||
|
||||
class BINARY_OP(OP_CODE):
|
||||
_params = ['operator']
|
||||
|
||||
def __init__(self, operator):
|
||||
self.operator = operator
|
||||
|
||||
def eval(self, ctx):
|
||||
right = ctx.stack.pop()
|
||||
left = ctx.stack.pop()
|
||||
ctx.stack.append(BINARY_OPERATIONS[self.operator](left, right))
|
||||
|
||||
|
||||
# &&, || and conditional are implemented in bytecode
|
||||
|
||||
# --------------------- JUMPS ----------------------
|
||||
|
||||
|
||||
# simple label that will be removed from code after compilation. labels ID will be translated
|
||||
# to source code position.
|
||||
class LABEL(OP_CODE):
|
||||
_params = ['num']
|
||||
|
||||
def __init__(self, num):
|
||||
self.num = num
|
||||
|
||||
|
||||
# I implemented interpreter in the way that when an integer is returned by eval operation the execution will jump
|
||||
# to the location of the label (it is loc = label_locations[label])
|
||||
|
||||
|
||||
class BASE_JUMP(OP_CODE):
|
||||
_params = ['label']
|
||||
|
||||
def __init__(self, label):
|
||||
self.label = label
|
||||
|
||||
|
||||
class JUMP(BASE_JUMP):
|
||||
def eval(self, ctx):
|
||||
return self.label
|
||||
|
||||
|
||||
class JUMP_IF_TRUE(BASE_JUMP):
|
||||
def eval(self, ctx):
|
||||
val = ctx.stack.pop()
|
||||
if to_boolean(val):
|
||||
return self.label
|
||||
|
||||
|
||||
class JUMP_IF_EQ(BASE_JUMP):
|
||||
# this one is used in switch statement - compares last 2 values using === operator and jumps popping both if true else pops last.
|
||||
def eval(self, ctx):
|
||||
cmp = ctx.stack.pop()
|
||||
if strict_equality_op(ctx.stack[-1], cmp):
|
||||
ctx.stack.pop()
|
||||
return self.label
|
||||
|
||||
|
||||
class JUMP_IF_TRUE_WITHOUT_POP(BASE_JUMP):
|
||||
def eval(self, ctx):
|
||||
val = ctx.stack[-1]
|
||||
if to_boolean(val):
|
||||
return self.label
|
||||
|
||||
|
||||
class JUMP_IF_FALSE(BASE_JUMP):
|
||||
def eval(self, ctx):
|
||||
val = ctx.stack.pop()
|
||||
if not to_boolean(val):
|
||||
return self.label
|
||||
|
||||
|
||||
class JUMP_IF_FALSE_WITHOUT_POP(BASE_JUMP):
|
||||
def eval(self, ctx):
|
||||
val = ctx.stack[-1]
|
||||
if not to_boolean(val):
|
||||
return self.label
|
||||
|
||||
|
||||
class POP(OP_CODE):
|
||||
def eval(self, ctx):
|
||||
# todo remove this check later
|
||||
assert len(ctx.stack), 'Popped from empty stack!'
|
||||
del ctx.stack[-1]
|
||||
|
||||
|
||||
# class REDUCE(OP_CODE):
|
||||
# def eval(self, ctx):
|
||||
# assert len(ctx.stack)==2
|
||||
# ctx.stack[0] = ctx.stack[1]
|
||||
# del ctx.stack[1]
|
||||
|
||||
# --------------- LOADING --------------
|
||||
|
||||
|
||||
class LOAD_NONE(OP_CODE): # be careful with this :)
|
||||
_params = []
|
||||
|
||||
def eval(self, ctx):
|
||||
ctx.stack.append(None)
|
||||
|
||||
|
||||
class LOAD_N_TUPLE(
|
||||
OP_CODE
|
||||
): # loads the tuple composed of n last elements on stack. elements are popped.
|
||||
_params = ['n']
|
||||
|
||||
def __init__(self, n):
|
||||
self.n = n
|
||||
|
||||
def eval(self, ctx):
|
||||
tup = tuple(ctx.stack[-self.n:])
|
||||
del ctx.stack[-self.n:]
|
||||
ctx.stack.append(tup)
|
||||
|
||||
|
||||
class LOAD_UNDEFINED(OP_CODE):
|
||||
def eval(self, ctx):
|
||||
ctx.stack.append(undefined)
|
||||
|
||||
|
||||
class LOAD_NULL(OP_CODE):
|
||||
def eval(self, ctx):
|
||||
ctx.stack.append(null)
|
||||
|
||||
|
||||
class LOAD_BOOLEAN(OP_CODE):
|
||||
_params = ['val']
|
||||
|
||||
def __init__(self, val):
|
||||
assert val in (0, 1)
|
||||
self.val = bool(val)
|
||||
|
||||
def eval(self, ctx):
|
||||
ctx.stack.append(self.val)
|
||||
|
||||
|
||||
class LOAD_STRING(OP_CODE):
|
||||
_params = ['val']
|
||||
|
||||
def __init__(self, val):
|
||||
assert isinstance(val, basestring)
|
||||
self.val = unicode(val)
|
||||
|
||||
def eval(self, ctx):
|
||||
ctx.stack.append(self.val)
|
||||
|
||||
|
||||
class LOAD_NUMBER(OP_CODE):
|
||||
_params = ['val']
|
||||
|
||||
def __init__(self, val):
|
||||
assert isinstance(val, (float, int, long))
|
||||
self.val = float(val)
|
||||
|
||||
def eval(self, ctx):
|
||||
ctx.stack.append(self.val)
|
||||
|
||||
|
||||
class LOAD_REGEXP(OP_CODE):
|
||||
_params = ['body', 'flags']
|
||||
|
||||
def __init__(self, body, flags):
|
||||
self.body = body
|
||||
self.flags = flags
|
||||
|
||||
def eval(self, ctx):
|
||||
# we have to generate a new regexp - they are mutable
|
||||
ctx.stack.append(ctx.space.NewRegExp(self.body, self.flags))
|
||||
|
||||
|
||||
class LOAD_FUNCTION(OP_CODE):
|
||||
_params = ['start', 'params', 'name', 'is_declaration', 'definitions']
|
||||
|
||||
def __init__(self, start, params, name, is_declaration, definitions):
|
||||
assert type(start) == int
|
||||
self.start = start # its an ID of label pointing to the beginning of the function bytecode
|
||||
self.params = params
|
||||
self.name = name
|
||||
self.is_declaration = bool(is_declaration)
|
||||
self.definitions = tuple(set(definitions + params))
|
||||
|
||||
def eval(self, ctx):
|
||||
ctx.stack.append(
|
||||
ctx.space.NewFunction(self.start, ctx, self.params, self.name,
|
||||
self.is_declaration, self.definitions))
|
||||
|
||||
|
||||
class LOAD_OBJECT(OP_CODE):
|
||||
_params = [
|
||||
'props'
|
||||
] # props are py string pairs (prop_name, kind): kind can be either i, g or s. (init, get, set)
|
||||
|
||||
def __init__(self, props):
|
||||
self.num = len(props)
|
||||
self.props = props
|
||||
|
||||
def eval(self, ctx):
|
||||
obj = ctx.space.NewObject()
|
||||
if self.num:
|
||||
obj._init(self.props, ctx.stack[-self.num:])
|
||||
del ctx.stack[-self.num:]
|
||||
|
||||
ctx.stack.append(obj)
|
||||
|
||||
|
||||
class LOAD_ARRAY(OP_CODE):
|
||||
_params = ['num']
|
||||
|
||||
def __init__(self, num):
|
||||
self.num = num
|
||||
|
||||
def eval(self, ctx):
|
||||
arr = ctx.space.NewArray(self.num)
|
||||
if self.num:
|
||||
arr._init(ctx.stack[-self.num:])
|
||||
del ctx.stack[-self.num:]
|
||||
ctx.stack.append(arr)
|
||||
|
||||
|
||||
class LOAD_THIS(OP_CODE):
|
||||
def eval(self, ctx):
|
||||
ctx.stack.append(ctx.THIS_BINDING)
|
||||
|
||||
|
||||
class LOAD(OP_CODE): # todo check!
|
||||
_params = ['identifier']
|
||||
|
||||
def __init__(self, identifier):
|
||||
self.identifier = identifier
|
||||
|
||||
# 11.1.2
|
||||
def eval(self, ctx):
|
||||
ctx.stack.append(ctx.get(self.identifier, throw=True))
|
||||
|
||||
|
||||
class LOAD_MEMBER(OP_CODE):
|
||||
def eval(self, ctx):
|
||||
prop = ctx.stack.pop()
|
||||
obj = ctx.stack.pop()
|
||||
ctx.stack.append(get_member(obj, prop, ctx.space))
|
||||
|
||||
|
||||
class LOAD_MEMBER_DOT(OP_CODE):
|
||||
_params = ['prop']
|
||||
|
||||
def __init__(self, prop):
|
||||
self.prop = prop
|
||||
|
||||
def eval(self, ctx):
|
||||
obj = ctx.stack.pop()
|
||||
ctx.stack.append(get_member_dot(obj, self.prop, ctx.space))
|
||||
|
||||
|
||||
# --------------- STORING --------------
|
||||
|
||||
|
||||
class STORE(OP_CODE):
|
||||
_params = ['identifier']
|
||||
|
||||
def __init__(self, identifier):
|
||||
self.identifier = identifier
|
||||
|
||||
def eval(self, ctx):
|
||||
value = ctx.stack[-1] # don't pop
|
||||
ctx.put(self.identifier, value)
|
||||
|
||||
|
||||
class STORE_MEMBER(OP_CODE):
|
||||
def eval(self, ctx):
|
||||
value = ctx.stack.pop()
|
||||
name = ctx.stack.pop()
|
||||
left = ctx.stack.pop()
|
||||
|
||||
typ = type(left)
|
||||
if typ in PRIMITIVES:
|
||||
prop = to_string(name)
|
||||
if typ == NULL_TYPE:
|
||||
raise MakeError('TypeError',
|
||||
"Cannot set property '%s' of null" % prop)
|
||||
elif typ == UNDEFINED_TYPE:
|
||||
raise MakeError('TypeError',
|
||||
"Cannot set property '%s' of undefined" % prop)
|
||||
# just ignore...
|
||||
else:
|
||||
left.put_member(name, value)
|
||||
|
||||
ctx.stack.append(value)
|
||||
|
||||
|
||||
class STORE_MEMBER_DOT(OP_CODE):
|
||||
_params = ['prop']
|
||||
|
||||
def __init__(self, prop):
|
||||
self.prop = prop
|
||||
|
||||
def eval(self, ctx):
|
||||
value = ctx.stack.pop()
|
||||
left = ctx.stack.pop()
|
||||
|
||||
typ = type(left)
|
||||
if typ in PRIMITIVES:
|
||||
if typ == NULL_TYPE:
|
||||
raise MakeError('TypeError',
|
||||
"Cannot set property '%s' of null" % self.prop)
|
||||
elif typ == UNDEFINED_TYPE:
|
||||
raise MakeError(
|
||||
'TypeError',
|
||||
"Cannot set property '%s' of undefined" % self.prop)
|
||||
# just ignore...
|
||||
else:
|
||||
left.put(self.prop, value)
|
||||
ctx.stack.append(value)
|
||||
|
||||
|
||||
class STORE_OP(OP_CODE):
|
||||
_params = ['identifier', 'op']
|
||||
|
||||
def __init__(self, identifier, op):
|
||||
self.identifier = identifier
|
||||
self.op = op
|
||||
|
||||
def eval(self, ctx):
|
||||
value = ctx.stack.pop()
|
||||
new_value = BINARY_OPERATIONS[self.op](ctx.get(self.identifier), value)
|
||||
ctx.put(self.identifier, new_value)
|
||||
ctx.stack.append(new_value)
|
||||
|
||||
|
||||
class STORE_MEMBER_OP(OP_CODE):
|
||||
_params = ['op']
|
||||
|
||||
def __init__(self, op):
|
||||
self.op = op
|
||||
|
||||
def eval(self, ctx):
|
||||
value = ctx.stack.pop()
|
||||
name = ctx.stack.pop()
|
||||
left = ctx.stack.pop()
|
||||
|
||||
typ = type(left)
|
||||
if typ in PRIMITIVES:
|
||||
if typ is NULL_TYPE:
|
||||
raise MakeError(
|
||||
'TypeError',
|
||||
"Cannot set property '%s' of null" % to_string(name))
|
||||
elif typ is UNDEFINED_TYPE:
|
||||
raise MakeError(
|
||||
'TypeError',
|
||||
"Cannot set property '%s' of undefined" % to_string(name))
|
||||
ctx.stack.append(BINARY_OPERATIONS[self.op](get_member(
|
||||
left, name, ctx.space), value))
|
||||
return
|
||||
else:
|
||||
ctx.stack.append(BINARY_OPERATIONS[self.op](get_member(
|
||||
left, name, ctx.space), value))
|
||||
left.put_member(name, ctx.stack[-1])
|
||||
|
||||
|
||||
class STORE_MEMBER_DOT_OP(OP_CODE):
|
||||
_params = ['prop', 'op']
|
||||
|
||||
def __init__(self, prop, op):
|
||||
self.prop = prop
|
||||
self.op = op
|
||||
|
||||
def eval(self, ctx):
|
||||
value = ctx.stack.pop()
|
||||
left = ctx.stack.pop()
|
||||
|
||||
typ = type(left)
|
||||
if typ in PRIMITIVES:
|
||||
if typ == NULL_TYPE:
|
||||
raise MakeError('TypeError',
|
||||
"Cannot set property '%s' of null" % self.prop)
|
||||
elif typ == UNDEFINED_TYPE:
|
||||
raise MakeError(
|
||||
'TypeError',
|
||||
"Cannot set property '%s' of undefined" % self.prop)
|
||||
ctx.stack.append(BINARY_OPERATIONS[self.op](get_member_dot(
|
||||
left, self.prop, ctx.space), value))
|
||||
return
|
||||
else:
|
||||
ctx.stack.append(BINARY_OPERATIONS[self.op](get_member_dot(
|
||||
left, self.prop, ctx.space), value))
|
||||
left.put(self.prop, ctx.stack[-1])
|
||||
|
||||
|
||||
# --------------- CALLS --------------
|
||||
|
||||
|
||||
def bytecode_call(ctx, func, this, args):
|
||||
if type(func) is not PyJsFunction:
|
||||
raise MakeError('TypeError', "%s is not a function" % Type(func))
|
||||
if func.is_native: # call to built-in function or method
|
||||
ctx.stack.append(func.call(this, args))
|
||||
return None
|
||||
|
||||
# therefore not native. we have to return (new_context, function_label) to instruct interpreter to call
|
||||
return func._generate_my_context(this, args), func.code
|
||||
|
||||
|
||||
class CALL(OP_CODE):
|
||||
def eval(self, ctx):
|
||||
args = ctx.stack.pop()
|
||||
func = ctx.stack.pop()
|
||||
|
||||
return bytecode_call(ctx, func, ctx.space.GlobalObj, args)
|
||||
|
||||
|
||||
class CALL_METHOD(OP_CODE):
|
||||
def eval(self, ctx):
|
||||
args = ctx.stack.pop()
|
||||
prop = ctx.stack.pop()
|
||||
base = ctx.stack.pop()
|
||||
|
||||
func = get_member(base, prop, ctx.space)
|
||||
|
||||
return bytecode_call(ctx, func, base, args)
|
||||
|
||||
|
||||
class CALL_METHOD_DOT(OP_CODE):
|
||||
_params = ['prop']
|
||||
|
||||
def __init__(self, prop):
|
||||
self.prop = prop
|
||||
|
||||
def eval(self, ctx):
|
||||
args = ctx.stack.pop()
|
||||
base = ctx.stack.pop()
|
||||
|
||||
func = get_member_dot(base, self.prop, ctx.space)
|
||||
|
||||
return bytecode_call(ctx, func, base, args)
|
||||
|
||||
|
||||
class CALL_NO_ARGS(OP_CODE):
|
||||
def eval(self, ctx):
|
||||
func = ctx.stack.pop()
|
||||
|
||||
return bytecode_call(ctx, func, ctx.space.GlobalObj, ())
|
||||
|
||||
|
||||
class CALL_METHOD_NO_ARGS(OP_CODE):
|
||||
def eval(self, ctx):
|
||||
prop = ctx.stack.pop()
|
||||
base = ctx.stack.pop()
|
||||
|
||||
func = get_member(base, prop, ctx.space)
|
||||
|
||||
return bytecode_call(ctx, func, base, ())
|
||||
|
||||
|
||||
class CALL_METHOD_DOT_NO_ARGS(OP_CODE):
|
||||
_params = ['prop']
|
||||
|
||||
def __init__(self, prop):
|
||||
self.prop = prop
|
||||
|
||||
def eval(self, ctx):
|
||||
base = ctx.stack.pop()
|
||||
|
||||
func = get_member_dot(base, self.prop, ctx.space)
|
||||
|
||||
return bytecode_call(ctx, func, base, ())
|
||||
|
||||
|
||||
class NOP(OP_CODE):
|
||||
def eval(self, ctx):
|
||||
pass
|
||||
|
||||
|
||||
class RETURN(OP_CODE):
|
||||
def eval(
|
||||
self, ctx
|
||||
): # remember to load the return value on stack before using RETURN op.
|
||||
return (None, None)
|
||||
|
||||
|
||||
class NEW(OP_CODE):
|
||||
def eval(self, ctx):
|
||||
args = ctx.stack.pop()
|
||||
constructor = ctx.stack.pop()
|
||||
if type(constructor) in PRIMITIVES or not hasattr(
|
||||
constructor, 'create'):
|
||||
raise MakeError('TypeError',
|
||||
'%s is not a constructor' % Type(constructor))
|
||||
ctx.stack.append(constructor.create(args, space=ctx.space))
|
||||
|
||||
|
||||
class NEW_NO_ARGS(OP_CODE):
|
||||
def eval(self, ctx):
|
||||
constructor = ctx.stack.pop()
|
||||
if type(constructor) in PRIMITIVES or not hasattr(
|
||||
constructor, 'create'):
|
||||
raise MakeError('TypeError',
|
||||
'%s is not a constructor' % Type(constructor))
|
||||
ctx.stack.append(constructor.create((), space=ctx.space))
|
||||
|
||||
|
||||
# --------------- EXCEPTIONS --------------
|
||||
|
||||
|
||||
class THROW(OP_CODE):
|
||||
def eval(self, ctx):
|
||||
raise MakeError(None, None, ctx.stack.pop())
|
||||
|
||||
|
||||
class TRY_CATCH_FINALLY(OP_CODE):
|
||||
_params = [
|
||||
'try_label', 'catch_label', 'catch_variable', 'finally_label',
|
||||
'finally_present', 'end_label'
|
||||
]
|
||||
|
||||
def __init__(self, try_label, catch_label, catch_variable, finally_label,
|
||||
finally_present, end_label):
|
||||
self.try_label = try_label
|
||||
self.catch_label = catch_label
|
||||
self.catch_variable = catch_variable
|
||||
self.finally_label = finally_label
|
||||
self.finally_present = finally_present
|
||||
self.end_label = end_label
|
||||
|
||||
def eval(self, ctx):
|
||||
# 4 different exectution results
|
||||
# 0=normal, 1=return, 2=jump_outside, 3=errors
|
||||
# execute_fragment_under_context returns:
|
||||
# (return_value, typ, jump_loc/error)
|
||||
|
||||
ctx.stack.pop()
|
||||
|
||||
# execute try statement
|
||||
try_status = ctx.space.exe.execute_fragment_under_context(
|
||||
ctx, self.try_label, self.catch_label)
|
||||
|
||||
errors = try_status[1] == 3
|
||||
|
||||
# catch
|
||||
if errors and self.catch_variable is not None:
|
||||
# generate catch block context...
|
||||
catch_context = Scope({
|
||||
self.catch_variable:
|
||||
try_status[2].get_thrown_value(ctx.space)
|
||||
}, ctx.space, ctx)
|
||||
catch_context.THIS_BINDING = ctx.THIS_BINDING
|
||||
catch_status = ctx.space.exe.execute_fragment_under_context(
|
||||
catch_context, self.catch_label, self.finally_label)
|
||||
else:
|
||||
catch_status = None
|
||||
|
||||
# finally
|
||||
if self.finally_present:
|
||||
finally_status = ctx.space.exe.execute_fragment_under_context(
|
||||
ctx, self.finally_label, self.end_label)
|
||||
else:
|
||||
finally_status = None
|
||||
|
||||
# now return controls
|
||||
other_status = catch_status or try_status
|
||||
if finally_status is None or (finally_status[1] == 0
|
||||
and other_status[1] != 0):
|
||||
winning_status = other_status
|
||||
else:
|
||||
winning_status = finally_status
|
||||
|
||||
val, typ, spec = winning_status
|
||||
if typ == 0: # normal
|
||||
ctx.stack.append(val)
|
||||
return
|
||||
elif typ == 1: # return
|
||||
ctx.stack.append(spec)
|
||||
return None, None # send return signal
|
||||
elif typ == 2: # jump outside
|
||||
ctx.stack.append(val)
|
||||
return spec
|
||||
elif typ == 3:
|
||||
# throw is made with empty stack as usual
|
||||
raise spec
|
||||
else:
|
||||
raise RuntimeError('Invalid return code')
|
||||
|
||||
|
||||
# ------------ WITH + ITERATORS ----------
|
||||
|
||||
|
||||
class WITH(OP_CODE):
|
||||
_params = ['beg_label', 'end_label']
|
||||
|
||||
def __init__(self, beg_label, end_label):
|
||||
self.beg_label = beg_label
|
||||
self.end_label = end_label
|
||||
|
||||
def eval(self, ctx):
|
||||
obj = to_object(ctx.stack.pop(), ctx.space)
|
||||
|
||||
with_context = Scope(
|
||||
obj, ctx.space, ctx) # todo actually use the obj to modify the ctx
|
||||
with_context.THIS_BINDING = ctx.THIS_BINDING
|
||||
status = ctx.space.exe.execute_fragment_under_context(
|
||||
with_context, self.beg_label, self.end_label)
|
||||
|
||||
val, typ, spec = status
|
||||
|
||||
if typ != 3: # exception
|
||||
ctx.stack.pop()
|
||||
|
||||
if typ == 0: # normal
|
||||
ctx.stack.append(val)
|
||||
return
|
||||
elif typ == 1: # return
|
||||
ctx.stack.append(spec)
|
||||
return None, None # send return signal
|
||||
elif typ == 2: # jump outside
|
||||
ctx.stack.append(val)
|
||||
return spec
|
||||
elif typ == 3: # exception
|
||||
# throw is made with empty stack as usual
|
||||
raise spec
|
||||
else:
|
||||
raise RuntimeError('Invalid return code')
|
||||
|
||||
|
||||
class FOR_IN(OP_CODE):
|
||||
_params = ['name', 'body_start_label', 'continue_label', 'break_label']
|
||||
|
||||
def __init__(self, name, body_start_label, continue_label, break_label):
|
||||
self.name = name
|
||||
self.body_start_label = body_start_label
|
||||
self.continue_label = continue_label
|
||||
self.break_label = break_label
|
||||
|
||||
def eval(self, ctx):
|
||||
iterable = ctx.stack.pop()
|
||||
if is_null(iterable) or is_undefined(iterable):
|
||||
ctx.stack.pop()
|
||||
ctx.stack.append(undefined)
|
||||
return self.break_label
|
||||
|
||||
obj = to_object(iterable, ctx.space)
|
||||
|
||||
for e in sorted(obj.own):
|
||||
if not obj.own[e]['enumerable']:
|
||||
continue
|
||||
|
||||
ctx.put(
|
||||
self.name, e
|
||||
) # JS would have been so much nicer if this was ctx.space.put(self.name, obj.get(e))
|
||||
|
||||
# evaluate the body
|
||||
status = ctx.space.exe.execute_fragment_under_context(
|
||||
ctx, self.body_start_label, self.break_label)
|
||||
|
||||
val, typ, spec = status
|
||||
|
||||
if typ != 3: # exception
|
||||
ctx.stack.pop()
|
||||
|
||||
if typ == 0: # normal
|
||||
ctx.stack.append(val)
|
||||
continue
|
||||
elif typ == 1: # return
|
||||
ctx.stack.append(spec)
|
||||
return None, None # send return signal
|
||||
elif typ == 2: # jump outside
|
||||
# now have to figure out whether this is a continue or something else...
|
||||
ctx.stack.append(val)
|
||||
if spec == self.continue_label:
|
||||
# just a continue, perform next iteration as normal
|
||||
continue
|
||||
return spec # break or smth, go there and finish the iteration
|
||||
elif typ == 3: # exception
|
||||
# throw is made with empty stack as usual
|
||||
raise spec
|
||||
else:
|
||||
raise RuntimeError('Invalid return code')
|
||||
|
||||
return self.break_label
|
||||
|
||||
|
||||
# all opcodes...
|
||||
OP_CODES = {}
|
||||
g = ''
|
||||
for g in globals():
|
||||
try:
|
||||
if not issubclass(globals()[g], OP_CODE) or g is 'OP_CODE':
|
||||
continue
|
||||
except:
|
||||
continue
|
||||
OP_CODES[g] = globals()[g]
|
||||
@@ -1,314 +0,0 @@
|
||||
from __future__ import unicode_literals
|
||||
from .simplex import *
|
||||
from .conversions import *
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Unary operations
|
||||
|
||||
|
||||
# -x
|
||||
def minus_uop(self):
|
||||
return -to_number(self)
|
||||
|
||||
|
||||
# +x
|
||||
def plus_uop(self): # +u
|
||||
return to_number(self)
|
||||
|
||||
|
||||
# !x
|
||||
def logical_negation_uop(self): # !u cant do 'not u' :(
|
||||
return not to_boolean(self)
|
||||
|
||||
|
||||
# typeof x
|
||||
def typeof_uop(self):
|
||||
if is_callable(self):
|
||||
return u'function'
|
||||
typ = Type(self).lower()
|
||||
if typ == u'null':
|
||||
typ = u'object' # absolutely idiotic...
|
||||
return typ
|
||||
|
||||
|
||||
# ~u
|
||||
def bit_invert_uop(self):
|
||||
return float(to_int32(float(~to_int32(self))))
|
||||
|
||||
|
||||
# void
|
||||
def void_op(self):
|
||||
return undefined
|
||||
|
||||
|
||||
UNARY_OPERATIONS = {
|
||||
'+': plus_uop,
|
||||
'-': minus_uop,
|
||||
'!': logical_negation_uop,
|
||||
'~': bit_invert_uop,
|
||||
'void': void_op,
|
||||
'typeof':
|
||||
typeof_uop, # this one only for member expressions! for identifiers its slightly different...
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# ----- binary ops -------
|
||||
|
||||
# Bitwise operators
|
||||
# <<, >>, &, ^, |, ~
|
||||
|
||||
|
||||
# <<
|
||||
def bit_lshift_op(self, other):
|
||||
lnum = to_int32(self)
|
||||
rnum = to_uint32(other)
|
||||
shiftCount = rnum & 0x1F
|
||||
return float(to_int32(float(lnum << shiftCount)))
|
||||
|
||||
|
||||
# >>
|
||||
def bit_rshift_op(self, other):
|
||||
lnum = to_int32(self)
|
||||
rnum = to_uint32(other)
|
||||
shiftCount = rnum & 0x1F
|
||||
return float(to_int32(float(lnum >> shiftCount)))
|
||||
|
||||
|
||||
# >>>
|
||||
def bit_bshift_op(self, other):
|
||||
lnum = to_uint32(self)
|
||||
rnum = to_uint32(other)
|
||||
shiftCount = rnum & 0x1F
|
||||
return float(to_uint32(float(lnum >> shiftCount)))
|
||||
|
||||
|
||||
# &
|
||||
def bit_and_op(self, other):
|
||||
lnum = to_int32(self)
|
||||
rnum = to_int32(other)
|
||||
return float(to_int32(float(lnum & rnum)))
|
||||
|
||||
|
||||
# ^
|
||||
def bit_xor_op(self, other):
|
||||
lnum = to_int32(self)
|
||||
rnum = to_int32(other)
|
||||
return float(to_int32(float(lnum ^ rnum)))
|
||||
|
||||
|
||||
# |
|
||||
def bit_or_op(self, other):
|
||||
lnum = to_int32(self)
|
||||
rnum = to_int32(other)
|
||||
return float(to_int32(float(lnum | rnum)))
|
||||
|
||||
|
||||
# Additive operators
|
||||
# + and - are implemented here
|
||||
|
||||
|
||||
# +
|
||||
def add_op(self, other):
|
||||
if type(self) is float and type(other) is float:
|
||||
return self + other
|
||||
if type(self) is unicode and type(other) is unicode:
|
||||
return self + other
|
||||
# standard way...
|
||||
a = to_primitive(self)
|
||||
b = to_primitive(other)
|
||||
if type(a) is unicode or type(b) is unicode: # string wins hehe
|
||||
return to_string(a) + to_string(b)
|
||||
return to_number(a) + to_number(b)
|
||||
|
||||
|
||||
# -
|
||||
def sub_op(self, other):
|
||||
return to_number(self) - to_number(other)
|
||||
|
||||
|
||||
# Multiplicative operators
|
||||
# *, / and % are implemented here
|
||||
|
||||
|
||||
# *
|
||||
def mul_op(self, other):
|
||||
return to_number(self) * to_number(other)
|
||||
|
||||
|
||||
# /
|
||||
def div_op(self, other):
|
||||
a = to_number(self)
|
||||
b = to_number(other)
|
||||
if b:
|
||||
return a / float(b) # ensure at least one is a float.
|
||||
if not a or a != a:
|
||||
return NaN
|
||||
return Infinity if a > 0 else -Infinity
|
||||
|
||||
|
||||
# %
|
||||
def mod_op(self, other):
|
||||
a = to_number(self)
|
||||
b = to_number(other)
|
||||
if abs(a) == Infinity or not b:
|
||||
return NaN
|
||||
if abs(b) == Infinity:
|
||||
return a
|
||||
pyres = a % b # different signs in python and javascript
|
||||
# python has the same sign as b and js has the same
|
||||
# sign as a.
|
||||
if a < 0 and pyres > 0:
|
||||
pyres -= abs(b)
|
||||
elif a > 0 and pyres < 0:
|
||||
pyres += abs(b)
|
||||
return float(pyres)
|
||||
|
||||
|
||||
# Comparisons
|
||||
# <, <=, !=, ==, >=, > are implemented here.
|
||||
def abstract_relational_comparison(self, other,
|
||||
self_first=True): # todo speed up!
|
||||
''' self<other if self_first else other<self.
|
||||
Returns the result of the question: is self smaller than other?
|
||||
in case self_first is false it returns the answer of:
|
||||
is other smaller than self.
|
||||
result is PyJs type: bool or undefined'''
|
||||
|
||||
px = to_primitive(self, 'Number')
|
||||
py = to_primitive(other, 'Number')
|
||||
if not self_first: # reverse order
|
||||
px, py = py, px
|
||||
if not (Type(px) == 'String' and Type(py) == 'String'):
|
||||
px, py = to_number(px), to_number(py)
|
||||
if is_nan(px) or is_nan(py):
|
||||
return None # watch out here!
|
||||
return px < py # same cmp algorithm
|
||||
else:
|
||||
# I am pretty sure that python has the same
|
||||
# string cmp algorithm but I have to confirm it
|
||||
return px < py
|
||||
|
||||
|
||||
# <
|
||||
def less_op(self, other):
|
||||
res = abstract_relational_comparison(self, other, True)
|
||||
if res is None:
|
||||
return False
|
||||
return res
|
||||
|
||||
|
||||
# <=
|
||||
def less_eq_op(self, other):
|
||||
res = abstract_relational_comparison(self, other, False)
|
||||
if res is None:
|
||||
return False
|
||||
return not res
|
||||
|
||||
|
||||
# >=
|
||||
def greater_eq_op(self, other):
|
||||
res = abstract_relational_comparison(self, other, True)
|
||||
if res is None:
|
||||
return False
|
||||
return not res
|
||||
|
||||
|
||||
# >
|
||||
def greater_op(self, other):
|
||||
res = abstract_relational_comparison(self, other, False)
|
||||
if res is None:
|
||||
return False
|
||||
return res
|
||||
|
||||
|
||||
# equality
|
||||
|
||||
|
||||
def abstract_equality_op(self, other):
|
||||
''' returns the result of JS == compare.
|
||||
result is PyJs type: bool'''
|
||||
tx, ty = Type(self), Type(other)
|
||||
if tx == ty:
|
||||
if tx == 'Undefined' or tx == 'Null':
|
||||
return True
|
||||
if tx == 'Number' or tx == 'String' or tx == 'Boolean':
|
||||
return self == other
|
||||
return self is other # Object
|
||||
elif (tx == 'Undefined' and ty == 'Null') or (ty == 'Undefined'
|
||||
and tx == 'Null'):
|
||||
return True
|
||||
elif tx == 'Number' and ty == 'String':
|
||||
return abstract_equality_op(self, to_number(other))
|
||||
elif tx == 'String' and ty == 'Number':
|
||||
return abstract_equality_op(to_number(self), other)
|
||||
elif tx == 'Boolean':
|
||||
return abstract_equality_op(to_number(self), other)
|
||||
elif ty == 'Boolean':
|
||||
return abstract_equality_op(self, to_number(other))
|
||||
elif (tx == 'String' or tx == 'Number') and is_object(other):
|
||||
return abstract_equality_op(self, to_primitive(other))
|
||||
elif (ty == 'String' or ty == 'Number') and is_object(self):
|
||||
return abstract_equality_op(to_primitive(self), other)
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def abstract_inequality_op(self, other):
|
||||
return not abstract_equality_op(self, other)
|
||||
|
||||
|
||||
def strict_equality_op(self, other):
|
||||
typ = Type(self)
|
||||
if typ != Type(other):
|
||||
return False
|
||||
if typ == 'Undefined' or typ == 'Null':
|
||||
return True
|
||||
if typ == 'Boolean' or typ == 'String' or typ == 'Number':
|
||||
return self == other
|
||||
else: # object
|
||||
return self is other # Id compare.
|
||||
|
||||
|
||||
def strict_inequality_op(self, other):
|
||||
return not strict_equality_op(self, other)
|
||||
|
||||
|
||||
def instanceof_op(self, other):
|
||||
'''checks if self is instance of other'''
|
||||
if not hasattr(other, 'has_instance'):
|
||||
return False
|
||||
return other.has_instance(self)
|
||||
|
||||
|
||||
def in_op(self, other):
|
||||
'''checks if self is in other'''
|
||||
if not is_object(other):
|
||||
raise MakeError(
|
||||
'TypeError',
|
||||
"You can\'t use 'in' operator to search in non-objects")
|
||||
return other.has_property(to_string(self))
|
||||
|
||||
|
||||
BINARY_OPERATIONS = {
|
||||
'+': add_op,
|
||||
'-': sub_op,
|
||||
'*': mul_op,
|
||||
'/': div_op,
|
||||
'%': mod_op,
|
||||
'<<': bit_lshift_op,
|
||||
'>>': bit_rshift_op,
|
||||
'>>>': bit_bshift_op,
|
||||
'|': bit_or_op,
|
||||
'&': bit_and_op,
|
||||
'^': bit_xor_op,
|
||||
'==': abstract_equality_op,
|
||||
'!=': abstract_inequality_op,
|
||||
'===': strict_equality_op,
|
||||
'!==': strict_inequality_op,
|
||||
'<': less_op,
|
||||
'<=': less_eq_op,
|
||||
'>': greater_op,
|
||||
'>=': greater_eq_op,
|
||||
'in': in_op,
|
||||
'instanceof': instanceof_op,
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
__author__ = 'Piotr Dabkowski'
|
||||
@@ -1,489 +0,0 @@
|
||||
from __future__ import unicode_literals
|
||||
from ..conversions import *
|
||||
from ..func_utils import *
|
||||
from ..operations import strict_equality_op
|
||||
|
||||
import six
|
||||
|
||||
if six.PY3:
|
||||
xrange = range
|
||||
import functools
|
||||
|
||||
ARR_STACK = set({})
|
||||
|
||||
|
||||
class ArrayPrototype:
|
||||
def toString(this, args):
|
||||
arr = to_object(this, args.space)
|
||||
func = arr.get('join')
|
||||
if not is_callable(func):
|
||||
return u'[object %s]' % GetClass(arr)
|
||||
return func.call(this, ())
|
||||
|
||||
def toLocaleString(this, args):
|
||||
array = to_object(this, args.space)
|
||||
arr_len = js_arr_length(array)
|
||||
# separator is simply a comma ','
|
||||
if not arr_len:
|
||||
return ''
|
||||
res = []
|
||||
for i in xrange(arr_len):
|
||||
element = array.get(unicode(i))
|
||||
if is_undefined(element) or is_null(element):
|
||||
res.append('')
|
||||
else:
|
||||
cand = to_object(element, args.space)
|
||||
str_func = cand.get('toLocaleString')
|
||||
if not is_callable(str_func):
|
||||
raise MakeError(
|
||||
'TypeError',
|
||||
'toLocaleString method of item at index %d is not callable'
|
||||
% i)
|
||||
res.append(to_string(str_func.call(cand, ())))
|
||||
return ','.join(res)
|
||||
|
||||
def concat(this, args):
|
||||
array = to_object(this, args.space)
|
||||
items = [array]
|
||||
items.extend(tuple(args))
|
||||
A = []
|
||||
for E in items:
|
||||
if GetClass(E) == 'Array':
|
||||
k = 0
|
||||
e_len = js_arr_length(E)
|
||||
while k < e_len:
|
||||
if E.has_property(unicode(k)):
|
||||
A.append(E.get(unicode(k)))
|
||||
k += 1
|
||||
else:
|
||||
A.append(E)
|
||||
return args.space.ConstructArray(A)
|
||||
|
||||
def join(this, args):
|
||||
ARR_STACK.add(this)
|
||||
array = to_object(this, args.space)
|
||||
separator = get_arg(args, 0)
|
||||
arr_len = js_arr_length(array)
|
||||
separator = ',' if is_undefined(separator) else to_string(separator)
|
||||
elems = []
|
||||
for e in xrange(arr_len):
|
||||
elem = array.get(unicode(e))
|
||||
if elem in ARR_STACK:
|
||||
s = ''
|
||||
else:
|
||||
s = to_string(elem)
|
||||
elems.append(
|
||||
s if not (is_undefined(elem) or is_null(elem)) else '')
|
||||
res = separator.join(elems)
|
||||
ARR_STACK.remove(this)
|
||||
return res
|
||||
|
||||
def pop(this, args): #todo check
|
||||
array = to_object(this, args.space)
|
||||
arr_len = js_arr_length(array)
|
||||
if not arr_len:
|
||||
array.put('length', float(arr_len))
|
||||
return undefined
|
||||
ind = unicode(arr_len - 1)
|
||||
element = array.get(ind)
|
||||
array.delete(ind)
|
||||
array.put('length', float(arr_len - 1))
|
||||
return element
|
||||
|
||||
def push(this, args):
|
||||
array = to_object(this, args.space)
|
||||
arr_len = js_arr_length(array)
|
||||
to_put = tuple(args)
|
||||
i = arr_len
|
||||
for i, e in enumerate(to_put, arr_len):
|
||||
array.put(unicode(i), e, True)
|
||||
array.put('length', float(arr_len + len(to_put)), True)
|
||||
return float(i)
|
||||
|
||||
def reverse(this, args):
|
||||
array = to_object(this, args.space)
|
||||
vals = js_array_to_list(array)
|
||||
has_props = [
|
||||
array.has_property(unicode(e))
|
||||
for e in xrange(js_arr_length(array))
|
||||
]
|
||||
vals.reverse()
|
||||
has_props.reverse()
|
||||
for i, val in enumerate(vals):
|
||||
if has_props[i]:
|
||||
array.put(unicode(i), val)
|
||||
else:
|
||||
array.delete(unicode(i))
|
||||
return array
|
||||
|
||||
def shift(this, args):
|
||||
array = to_object(this, args.space)
|
||||
arr_len = js_arr_length(array)
|
||||
if not arr_len:
|
||||
array.put('length', 0.)
|
||||
return undefined
|
||||
first = array.get('0')
|
||||
for k in xrange(1, arr_len):
|
||||
from_s, to_s = unicode(k), unicode(k - 1)
|
||||
if array.has_property(from_s):
|
||||
array.put(to_s, array.get(from_s))
|
||||
else:
|
||||
array.delete(to_s)
|
||||
array.delete(unicode(arr_len - 1))
|
||||
array.put('length', float(arr_len - 1))
|
||||
return first
|
||||
|
||||
def slice(this, args): # todo check
|
||||
array = to_object(this, args.space)
|
||||
start = get_arg(args, 0)
|
||||
end = get_arg(args, 1)
|
||||
arr_len = js_arr_length(array)
|
||||
relative_start = to_int(start)
|
||||
k = max((arr_len + relative_start), 0) if relative_start < 0 else min(
|
||||
relative_start, arr_len)
|
||||
relative_end = arr_len if is_undefined(end) else to_int(end)
|
||||
final = max((arr_len + relative_end), 0) if relative_end < 0 else min(
|
||||
relative_end, arr_len)
|
||||
res = []
|
||||
n = 0
|
||||
while k < final:
|
||||
pk = unicode(k)
|
||||
if array.has_property(pk):
|
||||
res.append(array.get(pk))
|
||||
k += 1
|
||||
n += 1
|
||||
return args.space.ConstructArray(res)
|
||||
|
||||
def sort(
|
||||
this, args
|
||||
): # todo: this assumes array continous (not sparse) - fix for sparse arrays
|
||||
cmpfn = get_arg(args, 0)
|
||||
if not GetClass(this) in ('Array', 'Arguments'):
|
||||
return to_object(this, args.space) # do nothing
|
||||
arr_len = js_arr_length(this)
|
||||
if not arr_len:
|
||||
return this
|
||||
arr = [
|
||||
(this.get(unicode(e)) if this.has_property(unicode(e)) else None)
|
||||
for e in xrange(arr_len)
|
||||
]
|
||||
if not is_callable(cmpfn):
|
||||
cmpfn = None
|
||||
cmp = lambda a, b: sort_compare(a, b, cmpfn)
|
||||
if six.PY3:
|
||||
key = functools.cmp_to_key(cmp)
|
||||
arr.sort(key=key)
|
||||
else:
|
||||
arr.sort(cmp=cmp)
|
||||
for i in xrange(arr_len):
|
||||
if arr[i] is None:
|
||||
this.delete(unicode(i))
|
||||
else:
|
||||
this.put(unicode(i), arr[i])
|
||||
return this
|
||||
|
||||
def splice(this, args):
|
||||
# 1-8
|
||||
array = to_object(this, args.space)
|
||||
start = get_arg(args, 0)
|
||||
deleteCount = get_arg(args, 1)
|
||||
arr_len = js_arr_length(this)
|
||||
relative_start = to_int(start)
|
||||
actual_start = max(
|
||||
(arr_len + relative_start), 0) if relative_start < 0 else min(
|
||||
relative_start, arr_len)
|
||||
actual_delete_count = min(
|
||||
max(to_int(deleteCount), 0), arr_len - actual_start)
|
||||
k = 0
|
||||
A = args.space.NewArray(0)
|
||||
# 9
|
||||
while k < actual_delete_count:
|
||||
if array.has_property(unicode(actual_start + k)):
|
||||
A.put(unicode(k), array.get(unicode(actual_start + k)))
|
||||
k += 1
|
||||
# 10-11
|
||||
items = list(args)[2:]
|
||||
items_len = len(items)
|
||||
# 12
|
||||
if items_len < actual_delete_count:
|
||||
k = actual_start
|
||||
while k < (arr_len - actual_delete_count):
|
||||
fr = unicode(k + actual_delete_count)
|
||||
to = unicode(k + items_len)
|
||||
if array.has_property(fr):
|
||||
array.put(to, array.get(fr))
|
||||
else:
|
||||
array.delete(to)
|
||||
k += 1
|
||||
k = arr_len
|
||||
while k > (arr_len - actual_delete_count + items_len):
|
||||
array.delete(unicode(k - 1))
|
||||
k -= 1
|
||||
# 13
|
||||
elif items_len > actual_delete_count:
|
||||
k = arr_len - actual_delete_count
|
||||
while k > actual_start:
|
||||
fr = unicode(k + actual_delete_count - 1)
|
||||
to = unicode(k + items_len - 1)
|
||||
if array.has_property(fr):
|
||||
array.put(to, array.get(fr))
|
||||
else:
|
||||
array.delete(to)
|
||||
k -= 1
|
||||
# 14-17
|
||||
k = actual_start
|
||||
while items:
|
||||
E = items.pop(0)
|
||||
array.put(unicode(k), E)
|
||||
k += 1
|
||||
array.put('length', float(arr_len - actual_delete_count + items_len))
|
||||
return A
|
||||
|
||||
def unshift(this, args):
|
||||
array = to_object(this, args.space)
|
||||
arr_len = js_arr_length(array)
|
||||
argCount = len(args)
|
||||
k = arr_len
|
||||
while k > 0:
|
||||
fr = unicode(k - 1)
|
||||
to = unicode(k + argCount - 1)
|
||||
if array.has_property(fr):
|
||||
array.put(to, array.get(fr))
|
||||
else:
|
||||
array.delete(to)
|
||||
k -= 1
|
||||
items = tuple(args)
|
||||
for j, e in enumerate(items):
|
||||
array.put(unicode(j), e)
|
||||
array.put('length', float(arr_len + argCount))
|
||||
return float(arr_len + argCount)
|
||||
|
||||
def indexOf(this, args):
|
||||
array = to_object(this, args.space)
|
||||
searchElement = get_arg(args, 0)
|
||||
arr_len = js_arr_length(array)
|
||||
if arr_len == 0:
|
||||
return -1.
|
||||
if len(args) > 1:
|
||||
n = to_int(args[1])
|
||||
else:
|
||||
n = 0
|
||||
if n >= arr_len:
|
||||
return -1.
|
||||
if n >= 0:
|
||||
k = n
|
||||
else:
|
||||
k = arr_len - abs(n)
|
||||
if k < 0:
|
||||
k = 0
|
||||
while k < arr_len:
|
||||
if array.has_property(unicode(k)):
|
||||
elementK = array.get(unicode(k))
|
||||
if strict_equality_op(searchElement, elementK):
|
||||
return float(k)
|
||||
k += 1
|
||||
return -1.
|
||||
|
||||
def lastIndexOf(this, args):
|
||||
array = to_object(this, args.space)
|
||||
searchElement = get_arg(args, 0)
|
||||
arr_len = js_arr_length(array)
|
||||
if arr_len == 0:
|
||||
return -1.
|
||||
if len(args) > 1:
|
||||
n = to_int(args[1])
|
||||
else:
|
||||
n = arr_len - 1
|
||||
if n >= 0:
|
||||
k = min(n, arr_len - 1)
|
||||
else:
|
||||
k = arr_len - abs(n)
|
||||
while k >= 0:
|
||||
if array.has_property(unicode(k)):
|
||||
elementK = array.get(unicode(k))
|
||||
if strict_equality_op(searchElement, elementK):
|
||||
return float(k)
|
||||
k -= 1
|
||||
return -1.
|
||||
|
||||
def every(this, args):
|
||||
array = to_object(this, args.space)
|
||||
callbackfn = get_arg(args, 0)
|
||||
arr_len = js_arr_length(array)
|
||||
if not is_callable(callbackfn):
|
||||
raise MakeError('TypeError', 'callbackfn must be a function')
|
||||
T = get_arg(args, 1)
|
||||
k = 0
|
||||
while k < arr_len:
|
||||
if array.has_property(unicode(k)):
|
||||
kValue = array.get(unicode(k))
|
||||
if not to_boolean(
|
||||
callbackfn.call(T, (kValue, float(k), array))):
|
||||
return False
|
||||
k += 1
|
||||
return True
|
||||
|
||||
def some(this, args):
|
||||
array = to_object(this, args.space)
|
||||
callbackfn = get_arg(args, 0)
|
||||
arr_len = js_arr_length(array)
|
||||
if not is_callable(callbackfn):
|
||||
raise MakeError('TypeError', 'callbackfn must be a function')
|
||||
T = get_arg(args, 1)
|
||||
k = 0
|
||||
while k < arr_len:
|
||||
if array.has_property(unicode(k)):
|
||||
kValue = array.get(unicode(k))
|
||||
if to_boolean(callbackfn.call(T, (kValue, float(k), array))):
|
||||
return True
|
||||
k += 1
|
||||
return False
|
||||
|
||||
def forEach(this, args):
|
||||
array = to_object(this, args.space)
|
||||
callbackfn = get_arg(args, 0)
|
||||
arr_len = js_arr_length(array)
|
||||
if not is_callable(callbackfn):
|
||||
raise MakeError('TypeError', 'callbackfn must be a function')
|
||||
_this = get_arg(args, 1)
|
||||
k = 0
|
||||
while k < arr_len:
|
||||
sk = unicode(k)
|
||||
if array.has_property(sk):
|
||||
kValue = array.get(sk)
|
||||
callbackfn.call(_this, (kValue, float(k), array))
|
||||
k += 1
|
||||
return undefined
|
||||
|
||||
def map(this, args):
|
||||
array = to_object(this, args.space)
|
||||
callbackfn = get_arg(args, 0)
|
||||
arr_len = js_arr_length(array)
|
||||
if not is_callable(callbackfn):
|
||||
raise MakeError('TypeError', 'callbackfn must be a function')
|
||||
_this = get_arg(args, 1)
|
||||
k = 0
|
||||
A = args.space.NewArray(0)
|
||||
while k < arr_len:
|
||||
Pk = unicode(k)
|
||||
if array.has_property(Pk):
|
||||
kValue = array.get(Pk)
|
||||
mappedValue = callbackfn.call(_this, (kValue, float(k), array))
|
||||
A.define_own_property(
|
||||
Pk, {
|
||||
'value': mappedValue,
|
||||
'writable': True,
|
||||
'enumerable': True,
|
||||
'configurable': True
|
||||
}, False)
|
||||
k += 1
|
||||
return A
|
||||
|
||||
def filter(this, args):
|
||||
array = to_object(this, args.space)
|
||||
callbackfn = get_arg(args, 0)
|
||||
arr_len = js_arr_length(array)
|
||||
if not is_callable(callbackfn):
|
||||
raise MakeError('TypeError', 'callbackfn must be a function')
|
||||
_this = get_arg(args, 1)
|
||||
k = 0
|
||||
res = []
|
||||
while k < arr_len:
|
||||
if array.has_property(unicode(k)):
|
||||
kValue = array.get(unicode(k))
|
||||
if to_boolean(
|
||||
callbackfn.call(_this, (kValue, float(k), array))):
|
||||
res.append(kValue)
|
||||
k += 1
|
||||
return args.space.ConstructArray(res)
|
||||
|
||||
def reduce(this, args):
|
||||
array = to_object(this, args.space)
|
||||
callbackfn = get_arg(args, 0)
|
||||
arr_len = js_arr_length(array)
|
||||
if not is_callable(callbackfn):
|
||||
raise MakeError('TypeError', 'callbackfn must be a function')
|
||||
if not arr_len and len(args) < 2:
|
||||
raise MakeError('TypeError',
|
||||
'Reduce of empty array with no initial value')
|
||||
k = 0
|
||||
accumulator = undefined
|
||||
if len(args) > 1: # initial value present
|
||||
accumulator = args[1]
|
||||
else:
|
||||
kPresent = False
|
||||
while not kPresent and k < arr_len:
|
||||
kPresent = array.has_property(unicode(k))
|
||||
if kPresent:
|
||||
accumulator = array.get(unicode(k))
|
||||
k += 1
|
||||
if not kPresent:
|
||||
raise MakeError('TypeError',
|
||||
'Reduce of empty array with no initial value')
|
||||
while k < arr_len:
|
||||
if array.has_property(unicode(k)):
|
||||
kValue = array.get(unicode(k))
|
||||
accumulator = callbackfn.call(
|
||||
undefined, (accumulator, kValue, float(k), array))
|
||||
k += 1
|
||||
return accumulator
|
||||
|
||||
def reduceRight(this, args):
|
||||
array = to_object(this, args.space)
|
||||
callbackfn = get_arg(args, 0)
|
||||
arr_len = js_arr_length(array)
|
||||
if not is_callable(callbackfn):
|
||||
raise MakeError('TypeError', 'callbackfn must be a function')
|
||||
if not arr_len and len(args) < 2:
|
||||
raise MakeError('TypeError',
|
||||
'Reduce of empty array with no initial value')
|
||||
k = arr_len - 1
|
||||
accumulator = undefined
|
||||
|
||||
if len(args) > 1: # initial value present
|
||||
accumulator = args[1]
|
||||
else:
|
||||
kPresent = False
|
||||
while not kPresent and k >= 0:
|
||||
kPresent = array.has_property(unicode(k))
|
||||
if kPresent:
|
||||
accumulator = array.get(unicode(k))
|
||||
k -= 1
|
||||
if not kPresent:
|
||||
raise MakeError('TypeError',
|
||||
'Reduce of empty array with no initial value')
|
||||
while k >= 0:
|
||||
if array.has_property(unicode(k)):
|
||||
kValue = array.get(unicode(k))
|
||||
accumulator = callbackfn.call(
|
||||
undefined, (accumulator, kValue, float(k), array))
|
||||
k -= 1
|
||||
return accumulator
|
||||
|
||||
|
||||
def sort_compare(a, b, comp):
|
||||
if a is None:
|
||||
if b is None:
|
||||
return 0
|
||||
return 1
|
||||
if b is None:
|
||||
if a is None:
|
||||
return 0
|
||||
return -1
|
||||
if is_undefined(a):
|
||||
if is_undefined(b):
|
||||
return 0
|
||||
return 1
|
||||
if is_undefined(b):
|
||||
if is_undefined(a):
|
||||
return 0
|
||||
return -1
|
||||
if comp is not None:
|
||||
res = comp.call(undefined, (a, b))
|
||||
return to_int(res)
|
||||
x, y = to_string(a), to_string(b)
|
||||
if x < y:
|
||||
return -1
|
||||
elif x > y:
|
||||
return 1
|
||||
return 0
|
||||
@@ -1,22 +0,0 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from ..conversions import *
|
||||
from ..func_utils import *
|
||||
|
||||
|
||||
class BooleanPrototype:
|
||||
def toString(this, args):
|
||||
if GetClass(this) != 'Boolean':
|
||||
raise MakeError('TypeError',
|
||||
'Boolean.prototype.toString is not generic')
|
||||
if is_object(this):
|
||||
this = this.value
|
||||
return u'true' if this else u'false'
|
||||
|
||||
def valueOf(this, args):
|
||||
if GetClass(this) != 'Boolean':
|
||||
raise MakeError('TypeError',
|
||||
'Boolean.prototype.valueOf is not generic')
|
||||
if is_object(this):
|
||||
this = this.value
|
||||
return this
|
||||
@@ -1,15 +0,0 @@
|
||||
from __future__ import unicode_literals
|
||||
from ..conversions import *
|
||||
from ..func_utils import *
|
||||
|
||||
|
||||
class ErrorPrototype:
|
||||
def toString(this, args):
|
||||
if Type(this) != 'Object':
|
||||
raise MakeError('TypeError',
|
||||
'Error.prototype.toString called on non-object')
|
||||
name = this.get('name')
|
||||
name = u'Error' if is_undefined(name) else to_string(name)
|
||||
msg = this.get('message')
|
||||
msg = '' if is_undefined(msg) else to_string(msg)
|
||||
return name + (name and msg and ': ') + msg
|
||||
@@ -1,61 +0,0 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from ..conversions import *
|
||||
from ..func_utils import *
|
||||
|
||||
# python 3 support
|
||||
import six
|
||||
if six.PY3:
|
||||
basestring = str
|
||||
long = int
|
||||
xrange = range
|
||||
unicode = str
|
||||
|
||||
# todo fix apply and bind
|
||||
|
||||
|
||||
class FunctionPrototype:
|
||||
def toString(this, args):
|
||||
if not is_callable(this):
|
||||
raise MakeError('TypeError',
|
||||
'Function.prototype.toString is not generic')
|
||||
|
||||
args = u', '.join(map(unicode, this.params))
|
||||
return u'function %s(%s) { [native code] }' % (this.name if this.name
|
||||
else u'', args)
|
||||
|
||||
def call(this, args):
|
||||
if not is_callable(this):
|
||||
raise MakeError('TypeError',
|
||||
'Function.prototype.call is not generic')
|
||||
_this = get_arg(args, 0)
|
||||
_args = tuple(args)[1:]
|
||||
return this.call(_this, _args)
|
||||
|
||||
def apply(this, args):
|
||||
if not is_callable(this):
|
||||
raise MakeError('TypeError',
|
||||
'Function.prototype.apply is not generic')
|
||||
_args = get_arg(args, 1)
|
||||
if not is_object(_args):
|
||||
raise MakeError(
|
||||
'TypeError',
|
||||
'argList argument to Function.prototype.apply must an Object')
|
||||
_this = get_arg(args, 0)
|
||||
return this.call(_this, js_array_to_tuple(_args))
|
||||
|
||||
def bind(this, args):
|
||||
if not is_callable(this):
|
||||
raise MakeError('TypeError',
|
||||
'Function.prototype.bind is not generic')
|
||||
bound_this = get_arg(args, 0)
|
||||
bound_args = tuple(args)[1:]
|
||||
|
||||
def bound(dummy_this, extra_args):
|
||||
return this.call(bound_this, bound_args + tuple(extra_args))
|
||||
|
||||
js_bound = args.space.NewFunction(bound, this.ctx, (), u'', False, ())
|
||||
js_bound.put(u'length',
|
||||
float(max(len(this.params) - len(bound_args), 0.)))
|
||||
js_bound.put(u'name', u'boundFunc')
|
||||
return js_bound
|
||||
@@ -1,205 +0,0 @@
|
||||
from __future__ import unicode_literals
|
||||
from ..conversions import *
|
||||
from ..func_utils import *
|
||||
from ..operations import strict_equality_op
|
||||
import json
|
||||
|
||||
indent = ''
|
||||
# python 3 support
|
||||
import six
|
||||
if six.PY3:
|
||||
basestring = str
|
||||
long = int
|
||||
xrange = range
|
||||
unicode = str
|
||||
|
||||
|
||||
def parse(this, args):
|
||||
text, reviver = get_arg(args, 0), get_arg(args, 1)
|
||||
s = to_string(text)
|
||||
try:
|
||||
unfiltered = json.loads(s)
|
||||
except:
|
||||
raise MakeError(
|
||||
'SyntaxError',
|
||||
'JSON.parse could not parse JSON string - Invalid syntax')
|
||||
unfiltered = to_js(unfiltered, args.space)
|
||||
if is_callable(reviver):
|
||||
root = args.space.ConstructObject({'': unfiltered})
|
||||
return walk(root, '', reviver)
|
||||
else:
|
||||
return unfiltered
|
||||
|
||||
|
||||
def stringify(this, args):
|
||||
global indent
|
||||
value, replacer, space = get_arg(args, 0), get_arg(args, 1), get_arg(
|
||||
args, 2)
|
||||
stack = set([])
|
||||
indent = ''
|
||||
property_list = replacer_function = undefined
|
||||
if is_object(replacer):
|
||||
if is_callable(replacer):
|
||||
replacer_function = replacer
|
||||
elif replacer.Class == 'Array':
|
||||
property_list = []
|
||||
for e in replacer:
|
||||
v = replacer[e]
|
||||
item = undefined
|
||||
typ = Type(v)
|
||||
if typ == 'Number':
|
||||
item = to_string(v)
|
||||
elif typ == 'String':
|
||||
item = v
|
||||
elif typ == 'Object':
|
||||
if GetClass(v) in ('String', 'Number'):
|
||||
item = to_string(v)
|
||||
if not is_undefined(item) and item not in property_list:
|
||||
property_list.append(item)
|
||||
if is_object(space):
|
||||
if GetClass(space) == 'Number':
|
||||
space = to_number(space)
|
||||
elif GetClass(space) == 'String':
|
||||
space = to_string(space)
|
||||
if Type(space) == 'Number':
|
||||
space = min(10, to_int(space))
|
||||
gap = max(int(space), 0) * ' '
|
||||
elif Type(space) == 'String':
|
||||
gap = space[:10]
|
||||
else:
|
||||
gap = ''
|
||||
return Str('', args.space.ConstructObject({
|
||||
'': value
|
||||
}), replacer_function, property_list, gap, stack, space)
|
||||
|
||||
|
||||
def Str(key, holder, replacer_function, property_list, gap, stack, space):
|
||||
value = holder.get(key)
|
||||
if is_object(value):
|
||||
to_json = value.get('toJSON')
|
||||
if is_callable(to_json):
|
||||
value = to_json.call(value, (key, ))
|
||||
if not is_undefined(replacer_function):
|
||||
value = replacer_function.call(holder, (key, value))
|
||||
|
||||
if is_object(value):
|
||||
if value.Class == 'String':
|
||||
value = to_string(value)
|
||||
elif value.Class == 'Number':
|
||||
value = to_number(value)
|
||||
elif value.Class == 'Boolean':
|
||||
value = to_boolean(value)
|
||||
typ = Type(value)
|
||||
if is_null(value):
|
||||
return 'null'
|
||||
elif typ == 'Boolean':
|
||||
return 'true' if value else 'false'
|
||||
elif typ == 'String':
|
||||
return Quote(value)
|
||||
elif typ == 'Number':
|
||||
if not is_infinity(value):
|
||||
return to_string(value)
|
||||
return 'null'
|
||||
if is_object(value) and not is_callable(value):
|
||||
if value.Class == 'Array':
|
||||
return ja(value, stack, gap, property_list, replacer_function,
|
||||
space)
|
||||
else:
|
||||
return jo(value, stack, gap, property_list, replacer_function,
|
||||
space)
|
||||
return undefined
|
||||
|
||||
|
||||
def jo(value, stack, gap, property_list, replacer_function, space):
|
||||
global indent
|
||||
if value in stack:
|
||||
raise MakeError('TypeError', 'Converting circular structure to JSON')
|
||||
stack.add(value)
|
||||
stepback = indent
|
||||
indent += gap
|
||||
if not is_undefined(property_list):
|
||||
k = property_list
|
||||
else:
|
||||
k = [unicode(e) for e, d in value.own.items() if d.get('enumerable')]
|
||||
partial = []
|
||||
for p in k:
|
||||
str_p = Str(p, value, replacer_function, property_list, gap, stack,
|
||||
space)
|
||||
if not is_undefined(str_p):
|
||||
member = json.dumps(p) + ':' + (
|
||||
' ' if gap else
|
||||
'') + str_p # todo not sure here - what space character?
|
||||
partial.append(member)
|
||||
if not partial:
|
||||
final = '{}'
|
||||
else:
|
||||
if not gap:
|
||||
final = '{%s}' % ','.join(partial)
|
||||
else:
|
||||
sep = ',\n' + indent
|
||||
properties = sep.join(partial)
|
||||
final = '{\n' + indent + properties + '\n' + stepback + '}'
|
||||
stack.remove(value)
|
||||
indent = stepback
|
||||
return final
|
||||
|
||||
|
||||
def ja(value, stack, gap, property_list, replacer_function, space):
|
||||
global indent
|
||||
if value in stack:
|
||||
raise MakeError('TypeError', 'Converting circular structure to JSON')
|
||||
stack.add(value)
|
||||
stepback = indent
|
||||
indent += gap
|
||||
partial = []
|
||||
length = js_arr_length(value)
|
||||
for index in xrange(length):
|
||||
index = unicode(index)
|
||||
str_index = Str(index, value, replacer_function, property_list, gap,
|
||||
stack, space)
|
||||
if is_undefined(str_index):
|
||||
partial.append('null')
|
||||
else:
|
||||
partial.append(str_index)
|
||||
if not partial:
|
||||
final = '[]'
|
||||
else:
|
||||
if not gap:
|
||||
final = '[%s]' % ','.join(partial)
|
||||
else:
|
||||
sep = ',\n' + indent
|
||||
properties = sep.join(partial)
|
||||
final = '[\n' + indent + properties + '\n' + stepback + ']'
|
||||
stack.remove(value)
|
||||
indent = stepback
|
||||
return final
|
||||
|
||||
|
||||
def Quote(string):
|
||||
return json.dumps(string)
|
||||
|
||||
|
||||
def to_js(d, _args_space):
|
||||
return convert_to_js_type(d, _args_space)
|
||||
|
||||
|
||||
def walk(holder, name, reviver):
|
||||
val = holder.get(name)
|
||||
if GetClass(val) == 'Array':
|
||||
for i in xrange(js_arr_length(val)):
|
||||
i = unicode(i)
|
||||
new_element = walk(val, i, reviver)
|
||||
if is_undefined(new_element):
|
||||
val.delete(i)
|
||||
else:
|
||||
new_element.put(i, new_element)
|
||||
elif is_object(val):
|
||||
for key in [
|
||||
unicode(e) for e, d in val.own.items() if d.get('enumerable')
|
||||
]:
|
||||
new_element = walk(val, key, reviver)
|
||||
if is_undefined(new_element):
|
||||
val.delete(key)
|
||||
else:
|
||||
val.put(key, new_element)
|
||||
return reviver.call(holder, (name, val))
|
||||
@@ -1,163 +0,0 @@
|
||||
from __future__ import unicode_literals
|
||||
from ..conversions import *
|
||||
from ..func_utils import *
|
||||
|
||||
import six
|
||||
if six.PY3:
|
||||
basestring = str
|
||||
long = int
|
||||
xrange = range
|
||||
unicode = str
|
||||
|
||||
RADIX_SYMBOLS = {
|
||||
0: '0',
|
||||
1: '1',
|
||||
2: '2',
|
||||
3: '3',
|
||||
4: '4',
|
||||
5: '5',
|
||||
6: '6',
|
||||
7: '7',
|
||||
8: '8',
|
||||
9: '9',
|
||||
10: 'a',
|
||||
11: 'b',
|
||||
12: 'c',
|
||||
13: 'd',
|
||||
14: 'e',
|
||||
15: 'f',
|
||||
16: 'g',
|
||||
17: 'h',
|
||||
18: 'i',
|
||||
19: 'j',
|
||||
20: 'k',
|
||||
21: 'l',
|
||||
22: 'm',
|
||||
23: 'n',
|
||||
24: 'o',
|
||||
25: 'p',
|
||||
26: 'q',
|
||||
27: 'r',
|
||||
28: 's',
|
||||
29: 't',
|
||||
30: 'u',
|
||||
31: 'v',
|
||||
32: 'w',
|
||||
33: 'x',
|
||||
34: 'y',
|
||||
35: 'z'
|
||||
}
|
||||
|
||||
|
||||
def to_str_rep(num):
|
||||
if is_nan(num):
|
||||
return 'NaN'
|
||||
elif is_infinity(num):
|
||||
sign = '-' if num < 0 else ''
|
||||
return sign + 'Infinity'
|
||||
elif int(num) == num: # dont print .0
|
||||
return unicode(int(num))
|
||||
return unicode(num) # todo: Make it 100% consistent with Node
|
||||
|
||||
|
||||
class NumberPrototype:
|
||||
def toString(this, args):
|
||||
if GetClass(this) != 'Number':
|
||||
raise MakeError('TypeError',
|
||||
'Number.prototype.valueOf is not generic')
|
||||
if type(this) != float:
|
||||
this = this.value
|
||||
radix = get_arg(args, 0)
|
||||
if is_undefined(radix):
|
||||
return to_str_rep(this)
|
||||
r = to_int(radix)
|
||||
if r == 10:
|
||||
return to_str_rep(this)
|
||||
if r not in xrange(2, 37) or radix != r:
|
||||
raise MakeError(
|
||||
'RangeError',
|
||||
'Number.prototype.toString() radix argument must be an integer between 2 and 36'
|
||||
)
|
||||
num = to_int(this)
|
||||
if num < 0:
|
||||
num = -num
|
||||
sign = '-'
|
||||
else:
|
||||
sign = ''
|
||||
res = ''
|
||||
while num:
|
||||
s = RADIX_SYMBOLS[num % r]
|
||||
num = num // r
|
||||
res = s + res
|
||||
return sign + (res if res else '0')
|
||||
|
||||
def valueOf(this, args):
|
||||
if GetClass(this) != 'Number':
|
||||
raise MakeError('TypeError',
|
||||
'Number.prototype.valueOf is not generic')
|
||||
if type(this) != float:
|
||||
this = this.value
|
||||
return this
|
||||
|
||||
def toFixed(this, args):
|
||||
if GetClass(this) != 'Number':
|
||||
raise MakeError(
|
||||
'TypeError',
|
||||
'Number.prototype.toFixed called on incompatible receiver')
|
||||
if type(this) != float:
|
||||
this = this.value
|
||||
fractionDigits = get_arg(args, 0)
|
||||
digs = to_int(fractionDigits)
|
||||
if digs < 0 or digs > 20:
|
||||
raise MakeError(
|
||||
'RangeError',
|
||||
'toFixed() digits argument must be between 0 and 20')
|
||||
elif is_infinity(this):
|
||||
return 'Infinity' if this > 0 else '-Infinity'
|
||||
elif is_nan(this):
|
||||
return 'NaN'
|
||||
return format(this, '-.%df' % digs)
|
||||
|
||||
def toExponential(this, args):
|
||||
if GetClass(this) != 'Number':
|
||||
raise MakeError(
|
||||
'TypeError',
|
||||
'Number.prototype.toExponential called on incompatible receiver'
|
||||
)
|
||||
if type(this) != float:
|
||||
this = this.value
|
||||
fractionDigits = get_arg(args, 0)
|
||||
digs = to_int(fractionDigits)
|
||||
if digs < 0 or digs > 20:
|
||||
raise MakeError(
|
||||
'RangeError',
|
||||
'toFixed() digits argument must be between 0 and 20')
|
||||
elif is_infinity(this):
|
||||
return 'Infinity' if this > 0 else '-Infinity'
|
||||
elif is_nan(this):
|
||||
return 'NaN'
|
||||
return format(this, '-.%de' % digs)
|
||||
|
||||
def toPrecision(this, args):
|
||||
if GetClass(this) != 'Number':
|
||||
raise MakeError(
|
||||
'TypeError',
|
||||
'Number.prototype.toPrecision called on incompatible receiver')
|
||||
if type(this) != float:
|
||||
this = this.value
|
||||
precision = get_arg(args, 0)
|
||||
if is_undefined(precision):
|
||||
return to_string(this)
|
||||
prec = to_int(precision)
|
||||
if is_nan(this):
|
||||
return 'NaN'
|
||||
elif is_infinity(this):
|
||||
return 'Infinity' if this > 0 else '-Infinity'
|
||||
digs = prec - len(str(int(this)))
|
||||
if digs >= 0:
|
||||
return format(this, '-.%df' % digs)
|
||||
else:
|
||||
return format(this, '-.%df' % (prec - 1))
|
||||
|
||||
|
||||
NumberPrototype.toLocaleString = NumberPrototype.toString
|
||||
@@ -1,48 +0,0 @@
|
||||
from __future__ import unicode_literals
|
||||
from ..conversions import *
|
||||
from ..func_utils import *
|
||||
|
||||
|
||||
class ObjectPrototype:
|
||||
def toString(this, args):
|
||||
if type(this) == UNDEFINED_TYPE:
|
||||
return u'[object Undefined]'
|
||||
elif type(this) == NULL_TYPE:
|
||||
return u'[object Null]'
|
||||
return u'[object %s]' % GetClass(to_object(this, args.space))
|
||||
|
||||
def valueOf(this, args):
|
||||
return to_object(this, args.space)
|
||||
|
||||
def toLocaleString(this, args):
|
||||
o = to_object(this, args.space)
|
||||
toString = o.get(u'toString')
|
||||
if not is_callable(toString):
|
||||
raise MakeError('TypeError', 'toString of this is not callcable')
|
||||
else:
|
||||
return toString.call(this, args)
|
||||
|
||||
def hasOwnProperty(this, args):
|
||||
prop = get_arg(args, 0)
|
||||
o = to_object(this, args.space)
|
||||
return o.get_own_property(to_string(prop)) is not None
|
||||
|
||||
def isPrototypeOf(this, args):
|
||||
# a bit stupid specification because of object conversion that will cause invalid values for primitives
|
||||
# for example Object.prototype.isPrototypeOf.call((5).__proto__, 5) gives false
|
||||
obj = get_arg(args, 0)
|
||||
if not is_object(obj):
|
||||
return False
|
||||
o = to_object(this, args.space)
|
||||
while 1:
|
||||
obj = obj.prototype
|
||||
if obj is None or is_null(obj):
|
||||
return False
|
||||
if obj is o:
|
||||
return True
|
||||
|
||||
def propertyIsEnumerable(this, args):
|
||||
prop = get_arg(args, 0)
|
||||
o = to_object(this, args.space)
|
||||
cand = o.own.get(to_string(prop))
|
||||
return cand is not None and cand.get('enumerable')
|
||||
@@ -1,56 +0,0 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from ..conversions import *
|
||||
from ..func_utils import *
|
||||
|
||||
|
||||
class RegExpPrototype:
|
||||
def toString(this, args):
|
||||
flags = u''
|
||||
try:
|
||||
if this.glob:
|
||||
flags += u'g'
|
||||
if this.ignore_case:
|
||||
flags += u'i'
|
||||
if this.multiline:
|
||||
flags += u'm'
|
||||
except:
|
||||
pass
|
||||
try:
|
||||
v = this.value if this.value else u'(?:)'
|
||||
except:
|
||||
v = u'(?:)'
|
||||
return u'/%s/' % v + flags
|
||||
|
||||
def test(this, args):
|
||||
string = get_arg(args, 0)
|
||||
return RegExpExec(this, string, args.space) is not null
|
||||
|
||||
def _exec(
|
||||
this, args
|
||||
): # will be changed to exec in base.py. cant name it exec here...
|
||||
string = get_arg(args, 0)
|
||||
return RegExpExec(this, string, args.space)
|
||||
|
||||
|
||||
def RegExpExec(this, string, space):
|
||||
if GetClass(this) != 'RegExp':
|
||||
raise MakeError('TypeError', 'RegExp.prototype.exec is not generic!')
|
||||
string = to_string(string)
|
||||
length = len(string)
|
||||
i = to_int(this.get('lastIndex')) if this.glob else 0
|
||||
matched = False
|
||||
while not matched:
|
||||
if i < 0 or i > length:
|
||||
this.put('lastIndex', 0.)
|
||||
return null
|
||||
matched = this.match(string, i)
|
||||
i += 1
|
||||
start, end = matched.span() #[0]+i-1, matched.span()[1]+i-1
|
||||
if this.glob:
|
||||
this.put('lastIndex', float(end))
|
||||
arr = convert_to_js_type(
|
||||
[matched.group()] + list(matched.groups()), space=space)
|
||||
arr.put('index', float(start))
|
||||
arr.put('input', unicode(string))
|
||||
return arr
|
||||
@@ -1,323 +0,0 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
from ..conversions import *
|
||||
from ..func_utils import *
|
||||
from jsregexp import RegExpExec
|
||||
|
||||
DIGS = set(u'0123456789')
|
||||
WHITE = u"\u0009\u000A\u000B\u000C\u000D\u0020\u00A0\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u2028\u2029\u202F\u205F\u3000\uFEFF"
|
||||
|
||||
|
||||
def replacement_template(rep, source, span, npar):
|
||||
"""Takes the replacement template and some info about the match and returns filled template
|
||||
"""
|
||||
n = 0
|
||||
res = ''
|
||||
while n < len(rep) - 1:
|
||||
char = rep[n]
|
||||
if char == '$':
|
||||
if rep[n + 1] == '$':
|
||||
res += '$'
|
||||
n += 2
|
||||
continue
|
||||
elif rep[n + 1] == '`':
|
||||
# replace with string that is BEFORE match
|
||||
res += source[:span[0]]
|
||||
n += 2
|
||||
continue
|
||||
elif rep[n + 1] == '\'':
|
||||
# replace with string that is AFTER match
|
||||
res += source[span[1]:]
|
||||
n += 2
|
||||
continue
|
||||
elif rep[n + 1] in DIGS:
|
||||
dig = rep[n + 1]
|
||||
if n + 2 < len(rep) and rep[n + 2] in DIGS:
|
||||
dig += rep[n + 2]
|
||||
num = int(dig)
|
||||
# we will not do any replacements if we dont have this npar or dig is 0
|
||||
if not num or num > len(npar):
|
||||
res += '$' + dig
|
||||
else:
|
||||
# None - undefined has to be replaced with ''
|
||||
res += npar[num - 1] if npar[num - 1] else ''
|
||||
n += 1 + len(dig)
|
||||
continue
|
||||
res += char
|
||||
n += 1
|
||||
if n < len(rep):
|
||||
res += rep[-1]
|
||||
return res
|
||||
|
||||
|
||||
###################################################
|
||||
|
||||
|
||||
class StringPrototype:
|
||||
def toString(this, args):
|
||||
if GetClass(this) != 'String':
|
||||
raise MakeError('TypeError',
|
||||
'String.prototype.toString is not generic')
|
||||
if type(this) == unicode:
|
||||
return this
|
||||
assert type(this.value) == unicode
|
||||
return this.value
|
||||
|
||||
def valueOf(this, args):
|
||||
if GetClass(this) != 'String':
|
||||
raise MakeError('TypeError',
|
||||
'String.prototype.valueOf is not generic')
|
||||
if type(this) == unicode:
|
||||
return this
|
||||
assert type(this.value) == unicode
|
||||
return this.value
|
||||
|
||||
def charAt(this, args):
|
||||
cok(this)
|
||||
pos = to_int(get_arg(args, 0))
|
||||
s = to_string(this)
|
||||
if 0 <= pos < len(s):
|
||||
return s[pos]
|
||||
return u''
|
||||
|
||||
def charCodeAt(this, args):
|
||||
cok(this)
|
||||
pos = to_int(get_arg(args, 0))
|
||||
s = to_string(this)
|
||||
if 0 <= pos < len(s):
|
||||
return float(ord(s[pos]))
|
||||
return NaN
|
||||
|
||||
def concat(this, args):
|
||||
cok(this)
|
||||
return to_string(this) + u''.join(map(to_string, args))
|
||||
|
||||
def indexOf(this, args):
|
||||
cok(this)
|
||||
search = to_string(get_arg(args, 0))
|
||||
pos = to_int(get_arg(args, 1))
|
||||
s = to_string(this)
|
||||
return float(s.find(search, min(max(pos, 0), len(s))))
|
||||
|
||||
def lastIndexOf(this, args):
|
||||
cok(this)
|
||||
search = to_string(get_arg(args, 0))
|
||||
pos = get_arg(args, 1)
|
||||
s = to_string(this)
|
||||
pos = 10**12 if is_nan(pos) else to_int(pos)
|
||||
return float(s.rfind(search, 0, min(max(pos, 0) + 1, len(s))))
|
||||
|
||||
def localeCompare(this, args):
|
||||
cok(this)
|
||||
s = to_string(this)
|
||||
that = to_string(get_arg(args, 0))
|
||||
if s < that:
|
||||
return -1.
|
||||
elif s > that:
|
||||
return 1.
|
||||
return 0.
|
||||
|
||||
def match(this, args):
|
||||
cok(this)
|
||||
s = to_string(this)
|
||||
regexp = get_arg(args, 0)
|
||||
r = args.space.NewRegExp(
|
||||
regexp, '') if GetClass(regexp) != 'RegExp' else regexp
|
||||
if not r.glob:
|
||||
return RegExpExec(r, s, space=args.space)
|
||||
r.put('lastIndex', float(0))
|
||||
found = []
|
||||
previous_last_index = 0
|
||||
last_match = True
|
||||
while last_match:
|
||||
result = RegExpExec(r, s, space=args.space)
|
||||
if is_null(result):
|
||||
last_match = False
|
||||
else:
|
||||
this_index = r.get('lastIndex')
|
||||
if this_index == previous_last_index:
|
||||
r.put('lastIndex', float(this_index + 1))
|
||||
previous_last_index += 1
|
||||
else:
|
||||
previous_last_index = this_index
|
||||
matchStr = result.get('0')
|
||||
found.append(matchStr)
|
||||
if not found:
|
||||
return null
|
||||
return args.space.ConstructArray(found)
|
||||
|
||||
def replace(this, args):
|
||||
# VERY COMPLICATED. to check again.
|
||||
cok(this)
|
||||
s = to_string(this)
|
||||
searchValue = get_arg(args, 0)
|
||||
replaceValue = get_arg(args, 1)
|
||||
res = ''
|
||||
if not is_callable(replaceValue):
|
||||
replaceValue = to_string(replaceValue)
|
||||
func = False
|
||||
else:
|
||||
func = True
|
||||
# Replace all ( global )
|
||||
if GetClass(searchValue) == 'RegExp' and searchValue.glob:
|
||||
last = 0
|
||||
for e in re.finditer(searchValue.pat, s):
|
||||
res += s[last:e.span()[0]]
|
||||
if func:
|
||||
# prepare arguments for custom func (replaceValue)
|
||||
call_args = (e.group(), ) + e.groups() + (e.span()[1], s)
|
||||
# convert all types to JS before Js bytecode call...
|
||||
res += to_string(
|
||||
replaceValue.call(
|
||||
this, ensure_js_types(call_args,
|
||||
space=args.space)))
|
||||
else:
|
||||
res += replacement_template(replaceValue, s, e.span(),
|
||||
e.groups())
|
||||
last = e.span()[1]
|
||||
res += s[last:]
|
||||
return res
|
||||
elif GetClass(searchValue) == 'RegExp':
|
||||
e = re.search(searchValue.pat, s)
|
||||
if e is None:
|
||||
return s
|
||||
span = e.span()
|
||||
pars = e.groups()
|
||||
match = e.group()
|
||||
else:
|
||||
match = to_string(searchValue)
|
||||
ind = s.find(match)
|
||||
if ind == -1:
|
||||
return s
|
||||
span = ind, ind + len(match)
|
||||
pars = ()
|
||||
res = s[:span[0]]
|
||||
if func:
|
||||
call_args = (match, ) + pars + (span[1], s)
|
||||
# convert all types to JS before Js bytecode call...
|
||||
res += to_string(
|
||||
replaceValue.call(this,
|
||||
ensure_js_types(call_args,
|
||||
space=args.space)))
|
||||
else:
|
||||
res += replacement_template(replaceValue, s, span, pars)
|
||||
res += s[span[1]:]
|
||||
return res
|
||||
|
||||
def search(this, args):
|
||||
cok(this)
|
||||
string = to_string(this)
|
||||
regexp = get_arg(args, 0)
|
||||
if GetClass(regexp) == 'RegExp':
|
||||
rx = regexp
|
||||
else:
|
||||
rx = args.space.NewRegExp(regexp, '')
|
||||
res = re.search(rx.pat, string)
|
||||
if res is not None:
|
||||
return float(res.span()[0])
|
||||
return -1.
|
||||
|
||||
def slice(this, args):
|
||||
cok(this)
|
||||
s = to_string(this)
|
||||
start = to_int(get_arg(args, 0))
|
||||
length = len(s)
|
||||
end = get_arg(args, 1)
|
||||
end = length if is_undefined(end) else to_int(end)
|
||||
#From = max(length+start, 0) if start<0 else min(length, start)
|
||||
#To = max(length+end, 0) if end<0 else min(length, end)
|
||||
return s[start:end]
|
||||
|
||||
def split(this, args):
|
||||
# its a bit different from re.split!
|
||||
cok(this)
|
||||
s = to_string(this)
|
||||
separator = get_arg(args, 0)
|
||||
limit = get_arg(args, 1)
|
||||
lim = 2**32 - 1 if is_undefined(limit) else to_uint32(limit)
|
||||
if not lim:
|
||||
return args.space.ConstructArray([])
|
||||
if is_undefined(separator):
|
||||
return args.space.ConstructArray([s])
|
||||
len_s = len(s)
|
||||
res = []
|
||||
R = separator if GetClass(separator) == 'RegExp' else to_string(
|
||||
separator)
|
||||
if not len_s:
|
||||
if SplitMatch(s, 0, R) is None:
|
||||
return args.space.ConstructArray([s])
|
||||
return args.space.ConstructArray([])
|
||||
p = q = 0
|
||||
while q != len_s:
|
||||
e, cap = SplitMatch(s, q, R)
|
||||
if e is None or e == p:
|
||||
q += 1
|
||||
continue
|
||||
res.append(s[p:q])
|
||||
p = q = e
|
||||
if len(res) == lim:
|
||||
return args.space.ConstructArray(res)
|
||||
for element in cap:
|
||||
res.append(element)
|
||||
if len(res) == lim:
|
||||
return args.space.ConstructArray(res)
|
||||
res.append(s[p:])
|
||||
return args.space.ConstructArray(res)
|
||||
|
||||
def substring(this, args):
|
||||
cok(this)
|
||||
s = to_string(this)
|
||||
start = to_int(get_arg(args, 0))
|
||||
length = len(s)
|
||||
end = get_arg(args, 1)
|
||||
end = length if is_undefined(end) else to_int(end)
|
||||
fstart = min(max(start, 0), length)
|
||||
fend = min(max(end, 0), length)
|
||||
return s[min(fstart, fend):max(fstart, fend)]
|
||||
|
||||
def substr(this, args):
|
||||
cok(this)
|
||||
#I hate this function and its description in specification
|
||||
r1 = to_string(this)
|
||||
r2 = to_int(get_arg(args, 0))
|
||||
length = get_arg(args, 1)
|
||||
r3 = 10**20 if is_undefined(length) else to_int(length)
|
||||
r4 = len(r1)
|
||||
r5 = r2 if r2 >= 0 else max(0, r2 + r4)
|
||||
r6 = min(max(r3, 0), r4 - r5)
|
||||
if r6 <= 0:
|
||||
return ''
|
||||
return r1[r5:r5 + r6]
|
||||
|
||||
def toLowerCase(this, args):
|
||||
cok(this)
|
||||
return to_string(this).lower()
|
||||
|
||||
def toLocaleLowerCase(this, args):
|
||||
cok(this)
|
||||
return to_string(this).lower()
|
||||
|
||||
def toUpperCase(this, args):
|
||||
cok(this)
|
||||
return to_string(this).upper()
|
||||
|
||||
def toLocaleUpperCase(this, args):
|
||||
cok(this)
|
||||
return to_string(this).upper()
|
||||
|
||||
def trim(this, args):
|
||||
cok(this)
|
||||
return to_string(this).strip(WHITE)
|
||||
|
||||
|
||||
def SplitMatch(s, q, R):
|
||||
# s is Py String to match, q is the py int match start and R is Js RegExp or String.
|
||||
if GetClass(R) == 'RegExp':
|
||||
res = R.match(s, q)
|
||||
return (None, ()) if res is None else (res.span()[1], res.groups())
|
||||
# R is just a string
|
||||
if s[q:].startswith(R):
|
||||
return q + len(R), ()
|
||||
return None, ()
|
||||
@@ -1,149 +0,0 @@
|
||||
from __future__ import unicode_literals
|
||||
from ..conversions import *
|
||||
from ..func_utils import *
|
||||
|
||||
RADIX_CHARS = {
|
||||
'1': 1,
|
||||
'0': 0,
|
||||
'3': 3,
|
||||
'2': 2,
|
||||
'5': 5,
|
||||
'4': 4,
|
||||
'7': 7,
|
||||
'6': 6,
|
||||
'9': 9,
|
||||
'8': 8,
|
||||
'a': 10,
|
||||
'c': 12,
|
||||
'b': 11,
|
||||
'e': 14,
|
||||
'd': 13,
|
||||
'g': 16,
|
||||
'f': 15,
|
||||
'i': 18,
|
||||
'h': 17,
|
||||
'k': 20,
|
||||
'j': 19,
|
||||
'm': 22,
|
||||
'l': 21,
|
||||
'o': 24,
|
||||
'n': 23,
|
||||
'q': 26,
|
||||
'p': 25,
|
||||
's': 28,
|
||||
'r': 27,
|
||||
'u': 30,
|
||||
't': 29,
|
||||
'w': 32,
|
||||
'v': 31,
|
||||
'y': 34,
|
||||
'x': 33,
|
||||
'z': 35,
|
||||
'A': 10,
|
||||
'C': 12,
|
||||
'B': 11,
|
||||
'E': 14,
|
||||
'D': 13,
|
||||
'G': 16,
|
||||
'F': 15,
|
||||
'I': 18,
|
||||
'H': 17,
|
||||
'K': 20,
|
||||
'J': 19,
|
||||
'M': 22,
|
||||
'L': 21,
|
||||
'O': 24,
|
||||
'N': 23,
|
||||
'Q': 26,
|
||||
'P': 25,
|
||||
'S': 28,
|
||||
'R': 27,
|
||||
'U': 30,
|
||||
'T': 29,
|
||||
'W': 32,
|
||||
'V': 31,
|
||||
'Y': 34,
|
||||
'X': 33,
|
||||
'Z': 35
|
||||
}
|
||||
|
||||
# parseFloat
|
||||
# parseInt
|
||||
# isFinite
|
||||
# isNaN
|
||||
|
||||
|
||||
def parseInt(this, args):
|
||||
string, radix = get_arg(args, 0), get_arg(args, 1)
|
||||
string = to_string(string).lstrip()
|
||||
sign = 1
|
||||
if string and string[0] in ('+', '-'):
|
||||
if string[0] == '-':
|
||||
sign = -1
|
||||
string = string[1:]
|
||||
r = to_int32(radix)
|
||||
strip_prefix = True
|
||||
if r:
|
||||
if r < 2 or r > 36:
|
||||
return NaN
|
||||
if r != 16:
|
||||
strip_prefix = False
|
||||
else:
|
||||
r = 10
|
||||
if strip_prefix:
|
||||
if len(string) >= 2 and string[:2] in ('0x', '0X'):
|
||||
string = string[2:]
|
||||
r = 16
|
||||
n = 0
|
||||
num = 0
|
||||
while n < len(string):
|
||||
cand = RADIX_CHARS.get(string[n])
|
||||
if cand is None or not cand < r:
|
||||
break
|
||||
num = cand + num * r
|
||||
n += 1
|
||||
if not n:
|
||||
return NaN
|
||||
return float(sign * num)
|
||||
|
||||
|
||||
def parseFloat(this, args):
|
||||
string = get_arg(args, 0)
|
||||
string = to_string(string).strip()
|
||||
sign = 1
|
||||
if string and string[0] in ('+', '-'):
|
||||
if string[0] == '-':
|
||||
sign = -1
|
||||
string = string[1:]
|
||||
num = None
|
||||
length = 1
|
||||
max_len = None
|
||||
failed = 0
|
||||
while length <= len(string):
|
||||
try:
|
||||
num = float(string[:length])
|
||||
max_len = length
|
||||
failed = 0
|
||||
except:
|
||||
failed += 1
|
||||
if failed > 4: # cant be a number anymore
|
||||
break
|
||||
length += 1
|
||||
if num is None:
|
||||
return NaN
|
||||
return sign * float(string[:max_len])
|
||||
|
||||
|
||||
def isNaN(this, args):
|
||||
number = get_arg(args, 0)
|
||||
if is_nan(to_number(number)):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def isFinite(this, args):
|
||||
number = get_arg(args, 0)
|
||||
num = to_number(number)
|
||||
if is_nan(num) or is_infinity(num):
|
||||
return False
|
||||
return True
|
||||
@@ -1,33 +0,0 @@
|
||||
import pyjsparser
|
||||
from .space import Space
|
||||
from . import fill_space
|
||||
from .byte_trans import ByteCodeGenerator
|
||||
from .code import Code
|
||||
from .simplex import *
|
||||
|
||||
|
||||
pyjsparser.parser.ENABLE_JS2PY_ERRORS = lambda msg: MakeError(u'SyntaxError', unicode(msg))
|
||||
|
||||
def get_js_bytecode(js):
|
||||
a = ByteCodeGenerator(Code())
|
||||
d = pyjsparser.parse(js)
|
||||
a.emit(d)
|
||||
return a.exe.tape
|
||||
|
||||
def eval_js_vm(js, debug=False):
|
||||
a = ByteCodeGenerator(Code(debug_mode=debug))
|
||||
s = Space()
|
||||
a.exe.space = s
|
||||
s.exe = a.exe
|
||||
|
||||
d = pyjsparser.parse(js)
|
||||
|
||||
a.emit(d)
|
||||
fill_space.fill_space(s, a)
|
||||
if debug:
|
||||
from pprint import pprint
|
||||
pprint(a.exe.tape)
|
||||
print()
|
||||
a.exe.compile()
|
||||
|
||||
return a.exe.run(a.exe.space.GlobalObj)
|
||||
@@ -1,160 +0,0 @@
|
||||
from __future__ import unicode_literals
|
||||
import six
|
||||
if six.PY3:
|
||||
basestring = str
|
||||
long = int
|
||||
xrange = range
|
||||
unicode = str
|
||||
|
||||
#Undefined
|
||||
class PyJsUndefined(object):
|
||||
TYPE = 'Undefined'
|
||||
Class = 'Undefined'
|
||||
|
||||
|
||||
undefined = PyJsUndefined()
|
||||
|
||||
|
||||
#Null
|
||||
class PyJsNull(object):
|
||||
TYPE = 'Null'
|
||||
Class = 'Null'
|
||||
|
||||
|
||||
null = PyJsNull()
|
||||
|
||||
Infinity = float('inf')
|
||||
NaN = float('nan')
|
||||
|
||||
UNDEFINED_TYPE = PyJsUndefined
|
||||
NULL_TYPE = PyJsNull
|
||||
STRING_TYPE = unicode if six.PY2 else str
|
||||
NUMBER_TYPE = float
|
||||
BOOLEAN_TYPE = bool
|
||||
|
||||
# exactly 5 simplexes!
|
||||
PRIMITIVES = frozenset(
|
||||
[UNDEFINED_TYPE, NULL_TYPE, STRING_TYPE, NUMBER_TYPE, BOOLEAN_TYPE])
|
||||
|
||||
TYPE_NAMES = {
|
||||
UNDEFINED_TYPE: 'Undefined',
|
||||
NULL_TYPE: 'Null',
|
||||
STRING_TYPE: 'String',
|
||||
NUMBER_TYPE: 'Number',
|
||||
BOOLEAN_TYPE: 'Boolean',
|
||||
}
|
||||
|
||||
|
||||
def Type(x):
|
||||
# Any -> Str
|
||||
return TYPE_NAMES.get(type(x), 'Object')
|
||||
|
||||
|
||||
def GetClass(x):
|
||||
# Any -> Str
|
||||
cand = TYPE_NAMES.get(type(x))
|
||||
if cand is None:
|
||||
return x.Class
|
||||
return cand
|
||||
|
||||
|
||||
def is_undefined(self):
|
||||
return self is undefined
|
||||
|
||||
|
||||
def is_null(self):
|
||||
return self is null
|
||||
|
||||
|
||||
def is_primitive(self):
|
||||
return type(self) in PRIMITIVES
|
||||
|
||||
|
||||
def is_object(self):
|
||||
return not is_primitive(self)
|
||||
|
||||
|
||||
def is_callable(self):
|
||||
return hasattr(self, 'call')
|
||||
|
||||
|
||||
def is_infinity(self):
|
||||
return self == Infinity or self == -Infinity
|
||||
|
||||
|
||||
def is_nan(self):
|
||||
return self != self # nan!=nan evaluates to True
|
||||
|
||||
|
||||
def is_finite(self):
|
||||
return not (is_nan(self) or is_infinity(self))
|
||||
|
||||
|
||||
class JsException(Exception):
|
||||
def __init__(self, typ=None, message=None, throw=None):
|
||||
if typ is None and message is None and throw is None:
|
||||
# it means its the trasnlator based error (old format), do nothing
|
||||
self._translator_based = True
|
||||
else:
|
||||
assert throw is None or (typ is None
|
||||
and message is None), (throw, typ,
|
||||
message)
|
||||
self._translator_based = False
|
||||
self.typ = typ
|
||||
self.message = message
|
||||
self.throw = throw
|
||||
|
||||
def get_thrown_value(self, space):
|
||||
if self.throw is not None:
|
||||
return self.throw
|
||||
else:
|
||||
return space.NewError(self.typ, self.message)
|
||||
|
||||
def __str__(self):
|
||||
if self._translator_based:
|
||||
if self.mes.Class == 'Error':
|
||||
return self.mes.callprop('toString').value
|
||||
else:
|
||||
return self.mes.to_string().value
|
||||
else:
|
||||
if self.throw is not None:
|
||||
from .conversions import to_string
|
||||
return to_string(self.throw)
|
||||
else:
|
||||
return self.typ + ': ' + self.message
|
||||
|
||||
|
||||
def MakeError(typ, message=u'no info', throw=None):
|
||||
return JsException(typ,
|
||||
unicode(message) if message is not None else message,
|
||||
throw)
|
||||
|
||||
|
||||
def value_from_js_exception(js_exception, space):
|
||||
if js_exception.throw is not None:
|
||||
return js_exception.throw
|
||||
else:
|
||||
return space.NewError(js_exception.typ, js_exception.message)
|
||||
|
||||
|
||||
def js_dtoa(number):
|
||||
if is_nan(number):
|
||||
return u'NaN'
|
||||
elif is_infinity(number):
|
||||
if number > 0:
|
||||
return u'Infinity'
|
||||
return u'-Infinity'
|
||||
elif number == 0.:
|
||||
return u'0'
|
||||
elif abs(number) < 1e-6 or abs(number) >= 1e21:
|
||||
frac, exponent = unicode(repr(float(number))).split('e')
|
||||
# Remove leading zeros from the exponent.
|
||||
exponent = int(exponent)
|
||||
return frac + ('e' if exponent < 0 else 'e+') + unicode(exponent)
|
||||
elif abs(number) < 1e-4: # python starts to return exp notation while we still want the prec
|
||||
frac, exponent = unicode(repr(float(number))).split('e-')
|
||||
base = u'0.' + u'0' * (int(exponent) - 1) + frac.lstrip('-').replace('.', '')
|
||||
return base if number > 0. else u'-' + base
|
||||
elif isinstance(number, long) or number.is_integer(): # dont print .0
|
||||
return unicode(int(number))
|
||||
return unicode(repr(number)) # python representation should be equivalent.
|
||||
@@ -1,92 +0,0 @@
|
||||
from .base import *
|
||||
from .simplex import *
|
||||
|
||||
|
||||
class Space(object):
|
||||
def __init__(self):
|
||||
self.GlobalObj = None
|
||||
self.ctx = None
|
||||
self.byte_generator = None
|
||||
|
||||
self.Number = None
|
||||
self.String = None
|
||||
self.Boolean = None
|
||||
self.RegExp = None
|
||||
self.Object = None
|
||||
self.Array = None
|
||||
self.Function = None
|
||||
|
||||
self.BooleanPrototype = None
|
||||
self.NumberPrototype = None
|
||||
self.StringPrototype = None
|
||||
|
||||
self.FunctionPrototype = None
|
||||
self.ArrayPrototype = None
|
||||
self.RegExpPrototype = None
|
||||
self.DatePrototype = None
|
||||
self.ObjectPrototype = None
|
||||
|
||||
self.ErrorPrototype = None
|
||||
self.EvalErrorPrototype = None
|
||||
self.RangeErrorPrototype = None
|
||||
self.ReferenceErrorPrototype = None
|
||||
self.SyntaxErrorPrototype = None
|
||||
self.TypeErrorPrototype = None
|
||||
self.URIErrorPrototype = None
|
||||
|
||||
self.interpreter = None
|
||||
|
||||
@property
|
||||
def ERROR_TYPES(self):
|
||||
return {
|
||||
'Error': self.ErrorPrototype,
|
||||
'EvalError': self.EvalErrorPrototype,
|
||||
'RangeError': self.RangeErrorPrototype,
|
||||
'ReferenceError': self.ReferenceErrorPrototype,
|
||||
'SyntaxError': self.SyntaxErrorPrototype,
|
||||
'TypeError': self.TypeErrorPrototype,
|
||||
'URIError': self.URIErrorPrototype,
|
||||
}
|
||||
|
||||
def get_global_environment(self):
|
||||
return self.GlobalCtx.variable_environment()
|
||||
|
||||
def NewObject(self):
|
||||
return PyJsObject(self.ObjectPrototype)
|
||||
|
||||
def NewFunction(self, code, ctx, params, name, is_declaration,
|
||||
definitions):
|
||||
return PyJsFunction(
|
||||
code,
|
||||
ctx,
|
||||
params,
|
||||
name,
|
||||
self,
|
||||
is_declaration,
|
||||
definitions,
|
||||
prototype=self.FunctionPrototype)
|
||||
|
||||
def NewDate(self, value):
|
||||
return PyJsDate(value, self.DatePrototype)
|
||||
|
||||
def NewArray(self, length=0):
|
||||
return PyJsArray(length, self.ArrayPrototype)
|
||||
|
||||
def NewError(self, typ, message):
|
||||
return PyJsError(message, self.ERROR_TYPES[typ])
|
||||
|
||||
def NewRegExp(self, body, flags):
|
||||
return PyJsRegExp(body, flags, self.RegExpPrototype)
|
||||
|
||||
def ConstructArray(self, py_arr):
|
||||
''' note py_arr elems are NOT converted to PyJs types!'''
|
||||
arr = self.NewArray(len(py_arr))
|
||||
arr._init(py_arr)
|
||||
return arr
|
||||
|
||||
def ConstructObject(self, py_obj):
|
||||
''' note py_obj items are NOT converted to PyJs types! '''
|
||||
obj = self.NewObject()
|
||||
for k, v in py_obj.items():
|
||||
obj.put(unicode(k), v)
|
||||
return obj
|
||||
@@ -1,62 +0,0 @@
|
||||
from timeit import timeit
|
||||
from collections import namedtuple
|
||||
from array import array
|
||||
from itertools import izip
|
||||
from collections import deque
|
||||
|
||||
|
||||
class Y(object):
|
||||
UUU = 88
|
||||
|
||||
def __init__(self, x):
|
||||
self.x = x
|
||||
|
||||
def s(self, x):
|
||||
return self.x + 1
|
||||
|
||||
|
||||
class X(Y):
|
||||
A = 10
|
||||
B = 2
|
||||
C = 4
|
||||
D = 9
|
||||
|
||||
def __init__(self, x):
|
||||
self.x = x
|
||||
self.stack = []
|
||||
self.par = super(X, self)
|
||||
|
||||
def s(self, x):
|
||||
pass
|
||||
|
||||
def __add__(self, other):
|
||||
return self.x + other.x
|
||||
|
||||
def another(self):
|
||||
return Y.s(self, 1)
|
||||
|
||||
def yet_another(self):
|
||||
return self.par.s(1)
|
||||
|
||||
|
||||
def add(a, b):
|
||||
return a.x + b.x
|
||||
|
||||
|
||||
t = []
|
||||
|
||||
Type = None
|
||||
try:
|
||||
print timeit(
|
||||
"""
|
||||
|
||||
t.append(4)
|
||||
t.pop()
|
||||
|
||||
|
||||
|
||||
""",
|
||||
"from __main__ import X,Y,namedtuple,array,t,add,Type, izip",
|
||||
number=1000000)
|
||||
except:
|
||||
raise
|
||||
@@ -1,35 +0,0 @@
|
||||
import six
|
||||
if six.PY3:
|
||||
basestring = str
|
||||
long = int
|
||||
xrange = range
|
||||
unicode = str
|
||||
|
||||
def to_key(literal_or_identifier):
|
||||
''' returns string representation of this object'''
|
||||
if literal_or_identifier['type'] == 'Identifier':
|
||||
return literal_or_identifier['name']
|
||||
elif literal_or_identifier['type'] == 'Literal':
|
||||
k = literal_or_identifier['value']
|
||||
if isinstance(k, float):
|
||||
return unicode(float_repr(k))
|
||||
elif 'regex' in literal_or_identifier:
|
||||
return compose_regex(k)
|
||||
elif isinstance(k, bool):
|
||||
return u'true' if k else u'false'
|
||||
elif k is None:
|
||||
return u'null'
|
||||
else:
|
||||
return unicode(k)
|
||||
|
||||
|
||||
def compose_regex(val):
|
||||
reg, flags = val
|
||||
# reg = REGEXP_CONVERTER._unescape_string(reg)
|
||||
return u'/%s/%s' % (reg, flags)
|
||||
|
||||
|
||||
def float_repr(f):
|
||||
if int(f) == f:
|
||||
return unicode(repr(int(f)))
|
||||
return unicode(repr(f))
|
||||
Reference in New Issue
Block a user