[swfinterp] Add support for calls to instance methods

This commit is contained in:
Philipp Hagemeister 2014-07-20 12:47:15 +02:00
parent 70f767dc65
commit 01b4b74574
2 changed files with 77 additions and 57 deletions

View File

@ -0,0 +1,17 @@
// input: []
// output: 121
package {
public class ClassCall {
public static function main():int{
var f:OtherClass = new OtherClass();
return f.func(100,20);
}
}
}
class OtherClass {
public function func(x: int, y: int):int {
return x+y+1;
}
}

View File

@ -42,16 +42,6 @@ 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
@ -74,14 +64,6 @@ def __init__(self, avm_class):
super(ScopeDict, self).__init__() super(ScopeDict, self).__init__()
self.avm_class = avm_class self.avm_class = avm_class
def __getitem__(self, k):
print('getting %r' % k)
return super(ScopeDict, self).__getitem__(k)
def __contains__(self, k):
print('contains %r' % k)
return super(ScopeDict, self).__contains__(k)
def __repr__(self): def __repr__(self):
return '%s__Scope(%s)' % ( return '%s__Scope(%s)' % (
self.avm_class.name, self.avm_class.name,
@ -92,6 +74,15 @@ def __repr__(self):
def make_object(self): def make_object(self):
return _AVMClass_Object(self) return _AVMClass_Object(self)
def __repr__(self):
return '_AVMClass(%s)' % (self.name)
def register_methods(self, methods):
self.method_names.update(methods.items())
self.method_idxs.update(dict(
(idx, name)
for name, idx in methods.items()))
def _read_int(reader): def _read_int(reader):
res = 0 res = 0
@ -290,7 +281,11 @@ def parse_traits_info():
classes = [] classes = []
for class_id in range(class_count): for class_id in range(class_count):
name_idx = u30() name_idx = u30()
classes.append(_AVMClass(name_idx, self.multinames[name_idx]))
cname = self.multinames[name_idx]
avm_class = _AVMClass(name_idx, cname)
classes.append(avm_class)
u30() # super_name idx u30() # super_name idx
flags = read_byte() flags = read_byte()
if flags & 0x08 != 0: # Protected namespace is present if flags & 0x08 != 0: # Protected namespace is present
@ -301,7 +296,9 @@ def parse_traits_info():
u30() # iinit u30() # iinit
trait_count = u30() trait_count = u30()
for _c2 in range(trait_count): for _c2 in range(trait_count):
parse_traits_info() trait_methods = parse_traits_info()
avm_class.register_methods(trait_methods)
assert len(classes) == class_count assert len(classes) == class_count
self._classes_by_name = dict((c.name, c) for c in classes) self._classes_by_name = dict((c.name, c) for c in classes)
@ -310,10 +307,7 @@ def parse_traits_info():
trait_count = u30() trait_count = u30()
for _c2 in range(trait_count): for _c2 in range(trait_count):
trait_methods = parse_traits_info() trait_methods = parse_traits_info()
avm_class.method_names.update(trait_methods.items()) avm_class.register_methods(trait_methods)
avm_class.method_idxs.update(dict(
(idx, name)
for name, idx in trait_methods.items()))
# Scripts # Scripts
script_count = u30() script_count = u30()
@ -358,12 +352,14 @@ def extract_class(self, class_name):
raise ExtractorError('Class %r not found' % class_name) raise ExtractorError('Class %r not found' % class_name)
def extract_function(self, avm_class, func_name): def extract_function(self, avm_class, func_name):
print('Extracting %s.%s' % (avm_class.name, func_name))
if func_name in avm_class.method_pyfunctions: if func_name in avm_class.method_pyfunctions:
return avm_class.method_pyfunctions[func_name] return avm_class.method_pyfunctions[func_name]
if func_name in self._classes_by_name: if func_name in self._classes_by_name:
return self._classes_by_name[func_name].make_object() return self._classes_by_name[func_name].make_object()
if func_name not in avm_class.methods: if func_name not in avm_class.methods:
raise ExtractorError('Cannot find function %r' % func_name) raise ExtractorError('Cannot find function %s.%s' % (
avm_class.name, func_name))
m = avm_class.methods[func_name] m = avm_class.methods[func_name]
def resfunc(args): def resfunc(args):
@ -375,7 +371,8 @@ def resfunc(args):
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 = [avm_class.variables] + list(args) + [None] * m.local_count registers = [avm_class.variables] + list(args) + [None] * m.local_count
stack = [] stack = []
scopes = collections.deque([avm_class.variables]) scopes = collections.deque([
self._classes_by_name, 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))
@ -408,33 +405,38 @@ def resfunc(args):
args = list(reversed( args = list(reversed(
[stack.pop() for _ in range(arg_count)])) [stack.pop() for _ in range(arg_count)]))
obj = stack.pop() obj = stack.pop()
if mname == 'split':
assert len(args) == 1 if isinstance(obj, _AVMClass_Object):
assert isinstance(args[0], compat_str) func = self.extract_function(obj.avm_class, mname)
assert isinstance(obj, compat_str) res = func(args)
if args[0] == '':
res = list(obj)
else:
res = obj.split(args[0])
stack.append(res) stack.append(res)
elif mname == 'slice': continue
assert len(args) == 1 elif isinstance(obj, compat_str):
assert isinstance(args[0], int) if mname == 'split':
assert isinstance(obj, list) assert len(args) == 1
res = obj[args[0]:] assert isinstance(args[0], compat_str)
stack.append(res) if args[0] == '':
elif mname == 'join': res = list(obj)
assert len(args) == 1 else:
assert isinstance(args[0], compat_str) res = obj.split(args[0])
assert isinstance(obj, list) stack.append(res)
res = args[0].join(obj) continue
stack.append(res) elif isinstance(obj, list):
elif mname in avm_class.method_pyfunctions: if mname == 'slice':
stack.append(avm_class.method_pyfunctions[mname](args)) assert len(args) == 1
else: assert isinstance(args[0], int)
raise NotImplementedError( res = obj[args[0]:]
'Unsupported property %r on %r' stack.append(res)
% (mname, obj)) continue
elif mname == 'join':
assert len(args) == 1
assert isinstance(args[0], compat_str)
res = args[0].join(obj)
stack.append(res)
continue
raise NotImplementedError(
'Unsupported property %r on %r'
% (mname, obj))
elif opcode == 72: # returnvalue elif opcode == 72: # returnvalue
res = stack.pop() res = stack.pop()
return res return res
@ -446,11 +448,12 @@ def resfunc(args):
obj = stack.pop() obj = stack.pop()
mname = self.multinames[index] mname = self.multinames[index]
assert isinstance(obj, _AVMClass)
construct_method = self.extract_function( construct_method = self.extract_function(
obj.avm_class, mname) obj, mname)
# We do not actually call the constructor for now; # We do not actually call the constructor for now;
# we just pretend it does nothing # we just pretend it does nothing
stack.append(obj) stack.append(obj.make_object())
elif opcode == 79: # callpropvoid elif opcode == 79: # callpropvoid
index = u30() index = u30()
mname = self.multinames[index] mname = self.multinames[index]
@ -481,7 +484,7 @@ def resfunc(args):
break break
else: else:
res = scopes[0] res = scopes[0]
stack.append(res) stack.append(res[mname])
elif opcode == 94: # findproperty elif opcode == 94: # findproperty
index = u30() index = u30()
mname = self.multinames[index] mname = self.multinames[index]
@ -490,7 +493,7 @@ def resfunc(args):
res = s res = s
break break
else: else:
res = scopes[0] res = avm_class.variables
stack.append(res) stack.append(res)
elif opcode == 96: # getlex elif opcode == 96: # getlex
index = u30() index = u30()
@ -500,7 +503,7 @@ def resfunc(args):
scope = s scope = s
break break
else: else:
scope = scopes[0] scope = avm_class.variables
# I cannot find where static variables are initialized # I cannot find where static variables are initialized
# so let's just return None # so let's just return None
res = scope.get(mname) res = scope.get(mname)