youtube-dl

Another place where youtube-dl lives on
git clone git://git.oshgnacknak.de/youtube-dl.git
Log | Files | Refs | README | LICENSE

swfinterp.py (31486B)


      1 from __future__ import unicode_literals
      2 
      3 import collections
      4 import io
      5 import zlib
      6 
      7 from .compat import (
      8     compat_str,
      9     compat_struct_unpack,
     10 )
     11 from .utils import (
     12     ExtractorError,
     13 )
     14 
     15 
     16 def _extract_tags(file_contents):
     17     if file_contents[1:3] != b'WS':
     18         raise ExtractorError(
     19             'Not an SWF file; header is %r' % file_contents[:3])
     20     if file_contents[:1] == b'C':
     21         content = zlib.decompress(file_contents[8:])
     22     else:
     23         raise NotImplementedError(
     24             'Unsupported compression format %r' %
     25             file_contents[:1])
     26 
     27     # Determine number of bits in framesize rectangle
     28     framesize_nbits = compat_struct_unpack('!B', content[:1])[0] >> 3
     29     framesize_len = (5 + 4 * framesize_nbits + 7) // 8
     30 
     31     pos = framesize_len + 2 + 2
     32     while pos < len(content):
     33         header16 = compat_struct_unpack('<H', content[pos:pos + 2])[0]
     34         pos += 2
     35         tag_code = header16 >> 6
     36         tag_len = header16 & 0x3f
     37         if tag_len == 0x3f:
     38             tag_len = compat_struct_unpack('<I', content[pos:pos + 4])[0]
     39             pos += 4
     40         assert pos + tag_len <= len(content), \
     41             ('Tag %d ends at %d+%d - that\'s longer than the file (%d)'
     42                 % (tag_code, pos, tag_len, len(content)))
     43         yield (tag_code, content[pos:pos + tag_len])
     44         pos += tag_len
     45 
     46 
     47 class _AVMClass_Object(object):
     48     def __init__(self, avm_class):
     49         self.avm_class = avm_class
     50 
     51     def __repr__(self):
     52         return '%s#%x' % (self.avm_class.name, id(self))
     53 
     54 
     55 class _ScopeDict(dict):
     56     def __init__(self, avm_class):
     57         super(_ScopeDict, self).__init__()
     58         self.avm_class = avm_class
     59 
     60     def __repr__(self):
     61         return '%s__Scope(%s)' % (
     62             self.avm_class.name,
     63             super(_ScopeDict, self).__repr__())
     64 
     65 
     66 class _AVMClass(object):
     67     def __init__(self, name_idx, name, static_properties=None):
     68         self.name_idx = name_idx
     69         self.name = name
     70         self.method_names = {}
     71         self.method_idxs = {}
     72         self.methods = {}
     73         self.method_pyfunctions = {}
     74         self.static_properties = static_properties if static_properties else {}
     75 
     76         self.variables = _ScopeDict(self)
     77         self.constants = {}
     78 
     79     def make_object(self):
     80         return _AVMClass_Object(self)
     81 
     82     def __repr__(self):
     83         return '_AVMClass(%s)' % (self.name)
     84 
     85     def register_methods(self, methods):
     86         self.method_names.update(methods.items())
     87         self.method_idxs.update(dict(
     88             (idx, name)
     89             for name, idx in methods.items()))
     90 
     91 
     92 class _Multiname(object):
     93     def __init__(self, kind):
     94         self.kind = kind
     95 
     96     def __repr__(self):
     97         return '[MULTINAME kind: 0x%x]' % self.kind
     98 
     99 
    100 def _read_int(reader):
    101     res = 0
    102     shift = 0
    103     for _ in range(5):
    104         buf = reader.read(1)
    105         assert len(buf) == 1
    106         b = compat_struct_unpack('<B', buf)[0]
    107         res = res | ((b & 0x7f) << shift)
    108         if b & 0x80 == 0:
    109             break
    110         shift += 7
    111     return res
    112 
    113 
    114 def _u30(reader):
    115     res = _read_int(reader)
    116     assert res & 0xf0000000 == 0
    117     return res
    118 
    119 
    120 _u32 = _read_int
    121 
    122 
    123 def _s32(reader):
    124     v = _read_int(reader)
    125     if v & 0x80000000 != 0:
    126         v = - ((v ^ 0xffffffff) + 1)
    127     return v
    128 
    129 
    130 def _s24(reader):
    131     bs = reader.read(3)
    132     assert len(bs) == 3
    133     last_byte = b'\xff' if (ord(bs[2:3]) >= 0x80) else b'\x00'
    134     return compat_struct_unpack('<i', bs + last_byte)[0]
    135 
    136 
    137 def _read_string(reader):
    138     slen = _u30(reader)
    139     resb = reader.read(slen)
    140     assert len(resb) == slen
    141     return resb.decode('utf-8')
    142 
    143 
    144 def _read_bytes(count, reader):
    145     assert count >= 0
    146     resb = reader.read(count)
    147     assert len(resb) == count
    148     return resb
    149 
    150 
    151 def _read_byte(reader):
    152     resb = _read_bytes(1, reader=reader)
    153     res = compat_struct_unpack('<B', resb)[0]
    154     return res
    155 
    156 
    157 StringClass = _AVMClass('(no name idx)', 'String')
    158 ByteArrayClass = _AVMClass('(no name idx)', 'ByteArray')
    159 TimerClass = _AVMClass('(no name idx)', 'Timer')
    160 TimerEventClass = _AVMClass('(no name idx)', 'TimerEvent', {'TIMER': 'timer'})
    161 _builtin_classes = {
    162     StringClass.name: StringClass,
    163     ByteArrayClass.name: ByteArrayClass,
    164     TimerClass.name: TimerClass,
    165     TimerEventClass.name: TimerEventClass,
    166 }
    167 
    168 
    169 class _Undefined(object):
    170     def __bool__(self):
    171         return False
    172     __nonzero__ = __bool__
    173 
    174     def __hash__(self):
    175         return 0
    176 
    177     def __str__(self):
    178         return 'undefined'
    179     __repr__ = __str__
    180 
    181 
    182 undefined = _Undefined()
    183 
    184 
    185 class SWFInterpreter(object):
    186     def __init__(self, file_contents):
    187         self._patched_functions = {
    188             (TimerClass, 'addEventListener'): lambda params: undefined,
    189         }
    190         code_tag = next(tag
    191                         for tag_code, tag in _extract_tags(file_contents)
    192                         if tag_code == 82)
    193         p = code_tag.index(b'\0', 4) + 1
    194         code_reader = io.BytesIO(code_tag[p:])
    195 
    196         # Parse ABC (AVM2 ByteCode)
    197 
    198         # Define a couple convenience methods
    199         u30 = lambda *args: _u30(*args, reader=code_reader)
    200         s32 = lambda *args: _s32(*args, reader=code_reader)
    201         u32 = lambda *args: _u32(*args, reader=code_reader)
    202         read_bytes = lambda *args: _read_bytes(*args, reader=code_reader)
    203         read_byte = lambda *args: _read_byte(*args, reader=code_reader)
    204 
    205         # minor_version + major_version
    206         read_bytes(2 + 2)
    207 
    208         # Constant pool
    209         int_count = u30()
    210         self.constant_ints = [0]
    211         for _c in range(1, int_count):
    212             self.constant_ints.append(s32())
    213         self.constant_uints = [0]
    214         uint_count = u30()
    215         for _c in range(1, uint_count):
    216             self.constant_uints.append(u32())
    217         double_count = u30()
    218         read_bytes(max(0, (double_count - 1)) * 8)
    219         string_count = u30()
    220         self.constant_strings = ['']
    221         for _c in range(1, string_count):
    222             s = _read_string(code_reader)
    223             self.constant_strings.append(s)
    224         namespace_count = u30()
    225         for _c in range(1, namespace_count):
    226             read_bytes(1)  # kind
    227             u30()  # name
    228         ns_set_count = u30()
    229         for _c in range(1, ns_set_count):
    230             count = u30()
    231             for _c2 in range(count):
    232                 u30()
    233         multiname_count = u30()
    234         MULTINAME_SIZES = {
    235             0x07: 2,  # QName
    236             0x0d: 2,  # QNameA
    237             0x0f: 1,  # RTQName
    238             0x10: 1,  # RTQNameA
    239             0x11: 0,  # RTQNameL
    240             0x12: 0,  # RTQNameLA
    241             0x09: 2,  # Multiname
    242             0x0e: 2,  # MultinameA
    243             0x1b: 1,  # MultinameL
    244             0x1c: 1,  # MultinameLA
    245         }
    246         self.multinames = ['']
    247         for _c in range(1, multiname_count):
    248             kind = u30()
    249             assert kind in MULTINAME_SIZES, 'Invalid multiname kind %r' % kind
    250             if kind == 0x07:
    251                 u30()  # namespace_idx
    252                 name_idx = u30()
    253                 self.multinames.append(self.constant_strings[name_idx])
    254             elif kind == 0x09:
    255                 name_idx = u30()
    256                 u30()
    257                 self.multinames.append(self.constant_strings[name_idx])
    258             else:
    259                 self.multinames.append(_Multiname(kind))
    260                 for _c2 in range(MULTINAME_SIZES[kind]):
    261                     u30()
    262 
    263         # Methods
    264         method_count = u30()
    265         MethodInfo = collections.namedtuple(
    266             'MethodInfo',
    267             ['NEED_ARGUMENTS', 'NEED_REST'])
    268         method_infos = []
    269         for method_id in range(method_count):
    270             param_count = u30()
    271             u30()  # return type
    272             for _ in range(param_count):
    273                 u30()  # param type
    274             u30()  # name index (always 0 for youtube)
    275             flags = read_byte()
    276             if flags & 0x08 != 0:
    277                 # Options present
    278                 option_count = u30()
    279                 for c in range(option_count):
    280                     u30()  # val
    281                     read_bytes(1)  # kind
    282             if flags & 0x80 != 0:
    283                 # Param names present
    284                 for _ in range(param_count):
    285                     u30()  # param name
    286             mi = MethodInfo(flags & 0x01 != 0, flags & 0x04 != 0)
    287             method_infos.append(mi)
    288 
    289         # Metadata
    290         metadata_count = u30()
    291         for _c in range(metadata_count):
    292             u30()  # name
    293             item_count = u30()
    294             for _c2 in range(item_count):
    295                 u30()  # key
    296                 u30()  # value
    297 
    298         def parse_traits_info():
    299             trait_name_idx = u30()
    300             kind_full = read_byte()
    301             kind = kind_full & 0x0f
    302             attrs = kind_full >> 4
    303             methods = {}
    304             constants = None
    305             if kind == 0x00:  # Slot
    306                 u30()  # Slot id
    307                 u30()  # type_name_idx
    308                 vindex = u30()
    309                 if vindex != 0:
    310                     read_byte()  # vkind
    311             elif kind == 0x06:  # Const
    312                 u30()  # Slot id
    313                 u30()  # type_name_idx
    314                 vindex = u30()
    315                 vkind = 'any'
    316                 if vindex != 0:
    317                     vkind = read_byte()
    318                 if vkind == 0x03:  # Constant_Int
    319                     value = self.constant_ints[vindex]
    320                 elif vkind == 0x04:  # Constant_UInt
    321                     value = self.constant_uints[vindex]
    322                 else:
    323                     return {}, None  # Ignore silently for now
    324                 constants = {self.multinames[trait_name_idx]: value}
    325             elif kind in (0x01, 0x02, 0x03):  # Method / Getter / Setter
    326                 u30()  # disp_id
    327                 method_idx = u30()
    328                 methods[self.multinames[trait_name_idx]] = method_idx
    329             elif kind == 0x04:  # Class
    330                 u30()  # slot_id
    331                 u30()  # classi
    332             elif kind == 0x05:  # Function
    333                 u30()  # slot_id
    334                 function_idx = u30()
    335                 methods[function_idx] = self.multinames[trait_name_idx]
    336             else:
    337                 raise ExtractorError('Unsupported trait kind %d' % kind)
    338 
    339             if attrs & 0x4 != 0:  # Metadata present
    340                 metadata_count = u30()
    341                 for _c3 in range(metadata_count):
    342                     u30()  # metadata index
    343 
    344             return methods, constants
    345 
    346         # Classes
    347         class_count = u30()
    348         classes = []
    349         for class_id in range(class_count):
    350             name_idx = u30()
    351 
    352             cname = self.multinames[name_idx]
    353             avm_class = _AVMClass(name_idx, cname)
    354             classes.append(avm_class)
    355 
    356             u30()  # super_name idx
    357             flags = read_byte()
    358             if flags & 0x08 != 0:  # Protected namespace is present
    359                 u30()  # protected_ns_idx
    360             intrf_count = u30()
    361             for _c2 in range(intrf_count):
    362                 u30()
    363             u30()  # iinit
    364             trait_count = u30()
    365             for _c2 in range(trait_count):
    366                 trait_methods, trait_constants = parse_traits_info()
    367                 avm_class.register_methods(trait_methods)
    368                 if trait_constants:
    369                     avm_class.constants.update(trait_constants)
    370 
    371         assert len(classes) == class_count
    372         self._classes_by_name = dict((c.name, c) for c in classes)
    373 
    374         for avm_class in classes:
    375             avm_class.cinit_idx = u30()
    376             trait_count = u30()
    377             for _c2 in range(trait_count):
    378                 trait_methods, trait_constants = parse_traits_info()
    379                 avm_class.register_methods(trait_methods)
    380                 if trait_constants:
    381                     avm_class.constants.update(trait_constants)
    382 
    383         # Scripts
    384         script_count = u30()
    385         for _c in range(script_count):
    386             u30()  # init
    387             trait_count = u30()
    388             for _c2 in range(trait_count):
    389                 parse_traits_info()
    390 
    391         # Method bodies
    392         method_body_count = u30()
    393         Method = collections.namedtuple('Method', ['code', 'local_count'])
    394         self._all_methods = []
    395         for _c in range(method_body_count):
    396             method_idx = u30()
    397             u30()  # max_stack
    398             local_count = u30()
    399             u30()  # init_scope_depth
    400             u30()  # max_scope_depth
    401             code_length = u30()
    402             code = read_bytes(code_length)
    403             m = Method(code, local_count)
    404             self._all_methods.append(m)
    405             for avm_class in classes:
    406                 if method_idx in avm_class.method_idxs:
    407                     avm_class.methods[avm_class.method_idxs[method_idx]] = m
    408             exception_count = u30()
    409             for _c2 in range(exception_count):
    410                 u30()  # from
    411                 u30()  # to
    412                 u30()  # target
    413                 u30()  # exc_type
    414                 u30()  # var_name
    415             trait_count = u30()
    416             for _c2 in range(trait_count):
    417                 parse_traits_info()
    418 
    419         assert p + code_reader.tell() == len(code_tag)
    420 
    421     def patch_function(self, avm_class, func_name, f):
    422         self._patched_functions[(avm_class, func_name)] = f
    423 
    424     def extract_class(self, class_name, call_cinit=True):
    425         try:
    426             res = self._classes_by_name[class_name]
    427         except KeyError:
    428             raise ExtractorError('Class %r not found' % class_name)
    429 
    430         if call_cinit and hasattr(res, 'cinit_idx'):
    431             res.register_methods({'$cinit': res.cinit_idx})
    432             res.methods['$cinit'] = self._all_methods[res.cinit_idx]
    433             cinit = self.extract_function(res, '$cinit')
    434             cinit([])
    435 
    436         return res
    437 
    438     def extract_function(self, avm_class, func_name):
    439         p = self._patched_functions.get((avm_class, func_name))
    440         if p:
    441             return p
    442         if func_name in avm_class.method_pyfunctions:
    443             return avm_class.method_pyfunctions[func_name]
    444         if func_name in self._classes_by_name:
    445             return self._classes_by_name[func_name].make_object()
    446         if func_name not in avm_class.methods:
    447             raise ExtractorError('Cannot find function %s.%s' % (
    448                 avm_class.name, func_name))
    449         m = avm_class.methods[func_name]
    450 
    451         def resfunc(args):
    452             # Helper functions
    453             coder = io.BytesIO(m.code)
    454             s24 = lambda: _s24(coder)
    455             u30 = lambda: _u30(coder)
    456 
    457             registers = [avm_class.variables] + list(args) + [None] * m.local_count
    458             stack = []
    459             scopes = collections.deque([
    460                 self._classes_by_name, avm_class.constants, avm_class.variables])
    461             while True:
    462                 opcode = _read_byte(coder)
    463                 if opcode == 9:  # label
    464                     pass  # Spec says: "Do nothing."
    465                 elif opcode == 16:  # jump
    466                     offset = s24()
    467                     coder.seek(coder.tell() + offset)
    468                 elif opcode == 17:  # iftrue
    469                     offset = s24()
    470                     value = stack.pop()
    471                     if value:
    472                         coder.seek(coder.tell() + offset)
    473                 elif opcode == 18:  # iffalse
    474                     offset = s24()
    475                     value = stack.pop()
    476                     if not value:
    477                         coder.seek(coder.tell() + offset)
    478                 elif opcode == 19:  # ifeq
    479                     offset = s24()
    480                     value2 = stack.pop()
    481                     value1 = stack.pop()
    482                     if value2 == value1:
    483                         coder.seek(coder.tell() + offset)
    484                 elif opcode == 20:  # ifne
    485                     offset = s24()
    486                     value2 = stack.pop()
    487                     value1 = stack.pop()
    488                     if value2 != value1:
    489                         coder.seek(coder.tell() + offset)
    490                 elif opcode == 21:  # iflt
    491                     offset = s24()
    492                     value2 = stack.pop()
    493                     value1 = stack.pop()
    494                     if value1 < value2:
    495                         coder.seek(coder.tell() + offset)
    496                 elif opcode == 32:  # pushnull
    497                     stack.append(None)
    498                 elif opcode == 33:  # pushundefined
    499                     stack.append(undefined)
    500                 elif opcode == 36:  # pushbyte
    501                     v = _read_byte(coder)
    502                     stack.append(v)
    503                 elif opcode == 37:  # pushshort
    504                     v = u30()
    505                     stack.append(v)
    506                 elif opcode == 38:  # pushtrue
    507                     stack.append(True)
    508                 elif opcode == 39:  # pushfalse
    509                     stack.append(False)
    510                 elif opcode == 40:  # pushnan
    511                     stack.append(float('NaN'))
    512                 elif opcode == 42:  # dup
    513                     value = stack[-1]
    514                     stack.append(value)
    515                 elif opcode == 44:  # pushstring
    516                     idx = u30()
    517                     stack.append(self.constant_strings[idx])
    518                 elif opcode == 48:  # pushscope
    519                     new_scope = stack.pop()
    520                     scopes.append(new_scope)
    521                 elif opcode == 66:  # construct
    522                     arg_count = u30()
    523                     args = list(reversed(
    524                         [stack.pop() for _ in range(arg_count)]))
    525                     obj = stack.pop()
    526                     res = obj.avm_class.make_object()
    527                     stack.append(res)
    528                 elif opcode == 70:  # callproperty
    529                     index = u30()
    530                     mname = self.multinames[index]
    531                     arg_count = u30()
    532                     args = list(reversed(
    533                         [stack.pop() for _ in range(arg_count)]))
    534                     obj = stack.pop()
    535 
    536                     if obj == StringClass:
    537                         if mname == 'String':
    538                             assert len(args) == 1
    539                             assert isinstance(args[0], (
    540                                 int, compat_str, _Undefined))
    541                             if args[0] == undefined:
    542                                 res = 'undefined'
    543                             else:
    544                                 res = compat_str(args[0])
    545                             stack.append(res)
    546                             continue
    547                         else:
    548                             raise NotImplementedError(
    549                                 'Function String.%s is not yet implemented'
    550                                 % mname)
    551                     elif isinstance(obj, _AVMClass_Object):
    552                         func = self.extract_function(obj.avm_class, mname)
    553                         res = func(args)
    554                         stack.append(res)
    555                         continue
    556                     elif isinstance(obj, _AVMClass):
    557                         func = self.extract_function(obj, mname)
    558                         res = func(args)
    559                         stack.append(res)
    560                         continue
    561                     elif isinstance(obj, _ScopeDict):
    562                         if mname in obj.avm_class.method_names:
    563                             func = self.extract_function(obj.avm_class, mname)
    564                             res = func(args)
    565                         else:
    566                             res = obj[mname]
    567                         stack.append(res)
    568                         continue
    569                     elif isinstance(obj, compat_str):
    570                         if mname == 'split':
    571                             assert len(args) == 1
    572                             assert isinstance(args[0], compat_str)
    573                             if args[0] == '':
    574                                 res = list(obj)
    575                             else:
    576                                 res = obj.split(args[0])
    577                             stack.append(res)
    578                             continue
    579                         elif mname == 'charCodeAt':
    580                             assert len(args) <= 1
    581                             idx = 0 if len(args) == 0 else args[0]
    582                             assert isinstance(idx, int)
    583                             res = ord(obj[idx])
    584                             stack.append(res)
    585                             continue
    586                     elif isinstance(obj, list):
    587                         if mname == 'slice':
    588                             assert len(args) == 1
    589                             assert isinstance(args[0], int)
    590                             res = obj[args[0]:]
    591                             stack.append(res)
    592                             continue
    593                         elif mname == 'join':
    594                             assert len(args) == 1
    595                             assert isinstance(args[0], compat_str)
    596                             res = args[0].join(obj)
    597                             stack.append(res)
    598                             continue
    599                     raise NotImplementedError(
    600                         'Unsupported property %r on %r'
    601                         % (mname, obj))
    602                 elif opcode == 71:  # returnvoid
    603                     res = undefined
    604                     return res
    605                 elif opcode == 72:  # returnvalue
    606                     res = stack.pop()
    607                     return res
    608                 elif opcode == 73:  # constructsuper
    609                     # Not yet implemented, just hope it works without it
    610                     arg_count = u30()
    611                     args = list(reversed(
    612                         [stack.pop() for _ in range(arg_count)]))
    613                     obj = stack.pop()
    614                 elif opcode == 74:  # constructproperty
    615                     index = u30()
    616                     arg_count = u30()
    617                     args = list(reversed(
    618                         [stack.pop() for _ in range(arg_count)]))
    619                     obj = stack.pop()
    620 
    621                     mname = self.multinames[index]
    622                     assert isinstance(obj, _AVMClass)
    623 
    624                     # We do not actually call the constructor for now;
    625                     # we just pretend it does nothing
    626                     stack.append(obj.make_object())
    627                 elif opcode == 79:  # callpropvoid
    628                     index = u30()
    629                     mname = self.multinames[index]
    630                     arg_count = u30()
    631                     args = list(reversed(
    632                         [stack.pop() for _ in range(arg_count)]))
    633                     obj = stack.pop()
    634                     if isinstance(obj, _AVMClass_Object):
    635                         func = self.extract_function(obj.avm_class, mname)
    636                         res = func(args)
    637                         assert res is undefined
    638                         continue
    639                     if isinstance(obj, _ScopeDict):
    640                         assert mname in obj.avm_class.method_names
    641                         func = self.extract_function(obj.avm_class, mname)
    642                         res = func(args)
    643                         assert res is undefined
    644                         continue
    645                     if mname == 'reverse':
    646                         assert isinstance(obj, list)
    647                         obj.reverse()
    648                     else:
    649                         raise NotImplementedError(
    650                             'Unsupported (void) property %r on %r'
    651                             % (mname, obj))
    652                 elif opcode == 86:  # newarray
    653                     arg_count = u30()
    654                     arr = []
    655                     for i in range(arg_count):
    656                         arr.append(stack.pop())
    657                     arr = arr[::-1]
    658                     stack.append(arr)
    659                 elif opcode == 93:  # findpropstrict
    660                     index = u30()
    661                     mname = self.multinames[index]
    662                     for s in reversed(scopes):
    663                         if mname in s:
    664                             res = s
    665                             break
    666                     else:
    667                         res = scopes[0]
    668                     if mname not in res and mname in _builtin_classes:
    669                         stack.append(_builtin_classes[mname])
    670                     else:
    671                         stack.append(res[mname])
    672                 elif opcode == 94:  # findproperty
    673                     index = u30()
    674                     mname = self.multinames[index]
    675                     for s in reversed(scopes):
    676                         if mname in s:
    677                             res = s
    678                             break
    679                     else:
    680                         res = avm_class.variables
    681                     stack.append(res)
    682                 elif opcode == 96:  # getlex
    683                     index = u30()
    684                     mname = self.multinames[index]
    685                     for s in reversed(scopes):
    686                         if mname in s:
    687                             scope = s
    688                             break
    689                     else:
    690                         scope = avm_class.variables
    691 
    692                     if mname in scope:
    693                         res = scope[mname]
    694                     elif mname in _builtin_classes:
    695                         res = _builtin_classes[mname]
    696                     else:
    697                         # Assume uninitialized
    698                         # TODO warn here
    699                         res = undefined
    700                     stack.append(res)
    701                 elif opcode == 97:  # setproperty
    702                     index = u30()
    703                     value = stack.pop()
    704                     idx = self.multinames[index]
    705                     if isinstance(idx, _Multiname):
    706                         idx = stack.pop()
    707                     obj = stack.pop()
    708                     obj[idx] = value
    709                 elif opcode == 98:  # getlocal
    710                     index = u30()
    711                     stack.append(registers[index])
    712                 elif opcode == 99:  # setlocal
    713                     index = u30()
    714                     value = stack.pop()
    715                     registers[index] = value
    716                 elif opcode == 102:  # getproperty
    717                     index = u30()
    718                     pname = self.multinames[index]
    719                     if pname == 'length':
    720                         obj = stack.pop()
    721                         assert isinstance(obj, (compat_str, list))
    722                         stack.append(len(obj))
    723                     elif isinstance(pname, compat_str):  # Member access
    724                         obj = stack.pop()
    725                         if isinstance(obj, _AVMClass):
    726                             res = obj.static_properties[pname]
    727                             stack.append(res)
    728                             continue
    729 
    730                         assert isinstance(obj, (dict, _ScopeDict)),\
    731                             'Accessing member %r on %r' % (pname, obj)
    732                         res = obj.get(pname, undefined)
    733                         stack.append(res)
    734                     else:  # Assume attribute access
    735                         idx = stack.pop()
    736                         assert isinstance(idx, int)
    737                         obj = stack.pop()
    738                         assert isinstance(obj, list)
    739                         stack.append(obj[idx])
    740                 elif opcode == 104:  # initproperty
    741                     index = u30()
    742                     value = stack.pop()
    743                     idx = self.multinames[index]
    744                     if isinstance(idx, _Multiname):
    745                         idx = stack.pop()
    746                     obj = stack.pop()
    747                     obj[idx] = value
    748                 elif opcode == 115:  # convert_
    749                     value = stack.pop()
    750                     intvalue = int(value)
    751                     stack.append(intvalue)
    752                 elif opcode == 128:  # coerce
    753                     u30()
    754                 elif opcode == 130:  # coerce_a
    755                     value = stack.pop()
    756                     # um, yes, it's any value
    757                     stack.append(value)
    758                 elif opcode == 133:  # coerce_s
    759                     assert isinstance(stack[-1], (type(None), compat_str))
    760                 elif opcode == 147:  # decrement
    761                     value = stack.pop()
    762                     assert isinstance(value, int)
    763                     stack.append(value - 1)
    764                 elif opcode == 149:  # typeof
    765                     value = stack.pop()
    766                     return {
    767                         _Undefined: 'undefined',
    768                         compat_str: 'String',
    769                         int: 'Number',
    770                         float: 'Number',
    771                     }[type(value)]
    772                 elif opcode == 160:  # add
    773                     value2 = stack.pop()
    774                     value1 = stack.pop()
    775                     res = value1 + value2
    776                     stack.append(res)
    777                 elif opcode == 161:  # subtract
    778                     value2 = stack.pop()
    779                     value1 = stack.pop()
    780                     res = value1 - value2
    781                     stack.append(res)
    782                 elif opcode == 162:  # multiply
    783                     value2 = stack.pop()
    784                     value1 = stack.pop()
    785                     res = value1 * value2
    786                     stack.append(res)
    787                 elif opcode == 164:  # modulo
    788                     value2 = stack.pop()
    789                     value1 = stack.pop()
    790                     res = value1 % value2
    791                     stack.append(res)
    792                 elif opcode == 168:  # bitand
    793                     value2 = stack.pop()
    794                     value1 = stack.pop()
    795                     assert isinstance(value1, int)
    796                     assert isinstance(value2, int)
    797                     res = value1 & value2
    798                     stack.append(res)
    799                 elif opcode == 171:  # equals
    800                     value2 = stack.pop()
    801                     value1 = stack.pop()
    802                     result = value1 == value2
    803                     stack.append(result)
    804                 elif opcode == 175:  # greaterequals
    805                     value2 = stack.pop()
    806                     value1 = stack.pop()
    807                     result = value1 >= value2
    808                     stack.append(result)
    809                 elif opcode == 192:  # increment_i
    810                     value = stack.pop()
    811                     assert isinstance(value, int)
    812                     stack.append(value + 1)
    813                 elif opcode == 208:  # getlocal_0
    814                     stack.append(registers[0])
    815                 elif opcode == 209:  # getlocal_1
    816                     stack.append(registers[1])
    817                 elif opcode == 210:  # getlocal_2
    818                     stack.append(registers[2])
    819                 elif opcode == 211:  # getlocal_3
    820                     stack.append(registers[3])
    821                 elif opcode == 212:  # setlocal_0
    822                     registers[0] = stack.pop()
    823                 elif opcode == 213:  # setlocal_1
    824                     registers[1] = stack.pop()
    825                 elif opcode == 214:  # setlocal_2
    826                     registers[2] = stack.pop()
    827                 elif opcode == 215:  # setlocal_3
    828                     registers[3] = stack.pop()
    829                 else:
    830                     raise NotImplementedError(
    831                         'Unsupported opcode %d' % opcode)
    832 
    833         avm_class.method_pyfunctions[func_name] = resfunc
    834         return resfunc