diff --git a/test/swftests/ConstArrayAccess.as b/test/swftests/ConstArrayAccess.as new file mode 100644 index 000000000..07dc3f460 --- /dev/null +++ b/test/swftests/ConstArrayAccess.as @@ -0,0 +1,18 @@ +// input: [] +// output: 4 + +package { +public class ConstArrayAccess { + private static const x:int = 2; + private static const ar:Array = ["42", "3411"]; + + public static function main():int{ + var c:ConstArrayAccess = new ConstArrayAccess(); + return c.f(); + } + + public function f(): int { + return ar[1].length; + } +} +} diff --git a/youtube_dl/swfinterp.py b/youtube_dl/swfinterp.py index e9bf178e4..6f3c3bf95 100644 --- a/youtube_dl/swfinterp.py +++ b/youtube_dl/swfinterp.py @@ -62,13 +62,14 @@ class _ScopeDict(dict): class _AVMClass(object): - def __init__(self, name_idx, name): + def __init__(self, name_idx, name, static_properties=None): self.name_idx = name_idx self.name = name self.method_names = {} self.method_idxs = {} self.methods = {} self.method_pyfunctions = {} + self.static_properties = static_properties if static_properties else {} self.variables = _ScopeDict(self) self.constants = {} @@ -151,15 +152,20 @@ def _read_byte(reader): StringClass = _AVMClass('(no name idx)', 'String') ByteArrayClass = _AVMClass('(no name idx)', 'ByteArray') +TimerClass = _AVMClass('(no name idx)', 'Timer') +TimerEventClass = _AVMClass('(no name idx)', 'TimerEvent', {'TIMER': 'timer'}) _builtin_classes = { StringClass.name: StringClass, ByteArrayClass.name: ByteArrayClass, + TimerClass.name: TimerClass, + TimerEventClass.name: TimerEventClass, } class _Undefined(object): - def __boolean__(self): + def __bool__(self): return False + __nonzero__ = __bool__ def __hash__(self): return 0 @@ -169,7 +175,9 @@ undefined = _Undefined() class SWFInterpreter(object): def __init__(self, file_contents): - self._patched_functions = {} + self._patched_functions = { + (TimerClass, 'addEventListener'): lambda params: undefined, + } code_tag = next(tag for tag_code, tag in _extract_tags(file_contents) if tag_code == 82) @@ -346,9 +354,10 @@ class SWFInterpreter(object): u30() # iinit trait_count = u30() for _c2 in range(trait_count): - trait_methods, constants = parse_traits_info() + trait_methods, trait_constants = parse_traits_info() avm_class.register_methods(trait_methods) - assert constants is None + if trait_constants: + avm_class.constants.update(trait_constants) assert len(classes) == class_count self._classes_by_name = dict((c.name, c) for c in classes) @@ -439,7 +448,7 @@ class SWFInterpreter(object): registers = [avm_class.variables] + list(args) + [None] * m.local_count stack = [] scopes = collections.deque([ - self._classes_by_name, avm_class.variables]) + self._classes_by_name, avm_class.constants, avm_class.variables]) while True: opcode = _read_byte(coder) if opcode == 9: # label @@ -587,6 +596,12 @@ class SWFInterpreter(object): elif opcode == 72: # returnvalue res = stack.pop() return res + elif opcode == 73: # constructsuper + # Not yet implemented, just hope it works without it + arg_count = u30() + args = list(reversed( + [stack.pop() for _ in range(arg_count)])) + obj = stack.pop() elif opcode == 74: # constructproperty index = u30() arg_count = u30() @@ -667,8 +682,11 @@ class SWFInterpreter(object): if mname in scope: res = scope[mname] + elif mname in _builtin_classes: + res = _builtin_classes[mname] else: - res = avm_class.constants[mname] + # Assume unitialized + res = undefined stack.append(res) elif opcode == 97: # setproperty index = u30() @@ -694,7 +712,12 @@ class SWFInterpreter(object): stack.append(len(obj)) elif isinstance(pname, compat_str): # Member access obj = stack.pop() - assert isinstance(obj, (dict, _ScopeDict)), \ + if isinstance(obj, _AVMClass): + res = obj.static_properties[pname] + stack.append(res) + continue + + assert isinstance(obj, (dict, _ScopeDict)),\ 'Accessing member %r on %r' % (pname, obj) res = obj.get(pname, undefined) stack.append(res)