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