1
0
Fork 0
mirror of https://github.com/morpheus65535/bazarr synced 2024-12-25 09:12:38 +00:00
bazarr/libs/js2py/base.py

3273 lines
113 KiB
Python

'''Most important file in Js2Py implementation: PyJs class - father of all PyJs objects'''
from copy import copy
import re
from .translators.friendly_nodes import REGEXP_CONVERTER
from .utils.injector import fix_js_args
from types import FunctionType, ModuleType, GeneratorType, BuiltinFunctionType, MethodType, BuiltinMethodType
from math import floor, log10
import traceback
try:
import numpy
NUMPY_AVAILABLE = True
except:
NUMPY_AVAILABLE = False
# python 3 support
import six
if six.PY3:
basestring = str
long = int
xrange = range
unicode = str
def str_repr(s):
if six.PY2:
return repr(s.encode('utf-8'))
else:
return repr(s)
def MakeError(name, message):
"""Returns PyJsException with PyJsError inside"""
return JsToPyException(ERRORS[name](Js(message)))
def to_python(val):
if not isinstance(val, PyJs):
return val
if isinstance(val, PyJsUndefined) or isinstance(val, PyJsNull):
return None
elif isinstance(val, PyJsNumber):
# this can be either float or long/int better to assume its int/long when a whole number...
v = val.value
try:
i = int(v) if v == v else v # nan...
return v if i != v else i
except:
return v
elif isinstance(val, (PyJsString, PyJsBoolean)):
return val.value
elif isinstance(val, PyObjectWrapper):
return val.__dict__['obj']
elif isinstance(val, PyJsArray) and val.CONVERT_TO_PY_PRIMITIVES:
return to_list(val)
elif isinstance(val, PyJsObject) and val.CONVERT_TO_PY_PRIMITIVES:
return to_dict(val)
else:
return JsObjectWrapper(val)
def to_dict(js_obj,
known=None): # fixed recursion error in self referencing objects
res = {}
if known is None:
known = {}
if js_obj in known:
return known[js_obj]
known[js_obj] = res
for k in js_obj:
name = k.value
input = js_obj.get(name)
output = to_python(input)
if isinstance(output, JsObjectWrapper):
if output._obj.Class == 'Object':
output = to_dict(output._obj, known)
known[input] = output
elif output._obj.Class in [
'Array', 'Int8Array', 'Uint8Array', 'Uint8ClampedArray',
'Int16Array', 'Uint16Array', 'Int32Array', 'Uint32Array',
'Float32Array', 'Float64Array'
]:
output = to_list(output._obj)
known[input] = output
res[name] = output
return res
def to_list(js_obj, known=None):
res = len(js_obj) * [None]
if known is None:
known = {}
if js_obj in known:
return known[js_obj]
known[js_obj] = res
for k in js_obj:
try:
name = int(k.value)
except:
continue
input = js_obj.get(str(name))
output = to_python(input)
if isinstance(output, JsObjectWrapper):
if output._obj.Class in [
'Array', 'Int8Array', 'Uint8Array', 'Uint8ClampedArray',
'Int16Array', 'Uint16Array', 'Int32Array', 'Uint32Array',
'Float32Array', 'Float64Array', 'Arguments'
]:
output = to_list(output._obj, known)
known[input] = output
elif output._obj.Class in ['Object']:
output = to_dict(output._obj)
known[input] = output
res[name] = output
return res
def HJs(val):
if hasattr(val, '__call__'): #
@Js
def PyWrapper(this, arguments, var=None):
args = tuple(to_python(e) for e in arguments.to_list())
try:
py_res = val.__call__(*args)
except Exception as e:
message = 'your Python function failed! '
try:
message += e.message
except:
pass
raise MakeError('Error', message)
return py_wrap(py_res)
try:
PyWrapper.func_name = val.__name__
except:
pass
return PyWrapper
if isinstance(val, tuple):
val = list(val)
return Js(val)
def Js(val, Clamped=False):
'''Converts Py type to PyJs type'''
if isinstance(val, PyJs):
return val
elif val is None:
return undefined
elif isinstance(val, basestring):
return PyJsString(val, StringPrototype)
elif isinstance(val, bool):
return true if val else false
elif isinstance(val, float) or isinstance(val, int) or isinstance(
val, long) or (NUMPY_AVAILABLE and isinstance(
val,
(numpy.int8, numpy.uint8, numpy.int16, numpy.uint16,
numpy.int32, numpy.uint32, numpy.float32, numpy.float64))):
# This is supposed to speed things up. may not be the case
if val in NUM_BANK:
return NUM_BANK[val]
return PyJsNumber(float(val), NumberPrototype)
elif isinstance(val, FunctionType):
return PyJsFunction(val, FunctionPrototype)
#elif isinstance(val, ModuleType):
# mod = {}
# for name in dir(val):
# value = getattr(val, name)
# if isinstance(value, ModuleType):
# continue # prevent recursive module conversion
# try:
# jsval = HJs(value)
# except RuntimeError:
# print 'Could not convert %s to PyJs object!' % name
# continue
# mod[name] = jsval
# return Js(mod)
#elif isintance(val, ClassType):
elif isinstance(val, dict): # convert to object
temp = PyJsObject({}, ObjectPrototype)
for k, v in six.iteritems(val):
temp.put(Js(k), Js(v))
return temp
elif isinstance(val, (list, tuple)): #Convert to array
return PyJsArray(val, ArrayPrototype)
# convert to typedarray
elif isinstance(val, JsObjectWrapper):
return val.__dict__['_obj']
elif NUMPY_AVAILABLE and isinstance(val, numpy.ndarray):
if val.dtype == numpy.int8:
return PyJsInt8Array(val, Int8ArrayPrototype)
elif val.dtype == numpy.uint8 and not Clamped:
return PyJsUint8Array(val, Uint8ArrayPrototype)
elif val.dtype == numpy.uint8 and Clamped:
return PyJsUint8ClampedArray(val, Uint8ClampedArrayPrototype)
elif val.dtype == numpy.int16:
return PyJsInt16Array(val, Int16ArrayPrototype)
elif val.dtype == numpy.uint16:
return PyJsUint16Array(val, Uint16ArrayPrototype)
elif val.dtype == numpy.int32:
return PyJsInt32Array(val, Int32ArrayPrototype)
elif val.dtype == numpy.uint32:
return PyJsUint16Array(val, Uint32ArrayPrototype)
elif val.dtype == numpy.float32:
return PyJsFloat32Array(val, Float32ArrayPrototype)
elif val.dtype == numpy.float64:
return PyJsFloat64Array(val, Float64ArrayPrototype)
else: # try to convert to js object
return py_wrap(val)
#raise RuntimeError('Cant convert python type to js (%s)' % repr(val))
#try:
# obj = {}
# for name in dir(val):
# if name.startswith('_'): #dont wrap attrs that start with _
# continue
# value = getattr(val, name)
# import types
# if not isinstance(value, (FunctionType, BuiltinFunctionType, MethodType, BuiltinMethodType,
# dict, int, basestring, bool, float, long, list, tuple)):
# continue
# obj[name] = HJs(value)
# return Js(obj)
#except:
# raise RuntimeError('Cant convert python type to js (%s)' % repr(val))
def Type(val):
try:
return val.TYPE
except:
raise RuntimeError('Invalid type: ' + str(val))
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):
return desc and not (is_data_descriptor(desc)
or is_accessor_descriptor(desc))
##############################################################################
class PyJs(object):
PRIMITIVES = frozenset(
['String', 'Number', 'Boolean', 'Undefined', 'Null'])
TYPE = 'Object'
Class = None
extensible = True
prototype = None
own = {}
GlobalObject = None
IS_CHILD_SCOPE = False
CONVERT_TO_PY_PRIMITIVES = False
value = None
buff = None
def __init__(self, value=None, prototype=None, extensible=False):
'''Constructor for Number String and Boolean'''
# I dont think this is needed anymore
# if self.Class=='String' and not isinstance(value, basestring):
# raise TypeError
# if self.Class=='Number':
# if not isinstance(value, float):
# if not (isinstance(value, int) or isinstance(value, long)):
# raise TypeError
# value = float(value)
# if self.Class=='Boolean' and not isinstance(value, bool):
# raise TypeError
self.value = value
self.extensible = extensible
self.prototype = prototype
self.own = {}
self.buff = None
def is_undefined(self):
return self.Class == 'Undefined'
def is_null(self):
return self.Class == 'Null'
def is_primitive(self):
return self.TYPE in self.PRIMITIVES
def is_object(self):
return not self.is_primitive()
def _type(self):
return Type(self)
def is_callable(self):
return hasattr(self, 'call')
def get_own_property(self, prop):
return self.own.get(prop)
def get_property(self, prop):
cand = self.get_own_property(prop)
if cand:
return cand
if self.prototype is not None:
return self.prototype.get_property(prop)
def update_array(self):
for i in range(self.get('length').to_uint32()):
self.put(str(i), Js(self.buff[i]))
def get(self, prop): #external use!
#prop = prop.value
if self.Class == 'Undefined' or self.Class == 'Null':
raise MakeError('TypeError',
'Undefined and null dont have properties!')
if not isinstance(prop, basestring):
prop = prop.to_string().value
if not isinstance(prop, basestring): raise RuntimeError('Bug')
if NUMPY_AVAILABLE and prop.isdigit():
if isinstance(self.buff, numpy.ndarray):
self.update_array()
cand = self.get_property(prop)
if cand is None:
return Js(None)
if is_data_descriptor(cand):
return cand['value']
if cand['get'].is_undefined():
return cand['get']
return cand['get'].call(self)
def can_put(self, prop): #to check
desc = self.get_own_property(prop)
if desc: #if we have this property
if is_accessor_descriptor(desc):
return desc['set'].is_callable(
) # Check if setter method is defined
else: #data desc
return desc['writable']
if self.prototype is not None:
return self.extensible
inherited = self.get_property(prop)
if inherited is None:
return self.extensible
if is_accessor_descriptor(inherited):
return not inherited['set'].is_undefined()
elif self.extensible:
return inherited['writable']
return False
def put(self, prop, val, op=None): #external use!
'''Just like in js: self.prop op= val
for example when op is '+' it will be self.prop+=val
op can be either None for simple assignment or one of:
* / % + - << >> & ^ |'''
if self.Class == 'Undefined' or self.Class == 'Null':
raise MakeError('TypeError',
'Undefined and null dont have properties!')
if not isinstance(prop, basestring):
prop = prop.to_string().value
if NUMPY_AVAILABLE and prop.isdigit():
if self.Class == 'Int8Array':
val = Js(numpy.int8(val.to_number().value))
elif self.Class == 'Uint8Array':
val = Js(numpy.uint8(val.to_number().value))
elif self.Class == 'Uint8ClampedArray':
if val < Js(numpy.uint8(0)):
val = Js(numpy.uint8(0))
elif val > Js(numpy.uint8(255)):
val = Js(numpy.uint8(255))
else:
val = Js(numpy.uint8(val.to_number().value))
elif self.Class == 'Int16Array':
val = Js(numpy.int16(val.to_number().value))
elif self.Class == 'Uint16Array':
val = Js(numpy.uint16(val.to_number().value))
elif self.Class == 'Int32Array':
val = Js(numpy.int32(val.to_number().value))
elif self.Class == 'Uint32Array':
val = Js(numpy.uint32(val.to_number().value))
elif self.Class == 'Float32Array':
val = Js(numpy.float32(val.to_number().value))
elif self.Class == 'Float64Array':
val = Js(numpy.float64(val.to_number().value))
if isinstance(self.buff, numpy.ndarray):
self.buff[int(prop)] = int(val.to_number().value)
#we need to set the value to the incremented one
if op is not None:
val = getattr(self.get(prop), OP_METHODS[op])(val)
if not self.can_put(prop):
return val
own_desc = self.get_own_property(prop)
if is_data_descriptor(own_desc):
if self.Class in [
'Array', 'Int8Array', 'Uint8Array', 'Uint8ClampedArray',
'Int16Array', 'Uint16Array', 'Int32Array', 'Uint32Array',
'Float32Array', 'Float64Array'
]:
self.define_own_property(prop, {'value': val})
else:
self.own[prop]['value'] = val
return val
desc = self.get_property(prop)
if is_accessor_descriptor(desc):
desc['set'].call(self, (val, ))
else:
new = {
'value': val,
'writable': True,
'configurable': True,
'enumerable': True
}
if self.Class in [
'Array', 'Int8Array', 'Uint8Array', 'Uint8ClampedArray',
'Int16Array', 'Uint16Array', 'Int32Array', 'Uint32Array',
'Float32Array', 'Float64Array'
]:
self.define_own_property(prop, new)
else:
self.own[prop] = new
return val
def has_property(self, prop):
return self.get_property(prop) is not None
def delete(self, prop):
if not isinstance(prop, basestring):
prop = prop.to_string().value
desc = self.get_own_property(prop)
if desc is None:
return Js(True)
if desc['configurable']:
del self.own[prop]
return Js(True)
return Js(False)
def default_value(
self, hint=None
): # made a mistake at the very early stage and made it to prefer string... caused lots! of problems
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 method.is_callable():
cand = method.call(self)
if cand.is_primitive():
return cand
raise MakeError('TypeError',
'Cannot convert object to primitive value')
def define_own_property(self, prop,
desc): #Internal use only. External through Object
# 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 property
if not extensible:
return False
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
if not desc or desc == current: #We dont need to change anything.
return True
configurable = current['configurable']
if not configurable: #Prevent changing configurable or enumerable
if desc.get('configurable'):
return False
if 'enumerable' in desc and desc['enumerable'] != current[
'enumerable']:
return False
if is_generic_descriptor(desc):
pass
elif is_data_descriptor(current) != is_data_descriptor(desc):
if not configurable:
return False
if is_data_descriptor(current):
del current['value']
del current['writable']
current['set'] = undefined #undefined
current['get'] = undefined #undefined
else:
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'):
return False
if not current['writable'] and 'value' in desc and current[
'value'] != desc['value']:
return False
elif is_accessor_descriptor(current) and is_accessor_descriptor(desc):
if not configurable:
if 'set' in desc and desc['set'] is not current['set']:
return False
if 'get' in desc and desc['get'] is not current['get']:
return False
current.update(desc)
return True
#these methods will work only for Number class
def is_infinity(self):
assert self.Class == 'Number'
return self.value == float('inf') or self.value == -float('inf')
def is_nan(self):
assert self.Class == 'Number'
return self.value != self.value #nan!=nan evaluates to true
def is_finite(self):
return not (self.is_nan() or self.is_infinity())
#Type Conversions. to_type. All must return pyjs subclass instance
def to_primitive(self, hint=None):
if self.is_primitive():
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' or typ == 'String': #false only for 0, '' and NaN
return Js(bool(
self.value
and self.value == self.value)) # test for nan (nan -> flase)
else: #object - always true
return true
def to_number(self):
typ = Type(self)
if typ == 'Null': #null is 0
return Js(0)
elif typ == 'Undefined': # undefined is NaN
return NaN
elif typ == 'Boolean': # 1 for true 0 for false
return Js(int(self.value))
elif typ == 'Number': # or self.Class=='Number': # no need to convert
return self
elif typ == 'String':
s = self.value.strip() #Strip white space
if not s: # '' is simply 0
return Js(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 Js(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 Js(sign * float('inf'))
try: #decimal try
num = sign * float(s) # Converted
except ValueError:
return NaN # could not convert to decimal > return NaN
return Js(num)
else: #object - most likely it will be NaN.
return self.to_primitive('Number').to_number()
def to_string(self):
typ = Type(self)
if typ == 'Null':
return Js('null')
elif typ == 'Undefined':
return Js('undefined')
elif typ == 'Boolean':
return Js('true') if self.value else Js('false')
elif typ == 'Number': #or self.Class=='Number':
return Js(unicode(js_dtoa(self.value)))
elif typ == 'String':
return self
else: #object
return self.to_primitive('String').to_string()
def to_object(self):
typ = self.TYPE
if typ == 'Null' or typ == 'Undefined':
raise MakeError('TypeError',
'undefined or null can\'t be converted to object')
elif typ == 'Boolean': # Unsure here... todo repair here
return Boolean.create(self)
elif typ == 'Number': #?
return Number.create(self)
elif typ == 'String': #?
return String.create(self)
else: #object
return self
def to_int32(self):
num = self.to_number()
if num.is_nan() or num.is_infinity():
return 0
int32 = int(num.value) % 2**32
return int(int32 - 2**32 if int32 >= 2**31 else int32)
def strict_equality_comparison(self, other):
return PyJsStrictEq(self, other)
def cok(self):
"""Check object coercible"""
if self.Class in ('Undefined', 'Null'):
raise MakeError('TypeError',
'undefined or null can\'t be converted to object')
def to_int(self):
num = self.to_number()
if num.is_nan():
return 0
elif num.is_infinity():
return 10**20 if num.value > 0 else -10**20
return int(num.value)
def to_uint32(self):
num = self.to_number()
if num.is_nan() or num.is_infinity():
return 0
return int(num.value) % 2**32
def to_uint16(self):
num = self.to_number()
if num.is_nan() or num.is_infinity():
return 0
return int(num.value) % 2**16
def to_int16(self):
num = self.to_number()
if num.is_nan() or num.is_infinity():
return 0
int16 = int(num.value) % 2**16
return int(int16 - 2**16 if int16 >= 2**15 else int16)
def same_as(self, other):
typ = Type(self)
if typ != other.Class:
return False
if typ == 'Undefined' or typ == 'Null':
return True
if typ == 'Boolean' or typ == 'Number' or typ == 'String':
return self.value == other.value
else: #object
return self is other #Id compare.
#Not to be used by translation (only internal use)
def __getitem__(self, item):
return self.get(
str(item) if not isinstance(item, PyJs) else item.to_string().
value)
def __setitem__(self, item, value):
self.put(
str(item) if not isinstance(item, PyJs) else
item.to_string().value, Js(value))
def __len__(self):
try:
return self.get('length').to_uint32()
except:
raise TypeError(
'This object (%s) does not have length property' % self.Class)
#Oprators-------------
#Unary, other will be implemented as functions. Increments and decrements
# will be methods of Number class
def __neg__(self): #-u
return Js(-self.to_number().value)
def __pos__(self): #+u
return self.to_number()
def __invert__(self): #~u
return Js(Js(~self.to_int32()).to_int32())
def neg(self): # !u cant do 'not u' :(
return Js(not self.to_boolean().value)
def __nonzero__(self):
return self.to_boolean().value
def __bool__(self):
return self.to_boolean().value
def typeof(self):
if self.is_callable():
return Js('function')
typ = Type(self).lower()
if typ == 'null':
typ = 'object'
return Js(typ)
#Bitwise operators
# <<, >>, &, ^, |
# <<
def __lshift__(self, other):
lnum = self.to_int32()
rnum = other.to_uint32()
shiftCount = rnum & 0x1F
return Js(Js(lnum << shiftCount).to_int32())
# >>
def __rshift__(self, other):
lnum = self.to_int32()
rnum = other.to_uint32()
shiftCount = rnum & 0x1F
return Js(Js(lnum >> shiftCount).to_int32())
# >>>
def pyjs_bshift(self, other):
lnum = self.to_uint32()
rnum = other.to_uint32()
shiftCount = rnum & 0x1F
return Js(Js(lnum >> shiftCount).to_uint32())
# &
def __and__(self, other):
lnum = self.to_int32()
rnum = other.to_int32()
return Js(Js(lnum & rnum).to_int32())
# ^
def __xor__(self, other):
lnum = self.to_int32()
rnum = other.to_int32()
return Js(Js(lnum ^ rnum).to_int32())
# |
def __or__(self, other):
lnum = self.to_int32()
rnum = other.to_int32()
return Js(Js(lnum | rnum).to_int32())
# Additive operators
# + and - are implemented here
# +
def __add__(self, other):
a = self.to_primitive()
b = other.to_primitive()
if a.TYPE == 'String' or b.TYPE == 'String':
return Js(a.to_string().value + b.to_string().value)
a = a.to_number()
b = b.to_number()
return Js(a.value + b.value)
# -
def __sub__(self, other):
return Js(self.to_number().value - other.to_number().value)
#Multiplicative operators
# *, / and % are implemented here
# *
def __mul__(self, other):
return Js(self.to_number().value * other.to_number().value)
# /
def __div__(self, other):
a = self.to_number().value
b = other.to_number().value
if b:
return Js(a / b)
if not a or a != a:
return NaN
return Infinity if a > 0 else -Infinity
# %
def __mod__(self, other):
a = self.to_number().value
b = other.to_number().value
if abs(a) == float('inf') or not b:
return NaN
if abs(b) == float('inf'):
return Js(a)
pyres = Js(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.value > 0:
pyres.value -= abs(b)
elif a > 0 and pyres.value < 0:
pyres.value += abs(b)
return Js(pyres)
#Comparisons (I dont implement === and !== here, these
# will be implemented as external functions later)
# <, <=, !=, ==, >=, > are implemented here.
def abstract_relational_comparison(self, other, self_first=True):
''' 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 = self.to_primitive('Number')
py = other.to_primitive('Number')
if not self_first: #reverse order
px, py = py, px
if not (px.Class == 'String' and py.Class == 'String'):
px, py = px.to_number(), py.to_number()
if px.is_nan() or py.is_nan():
return undefined
return Js(px.value < py.value) # same cmp algorithm
else:
# I am pretty sure that python has the same
# string cmp algorithm but I have to confirm it
return Js(px.value < py.value)
#<
def __lt__(self, other):
res = self.abstract_relational_comparison(other, True)
if res.is_undefined():
return false
return res
#<=
def __le__(self, other):
res = self.abstract_relational_comparison(other, False)
if res.is_undefined():
return false
return res.neg()
#>=
def __ge__(self, other):
res = self.abstract_relational_comparison(other, True)
if res.is_undefined():
return false
return res.neg()
#>
def __gt__(self, other):
res = self.abstract_relational_comparison(other, False)
if res.is_undefined():
return false
return res
def abstract_equality_comparison(self, other):
''' returns the result of JS == compare.
result is PyJs type: bool'''
tx, ty = self.TYPE, other.TYPE
if tx == ty:
if tx == 'Undefined' or tx == 'Null':
return true
if tx == 'Number' or tx == 'String' or tx == 'Boolean':
return Js(self.value == other.value)
return Js(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 self.abstract_equality_comparison(other.to_number())
elif tx == 'String' and ty == 'Number':
return self.to_number().abstract_equality_comparison(other)
elif tx == 'Boolean':
return self.to_number().abstract_equality_comparison(other)
elif ty == 'Boolean':
return self.abstract_equality_comparison(other.to_number())
elif (tx == 'String' or tx == 'Number') and other.is_object():
return self.abstract_equality_comparison(other.to_primitive())
elif (ty == 'String' or ty == 'Number') and self.is_object():
return self.to_primitive().abstract_equality_comparison(other)
else:
return false
#==
def __eq__(self, other):
return self.abstract_equality_comparison(other)
#!=
def __ne__(self, other):
return self.abstract_equality_comparison(other).neg()
#Other methods (instanceof)
def instanceof(self, other):
'''checks if self is instance of other'''
if not hasattr(other, 'has_instance'):
return false
return other.has_instance(self)
#iteration
def __iter__(self):
#Returns a generator of all own enumerable properties
# since the size od self.own can change we need to use different method of iteration.
# SLOW! New items will NOT show up.
returned = {}
if not self.IS_CHILD_SCOPE:
cands = sorted(
name for name in self.own if self.own[name]['enumerable'])
else:
cands = sorted(name for name in self.own)
for cand in cands:
check = self.own.get(cand)
if check and check['enumerable']:
yield Js(cand)
def contains(self, other):
if not self.is_object():
raise MakeError(
'TypeError',
"You can\'t use 'in' operator to search in non-objects")
return Js(self.has_property(other.to_string().value))
#Other Special methods
def __call__(self, *args):
'''Call a property prop as a function (this will be global object).
NOTE: dont pass this and arguments here, these will be added
automatically!'''
if not self.is_callable():
raise MakeError('TypeError',
'%s is not a function' % self.typeof())
return self.call(self.GlobalObject, args)
def create(self, *args):
'''Generally not a constructor, raise an error'''
raise MakeError('TypeError', '%s is not a constructor' % self.Class)
def __unicode__(self):
return self.to_string().value
def __repr__(self):
if self.Class == 'Object':
res = []
for e in self:
res.append(str_repr(e.value) + ': ' + str_repr(self.get(e)))
return '{%s}' % ', '.join(res)
elif self.Class == 'String':
return str_repr(self.value)
elif self.Class in [
'Array', 'Int8Array', 'Uint8Array', 'Uint8ClampedArray',
'Int16Array', 'Uint16Array', 'Int32Array', 'Uint32Array',
'Float32Array', 'Float64Array'
]:
res = []
for e in self:
res.append(repr(self.get(e)))
return '[%s]' % ', '.join(res)
else:
val = str_repr(self.to_string().value)
return val
def _fuck_python3(
self
): # hack to make object hashable in python 3 (__eq__ causes problems)
return object.__hash__(self)
def callprop(self, prop, *args):
'''Call a property prop as a method (this will be self).
NOTE: dont pass this and arguments here, these will be added
automatically!'''
if not isinstance(prop, basestring):
prop = prop.to_string().value
cand = self.get(prop)
if not cand.is_callable():
raise MakeError('TypeError',
'%s is not a function' % cand.typeof())
return cand.call(self, args)
def to_python(self):
"""returns equivalent python object.
for example if this object is javascript array then this method will return equivalent python array"""
return to_python(self)
def to_py(self):
"""returns equivalent python object.
for example if this object is javascript array then this method will return equivalent python array"""
return self.to_python()
if six.PY3:
PyJs.__hash__ = PyJs._fuck_python3
PyJs.__truediv__ = PyJs.__div__
#Define some more classes representing operators:
def PyJsStrictEq(a, b):
'''a===b'''
tx, ty = Type(a), Type(b)
if tx != ty:
return false
if tx == 'Undefined' or tx == 'Null':
return true
if a.is_primitive(): #string bool and number case
return Js(a.value == b.value)
if a.Class == b.Class == 'PyObjectWrapper':
return Js(a.obj == b.obj)
return Js(a is b) # object comparison
def PyJsStrictNeq(a, b):
''' a!==b'''
return PyJsStrictEq(a, b).neg()
def PyJsBshift(a, b):
"""a>>>b"""
return a.pyjs_bshift(b)
def PyJsComma(a, b):
return b
from .internals.simplex import JsException as PyJsException, js_dtoa
import pyjsparser
pyjsparser.parser.ENABLE_JS2PY_ERRORS = lambda msg: MakeError('SyntaxError', msg)
class PyJsSwitchException(Exception):
pass
PyJs.MakeError = staticmethod(MakeError)
def JsToPyException(js):
temp = PyJsException()
temp.mes = js
return temp
def PyExceptionToJs(py):
return py.mes
#Scope class it will hold all the variables accessible to user
class Scope(PyJs):
Class = 'global'
extensible = True
IS_CHILD_SCOPE = True
# 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, closure=None):
"""Doc"""
self.prototype = closure
if closure 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
})
else:
# not global, less powerful but faster closure.
self.own = scope # simple dictionary which maps name directly to js object.
def register(self, lval):
# registered keeps only global registered variables
if self.prototype is None:
# define in global scope
if lval in self.own:
self.own[lval]['configurable'] = False
else:
self.define_own_property(
lval, {
'value': undefined,
'configurable': False,
'writable': True,
'enumerable': True
})
elif lval not in self.own:
# define in local scope since it has not been defined yet
self.own[lval] = undefined # default value
def registers(self, lvals):
"""register multiple variables"""
for lval in lvals:
self.register(lval)
def put(self, lval, val, op=None):
if self.prototype is None:
# global scope put, simple
return PyJs.put(self, lval, val, op)
else:
# trying to put in local scope
# we dont know yet in which scope we should place this var
if lval in self.own:
if op: # increment operation
val = getattr(self.own[lval], OP_METHODS[op])(val)
self.own[lval] = 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(lval, val, op)
def force_own_put(self, prop, val, configurable=False):
if self.prototype is None: # global scope
self.own[prop] = {
'value': val,
'writable': True,
'enumerable': True,
'configurable': configurable
}
else:
self.own[prop] = val
def get(self, prop, throw=True):
#note prop is always a Py String
if not isinstance(prop, basestring):
prop = prop.to_string().value
if self.prototype is not None:
# fast local scope
cand = self.own.get(prop)
if cand is None:
return self.prototype.get(prop, throw)
return cand
# slow, global scope
if prop not in self.own:
if throw:
raise MakeError('ReferenceError', '%s is not defined' % prop)
return undefined
return PyJs.get(self, prop)
def delete(self, lval):
if self.prototype is not None:
if lval in self.own:
return false
return self.prototype.delete(lval)
# we are in global scope here. Must exist and be configurable to delete
if lval not in self.own:
# this lval does not exist, why do you want to delete it???
return true
if self.own[lval]['configurable']:
del self.own[lval]
return true
# not configurable, cant delete
return false
def pyimport(self, name, module):
self.register(name)
self.put(name, py_wrap(module))
def __repr__(self):
return u'[Object Global]'
def to_python(self):
return to_python(self)
class This(Scope):
IS_CHILD_SCOPE = False
def get(self, prop, throw=False):
return Scope.get(self, prop, throw)
class JsObjectWrapper(object):
def __init__(self, obj):
self.__dict__['_obj'] = obj
def __call__(self, *args):
args = tuple(Js(e) for e in args)
if '_prop_of' in self.__dict__:
parent, meth = self.__dict__['_prop_of']
return to_python(parent._obj.callprop(meth, *args))
return to_python(self._obj(*args))
def __getattr__(self, item):
if item == 'new' and self._obj.is_callable():
# return instance initializer
def PyJsInstanceInit(*args):
args = tuple(Js(e) for e in args)
return self._obj.create(*args).to_python()
return PyJsInstanceInit
cand = to_python(self._obj.get(str(item)))
# handling method calling... obj.meth(). Value of this in meth should be self
if isinstance(cand, self.__class__):
cand.__dict__['_prop_of'] = self, str(item)
return cand
def __setattr__(self, item, value):
self._obj.put(str(item), Js(value))
def __getitem__(self, item):
cand = to_python(self._obj.get(str(item)))
if isinstance(cand, self.__class__):
cand.__dict__['_prop_of'] = self, str(item)
return cand
def __setitem__(self, item, value):
self._obj.put(str(item), Js(value))
def __iter__(self):
if self._obj.Class in [
'Array', 'Int8Array', 'Uint8Array', 'Uint8ClampedArray',
'Int16Array', 'Uint16Array', 'Int32Array', 'Uint32Array',
'Float32Array', 'Float64Array'
]:
return iter(self.to_list())
elif self._obj.Class == 'Object':
return iter(self.to_dict())
else:
raise MakeError('TypeError',
'%s is not iterable in Python' % self._obj.Class)
def __repr__(self):
if self._obj.is_primitive() or self._obj.is_callable():
return repr(self._obj)
elif self._obj.Class in ('Array', 'Int8Array', 'Uint8Array',
'Uint8ClampedArray', 'Int16Array',
'Uint16Array', 'Int32Array', 'Uint32Array',
'Float32Array', 'Float64Array', 'Arguments'):
return repr(self.to_list())
return repr(self.to_dict())
def __len__(self):
return len(self._obj)
def __nonzero__(self):
return bool(self._obj)
def __bool__(self):
return bool(self._obj)
def to_dict(self):
return to_dict(self.__dict__['_obj'])
def to_list(self):
return to_list(self.__dict__['_obj'])
class PyObjectWrapper(PyJs):
Class = 'PyObjectWrapper'
def __init__(self, obj):
self.obj = obj
def get(self, prop):
if not isinstance(prop, basestring):
prop = prop.to_string().value
try:
if prop.isdigit():
return py_wrap(self.obj[int(prop)])
return py_wrap(getattr(self.obj, prop))
except:
return undefined
def put(self, prop, val, op=None, throw=False):
if not isinstance(prop, basestring):
prop = prop.to_string().value
try:
if isinstance(op, bool):
raise ValueError("Op must be a string")
elif op is not None:
if op: # increment operation
val = getattr(self.get(prop), OP_METHODS[op])(val)
setattr(self.obj, prop, to_python(val))
except AttributeError:
raise MakeError('TypeError', 'Read only object probably...')
return val
def __call__(self, *args):
py_args = tuple(to_python(e) for e in args)
try:
py_res = self.obj.__call__(*py_args)
except Exception as e:
message = 'your Python function failed! '
try:
message += e.message
except:
pass
raise MakeError('Error', message)
return py_wrap(py_res)
def callprop(self, prop, *args):
py_args = tuple(to_python(e) for e in args)
if not isinstance(prop, basestring):
prop = prop.to_string().value
return self.get(prop)(*py_args)
def delete(self, prop):
if not isinstance(prop, basestring):
prop = prop.to_string().value
try:
if prop.isdigit():
del self.obj[int(prop)]
else:
delattr(self.obj, prop)
return true
except:
return false
def __repr__(self):
return 'PyObjectWrapper(%s)' % str(self.obj)
def to_python(self):
return self.obj
def to_py(self):
return self.obj
def py_wrap(py):
if isinstance(py, (FunctionType, BuiltinFunctionType, MethodType,
BuiltinMethodType, dict, int, str, bool, float, list,
tuple, long, basestring)) or py is None:
return HJs(py)
return PyObjectWrapper(py)
##############################################################################
#Define types
#Object
class PyJsObject(PyJs):
Class = 'Object'
def __init__(self, prop_descs={}, prototype=None, extensible=True):
self.prototype = prototype
self.extensible = extensible
self.own = {}
for prop, desc in six.iteritems(prop_descs):
self.define_own_property(prop, desc)
def __repr__(self):
return repr(self.to_python().to_dict())
ObjectPrototype = PyJsObject()
#Function
class PyJsFunction(PyJs):
Class = 'Function'
def __init__(self, func, prototype=None, extensible=True, source=None):
cand = fix_js_args(func)
has_scope = cand is func
func = cand
self.argcount = six.get_function_code(func).co_argcount - 2 - has_scope
self.code = func
self.source = source if source else '{ [python code] }'
self.func_name = func.__name__ if not func.__name__.startswith(
'PyJs_anonymous') else ''
self.extensible = extensible
self.prototype = prototype
self.own = {}
#set own property length to the number of arguments
self.define_own_property(
'length', {
'value': Js(self.argcount),
'writable': False,
'enumerable': False,
'configurable': False
})
if self.func_name:
self.define_own_property(
'name', {
'value': Js(self.func_name),
'writable': False,
'enumerable': False,
'configurable': True
})
# set own prototype
proto = Js({})
# constructor points to this function
proto.define_own_property(
'constructor', {
'value': self,
'writable': True,
'enumerable': False,
'configurable': True
})
self.define_own_property(
'prototype', {
'value': proto,
'writable': True,
'enumerable': False,
'configurable': False
})
def _set_name(self, name):
'''name is py type'''
if self.own.get('name'):
self.func_name = name
self.own['name']['value'] = Js(name)
def construct(self, *args):
proto = self.get('prototype')
if not proto.is_object(): # set to standard prototype
proto = ObjectPrototype
obj = PyJsObject(prototype=proto)
cand = self.call(obj, *args)
return cand if cand.is_object() else obj
def call(self, this, args=()):
'''Calls this function and returns a result
(converted to PyJs type so func can return python types)
this must be a PyJs object and args must be a python tuple of PyJs objects.
arguments object is passed automatically and will be equal to Js(args)
(tuple converted to arguments object).You dont need to worry about number
of arguments you provide if you supply less then missing ones will be set
to undefined (but not present in arguments object).
And if you supply too much then excess will not be passed
(but they will be present in arguments object).
'''
if not hasattr(args, '__iter__'): #get rid of it later
args = (args, )
args = tuple(Js(e) for e in args) # this wont be needed later
arguments = PyJsArguments(
args, self) # tuple will be converted to arguments object.
arglen = self.argcount #function expects this number of args.
if len(args) > arglen:
args = args[0:arglen]
elif len(args) < arglen:
args += (undefined, ) * (arglen - len(args))
args += this, arguments #append extra params to the arg list
try:
return Js(self.code(*args))
except NotImplementedError:
raise
except RuntimeError as e: # maximum recursion
raise MakeError(
'RangeError', e.message if
not isinstance(e, NotImplementedError) else 'Not implemented!')
def has_instance(self, other):
# I am not sure here so instanceof may not work lol.
if not other.is_object():
return false
proto = self.get('prototype')
if not proto.is_object():
raise 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):
proto = self.get('prototype')
if not proto.is_object():
proto = ObjectPrototype
new = PyJsObject(prototype=proto)
res = self.call(new, args)
if res.is_object():
return res
return new
class PyJsBoundFunction(PyJsFunction):
def __init__(self, target, bound_this, bound_args):
self.target = target
self.bound_this = bound_this
self.bound_args = bound_args
self.argcount = target.argcount
self.code = target.code
self.source = target.source
self.func_name = target.func_name
self.extensible = True
self.prototype = FunctionPrototype
self.own = {}
# set own property length to the number of arguments
self.define_own_property(
'length', {
'value': target.get('length') - Js(len(self.bound_args)),
'writable': False,
'enumerable': False,
'configurable': False
})
if self.func_name:
self.define_own_property(
'name', {
'value': Js(self.func_name),
'writable': False,
'enumerable': False,
'configurable': True
})
# set own prototype
proto = Js({})
# constructor points to this function
proto.define_own_property(
'constructor', {
'value': self,
'writable': True,
'enumerable': False,
'configurable': True
})
self.define_own_property(
'prototype', {
'value': proto,
'writable': True,
'enumerable': False,
'configurable': False
})
def call(self, this, args=()):
return self.target.call(self.bound_this, self.bound_args + args)
def has_instance(self, other):
return self.target.has_instance(other)
PyJs.PyJsBoundFunction = PyJsBoundFunction
OP_METHODS = {
'*': '__mul__',
'/': '__div__',
'%': '__mod__',
'+': '__add__',
'-': '__sub__',
'<<': '__lshift__',
'>>': '__rshift__',
'&': '__and__',
'^': '__xor__',
'|': '__or__',
'>>>': 'pyjs_bshift'
}
def Empty():
return Js(None)
#Number
class PyJsNumber(PyJs): #Note i dont implement +0 and -0. Just 0.
TYPE = 'Number'
Class = 'Number'
NumberPrototype = PyJsObject({}, ObjectPrototype)
NumberPrototype.Class = 'Number'
NumberPrototype.value = 0
Infinity = PyJsNumber(float('inf'), NumberPrototype)
NaN = PyJsNumber(float('nan'), NumberPrototype)
PyJs.NaN = NaN
PyJs.Infinity = Infinity
# This dict aims to increase speed of string creation by storing character instances
CHAR_BANK = {}
NUM_BANK = {}
PyJs.CHAR_BANK = CHAR_BANK
#String
# Different than implementation design in order to improve performance
#for example I dont create separate property for each character in string, it would take ages.
class PyJsString(PyJs):
TYPE = 'String'
Class = 'String'
extensible = False
def __init__(self, value=None, prototype=None):
'''Constructor for Number String and Boolean'''
if not isinstance(value, basestring):
raise TypeError # this will be internal error
self.value = value
self.prototype = prototype
self.own = {}
# this should be optimized because its mych slower than python str creation (about 50 times!)
# Dont create separate properties for every index. Just
self.own['length'] = {
'value': Js(len(value)),
'writable': False,
'enumerable': False,
'configurable': False
}
if len(value) == 1:
CHAR_BANK[value] = self #, 'writable': False,
# 'enumerable': True, 'configurable': False}
def get(self, prop):
if not isinstance(prop, basestring):
prop = prop.to_string().value
try:
index = int(prop)
if index < 0:
return undefined
char = self.value[index]
if char not in CHAR_BANK:
Js(char) # this will add char to CHAR BANK
return CHAR_BANK[char]
except Exception:
pass
return PyJs.get(self, prop)
def can_put(self, prop):
return False
def __iter__(self):
for i in xrange(len(self.value)):
yield Js(i) # maybe create an int bank?
StringPrototype = PyJsObject({}, ObjectPrototype)
StringPrototype.Class = 'String'
StringPrototype.value = ''
CHAR_BANK[''] = Js('')
#Boolean
class PyJsBoolean(PyJs):
TYPE = 'Boolean'
Class = 'Boolean'
BooleanPrototype = PyJsObject({}, ObjectPrototype)
BooleanPrototype.Class = 'Boolean'
BooleanPrototype.value = False
true = PyJsBoolean(True, BooleanPrototype)
false = PyJsBoolean(False, BooleanPrototype)
#Undefined
class PyJsUndefined(PyJs):
TYPE = 'Undefined'
Class = 'Undefined'
def __init__(self):
pass
undefined = PyJsUndefined()
#Null
class PyJsNull(PyJs):
TYPE = 'Null'
Class = 'Null'
def __init__(self):
pass
null = PyJsNull()
PyJs.null = null
class PyJsArray(PyJs):
Class = 'Array'
def __init__(self, arr=[], prototype=None):
self.extensible = True
self.prototype = prototype
self.own = {
'length': {
'value': Js(0),
'writable': True,
'enumerable': False,
'configurable': False
}
}
for i, e in enumerate(arr):
self.define_own_property(
str(i), {
'value': Js(e),
'writable': True,
'enumerable': True,
'configurable': True
})
def define_own_property(self, prop, desc):
old_len_desc = self.get_own_property('length')
old_len = old_len_desc[
'value'].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)
new_len = desc['value'].to_uint32()
if new_len != desc['value'].to_number().value:
raise MakeError('RangeError', 'Invalid range!')
new_desc = dict((k, v) for k, v in six.iteritems(desc))
new_desc['value'] = Js(new_len)
if new_len >= old_len:
return PyJs.define_own_property(self, prop, new_desc)
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):
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'] = Js(old_len + 1)
if not new_writable:
new_desc['writable'] = False
PyJs.define_own_property(self, prop, new_desc)
return False
old_len = new_len
else: # standard method
while new_len < old_len:
old_len -= 1
if not self.delete(
str(int(old_len))
): # if failed to delete set len to current len and reject.
new_desc['value'] = Js(old_len + 1)
if not new_writable:
new_desc['writable'] = False
PyJs.define_own_property(self, prop, new_desc)
return False
if not new_writable:
self.own['length']['writable'] = False
return True
elif prop.isdigit():
index = int(int(prop) % 2**32)
if index >= old_len and not old_len_desc['writable']:
return False
if not PyJs.define_own_property(self, prop, desc):
return False
if index >= old_len:
old_len_desc['value'] = Js(index + 1)
return True
else:
return PyJs.define_own_property(self, prop, desc)
def to_list(self):
return [
self.get(str(e)) for e in xrange(self.get('length').to_uint32())
]
def __repr__(self):
return repr(self.to_python().to_list())
class PyJsArrayBuffer(PyJs):
Class = 'ArrayBuffer'
def __init__(self, arr=[], prototype=None):
self.extensible = True
self.prototype = prototype
self.own = {
'length': {
'value': Js(0),
'writable': True,
'enumerable': False,
'configurable': False
}
}
for i, e in enumerate(arr):
self.define_own_property(
str(i), {
'value': Js(e),
'writable': True,
'enumerable': True,
'configurable': True
})
def define_own_property(self, prop, desc):
old_len_desc = self.get_own_property('length')
old_len = old_len_desc[
'value'].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)
new_len = desc['value'].to_uint32()
if new_len != desc['value'].to_number().value:
raise MakeError('RangeError', 'Invalid range!')
new_desc = dict((k, v) for k, v in six.iteritems(desc))
new_desc['value'] = Js(new_len)
if new_len >= old_len:
return PyJs.define_own_property(self, prop, new_desc)
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):
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'] = Js(old_len + 1)
if not new_writable:
new_desc['writable'] = False
PyJs.define_own_property(self, prop, new_desc)
return False
old_len = new_len
else: # standard method
while new_len < old_len:
old_len -= 1
if not self.delete(
str(int(old_len))
): # if failed to delete set len to current len and reject.
new_desc['value'] = Js(old_len + 1)
if not new_writable:
new_desc['writable'] = False
PyJs.define_own_property(self, prop, new_desc)
return False
if not new_writable:
self.own['length']['writable'] = False
return True
elif prop.isdigit():
index = int(int(prop) % 2**32)
if index >= old_len and not old_len_desc['writable']:
return False
if not PyJs.define_own_property(self, prop, desc):
return False
if index >= old_len:
old_len_desc['value'] = Js(index + 1)
return True
else:
return PyJs.define_own_property(self, prop, desc)
def to_list(self):
return [
self.get(str(e)) for e in xrange(self.get('length').to_uint32())
]
def __repr__(self):
return repr(self.to_python().to_list())
class PyJsInt8Array(PyJs):
Class = 'Int8Array'
def __init__(self, arr=[], prototype=None):
self.extensible = True
self.prototype = prototype
self.own = {
'length': {
'value': Js(0),
'writable': True,
'enumerable': False,
'configurable': False
}
}
for i, e in enumerate(arr):
self.define_own_property(
str(i), {
'value': Js(e),
'writable': True,
'enumerable': True,
'configurable': True
})
def define_own_property(self, prop, desc):
old_len_desc = self.get_own_property('length')
old_len = old_len_desc[
'value'].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)
new_len = desc['value'].to_uint32()
if new_len != desc['value'].to_number().value:
raise MakeError('RangeError', 'Invalid range!')
new_desc = dict((k, v) for k, v in six.iteritems(desc))
new_desc['value'] = Js(new_len)
if new_len >= old_len:
return PyJs.define_own_property(self, prop, new_desc)
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):
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'] = Js(old_len + 1)
if not new_writable:
new_desc['writable'] = False
PyJs.define_own_property(self, prop, new_desc)
return False
old_len = new_len
else: # standard method
while new_len < old_len:
old_len -= 1
if not self.delete(
str(int(old_len))
): # if failed to delete set len to current len and reject.
new_desc['value'] = Js(old_len + 1)
if not new_writable:
new_desc['writable'] = False
PyJs.define_own_property(self, prop, new_desc)
return False
if not new_writable:
self.own['length']['writable'] = False
return True
elif prop.isdigit():
index = int(int(prop) % 2**32)
if index >= old_len and not old_len_desc['writable']:
return False
if not PyJs.define_own_property(self, prop, desc):
return False
if index >= old_len:
old_len_desc['value'] = Js(index + 1)
return True
else:
return PyJs.define_own_property(self, prop, desc)
def to_list(self):
return [
self.get(str(e)) for e in xrange(self.get('length').to_uint32())
]
def __repr__(self):
return repr(self.to_python().to_list())
class PyJsUint8Array(PyJs):
Class = 'Uint8Array'
def __init__(self, arr=[], prototype=None):
self.extensible = True
self.prototype = prototype
self.own = {
'length': {
'value': Js(0),
'writable': True,
'enumerable': False,
'configurable': False
}
}
for i, e in enumerate(arr):
self.define_own_property(
str(i), {
'value': Js(e),
'writable': True,
'enumerable': True,
'configurable': True
})
def define_own_property(self, prop, desc):
old_len_desc = self.get_own_property('length')
old_len = old_len_desc[
'value'].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)
new_len = desc['value'].to_uint32()
if new_len != desc['value'].to_number().value:
raise MakeError('RangeError', 'Invalid range!')
new_desc = dict((k, v) for k, v in six.iteritems(desc))
new_desc['value'] = Js(new_len)
if new_len >= old_len:
return PyJs.define_own_property(self, prop, new_desc)
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):
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'] = Js(old_len + 1)
if not new_writable:
new_desc['writable'] = False
PyJs.define_own_property(self, prop, new_desc)
return False
old_len = new_len
else: # standard method
while new_len < old_len:
old_len -= 1
if not self.delete(
str(int(old_len))
): # if failed to delete set len to current len and reject.
new_desc['value'] = Js(old_len + 1)
if not new_writable:
new_desc['writable'] = False
PyJs.define_own_property(self, prop, new_desc)
return False
if not new_writable:
self.own['length']['writable'] = False
return True
elif prop.isdigit():
index = int(int(prop) % 2**32)
if index >= old_len and not old_len_desc['writable']:
return False
if not PyJs.define_own_property(self, prop, desc):
return False
if index >= old_len:
old_len_desc['value'] = Js(index + 1)
return True
else:
return PyJs.define_own_property(self, prop, desc)
def to_list(self):
return [
self.get(str(e)) for e in xrange(self.get('length').to_uint32())
]
def __repr__(self):
return repr(self.to_python().to_list())
class PyJsUint8ClampedArray(PyJs):
Class = 'Uint8ClampedArray'
def __init__(self, arr=[], prototype=None):
self.extensible = True
self.prototype = prototype
self.own = {
'length': {
'value': Js(0),
'writable': True,
'enumerable': False,
'configurable': False
}
}
for i, e in enumerate(arr):
self.define_own_property(
str(i), {
'value': Js(e),
'writable': True,
'enumerable': True,
'configurable': True
})
def define_own_property(self, prop, desc):
old_len_desc = self.get_own_property('length')
old_len = old_len_desc[
'value'].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)
new_len = desc['value'].to_uint32()
if new_len != desc['value'].to_number().value:
raise MakeError('RangeError', 'Invalid range!')
new_desc = dict((k, v) for k, v in six.iteritems(desc))
new_desc['value'] = Js(new_len)
if new_len >= old_len:
return PyJs.define_own_property(self, prop, new_desc)
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):
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'] = Js(old_len + 1)
if not new_writable:
new_desc['writable'] = False
PyJs.define_own_property(self, prop, new_desc)
return False
old_len = new_len
else: # standard method
while new_len < old_len:
old_len -= 1
if not self.delete(
str(int(old_len))
): # if failed to delete set len to current len and reject.
new_desc['value'] = Js(old_len + 1)
if not new_writable:
new_desc['writable'] = False
PyJs.define_own_property(self, prop, new_desc)
return False
if not new_writable:
self.own['length']['writable'] = False
return True
elif prop.isdigit():
index = int(int(prop) % 2**32)
if index >= old_len and not old_len_desc['writable']:
return False
if not PyJs.define_own_property(self, prop, desc):
return False
if index >= old_len:
old_len_desc['value'] = Js(index + 1)
return True
else:
return PyJs.define_own_property(self, prop, desc)
def to_list(self):
return [
self.get(str(e)) for e in xrange(self.get('length').to_uint32())
]
def __repr__(self):
return repr(self.to_python().to_list())
class PyJsInt16Array(PyJs):
Class = 'Int16Array'
def __init__(self, arr=[], prototype=None):
self.extensible = True
self.prototype = prototype
self.own = {
'length': {
'value': Js(0),
'writable': True,
'enumerable': False,
'configurable': False
}
}
for i, e in enumerate(arr):
self.define_own_property(
str(i), {
'value': Js(e),
'writable': True,
'enumerable': True,
'configurable': True
})
def define_own_property(self, prop, desc):
old_len_desc = self.get_own_property('length')
old_len = old_len_desc[
'value'].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)
new_len = desc['value'].to_uint32()
if new_len != desc['value'].to_number().value:
raise MakeError('RangeError', 'Invalid range!')
new_desc = dict((k, v) for k, v in six.iteritems(desc))
new_desc['value'] = Js(new_len)
if new_len >= old_len:
return PyJs.define_own_property(self, prop, new_desc)
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):
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'] = Js(old_len + 1)
if not new_writable:
new_desc['writable'] = False
PyJs.define_own_property(self, prop, new_desc)
return False
old_len = new_len
else: # standard method
while new_len < old_len:
old_len -= 1
if not self.delete(
str(int(old_len))
): # if failed to delete set len to current len and reject.
new_desc['value'] = Js(old_len + 1)
if not new_writable:
new_desc['writable'] = False
PyJs.define_own_property(self, prop, new_desc)
return False
if not new_writable:
self.own['length']['writable'] = False
return True
elif prop.isdigit():
index = int(int(prop) % 2**32)
if index >= old_len and not old_len_desc['writable']:
return False
if not PyJs.define_own_property(self, prop, desc):
return False
if index >= old_len:
old_len_desc['value'] = Js(index + 1)
return True
else:
return PyJs.define_own_property(self, prop, desc)
def to_list(self):
return [
self.get(str(e)) for e in xrange(self.get('length').to_uint32())
]
def __repr__(self):
return repr(self.to_python().to_list())
class PyJsUint16Array(PyJs):
Class = 'Uint16Array'
def __init__(self, arr=[], prototype=None):
self.extensible = True
self.prototype = prototype
self.own = {
'length': {
'value': Js(0),
'writable': True,
'enumerable': False,
'configurable': False
}
}
for i, e in enumerate(arr):
self.define_own_property(
str(i), {
'value': Js(e),
'writable': True,
'enumerable': True,
'configurable': True
})
def define_own_property(self, prop, desc):
old_len_desc = self.get_own_property('length')
old_len = old_len_desc[
'value'].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)
new_len = desc['value'].to_uint32()
if new_len != desc['value'].to_number().value:
raise MakeError('RangeError', 'Invalid range!')
new_desc = dict((k, v) for k, v in six.iteritems(desc))
new_desc['value'] = Js(new_len)
if new_len >= old_len:
return PyJs.define_own_property(self, prop, new_desc)
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):
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'] = Js(old_len + 1)
if not new_writable:
new_desc['writable'] = False
PyJs.define_own_property(self, prop, new_desc)
return False
old_len = new_len
else: # standard method
while new_len < old_len:
old_len -= 1
if not self.delete(
str(int(old_len))
): # if failed to delete set len to current len and reject.
new_desc['value'] = Js(old_len + 1)
if not new_writable:
new_desc['writable'] = False
PyJs.define_own_property(self, prop, new_desc)
return False
if not new_writable:
self.own['length']['writable'] = False
return True
elif prop.isdigit():
index = int(int(prop) % 2**32)
if index >= old_len and not old_len_desc['writable']:
return False
if not PyJs.define_own_property(self, prop, desc):
return False
if index >= old_len:
old_len_desc['value'] = Js(index + 1)
return True
else:
return PyJs.define_own_property(self, prop, desc)
def to_list(self):
return [
self.get(str(e)) for e in xrange(self.get('length').to_uint32())
]
def __repr__(self):
return repr(self.to_python().to_list())
class PyJsInt32Array(PyJs):
Class = 'Int32Array'
def __init__(self, arr=[], prototype=None):
self.extensible = True
self.prototype = prototype
self.own = {
'length': {
'value': Js(0),
'writable': True,
'enumerable': False,
'configurable': False
}
}
for i, e in enumerate(arr):
self.define_own_property(
str(i), {
'value': Js(e),
'writable': True,
'enumerable': True,
'configurable': True
})
def define_own_property(self, prop, desc):
old_len_desc = self.get_own_property('length')
old_len = old_len_desc[
'value'].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)
new_len = desc['value'].to_uint32()
if new_len != desc['value'].to_number().value:
raise MakeError('RangeError', 'Invalid range!')
new_desc = dict((k, v) for k, v in six.iteritems(desc))
new_desc['value'] = Js(new_len)
if new_len >= old_len:
return PyJs.define_own_property(self, prop, new_desc)
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):
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'] = Js(old_len + 1)
if not new_writable:
new_desc['writable'] = False
PyJs.define_own_property(self, prop, new_desc)
return False
old_len = new_len
else: # standard method
while new_len < old_len:
old_len -= 1
if not self.delete(
str(int(old_len))
): # if failed to delete set len to current len and reject.
new_desc['value'] = Js(old_len + 1)
if not new_writable:
new_desc['writable'] = False
PyJs.define_own_property(self, prop, new_desc)
return False
if not new_writable:
self.own['length']['writable'] = False
return True
elif prop.isdigit():
index = int(int(prop) % 2**32)
if index >= old_len and not old_len_desc['writable']:
return False
if not PyJs.define_own_property(self, prop, desc):
return False
if index >= old_len:
old_len_desc['value'] = Js(index + 1)
return True
else:
return PyJs.define_own_property(self, prop, desc)
def to_list(self):
return [
self.get(str(e)) for e in xrange(self.get('length').to_uint32())
]
def __repr__(self):
return repr(self.to_python().to_list())
class PyJsUint32Array(PyJs):
Class = 'Uint32Array'
def __init__(self, arr=[], prototype=None):
self.extensible = True
self.prototype = prototype
self.own = {
'length': {
'value': Js(0),
'writable': True,
'enumerable': False,
'configurable': False
}
}
for i, e in enumerate(arr):
self.define_own_property(
str(i), {
'value': Js(e),
'writable': True,
'enumerable': True,
'configurable': True
})
def define_own_property(self, prop, desc):
old_len_desc = self.get_own_property('length')
old_len = old_len_desc[
'value'].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)
new_len = desc['value'].to_uint32()
if new_len != desc['value'].to_number().value:
raise MakeError('RangeError', 'Invalid range!')
new_desc = dict((k, v) for k, v in six.iteritems(desc))
new_desc['value'] = Js(new_len)
if new_len >= old_len:
return PyJs.define_own_property(self, prop, new_desc)
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):
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'] = Js(old_len + 1)
if not new_writable:
new_desc['writable'] = False
PyJs.define_own_property(self, prop, new_desc)
return False
old_len = new_len
else: # standard method
while new_len < old_len:
old_len -= 1
if not self.delete(
str(int(old_len))
): # if failed to delete set len to current len and reject.
new_desc['value'] = Js(old_len + 1)
if not new_writable:
new_desc['writable'] = False
PyJs.define_own_property(self, prop, new_desc)
return False
if not new_writable:
self.own['length']['writable'] = False
return True
elif prop.isdigit():
index = int(int(prop) % 2**32)
if index >= old_len and not old_len_desc['writable']:
return False
if not PyJs.define_own_property(self, prop, desc):
return False
if index >= old_len:
old_len_desc['value'] = Js(index + 1)
return True
else:
return PyJs.define_own_property(self, prop, desc)
def to_list(self):
return [
self.get(str(e)) for e in xrange(self.get('length').to_uint32())
]
def __repr__(self):
return repr(self.to_python().to_list())
class PyJsFloat32Array(PyJs):
Class = 'Float32Array'
def __init__(self, arr=[], prototype=None):
self.extensible = True
self.prototype = prototype
self.own = {
'length': {
'value': Js(0),
'writable': True,
'enumerable': False,
'configurable': False
}
}
for i, e in enumerate(arr):
self.define_own_property(
str(i), {
'value': Js(e),
'writable': True,
'enumerable': True,
'configurable': True
})
def define_own_property(self, prop, desc):
old_len_desc = self.get_own_property('length')
old_len = old_len_desc[
'value'].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)
new_len = desc['value'].to_uint32()
if new_len != desc['value'].to_number().value:
raise MakeError('RangeError', 'Invalid range!')
new_desc = dict((k, v) for k, v in six.iteritems(desc))
new_desc['value'] = Js(new_len)
if new_len >= old_len:
return PyJs.define_own_property(self, prop, new_desc)
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):
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'] = Js(old_len + 1)
if not new_writable:
new_desc['writable'] = False
PyJs.define_own_property(self, prop, new_desc)
return False
old_len = new_len
else: # standard method
while new_len < old_len:
old_len -= 1
if not self.delete(
str(int(old_len))
): # if failed to delete set len to current len and reject.
new_desc['value'] = Js(old_len + 1)
if not new_writable:
new_desc['writable'] = False
PyJs.define_own_property(self, prop, new_desc)
return False
if not new_writable:
self.own['length']['writable'] = False
return True
elif prop.isdigit():
index = int(int(prop) % 2**32)
if index >= old_len and not old_len_desc['writable']:
return False
if not PyJs.define_own_property(self, prop, desc):
return False
if index >= old_len:
old_len_desc['value'] = Js(index + 1)
return True
else:
return PyJs.define_own_property(self, prop, desc)
def to_list(self):
return [
self.get(str(e)) for e in xrange(self.get('length').to_uint32())
]
def __repr__(self):
return repr(self.to_python().to_list())
class PyJsFloat64Array(PyJs):
Class = 'Float64Array'
def __init__(self, arr=[], prototype=None):
self.extensible = True
self.prototype = prototype
self.own = {
'length': {
'value': Js(0),
'writable': True,
'enumerable': False,
'configurable': False
}
}
for i, e in enumerate(arr):
self.define_own_property(
str(i), {
'value': Js(e),
'writable': True,
'enumerable': True,
'configurable': True
})
def define_own_property(self, prop, desc):
old_len_desc = self.get_own_property('length')
old_len = old_len_desc[
'value'].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)
new_len = desc['value'].to_uint32()
if new_len != desc['value'].to_number().value:
raise MakeError('RangeError', 'Invalid range!')
new_desc = dict((k, v) for k, v in six.iteritems(desc))
new_desc['value'] = Js(new_len)
if new_len >= old_len:
return PyJs.define_own_property(self, prop, new_desc)
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):
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'] = Js(old_len + 1)
if not new_writable:
new_desc['writable'] = False
PyJs.define_own_property(self, prop, new_desc)
return False
old_len = new_len
else: # standard method
while new_len < old_len:
old_len -= 1
if not self.delete(
str(int(old_len))
): # if failed to delete set len to current len and reject.
new_desc['value'] = Js(old_len + 1)
if not new_writable:
new_desc['writable'] = False
PyJs.define_own_property(self, prop, new_desc)
return False
if not new_writable:
self.own['length']['writable'] = False
return True
elif prop.isdigit():
index = int(int(prop) % 2**32)
if index >= old_len and not old_len_desc['writable']:
return False
if not PyJs.define_own_property(self, prop, desc):
return False
if index >= old_len:
old_len_desc['value'] = Js(index + 1)
return True
else:
return PyJs.define_own_property(self, prop, desc)
def to_list(self):
return [
self.get(str(e)) for e in xrange(self.get('length').to_uint32())
]
def __repr__(self):
return repr(self.to_python().to_list())
ArrayPrototype = PyJsArray([], ObjectPrototype)
ArrayBufferPrototype = PyJsArrayBuffer([], ObjectPrototype)
Int8ArrayPrototype = PyJsInt8Array([], ObjectPrototype)
Uint8ArrayPrototype = PyJsUint8Array([], ObjectPrototype)
Uint8ClampedArrayPrototype = PyJsUint8ClampedArray([], ObjectPrototype)
Int16ArrayPrototype = PyJsInt16Array([], ObjectPrototype)
Uint16ArrayPrototype = PyJsUint16Array([], ObjectPrototype)
Int32ArrayPrototype = PyJsInt32Array([], ObjectPrototype)
Uint32ArrayPrototype = PyJsUint32Array([], ObjectPrototype)
Float32ArrayPrototype = PyJsFloat32Array([], ObjectPrototype)
Float64ArrayPrototype = PyJsFloat64Array([], ObjectPrototype)
class PyJsArguments(PyJs):
Class = 'Arguments'
def __init__(self, args, callee):
self.own = {}
self.extensible = True
self.prototype = ObjectPrototype
self.define_own_property(
'length', {
'value': Js(len(args)),
'writable': True,
'enumerable': False,
'configurable': True
})
self.define_own_property(
'callee', {
'value': callee,
'writable': True,
'enumerable': False,
'configurable': True
})
for i, e in enumerate(args):
self.put(str(i), Js(e))
def to_list(self):
return [
self.get(str(e)) for e in xrange(self.get('length').to_uint32())
]
#We can define function proto after number proto because func uses number in its init
FunctionPrototype = PyJsFunction(Empty, ObjectPrototype)
FunctionPrototype.own['name']['value'] = Js('')
# I will not rewrite RegExp engine from scratch. I will use re because its much faster.
# I have to only make sure that I am handling all the differences correctly.
REGEXP_DB = {}
class PyJsRegExp(PyJs):
Class = 'RegExp'
extensible = True
def __init__(self, regexp, prototype=None):
self.prototype = prototype
self.glob = False
self.ignore_case = 0
self.multiline = 0
# self._cache = {'str':'NoStringEmpty23093',
# 'iterator': None,
# 'lastpos': -1,
# 'matches': {}}
flags = ''
if not regexp[-1] == '/':
#contains some flags (allowed are i, g, m
spl = regexp.rfind('/')
flags = set(regexp[spl + 1:])
self.value = regexp[1:spl]
if 'g' in flags:
self.glob = True
if 'i' in flags:
self.ignore_case = re.IGNORECASE
if 'm' in flags:
self.multiline = re.MULTILINE
else:
self.value = regexp[1:-1]
try:
if self.value in REGEXP_DB:
self.pat = REGEXP_DB[regexp]
else:
comp = 'None'
# we have to check whether pattern is valid.
# also this will speed up matching later
# todo critical fix patter conversion etc. ..!!!!!
# ugly hacks porting js reg exp to py reg exp works in 99% of cases ;)
possible_fixes = [(u'[]', u'[\0]'), (u'[^]', u'[^\0]'),
(u'nofix1791', u'nofix1791')]
reg = self.value
for fix, rep in possible_fixes:
comp = REGEXP_CONVERTER._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
REGEXP_DB[regexp] = self.pat
except:
#print 'Invalid pattern but fuck it', self.value, comp
raise MakeError(
'SyntaxError',
'Invalid RegExp pattern: %s -> %s' % (repr(self.value),
repr(comp)))
# now set own properties:
self.own = {
'source': {
'value': Js(self.value),
'enumerable': False,
'writable': False,
'configurable': False
},
'global': {
'value': Js(self.glob),
'enumerable': False,
'writable': False,
'configurable': False
},
'ignoreCase': {
'value': Js(bool(self.ignore_case)),
'enumerable': False,
'writable': False,
'configurable': False
},
'multiline': {
'value': Js(bool(self.multiline)),
'enumerable': False,
'writable': False,
'configurable': False
},
'lastIndex': {
'value': Js(0),
'enumerable': False,
'writable': True,
'configurable': False
}
}
def match(self, string, pos):
'''string is of course py string'''
return self.pat.match(string, pos) # way easier :)
# assert 0<=pos <= len(string)
# if not pos:
# return re.match(self.pat, string)
# else:
# if self._cache['str']==string:
# if pos>self._cache['lastpos']:
# for m in self._cache['iterator']:
# start = m.start()
# self._cache['lastpos'] = start
# self._cache['matches'][start] = m
# if start==pos:
# return m
# elif start>pos:
# return None
# self._cache['lastpos'] = len(string)
# return None
# else:
# return self._cache['matches'].get(pos)
# else:
# self._cache['str'] = string
# self._cache['matches'] = {}
# self._cache['lastpos'] = -1
# self._cache['iterator'] = re.finditer(self.pat, string)
# return self.match(string, pos)
def JsRegExp(source):
# Takes regexp literal!
return PyJsRegExp(source, RegExpPrototype)
RegExpPrototype = PyJsRegExp('/(?:)/', ObjectPrototype)
####Exceptions:
default_attrs = {'writable': True, 'enumerable': False, 'configurable': True}
def fill_in_props(obj, props, default_desc):
for prop, value in props.items():
default_desc['value'] = Js(value)
obj.define_own_property(prop, default_desc)
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', Js(message).to_string())
self.own['message']['enumerable'] = False
ErrorPrototype = PyJsError(Js(''), ObjectPrototype)
@Js
def Error(message):
return PyJsError(None if message.is_undefined() else message,
ErrorPrototype)
Error.create = Error
err = {'name': 'Error', 'constructor': Error}
fill_in_props(ErrorPrototype, err, default_attrs)
Error.define_own_property(
'prototype', {
'value': ErrorPrototype,
'enumerable': False,
'writable': False,
'configurable': False
})
def define_error_type(name):
TypeErrorPrototype = PyJsError(None, ErrorPrototype)
@Js
def TypeError(message):
return PyJsError(None if message.is_undefined() else message,
TypeErrorPrototype)
err = {'name': name, 'constructor': TypeError}
fill_in_props(TypeErrorPrototype, err, default_attrs)
TypeError.define_own_property(
'prototype', {
'value': TypeErrorPrototype,
'enumerable': False,
'writable': False,
'configurable': False
})
ERRORS[name] = TypeError
ERRORS = {'Error': Error}
ERROR_NAMES = ['Eval', 'Type', 'Range', 'Reference', 'Syntax', 'URI']
for e in ERROR_NAMES:
define_error_type(e + 'Error')
##############################################################################
# Import and fill prototypes here.
#this works only for data properties
def fill_prototype(prototype, Class, attrs, constructor=False):
for i in dir(Class):
e = getattr(Class, i)
if six.PY2:
if hasattr(e, '__func__'):
temp = PyJsFunction(e.__func__, FunctionPrototype)
attrs = dict((k, v) for k, v in attrs.iteritems())
attrs['value'] = temp
prototype.define_own_property(i, attrs)
else:
if hasattr(e, '__call__') and not i.startswith('__'):
temp = PyJsFunction(e, FunctionPrototype)
attrs = dict((k, v) for k, v in attrs.items())
attrs['value'] = temp
prototype.define_own_property(i, attrs)
if constructor:
attrs['value'] = constructor
prototype.define_own_property('constructor', attrs)
PyJs.undefined = undefined
PyJs.Js = staticmethod(Js)
from .prototypes import jsfunction, jsobject, jsnumber, jsstring, jsboolean, jsarray, jsregexp, jserror, jsarraybuffer, jstypedarray
#Object proto
fill_prototype(ObjectPrototype, jsobject.ObjectPrototype, default_attrs)
#Define __proto__ accessor (this cant be done by fill_prototype since)
@Js
def __proto__():
return this.prototype if this.prototype is not None else null
getter = __proto__
@Js
def __proto__(val):
if val.is_object():
this.prototype = val
setter = __proto__
ObjectPrototype.define_own_property('__proto__', {
'set': setter,
'get': getter,
'enumerable': False,
'configurable': True
})
#Function proto
fill_prototype(FunctionPrototype, jsfunction.FunctionPrototype, default_attrs)
#Number proto
fill_prototype(NumberPrototype, jsnumber.NumberPrototype, default_attrs)
#String proto
fill_prototype(StringPrototype, jsstring.StringPrototype, default_attrs)
#Boolean proto
fill_prototype(BooleanPrototype, jsboolean.BooleanPrototype, default_attrs)
#Array proto
fill_prototype(ArrayPrototype, jsarray.ArrayPrototype, default_attrs)
# ArrayBuffer proto
fill_prototype(ArrayBufferPrototype, jsarraybuffer.ArrayBufferPrototype,
default_attrs)
# Int8Array proto
fill_prototype(Int8ArrayPrototype, jstypedarray.TypedArrayPrototype,
default_attrs)
# Uint8Array proto
fill_prototype(Uint8ArrayPrototype, jstypedarray.TypedArrayPrototype,
default_attrs)
# Uint8ClampedArray proto
fill_prototype(Uint8ClampedArrayPrototype, jstypedarray.TypedArrayPrototype,
default_attrs)
# Int16Array proto
fill_prototype(Int16ArrayPrototype, jstypedarray.TypedArrayPrototype,
default_attrs)
# Uint16Array proto
fill_prototype(Uint16ArrayPrototype, jstypedarray.TypedArrayPrototype,
default_attrs)
# Int32Array proto
fill_prototype(Int32ArrayPrototype, jstypedarray.TypedArrayPrototype,
default_attrs)
# Uint32Array proto
fill_prototype(Uint32ArrayPrototype, jstypedarray.TypedArrayPrototype,
default_attrs)
# Float32Array proto
fill_prototype(Float32ArrayPrototype, jstypedarray.TypedArrayPrototype,
default_attrs)
# Float64Array proto
fill_prototype(Float64ArrayPrototype, jstypedarray.TypedArrayPrototype,
default_attrs)
#Error proto
fill_prototype(ErrorPrototype, jserror.ErrorPrototype, default_attrs)
#RegExp proto
fill_prototype(RegExpPrototype, jsregexp.RegExpPrototype, default_attrs)
# add exec to regexpfunction (cant add it automatically because of its name :(
RegExpPrototype.own['exec'] = RegExpPrototype.own['exec2']
del RegExpPrototype.own['exec2']
#########################################################################
# Constructors
# String
@Js
def String(st):
if not len(arguments):
return Js('')
return arguments[0].to_string()
@Js
def string_constructor():
temp = PyJsObject(prototype=StringPrototype)
temp.Class = 'String'
#temp.TYPE = 'String'
if not len(arguments):
temp.value = ''
else:
temp.value = arguments[0].to_string().value
for i, ch in enumerate(temp.value): # this will make things long...
temp.own[str(i)] = {
'value': Js(ch),
'writable': False,
'enumerable': True,
'configurable': True
}
temp.own['length'] = {
'value': Js(len(temp.value)),
'writable': False,
'enumerable': False,
'configurable': False
}
return temp
String.create = string_constructor
# RegExp
REG_EXP_FLAGS = ('g', 'i', 'm')
@Js
def RegExp(pattern, flags):
if pattern.Class == 'RegExp':
if not flags.is_undefined():
raise MakeError(
'TypeError',
'Cannot supply flags when constructing one RegExp from another'
)
# return unchanged
return pattern
#pattern is not a regexp
if pattern.is_undefined():
pattern = ''
else:
pattern = pattern.to_string().value
# try:
# pattern = REGEXP_CONVERTER._unescape_string(pattern.to_string().value)
# except:
# raise MakeError('SyntaxError', 'Invalid regexp')
flags = flags.to_string().value if not flags.is_undefined() else ''
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)
pattern = '/%s/' % (pattern if pattern else '(?:)') + flags
return JsRegExp(pattern)
RegExp.create = RegExp
PyJs.RegExp = RegExp
# Number
@Js
def Number():
if len(arguments):
return arguments[0].to_number()
else:
return Js(0)
@Js
def number_constructor():
temp = PyJsObject(prototype=NumberPrototype)
temp.Class = 'Number'
#temp.TYPE = 'Number'
if len(arguments):
temp.value = arguments[0].to_number().value
else:
temp.value = 0
return temp
Number.create = number_constructor
# Boolean
@Js
def Boolean(value):
return value.to_boolean()
@Js
def boolean_constructor(value):
temp = PyJsObject(prototype=BooleanPrototype)
temp.Class = 'Boolean'
#temp.TYPE = 'Boolean'
temp.value = value.to_boolean().value
return temp
Boolean.create = boolean_constructor
##############################################################################
def appengine(code):
try:
return translator.translate_js(code.decode('utf-8'))
except:
return traceback.format_exc()
builtins = ('true', 'false', 'null', 'undefined', 'Infinity', 'NaN')
scope = dict(zip(builtins, [eval(e) for e in builtins]))
JS_BUILTINS = dict((k, v) for k, v in scope.items())
# Fill in NUM_BANK
for e in xrange(-2**10, 2**14):
NUM_BANK[e] = Js(e)
if __name__ == '__main__':
print(ObjectPrototype.get('toString').callprop('call'))
print(FunctionPrototype.own)
a = null - Js(49404)
x = a.put('ser', Js('der'))
print(Js(0) or Js('p') and Js(4.0000000000050000001))
FunctionPrototype.put('Chuj', Js(409))
for e in FunctionPrototype:
print('Obk', e.get('__proto__').get('__proto__').get('__proto__'), e)
import code
s = Js(4)
b = Js(6)
s2 = Js(4)
o = ObjectPrototype
o.put('x', Js(100))
var = Scope(scope)
e = code.InteractiveConsole(globals())
#e.raw_input = interactor
e.interact()