mirror of
https://github.com/ytdl-org/youtube-dl.git
synced 2024-11-21 17:51:51 +00:00
[swfinterp] Extend tests and fix parsing
This commit is contained in:
parent
0cb2056304
commit
e75c24e889
4 changed files with 70 additions and 15 deletions
13
test/swftests/StaticAssignment.as
Normal file
13
test/swftests/StaticAssignment.as
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
16
test/swftests/StaticRetrieval.as
Normal file
16
test/swftests/StaticRetrieval.as
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -23,10 +23,10 @@ class TestSWFInterpreter(unittest.TestCase):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
for testfile in os.listdir(TEST_DIR):
|
def _make_testfunc(testfile):
|
||||||
m = re.match(r'^(.*)\.(as)$', testfile)
|
m = re.match(r'^(.*)\.(as)$', testfile)
|
||||||
if not m:
|
if not m:
|
||||||
continue
|
return
|
||||||
test_id = m.group(1)
|
test_id = m.group(1)
|
||||||
|
|
||||||
def test_func(self):
|
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)):
|
or os.path.getmtime(swf_file) < os.path.getmtime(as_file)):
|
||||||
# Recompile
|
# Recompile
|
||||||
try:
|
try:
|
||||||
subprocess.check_call(['mxmlc', '--output', swf_file, as_file])
|
subprocess.check_call(['mxmlc', '-output', swf_file, as_file])
|
||||||
except OSError as ose:
|
except OSError as ose:
|
||||||
if ose.errno == errno.ENOENT:
|
if ose.errno == errno.ENOENT:
|
||||||
print('mxmlc not found! Skipping test.')
|
print('mxmlc not found! Skipping test.')
|
||||||
|
@ -69,5 +69,8 @@ for testfile in os.listdir(TEST_DIR):
|
||||||
setattr(TestSWFInterpreter, test_func.__name__, test_func)
|
setattr(TestSWFInterpreter, test_func.__name__, test_func)
|
||||||
|
|
||||||
|
|
||||||
|
for testfile in os.listdir(TEST_DIR):
|
||||||
|
_make_testfunc(testfile)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -39,6 +39,16 @@ def _extract_tags(file_contents):
|
||||||
pos += tag_len
|
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):
|
class _AVMClass_Object(object):
|
||||||
def __init__(self, avm_class):
|
def __init__(self, avm_class):
|
||||||
self.avm_class = avm_class
|
self.avm_class = avm_class
|
||||||
|
@ -92,8 +102,8 @@ def _s32(reader):
|
||||||
def _s24(reader):
|
def _s24(reader):
|
||||||
bs = reader.read(3)
|
bs = reader.read(3)
|
||||||
assert len(bs) == 3
|
assert len(bs) == 3
|
||||||
first_byte = b'\xff' if (ord(bs[0:1]) >= 0x80) else b'\x00'
|
last_byte = b'\xff' if (ord(bs[2:3]) >= 0x80) else b'\x00'
|
||||||
return struct.unpack('!i', first_byte + bs)
|
return struct.unpack('<i', bs + last_byte)[0]
|
||||||
|
|
||||||
|
|
||||||
def _read_string(reader):
|
def _read_string(reader):
|
||||||
|
@ -341,8 +351,9 @@ class SWFInterpreter(object):
|
||||||
u30 = lambda: _u30(coder)
|
u30 = lambda: _u30(coder)
|
||||||
|
|
||||||
print('Invoking %s.%s(%r)' % (avm_class.name, func_name, tuple(args)))
|
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 = []
|
stack = []
|
||||||
|
scopes = collections.deque([avm_class.variables])
|
||||||
while True:
|
while True:
|
||||||
opcode = _read_byte(coder)
|
opcode = _read_byte(coder)
|
||||||
print('opcode: %r, stack(%d): %r' % (opcode, len(stack), stack))
|
print('opcode: %r, stack(%d): %r' % (opcode, len(stack), stack))
|
||||||
|
@ -351,6 +362,11 @@ class SWFInterpreter(object):
|
||||||
value = stack.pop()
|
value = stack.pop()
|
||||||
if value:
|
if value:
|
||||||
coder.seek(coder.tell() + offset)
|
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
|
elif opcode == 36: # pushbyte
|
||||||
v = _read_byte(coder)
|
v = _read_byte(coder)
|
||||||
stack.append(v)
|
stack.append(v)
|
||||||
|
@ -361,9 +377,8 @@ class SWFInterpreter(object):
|
||||||
idx = u30()
|
idx = u30()
|
||||||
stack.append(constant_strings[idx])
|
stack.append(constant_strings[idx])
|
||||||
elif opcode == 48: # pushscope
|
elif opcode == 48: # pushscope
|
||||||
# We don't implement the scope register, so we'll just
|
|
||||||
# ignore the popped value
|
|
||||||
new_scope = stack.pop()
|
new_scope = stack.pop()
|
||||||
|
scopes.append(new_scope)
|
||||||
elif opcode == 70: # callproperty
|
elif opcode == 70: # callproperty
|
||||||
index = u30()
|
index = u30()
|
||||||
mname = self.multinames[index]
|
mname = self.multinames[index]
|
||||||
|
@ -435,20 +450,28 @@ class SWFInterpreter(object):
|
||||||
arr.append(stack.pop())
|
arr.append(stack.pop())
|
||||||
arr = arr[::-1]
|
arr = arr[::-1]
|
||||||
stack.append(arr)
|
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
|
elif opcode == 94: # findproperty
|
||||||
index = u30()
|
index = u30()
|
||||||
mname = self.multinames[index]
|
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)
|
stack.append(res)
|
||||||
elif opcode == 96: # getlex
|
elif opcode == 96: # getlex
|
||||||
index = u30()
|
index = u30()
|
||||||
mname = self.multinames[index]
|
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)
|
stack.append(res)
|
||||||
elif opcode == 97: # setproperty
|
elif opcode == 97: # setproperty
|
||||||
index = u30()
|
index = u30()
|
||||||
|
|
Loading…
Reference in a new issue