[swfinterp] Start working on basic tests
parent
5425626790
commit
0cb2056304
|
@ -0,0 +1 @@
|
||||||
|
*.swf
|
|
@ -0,0 +1,13 @@
|
||||||
|
// input: [1, 2]
|
||||||
|
// output: 3
|
||||||
|
|
||||||
|
package {
|
||||||
|
public class LocalVars {
|
||||||
|
public static function main(a:int, b:int):int{
|
||||||
|
var c:int = a + b + b;
|
||||||
|
var d:int = c - b;
|
||||||
|
var e:int = d;
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,73 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
# Allow direct execution
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import unittest
|
||||||
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
|
|
||||||
|
|
||||||
|
import io
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
from youtube_dl.swfinterp import SWFInterpreter
|
||||||
|
|
||||||
|
|
||||||
|
TEST_DIR = os.path.join(
|
||||||
|
os.path.dirname(os.path.abspath(__file__)), 'swftests')
|
||||||
|
|
||||||
|
|
||||||
|
class TestSWFInterpreter(unittest.TestCase):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
for testfile in os.listdir(TEST_DIR):
|
||||||
|
m = re.match(r'^(.*)\.(as)$', testfile)
|
||||||
|
if not m:
|
||||||
|
continue
|
||||||
|
test_id = m.group(1)
|
||||||
|
|
||||||
|
def test_func(self):
|
||||||
|
as_file = os.path.join(TEST_DIR, testfile)
|
||||||
|
swf_file = os.path.join(TEST_DIR, test_id + '.swf')
|
||||||
|
if ((not os.path.exists(swf_file))
|
||||||
|
or os.path.getmtime(swf_file) < os.path.getmtime(as_file)):
|
||||||
|
# Recompile
|
||||||
|
try:
|
||||||
|
subprocess.check_call(['mxmlc', '--output', swf_file, as_file])
|
||||||
|
except OSError as ose:
|
||||||
|
if ose.errno == errno.ENOENT:
|
||||||
|
print('mxmlc not found! Skipping test.')
|
||||||
|
return
|
||||||
|
raise
|
||||||
|
|
||||||
|
with open(swf_file, 'rb') as swf_f:
|
||||||
|
swf_content = swf_f.read()
|
||||||
|
swfi = SWFInterpreter(swf_content)
|
||||||
|
|
||||||
|
with io.open(as_file, 'r', encoding='utf-8') as as_f:
|
||||||
|
as_content = as_f.read()
|
||||||
|
|
||||||
|
def _find_spec(key):
|
||||||
|
m = re.search(
|
||||||
|
r'(?m)^//\s*%s:\s*(.*?)\n' % re.escape(key), as_content)
|
||||||
|
if not m:
|
||||||
|
raise ValueError('Cannot find %s in %s' % (key, testfile))
|
||||||
|
return json.loads(m.group(1))
|
||||||
|
|
||||||
|
input_args = _find_spec('input')
|
||||||
|
output = _find_spec('output')
|
||||||
|
|
||||||
|
swf_class = swfi.extract_class(test_id)
|
||||||
|
func = swfi.extract_function(swf_class, 'main')
|
||||||
|
res = func(input_args)
|
||||||
|
self.assertEqual(res, output)
|
||||||
|
|
||||||
|
test_func.__name__ = str('test_swf_' + test_id)
|
||||||
|
setattr(TestSWFInterpreter, test_func.__name__, test_func)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
|
@ -8,8 +8,22 @@ import zlib
|
||||||
from .utils import ExtractorError
|
from .utils import ExtractorError
|
||||||
|
|
||||||
|
|
||||||
def _extract_tags(content):
|
def _extract_tags(file_contents):
|
||||||
pos = 0
|
if file_contents[1:3] != b'WS':
|
||||||
|
raise ExtractorError(
|
||||||
|
'Not an SWF file; header is %r' % file_contents[:3])
|
||||||
|
if file_contents[:1] == b'C':
|
||||||
|
content = zlib.decompress(file_contents[8:])
|
||||||
|
else:
|
||||||
|
raise NotImplementedError(
|
||||||
|
'Unsupported compression format %r' %
|
||||||
|
file_contents[:1])
|
||||||
|
|
||||||
|
# Determine number of bits in framesize rectangle
|
||||||
|
framesize_nbits = struct.unpack('!B', content[:1])[0] >> 3
|
||||||
|
framesize_len = (5 + 4 * framesize_nbits + 7) // 8
|
||||||
|
|
||||||
|
pos = framesize_len + 2 + 2
|
||||||
while pos < len(content):
|
while pos < len(content):
|
||||||
header16 = struct.unpack('<H', content[pos:pos + 2])[0]
|
header16 = struct.unpack('<H', content[pos:pos + 2])[0]
|
||||||
pos += 2
|
pos += 2
|
||||||
|
@ -18,7 +32,9 @@ def _extract_tags(content):
|
||||||
if tag_len == 0x3f:
|
if tag_len == 0x3f:
|
||||||
tag_len = struct.unpack('<I', content[pos:pos + 4])[0]
|
tag_len = struct.unpack('<I', content[pos:pos + 4])[0]
|
||||||
pos += 4
|
pos += 4
|
||||||
assert pos + tag_len <= len(content)
|
assert pos + tag_len <= len(content), \
|
||||||
|
('Tag %d ends at %d+%d - that\'s longer than the file (%d)'
|
||||||
|
% (tag_code, pos, tag_len, len(content)))
|
||||||
yield (tag_code, content[pos:pos + tag_len])
|
yield (tag_code, content[pos:pos + tag_len])
|
||||||
pos += tag_len
|
pos += tag_len
|
||||||
|
|
||||||
|
@ -88,8 +104,7 @@ def _read_string(reader):
|
||||||
|
|
||||||
|
|
||||||
def _read_bytes(count, reader):
|
def _read_bytes(count, reader):
|
||||||
if reader is None:
|
assert count >= 0
|
||||||
reader = code_reader
|
|
||||||
resb = reader.read(count)
|
resb = reader.read(count)
|
||||||
assert len(resb) == count
|
assert len(resb) == count
|
||||||
return resb
|
return resb
|
||||||
|
@ -103,18 +118,8 @@ def _read_byte(reader):
|
||||||
|
|
||||||
class SWFInterpreter(object):
|
class SWFInterpreter(object):
|
||||||
def __init__(self, file_contents):
|
def __init__(self, file_contents):
|
||||||
if file_contents[1:3] != b'WS':
|
|
||||||
raise ExtractorError(
|
|
||||||
'Not an SWF file; header is %r' % file_contents[:3])
|
|
||||||
if file_contents[:1] == b'C':
|
|
||||||
content = zlib.decompress(file_contents[8:])
|
|
||||||
else:
|
|
||||||
raise NotImplementedError(
|
|
||||||
'Unsupported compression format %r' %
|
|
||||||
file_contents[:1])
|
|
||||||
|
|
||||||
code_tag = next(tag
|
code_tag = next(tag
|
||||||
for tag_code, tag in _extract_tags(content)
|
for tag_code, tag in _extract_tags(file_contents)
|
||||||
if tag_code == 82)
|
if tag_code == 82)
|
||||||
p = code_tag.index(b'\0', 4) + 1
|
p = code_tag.index(b'\0', 4) + 1
|
||||||
code_reader = io.BytesIO(code_tag[p:])
|
code_reader = io.BytesIO(code_tag[p:])
|
||||||
|
@ -139,7 +144,7 @@ class SWFInterpreter(object):
|
||||||
for _c in range(1, uint_count):
|
for _c in range(1, uint_count):
|
||||||
u32()
|
u32()
|
||||||
double_count = u30()
|
double_count = u30()
|
||||||
read_bytes((double_count - 1) * 8)
|
read_bytes(max(0, (double_count - 1)) * 8)
|
||||||
string_count = u30()
|
string_count = u30()
|
||||||
constant_strings = ['']
|
constant_strings = ['']
|
||||||
for _c in range(1, string_count):
|
for _c in range(1, string_count):
|
||||||
|
@ -349,6 +354,9 @@ class SWFInterpreter(object):
|
||||||
elif opcode == 36: # pushbyte
|
elif opcode == 36: # pushbyte
|
||||||
v = _read_byte(coder)
|
v = _read_byte(coder)
|
||||||
stack.append(v)
|
stack.append(v)
|
||||||
|
elif opcode == 42: # dup
|
||||||
|
value = stack[-1]
|
||||||
|
stack.append(value)
|
||||||
elif opcode == 44: # pushstring
|
elif opcode == 44: # pushstring
|
||||||
idx = u30()
|
idx = u30()
|
||||||
stack.append(constant_strings[idx])
|
stack.append(constant_strings[idx])
|
||||||
|
@ -468,10 +476,24 @@ class SWFInterpreter(object):
|
||||||
obj = stack.pop()
|
obj = stack.pop()
|
||||||
assert isinstance(obj, list)
|
assert isinstance(obj, list)
|
||||||
stack.append(obj[idx])
|
stack.append(obj[idx])
|
||||||
|
elif opcode == 115: # convert_
|
||||||
|
value = stack.pop()
|
||||||
|
intvalue = int(value)
|
||||||
|
stack.append(intvalue)
|
||||||
elif opcode == 128: # coerce
|
elif opcode == 128: # coerce
|
||||||
u30()
|
u30()
|
||||||
elif opcode == 133: # coerce_s
|
elif opcode == 133: # coerce_s
|
||||||
assert isinstance(stack[-1], (type(None), compat_str))
|
assert isinstance(stack[-1], (type(None), compat_str))
|
||||||
|
elif opcode == 160: # add
|
||||||
|
value2 = stack.pop()
|
||||||
|
value1 = stack.pop()
|
||||||
|
res = value1 + value2
|
||||||
|
stack.append(res)
|
||||||
|
elif opcode == 161: # subtract
|
||||||
|
value2 = stack.pop()
|
||||||
|
value1 = stack.pop()
|
||||||
|
res = value1 - value2
|
||||||
|
stack.append(res)
|
||||||
elif opcode == 164: # modulo
|
elif opcode == 164: # modulo
|
||||||
value2 = stack.pop()
|
value2 = stack.pop()
|
||||||
value1 = stack.pop()
|
value1 = stack.pop()
|
||||||
|
|
Loading…
Reference in New Issue