1from .operations import * 2from .base import get_member, get_member_dot, PyJsFunction, Scope 3 4 5class OP_CODE(object): 6 _params = [] 7 8 # def eval(self, ctx): 9 # raise 10 11 def __repr__(self): 12 return self.__class__.__name__ + str( 13 tuple([getattr(self, e) for e in self._params])) 14 15 16# --------------------- UNARY ---------------------- 17 18 19class UNARY_OP(OP_CODE): 20 _params = ['operator'] 21 22 def __init__(self, operator): 23 self.operator = operator 24 25 def eval(self, ctx): 26 val = ctx.stack.pop() 27 ctx.stack.append(UNARY_OPERATIONS[self.operator](val)) 28 29 30# special unary operations 31 32 33class TYPEOF(OP_CODE): 34 _params = ['identifier'] 35 36 def __init__(self, identifier): 37 self.identifier = identifier 38 39 def eval(self, ctx): 40 # typeof something_undefined does not throw reference error 41 val = ctx.get(self.identifier, 42 False) # <= this makes it slightly different! 43 ctx.stack.append(typeof_uop(val)) 44 45 46class POSTFIX(OP_CODE): 47 _params = ['cb', 'ca', 'identifier'] 48 49 def __init__(self, post, incr, identifier): 50 self.identifier = identifier 51 self.cb = 1 if incr else -1 52 self.ca = -self.cb if post else 0 53 54 def eval(self, ctx): 55 target = to_number(ctx.get(self.identifier)) + self.cb 56 ctx.put(self.identifier, target) 57 ctx.stack.append(target + self.ca) 58 59 60class POSTFIX_MEMBER(OP_CODE): 61 _params = ['cb', 'ca'] 62 63 def __init__(self, post, incr): 64 self.cb = 1 if incr else -1 65 self.ca = -self.cb if post else 0 66 67 def eval(self, ctx): 68 name = ctx.stack.pop() 69 left = ctx.stack.pop() 70 71 target = to_number(get_member(left, name, ctx.space)) + self.cb 72 if type(left) not in PRIMITIVES: 73 left.put_member(name, target) 74 75 ctx.stack.append(target + self.ca) 76 77 78class POSTFIX_MEMBER_DOT(OP_CODE): 79 _params = ['cb', 'ca', 'prop'] 80 81 def __init__(self, post, incr, prop): 82 self.cb = 1 if incr else -1 83 self.ca = -self.cb if post else 0 84 self.prop = prop 85 86 def eval(self, ctx): 87 left = ctx.stack.pop() 88 89 target = to_number(get_member_dot(left, self.prop, 90 ctx.space)) + self.cb 91 if type(left) not in PRIMITIVES: 92 left.put(self.prop, target) 93 94 ctx.stack.append(target + self.ca) 95 96 97class DELETE(OP_CODE): 98 _params = ['name'] 99 100 def __init__(self, name): 101 self.name = name 102 103 def eval(self, ctx): 104 ctx.stack.append(ctx.delete(self.name)) 105 106 107class DELETE_MEMBER(OP_CODE): 108 def eval(self, ctx): 109 prop = to_string(ctx.stack.pop()) 110 obj = to_object(ctx.stack.pop(), ctx) 111 ctx.stack.append(obj.delete(prop, False)) 112 113 114# --------------------- BITWISE ---------------------- 115 116 117class BINARY_OP(OP_CODE): 118 _params = ['operator'] 119 120 def __init__(self, operator): 121 self.operator = operator 122 123 def eval(self, ctx): 124 right = ctx.stack.pop() 125 left = ctx.stack.pop() 126 ctx.stack.append(BINARY_OPERATIONS[self.operator](left, right)) 127 128 129# &&, || and conditional are implemented in bytecode 130 131# --------------------- JUMPS ---------------------- 132 133 134# simple label that will be removed from code after compilation. labels ID will be translated 135# to source code position. 136class LABEL(OP_CODE): 137 _params = ['num'] 138 139 def __init__(self, num): 140 self.num = num 141 142 143# I implemented interpreter in the way that when an integer is returned by eval operation the execution will jump 144# to the location of the label (it is loc = label_locations[label]) 145 146 147class BASE_JUMP(OP_CODE): 148 _params = ['label'] 149 150 def __init__(self, label): 151 self.label = label 152 153 154class JUMP(BASE_JUMP): 155 def eval(self, ctx): 156 return self.label 157 158 159class JUMP_IF_TRUE(BASE_JUMP): 160 def eval(self, ctx): 161 val = ctx.stack.pop() 162 if to_boolean(val): 163 return self.label 164 165 166class JUMP_IF_EQ(BASE_JUMP): 167 # this one is used in switch statement - compares last 2 values using === operator and jumps popping both if true else pops last. 168 def eval(self, ctx): 169 cmp = ctx.stack.pop() 170 if strict_equality_op(ctx.stack[-1], cmp): 171 ctx.stack.pop() 172 return self.label 173 174 175class JUMP_IF_TRUE_WITHOUT_POP(BASE_JUMP): 176 def eval(self, ctx): 177 val = ctx.stack[-1] 178 if to_boolean(val): 179 return self.label 180 181 182class JUMP_IF_FALSE(BASE_JUMP): 183 def eval(self, ctx): 184 val = ctx.stack.pop() 185 if not to_boolean(val): 186 return self.label 187 188 189class JUMP_IF_FALSE_WITHOUT_POP(BASE_JUMP): 190 def eval(self, ctx): 191 val = ctx.stack[-1] 192 if not to_boolean(val): 193 return self.label 194 195 196class POP(OP_CODE): 197 def eval(self, ctx): 198 # todo remove this check later 199 assert len(ctx.stack), 'Popped from empty stack!' 200 del ctx.stack[-1] 201 202 203# class REDUCE(OP_CODE): 204# def eval(self, ctx): 205# assert len(ctx.stack)==2 206# ctx.stack[0] = ctx.stack[1] 207# del ctx.stack[1] 208 209# --------------- LOADING -------------- 210 211 212class LOAD_NONE(OP_CODE): # be careful with this :) 213 _params = [] 214 215 def eval(self, ctx): 216 ctx.stack.append(None) 217 218 219class LOAD_N_TUPLE( 220 OP_CODE 221): # loads the tuple composed of n last elements on stack. elements are popped. 222 _params = ['n'] 223 224 def __init__(self, n): 225 self.n = n 226 227 def eval(self, ctx): 228 tup = tuple(ctx.stack[-self.n:]) 229 del ctx.stack[-self.n:] 230 ctx.stack.append(tup) 231 232 233class LOAD_UNDEFINED(OP_CODE): 234 def eval(self, ctx): 235 ctx.stack.append(undefined) 236 237 238class LOAD_NULL(OP_CODE): 239 def eval(self, ctx): 240 ctx.stack.append(null) 241 242 243class LOAD_BOOLEAN(OP_CODE): 244 _params = ['val'] 245 246 def __init__(self, val): 247 assert val in (0, 1) 248 self.val = bool(val) 249 250 def eval(self, ctx): 251 ctx.stack.append(self.val) 252 253 254class LOAD_STRING(OP_CODE): 255 _params = ['val'] 256 257 def __init__(self, val): 258 assert isinstance(val, basestring) 259 self.val = unicode(val) 260 261 def eval(self, ctx): 262 ctx.stack.append(self.val) 263 264 265class LOAD_NUMBER(OP_CODE): 266 _params = ['val'] 267 268 def __init__(self, val): 269 assert isinstance(val, (float, int, long)) 270 self.val = float(val) 271 272 def eval(self, ctx): 273 ctx.stack.append(self.val) 274 275 276class LOAD_REGEXP(OP_CODE): 277 _params = ['body', 'flags'] 278 279 def __init__(self, body, flags): 280 self.body = body 281 self.flags = flags 282 283 def eval(self, ctx): 284 # we have to generate a new regexp - they are mutable 285 ctx.stack.append(ctx.space.NewRegExp(self.body, self.flags)) 286 287 288class LOAD_FUNCTION(OP_CODE): 289 _params = ['start', 'params', 'name', 'is_declaration', 'definitions'] 290 291 def __init__(self, start, params, name, is_declaration, definitions): 292 assert type(start) == int 293 self.start = start # its an ID of label pointing to the beginning of the function bytecode 294 self.params = params 295 self.name = name 296 self.is_declaration = bool(is_declaration) 297 self.definitions = tuple(set(definitions + params)) 298 299 def eval(self, ctx): 300 ctx.stack.append( 301 ctx.space.NewFunction(self.start, ctx, self.params, self.name, 302 self.is_declaration, self.definitions)) 303 304 305class LOAD_OBJECT(OP_CODE): 306 _params = [ 307 'props' 308 ] # props are py string pairs (prop_name, kind): kind can be either i, g or s. (init, get, set) 309 310 def __init__(self, props): 311 self.num = len(props) 312 self.props = props 313 314 def eval(self, ctx): 315 obj = ctx.space.NewObject() 316 if self.num: 317 obj._init(self.props, ctx.stack[-self.num:]) 318 del ctx.stack[-self.num:] 319 320 ctx.stack.append(obj) 321 322 323class LOAD_ARRAY(OP_CODE): 324 _params = ['num'] 325 326 def __init__(self, num): 327 self.num = num 328 329 def eval(self, ctx): 330 arr = ctx.space.NewArray(self.num) 331 if self.num: 332 arr._init(ctx.stack[-self.num:]) 333 del ctx.stack[-self.num:] 334 ctx.stack.append(arr) 335 336 337class LOAD_THIS(OP_CODE): 338 def eval(self, ctx): 339 ctx.stack.append(ctx.THIS_BINDING) 340 341 342class LOAD(OP_CODE): # todo check! 343 _params = ['identifier'] 344 345 def __init__(self, identifier): 346 self.identifier = identifier 347 348 # 11.1.2 349 def eval(self, ctx): 350 ctx.stack.append(ctx.get(self.identifier, throw=True)) 351 352 353class LOAD_MEMBER(OP_CODE): 354 def eval(self, ctx): 355 prop = ctx.stack.pop() 356 obj = ctx.stack.pop() 357 ctx.stack.append(get_member(obj, prop, ctx.space)) 358 359 360class LOAD_MEMBER_DOT(OP_CODE): 361 _params = ['prop'] 362 363 def __init__(self, prop): 364 self.prop = prop 365 366 def eval(self, ctx): 367 obj = ctx.stack.pop() 368 ctx.stack.append(get_member_dot(obj, self.prop, ctx.space)) 369 370 371# --------------- STORING -------------- 372 373 374class STORE(OP_CODE): 375 _params = ['identifier'] 376 377 def __init__(self, identifier): 378 self.identifier = identifier 379 380 def eval(self, ctx): 381 value = ctx.stack[-1] # don't pop 382 ctx.put(self.identifier, value) 383 384 385class STORE_MEMBER(OP_CODE): 386 def eval(self, ctx): 387 value = ctx.stack.pop() 388 name = ctx.stack.pop() 389 left = ctx.stack.pop() 390 391 typ = type(left) 392 if typ in PRIMITIVES: 393 prop = to_string(name) 394 if typ == NULL_TYPE: 395 raise MakeError('TypeError', 396 "Cannot set property '%s' of null" % prop) 397 elif typ == UNDEFINED_TYPE: 398 raise MakeError('TypeError', 399 "Cannot set property '%s' of undefined" % prop) 400 # just ignore... 401 else: 402 left.put_member(name, value) 403 404 ctx.stack.append(value) 405 406 407class STORE_MEMBER_DOT(OP_CODE): 408 _params = ['prop'] 409 410 def __init__(self, prop): 411 self.prop = prop 412 413 def eval(self, ctx): 414 value = ctx.stack.pop() 415 left = ctx.stack.pop() 416 417 typ = type(left) 418 if typ in PRIMITIVES: 419 if typ == NULL_TYPE: 420 raise MakeError('TypeError', 421 "Cannot set property '%s' of null" % self.prop) 422 elif typ == UNDEFINED_TYPE: 423 raise MakeError( 424 'TypeError', 425 "Cannot set property '%s' of undefined" % self.prop) 426 # just ignore... 427 else: 428 left.put(self.prop, value) 429 ctx.stack.append(value) 430 431 432class STORE_OP(OP_CODE): 433 _params = ['identifier', 'op'] 434 435 def __init__(self, identifier, op): 436 self.identifier = identifier 437 self.op = op 438 439 def eval(self, ctx): 440 value = ctx.stack.pop() 441 new_value = BINARY_OPERATIONS[self.op](ctx.get(self.identifier), value) 442 ctx.put(self.identifier, new_value) 443 ctx.stack.append(new_value) 444 445 446class STORE_MEMBER_OP(OP_CODE): 447 _params = ['op'] 448 449 def __init__(self, op): 450 self.op = op 451 452 def eval(self, ctx): 453 value = ctx.stack.pop() 454 name = ctx.stack.pop() 455 left = ctx.stack.pop() 456 457 typ = type(left) 458 if typ in PRIMITIVES: 459 if typ is NULL_TYPE: 460 raise MakeError( 461 'TypeError', 462 "Cannot set property '%s' of null" % to_string(name)) 463 elif typ is UNDEFINED_TYPE: 464 raise MakeError( 465 'TypeError', 466 "Cannot set property '%s' of undefined" % to_string(name)) 467 ctx.stack.append(BINARY_OPERATIONS[self.op](get_member( 468 left, name, ctx.space), value)) 469 return 470 else: 471 ctx.stack.append(BINARY_OPERATIONS[self.op](get_member( 472 left, name, ctx.space), value)) 473 left.put_member(name, ctx.stack[-1]) 474 475 476class STORE_MEMBER_DOT_OP(OP_CODE): 477 _params = ['prop', 'op'] 478 479 def __init__(self, prop, op): 480 self.prop = prop 481 self.op = op 482 483 def eval(self, ctx): 484 value = ctx.stack.pop() 485 left = ctx.stack.pop() 486 487 typ = type(left) 488 if typ in PRIMITIVES: 489 if typ == NULL_TYPE: 490 raise MakeError('TypeError', 491 "Cannot set property '%s' of null" % self.prop) 492 elif typ == UNDEFINED_TYPE: 493 raise MakeError( 494 'TypeError', 495 "Cannot set property '%s' of undefined" % self.prop) 496 ctx.stack.append(BINARY_OPERATIONS[self.op](get_member_dot( 497 left, self.prop, ctx.space), value)) 498 return 499 else: 500 ctx.stack.append(BINARY_OPERATIONS[self.op](get_member_dot( 501 left, self.prop, ctx.space), value)) 502 left.put(self.prop, ctx.stack[-1]) 503 504 505# --------------- CALLS -------------- 506 507 508def bytecode_call(ctx, func, this, args): 509 if type(func) is not PyJsFunction: 510 raise MakeError('TypeError', "%s is not a function" % Type(func)) 511 if func.is_native: # call to built-in function or method 512 ctx.stack.append(func.call(this, args)) 513 return None 514 515 # therefore not native. we have to return (new_context, function_label) to instruct interpreter to call 516 return func._generate_my_context(this, args), func.code 517 518 519class CALL(OP_CODE): 520 def eval(self, ctx): 521 args = ctx.stack.pop() 522 func = ctx.stack.pop() 523 524 return bytecode_call(ctx, func, ctx.space.GlobalObj, args) 525 526 527class CALL_METHOD(OP_CODE): 528 def eval(self, ctx): 529 args = ctx.stack.pop() 530 prop = ctx.stack.pop() 531 base = ctx.stack.pop() 532 533 func = get_member(base, prop, ctx.space) 534 535 return bytecode_call(ctx, func, base, args) 536 537 538class CALL_METHOD_DOT(OP_CODE): 539 _params = ['prop'] 540 541 def __init__(self, prop): 542 self.prop = prop 543 544 def eval(self, ctx): 545 args = ctx.stack.pop() 546 base = ctx.stack.pop() 547 548 func = get_member_dot(base, self.prop, ctx.space) 549 550 return bytecode_call(ctx, func, base, args) 551 552 553class CALL_NO_ARGS(OP_CODE): 554 def eval(self, ctx): 555 func = ctx.stack.pop() 556 557 return bytecode_call(ctx, func, ctx.space.GlobalObj, ()) 558 559 560class CALL_METHOD_NO_ARGS(OP_CODE): 561 def eval(self, ctx): 562 prop = ctx.stack.pop() 563 base = ctx.stack.pop() 564 565 func = get_member(base, prop, ctx.space) 566 567 return bytecode_call(ctx, func, base, ()) 568 569 570class CALL_METHOD_DOT_NO_ARGS(OP_CODE): 571 _params = ['prop'] 572 573 def __init__(self, prop): 574 self.prop = prop 575 576 def eval(self, ctx): 577 base = ctx.stack.pop() 578 579 func = get_member_dot(base, self.prop, ctx.space) 580 581 return bytecode_call(ctx, func, base, ()) 582 583 584class NOP(OP_CODE): 585 def eval(self, ctx): 586 pass 587 588 589class RETURN(OP_CODE): 590 def eval( 591 self, ctx 592 ): # remember to load the return value on stack before using RETURN op. 593 return (None, None) 594 595 596class NEW(OP_CODE): 597 def eval(self, ctx): 598 args = ctx.stack.pop() 599 constructor = ctx.stack.pop() 600 if type(constructor) in PRIMITIVES or not hasattr( 601 constructor, 'create'): 602 raise MakeError('TypeError', 603 '%s is not a constructor' % Type(constructor)) 604 ctx.stack.append(constructor.create(args, space=ctx.space)) 605 606 607class NEW_NO_ARGS(OP_CODE): 608 def eval(self, ctx): 609 constructor = ctx.stack.pop() 610 if type(constructor) in PRIMITIVES or not hasattr( 611 constructor, 'create'): 612 raise MakeError('TypeError', 613 '%s is not a constructor' % Type(constructor)) 614 ctx.stack.append(constructor.create((), space=ctx.space)) 615 616 617# --------------- EXCEPTIONS -------------- 618 619 620class THROW(OP_CODE): 621 def eval(self, ctx): 622 raise MakeError(None, None, ctx.stack.pop()) 623 624 625class TRY_CATCH_FINALLY(OP_CODE): 626 _params = [ 627 'try_label', 'catch_label', 'catch_variable', 'finally_label', 628 'finally_present', 'end_label' 629 ] 630 631 def __init__(self, try_label, catch_label, catch_variable, finally_label, 632 finally_present, end_label): 633 self.try_label = try_label 634 self.catch_label = catch_label 635 self.catch_variable = catch_variable 636 self.finally_label = finally_label 637 self.finally_present = finally_present 638 self.end_label = end_label 639 640 def eval(self, ctx): 641 # 4 different exectution results 642 # 0=normal, 1=return, 2=jump_outside, 3=errors 643 # execute_fragment_under_context returns: 644 # (return_value, typ, jump_loc/error) 645 646 ctx.stack.pop() 647 648 # execute try statement 649 try_status = ctx.space.exe.execute_fragment_under_context( 650 ctx, self.try_label, self.catch_label) 651 652 errors = try_status[1] == 3 653 654 # catch 655 if errors and self.catch_variable is not None: 656 # generate catch block context... 657 catch_context = Scope({ 658 self.catch_variable: 659 try_status[2].get_thrown_value(ctx.space) 660 }, ctx.space, ctx) 661 catch_context.THIS_BINDING = ctx.THIS_BINDING 662 catch_status = ctx.space.exe.execute_fragment_under_context( 663 catch_context, self.catch_label, self.finally_label) 664 else: 665 catch_status = None 666 667 # finally 668 if self.finally_present: 669 finally_status = ctx.space.exe.execute_fragment_under_context( 670 ctx, self.finally_label, self.end_label) 671 else: 672 finally_status = None 673 674 # now return controls 675 other_status = catch_status or try_status 676 if finally_status is None or (finally_status[1] == 0 677 and other_status[1] != 0): 678 winning_status = other_status 679 else: 680 winning_status = finally_status 681 682 val, typ, spec = winning_status 683 if typ == 0: # normal 684 ctx.stack.append(val) 685 return 686 elif typ == 1: # return 687 ctx.stack.append(spec) 688 return None, None # send return signal 689 elif typ == 2: # jump outside 690 ctx.stack.append(val) 691 return spec 692 elif typ == 3: 693 # throw is made with empty stack as usual 694 raise spec 695 else: 696 raise RuntimeError('Invalid return code') 697 698 699# ------------ WITH + ITERATORS ---------- 700 701 702class WITH(OP_CODE): 703 _params = ['beg_label', 'end_label'] 704 705 def __init__(self, beg_label, end_label): 706 self.beg_label = beg_label 707 self.end_label = end_label 708 709 def eval(self, ctx): 710 obj = to_object(ctx.stack.pop(), ctx.space) 711 712 with_context = Scope( 713 obj, ctx.space, ctx) # todo actually use the obj to modify the ctx 714 with_context.THIS_BINDING = ctx.THIS_BINDING 715 status = ctx.space.exe.execute_fragment_under_context( 716 with_context, self.beg_label, self.end_label) 717 718 val, typ, spec = status 719 720 if typ != 3: # exception 721 ctx.stack.pop() 722 723 if typ == 0: # normal 724 ctx.stack.append(val) 725 return 726 elif typ == 1: # return 727 ctx.stack.append(spec) 728 return None, None # send return signal 729 elif typ == 2: # jump outside 730 ctx.stack.append(val) 731 return spec 732 elif typ == 3: # exception 733 # throw is made with empty stack as usual 734 raise spec 735 else: 736 raise RuntimeError('Invalid return code') 737 738 739class FOR_IN(OP_CODE): 740 _params = ['name', 'body_start_label', 'continue_label', 'break_label'] 741 742 def __init__(self, name, body_start_label, continue_label, break_label): 743 self.name = name 744 self.body_start_label = body_start_label 745 self.continue_label = continue_label 746 self.break_label = break_label 747 748 def eval(self, ctx): 749 iterable = ctx.stack.pop() 750 if is_null(iterable) or is_undefined(iterable): 751 ctx.stack.pop() 752 ctx.stack.append(undefined) 753 return self.break_label 754 755 obj = to_object(iterable, ctx.space) 756 757 for e in sorted(obj.own): 758 if not obj.own[e]['enumerable']: 759 continue 760 761 ctx.put( 762 self.name, e 763 ) # JS would have been so much nicer if this was ctx.space.put(self.name, obj.get(e)) 764 765 # evaluate the body 766 status = ctx.space.exe.execute_fragment_under_context( 767 ctx, self.body_start_label, self.break_label) 768 769 val, typ, spec = status 770 771 if typ != 3: # exception 772 ctx.stack.pop() 773 774 if typ == 0: # normal 775 ctx.stack.append(val) 776 continue 777 elif typ == 1: # return 778 ctx.stack.append(spec) 779 return None, None # send return signal 780 elif typ == 2: # jump outside 781 # now have to figure out whether this is a continue or something else... 782 ctx.stack.append(val) 783 if spec == self.continue_label: 784 # just a continue, perform next iteration as normal 785 continue 786 return spec # break or smth, go there and finish the iteration 787 elif typ == 3: # exception 788 # throw is made with empty stack as usual 789 raise spec 790 else: 791 raise RuntimeError('Invalid return code') 792 793 return self.break_label 794 795 796# all opcodes... 797OP_CODES = {} 798g = '' 799for g in globals(): 800 try: 801 if not issubclass(globals()[g], OP_CODE) or g is 'OP_CODE': 802 continue 803 except: 804 continue 805 OP_CODES[g] = globals()[g] 806