(* * This file is part of SwfLib * Copyright (c)2004-2008 Nicolas Cannasse * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA *) open Extlib_leftovers open As3 open As3hl type parse_ctx = { as3 : as3_tag; mutable namespaces : hl_namespace array; mutable nsets : hl_ns_set array; mutable names : hl_name array; mutable methods : hl_method array; mutable classes : hl_class array; mutable jumps : (int * int) list; mutable pos : int; delta_mt : int; delta_cl : int; } let get = As3parse.iget let no_nz = As3parse.no_nz let idx n = As3parse.index_int n - 1 let ident ctx i = get ctx.as3.as3_idents i let name ctx n = ctx.names.(idx n) let method_type ctx n = ctx.methods.(idx (no_nz n)) let getclass ctx n = ctx.classes.(idx (no_nz n)) let opt f ctx = function | None -> None | Some x -> Some (f ctx x) let stack_delta = function | HBreakPoint -> 0 | HNop -> 0 | HThrow -> -1 | HGetSuper _ -> 0 | HSetSuper _ -> -2 | HDxNs _ -> 0 | HDxNsLate -> -1 | HRegKill _ -> 0 | HLabel -> 0 | HJump (cond,_) -> (match cond with | J3Always -> 0 | J3True | J3False -> -1 | _ -> -2) | HSwitch _ -> -1 | HPushWith -> -1 | HPopScope -> 0 | HForIn -> -1 | HHasNext -> -1 | HNull | HUndefined -> 1 | HForEach -> -1 | HSmallInt _ | HInt _ | HTrue | HFalse | HString _ | HIntRef _ | HUIntRef _ | HFunction _ | HFloat _ | HNaN -> 1 | HPop -> -1 | HDup -> 1 | HSwap -> 0 | HScope -> -1 | HNamespace _ -> 1 | HNext _ -> 1 | HCallStack n -> -(n + 1) | HConstruct n -> -n | HCallMethod (_,n) -> -n | HCallStatic (_,n) -> -n | HCallSuper (_,n) -> -n | HCallProperty (_,n) -> -n | HRetVoid -> 0 | HRet -> -1 | HConstructSuper n -> -(n + 1) | HConstructProperty (_,n) -> -n | HCallPropLex (_,n) -> -n | HCallSuperVoid (_,n) -> -(n + 1) | HCallPropVoid (_,n) -> -(n + 1) | HApplyType n -> -n | HObject n -> -(n * 2) + 1 | HArray n -> -n + 1 | HNewBlock -> 1 | HClassDef _ -> 0 | HGetDescendants _ -> 0 | HCatch _ -> 1 | HFindPropStrict _ -> 1 | HFindProp _ -> 1 | HFindDefinition _ -> 1 | HGetLex _ -> 1 | HSetProp _ -> -2 | HReg _ -> 1 | HSetReg _ | HSetThis -> -1 | HGetGlobalScope | HGetScope _ -> 1 | HGetProp _ -> 0 | HInitProp _ -> -2 | HDeleteProp _ -> -1 (* true/false *) | HGetSlot _ -> 0 | HSetSlot _ -> -2 | HToString | HToXml | HToXmlAttr | HToInt | HToUInt | HToNumber | HToObject | HAsAny | HAsType _ | HIsType _ | HAsObject | HAsString | HToBool -> 0 | HCheckIsXml -> 0 | HCast _ -> 0 | HTypeof -> 0 | HInstanceOf -> -1 | HIncrReg _ | HDecrReg _ | HIncrIReg _ | HDecrIReg _ -> 0 | HThis -> 1 | HDebugReg _ | HDebugLine _ | HBreakPointLine _ | HTimestamp | HDebugFile _ -> 0 | HOp op -> (match op with | A3ONeg | A3OINeg | A3OIncr | A3ODecr | A3ONot | A3OBitNot | A3OIIncr | A3OIDecr -> 0 | A3OMemGet8 | A3OMemGet16 | A3OMemGet32 | A3OMemGetFloat | A3OMemGetDouble | A3OSign1 | A3OSign8 | A3OSign16 -> 0 | A3OMemSet8 | A3OMemSet16 | A3OMemSet32 | A3OMemSetFloat | A3OMemSetDouble -> -2 | _ -> -1) | HUnk _ -> assert false let parse_opcode ctx i = function | A3BreakPoint -> HBreakPoint | A3Nop -> HNop | A3Throw -> HThrow | A3GetSuper n -> HGetSuper (name ctx n) | A3SetSuper n -> HSetSuper (name ctx n) | A3DxNs s -> HDxNs (ident ctx s) | A3DxNsLate -> HDxNsLate | A3RegKill r -> HRegKill r | A3Label -> HLabel | A3Jump (j,n) -> ctx.jumps <- (i,ctx.pos) :: ctx.jumps; HJump (j,n) | A3Switch (n,infos) as op -> ctx.jumps <- (i,ctx.pos - As3code.length op) :: ctx.jumps; HSwitch(n,infos) | A3PushWith -> HPushWith | A3PopScope -> HPopScope | A3ForIn -> HForIn | A3HasNext -> HHasNext | A3Null -> HNull | A3Undefined -> HUndefined | A3ForEach -> HForEach | A3SmallInt n -> HSmallInt n | A3Int n -> HInt n | A3True -> HTrue | A3False -> HFalse | A3NaN -> HNaN | A3Pop -> HPop | A3Dup -> HDup | A3Swap -> HSwap | A3String i -> HString (ident ctx i) | A3IntRef i -> HIntRef (get ctx.as3.as3_ints i) | A3UIntRef i -> HUIntRef (get ctx.as3.as3_uints i) | A3Float f -> HFloat (get ctx.as3.as3_floats f) | A3Scope -> HScope | A3Namespace n -> HNamespace ctx.namespaces.(idx n) | A3Next (r1,r2) -> HNext (r1,r2) | A3Function f -> HFunction (method_type ctx f) | A3CallStack n -> HCallStack n | A3Construct n -> HConstruct n | A3CallMethod (s,n) -> HCallMethod (s,n) | A3CallStatic (m,n) -> HCallStatic (ctx.methods.(idx m),n) | A3CallSuper (p,n) -> HCallSuper (name ctx p,n) | A3CallProperty (p,n) -> HCallProperty (name ctx p,n) | A3RetVoid -> HRetVoid | A3Ret -> HRet | A3ConstructSuper n -> HConstructSuper n | A3ConstructProperty (p,n) -> HConstructProperty (name ctx p,n) | A3CallPropLex (p,n) -> HCallPropLex (name ctx p,n) | A3CallSuperVoid (p,n) -> HCallSuperVoid (name ctx p,n) | A3CallPropVoid (p,n) -> HCallPropVoid (name ctx p,n) | A3ApplyType n -> HApplyType n | A3Object n -> HObject n | A3Array n -> HArray n | A3NewBlock -> HNewBlock | A3ClassDef n -> HClassDef (getclass ctx n) | A3GetDescendants p -> HGetDescendants (name ctx p) | A3Catch n -> HCatch n | A3FindPropStrict p -> HFindPropStrict (name ctx p) | A3FindProp p -> HFindProp (name ctx p) | A3FindDefinition p -> HFindDefinition (name ctx p) | A3GetLex p -> HGetLex (name ctx p) | A3SetProp p -> HSetProp (name ctx p) | A3Reg r -> HReg r | A3SetReg r -> HSetReg r | A3GetGlobalScope -> HGetGlobalScope | A3GetScope n -> HGetScope n | A3GetProp p -> HGetProp (name ctx p) | A3InitProp p -> HInitProp (name ctx p) | A3DeleteProp p -> HDeleteProp (name ctx p) | A3GetSlot n -> HGetSlot n | A3SetSlot n -> HSetSlot n | A3ToString -> HToString | A3ToXml -> HToXml | A3ToXmlAttr -> HToXmlAttr | A3ToInt -> HToInt | A3ToUInt -> HToUInt | A3ToNumber -> HToNumber | A3ToBool -> HToBool | A3ToObject -> HToObject | A3CheckIsXml -> HCheckIsXml | A3Cast p -> HCast (name ctx p) | A3AsAny -> HAsAny | A3AsString -> HAsString | A3AsType p -> HAsType (name ctx p) | A3AsObject -> HAsObject | A3IncrReg r -> HIncrReg r | A3DecrReg r -> HDecrReg r | A3Typeof -> HTypeof | A3InstanceOf -> HInstanceOf | A3IsType p -> HIsType (name ctx p) | A3IncrIReg r -> HIncrIReg r | A3DecrIReg r -> HDecrIReg r | A3This -> HThis | A3SetThis -> HSetThis | A3DebugReg (id,r,n) -> HDebugReg (ident ctx id,r,n) | A3DebugLine n -> HDebugLine n | A3DebugFile p -> HDebugFile (ident ctx p) | A3BreakPointLine n -> HBreakPointLine n | A3Timestamp -> HTimestamp | A3Op op -> HOp op | A3Unk n -> HUnk n let parse_code ctx f trys = let code = f.fun3_code in let old = ctx.pos , ctx.jumps in let indexes = MultiArray.create() in ctx.pos <- 0; ctx.jumps <- []; let codepos pos delta = let id = (try MultiArray.get indexes (pos + delta) with _ -> -1) in if id = -1 then begin (*Printf.eprintf "MISALIGNED JUMP AT %d %c %d IN #%d\n" pos (if delta < 0 then '-' else '+') (if delta < 0 then -delta else delta) (idx (no_nz f.fun3_id));*) MultiArray.get indexes pos; (* jump 0 *) end else id in let hcode = MultiArray.mapi (fun i op -> let len = As3code.length op in MultiArray.add indexes i; for k = 2 to len do MultiArray.add indexes (-1); done; ctx.pos <- ctx.pos + len; parse_opcode ctx i op ) code in (* in case we have a dead-jump at the end of code *) MultiArray.add indexes (MultiArray.length code); (* patch jumps *) List.iter (fun (j,pos) -> MultiArray.set hcode j (match MultiArray.get hcode j with | HJump (jc,n) -> HJump (jc,codepos pos n - j) | HSwitch (n,infos) -> HSwitch (codepos pos n - j, List.map (fun n -> codepos pos n - j) infos) | _ -> assert false) ) ctx.jumps; (* patch try/catches *) Array.iteri (fun i t -> Array.set trys i { hltc_start = codepos 0 t.hltc_start; hltc_end = codepos 0 t.hltc_end; hltc_handle = codepos 0 t.hltc_handle; hltc_type = t.hltc_type; hltc_name = t.hltc_name; } ) trys; ctx.pos <- fst old; ctx.jumps <- snd old; hcode let parse_metadata ctx m = { hlmeta_name = ident ctx m.meta3_name; hlmeta_data = Array.map (fun (i1,i2) -> opt ident ctx i1, ident ctx i2) m.meta3_data; } let parse_method ctx m = { hlm_type = method_type ctx m.m3_type; hlm_final = m.m3_final; hlm_override = m.m3_override; hlm_kind = m.m3_kind; } let parse_value ctx = function | A3VNone -> HVNone | A3VNull -> HVNull | A3VBool b -> HVBool b | A3VString s -> HVString (ident ctx s) | A3VInt i -> HVInt (get ctx.as3.as3_ints i) | A3VUInt i -> HVUInt (get ctx.as3.as3_uints i) | A3VFloat f -> HVFloat (get ctx.as3.as3_floats f) | A3VNamespace (n,ns) -> HVNamespace (n,ctx.namespaces.(idx ns)) let parse_var ctx v = { hlv_type = opt name ctx v.v3_type; hlv_value = parse_value ctx v.v3_value; hlv_const = v.v3_const; } let parse_field_kind ctx = function | A3FMethod m -> HFMethod (parse_method ctx m) | A3FVar v -> HFVar (parse_var ctx v) | A3FFunction f -> HFFunction (method_type ctx f) | A3FClass c -> HFClass (getclass ctx c) let parse_field ctx f = { hlf_name = name ctx f.f3_name; hlf_slot = f.f3_slot; hlf_kind = parse_field_kind ctx f.f3_kind; hlf_metas = match f.f3_metas with | None -> None | Some a -> Some (Array.map (fun i -> parse_metadata ctx (get ctx.as3.as3_metadatas (no_nz i)) ) a); } let parse_static ctx s = { hls_method = method_type ctx s.st3_method; hls_fields = Array.map (parse_field ctx) s.st3_fields; } let parse_namespace ctx = function | A3NPrivate id -> HNPrivate (opt ident ctx id) | A3NPublic id -> HNPublic (opt ident ctx id) | A3NInternal id -> HNInternal (opt ident ctx id) | A3NProtected id -> HNProtected (ident ctx id) | A3NNamespace id -> HNNamespace (ident ctx id) | A3NExplicit id -> HNExplicit (ident ctx id) | A3NStaticProtected id -> HNStaticProtected (opt ident ctx id) let parse_nset ctx l = List.map (fun n -> ctx.namespaces.(idx n)) l let rec parse_name names ctx = function | A3MName (id,ns) -> (match ctx.namespaces.(idx ns) with | HNPublic p -> let pack = (match p with None -> [] | Some i -> ExtString.String.nsplit i ".") in HMPath (pack, ident ctx id) | ns -> HMName (ident ctx id, ns)) | A3MNSAny (id) -> HMNSAny(ident ctx id) | A3MAny -> HMAny | A3MMultiName (id,ns) -> HMMultiName (opt ident ctx id,ctx.nsets.(idx ns)) | A3MRuntimeName id -> HMRuntimeName (ident ctx id) | A3MRuntimeNameLate -> HMRuntimeNameLate | A3MMultiNameLate ns -> HMMultiNameLate ctx.nsets.(idx ns) | A3MAttrib multi -> HMAttrib (parse_name names ctx multi) | A3MParams (id,pl) -> HMParams (parse_name names ctx names.(idx id),List.map (fun id -> if idx id = -1 then HMAny else parse_name names ctx names.(idx id)) pl) let parse_try_catch ctx t = { hltc_start = t.tc3_start; hltc_end = t.tc3_end; hltc_handle = t.tc3_handle; hltc_type = opt name ctx t.tc3_type; hltc_name = opt name ctx t.tc3_name; } let parse_function ctx f = { hlf_stack_size = f.fun3_stack_size; hlf_nregs = f.fun3_nregs; hlf_init_scope = f.fun3_init_scope; hlf_max_scope = f.fun3_max_scope; hlf_code = MultiArray.create(); (* keep for later *) hlf_trys = Array.map (parse_try_catch ctx) f.fun3_trys; hlf_locals = Array.map (fun f -> if f.f3_metas <> None then assert false; match f.f3_kind with | A3FVar v -> (* v3_value can be <> None if it's a fun parameter with a default value - which looks like a bug of the AS3 compiler *) name ctx f.f3_name , opt name ctx v.v3_type , f.f3_slot, v.v3_const | _ -> assert false ) f.fun3_locals; } let parse_method_type ctx idx f = let m = ctx.as3.as3_method_types.(idx) in { hlmt_index = idx + ctx.delta_mt; hlmt_ret = opt name ctx m.mt3_ret; hlmt_args = List.map (opt name ctx) m.mt3_args; hlmt_native = m.mt3_native; hlmt_var_args = m.mt3_var_args; hlmt_arguments_defined = m.mt3_arguments_defined; hlmt_uses_dxns = m.mt3_uses_dxns; hlmt_new_block = m.mt3_new_block; hlmt_unused_flag = m.mt3_unused_flag; hlmt_debug_name = opt ident ctx m.mt3_debug_name; hlmt_dparams = opt (fun ctx -> List.map (parse_value ctx)) ctx m.mt3_dparams; hlmt_pnames = opt (fun ctx -> List.map (opt ident ctx)) ctx m.mt3_pnames; hlmt_function = opt parse_function ctx f; } let parse_class ctx c s index = { hlc_index = index + ctx.delta_cl; hlc_name = name ctx c.cl3_name; hlc_super = opt name ctx c.cl3_super; hlc_sealed = c.cl3_sealed; hlc_final = c.cl3_final; hlc_interface = c.cl3_interface; hlc_namespace = opt (fun ctx i -> ctx.namespaces.(idx i)) ctx c.cl3_namespace; hlc_implements = Array.map (name ctx) c.cl3_implements; hlc_construct = method_type ctx c.cl3_construct; hlc_fields = Array.map (parse_field ctx) c.cl3_fields; hlc_static_construct = method_type ctx s.st3_method; hlc_static_fields = Array.map (parse_field ctx) s.st3_fields; } let parse_static ctx s = { hls_method = method_type ctx s.st3_method; hls_fields = Array.map (parse_field ctx) s.st3_fields; } let parse ?(delta_mt=0) ?(delta_cl=0) t = let ctx = { as3 = t; namespaces = [||]; nsets = [||]; names = [||]; methods = [||]; classes = [||]; jumps = []; pos = 0; delta_mt = delta_mt; delta_cl = delta_cl; } in ctx.namespaces <- Array.map (parse_namespace ctx) t.as3_namespaces; ctx.nsets <- Array.map (parse_nset ctx) t.as3_nsets; ctx.names <- Array.map (parse_name t.as3_names ctx) t.as3_names; let hfunctions = Hashtbl.create 0 in Array.iter (fun f -> Hashtbl.add hfunctions (idx (no_nz f.fun3_id)) f) t.as3_functions; ctx.methods <- Array.mapi (fun i m -> parse_method_type ctx i (try Some (Hashtbl.find hfunctions i) with Not_found -> None); ) t.as3_method_types; ctx.classes <- Array.mapi (fun i c -> parse_class ctx c t.as3_statics.(i) i ) t.as3_classes; let inits = List.map (parse_static ctx) (Array.to_list t.as3_inits) in Array.iter (fun f -> match (method_type ctx f.fun3_id).hlmt_function with | None -> assert false | Some fl -> fl.hlf_code <- parse_code ctx f fl.hlf_trys ) t.as3_functions; inits (* ************************************************************************ *) (* FLATTEN *) (* ************************************************************************ *) type ('hl,'item) lookup = { h : ('hl,int) Hashtbl.t; a : 'item DynArray.t; f : flatten_ctx -> 'hl -> 'item; } and ('hl,'item) index_lookup = { ordered_list : 'hl list; ordered_array : 'item option DynArray.t; map_f : flatten_ctx -> 'hl -> 'item; } and flatten_ctx = { fints : (hl_int,as3_int) lookup; fuints : (hl_uint,as3_uint) lookup; ffloats : (hl_float,as3_float) lookup; fidents : (hl_ident,as3_ident) lookup; fnamespaces : (hl_namespace,as3_namespace) lookup; fnsets : (hl_ns_set,as3_ns_set) lookup; fnames : (hl_name,as3_multi_name) lookup; fmetas : (hl_metadata,as3_metadata) lookup; fmethods : (hl_method,as3_method_type) index_lookup; fclasses : (hl_class,as3_class * as3_static) index_lookup; mutable ffunctions : as3_function list; mutable fjumps : int list; } let new_lookup f = { h = Hashtbl.create 0; a = DynArray.create(); f = f; } let new_index_lookup l f = { ordered_list = l; ordered_array = DynArray.init (List.length l) (fun _ -> None); map_f = f; } let lookup_array l = DynArray.to_array l.a let lookup_index_array l = Array.map (function None -> assert false | Some x -> x) (DynArray.to_array l.ordered_array) let lookup ctx (l:('a,'b) lookup) item : 'b index = let idx = try Hashtbl.find l.h item with Not_found -> let idx = DynArray.length l.a in (* set dummy value for recursion *) DynArray.add l.a (Obj.magic 0); Hashtbl.add l.h item (idx + 1); DynArray.set l.a idx (l.f ctx item); idx + 1 in As3parse.magic_index idx let lookup_index_nz ctx (l:('a,'b) index_lookup) item : 'c index_nz = let rec loop n = function | [] -> assert false | x :: l -> if x == item then n else loop (n + 1) l in let idx = loop 0 l.ordered_list in if DynArray.get l.ordered_array idx = None then begin (* set dummy value for recursion *) DynArray.set l.ordered_array idx (Some (Obj.magic 0)); DynArray.set l.ordered_array idx (Some (l.map_f ctx item)); end; As3parse.magic_index_nz idx let lookup_nz ctx l item = As3parse.magic_index_nz (As3parse.index_int (lookup ctx l item) - 1) let lookup_ident ctx i = lookup ctx ctx.fidents i let lookup_name ctx n = lookup ctx ctx.fnames n let lookup_method ctx m : as3_method_type index_nz = lookup_index_nz ctx ctx.fmethods m let lookup_class ctx c : as3_class index_nz = lookup_index_nz ctx ctx.fclasses c let flatten_namespace ctx = function | HNPrivate i -> A3NPrivate (opt lookup_ident ctx i) | HNPublic i -> A3NPublic (opt lookup_ident ctx i) | HNInternal i -> A3NInternal (opt lookup_ident ctx i) | HNProtected i -> A3NProtected (lookup_ident ctx i) | HNNamespace i -> A3NNamespace (lookup_ident ctx i) | HNExplicit i -> A3NExplicit (lookup_ident ctx i) | HNStaticProtected i -> A3NStaticProtected (opt lookup_ident ctx i) let flatten_ns_set ctx n = List.map (lookup ctx ctx.fnamespaces) n let rec flatten_name ctx = function | HMPath (pack,i) -> let ns = HNPublic (match pack with [] -> None | l -> Some (String.concat "." l)) in A3MName (lookup_ident ctx i,lookup ctx ctx.fnamespaces ns) | HMName (i,n) -> A3MName (lookup_ident ctx i,lookup ctx ctx.fnamespaces n) | HMNSAny (i) -> A3MNSAny (lookup_ident ctx i) | HMAny -> A3MAny | HMMultiName (i,ns) -> A3MMultiName (opt lookup_ident ctx i,lookup ctx ctx.fnsets ns) | HMRuntimeName i -> A3MRuntimeName (lookup_ident ctx i) | HMRuntimeNameLate -> A3MRuntimeNameLate | HMMultiNameLate ns -> A3MMultiNameLate (lookup ctx ctx.fnsets ns) | HMAttrib n -> A3MAttrib (flatten_name ctx n) | HMParams (i,nl) -> A3MParams (lookup_name ctx i,List.map (lookup_name ctx) nl) let flatten_meta ctx m = { meta3_name = lookup_ident ctx m.hlmeta_name; meta3_data = Array.map (fun (i,i2) -> opt lookup_ident ctx i, lookup_ident ctx i2) m.hlmeta_data; } let flatten_value ctx = function | HVNone -> A3VNone | HVNull -> A3VNull | HVBool b -> A3VBool b | HVString s -> A3VString (lookup_ident ctx s) | HVInt i -> A3VInt (lookup ctx ctx.fints i) | HVUInt i -> A3VUInt (lookup ctx ctx.fuints i) | HVFloat f -> A3VFloat (lookup ctx ctx.ffloats f) | HVNamespace (n,ns) -> A3VNamespace (n,lookup ctx ctx.fnamespaces ns) let flatten_field ctx f = { f3_name = lookup_name ctx f.hlf_name; f3_slot = f.hlf_slot; f3_kind = (match f.hlf_kind with | HFMethod m -> A3FMethod { m3_type = lookup_method ctx m.hlm_type; m3_final = m.hlm_final; m3_override = m.hlm_override; m3_kind = m.hlm_kind; } | HFVar v -> A3FVar { v3_type = opt lookup_name ctx v.hlv_type; v3_value = flatten_value ctx v.hlv_value; v3_const = v.hlv_const; } | HFFunction f -> A3FFunction (lookup_method ctx f) | HFClass c -> A3FClass (lookup_class ctx c) ); f3_metas = opt (fun ctx -> Array.map (fun m -> lookup_nz ctx ctx.fmetas m)) ctx f.hlf_metas; } let flatten_class ctx c = { cl3_name = lookup_name ctx c.hlc_name; cl3_super = opt lookup_name ctx c.hlc_super; cl3_sealed = c.hlc_sealed; cl3_final = c.hlc_final; cl3_interface = c.hlc_interface; cl3_namespace = opt (fun ctx -> lookup ctx ctx.fnamespaces) ctx c.hlc_namespace; cl3_implements = Array.map (lookup_name ctx) c.hlc_implements; cl3_construct = lookup_method ctx c.hlc_construct; cl3_fields = Array.map (flatten_field ctx) c.hlc_fields; }, { st3_method = lookup_method ctx c.hlc_static_construct; st3_fields = Array.map (flatten_field ctx) c.hlc_static_fields; } let flatten_opcode ctx i = function | HBreakPoint -> A3BreakPoint | HNop -> A3Nop | HThrow -> A3Throw | HGetSuper n -> A3GetSuper (lookup_name ctx n) | HSetSuper n -> A3SetSuper (lookup_name ctx n) | HDxNs s -> A3DxNs (lookup_ident ctx s) | HDxNsLate -> A3DxNsLate | HRegKill r -> A3RegKill r | HLabel -> A3Label | HJump (j,n) -> ctx.fjumps <- i :: ctx.fjumps; A3Jump (j,n) | HSwitch (n,l) -> ctx.fjumps <- i :: ctx.fjumps; A3Switch (n,l) | HPushWith -> A3PushWith | HPopScope -> A3PopScope | HForIn -> A3ForIn | HHasNext -> A3HasNext | HNull -> A3Null | HUndefined -> A3Undefined | HForEach -> A3ForEach | HSmallInt n -> A3SmallInt n | HInt n -> A3Int n | HTrue -> A3True | HFalse -> A3False | HNaN -> A3NaN | HPop -> A3Pop | HDup -> A3Dup | HSwap -> A3Swap | HString s -> A3String (lookup_ident ctx s) | HIntRef i -> A3IntRef (lookup ctx ctx.fints i) | HUIntRef i -> A3UIntRef (lookup ctx ctx.fuints i) | HFloat f -> A3Float (lookup ctx ctx.ffloats f) | HScope -> A3Scope | HNamespace n -> A3Namespace (lookup ctx ctx.fnamespaces n) | HNext (r1,r2) -> A3Next (r1,r2) | HFunction m -> A3Function (lookup_method ctx m) | HCallStack n -> A3CallStack n | HConstruct n -> A3Construct n | HCallMethod (s,n) -> A3CallMethod (s,n) | HCallStatic (m,n) -> A3CallStatic (no_nz (lookup_method ctx m),n) | HCallSuper (i,n) -> A3CallSuper (lookup_name ctx i,n) | HCallProperty (i,n) -> A3CallProperty (lookup_name ctx i,n) | HRetVoid -> A3RetVoid | HRet -> A3Ret | HConstructSuper n -> A3ConstructSuper n | HConstructProperty (i,n) -> A3ConstructProperty (lookup_name ctx i,n) | HCallPropLex (i,n) -> A3CallPropLex (lookup_name ctx i,n) | HCallSuperVoid (i,n) -> A3CallSuperVoid (lookup_name ctx i,n) | HCallPropVoid (i,n)-> A3CallPropVoid (lookup_name ctx i,n) | HApplyType n -> A3ApplyType n | HObject n -> A3Object n | HArray n -> A3Array n | HNewBlock -> A3NewBlock | HClassDef c -> A3ClassDef (As3parse.magic_index_nz (As3parse.index_nz_int (lookup_class ctx c))) | HGetDescendants i -> A3GetDescendants (lookup_name ctx i) | HCatch n -> A3Catch n | HFindPropStrict i -> A3FindPropStrict (lookup_name ctx i) | HFindProp i -> A3FindProp (lookup_name ctx i) | HFindDefinition i -> A3FindDefinition (lookup_name ctx i) | HGetLex i -> A3GetLex (lookup_name ctx i) | HSetProp i -> A3SetProp (lookup_name ctx i) | HReg r -> A3Reg r | HSetReg r -> A3SetReg r | HGetGlobalScope -> A3GetGlobalScope | HGetScope n -> A3GetScope n | HGetProp n -> A3GetProp (lookup_name ctx n) | HInitProp n -> A3InitProp (lookup_name ctx n) | HDeleteProp n -> A3DeleteProp (lookup_name ctx n) | HGetSlot s -> A3GetSlot s | HSetSlot s -> A3SetSlot s | HToString -> A3ToString | HToXml -> A3ToXml | HToXmlAttr -> A3ToXmlAttr | HToInt -> A3ToInt | HToUInt -> A3ToUInt | HToNumber -> A3ToNumber | HToBool -> A3ToBool | HToObject -> A3ToObject | HCheckIsXml -> A3CheckIsXml | HCast n -> A3Cast (lookup_name ctx n) | HAsAny -> A3AsAny | HAsString -> A3AsString | HAsType n -> A3AsType (lookup_name ctx n) | HAsObject -> A3AsObject | HIncrReg r -> A3IncrReg r | HDecrReg r -> A3DecrReg r | HTypeof -> A3Typeof | HInstanceOf -> A3InstanceOf | HIsType t -> A3IsType (lookup_name ctx t) | HIncrIReg r -> A3IncrIReg r | HDecrIReg r -> A3DecrIReg r | HThis -> A3This | HSetThis -> A3SetThis | HDebugReg (i,r,l) -> A3DebugReg (lookup_ident ctx i,r,l) | HDebugLine l -> A3DebugLine l | HDebugFile f -> A3DebugFile (lookup_ident ctx f) | HBreakPointLine n -> A3BreakPointLine n | HTimestamp -> A3Timestamp | HOp op -> A3Op op | HUnk c -> A3Unk c let flatten_code ctx hcode trys = let positions = MultiArray.make (MultiArray.length hcode + 1) 0 in let pos = ref 0 in let old = ctx.fjumps in ctx.fjumps <- []; let code = MultiArray.mapi (fun i op -> let op = flatten_opcode ctx i op in pos := !pos + As3code.length op; MultiArray.set positions (i + 1) !pos; op ) hcode in (* patch jumps *) List.iter (fun j -> MultiArray.set code j (match MultiArray.get code j with | A3Jump (jc,n) -> A3Jump (jc,MultiArray.get positions (j+n) - MultiArray.get positions (j+1)) | A3Switch (n,infos) -> A3Switch (MultiArray.get positions (j+n) - MultiArray.get positions (j),List.map (fun n -> MultiArray.get positions (j+n) - MultiArray.get positions (j)) infos) | _ -> assert false); ) ctx.fjumps; (* patch trys *) let trys = Array.mapi (fun i t -> { tc3_start = MultiArray.get positions t.hltc_start; tc3_end = MultiArray.get positions t.hltc_end; tc3_handle = MultiArray.get positions t.hltc_handle; tc3_type = opt lookup_name ctx t.hltc_type; tc3_name = opt lookup_name ctx t.hltc_name; } ) trys in ctx.fjumps <- old; code, trys let flatten_function ctx f mid = let code, trys = flatten_code ctx f.hlf_code f.hlf_trys in { fun3_id = mid; fun3_stack_size = f.hlf_stack_size; fun3_nregs = f.hlf_nregs; fun3_init_scope = f.hlf_init_scope; fun3_max_scope = f.hlf_max_scope; fun3_code = code; fun3_trys = trys; fun3_locals = Array.map (fun (n,t,s,c) -> { f3_name = lookup_name ctx n; f3_slot = s; f3_kind = A3FVar { v3_type = opt lookup_name ctx t; v3_value = A3VNone; v3_const = c }; f3_metas = None; } ) f.hlf_locals; } let flatten_method ctx m = let mid = lookup_method ctx m in (match m.hlmt_function with | None -> () | Some f -> let x = flatten_function ctx f mid in ctx.ffunctions <- x :: ctx.ffunctions); { mt3_ret = opt lookup_name ctx m.hlmt_ret; mt3_args = List.map (opt lookup_name ctx) m.hlmt_args; mt3_native = m.hlmt_native; mt3_var_args = m.hlmt_var_args; mt3_arguments_defined = m.hlmt_arguments_defined; mt3_uses_dxns = m.hlmt_uses_dxns; mt3_new_block = m.hlmt_new_block; mt3_unused_flag = m.hlmt_unused_flag; mt3_debug_name = opt lookup_ident ctx m.hlmt_debug_name; mt3_dparams = opt (fun ctx -> List.map (flatten_value ctx)) ctx m.hlmt_dparams; mt3_pnames = opt (fun ctx -> List.map (opt lookup_ident ctx)) ctx m.hlmt_pnames; } let flatten_static ctx s = { st3_method = lookup_method ctx s.hls_method; st3_fields = Array.map (flatten_field ctx) s.hls_fields; } let rec browse_method ctx m = let ml, _ = ctx in if not (List.memq m !ml) then begin ml := m :: !ml; match m.hlmt_function with | None -> () | Some f -> MultiArray.iter (function | HFunction f | HCallStatic (f,_) -> browse_method ctx f | HClassDef _ -> () (* ignore, should be in fields list anyway *) | _ -> () ) f.hlf_code end and browse_class ctx c = let _, cl = ctx in if not (List.memq c !cl) then begin cl := c :: !cl; browse_method ctx c.hlc_construct; browse_method ctx c.hlc_static_construct; Array.iter (browse_field ctx) c.hlc_fields; Array.iter (browse_field ctx) c.hlc_static_fields; end and browse_field ctx f = match f.hlf_kind with | HFMethod m -> browse_method ctx m.hlm_type | HFVar _ -> () | HFFunction m -> browse_method ctx m | HFClass c -> browse_class ctx c let flatten t = let id _ x = x in (* collect methods and classes, sort by index and force evaluation in order to keep order *) let methods = ref [] in let classes = ref [] in let ctx = (methods,classes) in List.iter (fun s -> Array.iter (browse_field ctx) s.hls_fields; browse_method ctx s.hls_method; ) t; let methods = List.sort (fun m1 m2 -> m1.hlmt_index - m2.hlmt_index) (List.rev !methods) in (* done *) let rec ctx = { fints = new_lookup id; fuints = new_lookup id; ffloats = new_lookup id; fidents = new_lookup id; fnamespaces = new_lookup flatten_namespace; fnsets = new_lookup flatten_ns_set; fnames = new_lookup flatten_name; fmetas = new_lookup flatten_meta; fmethods = new_index_lookup methods flatten_method; fclasses = new_index_lookup (List.rev !classes) flatten_class; fjumps = []; ffunctions = []; } in ignore(lookup_ident ctx ""); let inits = List.map (flatten_static ctx) t in let classes = lookup_index_array ctx.fclasses in { as3_ints = lookup_array ctx.fints; as3_uints = lookup_array ctx.fuints; as3_floats = lookup_array ctx.ffloats; as3_idents = lookup_array ctx.fidents; as3_namespaces = lookup_array ctx.fnamespaces; as3_nsets = lookup_array ctx.fnsets; as3_names = lookup_array ctx.fnames; as3_metadatas = lookup_array ctx.fmetas; as3_method_types = lookup_index_array ctx.fmethods; as3_classes = Array.map fst classes; as3_statics = Array.map snd classes; as3_functions = Array.of_list (List.rev ctx.ffunctions); as3_inits = Array.of_list inits; as3_unknown = ""; }