[swfinterp] Extend tests and fix parsing
authorPhilipp Hagemeister <phihag@phihag.de>
Sat, 19 Jul 2014 22:03:54 +0000 (00:03 +0200)
committerPhilipp Hagemeister <phihag@phihag.de>
Sat, 19 Jul 2014 22:03:54 +0000 (00:03 +0200)
test/swftests/StaticAssignment.as [new file with mode: 0644]
test/swftests/StaticRetrieval.as [new file with mode: 0644]
test/test_swfinterp.py
youtube_dl/swfinterp.py

diff --git a/test/swftests/StaticAssignment.as b/test/swftests/StaticAssignment.as
new file mode 100644 (file)
index 0000000..b061c21
--- /dev/null
@@ -0,0 +1,13 @@
+// input: [1]
+// output: 1
+
+package {
+public class StaticAssignment {
+       public static var v:int;
+
+    public static function main(a:int):int{
+        v = a;
+        return v;
+    }
+}
+}
diff --git a/test/swftests/StaticRetrieval.as b/test/swftests/StaticRetrieval.as
new file mode 100644 (file)
index 0000000..c8352d8
--- /dev/null
@@ -0,0 +1,16 @@
+// input: []
+// output: 1
+
+package {
+public class StaticRetrieval {
+       public static var v:int;
+
+    public static function main():int{
+        if (v) {
+               return 0;
+        } else {
+               return 1;
+        }
+    }
+}
+}
index 98a14a006e52595156c322205b10b21f9a1cfcc3..3bb5a6308ca03ceb3f6107e6fe3e48256a92a893 100644 (file)
@@ -23,10 +23,10 @@ class TestSWFInterpreter(unittest.TestCase):
     pass
 
 
-for testfile in os.listdir(TEST_DIR):
+def _make_testfunc(testfile):
     m = re.match(r'^(.*)\.(as)$', testfile)
     if not m:
-        continue
+        return
     test_id = m.group(1)
 
     def test_func(self):
@@ -36,7 +36,7 @@ for testfile in os.listdir(TEST_DIR):
                 or os.path.getmtime(swf_file) < os.path.getmtime(as_file)):
             # Recompile
             try:
-                subprocess.check_call(['mxmlc', '--output', swf_file, as_file])
+                subprocess.check_call(['mxmlc', '-output', swf_file, as_file])
             except OSError as ose:
                 if ose.errno == errno.ENOENT:
                     print('mxmlc not found! Skipping test.')
@@ -69,5 +69,8 @@ for testfile in os.listdir(TEST_DIR):
     setattr(TestSWFInterpreter, test_func.__name__, test_func)
 
 
+for testfile in os.listdir(TEST_DIR):
+    _make_testfunc(testfile)
+
 if __name__ == '__main__':
     unittest.main()
index 49fade364ac8963a713b46eccfcb8e9cd175e893..64a518fc61e420578a061d83231f87913f8d2960 100644 (file)
@@ -39,6 +39,16 @@ def _extract_tags(file_contents):
         pos += tag_len
 
 
+class _AVM_Object(object):
+    def __init__(self, value=None, name_hint=None):
+        self.value = value
+        self.name_hint = name_hint
+
+    def __repr__(self):
+        nh = '' if self.name_hint is None else (' %s' % self.name_hint)
+        return 'AVMObject%s(%r)' % (nh, self.value)
+
+
 class _AVMClass_Object(object):
     def __init__(self, avm_class):
         self.avm_class = avm_class
@@ -92,8 +102,8 @@ def _s32(reader):
 def _s24(reader):
     bs = reader.read(3)
     assert len(bs) == 3
-    first_byte = b'\xff' if (ord(bs[0:1]) >= 0x80) else b'\x00'
-    return struct.unpack('!i', first_byte + bs)
+    last_byte = b'\xff' if (ord(bs[2:3]) >= 0x80) else b'\x00'
+    return struct.unpack('<i', bs + last_byte)[0]
 
 
 def _read_string(reader):
@@ -341,8 +351,9 @@ class SWFInterpreter(object):
             u30 = lambda: _u30(coder)
 
             print('Invoking %s.%s(%r)' % (avm_class.name, func_name, tuple(args)))
-            registers = ['(this)'] + list(args) + [None] * m.local_count
+            registers = [avm_class.variables] + list(args) + [None] * m.local_count
             stack = []
+            scopes = collections.deque([avm_class.variables])
             while True:
                 opcode = _read_byte(coder)
                 print('opcode: %r, stack(%d): %r' % (opcode, len(stack), stack))
@@ -351,6 +362,11 @@ class SWFInterpreter(object):
                     value = stack.pop()
                     if value:
                         coder.seek(coder.tell() + offset)
+                elif opcode == 18:  # iffalse
+                    offset = s24()
+                    value = stack.pop()
+                    if not value:
+                        coder.seek(coder.tell() + offset)
                 elif opcode == 36:  # pushbyte
                     v = _read_byte(coder)
                     stack.append(v)
@@ -361,9 +377,8 @@ class SWFInterpreter(object):
                     idx = u30()
                     stack.append(constant_strings[idx])
                 elif opcode == 48:  # pushscope
-                    # We don't implement the scope register, so we'll just
-                    # ignore the popped value
                     new_scope = stack.pop()
+                    scopes.append(new_scope)
                 elif opcode == 70:  # callproperty
                     index = u30()
                     mname = self.multinames[index]
@@ -435,20 +450,28 @@ class SWFInterpreter(object):
                         arr.append(stack.pop())
                     arr = arr[::-1]
                     stack.append(arr)
-                elif opcode == 93:  # findpropstrict
-                    index = u30()
-                    mname = self.multinames[index]
-                    res = self.extract_function(avm_class, mname)
-                    stack.append(res)
                 elif opcode == 94:  # findproperty
                     index = u30()
                     mname = self.multinames[index]
-                    res = avm_class.variables.get(mname)
+                    for s in reversed(scopes):
+                        if mname in s:
+                            res = s
+                            break
+                    else:
+                        res = scopes[0]
                     stack.append(res)
                 elif opcode == 96:  # getlex
                     index = u30()
                     mname = self.multinames[index]
-                    res = avm_class.variables.get(mname, None)
+                    for s in reversed(scopes):
+                        if mname in s:
+                            scope = s
+                            break
+                    else:
+                        scope = scopes[0]
+                    # I cannot find where static variables are initialized
+                    # so let's just return None
+                    res = scope.get(mname)
                     stack.append(res)
                 elif opcode == 97:  # setproperty
                     index = u30()