1'''Most important file in Js2Py implementation: PyJs class - father of all PyJs objects'''
2from copy import copy
3import re
4
5from .translators.friendly_nodes import REGEXP_CONVERTER
6from .utils.injector import fix_js_args
7from types import FunctionType, ModuleType, GeneratorType, BuiltinFunctionType, MethodType, BuiltinMethodType
8from math import floor, log10
9import traceback
10try:
11    import numpy
12    NUMPY_AVAILABLE = True
13except:
14    NUMPY_AVAILABLE = False
15
16# python 3 support
17import six
18if six.PY3:
19    basestring = str
20    long = int
21    xrange = range
22    unicode = str
23
24
25def str_repr(s):
26    if six.PY2:
27        return repr(s.encode('utf-8'))
28    else:
29        return repr(s)
30
31
32def MakeError(name, message):
33    """Returns PyJsException with PyJsError inside"""
34    return JsToPyException(ERRORS[name](Js(message)))
35
36
37def to_python(val):
38    if not isinstance(val, PyJs):
39        return val
40    if isinstance(val, PyJsUndefined) or isinstance(val, PyJsNull):
41        return None
42    elif isinstance(val, PyJsNumber):
43        # this can be either float or long/int better to assume its int/long when a whole number...
44        v = val.value
45        try:
46            i = int(v) if v == v else v  # nan...
47            return v if i != v else i
48        except:
49            return v
50    elif isinstance(val, (PyJsString, PyJsBoolean)):
51        return val.value
52    elif isinstance(val, PyObjectWrapper):
53        return val.__dict__['obj']
54    elif isinstance(val, PyJsArray) and val.CONVERT_TO_PY_PRIMITIVES:
55        return to_list(val)
56    elif isinstance(val, PyJsObject) and val.CONVERT_TO_PY_PRIMITIVES:
57        return to_dict(val)
58    else:
59        return JsObjectWrapper(val)
60
61
62def to_dict(js_obj,
63            known=None):  # fixed recursion error in self referencing objects
64    res = {}
65    if known is None:
66        known = {}
67    if js_obj in known:
68        return known[js_obj]
69    known[js_obj] = res
70    for k in js_obj:
71        name = k.value
72        input = js_obj.get(name)
73        output = to_python(input)
74        if isinstance(output, JsObjectWrapper):
75            if output._obj.Class == 'Object':
76                output = to_dict(output._obj, known)
77                known[input] = output
78            elif output._obj.Class in [
79                    'Array', 'Int8Array', 'Uint8Array', 'Uint8ClampedArray',
80                    'Int16Array', 'Uint16Array', 'Int32Array', 'Uint32Array',
81                    'Float32Array', 'Float64Array'
82            ]:
83                output = to_list(output._obj)
84                known[input] = output
85        res[name] = output
86    return res
87
88
89def to_list(js_obj, known=None):
90    res = len(js_obj) * [None]
91    if known is None:
92        known = {}
93    if js_obj in known:
94        return known[js_obj]
95    known[js_obj] = res
96    for k in js_obj:
97        try:
98            name = int(k.value)
99        except:
100            continue
101        input = js_obj.get(str(name))
102        output = to_python(input)
103        if isinstance(output, JsObjectWrapper):
104            if output._obj.Class in [
105                    'Array', 'Int8Array', 'Uint8Array', 'Uint8ClampedArray',
106                    'Int16Array', 'Uint16Array', 'Int32Array', 'Uint32Array',
107                    'Float32Array', 'Float64Array', 'Arguments'
108            ]:
109                output = to_list(output._obj, known)
110                known[input] = output
111            elif output._obj.Class in ['Object']:
112                output = to_dict(output._obj)
113                known[input] = output
114        res[name] = output
115    return res
116
117
118def HJs(val):
119    if hasattr(val, '__call__'):  #
120
121        @Js
122        def PyWrapper(this, arguments, var=None):
123            args = tuple(to_python(e) for e in arguments.to_list())
124            try:
125                py_res = val.__call__(*args)
126            except Exception as e:
127                message = 'your Python function failed!  '
128                try:
129                    message += str(e)
130                except:
131                    pass
132                raise MakeError('Error', message)
133            return py_wrap(py_res)
134
135        try:
136            PyWrapper.func_name = val.__name__
137        except:
138            pass
139        return PyWrapper
140    if isinstance(val, tuple):
141        val = list(val)
142    return Js(val)
143
144
145def Js(val, Clamped=False):
146    '''Converts Py type to PyJs type'''
147    if isinstance(val, PyJs):
148        return val
149    elif val is None:
150        return undefined
151    elif isinstance(val, basestring):
152        return PyJsString(val, StringPrototype)
153    elif isinstance(val, bool):
154        return true if val else false
155    elif isinstance(val, float) or isinstance(val, int) or isinstance(
156            val, long) or (NUMPY_AVAILABLE and isinstance(
157                val,
158                (numpy.int8, numpy.uint8, numpy.int16, numpy.uint16,
159                 numpy.int32, numpy.uint32, numpy.float32, numpy.float64))):
160        # This is supposed to speed things up. may not be the case
161        if val in NUM_BANK:
162            return NUM_BANK[val]
163        return PyJsNumber(float(val), NumberPrototype)
164    elif isinstance(val, FunctionType):
165        return PyJsFunction(val, FunctionPrototype)
166    #elif isinstance(val, ModuleType):
167    #    mod = {}
168    #    for name in dir(val):
169    #        value = getattr(val, name)
170    #        if isinstance(value, ModuleType):
171    #            continue  # prevent recursive module conversion
172    #        try:
173    #            jsval = HJs(value)
174    #        except RuntimeError:
175    #            print 'Could not convert %s to PyJs object!' % name
176    #            continue
177    #        mod[name] = jsval
178    #    return Js(mod)
179    #elif isintance(val, ClassType):
180
181    elif isinstance(val, dict):  # convert to object
182        temp = PyJsObject({}, ObjectPrototype)
183        for k, v in six.iteritems(val):
184            temp.put(Js(k), Js(v))
185        return temp
186    elif isinstance(val, (list, tuple)):  #Convert to array
187        return PyJsArray(val, ArrayPrototype)
188    # convert to typedarray
189    elif isinstance(val, JsObjectWrapper):
190        return val.__dict__['_obj']
191    elif NUMPY_AVAILABLE and isinstance(val, numpy.ndarray):
192        if val.dtype == numpy.int8:
193            return PyJsInt8Array(val, Int8ArrayPrototype)
194        elif val.dtype == numpy.uint8 and not Clamped:
195            return PyJsUint8Array(val, Uint8ArrayPrototype)
196        elif val.dtype == numpy.uint8 and Clamped:
197            return PyJsUint8ClampedArray(val, Uint8ClampedArrayPrototype)
198        elif val.dtype == numpy.int16:
199            return PyJsInt16Array(val, Int16ArrayPrototype)
200        elif val.dtype == numpy.uint16:
201            return PyJsUint16Array(val, Uint16ArrayPrototype)
202
203        elif val.dtype == numpy.int32:
204            return PyJsInt32Array(val, Int32ArrayPrototype)
205        elif val.dtype == numpy.uint32:
206            return PyJsUint16Array(val, Uint32ArrayPrototype)
207
208        elif val.dtype == numpy.float32:
209            return PyJsFloat32Array(val, Float32ArrayPrototype)
210        elif val.dtype == numpy.float64:
211            return PyJsFloat64Array(val, Float64ArrayPrototype)
212    else:  # try to convert to js object
213        return py_wrap(val)
214        #raise RuntimeError('Cant convert python type to js (%s)' % repr(val))
215        #try:
216        #    obj = {}
217        #    for name in dir(val):
218        #        if name.startswith('_'):  #dont wrap attrs that start with _
219        #            continue
220        #        value = getattr(val, name)
221        #        import types
222        #        if not isinstance(value, (FunctionType, BuiltinFunctionType, MethodType, BuiltinMethodType,
223        #                                  dict, int, basestring, bool, float, long, list, tuple)):
224        #            continue
225        #        obj[name] = HJs(value)
226        #    return Js(obj)
227        #except:
228        #    raise RuntimeError('Cant convert python type to js (%s)' % repr(val))
229
230
231def Type(val):
232    try:
233        return val.TYPE
234    except:
235        raise RuntimeError('Invalid type: ' + str(val))
236
237
238def is_data_descriptor(desc):
239    return desc and ('value' in desc or 'writable' in desc)
240
241
242def is_accessor_descriptor(desc):
243    return desc and ('get' in desc or 'set' in desc)
244
245
246def is_generic_descriptor(desc):
247    return desc and not (is_data_descriptor(desc)
248                         or is_accessor_descriptor(desc))
249
250
251##############################################################################
252
253
254class PyJs(object):
255    PRIMITIVES = frozenset(
256        ['String', 'Number', 'Boolean', 'Undefined', 'Null'])
257    TYPE = 'Object'
258    Class = None
259    extensible = True
260    prototype = None
261    own = {}
262    GlobalObject = None
263    IS_CHILD_SCOPE = False
264    CONVERT_TO_PY_PRIMITIVES = False
265    value = None
266    buff = None
267
268    def __init__(self, value=None, prototype=None, extensible=False):
269        '''Constructor for Number String and Boolean'''
270        # I dont think this is needed anymore
271        # if self.Class=='String' and not isinstance(value, basestring):
272        #     raise TypeError
273        # if self.Class=='Number':
274        #     if not isinstance(value, float):
275        #         if not (isinstance(value, int) or isinstance(value, long)):
276        #             raise TypeError
277        #         value = float(value)
278        # if self.Class=='Boolean' and not isinstance(value, bool):
279        #     raise TypeError
280        self.value = value
281        self.extensible = extensible
282        self.prototype = prototype
283        self.own = {}
284        self.buff = None
285
286    def is_undefined(self):
287        return self.Class == 'Undefined'
288
289    def is_null(self):
290        return self.Class == 'Null'
291
292    def is_primitive(self):
293        return self.TYPE in self.PRIMITIVES
294
295    def is_object(self):
296        return not self.is_primitive()
297
298    def _type(self):
299        return Type(self)
300
301    def is_callable(self):
302        return hasattr(self, 'call')
303
304    def get_own_property(self, prop):
305        return self.own.get(prop)
306
307    def get_property(self, prop):
308        cand = self.get_own_property(prop)
309        if cand:
310            return cand
311        if self.prototype is not None:
312            return self.prototype.get_property(prop)
313
314    def update_array(self):
315        for i in range(self.get('length').to_uint32()):
316            self.put(str(i), Js(self.buff[i]))
317
318    def get(self, prop):  #external use!
319        #prop = prop.value
320        if self.Class == 'Undefined' or self.Class == 'Null':
321            raise MakeError('TypeError',
322                            'Undefined and null dont have properties (tried getting property %s)' % repr(prop))
323        if not isinstance(prop, basestring):
324            prop = prop.to_string().value
325        if not isinstance(prop, basestring): raise RuntimeError('Bug')
326        if NUMPY_AVAILABLE and prop.isdigit():
327            if isinstance(self.buff, numpy.ndarray):
328                self.update_array()
329        cand = self.get_property(prop)
330        if cand is None:
331            return Js(None)
332        if is_data_descriptor(cand):
333            return cand['value']
334        if cand['get'].is_undefined():
335            return cand['get']
336        return cand['get'].call(self)
337
338    def can_put(self, prop):  #to check
339        desc = self.get_own_property(prop)
340        if desc:  #if we have this property
341            if is_accessor_descriptor(desc):
342                return desc['set'].is_callable(
343                )  # Check if setter method is defined
344            else:  #data desc
345                return desc['writable']
346        if self.prototype is not None:
347            return self.extensible
348        inherited = self.get_property(prop)
349        if inherited is None:
350            return self.extensible
351        if is_accessor_descriptor(inherited):
352            return not inherited['set'].is_undefined()
353        elif self.extensible:
354            return inherited['writable']
355        return False
356
357    def put(self, prop, val, op=None):  #external use!
358        '''Just like in js: self.prop op= val
359           for example when op is '+' it will be self.prop+=val
360           op can be either None for simple assignment or one of:
361           * / % + - << >> & ^ |'''
362        if self.Class == 'Undefined' or self.Class == 'Null':
363            raise MakeError('TypeError',
364                            'Undefined and null don\'t have properties (tried setting property %s)' % repr(prop))
365        if not isinstance(prop, basestring):
366            prop = prop.to_string().value
367        if NUMPY_AVAILABLE and prop.isdigit():
368            if self.Class == 'Int8Array':
369                val = Js(numpy.int8(val.to_number().value))
370            elif self.Class == 'Uint8Array':
371                val = Js(numpy.uint8(val.to_number().value))
372            elif self.Class == 'Uint8ClampedArray':
373                if val < Js(numpy.uint8(0)):
374                    val = Js(numpy.uint8(0))
375                elif val > Js(numpy.uint8(255)):
376                    val = Js(numpy.uint8(255))
377                else:
378                    val = Js(numpy.uint8(val.to_number().value))
379            elif self.Class == 'Int16Array':
380                val = Js(numpy.int16(val.to_number().value))
381            elif self.Class == 'Uint16Array':
382                val = Js(numpy.uint16(val.to_number().value))
383            elif self.Class == 'Int32Array':
384                val = Js(numpy.int32(val.to_number().value))
385            elif self.Class == 'Uint32Array':
386                val = Js(numpy.uint32(val.to_number().value))
387            elif self.Class == 'Float32Array':
388                val = Js(numpy.float32(val.to_number().value))
389            elif self.Class == 'Float64Array':
390                val = Js(numpy.float64(val.to_number().value))
391            if isinstance(self.buff, numpy.ndarray):
392                self.buff[int(prop)] = int(val.to_number().value)
393        #we need to set the value to the incremented one
394        if op is not None:
395            val = getattr(self.get(prop), OP_METHODS[op])(val)
396        if not self.can_put(prop):
397            return val
398        own_desc = self.get_own_property(prop)
399        if is_data_descriptor(own_desc):
400            if self.Class in [
401                    'Array', 'Int8Array', 'Uint8Array', 'Uint8ClampedArray',
402                    'Int16Array', 'Uint16Array', 'Int32Array', 'Uint32Array',
403                    'Float32Array', 'Float64Array'
404            ]:
405                self.define_own_property(prop, {'value': val})
406            else:
407                self.own[prop]['value'] = val
408            return val
409        desc = self.get_property(prop)
410        if is_accessor_descriptor(desc):
411            desc['set'].call(self, (val, ))
412        else:
413            new = {
414                'value': val,
415                'writable': True,
416                'configurable': True,
417                'enumerable': True
418            }
419            if self.Class in [
420                    'Array', 'Int8Array', 'Uint8Array', 'Uint8ClampedArray',
421                    'Int16Array', 'Uint16Array', 'Int32Array', 'Uint32Array',
422                    'Float32Array', 'Float64Array'
423            ]:
424                self.define_own_property(prop, new)
425            else:
426                self.own[prop] = new
427        return val
428
429    def has_property(self, prop):
430        return self.get_property(prop) is not None
431
432    def delete(self, prop):
433        if not isinstance(prop, basestring):
434            prop = prop.to_string().value
435        desc = self.get_own_property(prop)
436        if desc is None:
437            return Js(True)
438        if desc['configurable']:
439            del self.own[prop]
440            return Js(True)
441        return Js(False)
442
443    def default_value(
444            self, hint=None
445    ):  # made a mistake at the very early stage and made it to prefer string... caused lots! of problems
446        order = ('valueOf', 'toString')
447        if hint == 'String' or (hint is None and self.Class == 'Date'):
448            order = ('toString', 'valueOf')
449        for meth_name in order:
450            method = self.get(meth_name)
451            if method is not None and method.is_callable():
452                cand = method.call(self)
453                if cand.is_primitive():
454                    return cand
455        raise MakeError('TypeError',
456                        'Cannot convert object to primitive value')
457
458    def define_own_property(self, prop,
459                            desc):  #Internal use only. External through Object
460        # prop must be a Py string. Desc is either a descriptor or accessor.
461        #Messy method -  raw translation from Ecma spec to prevent any bugs. # todo check this
462        current = self.get_own_property(prop)
463
464        extensible = self.extensible
465        if not current:  #We are creating a new property
466            if not extensible:
467                return False
468            if is_data_descriptor(desc) or is_generic_descriptor(desc):
469                DEFAULT_DATA_DESC = {
470                    'value': undefined,  #undefined
471                    'writable': False,
472                    'enumerable': False,
473                    'configurable': False
474                }
475                DEFAULT_DATA_DESC.update(desc)
476                self.own[prop] = DEFAULT_DATA_DESC
477            else:
478                DEFAULT_ACCESSOR_DESC = {
479                    'get': undefined,  #undefined
480                    'set': undefined,  #undefined
481                    'enumerable': False,
482                    'configurable': False
483                }
484                DEFAULT_ACCESSOR_DESC.update(desc)
485                self.own[prop] = DEFAULT_ACCESSOR_DESC
486            return True
487        if not desc or desc == current:  #We dont need to change anything.
488            return True
489        configurable = current['configurable']
490        if not configurable:  #Prevent changing configurable or enumerable
491            if desc.get('configurable'):
492                return False
493            if 'enumerable' in desc and desc['enumerable'] != current[
494                    'enumerable']:
495                return False
496        if is_generic_descriptor(desc):
497            pass
498        elif is_data_descriptor(current) != is_data_descriptor(desc):
499            if not configurable:
500                return False
501            if is_data_descriptor(current):
502                del current['value']
503                del current['writable']
504                current['set'] = undefined  #undefined
505                current['get'] = undefined  #undefined
506            else:
507                del current['set']
508                del current['get']
509                current['value'] = undefined  #undefined
510                current['writable'] = False
511        elif is_data_descriptor(current) and is_data_descriptor(desc):
512            if not configurable:
513                if not current['writable'] and desc.get('writable'):
514                    return False
515            if not current['writable'] and 'value' in desc and current[
516                    'value'] != desc['value']:
517                return False
518        elif is_accessor_descriptor(current) and is_accessor_descriptor(desc):
519            if not configurable:
520                if 'set' in desc and desc['set'] is not current['set']:
521                    return False
522                if 'get' in desc and desc['get'] is not current['get']:
523                    return False
524        current.update(desc)
525        return True
526
527    #these methods will work only for Number class
528    def is_infinity(self):
529        assert self.Class == 'Number'
530        return self.value == float('inf') or self.value == -float('inf')
531
532    def is_nan(self):
533        assert self.Class == 'Number'
534        return self.value != self.value  #nan!=nan evaluates to true
535
536    def is_finite(self):
537        return not (self.is_nan() or self.is_infinity())
538
539    #Type Conversions. to_type. All must return pyjs subclass instance
540
541    def to_primitive(self, hint=None):
542        if self.is_primitive():
543            return self
544        if hint is None and (
545                self.Class == 'Number' or self.Class == 'Boolean'
546        ):  # favour number for Class== Number or Boolean default = String
547            hint = 'Number'
548        return self.default_value(hint)
549
550    def to_boolean(self):
551        typ = Type(self)
552        if typ == 'Boolean':  #no need to convert
553            return self
554        elif typ == 'Null' or typ == 'Undefined':  #they are both always false
555            return false
556        elif typ == 'Number' or typ == 'String':  #false only for 0, '' and NaN
557            return Js(bool(
558                self.value
559                and self.value == self.value))  # test for nan (nan -> flase)
560        else:  #object -  always true
561            return true
562
563    def to_number(self):
564        typ = Type(self)
565        if typ == 'Null':  #null is 0
566            return Js(0)
567        elif typ == 'Undefined':  # undefined is NaN
568            return NaN
569        elif typ == 'Boolean':  # 1 for true 0 for false
570            return Js(int(self.value))
571        elif typ == 'Number':  # or self.Class=='Number':   # no need to convert
572            return self
573        elif typ == 'String':
574            s = self.value.strip()  #Strip white space
575            if not s:  # '' is simply 0
576                return Js(0)
577            if 'x' in s or 'X' in s[:3]:  #hex (positive only)
578                try:  # try to convert
579                    num = int(s, 16)
580                except ValueError:  # could not convert > NaN
581                    return NaN
582                return Js(num)
583            sign = 1  #get sign
584            if s[0] in '+-':
585                if s[0] == '-':
586                    sign = -1
587                s = s[1:]
588            if s == 'Infinity':  #Check for infinity keyword. 'NaN' will be NaN anyway.
589                return Js(sign * float('inf'))
590            try:  #decimal try
591                num = sign * float(s)  # Converted
592            except ValueError:
593                return NaN  # could not convert to decimal  > return NaN
594            return Js(num)
595        else:  #object -  most likely it will be NaN.
596            return self.to_primitive('Number').to_number()
597
598    def to_string(self):
599        typ = Type(self)
600        if typ == 'Null':
601            return Js('null')
602        elif typ == 'Undefined':
603            return Js('undefined')
604        elif typ == 'Boolean':
605            return Js('true') if self.value else Js('false')
606        elif typ == 'Number':  #or self.Class=='Number':
607            return Js(unicode(js_dtoa(self.value)))
608        elif typ == 'String':
609            return self
610        else:  #object
611            return self.to_primitive('String').to_string()
612
613    def to_object(self):
614        typ = self.TYPE
615        if typ == 'Null' or typ == 'Undefined':
616            raise MakeError('TypeError',
617                            'undefined or null can\'t be converted to object')
618        elif typ == 'Boolean':  # Unsure here... todo repair here
619            return Boolean.create(self)
620        elif typ == 'Number':  #?
621            return Number.create(self)
622        elif typ == 'String':  #?
623            return String.create(self)
624        else:  #object
625            return self
626
627    def to_int32(self):
628        num = self.to_number()
629        if num.is_nan() or num.is_infinity():
630            return 0
631        int32 = int(num.value) % 2**32
632        return int(int32 - 2**32 if int32 >= 2**31 else int32)
633
634    def strict_equality_comparison(self, other):
635        return PyJsStrictEq(self, other)
636
637    def cok(self):
638        """Check object coercible"""
639        if self.Class in ('Undefined', 'Null'):
640            raise MakeError('TypeError',
641                            'undefined or null can\'t be converted to object')
642
643    def to_int(self):
644        num = self.to_number()
645        if num.is_nan():
646            return 0
647        elif num.is_infinity():
648            return 10**20 if num.value > 0 else -10**20
649        return int(num.value)
650
651    def to_uint32(self):
652        num = self.to_number()
653        if num.is_nan() or num.is_infinity():
654            return 0
655        return int(num.value) % 2**32
656
657    def to_uint16(self):
658        num = self.to_number()
659        if num.is_nan() or num.is_infinity():
660            return 0
661        return int(num.value) % 2**16
662
663    def to_int16(self):
664        num = self.to_number()
665        if num.is_nan() or num.is_infinity():
666            return 0
667        int16 = int(num.value) % 2**16
668        return int(int16 - 2**16 if int16 >= 2**15 else int16)
669
670    def same_as(self, other):
671        typ = Type(self)
672        if typ != other.Class:
673            return False
674        if typ == 'Undefined' or typ == 'Null':
675            return True
676        if typ == 'Boolean' or typ == 'Number' or typ == 'String':
677            return self.value == other.value
678        else:  #object
679            return self is other  #Id compare.
680
681    #Not to be used by translation (only internal use)
682    def __getitem__(self, item):
683        return self.get(
684            str(item) if not isinstance(item, PyJs) else item.to_string().
685            value)
686
687    def __setitem__(self, item, value):
688        self.put(
689            str(item) if not isinstance(item, PyJs) else
690            item.to_string().value, Js(value))
691
692    def __len__(self):
693        try:
694            return self.get('length').to_uint32()
695        except:
696            raise TypeError(
697                'This object (%s) does not have length property' % self.Class)
698
699    #Oprators-------------
700    #Unary, other will be implemented as functions. Increments and decrements
701    # will be methods of Number class
702    def __neg__(self):  #-u
703        return Js(-self.to_number().value)
704
705    def __pos__(self):  #+u
706        return self.to_number()
707
708    def __invert__(self):  #~u
709        return Js(Js(~self.to_int32()).to_int32())
710
711    def neg(self):  # !u  cant do 'not u' :(
712        return Js(not self.to_boolean().value)
713
714    def __nonzero__(self):
715        return self.to_boolean().value
716
717    def __bool__(self):
718        return self.to_boolean().value
719
720    def typeof(self):
721        if self.is_callable():
722            return Js('function')
723        typ = Type(self).lower()
724        if typ == 'null':
725            typ = 'object'
726        return Js(typ)
727
728    #Bitwise operators
729    #  <<, >>,  &, ^, |
730
731    # <<
732    def __lshift__(self, other):
733        lnum = self.to_int32()
734        rnum = other.to_uint32()
735        shiftCount = rnum & 0x1F
736        return Js(Js(lnum << shiftCount).to_int32())
737
738    # >>
739    def __rshift__(self, other):
740        lnum = self.to_int32()
741        rnum = other.to_uint32()
742        shiftCount = rnum & 0x1F
743        return Js(Js(lnum >> shiftCount).to_int32())
744
745    # >>>
746    def pyjs_bshift(self, other):
747        lnum = self.to_uint32()
748        rnum = other.to_uint32()
749        shiftCount = rnum & 0x1F
750        return Js(Js(lnum >> shiftCount).to_uint32())
751
752    # &
753    def __and__(self, other):
754        lnum = self.to_int32()
755        rnum = other.to_int32()
756        return Js(Js(lnum & rnum).to_int32())
757
758    # ^
759    def __xor__(self, other):
760        lnum = self.to_int32()
761        rnum = other.to_int32()
762        return Js(Js(lnum ^ rnum).to_int32())
763
764    # |
765    def __or__(self, other):
766        lnum = self.to_int32()
767        rnum = other.to_int32()
768        return Js(Js(lnum | rnum).to_int32())
769
770    # Additive operators
771    # + and - are implemented here
772
773    # +
774    def __add__(self, other):
775        a = self.to_primitive()
776        b = other.to_primitive()
777        if a.TYPE == 'String' or b.TYPE == 'String':
778            return Js(a.to_string().value + b.to_string().value)
779        a = a.to_number()
780        b = b.to_number()
781        return Js(a.value + b.value)
782
783    # -
784    def __sub__(self, other):
785        return Js(self.to_number().value - other.to_number().value)
786
787    #Multiplicative operators
788    # *, / and % are implemented here
789
790    # *
791    def __mul__(self, other):
792        return Js(self.to_number().value * other.to_number().value)
793
794    # /
795    def __div__(self, other):
796        a = self.to_number().value
797        b = other.to_number().value
798        if b:
799            return Js(a / b)
800        if not a or a != a:
801            return NaN
802        return Infinity if a > 0 else -Infinity
803
804    # %
805    def __mod__(self, other):
806        a = self.to_number().value
807        b = other.to_number().value
808        if abs(a) == float('inf') or not b:
809            return NaN
810        if abs(b) == float('inf'):
811            return Js(a)
812        pyres = Js(a % b)  #different signs in python and javascript
813        #python has the same sign as b and js has the same
814        #sign as a.
815        if a < 0 and pyres.value > 0:
816            pyres.value -= abs(b)
817        elif a > 0 and pyres.value < 0:
818            pyres.value += abs(b)
819        return Js(pyres)
820
821    #Comparisons (I dont implement === and !== here, these
822    # will be implemented as external functions later)
823    # <, <=, !=, ==, >=, > are implemented here.
824
825    def abstract_relational_comparison(self, other, self_first=True):
826        ''' self<other if self_first else other<self.
827           Returns the result of the question: is self smaller than other?
828           in case self_first is false it returns the answer of:
829                                               is other smaller than self.
830           result is PyJs type: bool or undefined'''
831        px = self.to_primitive('Number')
832        py = other.to_primitive('Number')
833        if not self_first:  #reverse order
834            px, py = py, px
835        if not (px.Class == 'String' and py.Class == 'String'):
836            px, py = px.to_number(), py.to_number()
837            if px.is_nan() or py.is_nan():
838                return undefined
839            return Js(px.value < py.value)  # same cmp algorithm
840        else:
841            # I am pretty sure that python has the same
842            # string cmp algorithm but I have to confirm it
843            return Js(px.value < py.value)
844
845    #<
846    def __lt__(self, other):
847        res = self.abstract_relational_comparison(other, True)
848        if res.is_undefined():
849            return false
850        return res
851
852    #<=
853    def __le__(self, other):
854        res = self.abstract_relational_comparison(other, False)
855        if res.is_undefined():
856            return false
857        return res.neg()
858
859    #>=
860    def __ge__(self, other):
861        res = self.abstract_relational_comparison(other, True)
862        if res.is_undefined():
863            return false
864        return res.neg()
865
866    #>
867    def __gt__(self, other):
868        res = self.abstract_relational_comparison(other, False)
869        if res.is_undefined():
870            return false
871        return res
872
873    def abstract_equality_comparison(self, other):
874        ''' returns the result of JS == compare.
875           result is PyJs type: bool'''
876        tx, ty = self.TYPE, other.TYPE
877        if tx == ty:
878            if tx == 'Undefined' or tx == 'Null':
879                return true
880            if tx == 'Number' or tx == 'String' or tx == 'Boolean':
881                return Js(self.value == other.value)
882            return Js(self is other)  # Object
883        elif (tx == 'Undefined' and ty == 'Null') or (ty == 'Undefined'
884                                                      and tx == 'Null'):
885            return true
886        elif tx == 'Number' and ty == 'String':
887            return self.abstract_equality_comparison(other.to_number())
888        elif tx == 'String' and ty == 'Number':
889            return self.to_number().abstract_equality_comparison(other)
890        elif tx == 'Boolean':
891            return self.to_number().abstract_equality_comparison(other)
892        elif ty == 'Boolean':
893            return self.abstract_equality_comparison(other.to_number())
894        elif (tx == 'String' or tx == 'Number') and other.is_object():
895            return self.abstract_equality_comparison(other.to_primitive())
896        elif (ty == 'String' or ty == 'Number') and self.is_object():
897            return self.to_primitive().abstract_equality_comparison(other)
898        else:
899            return false
900
901    #==
902    def __eq__(self, other):
903        return self.abstract_equality_comparison(other)
904
905    #!=
906    def __ne__(self, other):
907        return self.abstract_equality_comparison(other).neg()
908
909    #Other methods (instanceof)
910
911    def instanceof(self, other):
912        '''checks if self is instance of other'''
913        if not hasattr(other, 'has_instance'):
914            return false
915        return other.has_instance(self)
916
917    #iteration
918    def __iter__(self):
919        #Returns a generator of all own enumerable properties
920        # since the size od self.own can change we need to use different method of iteration.
921        # SLOW! New items will NOT show up.
922        returned = {}
923        if not self.IS_CHILD_SCOPE:
924            cands = sorted(
925                name for name in self.own if self.own[name]['enumerable'])
926        else:
927            cands = sorted(name for name in self.own)
928        for cand in cands:
929            check = self.own.get(cand)
930            if check and check['enumerable']:
931                yield Js(cand)
932
933    def contains(self, other):
934        if not self.is_object():
935            raise MakeError(
936                'TypeError',
937                "You can\'t use 'in' operator to search in non-objects")
938        return Js(self.has_property(other.to_string().value))
939
940    #Other Special methods
941    def __call__(self, *args):
942        '''Call a property prop as a function (this will be global object).
943
944        NOTE: dont pass this and arguments here, these will be added
945        automatically!'''
946        if not self.is_callable():
947            raise MakeError('TypeError',
948                            '%s is not a function' % self.typeof())
949        return self.call(self.GlobalObject, args)
950
951    def create(self, *args):
952        '''Generally not a constructor, raise an error'''
953        raise MakeError('TypeError', '%s is not a constructor' % self.Class)
954
955    def __unicode__(self):
956        return self.to_string().value
957
958    def __repr__(self):
959        if self.Class == 'Object':
960            res = []
961            for e in self:
962                res.append(str_repr(e.value) + ': ' + str_repr(self.get(e)))
963            return '{%s}' % ', '.join(res)
964        elif self.Class == 'String':
965            return str_repr(self.value)
966        elif self.Class in [
967                'Array', 'Int8Array', 'Uint8Array', 'Uint8ClampedArray',
968                'Int16Array', 'Uint16Array', 'Int32Array', 'Uint32Array',
969                'Float32Array', 'Float64Array'
970        ]:
971            res = []
972            for e in self:
973                res.append(repr(self.get(e)))
974            return '[%s]' % ', '.join(res)
975        else:
976            val = str_repr(self.to_string().value)
977            return val
978
979    def _fuck_python3(
980            self
981    ):  # hack to make object hashable in python 3 (__eq__ causes problems)
982        return object.__hash__(self)
983
984    def callprop(self, prop, *args):
985        '''Call a property prop as a method (this will be self).
986
987        NOTE: dont pass this and arguments here, these will be added
988        automatically!'''
989        if not isinstance(prop, basestring):
990            prop = prop.to_string().value
991        cand = self.get(prop)
992        if not cand.is_callable():
993            raise MakeError('TypeError',
994                            '%s is not a function (tried calling property %s of %s)' % (
995                            cand.typeof(), repr(prop), repr(self.Class)))
996        return cand.call(self, args)
997
998    def to_python(self):
999        """returns equivalent python object.
1000         for example if this object is javascript array then this method will return equivalent python array"""
1001        return to_python(self)
1002
1003    def to_py(self):
1004        """returns equivalent python object.
1005         for example if this object is javascript array then this method will return equivalent python array"""
1006        return self.to_python()
1007
1008
1009if six.PY3:
1010    PyJs.__hash__ = PyJs._fuck_python3
1011    PyJs.__truediv__ = PyJs.__div__
1012#Define some more classes representing operators:
1013
1014
1015def PyJsStrictEq(a, b):
1016    '''a===b'''
1017    tx, ty = Type(a), Type(b)
1018    if tx != ty:
1019        return false
1020    if tx == 'Undefined' or tx == 'Null':
1021        return true
1022    if a.is_primitive():  #string bool and number case
1023        return Js(a.value == b.value)
1024    if a.Class == b.Class == 'PyObjectWrapper':
1025        return Js(a.obj == b.obj)
1026    return Js(a is b)  # object comparison
1027
1028
1029def PyJsStrictNeq(a, b):
1030    ''' a!==b'''
1031    return PyJsStrictEq(a, b).neg()
1032
1033
1034def PyJsBshift(a, b):
1035    """a>>>b"""
1036    return a.pyjs_bshift(b)
1037
1038
1039def PyJsComma(a, b):
1040    return b
1041
1042
1043from .internals.simplex import JsException as PyJsException, js_dtoa
1044import pyjsparser
1045pyjsparser.parser.ENABLE_JS2PY_ERRORS = lambda msg: MakeError('SyntaxError', msg)
1046
1047
1048class PyJsSwitchException(Exception):
1049    pass
1050
1051
1052PyJs.MakeError = staticmethod(MakeError)
1053
1054
1055def JsToPyException(js):
1056    temp = PyJsException()
1057    temp.mes = js
1058    return temp
1059
1060
1061def PyExceptionToJs(py):
1062    return py.mes
1063
1064
1065#Scope class it will hold all the variables accessible to user
1066class Scope(PyJs):
1067    Class = 'global'
1068    extensible = True
1069    IS_CHILD_SCOPE = True
1070
1071    # todo speed up
1072    # in order to speed up this very important class the top scope should behave differently than
1073    # child scopes, child scope should not have this property descriptor thing because they cant be changed anyway
1074    # they are all confugurable= False
1075
1076    def __init__(self, scope, closure=None):
1077        """Doc"""
1078        self.prototype = closure
1079        if closure is None:
1080            # global, top level scope
1081            self.own = {}
1082            for k, v in six.iteritems(scope):
1083                # set all the global items
1084                self.define_own_property(
1085                    k, {
1086                        'value': v,
1087                        'configurable': False,
1088                        'writable': False,
1089                        'enumerable': False
1090                    })
1091        else:
1092            # not global, less powerful but faster closure.
1093            self.own = scope  # simple dictionary which maps name directly to js object.
1094
1095    def register(self, lval):
1096        # registered keeps only global registered variables
1097        if self.prototype is None:
1098            # define in global scope
1099            if lval in self.own:
1100                self.own[lval]['configurable'] = False
1101            else:
1102                self.define_own_property(
1103                    lval, {
1104                        'value': undefined,
1105                        'configurable': False,
1106                        'writable': True,
1107                        'enumerable': True
1108                    })
1109        elif lval not in self.own:
1110            # define in local scope since it has not been defined yet
1111            self.own[lval] = undefined  # default value
1112
1113    def registers(self, lvals):
1114        """register multiple variables"""
1115        for lval in lvals:
1116            self.register(lval)
1117
1118    def put(self, lval, val, op=None):
1119        if self.prototype is None:
1120            # global scope put, simple
1121            return PyJs.put(self, lval, val, op)
1122        else:
1123            # trying to put in local scope
1124            # we dont know yet in which scope we should place this var
1125            if lval in self.own:
1126                if op:  # increment operation
1127                    val = getattr(self.own[lval], OP_METHODS[op])(val)
1128                self.own[lval] = val
1129                return val
1130            else:
1131                #try to put in the lower scope since we cant put in this one (var wasn't registered)
1132                return self.prototype.put(lval, val, op)
1133
1134    def force_own_put(self, prop, val, configurable=False):
1135        if self.prototype is None:  # global scope
1136            self.own[prop] = {
1137                'value': val,
1138                'writable': True,
1139                'enumerable': True,
1140                'configurable': configurable
1141            }
1142        else:
1143            self.own[prop] = val
1144
1145    def get(self, prop, throw=True):
1146        #note prop is always a Py String
1147        if not isinstance(prop, basestring):
1148            prop = prop.to_string().value
1149        if self.prototype is not None:
1150            # fast local scope
1151            cand = self.own.get(prop)
1152            if cand is None:
1153                return self.prototype.get(prop, throw)
1154            return cand
1155        # slow, global scope
1156        if prop not in self.own:
1157            if throw:
1158                raise MakeError('ReferenceError', '%s is not defined' % prop)
1159            return undefined
1160        return PyJs.get(self, prop)
1161
1162    def delete(self, lval):
1163        if self.prototype is not None:
1164            if lval in self.own:
1165                return false
1166            return self.prototype.delete(lval)
1167        # we are in global scope here. Must exist and be configurable to delete
1168        if lval not in self.own:
1169            # this lval does not exist, why do you want to delete it???
1170            return true
1171        if self.own[lval]['configurable']:
1172            del self.own[lval]
1173            return true
1174        # not configurable, cant delete
1175        return false
1176
1177    def pyimport(self, name, module):
1178        self.register(name)
1179        self.put(name, py_wrap(module))
1180
1181    def __repr__(self):
1182        return u'[Object Global]'
1183
1184    def to_python(self):
1185        return to_python(self)
1186
1187
1188class This(Scope):
1189    IS_CHILD_SCOPE = False
1190
1191    def get(self, prop, throw=False):
1192        return Scope.get(self, prop, throw)
1193
1194
1195class JsObjectWrapper(object):
1196    def __init__(self, obj):
1197        self.__dict__['_obj'] = obj
1198
1199    def __call__(self, *args):
1200        args = tuple(Js(e) for e in args)
1201        if '_prop_of' in self.__dict__:
1202            parent, meth = self.__dict__['_prop_of']
1203            return to_python(parent._obj.callprop(meth, *args))
1204        return to_python(self._obj(*args))
1205
1206    def __getattr__(self, item):
1207        if item == 'new' and self._obj.is_callable():
1208            # return instance initializer
1209            def PyJsInstanceInit(*args):
1210                args = tuple(Js(e) for e in args)
1211                return self._obj.create(*args).to_python()
1212
1213            return PyJsInstanceInit
1214        cand = to_python(self._obj.get(str(item)))
1215        # handling method calling... obj.meth(). Value of this in meth should be self
1216        if isinstance(cand, self.__class__):
1217            cand.__dict__['_prop_of'] = self, str(item)
1218        return cand
1219
1220    def __setattr__(self, item, value):
1221        self._obj.put(str(item), Js(value))
1222
1223    def __getitem__(self, item):
1224        cand = to_python(self._obj.get(str(item)))
1225        if isinstance(cand, self.__class__):
1226            cand.__dict__['_prop_of'] = self, str(item)
1227        return cand
1228
1229    def __setitem__(self, item, value):
1230        self._obj.put(str(item), Js(value))
1231
1232    def __iter__(self):
1233        if self._obj.Class in [
1234                'Array', 'Int8Array', 'Uint8Array', 'Uint8ClampedArray',
1235                'Int16Array', 'Uint16Array', 'Int32Array', 'Uint32Array',
1236                'Float32Array', 'Float64Array'
1237        ]:
1238            return iter(self.to_list())
1239        elif self._obj.Class == 'Object':
1240            return iter(self.to_dict())
1241        else:
1242            raise MakeError('TypeError',
1243                            '%s is not iterable in Python' % self._obj.Class)
1244
1245    def __repr__(self):
1246        if self._obj.is_primitive() or self._obj.is_callable():
1247            return repr(self._obj)
1248        elif self._obj.Class in ('Array', 'Int8Array', 'Uint8Array',
1249                                 'Uint8ClampedArray', 'Int16Array',
1250                                 'Uint16Array', 'Int32Array', 'Uint32Array',
1251                                 'Float32Array', 'Float64Array', 'Arguments'):
1252            return repr(self.to_list())
1253        return repr(self.to_dict())
1254
1255    def __len__(self):
1256        return len(self._obj)
1257
1258    def __nonzero__(self):
1259        return bool(self._obj)
1260
1261    def __bool__(self):
1262        return bool(self._obj)
1263
1264    def to_dict(self):
1265        return to_dict(self.__dict__['_obj'])
1266
1267    def to_list(self):
1268        return to_list(self.__dict__['_obj'])
1269
1270
1271class PyObjectWrapper(PyJs):
1272    Class = 'PyObjectWrapper'
1273
1274    def __init__(self, obj):
1275        self.obj = obj
1276
1277    def get(self, prop):
1278        if not isinstance(prop, basestring):
1279            prop = prop.to_string().value
1280        try:
1281            if prop.isdigit():
1282                return py_wrap(self.obj[int(prop)])
1283            return py_wrap(getattr(self.obj, prop))
1284        except:
1285            return undefined
1286
1287    def put(self, prop, val, op=None, throw=False):
1288        if not isinstance(prop, basestring):
1289            prop = prop.to_string().value
1290        try:
1291            if isinstance(op, bool):
1292                raise ValueError("Op must be a string")
1293            elif op is not None:
1294                if op:  # increment operation
1295                    val = getattr(self.get(prop), OP_METHODS[op])(val)
1296            setattr(self.obj, prop, to_python(val))
1297        except AttributeError:
1298            raise MakeError('TypeError', 'Read only object probably...')
1299        return val
1300
1301    def __call__(self, *args):
1302        py_args = tuple(to_python(e) for e in args)
1303        try:
1304            py_res = self.obj.__call__(*py_args)
1305        except Exception as e:
1306            message = 'your Python function failed!  '
1307            try:
1308                message += str(e)
1309            except:
1310                pass
1311            raise MakeError('Error', message)
1312        return py_wrap(py_res)
1313
1314    def callprop(self, prop, *args):
1315        py_args = tuple(to_python(e) for e in args)
1316        if not isinstance(prop, basestring):
1317            prop = prop.to_string().value
1318        return self.get(prop)(*py_args)
1319
1320    def delete(self, prop):
1321        if not isinstance(prop, basestring):
1322            prop = prop.to_string().value
1323        try:
1324            if prop.isdigit():
1325                del self.obj[int(prop)]
1326            else:
1327                delattr(self.obj, prop)
1328            return true
1329        except:
1330            return false
1331
1332    def __repr__(self):
1333        return 'PyObjectWrapper(%s)' % str(self.obj)
1334
1335    def to_python(self):
1336        return self.obj
1337
1338    def to_py(self):
1339        return self.obj
1340
1341
1342def py_wrap(py):
1343    if isinstance(py, (FunctionType, BuiltinFunctionType, MethodType,
1344                       BuiltinMethodType, dict, int, str, bool, float, list,
1345                       tuple, long, basestring)) or py is None:
1346        return HJs(py)
1347    return PyObjectWrapper(py)
1348
1349
1350##############################################################################
1351#Define types
1352
1353
1354#Object
1355class PyJsObject(PyJs):
1356    Class = 'Object'
1357
1358    def __init__(self, prop_descs={}, prototype=None, extensible=True):
1359        self.prototype = prototype
1360        self.extensible = extensible
1361        self.own = {}
1362        for prop, desc in six.iteritems(prop_descs):
1363            self.define_own_property(prop, desc)
1364
1365    def __repr__(self):
1366        return repr(self.to_python().to_dict())
1367
1368
1369ObjectPrototype = PyJsObject()
1370
1371
1372#Function
1373class PyJsFunction(PyJs):
1374    Class = 'Function'
1375
1376    def __init__(self, func, prototype=None, extensible=True, source=None):
1377        cand = fix_js_args(func)
1378        has_scope = cand is func
1379        func = cand
1380        self.argcount = six.get_function_code(func).co_argcount - 2 - has_scope
1381        self.code = func
1382        self.source = source if source else '{ [python code] }'
1383        self.func_name = func.__name__ if not func.__name__.startswith(
1384            'PyJs_anonymous') else ''
1385        self.extensible = extensible
1386        self.prototype = prototype
1387        self.own = {}
1388        #set own property length to the number of arguments
1389        self.define_own_property(
1390            'length', {
1391                'value': Js(self.argcount),
1392                'writable': False,
1393                'enumerable': False,
1394                'configurable': False
1395            })
1396
1397        if self.func_name:
1398            self.define_own_property(
1399                'name', {
1400                    'value': Js(self.func_name),
1401                    'writable': False,
1402                    'enumerable': False,
1403                    'configurable': True
1404                })
1405
1406        # set own prototype
1407        proto = Js({})
1408        # constructor points to this function
1409        proto.define_own_property(
1410            'constructor', {
1411                'value': self,
1412                'writable': True,
1413                'enumerable': False,
1414                'configurable': True
1415            })
1416        self.define_own_property(
1417            'prototype', {
1418                'value': proto,
1419                'writable': True,
1420                'enumerable': False,
1421                'configurable': False
1422            })
1423
1424    def _set_name(self, name):
1425        '''name is py type'''
1426        if self.own.get('name'):
1427            self.func_name = name
1428            self.own['name']['value'] = Js(name)
1429
1430    def construct(self, *args):
1431        proto = self.get('prototype')
1432        if not proto.is_object():  # set to standard prototype
1433            proto = ObjectPrototype
1434        obj = PyJsObject(prototype=proto)
1435        cand = self.call(obj, *args)
1436        return cand if cand.is_object() else obj
1437
1438    def call(self, this, args=()):
1439        '''Calls this function and returns a result
1440        (converted to PyJs type so func can return python types)
1441
1442        this must be a PyJs object and args must be a python tuple of PyJs objects.
1443
1444        arguments object is passed automatically and will be equal to Js(args)
1445        (tuple converted to arguments object).You dont need to worry about number
1446        of arguments you provide if you supply less then missing ones will be set
1447        to undefined (but not present in arguments object).
1448        And if you supply too much then excess will not be passed
1449        (but they will be present in arguments object).
1450        '''
1451        if not hasattr(args, '__iter__'):  #get rid of it later
1452            args = (args, )
1453        args = tuple(Js(e) for e in args)  # this wont be needed later
1454
1455        arguments = PyJsArguments(
1456            args, self)  # tuple will be converted to arguments object.
1457        arglen = self.argcount  #function expects this number of args.
1458        if len(args) > arglen:
1459            args = args[0:arglen]
1460        elif len(args) < arglen:
1461            args += (undefined, ) * (arglen - len(args))
1462        args += this, arguments  #append extra params to the arg list
1463        try:
1464            return Js(self.code(*args))
1465        except NotImplementedError:
1466            raise
1467        except RuntimeError as e:  # maximum recursion
1468            try:
1469                msg = e.message
1470            except:
1471                msg = repr(e)
1472            raise MakeError('RangeError', msg)
1473
1474    def has_instance(self, other):
1475        # I am not sure here so instanceof may not work lol.
1476        if not other.is_object():
1477            return false
1478        proto = self.get('prototype')
1479        if not proto.is_object():
1480            raise TypeError(
1481                'Function has non-object prototype in instanceof check')
1482        while True:
1483            other = other.prototype
1484            if not other:  # todo make sure that the condition is not None or null
1485                return false
1486            if other is proto:
1487                return true
1488
1489    def create(self, *args):
1490        proto = self.get('prototype')
1491        if not proto.is_object():
1492            proto = ObjectPrototype
1493        new = PyJsObject(prototype=proto)
1494        res = self.call(new, args)
1495        if res.is_object():
1496            return res
1497        return new
1498
1499
1500class PyJsBoundFunction(PyJsFunction):
1501    def __init__(self, target, bound_this, bound_args):
1502        self.target = target
1503        self.bound_this = bound_this
1504        self.bound_args = bound_args
1505        self.argcount = target.argcount
1506        self.code = target.code
1507        self.source = target.source
1508        self.func_name = target.func_name
1509        self.extensible = True
1510        self.prototype = FunctionPrototype
1511        self.own = {}
1512        # set own property length to the number of arguments
1513        self.define_own_property(
1514            'length', {
1515                'value': target.get('length') - Js(len(self.bound_args)),
1516                'writable': False,
1517                'enumerable': False,
1518                'configurable': False
1519            })
1520
1521        if self.func_name:
1522            self.define_own_property(
1523                'name', {
1524                    'value': Js(self.func_name),
1525                    'writable': False,
1526                    'enumerable': False,
1527                    'configurable': True
1528                })
1529
1530        # set own prototype
1531        proto = Js({})
1532        # constructor points to this function
1533        proto.define_own_property(
1534            'constructor', {
1535                'value': self,
1536                'writable': True,
1537                'enumerable': False,
1538                'configurable': True
1539            })
1540        self.define_own_property(
1541            'prototype', {
1542                'value': proto,
1543                'writable': True,
1544                'enumerable': False,
1545                'configurable': False
1546            })
1547
1548    def call(self, this, args=()):
1549        return self.target.call(self.bound_this, self.bound_args + args)
1550
1551    def has_instance(self, other):
1552        return self.target.has_instance(other)
1553
1554
1555PyJs.PyJsBoundFunction = PyJsBoundFunction
1556
1557OP_METHODS = {
1558    '*': '__mul__',
1559    '/': '__div__',
1560    '%': '__mod__',
1561    '+': '__add__',
1562    '-': '__sub__',
1563    '<<': '__lshift__',
1564    '>>': '__rshift__',
1565    '&': '__and__',
1566    '^': '__xor__',
1567    '|': '__or__',
1568    '>>>': 'pyjs_bshift'
1569}
1570
1571
1572def Empty():
1573    return Js(None)
1574
1575
1576#Number
1577class PyJsNumber(PyJs):  #Note i dont implement +0 and -0. Just 0.
1578    TYPE = 'Number'
1579    Class = 'Number'
1580
1581
1582NumberPrototype = PyJsObject({}, ObjectPrototype)
1583NumberPrototype.Class = 'Number'
1584NumberPrototype.value = 0
1585
1586Infinity = PyJsNumber(float('inf'), NumberPrototype)
1587NaN = PyJsNumber(float('nan'), NumberPrototype)
1588PyJs.NaN = NaN
1589PyJs.Infinity = Infinity
1590
1591# This dict aims to increase speed of string creation by storing character instances
1592CHAR_BANK = {}
1593NUM_BANK = {}
1594PyJs.CHAR_BANK = CHAR_BANK
1595
1596
1597#String
1598# Different than implementation design in order to improve performance
1599#for example I dont create separate property for each character in string, it would take ages.
1600class PyJsString(PyJs):
1601    TYPE = 'String'
1602    Class = 'String'
1603    extensible = False
1604
1605    def __init__(self, value=None, prototype=None):
1606        '''Constructor for Number String and Boolean'''
1607        if not isinstance(value, basestring):
1608            raise TypeError  # this will be internal error
1609        self.value = value
1610        self.prototype = prototype
1611        self.own = {}
1612        # this should be optimized because its mych slower than python str creation (about 50 times!)
1613        # Dont create separate properties for every index. Just
1614        self.own['length'] = {
1615            'value': Js(len(value)),
1616            'writable': False,
1617            'enumerable': False,
1618            'configurable': False
1619        }
1620        if len(value) == 1:
1621            CHAR_BANK[value] = self  #, 'writable': False,
1622            # 'enumerable': True, 'configurable': False}
1623
1624    def get(self, prop):
1625        if not isinstance(prop, basestring):
1626            prop = prop.to_string().value
1627        try:
1628            index = int(prop)
1629            if index < 0:
1630                return undefined
1631            char = self.value[index]
1632            if char not in CHAR_BANK:
1633                Js(char)  # this will add char to CHAR BANK
1634            return CHAR_BANK[char]
1635        except Exception:
1636            pass
1637        return PyJs.get(self, prop)
1638
1639    def can_put(self, prop):
1640        return False
1641
1642    def __iter__(self):
1643        for i in xrange(len(self.value)):
1644            yield Js(i)  # maybe create an int bank?
1645
1646
1647StringPrototype = PyJsObject({}, ObjectPrototype)
1648StringPrototype.Class = 'String'
1649StringPrototype.value = ''
1650
1651CHAR_BANK[''] = Js('')
1652
1653
1654#Boolean
1655class PyJsBoolean(PyJs):
1656    TYPE = 'Boolean'
1657    Class = 'Boolean'
1658
1659
1660BooleanPrototype = PyJsObject({}, ObjectPrototype)
1661BooleanPrototype.Class = 'Boolean'
1662BooleanPrototype.value = False
1663
1664true = PyJsBoolean(True, BooleanPrototype)
1665false = PyJsBoolean(False, BooleanPrototype)
1666
1667
1668#Undefined
1669class PyJsUndefined(PyJs):
1670    TYPE = 'Undefined'
1671    Class = 'Undefined'
1672
1673    def __init__(self):
1674        pass
1675
1676
1677undefined = PyJsUndefined()
1678
1679
1680#Null
1681class PyJsNull(PyJs):
1682    TYPE = 'Null'
1683    Class = 'Null'
1684
1685    def __init__(self):
1686        pass
1687
1688
1689null = PyJsNull()
1690PyJs.null = null
1691
1692
1693class PyJsArray(PyJs):
1694    Class = 'Array'
1695
1696    def __init__(self, arr=[], prototype=None):
1697        self.extensible = True
1698        self.prototype = prototype
1699        self.own = {
1700            'length': {
1701                'value': Js(0),
1702                'writable': True,
1703                'enumerable': False,
1704                'configurable': False
1705            }
1706        }
1707        for i, e in enumerate(arr):
1708            self.define_own_property(
1709                str(i), {
1710                    'value': Js(e),
1711                    'writable': True,
1712                    'enumerable': True,
1713                    'configurable': True
1714                })
1715
1716    def define_own_property(self, prop, desc):
1717        old_len_desc = self.get_own_property('length')
1718        old_len = old_len_desc[
1719            'value'].value  #  value is js type so convert to py.
1720        if prop == 'length':
1721            if 'value' not in desc:
1722                return PyJs.define_own_property(self, prop, desc)
1723            new_len = desc['value'].to_uint32()
1724            if new_len != desc['value'].to_number().value:
1725                raise MakeError('RangeError', 'Invalid range!')
1726            new_desc = dict((k, v) for k, v in six.iteritems(desc))
1727            new_desc['value'] = Js(new_len)
1728            if new_len >= old_len:
1729                return PyJs.define_own_property(self, prop, new_desc)
1730            if not old_len_desc['writable']:
1731                return False
1732            if 'writable' not in new_desc or new_desc['writable'] == True:
1733                new_writable = True
1734            else:
1735                new_writable = False
1736                new_desc['writable'] = True
1737            if not PyJs.define_own_property(self, prop, new_desc):
1738                return False
1739            if new_len < old_len:
1740                # not very efficient for sparse arrays, so using different method for sparse:
1741                if old_len > 30 * len(self.own):
1742                    for ele in self.own.keys():
1743                        if ele.isdigit() and int(ele) >= new_len:
1744                            if not self.delete(
1745                                    ele
1746                            ):  # if failed to delete set len to current len and reject.
1747                                new_desc['value'] = Js(old_len + 1)
1748                                if not new_writable:
1749                                    new_desc['writable'] = False
1750                                PyJs.define_own_property(self, prop, new_desc)
1751                                return False
1752                    old_len = new_len
1753                else:  # standard method
1754                    while new_len < old_len:
1755                        old_len -= 1
1756                        if not self.delete(
1757                                str(int(old_len))
1758                        ):  # if failed to delete set len to current len and reject.
1759                            new_desc['value'] = Js(old_len + 1)
1760                            if not new_writable:
1761                                new_desc['writable'] = False
1762                            PyJs.define_own_property(self, prop, new_desc)
1763                            return False
1764            if not new_writable:
1765                self.own['length']['writable'] = False
1766            return True
1767        elif prop.isdigit():
1768            index = int(int(prop) % 2**32)
1769            if index >= old_len and not old_len_desc['writable']:
1770                return False
1771            if not PyJs.define_own_property(self, prop, desc):
1772                return False
1773            if index >= old_len:
1774                old_len_desc['value'] = Js(index + 1)
1775            return True
1776        else:
1777            return PyJs.define_own_property(self, prop, desc)
1778
1779    def to_list(self):
1780        return [
1781            self.get(str(e)) for e in xrange(self.get('length').to_uint32())
1782        ]
1783
1784    def __repr__(self):
1785        return repr(self.to_python().to_list())
1786
1787
1788class PyJsArrayBuffer(PyJs):
1789    Class = 'ArrayBuffer'
1790
1791    def __init__(self, arr=[], prototype=None):
1792        self.extensible = True
1793        self.prototype = prototype
1794        self.own = {
1795            'length': {
1796                'value': Js(0),
1797                'writable': True,
1798                'enumerable': False,
1799                'configurable': False
1800            }
1801        }
1802        for i, e in enumerate(arr):
1803            self.define_own_property(
1804                str(i), {
1805                    'value': Js(e),
1806                    'writable': True,
1807                    'enumerable': True,
1808                    'configurable': True
1809                })
1810
1811    def define_own_property(self, prop, desc):
1812        old_len_desc = self.get_own_property('length')
1813        old_len = old_len_desc[
1814            'value'].value  #  value is js type so convert to py.
1815        if prop == 'length':
1816            if 'value' not in desc:
1817                return PyJs.define_own_property(self, prop, desc)
1818            new_len = desc['value'].to_uint32()
1819            if new_len != desc['value'].to_number().value:
1820                raise MakeError('RangeError', 'Invalid range!')
1821            new_desc = dict((k, v) for k, v in six.iteritems(desc))
1822            new_desc['value'] = Js(new_len)
1823            if new_len >= old_len:
1824                return PyJs.define_own_property(self, prop, new_desc)
1825            if not old_len_desc['writable']:
1826                return False
1827            if 'writable' not in new_desc or new_desc['writable'] == True:
1828                new_writable = True
1829            else:
1830                new_writable = False
1831                new_desc['writable'] = True
1832            if not PyJs.define_own_property(self, prop, new_desc):
1833                return False
1834            if new_len < old_len:
1835                # not very efficient for sparse arrays, so using different method for sparse:
1836                if old_len > 30 * len(self.own):
1837                    for ele in self.own.keys():
1838                        if ele.isdigit() and int(ele) >= new_len:
1839                            if not self.delete(
1840                                    ele
1841                            ):  # if failed to delete set len to current len and reject.
1842                                new_desc['value'] = Js(old_len + 1)
1843                                if not new_writable:
1844                                    new_desc['writable'] = False
1845                                PyJs.define_own_property(self, prop, new_desc)
1846                                return False
1847                    old_len = new_len
1848                else:  # standard method
1849                    while new_len < old_len:
1850                        old_len -= 1
1851                        if not self.delete(
1852                                str(int(old_len))
1853                        ):  # if failed to delete set len to current len and reject.
1854                            new_desc['value'] = Js(old_len + 1)
1855                            if not new_writable:
1856                                new_desc['writable'] = False
1857                            PyJs.define_own_property(self, prop, new_desc)
1858                            return False
1859            if not new_writable:
1860                self.own['length']['writable'] = False
1861            return True
1862        elif prop.isdigit():
1863            index = int(int(prop) % 2**32)
1864            if index >= old_len and not old_len_desc['writable']:
1865                return False
1866            if not PyJs.define_own_property(self, prop, desc):
1867                return False
1868            if index >= old_len:
1869                old_len_desc['value'] = Js(index + 1)
1870            return True
1871        else:
1872            return PyJs.define_own_property(self, prop, desc)
1873
1874    def to_list(self):
1875        return [
1876            self.get(str(e)) for e in xrange(self.get('length').to_uint32())
1877        ]
1878
1879    def __repr__(self):
1880        return repr(self.to_python().to_list())
1881
1882
1883class PyJsInt8Array(PyJs):
1884    Class = 'Int8Array'
1885
1886    def __init__(self, arr=[], prototype=None):
1887        self.extensible = True
1888        self.prototype = prototype
1889        self.own = {
1890            'length': {
1891                'value': Js(0),
1892                'writable': True,
1893                'enumerable': False,
1894                'configurable': False
1895            }
1896        }
1897
1898        for i, e in enumerate(arr):
1899            self.define_own_property(
1900                str(i), {
1901                    'value': Js(e),
1902                    'writable': True,
1903                    'enumerable': True,
1904                    'configurable': True
1905                })
1906
1907    def define_own_property(self, prop, desc):
1908        old_len_desc = self.get_own_property('length')
1909        old_len = old_len_desc[
1910            'value'].value  #  value is js type so convert to py.
1911        if prop == 'length':
1912            if 'value' not in desc:
1913                return PyJs.define_own_property(self, prop, desc)
1914            new_len = desc['value'].to_uint32()
1915            if new_len != desc['value'].to_number().value:
1916                raise MakeError('RangeError', 'Invalid range!')
1917            new_desc = dict((k, v) for k, v in six.iteritems(desc))
1918            new_desc['value'] = Js(new_len)
1919            if new_len >= old_len:
1920                return PyJs.define_own_property(self, prop, new_desc)
1921            if not old_len_desc['writable']:
1922                return False
1923            if 'writable' not in new_desc or new_desc['writable'] == True:
1924                new_writable = True
1925            else:
1926                new_writable = False
1927                new_desc['writable'] = True
1928            if not PyJs.define_own_property(self, prop, new_desc):
1929                return False
1930            if new_len < old_len:
1931                # not very efficient for sparse arrays, so using different method for sparse:
1932                if old_len > 30 * len(self.own):
1933                    for ele in self.own.keys():
1934                        if ele.isdigit() and int(ele) >= new_len:
1935                            if not self.delete(
1936                                    ele
1937                            ):  # if failed to delete set len to current len and reject.
1938                                new_desc['value'] = Js(old_len + 1)
1939                                if not new_writable:
1940                                    new_desc['writable'] = False
1941                                PyJs.define_own_property(self, prop, new_desc)
1942                                return False
1943                    old_len = new_len
1944                else:  # standard method
1945                    while new_len < old_len:
1946                        old_len -= 1
1947                        if not self.delete(
1948                                str(int(old_len))
1949                        ):  # if failed to delete set len to current len and reject.
1950                            new_desc['value'] = Js(old_len + 1)
1951                            if not new_writable:
1952                                new_desc['writable'] = False
1953                            PyJs.define_own_property(self, prop, new_desc)
1954                            return False
1955            if not new_writable:
1956                self.own['length']['writable'] = False
1957            return True
1958        elif prop.isdigit():
1959            index = int(int(prop) % 2**32)
1960            if index >= old_len and not old_len_desc['writable']:
1961                return False
1962            if not PyJs.define_own_property(self, prop, desc):
1963                return False
1964            if index >= old_len:
1965                old_len_desc['value'] = Js(index + 1)
1966            return True
1967        else:
1968            return PyJs.define_own_property(self, prop, desc)
1969
1970    def to_list(self):
1971        return [
1972            self.get(str(e)) for e in xrange(self.get('length').to_uint32())
1973        ]
1974
1975    def __repr__(self):
1976        return repr(self.to_python().to_list())
1977
1978
1979class PyJsUint8Array(PyJs):
1980    Class = 'Uint8Array'
1981
1982    def __init__(self, arr=[], prototype=None):
1983        self.extensible = True
1984        self.prototype = prototype
1985        self.own = {
1986            'length': {
1987                'value': Js(0),
1988                'writable': True,
1989                'enumerable': False,
1990                'configurable': False
1991            }
1992        }
1993
1994        for i, e in enumerate(arr):
1995            self.define_own_property(
1996                str(i), {
1997                    'value': Js(e),
1998                    'writable': True,
1999                    'enumerable': True,
2000                    'configurable': True
2001                })
2002
2003    def define_own_property(self, prop, desc):
2004        old_len_desc = self.get_own_property('length')
2005        old_len = old_len_desc[
2006            'value'].value  #  value is js type so convert to py.
2007        if prop == 'length':
2008            if 'value' not in desc:
2009                return PyJs.define_own_property(self, prop, desc)
2010            new_len = desc['value'].to_uint32()
2011            if new_len != desc['value'].to_number().value:
2012                raise MakeError('RangeError', 'Invalid range!')
2013            new_desc = dict((k, v) for k, v in six.iteritems(desc))
2014            new_desc['value'] = Js(new_len)
2015            if new_len >= old_len:
2016                return PyJs.define_own_property(self, prop, new_desc)
2017            if not old_len_desc['writable']:
2018                return False
2019            if 'writable' not in new_desc or new_desc['writable'] == True:
2020                new_writable = True
2021            else:
2022                new_writable = False
2023                new_desc['writable'] = True
2024            if not PyJs.define_own_property(self, prop, new_desc):
2025                return False
2026            if new_len < old_len:
2027                # not very efficient for sparse arrays, so using different method for sparse:
2028                if old_len > 30 * len(self.own):
2029                    for ele in self.own.keys():
2030                        if ele.isdigit() and int(ele) >= new_len:
2031                            if not self.delete(
2032                                    ele
2033                            ):  # if failed to delete set len to current len and reject.
2034                                new_desc['value'] = Js(old_len + 1)
2035                                if not new_writable:
2036                                    new_desc['writable'] = False
2037                                PyJs.define_own_property(self, prop, new_desc)
2038                                return False
2039                    old_len = new_len
2040                else:  # standard method
2041                    while new_len < old_len:
2042                        old_len -= 1
2043                        if not self.delete(
2044                                str(int(old_len))
2045                        ):  # if failed to delete set len to current len and reject.
2046                            new_desc['value'] = Js(old_len + 1)
2047                            if not new_writable:
2048                                new_desc['writable'] = False
2049                            PyJs.define_own_property(self, prop, new_desc)
2050                            return False
2051            if not new_writable:
2052                self.own['length']['writable'] = False
2053            return True
2054        elif prop.isdigit():
2055            index = int(int(prop) % 2**32)
2056            if index >= old_len and not old_len_desc['writable']:
2057                return False
2058            if not PyJs.define_own_property(self, prop, desc):
2059                return False
2060            if index >= old_len:
2061                old_len_desc['value'] = Js(index + 1)
2062            return True
2063        else:
2064            return PyJs.define_own_property(self, prop, desc)
2065
2066    def to_list(self):
2067        return [
2068            self.get(str(e)) for e in xrange(self.get('length').to_uint32())
2069        ]
2070
2071    def __repr__(self):
2072        return repr(self.to_python().to_list())
2073
2074
2075class PyJsUint8ClampedArray(PyJs):
2076    Class = 'Uint8ClampedArray'
2077
2078    def __init__(self, arr=[], prototype=None):
2079        self.extensible = True
2080        self.prototype = prototype
2081        self.own = {
2082            'length': {
2083                'value': Js(0),
2084                'writable': True,
2085                'enumerable': False,
2086                'configurable': False
2087            }
2088        }
2089
2090        for i, e in enumerate(arr):
2091            self.define_own_property(
2092                str(i), {
2093                    'value': Js(e),
2094                    'writable': True,
2095                    'enumerable': True,
2096                    'configurable': True
2097                })
2098
2099    def define_own_property(self, prop, desc):
2100        old_len_desc = self.get_own_property('length')
2101        old_len = old_len_desc[
2102            'value'].value  #  value is js type so convert to py.
2103        if prop == 'length':
2104            if 'value' not in desc:
2105                return PyJs.define_own_property(self, prop, desc)
2106            new_len = desc['value'].to_uint32()
2107            if new_len != desc['value'].to_number().value:
2108                raise MakeError('RangeError', 'Invalid range!')
2109            new_desc = dict((k, v) for k, v in six.iteritems(desc))
2110            new_desc['value'] = Js(new_len)
2111            if new_len >= old_len:
2112                return PyJs.define_own_property(self, prop, new_desc)
2113            if not old_len_desc['writable']:
2114                return False
2115            if 'writable' not in new_desc or new_desc['writable'] == True:
2116                new_writable = True
2117            else:
2118                new_writable = False
2119                new_desc['writable'] = True
2120            if not PyJs.define_own_property(self, prop, new_desc):
2121                return False
2122            if new_len < old_len:
2123                # not very efficient for sparse arrays, so using different method for sparse:
2124                if old_len > 30 * len(self.own):
2125                    for ele in self.own.keys():
2126                        if ele.isdigit() and int(ele) >= new_len:
2127                            if not self.delete(
2128                                    ele
2129                            ):  # if failed to delete set len to current len and reject.
2130                                new_desc['value'] = Js(old_len + 1)
2131                                if not new_writable:
2132                                    new_desc['writable'] = False
2133                                PyJs.define_own_property(self, prop, new_desc)
2134                                return False
2135                    old_len = new_len
2136                else:  # standard method
2137                    while new_len < old_len:
2138                        old_len -= 1
2139                        if not self.delete(
2140                                str(int(old_len))
2141                        ):  # if failed to delete set len to current len and reject.
2142                            new_desc['value'] = Js(old_len + 1)
2143                            if not new_writable:
2144                                new_desc['writable'] = False
2145                            PyJs.define_own_property(self, prop, new_desc)
2146                            return False
2147            if not new_writable:
2148                self.own['length']['writable'] = False
2149            return True
2150        elif prop.isdigit():
2151            index = int(int(prop) % 2**32)
2152            if index >= old_len and not old_len_desc['writable']:
2153                return False
2154            if not PyJs.define_own_property(self, prop, desc):
2155                return False
2156            if index >= old_len:
2157                old_len_desc['value'] = Js(index + 1)
2158            return True
2159        else:
2160            return PyJs.define_own_property(self, prop, desc)
2161
2162    def to_list(self):
2163        return [
2164            self.get(str(e)) for e in xrange(self.get('length').to_uint32())
2165        ]
2166
2167    def __repr__(self):
2168        return repr(self.to_python().to_list())
2169
2170
2171class PyJsInt16Array(PyJs):
2172    Class = 'Int16Array'
2173
2174    def __init__(self, arr=[], prototype=None):
2175        self.extensible = True
2176        self.prototype = prototype
2177        self.own = {
2178            'length': {
2179                'value': Js(0),
2180                'writable': True,
2181                'enumerable': False,
2182                'configurable': False
2183            }
2184        }
2185
2186        for i, e in enumerate(arr):
2187            self.define_own_property(
2188                str(i), {
2189                    'value': Js(e),
2190                    'writable': True,
2191                    'enumerable': True,
2192                    'configurable': True
2193                })
2194
2195    def define_own_property(self, prop, desc):
2196        old_len_desc = self.get_own_property('length')
2197        old_len = old_len_desc[
2198            'value'].value  #  value is js type so convert to py.
2199        if prop == 'length':
2200            if 'value' not in desc:
2201                return PyJs.define_own_property(self, prop, desc)
2202            new_len = desc['value'].to_uint32()
2203            if new_len != desc['value'].to_number().value:
2204                raise MakeError('RangeError', 'Invalid range!')
2205            new_desc = dict((k, v) for k, v in six.iteritems(desc))
2206            new_desc['value'] = Js(new_len)
2207            if new_len >= old_len:
2208                return PyJs.define_own_property(self, prop, new_desc)
2209            if not old_len_desc['writable']:
2210                return False
2211            if 'writable' not in new_desc or new_desc['writable'] == True:
2212                new_writable = True
2213            else:
2214                new_writable = False
2215                new_desc['writable'] = True
2216            if not PyJs.define_own_property(self, prop, new_desc):
2217                return False
2218            if new_len < old_len:
2219                # not very efficient for sparse arrays, so using different method for sparse:
2220                if old_len > 30 * len(self.own):
2221                    for ele in self.own.keys():
2222                        if ele.isdigit() and int(ele) >= new_len:
2223                            if not self.delete(
2224                                    ele
2225                            ):  # if failed to delete set len to current len and reject.
2226                                new_desc['value'] = Js(old_len + 1)
2227                                if not new_writable:
2228                                    new_desc['writable'] = False
2229                                PyJs.define_own_property(self, prop, new_desc)
2230                                return False
2231                    old_len = new_len
2232                else:  # standard method
2233                    while new_len < old_len:
2234                        old_len -= 1
2235                        if not self.delete(
2236                                str(int(old_len))
2237                        ):  # if failed to delete set len to current len and reject.
2238                            new_desc['value'] = Js(old_len + 1)
2239                            if not new_writable:
2240                                new_desc['writable'] = False
2241                            PyJs.define_own_property(self, prop, new_desc)
2242                            return False
2243            if not new_writable:
2244                self.own['length']['writable'] = False
2245            return True
2246        elif prop.isdigit():
2247            index = int(int(prop) % 2**32)
2248            if index >= old_len and not old_len_desc['writable']:
2249                return False
2250            if not PyJs.define_own_property(self, prop, desc):
2251                return False
2252            if index >= old_len:
2253                old_len_desc['value'] = Js(index + 1)
2254            return True
2255        else:
2256            return PyJs.define_own_property(self, prop, desc)
2257
2258    def to_list(self):
2259        return [
2260            self.get(str(e)) for e in xrange(self.get('length').to_uint32())
2261        ]
2262
2263    def __repr__(self):
2264        return repr(self.to_python().to_list())
2265
2266
2267class PyJsUint16Array(PyJs):
2268    Class = 'Uint16Array'
2269
2270    def __init__(self, arr=[], prototype=None):
2271        self.extensible = True
2272        self.prototype = prototype
2273        self.own = {
2274            'length': {
2275                'value': Js(0),
2276                'writable': True,
2277                'enumerable': False,
2278                'configurable': False
2279            }
2280        }
2281
2282        for i, e in enumerate(arr):
2283            self.define_own_property(
2284                str(i), {
2285                    'value': Js(e),
2286                    'writable': True,
2287                    'enumerable': True,
2288                    'configurable': True
2289                })
2290
2291    def define_own_property(self, prop, desc):
2292        old_len_desc = self.get_own_property('length')
2293        old_len = old_len_desc[
2294            'value'].value  #  value is js type so convert to py.
2295        if prop == 'length':
2296            if 'value' not in desc:
2297                return PyJs.define_own_property(self, prop, desc)
2298            new_len = desc['value'].to_uint32()
2299            if new_len != desc['value'].to_number().value:
2300                raise MakeError('RangeError', 'Invalid range!')
2301            new_desc = dict((k, v) for k, v in six.iteritems(desc))
2302            new_desc['value'] = Js(new_len)
2303            if new_len >= old_len:
2304                return PyJs.define_own_property(self, prop, new_desc)
2305            if not old_len_desc['writable']:
2306                return False
2307            if 'writable' not in new_desc or new_desc['writable'] == True:
2308                new_writable = True
2309            else:
2310                new_writable = False
2311                new_desc['writable'] = True
2312            if not PyJs.define_own_property(self, prop, new_desc):
2313                return False
2314            if new_len < old_len:
2315                # not very efficient for sparse arrays, so using different method for sparse:
2316                if old_len > 30 * len(self.own):
2317                    for ele in self.own.keys():
2318                        if ele.isdigit() and int(ele) >= new_len:
2319                            if not self.delete(
2320                                    ele
2321                            ):  # if failed to delete set len to current len and reject.
2322                                new_desc['value'] = Js(old_len + 1)
2323                                if not new_writable:
2324                                    new_desc['writable'] = False
2325                                PyJs.define_own_property(self, prop, new_desc)
2326                                return False
2327                    old_len = new_len
2328                else:  # standard method
2329                    while new_len < old_len:
2330                        old_len -= 1
2331                        if not self.delete(
2332                                str(int(old_len))
2333                        ):  # if failed to delete set len to current len and reject.
2334                            new_desc['value'] = Js(old_len + 1)
2335                            if not new_writable:
2336                                new_desc['writable'] = False
2337                            PyJs.define_own_property(self, prop, new_desc)
2338                            return False
2339            if not new_writable:
2340                self.own['length']['writable'] = False
2341            return True
2342        elif prop.isdigit():
2343            index = int(int(prop) % 2**32)
2344            if index >= old_len and not old_len_desc['writable']:
2345                return False
2346            if not PyJs.define_own_property(self, prop, desc):
2347                return False
2348            if index >= old_len:
2349                old_len_desc['value'] = Js(index + 1)
2350            return True
2351        else:
2352            return PyJs.define_own_property(self, prop, desc)
2353
2354    def to_list(self):
2355        return [
2356            self.get(str(e)) for e in xrange(self.get('length').to_uint32())
2357        ]
2358
2359    def __repr__(self):
2360        return repr(self.to_python().to_list())
2361
2362
2363class PyJsInt32Array(PyJs):
2364    Class = 'Int32Array'
2365
2366    def __init__(self, arr=[], prototype=None):
2367        self.extensible = True
2368        self.prototype = prototype
2369        self.own = {
2370            'length': {
2371                'value': Js(0),
2372                'writable': True,
2373                'enumerable': False,
2374                'configurable': False
2375            }
2376        }
2377
2378        for i, e in enumerate(arr):
2379            self.define_own_property(
2380                str(i), {
2381                    'value': Js(e),
2382                    'writable': True,
2383                    'enumerable': True,
2384                    'configurable': True
2385                })
2386
2387    def define_own_property(self, prop, desc):
2388        old_len_desc = self.get_own_property('length')
2389        old_len = old_len_desc[
2390            'value'].value  #  value is js type so convert to py.
2391        if prop == 'length':
2392            if 'value' not in desc:
2393                return PyJs.define_own_property(self, prop, desc)
2394            new_len = desc['value'].to_uint32()
2395            if new_len != desc['value'].to_number().value:
2396                raise MakeError('RangeError', 'Invalid range!')
2397            new_desc = dict((k, v) for k, v in six.iteritems(desc))
2398            new_desc['value'] = Js(new_len)
2399            if new_len >= old_len:
2400                return PyJs.define_own_property(self, prop, new_desc)
2401            if not old_len_desc['writable']:
2402                return False
2403            if 'writable' not in new_desc or new_desc['writable'] == True:
2404                new_writable = True
2405            else:
2406                new_writable = False
2407                new_desc['writable'] = True
2408            if not PyJs.define_own_property(self, prop, new_desc):
2409                return False
2410            if new_len < old_len:
2411                # not very efficient for sparse arrays, so using different method for sparse:
2412                if old_len > 30 * len(self.own):
2413                    for ele in self.own.keys():
2414                        if ele.isdigit() and int(ele) >= new_len:
2415                            if not self.delete(
2416                                    ele
2417                            ):  # if failed to delete set len to current len and reject.
2418                                new_desc['value'] = Js(old_len + 1)
2419                                if not new_writable:
2420                                    new_desc['writable'] = False
2421                                PyJs.define_own_property(self, prop, new_desc)
2422                                return False
2423                    old_len = new_len
2424                else:  # standard method
2425                    while new_len < old_len:
2426                        old_len -= 1
2427                        if not self.delete(
2428                                str(int(old_len))
2429                        ):  # if failed to delete set len to current len and reject.
2430                            new_desc['value'] = Js(old_len + 1)
2431                            if not new_writable:
2432                                new_desc['writable'] = False
2433                            PyJs.define_own_property(self, prop, new_desc)
2434                            return False
2435            if not new_writable:
2436                self.own['length']['writable'] = False
2437            return True
2438        elif prop.isdigit():
2439            index = int(int(prop) % 2**32)
2440            if index >= old_len and not old_len_desc['writable']:
2441                return False
2442            if not PyJs.define_own_property(self, prop, desc):
2443                return False
2444            if index >= old_len:
2445                old_len_desc['value'] = Js(index + 1)
2446            return True
2447        else:
2448            return PyJs.define_own_property(self, prop, desc)
2449
2450    def to_list(self):
2451        return [
2452            self.get(str(e)) for e in xrange(self.get('length').to_uint32())
2453        ]
2454
2455    def __repr__(self):
2456        return repr(self.to_python().to_list())
2457
2458
2459class PyJsUint32Array(PyJs):
2460    Class = 'Uint32Array'
2461
2462    def __init__(self, arr=[], prototype=None):
2463        self.extensible = True
2464        self.prototype = prototype
2465        self.own = {
2466            'length': {
2467                'value': Js(0),
2468                'writable': True,
2469                'enumerable': False,
2470                'configurable': False
2471            }
2472        }
2473
2474        for i, e in enumerate(arr):
2475            self.define_own_property(
2476                str(i), {
2477                    'value': Js(e),
2478                    'writable': True,
2479                    'enumerable': True,
2480                    'configurable': True
2481                })
2482
2483    def define_own_property(self, prop, desc):
2484        old_len_desc = self.get_own_property('length')
2485        old_len = old_len_desc[
2486            'value'].value  #  value is js type so convert to py.
2487        if prop == 'length':
2488            if 'value' not in desc:
2489                return PyJs.define_own_property(self, prop, desc)
2490            new_len = desc['value'].to_uint32()
2491            if new_len != desc['value'].to_number().value:
2492                raise MakeError('RangeError', 'Invalid range!')
2493            new_desc = dict((k, v) for k, v in six.iteritems(desc))
2494            new_desc['value'] = Js(new_len)
2495            if new_len >= old_len:
2496                return PyJs.define_own_property(self, prop, new_desc)
2497            if not old_len_desc['writable']:
2498                return False
2499            if 'writable' not in new_desc or new_desc['writable'] == True:
2500                new_writable = True
2501            else:
2502                new_writable = False
2503                new_desc['writable'] = True
2504            if not PyJs.define_own_property(self, prop, new_desc):
2505                return False
2506            if new_len < old_len:
2507                # not very efficient for sparse arrays, so using different method for sparse:
2508                if old_len > 30 * len(self.own):
2509                    for ele in self.own.keys():
2510                        if ele.isdigit() and int(ele) >= new_len:
2511                            if not self.delete(
2512                                    ele
2513                            ):  # if failed to delete set len to current len and reject.
2514                                new_desc['value'] = Js(old_len + 1)
2515                                if not new_writable:
2516                                    new_desc['writable'] = False
2517                                PyJs.define_own_property(self, prop, new_desc)
2518                                return False
2519                    old_len = new_len
2520                else:  # standard method
2521                    while new_len < old_len:
2522                        old_len -= 1
2523                        if not self.delete(
2524                                str(int(old_len))
2525                        ):  # if failed to delete set len to current len and reject.
2526                            new_desc['value'] = Js(old_len + 1)
2527                            if not new_writable:
2528                                new_desc['writable'] = False
2529                            PyJs.define_own_property(self, prop, new_desc)
2530                            return False
2531            if not new_writable:
2532                self.own['length']['writable'] = False
2533            return True
2534        elif prop.isdigit():
2535            index = int(int(prop) % 2**32)
2536            if index >= old_len and not old_len_desc['writable']:
2537                return False
2538            if not PyJs.define_own_property(self, prop, desc):
2539                return False
2540            if index >= old_len:
2541                old_len_desc['value'] = Js(index + 1)
2542            return True
2543        else:
2544            return PyJs.define_own_property(self, prop, desc)
2545
2546    def to_list(self):
2547        return [
2548            self.get(str(e)) for e in xrange(self.get('length').to_uint32())
2549        ]
2550
2551    def __repr__(self):
2552        return repr(self.to_python().to_list())
2553
2554
2555class PyJsFloat32Array(PyJs):
2556    Class = 'Float32Array'
2557
2558    def __init__(self, arr=[], prototype=None):
2559        self.extensible = True
2560        self.prototype = prototype
2561        self.own = {
2562            'length': {
2563                'value': Js(0),
2564                'writable': True,
2565                'enumerable': False,
2566                'configurable': False
2567            }
2568        }
2569
2570        for i, e in enumerate(arr):
2571            self.define_own_property(
2572                str(i), {
2573                    'value': Js(e),
2574                    'writable': True,
2575                    'enumerable': True,
2576                    'configurable': True
2577                })
2578
2579    def define_own_property(self, prop, desc):
2580        old_len_desc = self.get_own_property('length')
2581        old_len = old_len_desc[
2582            'value'].value  #  value is js type so convert to py.
2583        if prop == 'length':
2584            if 'value' not in desc:
2585                return PyJs.define_own_property(self, prop, desc)
2586            new_len = desc['value'].to_uint32()
2587            if new_len != desc['value'].to_number().value:
2588                raise MakeError('RangeError', 'Invalid range!')
2589            new_desc = dict((k, v) for k, v in six.iteritems(desc))
2590            new_desc['value'] = Js(new_len)
2591            if new_len >= old_len:
2592                return PyJs.define_own_property(self, prop, new_desc)
2593            if not old_len_desc['writable']:
2594                return False
2595            if 'writable' not in new_desc or new_desc['writable'] == True:
2596                new_writable = True
2597            else:
2598                new_writable = False
2599                new_desc['writable'] = True
2600            if not PyJs.define_own_property(self, prop, new_desc):
2601                return False
2602            if new_len < old_len:
2603                # not very efficient for sparse arrays, so using different method for sparse:
2604                if old_len > 30 * len(self.own):
2605                    for ele in self.own.keys():
2606                        if ele.isdigit() and int(ele) >= new_len:
2607                            if not self.delete(
2608                                    ele
2609                            ):  # if failed to delete set len to current len and reject.
2610                                new_desc['value'] = Js(old_len + 1)
2611                                if not new_writable:
2612                                    new_desc['writable'] = False
2613                                PyJs.define_own_property(self, prop, new_desc)
2614                                return False
2615                    old_len = new_len
2616                else:  # standard method
2617                    while new_len < old_len:
2618                        old_len -= 1
2619                        if not self.delete(
2620                                str(int(old_len))
2621                        ):  # if failed to delete set len to current len and reject.
2622                            new_desc['value'] = Js(old_len + 1)
2623                            if not new_writable:
2624                                new_desc['writable'] = False
2625                            PyJs.define_own_property(self, prop, new_desc)
2626                            return False
2627            if not new_writable:
2628                self.own['length']['writable'] = False
2629            return True
2630        elif prop.isdigit():
2631            index = int(int(prop) % 2**32)
2632            if index >= old_len and not old_len_desc['writable']:
2633                return False
2634            if not PyJs.define_own_property(self, prop, desc):
2635                return False
2636            if index >= old_len:
2637                old_len_desc['value'] = Js(index + 1)
2638            return True
2639        else:
2640            return PyJs.define_own_property(self, prop, desc)
2641
2642    def to_list(self):
2643        return [
2644            self.get(str(e)) for e in xrange(self.get('length').to_uint32())
2645        ]
2646
2647    def __repr__(self):
2648        return repr(self.to_python().to_list())
2649
2650
2651class PyJsFloat64Array(PyJs):
2652    Class = 'Float64Array'
2653
2654    def __init__(self, arr=[], prototype=None):
2655        self.extensible = True
2656        self.prototype = prototype
2657        self.own = {
2658            'length': {
2659                'value': Js(0),
2660                'writable': True,
2661                'enumerable': False,
2662                'configurable': False
2663            }
2664        }
2665
2666        for i, e in enumerate(arr):
2667            self.define_own_property(
2668                str(i), {
2669                    'value': Js(e),
2670                    'writable': True,
2671                    'enumerable': True,
2672                    'configurable': True
2673                })
2674
2675    def define_own_property(self, prop, desc):
2676        old_len_desc = self.get_own_property('length')
2677        old_len = old_len_desc[
2678            'value'].value  #  value is js type so convert to py.
2679        if prop == 'length':
2680            if 'value' not in desc:
2681                return PyJs.define_own_property(self, prop, desc)
2682            new_len = desc['value'].to_uint32()
2683            if new_len != desc['value'].to_number().value:
2684                raise MakeError('RangeError', 'Invalid range!')
2685            new_desc = dict((k, v) for k, v in six.iteritems(desc))
2686            new_desc['value'] = Js(new_len)
2687            if new_len >= old_len:
2688                return PyJs.define_own_property(self, prop, new_desc)
2689            if not old_len_desc['writable']:
2690                return False
2691            if 'writable' not in new_desc or new_desc['writable'] == True:
2692                new_writable = True
2693            else:
2694                new_writable = False
2695                new_desc['writable'] = True
2696            if not PyJs.define_own_property(self, prop, new_desc):
2697                return False
2698            if new_len < old_len:
2699                # not very efficient for sparse arrays, so using different method for sparse:
2700                if old_len > 30 * len(self.own):
2701                    for ele in self.own.keys():
2702                        if ele.isdigit() and int(ele) >= new_len:
2703                            if not self.delete(
2704                                    ele
2705                            ):  # if failed to delete set len to current len and reject.
2706                                new_desc['value'] = Js(old_len + 1)
2707                                if not new_writable:
2708                                    new_desc['writable'] = False
2709                                PyJs.define_own_property(self, prop, new_desc)
2710                                return False
2711                    old_len = new_len
2712                else:  # standard method
2713                    while new_len < old_len:
2714                        old_len -= 1
2715                        if not self.delete(
2716                                str(int(old_len))
2717                        ):  # if failed to delete set len to current len and reject.
2718                            new_desc['value'] = Js(old_len + 1)
2719                            if not new_writable:
2720                                new_desc['writable'] = False
2721                            PyJs.define_own_property(self, prop, new_desc)
2722                            return False
2723            if not new_writable:
2724                self.own['length']['writable'] = False
2725            return True
2726        elif prop.isdigit():
2727            index = int(int(prop) % 2**32)
2728            if index >= old_len and not old_len_desc['writable']:
2729                return False
2730            if not PyJs.define_own_property(self, prop, desc):
2731                return False
2732            if index >= old_len:
2733                old_len_desc['value'] = Js(index + 1)
2734            return True
2735        else:
2736            return PyJs.define_own_property(self, prop, desc)
2737
2738    def to_list(self):
2739        return [
2740            self.get(str(e)) for e in xrange(self.get('length').to_uint32())
2741        ]
2742
2743    def __repr__(self):
2744        return repr(self.to_python().to_list())
2745
2746
2747ArrayPrototype = PyJsArray([], ObjectPrototype)
2748
2749ArrayBufferPrototype = PyJsArrayBuffer([], ObjectPrototype)
2750
2751Int8ArrayPrototype = PyJsInt8Array([], ObjectPrototype)
2752
2753Uint8ArrayPrototype = PyJsUint8Array([], ObjectPrototype)
2754
2755Uint8ClampedArrayPrototype = PyJsUint8ClampedArray([], ObjectPrototype)
2756
2757Int16ArrayPrototype = PyJsInt16Array([], ObjectPrototype)
2758
2759Uint16ArrayPrototype = PyJsUint16Array([], ObjectPrototype)
2760
2761Int32ArrayPrototype = PyJsInt32Array([], ObjectPrototype)
2762
2763Uint32ArrayPrototype = PyJsUint32Array([], ObjectPrototype)
2764
2765Float32ArrayPrototype = PyJsFloat32Array([], ObjectPrototype)
2766
2767Float64ArrayPrototype = PyJsFloat64Array([], ObjectPrototype)
2768
2769
2770class PyJsArguments(PyJs):
2771    Class = 'Arguments'
2772
2773    def __init__(self, args, callee):
2774        self.own = {}
2775        self.extensible = True
2776        self.prototype = ObjectPrototype
2777        self.define_own_property(
2778            'length', {
2779                'value': Js(len(args)),
2780                'writable': True,
2781                'enumerable': False,
2782                'configurable': True
2783            })
2784        self.define_own_property(
2785            'callee', {
2786                'value': callee,
2787                'writable': True,
2788                'enumerable': False,
2789                'configurable': True
2790            })
2791        for i, e in enumerate(args):
2792            self.put(str(i), Js(e))
2793
2794    def to_list(self):
2795        return [
2796            self.get(str(e)) for e in xrange(self.get('length').to_uint32())
2797        ]
2798
2799
2800#We can define function proto after number proto because func uses number in its init
2801FunctionPrototype = PyJsFunction(Empty, ObjectPrototype)
2802FunctionPrototype.own['name']['value'] = Js('')
2803
2804# I will not rewrite RegExp engine from scratch. I will use re because its much faster.
2805# I have to only make sure that I am handling all the differences correctly.
2806REGEXP_DB = {}
2807
2808
2809class PyJsRegExp(PyJs):
2810    Class = 'RegExp'
2811    extensible = True
2812
2813    def __init__(self, regexp, prototype=None):
2814
2815        self.prototype = prototype
2816        self.glob = False
2817        self.ignore_case = 0
2818        self.multiline = 0
2819        # self._cache = {'str':'NoStringEmpty23093',
2820        #                'iterator': None,
2821        #                'lastpos': -1,
2822        #                'matches': {}}
2823        flags = ''
2824        if not regexp[-1] == '/':
2825            #contains some flags (allowed are i, g, m
2826            spl = regexp.rfind('/')
2827            flags = set(regexp[spl + 1:])
2828            self.value = regexp[1:spl]
2829            if 'g' in flags:
2830                self.glob = True
2831            if 'i' in flags:
2832                self.ignore_case = re.IGNORECASE
2833            if 'm' in flags:
2834                self.multiline = re.MULTILINE
2835        else:
2836            self.value = regexp[1:-1]
2837
2838        try:
2839            if self.value in REGEXP_DB:
2840                self.pat = REGEXP_DB[regexp]
2841            else:
2842                comp = 'None'
2843                # we have to check whether pattern is valid.
2844                # also this will speed up matching later
2845                # todo critical fix patter conversion etc. ..!!!!!
2846                # ugly hacks porting js reg exp to py reg exp works in 99% of cases ;)
2847                possible_fixes = [(u'[]', u'[\0]'), (u'[^]', u'[^\0]'),
2848                                  (u'nofix1791', u'nofix1791')]
2849                reg = self.value
2850                for fix, rep in possible_fixes:
2851                    comp = REGEXP_CONVERTER._interpret_regexp(reg, flags)
2852                    #print 'reg -> comp', reg, '->', comp
2853                    try:
2854                        self.pat = re.compile(
2855                            comp, self.ignore_case | self.multiline)
2856                        #print reg, '->', comp
2857                        break
2858                    except:
2859                        reg = reg.replace(fix, rep)
2860                    # print 'Fix', fix, '->', rep, '=', reg
2861                else:
2862                    raise
2863                REGEXP_DB[regexp] = self.pat
2864        except:
2865            #print 'Invalid pattern but fuck it', self.value, comp
2866            raise MakeError(
2867                'SyntaxError',
2868                'Invalid RegExp pattern: %s -> %s' % (repr(self.value),
2869                                                      repr(comp)))
2870        # now set own properties:
2871        self.own = {
2872            'source': {
2873                'value': Js(self.value),
2874                'enumerable': False,
2875                'writable': False,
2876                'configurable': False
2877            },
2878            'global': {
2879                'value': Js(self.glob),
2880                'enumerable': False,
2881                'writable': False,
2882                'configurable': False
2883            },
2884            'ignoreCase': {
2885                'value': Js(bool(self.ignore_case)),
2886                'enumerable': False,
2887                'writable': False,
2888                'configurable': False
2889            },
2890            'multiline': {
2891                'value': Js(bool(self.multiline)),
2892                'enumerable': False,
2893                'writable': False,
2894                'configurable': False
2895            },
2896            'lastIndex': {
2897                'value': Js(0),
2898                'enumerable': False,
2899                'writable': True,
2900                'configurable': False
2901            }
2902        }
2903
2904    def match(self, string, pos):
2905        '''string is of course py string'''
2906        return self.pat.match(string, pos)  # way easier :)
2907        # assert 0<=pos <= len(string)
2908        # if not pos:
2909        #     return re.match(self.pat, string)
2910        # else:
2911        #     if self._cache['str']==string:
2912        #         if pos>self._cache['lastpos']:
2913        #             for m in self._cache['iterator']:
2914        #                 start = m.start()
2915        #                 self._cache['lastpos'] = start
2916        #                 self._cache['matches'][start] = m
2917        #                 if start==pos:
2918        #                     return m
2919        #                 elif start>pos:
2920        #                     return None
2921        #             self._cache['lastpos'] = len(string)
2922        #             return None
2923        #         else:
2924        #             return self._cache['matches'].get(pos)
2925        #     else:
2926        #         self._cache['str'] = string
2927        #         self._cache['matches'] = {}
2928        #         self._cache['lastpos'] = -1
2929        #         self._cache['iterator'] = re.finditer(self.pat, string)
2930        #         return self.match(string, pos)
2931
2932
2933def JsRegExp(source):
2934    # Takes regexp literal!
2935    return PyJsRegExp(source, RegExpPrototype)
2936
2937
2938RegExpPrototype = PyJsRegExp('/(?:)/', ObjectPrototype)
2939
2940####Exceptions:
2941default_attrs = {'writable': True, 'enumerable': False, 'configurable': True}
2942
2943
2944def fill_in_props(obj, props, default_desc):
2945    for prop, value in props.items():
2946        default_desc['value'] = Js(value)
2947        obj.define_own_property(prop, default_desc)
2948
2949
2950class PyJsError(PyJs):
2951    Class = 'Error'
2952    extensible = True
2953
2954    def __init__(self, message=None, prototype=None):
2955        self.prototype = prototype
2956        self.own = {}
2957        if message is not None:
2958            self.put('message', Js(message).to_string())
2959            self.own['message']['enumerable'] = False
2960
2961
2962ErrorPrototype = PyJsError(Js(''), ObjectPrototype)
2963
2964
2965@Js
2966def Error(message):
2967    return PyJsError(None if message.is_undefined() else message,
2968                     ErrorPrototype)
2969
2970
2971Error.create = Error
2972err = {'name': 'Error', 'constructor': Error}
2973fill_in_props(ErrorPrototype, err, default_attrs)
2974Error.define_own_property(
2975    'prototype', {
2976        'value': ErrorPrototype,
2977        'enumerable': False,
2978        'writable': False,
2979        'configurable': False
2980    })
2981
2982
2983def define_error_type(name):
2984    TypeErrorPrototype = PyJsError(None, ErrorPrototype)
2985
2986    @Js
2987    def TypeError(message):
2988        return PyJsError(None if message.is_undefined() else message,
2989                         TypeErrorPrototype)
2990
2991    err = {'name': name, 'constructor': TypeError}
2992    fill_in_props(TypeErrorPrototype, err, default_attrs)
2993    TypeError.define_own_property(
2994        'prototype', {
2995            'value': TypeErrorPrototype,
2996            'enumerable': False,
2997            'writable': False,
2998            'configurable': False
2999        })
3000    ERRORS[name] = TypeError
3001
3002
3003ERRORS = {'Error': Error}
3004ERROR_NAMES = ['Eval', 'Type', 'Range', 'Reference', 'Syntax', 'URI']
3005
3006for e in ERROR_NAMES:
3007    define_error_type(e + 'Error')
3008
3009##############################################################################
3010# Import and fill prototypes here.
3011
3012
3013#this works only for data properties
3014def fill_prototype(prototype, Class, attrs, constructor=False):
3015    for i in dir(Class):
3016        e = getattr(Class, i)
3017        if six.PY2:
3018            if hasattr(e, '__func__'):
3019                temp = PyJsFunction(e.__func__, FunctionPrototype)
3020                attrs = dict((k, v) for k, v in attrs.iteritems())
3021                attrs['value'] = temp
3022                prototype.define_own_property(i, attrs)
3023        else:
3024            if hasattr(e, '__call__') and not i.startswith('__'):
3025                temp = PyJsFunction(e, FunctionPrototype)
3026                attrs = dict((k, v) for k, v in attrs.items())
3027                attrs['value'] = temp
3028                prototype.define_own_property(i, attrs)
3029        if constructor:
3030            attrs['value'] = constructor
3031            prototype.define_own_property('constructor', attrs)
3032
3033
3034PyJs.undefined = undefined
3035PyJs.Js = staticmethod(Js)
3036
3037from .prototypes import jsfunction, jsobject, jsnumber, jsstring, jsboolean, jsarray, jsregexp, jserror, jsarraybuffer, jstypedarray
3038
3039#Object proto
3040fill_prototype(ObjectPrototype, jsobject.ObjectPrototype, default_attrs)
3041
3042
3043#Define __proto__ accessor (this cant be done by fill_prototype since)
3044@Js
3045def __proto__():
3046    return this.prototype if this.prototype is not None else null
3047
3048
3049getter = __proto__
3050
3051
3052@Js
3053def __proto__(val):
3054    if val.is_object():
3055        this.prototype = val
3056
3057
3058setter = __proto__
3059ObjectPrototype.define_own_property('__proto__', {
3060    'set': setter,
3061    'get': getter,
3062    'enumerable': False,
3063    'configurable': True
3064})
3065
3066#Function proto
3067fill_prototype(FunctionPrototype, jsfunction.FunctionPrototype, default_attrs)
3068#Number proto
3069fill_prototype(NumberPrototype, jsnumber.NumberPrototype, default_attrs)
3070#String proto
3071fill_prototype(StringPrototype, jsstring.StringPrototype, default_attrs)
3072#Boolean proto
3073fill_prototype(BooleanPrototype, jsboolean.BooleanPrototype, default_attrs)
3074#Array proto
3075fill_prototype(ArrayPrototype, jsarray.ArrayPrototype, default_attrs)
3076# ArrayBuffer proto
3077fill_prototype(ArrayBufferPrototype, jsarraybuffer.ArrayBufferPrototype,
3078               default_attrs)
3079# Int8Array proto
3080fill_prototype(Int8ArrayPrototype, jstypedarray.TypedArrayPrototype,
3081               default_attrs)
3082# Uint8Array proto
3083fill_prototype(Uint8ArrayPrototype, jstypedarray.TypedArrayPrototype,
3084               default_attrs)
3085# Uint8ClampedArray proto
3086fill_prototype(Uint8ClampedArrayPrototype, jstypedarray.TypedArrayPrototype,
3087               default_attrs)
3088# Int16Array proto
3089fill_prototype(Int16ArrayPrototype, jstypedarray.TypedArrayPrototype,
3090               default_attrs)
3091# Uint16Array proto
3092fill_prototype(Uint16ArrayPrototype, jstypedarray.TypedArrayPrototype,
3093               default_attrs)
3094# Int32Array proto
3095fill_prototype(Int32ArrayPrototype, jstypedarray.TypedArrayPrototype,
3096               default_attrs)
3097# Uint32Array proto
3098fill_prototype(Uint32ArrayPrototype, jstypedarray.TypedArrayPrototype,
3099               default_attrs)
3100# Float32Array proto
3101fill_prototype(Float32ArrayPrototype, jstypedarray.TypedArrayPrototype,
3102               default_attrs)
3103# Float64Array proto
3104fill_prototype(Float64ArrayPrototype, jstypedarray.TypedArrayPrototype,
3105               default_attrs)
3106#Error proto
3107fill_prototype(ErrorPrototype, jserror.ErrorPrototype, default_attrs)
3108#RegExp proto
3109fill_prototype(RegExpPrototype, jsregexp.RegExpPrototype, default_attrs)
3110# add exec to regexpfunction (cant add it automatically because of its name :(
3111RegExpPrototype.own['exec'] = RegExpPrototype.own['exec2']
3112del RegExpPrototype.own['exec2']
3113
3114#########################################################################
3115# Constructors
3116
3117
3118# String
3119@Js
3120def String(st):
3121    if not len(arguments):
3122        return Js('')
3123    return arguments[0].to_string()
3124
3125
3126@Js
3127def string_constructor():
3128    temp = PyJsObject(prototype=StringPrototype)
3129    temp.Class = 'String'
3130    #temp.TYPE = 'String'
3131    if not len(arguments):
3132        temp.value = ''
3133    else:
3134        temp.value = arguments[0].to_string().value
3135        for i, ch in enumerate(temp.value):  # this will make things long...
3136            temp.own[str(i)] = {
3137                'value': Js(ch),
3138                'writable': False,
3139                'enumerable': True,
3140                'configurable': True
3141            }
3142    temp.own['length'] = {
3143        'value': Js(len(temp.value)),
3144        'writable': False,
3145        'enumerable': False,
3146        'configurable': False
3147    }
3148    return temp
3149
3150
3151String.create = string_constructor
3152
3153# RegExp
3154REG_EXP_FLAGS = ('g', 'i', 'm')
3155
3156
3157@Js
3158def RegExp(pattern, flags):
3159    if pattern.Class == 'RegExp':
3160        if not flags.is_undefined():
3161            raise MakeError(
3162                'TypeError',
3163                'Cannot supply flags when constructing one RegExp from another'
3164            )
3165        # return unchanged
3166        return pattern
3167    #pattern is not a regexp
3168    if pattern.is_undefined():
3169        pattern = ''
3170    else:
3171        pattern = pattern.to_string().value
3172        # try:
3173        #     pattern = REGEXP_CONVERTER._unescape_string(pattern.to_string().value)
3174        # except:
3175        #     raise MakeError('SyntaxError', 'Invalid regexp')
3176    flags = flags.to_string().value if not flags.is_undefined() else ''
3177    for flag in flags:
3178        if flag not in REG_EXP_FLAGS:
3179            raise MakeError(
3180                'SyntaxError',
3181                'Invalid flags supplied to RegExp constructor "%s"' % flag)
3182    if len(set(flags)) != len(flags):
3183        raise MakeError(
3184            'SyntaxError',
3185            'Invalid flags supplied to RegExp constructor "%s"' % flags)
3186    pattern = '/%s/' % (pattern if pattern else '(?:)') + flags
3187    return JsRegExp(pattern)
3188
3189
3190RegExp.create = RegExp
3191PyJs.RegExp = RegExp
3192
3193# Number
3194
3195
3196@Js
3197def Number():
3198    if len(arguments):
3199        return arguments[0].to_number()
3200    else:
3201        return Js(0)
3202
3203
3204@Js
3205def number_constructor():
3206    temp = PyJsObject(prototype=NumberPrototype)
3207    temp.Class = 'Number'
3208    #temp.TYPE = 'Number'
3209    if len(arguments):
3210        temp.value = arguments[0].to_number().value
3211    else:
3212        temp.value = 0
3213    return temp
3214
3215
3216Number.create = number_constructor
3217
3218# Boolean
3219
3220
3221@Js
3222def Boolean(value):
3223    return value.to_boolean()
3224
3225
3226@Js
3227def boolean_constructor(value):
3228    temp = PyJsObject(prototype=BooleanPrototype)
3229    temp.Class = 'Boolean'
3230    #temp.TYPE = 'Boolean'
3231    temp.value = value.to_boolean().value
3232    return temp
3233
3234
3235Boolean.create = boolean_constructor
3236
3237##############################################################################
3238
3239
3240def appengine(code):
3241    try:
3242        return translator.translate_js(code.decode('utf-8'))
3243    except:
3244        return traceback.format_exc()
3245
3246
3247builtins = ('true', 'false', 'null', 'undefined', 'Infinity', 'NaN')
3248
3249scope = dict(zip(builtins, [eval(e) for e in builtins]))
3250
3251JS_BUILTINS = dict((k, v) for k, v in scope.items())
3252
3253# Fill in NUM_BANK
3254for e in xrange(-2**10, 2**14):
3255    NUM_BANK[e] = Js(e)
3256
3257if __name__ == '__main__':
3258    print(ObjectPrototype.get('toString').callprop('call'))
3259    print(FunctionPrototype.own)
3260    a = null - Js(49404)
3261    x = a.put('ser', Js('der'))
3262    print(Js(0) or Js('p') and Js(4.0000000000050000001))
3263    FunctionPrototype.put('Chuj', Js(409))
3264    for e in FunctionPrototype:
3265        print('Obk', e.get('__proto__').get('__proto__').get('__proto__'), e)
3266    import code
3267    s = Js(4)
3268    b = Js(6)
3269
3270    s2 = Js(4)
3271    o = ObjectPrototype
3272    o.put('x', Js(100))
3273    var = Scope(scope)
3274    e = code.InteractiveConsole(globals())
3275    #e.raw_input = interactor
3276    e.interact()
3277