[swfinterp] Extend tests and fix parsing

This commit is contained in:
Philipp Hagemeister 2014-07-20 00:03:54 +02:00
parent 0cb2056304
commit e75c24e889
4 changed files with 70 additions and 15 deletions

View 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;
}
}
}

View 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;
}
}
}
}

View file

@ -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 @@ def test_func(self):
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 @@ def _find_spec(key):
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()

View file

@ -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 @@ def resfunc(args):
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 @@ def resfunc(args):
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 @@ def resfunc(args):
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 @@ def resfunc(args):
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()