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